summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorruki <[email protected]>2018-05-12 22:18:30 +0800
committerruki <[email protected]>2018-05-12 22:18:30 +0800
commit0d8e537bc562c39695de25371ba6e8a0adeac5dc (patch)
tree688df5b9753d765fa43af42f36133501d9591ce0
parent166a68fd2868db1cea82bfe2e3172d0ca1bd0ae6 (diff)
add wdk.mc rule and serial tests
-rw-r--r--.gitignore1
-rw-r--r--tests/projects/wdk/kmdf/serial/error.c67
-rw-r--r--tests/projects/wdk/kmdf/serial/flush.c86
-rw-r--r--tests/projects/wdk/kmdf/serial/immediat.c458
-rw-r--r--tests/projects/wdk/kmdf/serial/initunlo.c197
-rw-r--r--tests/projects/wdk/kmdf/serial/ioctl.c2187
-rw-r--r--tests/projects/wdk/kmdf/serial/isr.c1517
-rw-r--r--tests/projects/wdk/kmdf/serial/log.c97
-rw-r--r--tests/projects/wdk/kmdf/serial/log.h37
-rw-r--r--tests/projects/wdk/kmdf/serial/modmflow.c1714
-rw-r--r--tests/projects/wdk/kmdf/serial/openclos.c850
-rw-r--r--tests/projects/wdk/kmdf/serial/pnp.c2804
-rw-r--r--tests/projects/wdk/kmdf/serial/power.c331
-rw-r--r--tests/projects/wdk/kmdf/serial/precomp.h18
-rw-r--r--tests/projects/wdk/kmdf/serial/precompsrc.c1
-rw-r--r--tests/projects/wdk/kmdf/serial/purge.c175
-rw-r--r--tests/projects/wdk/kmdf/serial/qsfile.c180
-rw-r--r--tests/projects/wdk/kmdf/serial/read.c1748
-rw-r--r--tests/projects/wdk/kmdf/serial/registry.c443
-rw-r--r--tests/projects/wdk/kmdf/serial/serial.h1757
-rw-r--r--tests/projects/wdk/kmdf/serial/serial.inxbin0 -> 6212 bytes
-rw-r--r--tests/projects/wdk/kmdf/serial/serial.rc14
-rw-r--r--tests/projects/wdk/kmdf/serial/serialp.h596
-rw-r--r--tests/projects/wdk/kmdf/serial/serlog.mc290
-rw-r--r--tests/projects/wdk/kmdf/serial/trace.h118
-rw-r--r--tests/projects/wdk/kmdf/serial/utils.c1946
-rw-r--r--tests/projects/wdk/kmdf/serial/waitmask.c574
-rw-r--r--tests/projects/wdk/kmdf/serial/wmi.c295
-rw-r--r--tests/projects/wdk/kmdf/serial/write.c1195
-rw-r--r--tests/projects/wdk/kmdf/serial/xmake.lua23
-rw-r--r--xmake/rules/wdk/inf/xmake.lua81
-rw-r--r--xmake/rules/wdk/mc/xmake.lua120
-rw-r--r--xmake/rules/wdk/xmake.lua8
33 files changed, 19899 insertions, 29 deletions
diff --git a/.gitignore b/.gitignore
index af6daaa36..77458781a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -26,6 +26,7 @@
*.zip
*.gch
*.gch.d
+*.sys
cscope.*
gmon.out
other
diff --git a/tests/projects/wdk/kmdf/serial/error.c b/tests/projects/wdk/kmdf/serial/error.c
new file mode 100644
index 000000000..904e54b05
--- /dev/null
+++ b/tests/projects/wdk/kmdf/serial/error.c
@@ -0,0 +1,67 @@
+/*++
+Copyright (c) Microsoft Corporation
+
+Module Name:
+
+ error.c
+
+Abstract:
+
+ This module contains the code that is very specific to error
+ operations in the serial driver
+
+Environment:
+
+ Kernel mode
+
+--*/
+
+#include "precomp.h"
+
+#if defined(EVENT_TRACING)
+#include "error.tmh"
+#endif
+
+
+VOID
+SerialCommError(
+ IN WDFDPC Dpc
+ )
+/*++
+
+Routine Description:
+
+ This routine is invoked at dpc level to in response to
+ a comm error. All comm errors complete all read and writes
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PSERIAL_DEVICE_EXTENSION Extension = NULL;
+
+ Extension = SerialGetDeviceExtension(WdfDpcGetParentObject(Dpc));
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_INIT,
+ ">SerialCommError(%p)\n", Extension);
+
+ SerialFlushRequests(
+ Extension->WriteQueue,
+ &Extension->CurrentWriteRequest
+ );
+
+ SerialFlushRequests(
+ Extension->ReadQueue,
+ &Extension->CurrentReadRequest
+ );
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_INIT,
+ "<SerialCommError\n");
+}
+
+
diff --git a/tests/projects/wdk/kmdf/serial/flush.c b/tests/projects/wdk/kmdf/serial/flush.c
new file mode 100644
index 000000000..695a60a26
--- /dev/null
+++ b/tests/projects/wdk/kmdf/serial/flush.c
@@ -0,0 +1,86 @@
+/*++
+
+Copyright (c) 1991, 1992, 1993 Microsoft Corporation
+
+Module Name:
+
+ flush.c
+
+Abstract:
+
+ This module contains the code that is very specific to flush
+ operations in the serial driver
+
+Environment:
+
+ Kernel mode
+
+--*/
+
+#include "precomp.h"
+
+#if defined(EVENT_TRACING)
+#include "flush.tmh"
+#endif
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGE, SerialFlush)
+#endif
+
+#pragma warning(push)
+#pragma warning(disable:28118) // this callback will run at IRQL=PASSIVE_LEVEL
+_Use_decl_annotations_
+NTSTATUS
+SerialFlush(
+ WDFDEVICE Device,
+ PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ This is the dispatch routine for flush. Flushing works by placing
+ this request in the write queue. When this request reaches the
+ front of the write queue we simply complete it since this implies
+ that all previous writes have completed.
+
+Arguments:
+
+ DeviceObject - Pointer to the device object for this device
+
+ Irp - Pointer to the IRP for the current request
+
+Return Value:
+
+ Could return status success, cancelled, or pending.
+
+--*/
+
+{
+
+ PSERIAL_DEVICE_EXTENSION extension;
+
+ extension = SerialGetDeviceExtension(Device);
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_WRITE, ">SerialFlush(%p, %p)\n", Device, Irp);
+
+ PAGED_CODE();
+
+ WdfIoQueueStopSynchronously(extension->WriteQueue);
+ //
+ // Flush is done - restart the queue
+ //
+ WdfIoQueueStart(extension->WriteQueue);
+
+ Irp->IoStatus.Information = 0L;
+ Irp->IoStatus.Status = STATUS_SUCCESS;
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_WRITE, "<SerialFlush\n");
+
+ return STATUS_SUCCESS;
+ }
+#pragma warning(pop) // enable 28118 again
+
diff --git a/tests/projects/wdk/kmdf/serial/immediat.c b/tests/projects/wdk/kmdf/serial/immediat.c
new file mode 100644
index 000000000..9fd7683af
--- /dev/null
+++ b/tests/projects/wdk/kmdf/serial/immediat.c
@@ -0,0 +1,458 @@
+/*++
+
+Copyright (c) 1991, 1992, 1993 - 1997 Microsoft Corporation
+
+Module Name:
+
+ immediat.c
+
+Abstract:
+
+ This module contains the code that is very specific to transmit
+ immediate character operations in the serial driver
+
+Environment:
+
+ Kernel mode
+
+--*/
+
+#include "precomp.h"
+
+#if defined(EVENT_TRACING)
+#include "immediat.tmh"
+#endif
+
+
+VOID
+SerialGetNextImmediate(
+ IN WDFREQUEST *CurrentOpRequest,
+ IN WDFQUEUE QueueToProcess,
+ IN WDFREQUEST *NewRequest,
+ IN BOOLEAN CompleteCurrent,
+ IN PSERIAL_DEVICE_EXTENSION Extension
+ );
+
+EVT_WDF_REQUEST_CANCEL SerialCancelImmediate;
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialGiveImmediateToIsr;
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialGrabImmediateFromIsr;
+
+
+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
+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");
+
+}
+
+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");
+}
+
+VOID
+SerialGetNextImmediate(
+ IN WDFREQUEST *CurrentOpRequest,
+ IN WDFQUEUE QueueToProcess,
+ IN WDFREQUEST *NewRequest,
+ IN BOOLEAN CompleteCurrent,
+ IN PSERIAL_DEVICE_EXTENSION Extension
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to complete the current immediate
+ request. Even though the current immediate will always
+ be completed and there is no queue associated with it,
+ we use this routine so that we can try to satisfy
+ a wait for transmit queue empty event.
+
+Arguments:
+
+ CurrentOpRequest - Pointer to the pointer that points to the
+ current write request. This should point
+ to CurrentImmediateRequest.
+
+ QueueToProcess - Always NULL.
+
+ NewRequest - Always NULL on exit to this routine.
+
+ CompleteCurrent - Should always be true for this routine.
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ WDFREQUEST oldRequest = *CurrentOpRequest;
+ PREQUEST_CONTEXT reqContext = SerialGetRequestContext(oldRequest);
+
+ UNREFERENCED_PARAMETER(QueueToProcess);
+ UNREFERENCED_PARAMETER(CompleteCurrent);
+
+
+ ASSERT(Extension->TotalCharsQueued >= 1);
+ Extension->TotalCharsQueued--;
+
+ *CurrentOpRequest = NULL;
+ *NewRequest = NULL;
+ WdfInterruptSynchronize(
+ Extension->WdfInterrupt,
+ SerialProcessEmptyTransmit,
+ Extension
+ );
+
+ SerialCompleteRequest(oldRequest, reqContext->Status, reqContext->Information);
+}
+
+VOID
+SerialCancelImmediate(
+ IN WDFREQUEST Request
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to cancel a request that is waiting on
+ a comm event.
+
+Arguments:
+
+ Request - Pointer to the WDFREQUEST for the current request
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PSERIAL_DEVICE_EXTENSION Extension = NULL;
+ WDFDEVICE device = WdfIoQueueGetDevice(WdfRequestGetIoQueue(Request));
+
+ UNREFERENCED_PARAMETER(Request);
+
+ Extension = SerialGetDeviceExtension(device);
+
+ SerialTryToCompleteCurrent(
+ Extension,
+ SerialGrabImmediateFromIsr,
+ STATUS_CANCELLED,
+ &Extension->CurrentImmediateRequest,
+ NULL,
+ NULL,
+ Extension->ImmediateTotalTimer,
+ NULL,
+ SerialGetNextImmediate,
+ SERIAL_REF_CANCEL
+ );
+
+}
+
+BOOLEAN
+SerialGiveImmediateToIsr(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+/*++
+
+Routine Description:
+
+ Try to start off the write by slipping it in behind
+ a transmit immediate char, or if that isn't available
+ and the transmit holding register is empty, "tickle"
+ the UART into interrupting with a transmit buffer
+ empty.
+
+ NOTE: This routine is called by WdfInterruptSynchronize.
+
+ NOTE: This routine assumes that it is called with the
+ cancel spin lock held.
+
+Arguments:
+
+ Context - Really a pointer to the device extension.
+
+Return Value:
+
+ This routine always returns FALSE.
+
+--*/
+{
+ PSERIAL_DEVICE_EXTENSION Extension = Context;
+ PREQUEST_CONTEXT reqContext;
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ reqContext = SerialGetRequestContext(Extension->CurrentImmediateRequest);
+
+ Extension->TransmitImmediate = TRUE;
+ Extension->ImmediateChar = *((UCHAR *) (reqContext->SystemBuffer));
+
+ //
+ // The isr now has a reference to the request.
+ //
+
+ SERIAL_SET_REFERENCE(
+ reqContext,
+ SERIAL_REF_ISR
+ );
+
+ //
+ // Check first to see if a write is going on. If
+ // there is then we'll just slip in during the write.
+ //
+
+ if (!Extension->WriteLength) {
+
+ //
+ // If there is no normal write transmitting then we
+ // will "re-enable" the transmit holding register empty
+ // interrupt. The 8250 family of devices will always
+ // signal a transmit holding register empty interrupt
+ // *ANY* time this bit is set to one. By doing things
+ // this way we can simply use the normal interrupt code
+ // to start off this write.
+ //
+ // We've been keeping track of whether the transmit holding
+ // register is empty so it we only need to do this
+ // if the register is empty.
+ //
+
+ if (Extension->HoldingEmpty) {
+ DISABLE_ALL_INTERRUPTS(Extension, Extension->Controller);
+ ENABLE_ALL_INTERRUPTS(Extension, Extension->Controller);
+
+ }
+
+ }
+
+ return FALSE;
+
+}
+
+BOOLEAN
+SerialGrabImmediateFromIsr(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+
+ This routine is used to grab the current request, which could be timing
+ out or canceling, from the ISR
+
+ NOTE: This routine is being called from WdfInterruptSynchronize.
+
+ NOTE: This routine assumes that the cancel spin lock is held
+ when this routine is called.
+
+Arguments:
+
+ Context - Really a pointer to the device extension.
+
+Return Value:
+
+ Always false.
+
+--*/
+
+{
+ PSERIAL_DEVICE_EXTENSION Extension = Context;
+ PREQUEST_CONTEXT reqContext;
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ reqContext = SerialGetRequestContext(Extension->CurrentImmediateRequest);
+
+ if (Extension->TransmitImmediate) {
+
+ Extension->TransmitImmediate = FALSE;
+
+ //
+ // Since the isr no longer references this request, we can
+ // decrement it's reference count.
+ //
+
+ SERIAL_CLEAR_REFERENCE(
+ reqContext,
+ SERIAL_REF_ISR
+ );
+
+ }
+
+ return FALSE;
+
+}
+
+
diff --git a/tests/projects/wdk/kmdf/serial/initunlo.c b/tests/projects/wdk/kmdf/serial/initunlo.c
new file mode 100644
index 000000000..07e7fb003
--- /dev/null
+++ b/tests/projects/wdk/kmdf/serial/initunlo.c
@@ -0,0 +1,197 @@
+/*++
+
+Copyright (c) Microsoft Corporation
+
+Module Name:
+
+ initunlo.c
+
+Abstract:
+
+ This module contains the code that is very specific to initialization
+ and unload operations in the serial driver
+
+ WDF Version of serial sample doesn't support:
+ 1) Multiport Serial devices.
+ 2) Enumeration of Non PNP serial devices that are not detected by BIOS
+ (IO address range 0x2F0-0x2F7 using IRQ 9)
+Environment:
+
+ Kernel mode
+
+--*/
+
+#include "precomp.h"
+
+#if defined(EVENT_TRACING)
+#include "initunlo.tmh"
+#endif
+
+static const PHYSICAL_ADDRESS SerialPhysicalZero = {0};
+
+//
+// We use this to query into the registry as to whether we
+// should break at driver entry.
+//
+
+SERIAL_FIRMWARE_DATA driverDefaults;
+
+//
+// This is exported from the kernel. It is used to point
+// to the address that the kernel debugger is using.
+//
+extern PUCHAR *KdComPortInUse;
+//
+// INIT - only needed during init and then can be disposed
+// PAGESRP0 - always paged / never locked
+// PAGESER - must be locked when a device is open, else paged
+//
+//
+// INIT is used for DriverEntry() specific code
+//
+// PAGESRP0 is used for code that is not often called and has nothing
+// to do with I/O performance. An example, passive-level PNP
+// support functions
+//
+// PAGESER is used for code that needs to be locked after an open for both
+// performance and IRQL reasons.
+//
+
+ULONG DebugLevel = TRACE_LEVEL_INFORMATION;
+ULONG DebugFlag = 0xf;//0x46;//0x4FF; //0x00000006;
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(INIT, DriverEntry)
+#pragma alloc_text(PAGE, SerialEvtDriverContextCleanup)
+#endif
+
+
+
+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)\n");
+ //
+ // 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;
+}
+
+
+_Use_decl_annotations_
+VOID
+SerialEvtDriverContextCleanup(
+ WDFOBJECT 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");
+
+}
+
+
+
diff --git a/tests/projects/wdk/kmdf/serial/ioctl.c b/tests/projects/wdk/kmdf/serial/ioctl.c
new file mode 100644
index 000000000..07ff2b147
--- /dev/null
+++ b/tests/projects/wdk/kmdf/serial/ioctl.c
@@ -0,0 +1,2187 @@
+/*++
+
+Copyright (c) Microsoft Corporation
+
+Module Name:
+
+ ioctl.c
+
+Abstract:
+
+ This module contains the ioctl dispatcher as well as a couple
+ of routines that are generally just called in response to
+ ioctl calls.
+
+Environment:
+
+ Kernel mode
+
+--*/
+
+#include "precomp.h"
+
+#if defined(EVENT_TRACING)
+#include "ioctl.tmh"
+#endif
+
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialGetModemUpdate;
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialGetCommStatus;
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialSetEscapeChar;
+
+PCHAR
+SerialGetIoctlName(
+ IN ULONG IoControlCode
+ )
+/*++
+
+Routine Description:
+ SerialGetIoctlName returns the name of the ioctl
+
+--*/
+{
+ switch (IoControlCode)
+ {
+ case IOCTL_SERIAL_SET_BAUD_RATE : return "IOCTL_SERIAL_SET_BAUD_RATE";
+ case IOCTL_SERIAL_GET_BAUD_RATE: return "IOCTL_SERIAL_GET_BAUD_RATE";
+ case IOCTL_SERIAL_GET_MODEM_CONTROL: return "IOCTL_SERIAL_GET_MODEM_CONTROL";
+ case IOCTL_SERIAL_SET_MODEM_CONTROL: return "IOCTL_SERIAL_SET_MODEM_CONTROL";
+ case IOCTL_SERIAL_SET_FIFO_CONTROL: return "IOCTL_SERIAL_SET_FIFO_CONTROL";
+ case IOCTL_SERIAL_SET_LINE_CONTROL: return "IOCTL_SERIAL_SET_LINE_CONTROL";
+ case IOCTL_SERIAL_GET_LINE_CONTROL: return "IOCTL_SERIAL_GET_LINE_CONTROL";
+ case IOCTL_SERIAL_SET_TIMEOUTS: return "IOCTL_SERIAL_SET_TIMEOUTS";
+ case IOCTL_SERIAL_GET_TIMEOUTS: return "IOCTL_SERIAL_GET_TIMEOUTS";
+ case IOCTL_SERIAL_SET_CHARS: return "IOCTL_SERIAL_SET_CHARS";
+ case IOCTL_SERIAL_GET_CHARS: return "IOCTL_SERIAL_GET_CHARS";
+ case IOCTL_SERIAL_SET_DTR: return "IOCTL_SERIAL_SET_DTR";
+ case IOCTL_SERIAL_CLR_DTR: return "IOCTL_SERIAL_SET_DTR";
+ case IOCTL_SERIAL_RESET_DEVICE: return "IOCTL_SERIAL_RESET_DEVICE";
+ case IOCTL_SERIAL_SET_RTS: return "IOCTL_SERIAL_SET_RTS";
+ case IOCTL_SERIAL_CLR_RTS: return "IOCTL_SERIAL_CLR_RTS";
+ case IOCTL_SERIAL_SET_XOFF: return "IOCTL_SERIAL_SET_XOFF";
+ case IOCTL_SERIAL_SET_XON: return "IOCTL_SERIAL_SET_XON";
+ case IOCTL_SERIAL_SET_BREAK_ON: return "IOCTL_SERIAL_SET_BREAK_ON";
+ case IOCTL_SERIAL_SET_BREAK_OFF: return "IOCTL_SERIAL_SET_BREAK_OFF";
+ case IOCTL_SERIAL_SET_QUEUE_SIZE: return "IOCTL_SERIAL_SET_QUEUE_SIZE";
+ case IOCTL_SERIAL_GET_WAIT_MASK: return "IOCTL_SERIAL_GET_WAIT_MASK";
+ case IOCTL_SERIAL_SET_WAIT_MASK: return "IOCTL_SERIAL_SET_WAIT_MASK";
+ case IOCTL_SERIAL_WAIT_ON_MASK: return "IOCTL_SERIAL_WAIT_ON_MASK";
+ case IOCTL_SERIAL_IMMEDIATE_CHAR: return "IOCTL_SERIAL_IMMEDIATE_CHAR";
+ case IOCTL_SERIAL_PURGE: return "IOCTL_SERIAL_PURGE";
+ case IOCTL_SERIAL_GET_HANDFLOW: return "IOCTL_SERIAL_GET_HANDFLOW";
+ case IOCTL_SERIAL_SET_HANDFLOW: return "IOCTL_SERIAL_SET_HANDFLOW";
+ case IOCTL_SERIAL_GET_MODEMSTATUS: return "IOCTL_SERIAL_GET_MODEMSTATUS";
+ case IOCTL_SERIAL_GET_DTRRTS: return "IOCTL_SERIAL_GET_DTRRTS";
+ case IOCTL_SERIAL_GET_COMMSTATUS: return "IOCTL_SERIAL_GET_COMMSTATUS";
+ case IOCTL_SERIAL_GET_PROPERTIES: return "IOCTL_SERIAL_GET_PROPERTIES";
+ case IOCTL_SERIAL_XOFF_COUNTER: return "IOCTL_SERIAL_XOFF_COUNTER";
+ case IOCTL_SERIAL_LSRMST_INSERT: return "IOCTL_SERIAL_LSRMST_INSERT";
+ case IOCTL_SERIAL_CONFIG_SIZE: return "IOCTL_SERIAL_CONFIG_SIZE";
+ case IOCTL_SERIAL_GET_STATS: return "IOCTL_SERIAL_GET_STATS";
+ case IOCTL_SERIAL_CLEAR_STATS: return "IOCTL_SERIAL_CLEAR_STATS";
+ default: return "UnKnown ioctl";
+ }
+}
+
+
+
+BOOLEAN
+SerialGetStats(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ In sync with the interrpt service routine (which sets the perf stats)
+ return the perf stats to the caller.
+
+
+Arguments:
+
+ Context - Pointer to a the request.
+
+Return Value:
+
+ This routine always returns FALSE.
+
+--*/
+
+{
+ PREQUEST_CONTEXT reqContext = (PREQUEST_CONTEXT)Context;
+ PSERIAL_DEVICE_EXTENSION extension = SerialGetDeviceExtension(WdfInterruptGetDevice(Interrupt));
+ PSERIALPERF_STATS sp = reqContext->SystemBuffer;
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ *sp = extension->PerfStats;
+ return FALSE;
+
+}
+
+
+BOOLEAN
+SerialClearStats(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ In sync with the interrpt service routine (which sets the perf stats)
+ clear the perf stats.
+
+
+Arguments:
+
+ Context - Pointer to a the extension.
+
+Return Value:
+
+ This routine always returns FALSE.
+
+--*/
+
+{
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ RtlZeroMemory(
+ &((PSERIAL_DEVICE_EXTENSION)Context)->PerfStats,
+ sizeof(SERIALPERF_STATS)
+ );
+
+ RtlZeroMemory(&((PSERIAL_DEVICE_EXTENSION)Context)->WmiPerfData,
+ sizeof(SERIAL_WMI_PERF_DATA));
+
+ return FALSE;
+}
+
+
+
+BOOLEAN
+SerialSetChars(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to set the special characters for the
+ driver.
+
+Arguments:
+
+ Context - Pointer to a structure that contains a pointer to
+ the device extension and a pointer to a special characters
+ structure.
+
+Return Value:
+
+ This routine always returns FALSE.
+
+--*/
+
+{
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ ((PSERIAL_IOCTL_SYNC)Context)->Extension->SpecialChars =
+ *((PSERIAL_CHARS)(((PSERIAL_IOCTL_SYNC)Context)->Data));
+
+ return FALSE;
+}
+
+
+BOOLEAN
+SerialSetBaud(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to set the baud rate of the device.
+
+Arguments:
+
+ Context - Pointer to a structure that contains a pointer to
+ the device extension and what should be the current
+ baud rate.
+
+Return Value:
+
+ This routine always returns FALSE.
+
+--*/
+
+{
+ PSERIAL_DEVICE_EXTENSION Extension = ((PSERIAL_IOCTL_SYNC)Context)->Extension;
+ USHORT Appropriate = PtrToUshort(((PSERIAL_IOCTL_SYNC)Context)->Data);
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ WRITE_DIVISOR_LATCH(
+ Extension,
+ Extension->Controller,
+ Appropriate
+ );
+
+ return FALSE;
+}
+
+
+BOOLEAN
+SerialSetLineControl(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to set the buad rate of the device.
+
+Arguments:
+
+ Context - Pointer to the device extension.
+
+Return Value:
+
+ This routine always returns FALSE.
+
+--*/
+
+{
+ PSERIAL_DEVICE_EXTENSION Extension = Context;
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ WRITE_LINE_CONTROL(Extension,
+ Extension->Controller,
+ Extension->LineControl
+ );
+
+ return FALSE;
+}
+
+
+BOOLEAN
+SerialGetModemUpdate(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is simply used to call the interrupt level routine
+ that handles modem status update.
+
+Arguments:
+
+ Context - Pointer to a structure that contains a pointer to
+ the device extension and a pointer to a ulong.
+
+Return Value:
+
+ This routine always returns FALSE.
+
+--*/
+
+{
+ PSERIAL_DEVICE_EXTENSION Extension = ((PSERIAL_IOCTL_SYNC)Context)->Extension;
+ ULONG *Result = (ULONG *)(((PSERIAL_IOCTL_SYNC)Context)->Data);
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ *Result = SerialHandleModemUpdate(
+ Extension,
+ FALSE
+ );
+
+ return FALSE;
+}
+
+
+
+BOOLEAN
+SerialSetMCRContents(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+/*++
+
+Routine Description:
+
+ This routine is simply used to set the contents of the MCR
+
+Arguments:
+
+ Context - Pointer to a structure that contains a pointer to
+ the device extension and a pointer to a ulong.
+
+Return Value:
+
+ This routine always returns FALSE.
+
+--*/
+{
+ PSERIAL_DEVICE_EXTENSION Extension = ((PSERIAL_IOCTL_SYNC)Context)->Extension;
+ ULONG *Result = (ULONG *)(((PSERIAL_IOCTL_SYNC)Context)->Data);
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ //
+ // This is severe casting abuse!!!
+ //
+ WRITE_MODEM_CONTROL(Extension, Extension->Controller, (UCHAR)PtrToUlong(Result));
+
+ return FALSE;
+}
+
+
+
+
+BOOLEAN
+SerialGetMCRContents(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is simply used to get the contents of the MCR
+
+Arguments:
+
+ Context - Pointer to a structure that contains a pointer to
+ the device extension and a pointer to a ulong.
+
+Return Value:
+
+ This routine always returns FALSE.
+
+--*/
+
+{
+ PSERIAL_DEVICE_EXTENSION Extension = ((PSERIAL_IOCTL_SYNC)Context)->Extension;
+ ULONG *Result = (ULONG *)(((PSERIAL_IOCTL_SYNC)Context)->Data);
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ *Result = READ_MODEM_CONTROL(Extension, Extension->Controller);
+
+ return FALSE;
+}
+
+
+
+
+BOOLEAN
+SerialSetFCRContents(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+/*++
+
+Routine Description:
+
+ This routine is simply used to set the contents of the FCR
+
+Arguments:
+
+ Context - Pointer to a structure that contains a pointer to
+ the device extension and a pointer to a ulong.
+
+Return Value:
+
+ This routine always returns FALSE.
+
+--*/
+{
+ PSERIAL_DEVICE_EXTENSION Extension = ((PSERIAL_IOCTL_SYNC)Context)->Extension;
+ ULONG *Result = (ULONG *)(((PSERIAL_IOCTL_SYNC)Context)->Data);
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ //
+ // This is severe casting abuse!!!
+ //
+ WRITE_FIFO_CONTROL(Extension, Extension->Controller, (UCHAR)*Result);
+
+ return FALSE;
+}
+
+
+
+BOOLEAN
+SerialGetCommStatus(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This is used to get the current state of the serial driver.
+
+Arguments:
+
+ Context - Pointer to a structure that contains a pointer to
+ the device extension and a pointer to a serial status
+ record.
+
+Return Value:
+
+ This routine always returns FALSE.
+
+--*/
+
+{
+ PSERIAL_DEVICE_EXTENSION Extension = ((PSERIAL_IOCTL_SYNC)Context)->Extension;
+ PSERIAL_STATUS Stat = ((PSERIAL_IOCTL_SYNC)Context)->Data;
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ Stat->Errors = Extension->ErrorWord;
+ Extension->ErrorWord = 0;
+
+ //
+ // Eof isn't supported in binary mode
+ //
+ Stat->EofReceived = FALSE;
+
+ Stat->AmountInInQueue = Extension->CharsInInterruptBuffer;
+
+ Stat->AmountInOutQueue = Extension->TotalCharsQueued;
+
+ if (Extension->WriteLength) {
+
+ //
+ // By definition if we have a writelength the we have
+ // a current write request.
+ //
+ PREQUEST_CONTEXT reqContext = NULL;
+
+ ASSERT(Extension->CurrentWriteRequest);
+ ASSERT(Stat->AmountInOutQueue >= Extension->WriteLength);
+
+ reqContext = SerialGetRequestContext(Extension->CurrentWriteRequest);
+ Stat->AmountInOutQueue -= reqContext->Length - (Extension->WriteLength);
+
+ }
+
+ Stat->WaitForImmediate = Extension->TransmitImmediate;
+
+ Stat->HoldReasons = 0;
+ if (Extension->TXHolding) {
+
+ if (Extension->TXHolding & SERIAL_TX_CTS) {
+
+ Stat->HoldReasons |= SERIAL_TX_WAITING_FOR_CTS;
+
+ }
+
+ if (Extension->TXHolding & SERIAL_TX_DSR) {
+
+ Stat->HoldReasons |= SERIAL_TX_WAITING_FOR_DSR;
+
+ }
+
+ if (Extension->TXHolding & SERIAL_TX_DCD) {
+
+ Stat->HoldReasons |= SERIAL_TX_WAITING_FOR_DCD;
+
+ }
+
+ if (Extension->TXHolding & SERIAL_TX_XOFF) {
+
+ Stat->HoldReasons |= SERIAL_TX_WAITING_FOR_XON;
+
+ }
+
+ if (Extension->TXHolding & SERIAL_TX_BREAK) {
+
+ Stat->HoldReasons |= SERIAL_TX_WAITING_ON_BREAK;
+
+ }
+
+ }
+
+ if (Extension->RXHolding & SERIAL_RX_DSR) {
+
+ Stat->HoldReasons |= SERIAL_RX_WAITING_FOR_DSR;
+
+ }
+
+ if (Extension->RXHolding & SERIAL_RX_XOFF) {
+
+ Stat->HoldReasons |= SERIAL_TX_WAITING_XOFF_SENT;
+
+ }
+
+ return FALSE;
+}
+
+
+BOOLEAN
+SerialSetEscapeChar(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This is used to set the character that will be used to escape
+ line status and modem status information when the application
+ has set up that line status and modem status should be passed
+ back in the data stream.
+
+Arguments:
+
+ Context - Pointer to the request that is specify the escape character.
+ Implicitly - An escape character of 0 means no escaping
+ will occur.
+
+Return Value:
+
+ This routine always returns FALSE.
+
+--*/
+
+{
+
+ PREQUEST_CONTEXT reqContext = (PREQUEST_CONTEXT)Context;
+ PSERIAL_DEVICE_EXTENSION extension = SerialGetDeviceExtension(WdfInterruptGetDevice(Interrupt));
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ extension->EscapeChar = *(PUCHAR)reqContext->SystemBuffer;
+
+ return FALSE;
+}
+
+VOID
+SerialEvtIoDeviceControl(
+ IN WDFQUEUE Queue,
+ IN WDFREQUEST Request,
+ IN size_t OutputBufferLength,
+ IN size_t InputBufferLength,
+ IN ULONG IoControlCode
+ )
+
+/*++
+
+Routine Description:
+
+ This routine provides the initial processing for all of the
+ Ioctrls for the serial device.
+
+Arguments:
+
+ Request - Pointer to the WDFREQUEST for the current request
+
+Return Value:
+
+ The function value is the final status of the call
+
+--*/
+
+{
+ //
+ // The status that gets returned to the caller and
+ // set in the Request.
+ //
+ NTSTATUS Status;
+
+ //
+ // Just what it says. This is the serial specific device
+ // extension of the device object create for the serial driver.
+ //
+ PSERIAL_DEVICE_EXTENSION Extension = NULL;
+
+ PVOID buffer;
+ PREQUEST_CONTEXT reqContext;
+ size_t bufSize;
+
+ UNREFERENCED_PARAMETER(OutputBufferLength);
+ UNREFERENCED_PARAMETER(InputBufferLength);
+
+ reqContext = SerialGetRequestContext(Request);
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_IOCTLS, "%s for: %p\n",
+ SerialGetIoctlName(IoControlCode), Request);
+
+ Extension = SerialGetDeviceExtension(WdfIoQueueGetDevice(Queue));
+
+ //
+ // We expect to be open so all our pages are locked down. This is, after
+ // all, an IO operation, so the device should be open first.
+ //
+
+ if (Extension->DeviceIsOpened != TRUE) {
+ SerialCompleteRequest(Request, STATUS_INVALID_DEVICE_REQUEST, 0);
+ return;
+ }
+
+
+ if (SerialCompleteIfError(Extension, Request) != STATUS_SUCCESS) {
+
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS,
+ "<SerialEvtIoDeviceControl (2) %d\n", STATUS_CANCELLED);
+ return;
+
+ }
+
+ reqContext = SerialGetRequestContext(Request);
+ reqContext->Information = 0;
+ reqContext->Status = STATUS_SUCCESS;
+ reqContext->MajorFunction = IRP_MJ_DEVICE_CONTROL;
+
+
+ Status = STATUS_SUCCESS;
+
+ switch (IoControlCode) {
+
+ case IOCTL_SERIAL_SET_BAUD_RATE : {
+
+ ULONG BaudRate;
+ //
+ // Will hold the value of the appropriate divisor for
+ // the requested baud rate. If the baudrate is invalid
+ // (because the device won't support that baud rate) then
+ // this value is undefined.
+ //
+ // Note: in one sense the concept of a valid baud rate
+ // is cloudy. We could allow the user to request any
+ // baud rate. We could then calculate the divisor needed
+ // for that baud rate. As long as the divisor wasn't less
+ // than one we would be "ok". (The percentage difference
+ // between the "true" divisor and the "rounded" value given
+ // to the hardware might make it unusable, but... ) It would
+ // really be up to the user to "Know" whether the baud rate
+ // is suitable. So much for theory, *We* only support a given
+ // set of baud rates.
+ //
+ SHORT AppropriateDivisor;
+
+ Status = WdfRequestRetrieveInputBuffer (Request, sizeof(SERIAL_BAUD_RATE), &buffer, &bufSize );
+ if( !NT_SUCCESS(Status) ) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
+ break;
+ }
+
+ BaudRate = ((PSERIAL_BAUD_RATE)(buffer))->BaudRate;
+
+
+ //
+ // Get the baud rate from the request. We pass it
+ // to a routine which will set the correct divisor.
+ //
+
+ Status = SerialGetDivisorFromBaud(
+ Extension->ClockRate,
+ BaudRate,
+ &AppropriateDivisor
+ );
+
+
+ if (NT_SUCCESS(Status)) {
+
+ SERIAL_IOCTL_SYNC S;
+
+
+ Extension->CurrentBaud = BaudRate;
+ Extension->WmiCommData.BaudRate = BaudRate;
+
+ S.Extension = Extension;
+ S.Data = (PVOID) (ULONG_PTR) AppropriateDivisor;
+ WdfInterruptSynchronize(
+ Extension->WdfInterrupt,
+ SerialSetBaud,
+ &S
+ );
+
+ }
+
+ break;
+ }
+
+ case IOCTL_SERIAL_GET_BAUD_RATE: {
+
+ PSERIAL_BAUD_RATE Br;
+
+ Status = WdfRequestRetrieveOutputBuffer ( Request, sizeof(SERIAL_BAUD_RATE), &buffer, &bufSize );
+ if( !NT_SUCCESS(Status) ) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
+ break;
+ }
+
+ Br = (PSERIAL_BAUD_RATE)buffer;
+
+ Br->BaudRate = Extension->CurrentBaud;
+
+ reqContext->Information = sizeof(SERIAL_BAUD_RATE);
+
+ break;
+
+ }
+
+ case IOCTL_SERIAL_GET_MODEM_CONTROL: {
+ SERIAL_IOCTL_SYNC S;
+
+ Status = WdfRequestRetrieveOutputBuffer ( Request, sizeof(ULONG), &buffer, &bufSize );
+ if( !NT_SUCCESS(Status) ) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
+ break;
+ }
+
+ reqContext->Information = sizeof(ULONG);
+
+ S.Extension = Extension;
+ S.Data = buffer;
+
+ WdfInterruptSynchronize(
+ Extension->WdfInterrupt,
+ SerialGetMCRContents,
+ &S
+ );
+
+ break;
+ }
+ case IOCTL_SERIAL_SET_MODEM_CONTROL: {
+ SERIAL_IOCTL_SYNC S;
+
+ Status = WdfRequestRetrieveInputBuffer (Request, sizeof(ULONG), &buffer, &bufSize );
+ if( !NT_SUCCESS(Status) ) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
+ break;
+ }
+
+ S.Extension = Extension;
+ S.Data = buffer;
+
+
+ WdfInterruptSynchronize(
+ Extension->WdfInterrupt,
+ SerialSetMCRContents,
+ &S
+ );
+
+ break;
+ }
+ case IOCTL_SERIAL_SET_FIFO_CONTROL: {
+ SERIAL_IOCTL_SYNC S;
+
+ Status = WdfRequestRetrieveInputBuffer (Request, sizeof(ULONG), &buffer, &bufSize );
+ if( !NT_SUCCESS(Status) ) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
+ break;
+ }
+
+ S.Extension = Extension;
+ S.Data = buffer;
+
+
+ WdfInterruptSynchronize(
+ Extension->WdfInterrupt,
+ SerialSetFCRContents,
+ &S
+ );
+
+ break;
+ }
+ case IOCTL_SERIAL_SET_LINE_CONTROL: {
+
+ PSERIAL_LINE_CONTROL Lc;
+ UCHAR LData;
+ UCHAR LStop;
+ UCHAR LParity;
+ UCHAR Mask = 0xff;
+
+ Status = WdfRequestRetrieveInputBuffer (Request, sizeof(SERIAL_LINE_CONTROL), &buffer, &bufSize );
+ if( !NT_SUCCESS(Status) ) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
+ break;
+ }
+
+ //
+ // Points to the line control record in the Request.
+ //
+ Lc = (PSERIAL_LINE_CONTROL)buffer;
+
+ switch (Lc->WordLength) {
+ case 5: {
+
+ LData = SERIAL_5_DATA;
+ Mask = 0x1f;
+ break;
+
+ }
+ case 6: {
+
+ LData = SERIAL_6_DATA;
+ Mask = 0x3f;
+ break;
+
+ }
+ case 7: {
+
+ LData = SERIAL_7_DATA;
+ Mask = 0x7f;
+ break;
+
+ }
+ case 8: {
+
+ LData = SERIAL_8_DATA;
+ break;
+
+ }
+ default: {
+
+ Status = STATUS_INVALID_PARAMETER;
+ goto DoneWithIoctl;
+
+ }
+
+ }
+
+ Extension->WmiCommData.BitsPerByte = Lc->WordLength;
+
+ switch (Lc->Parity) {
+
+ case NO_PARITY: {
+ Extension->WmiCommData.Parity = SERIAL_WMI_PARITY_NONE;
+ LParity = SERIAL_NONE_PARITY;
+ break;
+
+ }
+ case EVEN_PARITY: {
+ Extension->WmiCommData.Parity = SERIAL_WMI_PARITY_EVEN;
+ LParity = SERIAL_EVEN_PARITY;
+ break;
+
+ }
+ case ODD_PARITY: {
+ Extension->WmiCommData.Parity = SERIAL_WMI_PARITY_ODD;
+ LParity = SERIAL_ODD_PARITY;
+ break;
+
+ }
+ case SPACE_PARITY: {
+ Extension->WmiCommData.Parity = SERIAL_WMI_PARITY_SPACE;
+ LParity = SERIAL_SPACE_PARITY;
+ break;
+
+ }
+ case MARK_PARITY: {
+ Extension->WmiCommData.Parity = SERIAL_WMI_PARITY_MARK;
+ LParity = SERIAL_MARK_PARITY;
+ break;
+
+ }
+ default: {
+
+ Status = STATUS_INVALID_PARAMETER;
+ goto DoneWithIoctl;
+ break;
+ }
+
+ }
+
+ switch (Lc->StopBits) {
+
+ case STOP_BIT_1: {
+ Extension->WmiCommData.StopBits = SERIAL_WMI_STOP_1;
+ LStop = SERIAL_1_STOP;
+ break;
+ }
+ case STOP_BITS_1_5: {
+
+ if (LData != SERIAL_5_DATA) {
+
+ Status = STATUS_INVALID_PARAMETER;
+ goto DoneWithIoctl;
+ }
+ Extension->WmiCommData.StopBits = SERIAL_WMI_STOP_1_5;
+ LStop = SERIAL_1_5_STOP;
+ break;
+
+ }
+ case STOP_BITS_2: {
+
+ if (LData == SERIAL_5_DATA) {
+
+ Status = STATUS_INVALID_PARAMETER;
+ goto DoneWithIoctl;
+ }
+ Extension->WmiCommData.StopBits = SERIAL_WMI_STOP_2;
+ LStop = SERIAL_2_STOP;
+ break;
+
+ }
+ default: {
+
+ Status = STATUS_INVALID_PARAMETER;
+ goto DoneWithIoctl;
+ }
+
+ }
+
+ Extension->LineControl =
+ (UCHAR)((Extension->LineControl & SERIAL_LCR_BREAK) |
+ (LData | LParity | LStop));
+ Extension->ValidDataMask = Mask;
+
+ WdfInterruptSynchronize(
+ Extension->WdfInterrupt,
+ SerialSetLineControl,
+ Extension
+ );
+
+ break;
+ }
+ case IOCTL_SERIAL_GET_LINE_CONTROL: {
+
+ PSERIAL_LINE_CONTROL Lc;
+
+ Status = WdfRequestRetrieveOutputBuffer ( Request, sizeof(SERIAL_LINE_CONTROL), &buffer, &bufSize );
+ if( !NT_SUCCESS(Status) ) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
+ break;
+ }
+
+ Lc = (PSERIAL_LINE_CONTROL)buffer;
+
+ RtlZeroMemory(buffer, OutputBufferLength);
+
+ if ((Extension->LineControl & SERIAL_DATA_MASK) == SERIAL_5_DATA) {
+ Lc->WordLength = 5;
+ } else if ((Extension->LineControl & SERIAL_DATA_MASK)
+ == SERIAL_6_DATA) {
+ Lc->WordLength = 6;
+ } else if ((Extension->LineControl & SERIAL_DATA_MASK)
+ == SERIAL_7_DATA) {
+ Lc->WordLength = 7;
+ } else if ((Extension->LineControl & SERIAL_DATA_MASK)
+ == SERIAL_8_DATA) {
+ Lc->WordLength = 8;
+ }
+
+ if ((Extension->LineControl & SERIAL_PARITY_MASK)
+ == SERIAL_NONE_PARITY) {
+ Lc->Parity = NO_PARITY;
+ } else if ((Extension->LineControl & SERIAL_PARITY_MASK)
+ == SERIAL_ODD_PARITY) {
+ Lc->Parity = ODD_PARITY;
+ } else if ((Extension->LineControl & SERIAL_PARITY_MASK)
+ == SERIAL_EVEN_PARITY) {
+ Lc->Parity = EVEN_PARITY;
+ } else if ((Extension->LineControl & SERIAL_PARITY_MASK)
+ == SERIAL_MARK_PARITY) {
+ Lc->Parity = MARK_PARITY;
+ } else if ((Extension->LineControl & SERIAL_PARITY_MASK)
+ == SERIAL_SPACE_PARITY) {
+ Lc->Parity = SPACE_PARITY;
+ }
+
+ if (Extension->LineControl & SERIAL_2_STOP) {
+ if (Lc->WordLength == 5) {
+ Lc->StopBits = STOP_BITS_1_5;
+ } else {
+ Lc->StopBits = STOP_BITS_2;
+ }
+ } else {
+ Lc->StopBits = STOP_BIT_1;
+ }
+
+ reqContext->Information = sizeof(SERIAL_LINE_CONTROL);
+
+ break;
+ }
+ case IOCTL_SERIAL_SET_TIMEOUTS: {
+
+ PSERIAL_TIMEOUTS NewTimeouts;
+
+ Status = WdfRequestRetrieveInputBuffer ( Request, sizeof(SERIAL_TIMEOUTS), &buffer, &bufSize );
+ if( !NT_SUCCESS(Status) ) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
+ break;
+ }
+
+ NewTimeouts =(PSERIAL_TIMEOUTS)buffer;
+
+ if ((NewTimeouts->ReadIntervalTimeout == MAXULONG) &&
+ (NewTimeouts->ReadTotalTimeoutMultiplier == MAXULONG) &&
+ (NewTimeouts->ReadTotalTimeoutConstant == MAXULONG)) {
+
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+
+ }
+
+
+ Extension->Timeouts.ReadIntervalTimeout =
+ NewTimeouts->ReadIntervalTimeout;
+
+ Extension->Timeouts.ReadTotalTimeoutMultiplier =
+ NewTimeouts->ReadTotalTimeoutMultiplier;
+
+ Extension->Timeouts.ReadTotalTimeoutConstant =
+ NewTimeouts->ReadTotalTimeoutConstant;
+
+ Extension->Timeouts.WriteTotalTimeoutMultiplier =
+ NewTimeouts->WriteTotalTimeoutMultiplier;
+
+ Extension->Timeouts.WriteTotalTimeoutConstant =
+ NewTimeouts->WriteTotalTimeoutConstant;
+
+ break;
+ }
+ case IOCTL_SERIAL_GET_TIMEOUTS: {
+
+ Status = WdfRequestRetrieveOutputBuffer ( Request, sizeof(SERIAL_TIMEOUTS), &buffer, &bufSize );
+ if( !NT_SUCCESS(Status) ) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
+ break;
+ }
+
+ *((PSERIAL_TIMEOUTS)buffer) = Extension->Timeouts;
+ reqContext->Information = sizeof(SERIAL_TIMEOUTS);
+
+ break;
+ }
+ case IOCTL_SERIAL_SET_CHARS: {
+
+ SERIAL_IOCTL_SYNC S;
+ PSERIAL_CHARS NewChars;
+
+ Status = WdfRequestRetrieveInputBuffer ( Request, sizeof(SERIAL_CHARS), &buffer, &bufSize );
+ if( !NT_SUCCESS(Status) ) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
+ break;
+ }
+
+ NewChars = (PSERIAL_CHARS)buffer;
+
+ //
+ // The only thing that can be wrong with the chars
+ // is that the xon and xoff characters are the
+ // same.
+ //
+#if 0
+ if (NewChars->XonChar == NewChars->XoffChar) {
+
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+
+ }
+#endif
+
+ //
+ // We acquire the control lock so that only
+ // one request can GET or SET the characters
+ // at a time. The sets could be synchronized
+ // by the interrupt spinlock, but that wouldn't
+ // prevent multiple gets at the same time.
+ //
+
+ S.Extension = Extension;
+ S.Data = NewChars;
+
+ //
+ // Under the protection of the lock, make sure that
+ // the xon and xoff characters aren't the same as
+ // the escape character.
+ //
+
+ if (Extension->EscapeChar) {
+
+ if ((Extension->EscapeChar == NewChars->XonChar) ||
+ (Extension->EscapeChar == NewChars->XoffChar)) {
+
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+
+ }
+
+ }
+
+ Extension->WmiCommData.XonCharacter = NewChars->XonChar;
+ Extension->WmiCommData.XoffCharacter = NewChars->XoffChar;
+
+ WdfInterruptSynchronize(
+ Extension->WdfInterrupt,
+ SerialSetChars,
+ &S
+ );
+
+
+ break;
+
+ }
+ case IOCTL_SERIAL_GET_CHARS: {
+
+ Status = WdfRequestRetrieveOutputBuffer ( Request, sizeof(SERIAL_CHARS), &buffer, &bufSize );
+ if( !NT_SUCCESS(Status) ) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
+ break;
+ }
+
+ *((PSERIAL_CHARS)buffer) = Extension->SpecialChars;
+ reqContext->Information = sizeof(SERIAL_CHARS);
+
+
+ break;
+ }
+ case IOCTL_SERIAL_SET_DTR:
+ case IOCTL_SERIAL_CLR_DTR: {
+
+
+ //
+ // We acquire the lock so that we can check whether
+ // automatic dtr flow control is enabled. If it is
+ // then we return an error since the app is not allowed
+ // to touch this if it is automatic.
+ //
+
+ if ((Extension->HandFlow.ControlHandShake & SERIAL_DTR_MASK)
+ == SERIAL_DTR_HANDSHAKE) {
+
+ Status = STATUS_INVALID_PARAMETER;
+
+ } else {
+
+ WdfInterruptSynchronize(
+ Extension->WdfInterrupt,
+ ((IoControlCode ==
+ IOCTL_SERIAL_SET_DTR)?
+ (SerialSetDTR):(SerialClrDTR)),
+ Extension
+ );
+
+ }
+
+ break;
+ }
+ case IOCTL_SERIAL_RESET_DEVICE: {
+
+ break;
+ }
+ case IOCTL_SERIAL_SET_RTS:
+ case IOCTL_SERIAL_CLR_RTS: {
+
+ //
+ // We acquire the lock so that we can check whether
+ // automatic rts flow control or transmit toggleing
+ // is enabled. If it is then we return an error since
+ // the app is not allowed to touch this if it is automatic
+ // or toggling.
+ //
+
+ if (((Extension->HandFlow.FlowReplace & SERIAL_RTS_MASK)
+ == SERIAL_RTS_HANDSHAKE) ||
+ ((Extension->HandFlow.FlowReplace & SERIAL_RTS_MASK)
+ == SERIAL_TRANSMIT_TOGGLE)) {
+
+ Status = STATUS_INVALID_PARAMETER;
+
+ } else {
+
+ WdfInterruptSynchronize(
+ Extension->WdfInterrupt,
+ ((IoControlCode ==
+ IOCTL_SERIAL_SET_RTS)?
+ (SerialSetRTS):(SerialClrRTS)),
+ Extension
+ );
+
+ }
+
+ break;
+
+ }
+ case IOCTL_SERIAL_SET_XOFF: {
+
+ WdfInterruptSynchronize(
+ Extension->WdfInterrupt,
+ SerialPretendXoff,
+ Extension
+ );
+
+ break;
+
+ }
+ case IOCTL_SERIAL_SET_XON: {
+
+ WdfInterruptSynchronize(
+ Extension->WdfInterrupt,
+ SerialPretendXon,
+ Extension
+ );
+
+ break;
+
+ }
+ case IOCTL_SERIAL_SET_BREAK_ON: {
+
+ WdfInterruptSynchronize(
+ Extension->WdfInterrupt,
+ SerialTurnOnBreak,
+ Extension
+ );
+
+ break;
+ }
+ case IOCTL_SERIAL_SET_BREAK_OFF: {
+
+ WdfInterruptSynchronize(
+ Extension->WdfInterrupt,
+ SerialTurnOffBreak,
+ Extension
+ );
+
+ break;
+ }
+ case IOCTL_SERIAL_SET_QUEUE_SIZE: {
+
+ //
+ // Type ahead buffer is fixed, so we just validate
+ // the the users request is not bigger that our
+ // own internal buffer size.
+ //
+
+ PSERIAL_QUEUE_SIZE Rs;
+
+ Status = WdfRequestRetrieveInputBuffer ( Request, sizeof(SERIAL_QUEUE_SIZE), &buffer, &bufSize );
+ if( !NT_SUCCESS(Status) ) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
+ break;
+ }
+
+ ASSERT(Extension->InterruptReadBuffer);
+
+ Rs = (PSERIAL_QUEUE_SIZE)buffer;
+
+ reqContext->SystemBuffer = buffer;
+
+ //
+ // We have to allocate the memory for the new
+ // buffer while we're still in the context of the
+ // caller. We don't even try to protect this
+ // with a lock because the value could be stale
+ // as soon as we release the lock - The only time
+ // we will know for sure is when we actually try
+ // to do the resize.
+ //
+
+ if (Rs->InSize <= Extension->BufferSize) {
+
+ Status = STATUS_SUCCESS;
+ break;
+
+ }
+
+ reqContext->Type3InputBuffer =
+ ExAllocatePoolWithQuotaTag(
+ NonPagedPoolNx | POOL_QUOTA_FAIL_INSTEAD_OF_RAISE,
+ Rs->InSize,
+ POOL_TAG
+ );
+
+ if (!reqContext->Type3InputBuffer) {
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ break;
+
+ }
+
+ //
+ // Well the data passed was big enough. Do the request.
+ //
+ // There are two reason we place it in the read queue:
+ //
+ // 1) We want to serialize these resize requests so that
+ // they don't contend with each other.
+ //
+ // 2) We want to serialize these requests with reads since
+ // we don't want reads and resizes contending over the
+ // read buffer.
+ //
+
+
+ SerialStartOrQueue(
+ Extension,
+ Request,
+ Extension->ReadQueue,
+ &Extension->CurrentReadRequest,
+ SerialStartRead
+ );
+
+ return;
+ }
+ case IOCTL_SERIAL_GET_WAIT_MASK: {
+
+ Status = WdfRequestRetrieveOutputBuffer ( Request, sizeof(ULONG), &buffer, &bufSize );
+ if( !NT_SUCCESS(Status) ) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
+ break;
+ }
+
+ //
+ // Simple scalar read. No reason to acquire a lock.
+ //
+
+ reqContext->Information = sizeof(ULONG);
+
+ *((ULONG *)buffer) = Extension->IsrWaitMask;
+
+ break;
+
+ }
+ case IOCTL_SERIAL_SET_WAIT_MASK: {
+
+ ULONG NewMask;
+
+ SerialDbgPrintEx(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "In Ioctl processing for set mask\n");
+
+ Status = WdfRequestRetrieveInputBuffer ( Request, sizeof(ULONG), &buffer, &bufSize );
+ if( !NT_SUCCESS(Status) ) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
+ break;
+ }
+
+ NewMask = *((ULONG *)buffer);
+ reqContext->SystemBuffer = buffer;
+
+ //
+ // Make sure that the mask only contains valid
+ // waitable events.
+ //
+
+ if (NewMask & ~(SERIAL_EV_RXCHAR |
+ SERIAL_EV_RXFLAG |
+ SERIAL_EV_TXEMPTY |
+ SERIAL_EV_CTS |
+ SERIAL_EV_DSR |
+ SERIAL_EV_RLSD |
+ SERIAL_EV_BREAK |
+ SERIAL_EV_ERR |
+ SERIAL_EV_RING |
+ SERIAL_EV_PERR |
+ SERIAL_EV_RX80FULL |
+ SERIAL_EV_EVENT1 |
+ SERIAL_EV_EVENT2)) {
+
+ SerialDbgPrintEx(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "Unknown mask %x\n", NewMask);
+
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+
+ }
+
+ //
+ // Either start this request or put it on the
+ // queue.
+ //
+
+ SerialDbgPrintEx(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "Starting or queuing set mask request %p"
+ "\n", Request);
+
+ SerialStartOrQueue(Extension, Request, Extension->MaskQueue,
+ &Extension->CurrentMaskRequest,
+ SerialStartMask);
+ return;
+
+ }
+ case IOCTL_SERIAL_WAIT_ON_MASK: {
+
+ SerialDbgPrintEx(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "In Ioctl processing for wait mask\n");
+
+ Status = WdfRequestRetrieveOutputBuffer ( Request, sizeof(ULONG), &buffer, &bufSize );
+ if( !NT_SUCCESS(Status) ) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
+ break;
+ }
+
+ reqContext->SystemBuffer = buffer;
+
+ //
+ // Either start this request or put it on the
+ // queue.
+ //
+
+ SerialDbgPrintEx(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "Starting or queuing wait mask request"
+ "%p\n", Request);
+
+ SerialStartOrQueue(
+ Extension,
+ Request,
+ Extension->MaskQueue,
+ &Extension->CurrentMaskRequest,
+ SerialStartMask
+ );
+ return;
+ }
+ case IOCTL_SERIAL_IMMEDIATE_CHAR: {
+
+ Status = WdfRequestRetrieveInputBuffer ( Request, sizeof(UCHAR), &buffer, &bufSize );
+ if( !NT_SUCCESS(Status) ) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
+ break;
+ }
+
+ reqContext->SystemBuffer = buffer;
+
+ if (Extension->CurrentImmediateRequest) {
+
+ Status = STATUS_INVALID_PARAMETER;
+
+ } else {
+
+ //
+ // We can queue the char. We need to set
+ // a cancel routine because flow control could
+ // keep the char from transmitting. Make sure
+ // that the request hasn't already been canceled.
+ //
+
+ Extension->CurrentImmediateRequest = Request;
+ Extension->TotalCharsQueued++;
+ SerialStartImmediate(Extension);
+ return;
+
+ }
+
+ break;
+
+ }
+ case IOCTL_SERIAL_PURGE: {
+
+ ULONG Mask;
+
+ Status = WdfRequestRetrieveInputBuffer ( Request, sizeof(ULONG), &buffer, &bufSize );
+ if( !NT_SUCCESS(Status) ) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
+ break;
+ }
+
+ //
+ // Check to make sure that the mask only has
+ // 0 or the other appropriate values.
+ //
+
+ Mask = *((ULONG *)(buffer));
+
+ if ((!Mask) || (Mask & (~(SERIAL_PURGE_TXABORT |
+ SERIAL_PURGE_RXABORT |
+ SERIAL_PURGE_TXCLEAR |
+ SERIAL_PURGE_RXCLEAR
+ )
+ )
+ )) {
+
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+
+ }
+
+ reqContext->SystemBuffer = buffer;
+
+ //
+ // Either start this request or put it on the
+ // queue.
+ //
+
+ SerialStartOrQueue(
+ Extension,
+ Request,
+ Extension->PurgeQueue,
+ &Extension->CurrentPurgeRequest,
+ SerialStartPurge
+ );
+ return;
+ }
+ case IOCTL_SERIAL_GET_HANDFLOW: {
+
+ Status = WdfRequestRetrieveOutputBuffer ( Request, sizeof(SERIAL_HANDFLOW), &buffer, &bufSize );
+ if( !NT_SUCCESS(Status) ) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
+ break;
+ }
+
+ reqContext->Information = sizeof(SERIAL_HANDFLOW);
+
+ *((PSERIAL_HANDFLOW)buffer) = Extension->HandFlow;
+
+ break;
+
+ }
+ case IOCTL_SERIAL_SET_HANDFLOW: {
+
+ SERIAL_IOCTL_SYNC S;
+ PSERIAL_HANDFLOW HandFlow;
+
+ //
+ // Make sure that the hand shake and control is the
+ // right size.
+ //
+
+ Status = WdfRequestRetrieveInputBuffer ( Request, sizeof(SERIAL_HANDFLOW), &buffer, &bufSize );
+ if( !NT_SUCCESS(Status) ) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
+ break;
+ }
+
+ HandFlow = (PSERIAL_HANDFLOW)buffer;
+
+ //
+ // Make sure that there are no invalid bits set in
+ // the control and handshake.
+ //
+
+ if (HandFlow->ControlHandShake & SERIAL_CONTROL_INVALID) {
+
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+
+ }
+
+ if (HandFlow->FlowReplace & SERIAL_FLOW_INVALID) {
+
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+
+ }
+
+ //
+ // Make sure that the app hasn't set an invlid DTR mode.
+ //
+
+ if ((HandFlow->ControlHandShake & SERIAL_DTR_MASK) ==
+ SERIAL_DTR_MASK) {
+
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+
+ }
+
+ //
+ // Make sure that haven't set totally invalid xon/xoff
+ // limits.
+ //
+
+ if ((HandFlow->XonLimit < 0) ||
+ ((ULONG)HandFlow->XonLimit > Extension->BufferSize)) {
+
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+
+ }
+
+ if ((HandFlow->XoffLimit < 0) ||
+ ((ULONG)HandFlow->XoffLimit > Extension->BufferSize)) {
+
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+
+ }
+
+ S.Extension = Extension;
+ S.Data = HandFlow;
+
+ //
+ // Under the protection of the lock, make sure that
+ // we aren't turning on error replacement when we
+ // are doing line status/modem status insertion.
+ //
+
+ if (Extension->EscapeChar) {
+
+ if (HandFlow->FlowReplace & SERIAL_ERROR_CHAR) {
+
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+
+ }
+
+ }
+
+ WdfInterruptSynchronize(
+ Extension->WdfInterrupt,
+ SerialSetHandFlow,
+ &S
+ );
+
+ break;
+
+ }
+ case IOCTL_SERIAL_GET_MODEMSTATUS: {
+
+ SERIAL_IOCTL_SYNC S;
+
+ Status = WdfRequestRetrieveOutputBuffer ( Request, sizeof(ULONG), &buffer, &bufSize );
+ if( !NT_SUCCESS(Status) ) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
+ break;
+ }
+
+ reqContext->Information = sizeof(ULONG);
+
+ S.Extension = Extension;
+ S.Data = buffer;
+
+ WdfInterruptSynchronize(
+ Extension->WdfInterrupt,
+ SerialGetModemUpdate,
+ &S
+ );
+
+ break;
+
+ }
+ case IOCTL_SERIAL_GET_DTRRTS: {
+
+ ULONG ModemControl;
+
+ Status = WdfRequestRetrieveOutputBuffer ( Request, sizeof(ULONG), &buffer, &bufSize );
+ if( !NT_SUCCESS(Status) ) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
+ break;
+ }
+
+ reqContext->Information = sizeof(ULONG);
+ reqContext->Status = STATUS_SUCCESS;
+
+ //
+ // Reading this hardware has no effect on the device.
+ //
+
+ ModemControl = READ_MODEM_CONTROL(Extension, Extension->Controller);
+
+ ModemControl &= SERIAL_DTR_STATE | SERIAL_RTS_STATE;
+
+ *(PULONG)buffer = ModemControl;
+
+ break;
+
+ }
+ case IOCTL_SERIAL_GET_COMMSTATUS: {
+
+ SERIAL_IOCTL_SYNC S;
+
+ Status = WdfRequestRetrieveOutputBuffer ( Request, sizeof(SERIAL_STATUS), &buffer, &bufSize );
+ if( !NT_SUCCESS(Status) ) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
+ break;
+ }
+
+ reqContext->Information = sizeof(SERIAL_STATUS);
+
+ S.Extension = Extension;
+ S.Data = buffer;
+
+ //
+ // Acquire the cancel spin lock so nothing much
+ // changes while were getting the state.
+ //
+
+ //IoAcquireCancelSpinLock(&OldIrql);
+
+ WdfInterruptSynchronize(
+ Extension->WdfInterrupt,
+ SerialGetCommStatus,
+ &S
+ );
+
+ //IoReleaseCancelSpinLock(OldIrql);
+
+ break;
+
+ }
+ case IOCTL_SERIAL_GET_PROPERTIES: {
+
+
+ Status = WdfRequestRetrieveOutputBuffer ( Request, sizeof(SERIAL_COMMPROP), &buffer, &bufSize );
+ if( !NT_SUCCESS(Status) ) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
+ break;
+ }
+
+ //
+ // No synchronization is required since this information
+ // is "static".
+ //
+
+ SerialGetProperties(
+ Extension,
+ buffer
+ );
+
+ reqContext->Information = sizeof(SERIAL_COMMPROP);
+ reqContext->Status = STATUS_SUCCESS;
+
+ break;
+ }
+ case IOCTL_SERIAL_XOFF_COUNTER: {
+
+ PSERIAL_XOFF_COUNTER Xc;
+
+ Status = WdfRequestRetrieveInputBuffer ( Request, sizeof(SERIAL_XOFF_COUNTER), &buffer, &bufSize );
+ if( !NT_SUCCESS(Status) ) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
+ break;
+ }
+
+ Xc = (PSERIAL_XOFF_COUNTER)buffer;
+
+ if (Xc->Counter <= 0) {
+
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+
+ }
+ reqContext->SystemBuffer = buffer;
+
+ //
+ // There is no output, so make that clear now
+ //
+
+ reqContext->Information = 0;
+
+ //
+ // So far so good. Put the request onto the write queue.
+ //
+
+ SerialStartOrQueue(
+ Extension,
+ Request,
+ Extension->WriteQueue,
+ &Extension->CurrentWriteRequest,
+ SerialStartWrite
+ );
+ return;
+
+ }
+ case IOCTL_SERIAL_LSRMST_INSERT: {
+
+ PUCHAR escapeChar;
+ SERIAL_IOCTL_SYNC S;
+
+ //
+ // Make sure we get a byte.
+ //
+ Status = WdfRequestRetrieveInputBuffer ( Request, sizeof(UCHAR), &buffer, &bufSize );
+ if( !NT_SUCCESS(Status) ) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
+ break;
+ }
+
+ reqContext->SystemBuffer = buffer;
+
+ escapeChar = (PUCHAR)buffer;
+
+ if (*escapeChar) {
+
+ //
+ // We've got some escape work to do. We will make sure that
+ // the character is not the same as the Xon or Xoff character,
+ // or that we are already doing error replacement.
+ //
+
+ if ((*escapeChar == Extension->SpecialChars.XoffChar) ||
+ (*escapeChar == Extension->SpecialChars.XonChar) ||
+ (Extension->HandFlow.FlowReplace & SERIAL_ERROR_CHAR)) {
+
+ Status = STATUS_INVALID_PARAMETER;
+
+ break;
+
+ }
+
+ }
+
+ S.Extension = Extension;
+ S.Data = buffer;
+
+ WdfInterruptSynchronize(
+ Extension->WdfInterrupt,
+ SerialSetEscapeChar,
+ reqContext
+ );
+
+ break;
+
+ }
+ case IOCTL_SERIAL_CONFIG_SIZE: {
+
+ Status = WdfRequestRetrieveOutputBuffer ( Request, sizeof(ULONG), &buffer, &bufSize );
+ if( !NT_SUCCESS(Status) ) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
+ break;
+ }
+
+ reqContext->Information = sizeof(ULONG);
+ reqContext->Status = STATUS_SUCCESS;
+
+ *(PULONG)buffer = 0;
+
+ break;
+ }
+ case IOCTL_SERIAL_GET_STATS: {
+
+ Status = WdfRequestRetrieveOutputBuffer ( Request, sizeof(SERIALPERF_STATS), &buffer, &bufSize );
+ if( !NT_SUCCESS(Status) ) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
+ break;
+ }
+
+ reqContext->SystemBuffer = buffer;
+
+ reqContext->Information = sizeof(SERIALPERF_STATS);
+ reqContext->Status = STATUS_SUCCESS;
+
+ WdfInterruptSynchronize(
+ Extension->WdfInterrupt,
+ SerialGetStats,
+ reqContext
+ );
+
+ break;
+ }
+ case IOCTL_SERIAL_CLEAR_STATS: {
+
+ WdfInterruptSynchronize(
+ Extension->WdfInterrupt,
+ SerialClearStats,
+ Extension
+ );
+ break;
+ }
+ default: {
+
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+ }
+
+DoneWithIoctl:;
+
+ reqContext->Status = Status;
+
+ SerialCompleteRequest(Request, Status, reqContext->Information);
+
+ return;
+
+}
+
+
+VOID
+SerialGetProperties(
+ IN PSERIAL_DEVICE_EXTENSION Extension,
+ IN PSERIAL_COMMPROP Properties
+ )
+
+/*++
+
+Routine Description:
+
+ This function returns the capabilities of this particular
+ serial device.
+
+Arguments:
+
+ Extension - The serial device extension.
+
+ Properties - The structure used to return the properties
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+
+ RtlZeroMemory(
+ Properties,
+ sizeof(SERIAL_COMMPROP)
+ );
+
+ Properties->PacketLength = sizeof(SERIAL_COMMPROP);
+ Properties->PacketVersion = 2;
+ Properties->ServiceMask = SERIAL_SP_SERIALCOMM;
+ Properties->MaxTxQueue = 0;
+ Properties->MaxRxQueue = 0;
+
+ Properties->MaxBaud = SERIAL_BAUD_USER;
+ Properties->SettableBaud = Extension->SupportedBauds;
+
+ Properties->ProvSubType = SERIAL_SP_RS232;
+ Properties->ProvCapabilities = SERIAL_PCF_DTRDSR |
+ SERIAL_PCF_RTSCTS |
+ SERIAL_PCF_CD |
+ SERIAL_PCF_PARITY_CHECK |
+ SERIAL_PCF_XONXOFF |
+ SERIAL_PCF_SETXCHAR |
+ SERIAL_PCF_TOTALTIMEOUTS |
+ SERIAL_PCF_INTTIMEOUTS;
+ Properties->SettableParams = SERIAL_SP_PARITY |
+ SERIAL_SP_BAUD |
+ SERIAL_SP_DATABITS |
+ SERIAL_SP_STOPBITS |
+ SERIAL_SP_HANDSHAKING |
+ SERIAL_SP_PARITY_CHECK |
+ SERIAL_SP_CARRIER_DETECT;
+
+
+ Properties->SettableData = SERIAL_DATABITS_5 |
+ SERIAL_DATABITS_6 |
+ SERIAL_DATABITS_7 |
+ SERIAL_DATABITS_8;
+ Properties->SettableStopParity = SERIAL_STOPBITS_10 |
+ SERIAL_STOPBITS_15 |
+ SERIAL_STOPBITS_20 |
+ SERIAL_PARITY_NONE |
+ SERIAL_PARITY_ODD |
+ SERIAL_PARITY_EVEN |
+ SERIAL_PARITY_MARK |
+ SERIAL_PARITY_SPACE;
+ Properties->CurrentTxQueue = 0;
+ Properties->CurrentRxQueue = Extension->BufferSize;
+
+}
+
+VOID
+SerialEvtIoInternalDeviceControl(
+ IN WDFQUEUE Queue,
+ IN WDFREQUEST Request,
+ IN size_t OutputBufferLength,
+ IN size_t InputBufferLength,
+ IN ULONG IoControlCode
+)
+/*++
+
+Routine Description:
+
+ This routine provides the initial processing for all of the
+ internal Ioctrls for the serial device.
+
+Arguments:
+
+ PDevObj - Pointer to the device object for this device
+
+ PIrp - Pointer to the WDFREQUEST for the current request
+
+Return Value:
+
+ The function value is the final status of the call
+
+--*/
+
+{
+ NTSTATUS status;
+ PSERIAL_DEVICE_EXTENSION pDevExt = NULL;
+ PVOID buffer;
+ PREQUEST_CONTEXT reqContext;
+ WDF_DEVICE_POWER_POLICY_WAKE_SETTINGS wakeSettings;
+ size_t bufSize;
+
+ UNREFERENCED_PARAMETER(OutputBufferLength);
+ UNREFERENCED_PARAMETER(InputBufferLength);
+
+ SerialDbgPrintEx(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "SerialEvtIoInternalDeviceControl for: %p\n", Request);
+
+ pDevExt = SerialGetDeviceExtension(WdfIoQueueGetDevice(Queue));
+
+ if (SerialCompleteIfError(pDevExt, Request) != STATUS_SUCCESS) {
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_IOCTLS,
+ "<SerialEvtIoDeviceControl (2) %d\n", STATUS_CANCELLED);
+ return;
+
+ }
+
+ reqContext = SerialGetRequestContext(Request);
+ reqContext->Information = 0;
+ reqContext->Status = STATUS_SUCCESS;
+ reqContext->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
+
+ switch (IoControlCode) {
+
+ case IOCTL_SERIAL_INTERNAL_DO_WAIT_WAKE:
+ //
+ // Init wait-wake policy structure.
+ //
+ WDF_DEVICE_POWER_POLICY_WAKE_SETTINGS_INIT(&wakeSettings);
+ //
+ // Override the default settings from allow user control to do not allow.
+ //
+ wakeSettings.UserControlOfWakeSettings = IdleDoNotAllowUserControl;
+ status = WdfDeviceAssignSxWakeSettings(pDevExt->WdfDevice, &wakeSettings);
+ if (!NT_SUCCESS(status)) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP, "WdfDeviceAssignSxWakeSettings failed %x \n", status);
+ break;
+ }
+
+ pDevExt->IsWakeEnabled = TRUE;
+ status = STATUS_SUCCESS;
+ break;
+
+ case IOCTL_SERIAL_INTERNAL_CANCEL_WAIT_WAKE:
+
+ WDF_DEVICE_POWER_POLICY_WAKE_SETTINGS_INIT(&wakeSettings);
+ //
+ // Override the default settings.
+ //
+ wakeSettings.Enabled = WdfFalse; // Disables wait-wake
+ wakeSettings.UserControlOfWakeSettings = IdleDoNotAllowUserControl;
+ status = WdfDeviceAssignSxWakeSettings(pDevExt->WdfDevice, &wakeSettings);
+ if (!NT_SUCCESS(status)) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP, "WdfDeviceAssignSxWakeSettings failed %x \n", status);
+ break;
+ }
+
+ pDevExt->IsWakeEnabled = FALSE;
+ status = STATUS_SUCCESS;
+ break;
+
+
+ //
+ // Put the serial port in a "filter-driver" appropriate state
+ //
+ // WARNING: This code assumes it is being called by a trusted kernel
+ // entity and no checking is done on the validity of the settings
+ // passed to IOCTL_SERIAL_INTERNAL_RESTORE_SETTINGS
+ //
+ // If validity checking is desired, the regular ioctl's should be used
+ //
+
+ case IOCTL_SERIAL_INTERNAL_BASIC_SETTINGS:
+ case IOCTL_SERIAL_INTERNAL_RESTORE_SETTINGS: {
+
+ SERIAL_BASIC_SETTINGS basic;
+ PSERIAL_BASIC_SETTINGS pBasic;
+ SERIAL_IOCTL_SYNC S;
+
+ if (IoControlCode == IOCTL_SERIAL_INTERNAL_BASIC_SETTINGS) {
+
+
+ //
+ // Check the buffer size
+ //
+ status = WdfRequestRetrieveOutputBuffer ( Request, sizeof(SERIAL_BASIC_SETTINGS), &buffer, &bufSize );
+ if( !NT_SUCCESS(status) ) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", status);
+ break;
+ }
+
+ reqContext->SystemBuffer = buffer;
+
+ //
+ // Everything is 0 -- timeouts and flow control and fifos. If
+ // We add additional features, this zero memory method
+ // may not work.
+ //
+
+ RtlZeroMemory(&basic, sizeof(SERIAL_BASIC_SETTINGS));
+
+ basic.TxFifo = 1;
+ basic.RxFifo = SERIAL_1_BYTE_HIGH_WATER;
+
+ reqContext->Information = sizeof(SERIAL_BASIC_SETTINGS);
+ pBasic = (PSERIAL_BASIC_SETTINGS)buffer;
+
+ //
+ // Save off the old settings
+ //
+
+ RtlCopyMemory(&pBasic->Timeouts, &pDevExt->Timeouts,
+ sizeof(SERIAL_TIMEOUTS));
+
+ RtlCopyMemory(&pBasic->HandFlow, &pDevExt->HandFlow,
+ sizeof(SERIAL_HANDFLOW));
+
+ pBasic->RxFifo = pDevExt->RxFifoTrigger;
+ pBasic->TxFifo = pDevExt->TxFifoAmount;
+
+ //
+ // Point to our new settings
+ //
+
+ pBasic = &basic;
+ } else { // restoring settings
+
+ status = WdfRequestRetrieveInputBuffer ( Request, sizeof(SERIAL_BASIC_SETTINGS), &buffer, &bufSize );
+ if( !NT_SUCCESS(status) ) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", status);
+ break;
+ }
+
+ pBasic = (PSERIAL_BASIC_SETTINGS)buffer;
+ }
+
+ //
+ // Set the timeouts
+ //
+
+ RtlCopyMemory(&pDevExt->Timeouts, &pBasic->Timeouts,
+ sizeof(SERIAL_TIMEOUTS));
+
+ //
+ // Set flowcontrol
+ //
+
+ S.Extension = pDevExt;
+ S.Data = &pBasic->HandFlow;
+ WdfInterruptSynchronize(pDevExt->WdfInterrupt, SerialSetHandFlow, &S);
+
+ if (pDevExt->FifoPresent) {
+ pDevExt->TxFifoAmount = pBasic->TxFifo;
+ pDevExt->RxFifoTrigger = (UCHAR)pBasic->RxFifo;
+
+ WRITE_FIFO_CONTROL(pDevExt, pDevExt->Controller, (UCHAR)0);
+ READ_RECEIVE_BUFFER(pDevExt, pDevExt->Controller);
+ WRITE_FIFO_CONTROL(pDevExt, pDevExt->Controller,
+ (UCHAR)(SERIAL_FCR_ENABLE | pDevExt->RxFifoTrigger
+ | SERIAL_FCR_RCVR_RESET
+ | SERIAL_FCR_TXMT_RESET));
+ } else {
+ pDevExt->TxFifoAmount = pDevExt->RxFifoTrigger = 0;
+ WRITE_FIFO_CONTROL(pDevExt, pDevExt->Controller, (UCHAR)0);
+ }
+
+
+ break;
+ }
+
+ default:
+ status = STATUS_INVALID_PARAMETER;
+ break;
+
+ }
+
+ reqContext->Status = status;
+
+ SerialCompleteRequest(Request, reqContext->Status, reqContext->Information);
+
+ return;
+}
+
+
+
diff --git a/tests/projects/wdk/kmdf/serial/isr.c b/tests/projects/wdk/kmdf/serial/isr.c
new file mode 100644
index 000000000..806164a61
--- /dev/null
+++ b/tests/projects/wdk/kmdf/serial/isr.c
@@ -0,0 +1,1517 @@
+/*++
+
+Copyright (c) Microsoft Corporation
+
+Module Name:
+
+ isr.c
+
+Abstract:
+
+ This module contains the interrupt service routine for the
+ serial driver.
+
+Environment:
+
+ Kernel mode
+
+--*/
+
+#include "precomp.h"
+
+#if defined(EVENT_TRACING)
+#include "isr.tmh"
+#endif
+
+
+NTSTATUS
+SerialEvtInterruptEnable(
+ IN WDFINTERRUPT Interrupt,
+ IN WDFDEVICE AssociatedDevice
+ )
+/*++
+
+Routine Description:
+
+ This event is called when the Framework moves the device to D0, and after
+ EvtDeviceD0Entry. The driver should enable 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 enabled.
+
+--*/
+{
+ UNREFERENCED_PARAMETER(Interrupt);
+ UNREFERENCED_PARAMETER(AssociatedDevice);
+
+ SerialDbgPrintEx(TRACE_LEVEL_VERBOSE, DBG_PNP, "--> SerialEvtInterruptEnable\n");
+
+ SerialDbgPrintEx(TRACE_LEVEL_VERBOSE, DBG_PNP, "<-- SerialEvtInterruptEnable\n");
+
+ return STATUS_SUCCESS;
+}
+
+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;
+}
+
+BOOLEAN
+SerialISR(
+ IN WDFINTERRUPT Interrupt,
+ IN ULONG MessageID
+ )
+
+/*++
+
+Routine Description:
+
+ This is the interrupt service routine for the serial port driver.
+ It will determine whether the serial port is the source of this
+ interrupt. If it is, then this routine will do the minimum of
+ processing to quiet the interrupt. It will store any information
+ necessary for later processing.
+
+Arguments:
+
+ InterruptObject - Points to the interrupt object declared for this
+ device. We *do not* use this parameter.
+
+
+Return Value:
+
+ This function will return TRUE if the serial port is the source
+ of this interrupt, FALSE otherwise.
+
+--*/
+
+{
+ //
+ // Holds the information specific to handling this device.
+ //
+ PSERIAL_DEVICE_EXTENSION Extension = NULL;
+
+ //
+ // Holds the contents of the interrupt identification record.
+ // A low bit of zero in this register indicates that there is
+ // an interrupt pending on this device.
+ //
+ UCHAR InterruptIdReg;
+
+ //
+ // Will hold whether we've serviced any interrupt causes in this
+ // routine.
+ //
+ BOOLEAN ServicedAnInterrupt;
+
+ UCHAR tempLSR;
+ PREQUEST_CONTEXT reqContext = NULL;
+
+ UNREFERENCED_PARAMETER(MessageID);
+
+ Extension = SerialGetDeviceExtension(WdfInterruptGetDevice(Interrupt));
+
+ //
+ // Make sure we have an interrupt pending. If we do then
+ // we need to make sure that the device is open. If the
+ // device isn't open or powered down then quiet the device. Note that
+ // if the device isn't opened when we enter this routine
+ // it can't open while we're in it.
+ //
+
+ InterruptIdReg = READ_INTERRUPT_ID_REG(Extension, Extension->Controller);
+
+ if ((InterruptIdReg & SERIAL_IIR_NO_INTERRUPT_PENDING)) {
+
+ ServicedAnInterrupt = FALSE;
+
+ } else if (!Extension->DeviceIsOpened/*
+ || (Extension->PowerState != PowerDeviceD0)*/) {
+
+
+ //
+ // We got an interrupt with the device being closed or when the
+ // device is supposed to be powered down. This
+ // is not unlikely with a serial device. We just quietly
+ // keep servicing the causes until it calms down.
+ //
+
+ ServicedAnInterrupt = TRUE;
+ do {
+
+ InterruptIdReg &= (~SERIAL_IIR_FIFOS_ENABLED);
+ switch (InterruptIdReg) {
+
+ case SERIAL_IIR_RLS: {
+
+ READ_LINE_STATUS(Extension, Extension->Controller);
+
+ break;
+
+ }
+
+ case SERIAL_IIR_RDA:
+ case SERIAL_IIR_CTI: {
+
+ READ_RECEIVE_BUFFER(Extension, Extension->Controller);
+
+ break;
+
+ }
+
+ case SERIAL_IIR_THR: {
+
+ //
+ // Alread clear from reading the iir.
+ //
+ // We want to keep close track of whether
+ // the holding register is empty.
+ //
+
+ Extension->HoldingEmpty = TRUE;
+ break;
+
+ }
+
+ case SERIAL_IIR_MS: {
+
+ READ_MODEM_STATUS(Extension, Extension->Controller);
+ break;
+
+ }
+
+ default: {
+
+ ASSERT(FALSE);
+ break;
+
+ }
+
+ }
+
+ } while (!((InterruptIdReg =
+ READ_INTERRUPT_ID_REG(Extension, Extension->Controller))
+ & SERIAL_IIR_NO_INTERRUPT_PENDING));
+
+ } else {
+
+ ServicedAnInterrupt = TRUE;
+ do {
+
+ //
+ // We only care about bits that can denote an interrupt.
+ //
+
+ InterruptIdReg &= SERIAL_IIR_RLS | SERIAL_IIR_RDA |
+ SERIAL_IIR_CTI | SERIAL_IIR_THR |
+ SERIAL_IIR_MS;
+
+ //
+ // We have an interrupt. We look for interrupt causes
+ // in priority order. The presence of a higher interrupt
+ // will mask out causes of a lower priority. When we service
+ // and quiet a higher priority interrupt we then need to check
+ // the interrupt causes to see if a new interrupt cause is
+ // present.
+ //
+
+ switch (InterruptIdReg) {
+
+ case SERIAL_IIR_RLS: {
+
+ SerialProcessLSR(Extension);
+
+ break;
+
+ }
+
+ case SERIAL_IIR_RDA:
+ case SERIAL_IIR_CTI:
+
+ {
+
+ //
+ // Reading the receive buffer will quiet this interrupt.
+ //
+ // It may also reveal a new interrupt cause.
+ //
+ UCHAR ReceivedChar;
+
+ do {
+
+ ReceivedChar =
+ READ_RECEIVE_BUFFER(Extension, Extension->Controller);
+ Extension->PerfStats.ReceivedCount++;
+ Extension->WmiPerfData.ReceivedCount++;
+
+ ReceivedChar &= Extension->ValidDataMask;
+
+ if (!ReceivedChar &&
+ (Extension->HandFlow.FlowReplace &
+ SERIAL_NULL_STRIPPING)) {
+
+ //
+ // If what we got is a null character
+ // and we're doing null stripping, then
+ // we simply act as if we didn't see it.
+ //
+
+ goto ReceiveDoLineStatus;
+
+ }
+
+ if ((Extension->HandFlow.FlowReplace &
+ SERIAL_AUTO_TRANSMIT) &&
+ ((ReceivedChar ==
+ Extension->SpecialChars.XonChar) ||
+ (ReceivedChar ==
+ Extension->SpecialChars.XoffChar))) {
+
+ //
+ // No matter what happens this character
+ // will never get seen by the app.
+ //
+
+ if (ReceivedChar ==
+ Extension->SpecialChars.XoffChar) {
+
+ Extension->TXHolding |= SERIAL_TX_XOFF;
+
+ if ((Extension->HandFlow.FlowReplace &
+ SERIAL_RTS_MASK) ==
+ SERIAL_TRANSMIT_TOGGLE) {
+
+ SerialInsertQueueDpc(
+ Extension->StartTimerLowerRTSDpc
+ )?Extension->CountOfTryingToLowerRTS++:0;
+
+ }
+
+
+ } else {
+
+ if (Extension->TXHolding & SERIAL_TX_XOFF) {
+
+ //
+ // We got the xon char **AND*** we
+ // were being held up on transmission
+ // by xoff. Clear that we are holding
+ // due to xoff. Transmission will
+ // automatically restart because of
+ // the code outside the main loop that
+ // catches problems chips like the
+ // SMC and the Winbond.
+ //
+
+ Extension->TXHolding &= ~SERIAL_TX_XOFF;
+
+ }
+
+ }
+
+ goto ReceiveDoLineStatus;
+
+ }
+
+ //
+ // Check to see if we should note
+ // the receive character or special
+ // character event.
+ //
+
+ if (Extension->IsrWaitMask) {
+
+ if (Extension->IsrWaitMask &
+ SERIAL_EV_RXCHAR) {
+
+ Extension->HistoryMask |= SERIAL_EV_RXCHAR;
+
+ }
+
+ if ((Extension->IsrWaitMask &
+ SERIAL_EV_RXFLAG) &&
+ (Extension->SpecialChars.EventChar ==
+ ReceivedChar)) {
+
+ Extension->HistoryMask |= SERIAL_EV_RXFLAG;
+
+ }
+
+ if (Extension->IrpMaskLocation &&
+ Extension->HistoryMask) {
+
+ *Extension->IrpMaskLocation =
+ Extension->HistoryMask;
+ Extension->IrpMaskLocation = NULL;
+ Extension->HistoryMask = 0;
+ reqContext = SerialGetRequestContext(Extension->CurrentWaitRequest);
+ reqContext->Information = sizeof(ULONG);
+ SerialInsertQueueDpc(
+ Extension->CommWaitDpc
+ );
+
+ }
+
+ }
+
+ SerialPutChar(
+ Extension,
+ ReceivedChar
+ );
+
+ //
+ // If we're doing line status and modem
+ // status insertion then we need to insert
+ // a zero following the character we just
+ // placed into the buffer to mark that this
+ // was reception of what we are using to
+ // escape.
+ //
+
+ if (Extension->EscapeChar &&
+ (Extension->EscapeChar ==
+ ReceivedChar)) {
+
+ SerialPutChar(
+ Extension,
+ SERIAL_LSRMST_ESCAPE
+ );
+
+ }
+
+
+ReceiveDoLineStatus: ;
+ //
+ // This reads the interrupt ID register and detemines if bits are 0
+ // If either of the reserved bits are 1, we stop servicing interrupts
+ // Since this detection method is not guarenteed this is enabled via
+ // a registry entry "UartDetectRemoval" and intialized on DriverEntry.
+ // This is disabled by default and will only be enabled on Stratus systems
+ // that allow hot replacement of serial cards
+ //
+ if(Extension->UartRemovalDetect)
+ {
+ UCHAR DetectRemoval;
+
+ DetectRemoval = READ_INTERRUPT_ID_REG(Extension, Extension->Controller);
+
+ if(DetectRemoval & SERIAL_IIR_MUST_BE_ZERO)
+ {
+ // break out of this loop and stop processing interrupts
+ break;
+ }
+ }
+
+ if (!((tempLSR = SerialProcessLSR(Extension)) &
+ SERIAL_LSR_DR)) {
+
+ //
+ // No more characters, get out of the
+ // loop.
+ //
+
+ break;
+
+ }
+
+ if ((tempLSR & ~(SERIAL_LSR_THRE | SERIAL_LSR_TEMT |
+ SERIAL_LSR_DR)) &&
+ Extension->EscapeChar) {
+
+ //
+ // An error was indicated and inserted into the
+ // stream, get out of the loop.
+ //
+
+ break;
+ }
+
+ } WHILE (TRUE);
+
+ break;
+
+ }
+
+ case SERIAL_IIR_THR: {
+
+doTrasmitStuff:;
+ Extension->HoldingEmpty = TRUE;
+
+ if (Extension->WriteLength ||
+ Extension->TransmitImmediate ||
+ Extension->SendXoffChar ||
+ Extension->SendXonChar) {
+
+ //
+ // Even though all of the characters being
+ // sent haven't all been sent, this variable
+ // will be checked when the transmit queue is
+ // empty. If it is still true and there is a
+ // wait on the transmit queue being empty then
+ // we know we finished transmitting all characters
+ // following the initiation of the wait since
+ // the code that initiates the wait will set
+ // this variable to false.
+ //
+ // One reason it could be false is that
+ // the writes were cancelled before they
+ // actually started, or that the writes
+ // failed due to timeouts. This variable
+ // basically says a character was written
+ // by the isr at some point following the
+ // initiation of the wait.
+ //
+
+ Extension->EmptiedTransmit = TRUE;
+
+ //
+ // If we have output flow control based on
+ // the modem status lines, then we have to do
+ // all the modem work before we output each
+ // character. (Otherwise we might miss a
+ // status line change.)
+ //
+
+ if (Extension->HandFlow.ControlHandShake &
+ SERIAL_OUT_HANDSHAKEMASK) {
+
+ SerialHandleModemUpdate(
+ Extension,
+ TRUE
+ );
+
+ }
+
+ //
+ // We can only send the xon character if
+ // the only reason we are holding is because
+ // of the xoff. (Hardware flow control or
+ // sending break preclude putting a new character
+ // on the wire.)
+ //
+
+ if (Extension->SendXonChar &&
+ !(Extension->TXHolding & ~SERIAL_TX_XOFF)) {
+
+ if ((Extension->HandFlow.FlowReplace &
+ SERIAL_RTS_MASK) ==
+ SERIAL_TRANSMIT_TOGGLE) {
+
+ //
+ // We have to raise if we're sending
+ // this character.
+ //
+
+ SerialSetRTS(Extension->WdfInterrupt, Extension);
+
+ Extension->PerfStats.TransmittedCount++;
+ Extension->WmiPerfData.TransmittedCount++;
+
+ WRITE_TRANSMIT_HOLDING(Extension, Extension->Controller,
+ Extension->SpecialChars.XonChar);
+ SerialInsertQueueDpc(
+ Extension->StartTimerLowerRTSDpc
+ )?Extension->CountOfTryingToLowerRTS++:0;
+
+
+ } else {
+
+ Extension->PerfStats.TransmittedCount++;
+ Extension->WmiPerfData.TransmittedCount++;
+
+ WRITE_TRANSMIT_HOLDING(Extension,
+ Extension->Controller,
+ Extension->SpecialChars.XonChar);
+ }
+
+
+ Extension->SendXonChar = FALSE;
+ Extension->HoldingEmpty = FALSE;
+
+ //
+ // If we send an xon, by definition we
+ // can't be holding by Xoff.
+ //
+
+ Extension->TXHolding &= ~SERIAL_TX_XOFF;
+
+ //
+ // If we are sending an xon char then
+ // by definition we can't be "holding"
+ // up reception by Xoff.
+ //
+
+ Extension->RXHolding &= ~SERIAL_RX_XOFF;
+
+ } else if (Extension->SendXoffChar &&
+ !Extension->TXHolding) {
+
+ if ((Extension->HandFlow.FlowReplace &
+ SERIAL_RTS_MASK) ==
+ SERIAL_TRANSMIT_TOGGLE) {
+
+ //
+ // We have to raise if we're sending
+ // this character.
+ //
+
+ SerialSetRTS(Extension->WdfInterrupt, Extension);
+
+ Extension->PerfStats.TransmittedCount++;
+ Extension->WmiPerfData.TransmittedCount++;
+ WRITE_TRANSMIT_HOLDING(Extension,
+ Extension->Controller,
+ Extension->SpecialChars.XoffChar);
+
+ SerialInsertQueueDpc(
+ Extension->StartTimerLowerRTSDpc
+ )?Extension->CountOfTryingToLowerRTS++:0;
+
+ } else {
+
+ Extension->PerfStats.TransmittedCount++;
+ Extension->WmiPerfData.TransmittedCount++;
+ WRITE_TRANSMIT_HOLDING(Extension,
+ Extension->Controller,
+ Extension->SpecialChars.XoffChar);
+
+ }
+
+ //
+ // We can't be sending an Xoff character
+ // if the transmission is already held
+ // up because of Xoff. Therefore, if we
+ // are holding then we can't send the char.
+ //
+
+ //
+ // If the application has set xoff continue
+ // mode then we don't actually stop sending
+ // characters if we send an xoff to the other
+ // side.
+ //
+
+ if (!(Extension->HandFlow.FlowReplace &
+ SERIAL_XOFF_CONTINUE)) {
+
+ Extension->TXHolding |= SERIAL_TX_XOFF;
+
+ if ((Extension->HandFlow.FlowReplace &
+ SERIAL_RTS_MASK) ==
+ SERIAL_TRANSMIT_TOGGLE) {
+
+ SerialInsertQueueDpc(
+ Extension->StartTimerLowerRTSDpc
+ )?Extension->CountOfTryingToLowerRTS++:0;
+
+ }
+
+ }
+
+ Extension->SendXoffChar = FALSE;
+ Extension->HoldingEmpty = FALSE;
+
+ //
+ // Even if transmission is being held
+ // up, we should still transmit an immediate
+ // character if all that is holding us
+ // up is xon/xoff (OS/2 rules).
+ //
+
+ } else if (Extension->TransmitImmediate &&
+ (!Extension->TXHolding ||
+ (Extension->TXHolding == SERIAL_TX_XOFF)
+ )) {
+
+ Extension->TransmitImmediate = FALSE;
+
+ if ((Extension->HandFlow.FlowReplace &
+ SERIAL_RTS_MASK) ==
+ SERIAL_TRANSMIT_TOGGLE) {
+
+ //
+ // We have to raise if we're sending
+ // this character.
+ //
+
+ SerialSetRTS(Extension->WdfInterrupt, Extension);
+
+ Extension->PerfStats.TransmittedCount++;
+ Extension->WmiPerfData.TransmittedCount++;
+ WRITE_TRANSMIT_HOLDING(Extension,
+ Extension->Controller,
+ Extension->ImmediateChar);
+
+ SerialInsertQueueDpc(
+ Extension->StartTimerLowerRTSDpc
+ )?Extension->CountOfTryingToLowerRTS++:0;
+
+ } else {
+
+ Extension->PerfStats.TransmittedCount++;
+ Extension->WmiPerfData.TransmittedCount++;
+ WRITE_TRANSMIT_HOLDING(Extension,
+ Extension->Controller,
+ Extension->ImmediateChar);
+
+ }
+
+ Extension->HoldingEmpty = FALSE;
+
+ SerialInsertQueueDpc(
+ Extension->CompleteImmediateDpc
+ );
+
+ } else if (!Extension->TXHolding) {
+
+ ULONG amountToWrite;
+
+ if (Extension->FifoPresent) {
+
+ amountToWrite = (Extension->TxFifoAmount <
+ Extension->WriteLength)?
+ Extension->TxFifoAmount:
+ Extension->WriteLength;
+
+ } else {
+
+ amountToWrite = 1;
+
+ }
+ if ((Extension->HandFlow.FlowReplace &
+ SERIAL_RTS_MASK) ==
+ SERIAL_TRANSMIT_TOGGLE) {
+
+ //
+ // We have to raise if we're sending
+ // this character.
+ //
+
+ SerialSetRTS(Extension->WdfInterrupt, Extension);
+
+ if (amountToWrite == 1) {
+
+ Extension->PerfStats.TransmittedCount++;
+ Extension->WmiPerfData.TransmittedCount++;
+ WRITE_TRANSMIT_HOLDING(Extension,
+ Extension->Controller,
+ *(Extension->WriteCurrentChar));
+
+ } else {
+
+ Extension->PerfStats.TransmittedCount +=
+ amountToWrite;
+ Extension->WmiPerfData.TransmittedCount +=
+ amountToWrite;
+ WRITE_TRANSMIT_FIFO_HOLDING(Extension,
+ Extension->Controller,
+ Extension->WriteCurrentChar,
+ amountToWrite);
+ }
+
+ SerialInsertQueueDpc(
+ Extension->StartTimerLowerRTSDpc
+ )?Extension->CountOfTryingToLowerRTS++:0;
+
+ } else {
+
+ if (amountToWrite == 1) {
+
+ Extension->PerfStats.TransmittedCount++;
+ Extension->WmiPerfData.TransmittedCount++;
+ WRITE_TRANSMIT_HOLDING(Extension,
+ Extension->Controller,
+ *(Extension->WriteCurrentChar));
+
+ } else {
+
+ Extension->PerfStats.TransmittedCount +=
+ amountToWrite;
+ Extension->WmiPerfData.TransmittedCount +=
+ amountToWrite;
+ WRITE_TRANSMIT_FIFO_HOLDING(Extension,
+ Extension->Controller,
+ Extension->WriteCurrentChar,
+ amountToWrite);
+
+ }
+
+ }
+
+ Extension->HoldingEmpty = FALSE;
+ Extension->WriteCurrentChar += amountToWrite;
+ Extension->WriteLength -= amountToWrite;
+
+ if (!Extension->WriteLength) {
+
+ //
+ // No More characters left. This
+ // write is complete. Take care
+ // when updating the information field,
+ // we could have an xoff counter masquerading
+ // as a write request.
+ //
+ reqContext = SerialGetRequestContext(Extension->CurrentWriteRequest);
+
+ reqContext->Information =
+ (reqContext->MajorFunction == IRP_MJ_WRITE)?
+ (reqContext->Length): (1);
+
+ SerialInsertQueueDpc(
+ Extension->CompleteWriteDpc
+ );
+
+ }
+
+ }
+
+ }
+
+ break;
+
+ }
+
+ case SERIAL_IIR_MS: {
+
+ SerialHandleModemUpdate(
+ Extension,
+ FALSE
+ );
+
+ break;
+
+ }
+
+ }
+
+ } while (!((InterruptIdReg =
+ READ_INTERRUPT_ID_REG(Extension, Extension->Controller))
+ & SERIAL_IIR_NO_INTERRUPT_PENDING));
+
+ //
+ // Besides catching the WINBOND and SMC chip problems this
+ // will also cause transmission to restart incase of an xon
+ // char being received. Don't remove.
+ //
+
+ if (SerialProcessLSR(Extension) & SERIAL_LSR_THRE) {
+
+ if (!Extension->TXHolding &&
+ (Extension->WriteLength ||
+ Extension->TransmitImmediate)) {
+
+ goto doTrasmitStuff;
+
+ }
+
+ }
+
+ }
+
+ return ServicedAnInterrupt;
+
+}
+
+VOID
+SerialPutChar(
+ IN PSERIAL_DEVICE_EXTENSION Extension,
+ IN UCHAR CharToPut
+ )
+
+/*++
+
+Routine Description:
+
+ This routine, which only runs at device level, takes care of
+ placing a character into the typeahead (receive) buffer.
+
+Arguments:
+
+ Extension - The serial device extension.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PREQUEST_CONTEXT reqContext = NULL;
+
+ //
+ // If we have dsr sensitivity enabled then
+ // we need to check the modem status register
+ // to see if it has changed.
+ //
+
+ if (Extension->HandFlow.ControlHandShake &
+ SERIAL_DSR_SENSITIVITY) {
+
+ SerialHandleModemUpdate(
+ Extension,
+ FALSE
+ );
+
+ if (Extension->RXHolding & SERIAL_RX_DSR) {
+
+ //
+ // We simply act as if we haven't
+ // seen the character if we have dsr
+ // sensitivity and the dsr line is low.
+ //
+
+ return;
+
+ }
+
+ }
+
+ //
+ // If the xoff counter is non-zero then decrement it.
+ // If the counter then goes to zero, complete that request.
+ //
+
+ if (Extension->CountSinceXoff) {
+
+ Extension->CountSinceXoff--;
+
+ if (!Extension->CountSinceXoff) {
+ reqContext = SerialGetRequestContext(Extension->CurrentXoffRequest);
+ reqContext->Status = STATUS_SUCCESS;
+ reqContext->Information = 0;
+ SerialInsertQueueDpc(
+ Extension->XoffCountCompleteDpc
+ );
+
+ }
+
+ }
+
+ //
+ // Check to see if we are copying into the
+ // users buffer or into the interrupt buffer.
+ //
+ // If we are copying into the user buffer
+ // then we know there is always room for one more.
+ // (We know this because if there wasn't room
+ // then that read would have completed and we
+ // would be using the interrupt buffer.)
+ //
+ // If we are copying into the interrupt buffer
+ // then we will need to check if we have enough
+ // room.
+ //
+
+ if (Extension->ReadBufferBase !=
+ Extension->InterruptReadBuffer) {
+
+ //
+ // Increment the following value so
+ // that the interval timer (if one exists
+ // for this read) can know that a character
+ // has been read.
+ //
+
+ Extension->ReadByIsr++;
+
+ //
+ // We are in the user buffer. Place the
+ // character into the buffer. See if the
+ // read is complete.
+ //
+
+ *Extension->CurrentCharSlot = CharToPut;
+
+ if (Extension->CurrentCharSlot ==
+ Extension->LastCharSlot) {
+
+ //
+ // We've filled up the users buffer.
+ // Switch back to the interrupt buffer
+ // and send off a DPC to Complete the read.
+ //
+ // It is inherent that when we were using
+ // a user buffer that the interrupt buffer
+ // was empty.
+ //
+
+ Extension->ReadBufferBase =
+ Extension->InterruptReadBuffer;
+ Extension->CurrentCharSlot =
+ Extension->InterruptReadBuffer;
+ Extension->FirstReadableChar =
+ Extension->InterruptReadBuffer;
+ Extension->LastCharSlot =
+ Extension->InterruptReadBuffer +
+ (Extension->BufferSize - 1);
+ Extension->CharsInInterruptBuffer = 0;
+ reqContext = SerialGetRequestContext(Extension->CurrentReadRequest);
+ reqContext->Information = reqContext->Length;
+
+ SerialInsertQueueDpc(
+ Extension->CompleteReadDpc
+ );
+
+ } else {
+
+ //
+ // Not done with the users read.
+ //
+
+ Extension->CurrentCharSlot++;
+
+ }
+
+ } else {
+
+ //
+ // We need to see if we reached our flow
+ // control threshold. If we have then
+ // we turn on whatever flow control the
+ // owner has specified. If no flow
+ // control was specified, well..., we keep
+ // trying to receive characters and hope that
+ // we have enough room. Note that no matter
+ // what flow control protocol we are using, it
+ // will not prevent us from reading whatever
+ // characters are available.
+ //
+
+ if ((Extension->HandFlow.ControlHandShake
+ & SERIAL_DTR_MASK) ==
+ SERIAL_DTR_HANDSHAKE) {
+
+ //
+ // If we are already doing a
+ // dtr hold then we don't have
+ // to do anything else.
+ //
+
+ if (!(Extension->RXHolding &
+ SERIAL_RX_DTR)) {
+
+ if ((Extension->BufferSize -
+ Extension->HandFlow.XoffLimit)
+ <= (Extension->CharsInInterruptBuffer+1)) {
+
+ Extension->RXHolding |= SERIAL_RX_DTR;
+
+ SerialClrDTR(Extension->WdfInterrupt, Extension);
+
+ }
+
+ }
+
+ }
+
+ if ((Extension->HandFlow.FlowReplace
+ & SERIAL_RTS_MASK) ==
+ SERIAL_RTS_HANDSHAKE) {
+
+ //
+ // If we are already doing a
+ // rts hold then we don't have
+ // to do anything else.
+ //
+
+ if (!(Extension->RXHolding &
+ SERIAL_RX_RTS)) {
+
+ if ((Extension->BufferSize -
+ Extension->HandFlow.XoffLimit)
+ <= (Extension->CharsInInterruptBuffer+1)) {
+
+ Extension->RXHolding |= SERIAL_RX_RTS;
+
+ SerialClrRTS(Extension->WdfInterrupt, Extension);
+
+ }
+
+ }
+
+ }
+
+ if (Extension->HandFlow.FlowReplace &
+ SERIAL_AUTO_RECEIVE) {
+
+ //
+ // If we are already doing a
+ // xoff hold then we don't have
+ // to do anything else.
+ //
+
+ if (!(Extension->RXHolding &
+ SERIAL_RX_XOFF)) {
+
+ if ((Extension->BufferSize -
+ Extension->HandFlow.XoffLimit)
+ <= (Extension->CharsInInterruptBuffer+1)) {
+
+ Extension->RXHolding |= SERIAL_RX_XOFF;
+
+ //
+ // If necessary cause an
+ // off to be sent.
+ //
+
+ SerialProdXonXoff(
+ Extension,
+ FALSE
+ );
+
+ }
+
+ }
+
+ }
+
+ if (Extension->CharsInInterruptBuffer <
+ Extension->BufferSize) {
+
+ *Extension->CurrentCharSlot = CharToPut;
+ Extension->CharsInInterruptBuffer++;
+
+ //
+ // If we've become 80% full on this character
+ // and this is an interesting event, note it.
+ //
+
+ if (Extension->CharsInInterruptBuffer ==
+ Extension->BufferSizePt8) {
+
+ if (Extension->IsrWaitMask &
+ SERIAL_EV_RX80FULL) {
+
+ Extension->HistoryMask |= SERIAL_EV_RX80FULL;
+
+ if (Extension->IrpMaskLocation) {
+
+ *Extension->IrpMaskLocation =
+ Extension->HistoryMask;
+ Extension->IrpMaskLocation = NULL;
+ Extension->HistoryMask = 0;
+
+ reqContext = SerialGetRequestContext(Extension->CurrentWaitRequest);
+ reqContext->Information = sizeof(ULONG);
+ SerialInsertQueueDpc(
+ Extension->CommWaitDpc
+ );
+
+ }
+
+ }
+
+ }
+
+ //
+ // Point to the next available space
+ // for a received character. Make sure
+ // that we wrap around to the beginning
+ // of the buffer if this last character
+ // received was placed at the last slot
+ // in the buffer.
+ //
+
+ if (Extension->CurrentCharSlot ==
+ Extension->LastCharSlot) {
+
+ Extension->CurrentCharSlot =
+ Extension->InterruptReadBuffer;
+
+ } else {
+
+ Extension->CurrentCharSlot++;
+
+ }
+
+ } else {
+
+ //
+ // We have a new character but no room for it.
+ //
+
+ Extension->PerfStats.BufferOverrunErrorCount++;
+ Extension->WmiPerfData.BufferOverrunErrorCount++;
+ Extension->ErrorWord |= SERIAL_ERROR_QUEUEOVERRUN;
+
+ if (Extension->HandFlow.FlowReplace &
+ SERIAL_ERROR_CHAR) {
+
+ //
+ // Place the error character into the last
+ // valid place for a character. Be careful!,
+ // that place might not be the previous location!
+ //
+
+ if (Extension->CurrentCharSlot ==
+ Extension->InterruptReadBuffer) {
+
+ *(Extension->InterruptReadBuffer+
+ (Extension->BufferSize-1)) =
+ Extension->SpecialChars.ErrorChar;
+
+ } else {
+
+ *(Extension->CurrentCharSlot-1) =
+ Extension->SpecialChars.ErrorChar;
+
+ }
+
+ }
+
+ //
+ // If the application has requested it, abort all reads
+ // and writes on an error.
+ //
+
+ if (Extension->HandFlow.ControlHandShake &
+ SERIAL_ERROR_ABORT) {
+
+ SerialInsertQueueDpc(
+ Extension->CommErrorDpc
+ );
+
+ }
+
+ }
+
+ }
+
+}
+
+UCHAR
+SerialProcessLSR(
+ IN PSERIAL_DEVICE_EXTENSION Extension
+ )
+
+/*++
+
+Routine Description:
+
+ This routine, which only runs at device level, reads the
+ ISR and totally processes everything that might have
+ changed.
+
+Arguments:
+
+ Extension - The serial device extension.
+
+Return Value:
+
+ The value of the line status register.
+
+--*/
+
+{
+ PREQUEST_CONTEXT reqContext = NULL;
+
+ UCHAR LineStatus = READ_LINE_STATUS(Extension, Extension->Controller);
+
+
+ Extension->HoldingEmpty = (LineStatus & SERIAL_LSR_THRE) ? TRUE : FALSE;
+
+ //
+ // If the line status register is just the fact that
+ // the trasmit registers are empty or a character is
+ // received then we want to reread the interrupt
+ // identification register so that we just pick up that.
+ //
+
+ if (LineStatus & ~(SERIAL_LSR_THRE | SERIAL_LSR_TEMT
+ | SERIAL_LSR_DR)) {
+
+ //
+ // We have some sort of data problem in the receive.
+ // For any of these errors we may abort all current
+ // reads and writes.
+ //
+ //
+ // If we are inserting the value of the line status
+ // into the data stream then we should put the escape
+ // character in now.
+ //
+
+ if (Extension->EscapeChar) {
+
+ SerialPutChar(
+ Extension,
+ Extension->EscapeChar
+ );
+
+ SerialPutChar(
+ Extension,
+ (UCHAR)((LineStatus & SERIAL_LSR_DR)?
+ (SERIAL_LSRMST_LSR_DATA):(SERIAL_LSRMST_LSR_NODATA))
+ );
+
+ SerialPutChar(
+ Extension,
+ LineStatus
+ );
+
+ if (LineStatus & SERIAL_LSR_DR) {
+
+ Extension->PerfStats.ReceivedCount++;
+ Extension->WmiPerfData.ReceivedCount++;
+ SerialPutChar(
+ Extension,
+ READ_RECEIVE_BUFFER(Extension, Extension->Controller)
+ );
+
+ }
+
+ }
+
+ if (LineStatus & SERIAL_LSR_OE) {
+
+ Extension->PerfStats.SerialOverrunErrorCount++;
+ Extension->WmiPerfData.SerialOverrunErrorCount++;
+ Extension->ErrorWord |= SERIAL_ERROR_OVERRUN;
+
+ if (Extension->HandFlow.FlowReplace &
+ SERIAL_ERROR_CHAR) {
+
+ SerialPutChar(
+ Extension,
+ Extension->SpecialChars.ErrorChar
+ );
+
+ if (LineStatus & SERIAL_LSR_DR) {
+
+ Extension->PerfStats.ReceivedCount++;
+ Extension->WmiPerfData.ReceivedCount++;
+ READ_RECEIVE_BUFFER(Extension, Extension->Controller);
+
+ }
+
+ } else {
+
+ if (LineStatus & SERIAL_LSR_DR) {
+
+ Extension->PerfStats.ReceivedCount++;
+ Extension->WmiPerfData.ReceivedCount++;
+ SerialPutChar(
+ Extension,
+ READ_RECEIVE_BUFFER(Extension,
+ Extension->Controller
+ )
+ );
+
+ }
+
+ }
+
+ }
+
+ if (LineStatus & SERIAL_LSR_BI) {
+
+ Extension->ErrorWord |= SERIAL_ERROR_BREAK;
+
+ if (Extension->HandFlow.FlowReplace &
+ SERIAL_BREAK_CHAR) {
+
+ SerialPutChar(
+ Extension,
+ Extension->SpecialChars.BreakChar
+ );
+
+ }
+
+ } else {
+
+ //
+ // Framing errors only count if they
+ // occur exclusive of a break being
+ // received.
+ //
+
+ if (LineStatus & SERIAL_LSR_PE) {
+
+ Extension->PerfStats.ParityErrorCount++;
+ Extension->WmiPerfData.ParityErrorCount++;
+ Extension->ErrorWord |= SERIAL_ERROR_PARITY;
+
+ if (Extension->HandFlow.FlowReplace &
+ SERIAL_ERROR_CHAR) {
+
+ SerialPutChar(
+ Extension,
+ Extension->SpecialChars.ErrorChar
+ );
+
+ if (LineStatus & SERIAL_LSR_DR) {
+
+ Extension->PerfStats.ReceivedCount++;
+ Extension->WmiPerfData.ReceivedCount++;
+ READ_RECEIVE_BUFFER(Extension, Extension->Controller);
+
+ }
+
+ }
+
+ }
+
+ if (LineStatus & SERIAL_LSR_FE) {
+
+ Extension->PerfStats.FrameErrorCount++;
+ Extension->WmiPerfData.FrameErrorCount++;
+ Extension->ErrorWord |= SERIAL_ERROR_FRAMING;
+
+ if (Extension->HandFlow.FlowReplace &
+ SERIAL_ERROR_CHAR) {
+
+ SerialPutChar(
+ Extension,
+ Extension->SpecialChars.ErrorChar
+ );
+ if (LineStatus & SERIAL_LSR_DR) {
+
+ Extension->PerfStats.ReceivedCount++;
+ Extension->WmiPerfData.ReceivedCount++;
+ READ_RECEIVE_BUFFER(Extension, Extension->Controller);
+
+ }
+
+ }
+
+ }
+
+ }
+
+ //
+ // If the application has requested it,
+ // abort all the reads and writes
+ // on an error.
+ //
+
+ if (Extension->HandFlow.ControlHandShake &
+ SERIAL_ERROR_ABORT) {
+
+ SerialInsertQueueDpc(
+ Extension->CommErrorDpc
+ );
+
+ }
+
+ //
+ // Check to see if we have a wait
+ // pending on the comm error events. If we
+ // do then we schedule a dpc to satisfy
+ // that wait.
+ //
+
+ if (Extension->IsrWaitMask) {
+
+ if ((Extension->IsrWaitMask & SERIAL_EV_ERR) &&
+ (LineStatus & (SERIAL_LSR_OE |
+ SERIAL_LSR_PE |
+ SERIAL_LSR_FE))) {
+
+ Extension->HistoryMask |= SERIAL_EV_ERR;
+
+ }
+
+ if ((Extension->IsrWaitMask & SERIAL_EV_BREAK) &&
+ (LineStatus & SERIAL_LSR_BI)) {
+
+ Extension->HistoryMask |= SERIAL_EV_BREAK;
+
+ }
+
+ if (Extension->IrpMaskLocation &&
+ Extension->HistoryMask) {
+
+ *Extension->IrpMaskLocation =
+ Extension->HistoryMask;
+ Extension->IrpMaskLocation = NULL;
+ Extension->HistoryMask = 0;
+ reqContext = SerialGetRequestContext(Extension->CurrentWaitRequest);
+ reqContext->Information = sizeof(ULONG);
+ SerialInsertQueueDpc(
+ Extension->CommWaitDpc
+ );
+
+ }
+
+ }
+
+ if (LineStatus & SERIAL_LSR_THRE) {
+
+ //
+ // There is a hardware bug in some versions
+ // of the 16450 and 550. If THRE interrupt
+ // is pending, but a higher interrupt comes
+ // in it will only return the higher and
+ // *forget* about the THRE.
+ //
+ // A suitable workaround - whenever we
+ // are *all* done reading line status
+ // of the device we check to see if the
+ // transmit holding register is empty. If it is
+ // AND we are currently transmitting data
+ // enable the interrupts which should cause
+ // an interrupt indication which we quiet
+ // when we read the interrupt id register.
+ //
+
+ if (Extension->WriteLength |
+ Extension->TransmitImmediate) {
+
+ DISABLE_ALL_INTERRUPTS(Extension,
+ Extension->Controller
+ );
+ ENABLE_ALL_INTERRUPTS(Extension,
+ Extension->Controller
+ );
+ }
+
+ }
+
+ }
+
+ return LineStatus;
+}
+
+
diff --git a/tests/projects/wdk/kmdf/serial/log.c b/tests/projects/wdk/kmdf/serial/log.c
new file mode 100644
index 000000000..285c8b2f8
--- /dev/null
+++ b/tests/projects/wdk/kmdf/serial/log.c
@@ -0,0 +1,97 @@
+/*++
+
+Copyright (c) Microsoft Corporation
+
+Module Name:
+
+ log.c
+
+Abstract:
+
+ Debug log Code for serial.
+
+Environment:
+
+ kernel mode only
+
+--*/
+
+#include "precomp.h"
+
+extern ULONG DebugLevel;
+extern ULONG DebugFlag;
+
+#if !defined(EVENT_TRACING)
+
+VOID
+SerialDbgPrintEx (
+ IN ULONG TraceEventsLevel,
+ IN ULONG TraceEventsFlag,
+ IN PCCHAR DebugMessage,
+ ...
+ )
+
+/*++
+
+Routine Description:
+
+ Debug print for the sample driver.
+
+Arguments:
+
+ TraceEventsLevel - print level between 0 and 3, with 3 the most verbose
+
+Return Value:
+
+ None.
+
+ --*/
+ {
+#if DBG
+
+#define TEMP_BUFFER_SIZE 1024
+
+ va_list list;
+ CHAR debugMessageBuffer [TEMP_BUFFER_SIZE];
+ NTSTATUS status;
+
+ va_start(list, DebugMessage);
+
+ if (DebugMessage) {
+
+ //
+ // Using new safe string functions instead of _vsnprintf.
+ // This function takes care of NULL terminating if the message
+ // is longer than the buffer.
+ //
+ status = RtlStringCbVPrintfA( debugMessageBuffer,
+ sizeof(debugMessageBuffer),
+ DebugMessage,
+ list );
+ if(!NT_SUCCESS(status)) {
+
+ KdPrint((_DRIVER_NAME_": RtlStringCbVPrintfA failed %x\n", status));
+ return;
+ }
+ if (TraceEventsLevel < TRACE_LEVEL_INFORMATION ||
+ (TraceEventsLevel <= DebugLevel &&
+ ((TraceEventsFlag & DebugFlag) == TraceEventsFlag))) {
+
+ KdPrint((debugMessageBuffer));
+ }
+ }
+ va_end(list);
+
+ return;
+
+#else
+
+ UNREFERENCED_PARAMETER(TraceEventsLevel);
+ UNREFERENCED_PARAMETER(TraceEventsFlag);
+ UNREFERENCED_PARAMETER(DebugMessage);
+
+#endif
+}
+
+#endif
+
diff --git a/tests/projects/wdk/kmdf/serial/log.h b/tests/projects/wdk/kmdf/serial/log.h
new file mode 100644
index 000000000..eedcd08f3
--- /dev/null
+++ b/tests/projects/wdk/kmdf/serial/log.h
@@ -0,0 +1,37 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+:ts=4
+
+Module Name:
+
+ log.h
+
+Abstract:
+
+ debug macros
+
+Environment:
+
+ Kernel & user mode
+
+--*/
+
+#ifndef __LOG_H__
+#define __LOG_H__
+
+#if !defined(EVENT_TRACING)
+
+VOID
+SerialDbgPrintEx (
+ IN ULONG DebugPrintLevel,
+ IN ULONG DebugPrintFlag,
+ IN PCCHAR DebugMessage,
+ ...
+ );
+
+#endif
+
+#endif // __LOG_H__
+
+
diff --git a/tests/projects/wdk/kmdf/serial/modmflow.c b/tests/projects/wdk/kmdf/serial/modmflow.c
new file mode 100644
index 000000000..1c71ae3fd
--- /dev/null
+++ b/tests/projects/wdk/kmdf/serial/modmflow.c
@@ -0,0 +1,1714 @@
+/*++
+
+Copyright (c) Microsoft Corporation
+
+Module Name:
+
+ modmflow.c
+
+Abstract:
+
+ This module contains *MOST* of the code used to manipulate
+ the modem control and status registers. The vast majority
+ of the remainder of flow control is concentrated in the
+ Interrupt service routine. A very small amount resides
+ in the read code that pull characters out of the interrupt
+ buffer.
+
+Environment:
+
+ Kernel mode
+
+--*/
+
+#include "precomp.h"
+
+#if defined(EVENT_TRACING)
+#include "modmflow.tmh"
+#endif
+
+
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialDecrementRTSCounter;
+
+BOOLEAN
+SerialSetDTR(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This routine which is only called at interrupt level is used
+ to set the DTR in the modem control register.
+
+Arguments:
+
+ Context - Really a pointer to the device extension.
+
+Return Value:
+
+ This routine always returns FALSE.
+
+--*/
+
+{
+ PSERIAL_DEVICE_EXTENSION Extension = Context;
+ UCHAR ModemControl;
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ ModemControl = READ_MODEM_CONTROL(Extension, Extension->Controller);
+
+ ModemControl |= SERIAL_MCR_DTR;
+
+ SerialDbgPrintEx(TRACE_LEVEL_VERBOSE, DBG_IOCTLS,
+ "Setting DTR for %p\n", Extension->Controller);
+
+ WRITE_MODEM_CONTROL(Extension, Extension->Controller, ModemControl);
+
+ return FALSE;
+
+}
+
+BOOLEAN
+SerialClrDTR(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This routine which is only called at interrupt level is used
+ to clear the DTR in the modem control register.
+
+Arguments:
+
+ Context - Really a pointer to the device extension.
+
+Return Value:
+
+ This routine always returns FALSE.
+
+--*/
+
+{
+
+ PSERIAL_DEVICE_EXTENSION Extension = Context;
+ UCHAR ModemControl;
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ ModemControl = READ_MODEM_CONTROL(Extension, Extension->Controller);
+
+ ModemControl &= ~SERIAL_MCR_DTR;
+
+ SerialDbgPrintEx(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "Clearing DTR for %p\n", Extension->Controller);
+
+ WRITE_MODEM_CONTROL(Extension, Extension->Controller, ModemControl);
+
+ return FALSE;
+
+}
+
+BOOLEAN
+SerialSetRTS(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This routine which is only called at interrupt level is used
+ to set the RTS in the modem control register.
+
+Arguments:
+
+ Context - Really a pointer to the device extension.
+
+Return Value:
+
+ This routine always returns FALSE.
+
+--*/
+
+{
+
+ PSERIAL_DEVICE_EXTENSION Extension = Context;
+ UCHAR ModemControl;
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ ModemControl = READ_MODEM_CONTROL(Extension, Extension->Controller);
+
+ ModemControl |= SERIAL_MCR_RTS;
+
+ SerialDbgPrintEx(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "Setting Rts for %p\n", Extension->Controller);
+
+ WRITE_MODEM_CONTROL(Extension, Extension->Controller, ModemControl);
+
+ return FALSE;
+
+}
+
+BOOLEAN
+SerialClrRTS(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This routine which is only called at interrupt level is used
+ to clear the RTS in the modem control register.
+
+Arguments:
+
+ Context - Really a pointer to the device extension.
+
+Return Value:
+
+ This routine always returns FALSE.
+
+--*/
+
+{
+
+ PSERIAL_DEVICE_EXTENSION Extension = Context;
+ UCHAR ModemControl;
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ ModemControl = READ_MODEM_CONTROL(Extension, Extension->Controller);
+
+ ModemControl &= ~SERIAL_MCR_RTS;
+
+ SerialDbgPrintEx(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "Clearing Rts for %p\n", Extension->Controller);
+
+ WRITE_MODEM_CONTROL(Extension, Extension->Controller, ModemControl);
+
+ return FALSE;
+
+}
+
+BOOLEAN
+SerialSetupNewHandFlow(
+ IN PSERIAL_DEVICE_EXTENSION Extension,
+ IN PSERIAL_HANDFLOW NewHandFlow
+ )
+
+/*++
+
+Routine Description:
+
+ This routine adjusts the flow control based on new
+ control flow.
+
+Arguments:
+
+ Extension - A pointer to the serial device extension.
+
+ NewHandFlow - A pointer to a serial handflow structure
+ that is to become the new setup for flow
+ control.
+
+Return Value:
+
+ This routine always returns FALSE.
+
+--*/
+
+{
+
+ SERIAL_HANDFLOW New = *NewHandFlow;
+
+ //
+ // If the Extension->DeviceIsOpened is FALSE that means
+ // we are entering this routine in response to an open request.
+ // If that is so, then we always proceed with the work regardless
+ // of whether things have changed.
+ //
+
+ //
+ // First we take care of the DTR flow control. We only
+ // do work if something has changed.
+ //
+
+ if ((!Extension->DeviceIsOpened) ||
+ ((Extension->HandFlow.ControlHandShake & SERIAL_DTR_MASK) !=
+ (New.ControlHandShake & SERIAL_DTR_MASK))) {
+
+ SerialDbgPrintEx(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "Processing DTR flow for %p\n",
+ Extension->Controller);
+
+ if (New.ControlHandShake & SERIAL_DTR_MASK) {
+
+ //
+ // Well we might want to set DTR.
+ //
+ // Before we do, we need to check whether we are doing
+ // dtr flow control. If we are then we need to check
+ // if then number of characters in the interrupt buffer
+ // exceeds the XoffLimit. If it does then we don't
+ // enable DTR AND we set the RXHolding to record that
+ // we are holding because of the dtr.
+ //
+
+ if ((New.ControlHandShake & SERIAL_DTR_MASK)
+ == SERIAL_DTR_HANDSHAKE) {
+
+ if ((Extension->BufferSize - New.XoffLimit) >
+ Extension->CharsInInterruptBuffer) {
+
+ //
+ // However if we are already holding we don't want
+ // to turn it back on unless we exceed the Xon
+ // limit.
+ //
+
+ if (Extension->RXHolding & SERIAL_RX_DTR) {
+
+ //
+ // We can assume that its DTR line is already low.
+ //
+
+ if (Extension->CharsInInterruptBuffer >
+ (ULONG)New.XonLimit) {
+
+ SerialDbgPrintEx(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "Removing DTR block on "
+ "reception for %p\n",
+ Extension->Controller);
+
+ Extension->RXHolding &= ~SERIAL_RX_DTR;
+ SerialSetDTR(Extension->WdfInterrupt, Extension);
+
+ }
+
+ } else {
+
+ SerialSetDTR(Extension->WdfInterrupt, Extension);
+
+ }
+
+ } else {
+
+ SerialDbgPrintEx(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "Setting DTR block on reception "
+ "for %p\n", Extension->Controller);
+ Extension->RXHolding |= SERIAL_RX_DTR;
+ SerialClrDTR(Extension->WdfInterrupt, Extension);
+
+ }
+
+ } else {
+
+ //
+ // Note that if we aren't currently doing dtr flow control then
+ // we MIGHT have been. So even if we aren't currently doing
+ // DTR flow control, we should still check if RX is holding
+ // because of DTR. If it is, then we should clear the holding
+ // of this bit.
+ //
+
+ if (Extension->RXHolding & SERIAL_RX_DTR) {
+ SerialDbgPrintEx(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "Removing dtr block of reception "
+ "for %p\n", Extension->Controller);
+ Extension->RXHolding &= ~SERIAL_RX_DTR;
+ }
+
+ SerialSetDTR(Extension->WdfInterrupt, Extension);
+
+ }
+
+ } else {
+
+ //
+ // The end result here will be that DTR is cleared.
+ //
+ // We first need to check whether reception is being held
+ // up because of previous DTR flow control. If it is then
+ // we should clear that reason in the RXHolding mask.
+ //
+
+ if (Extension->RXHolding & SERIAL_RX_DTR) {
+
+ SerialDbgPrintEx(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "removing dtr block of reception for"
+ " %p\n", Extension->Controller);
+ Extension->RXHolding &= ~SERIAL_RX_DTR;
+
+ }
+
+ SerialClrDTR(Extension->WdfInterrupt, Extension);
+
+ }
+
+ }
+
+ //
+ // Time to take care of the RTS Flow control.
+ //
+ // First we only do work if something has changed.
+ //
+
+ if ((!Extension->DeviceIsOpened) ||
+ ((Extension->HandFlow.FlowReplace & SERIAL_RTS_MASK) !=
+ (New.FlowReplace & SERIAL_RTS_MASK))) {
+
+ SerialDbgPrintEx(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "Processing RTS flow %p\n",
+ Extension->Controller);
+
+ if ((New.FlowReplace & SERIAL_RTS_MASK) ==
+ SERIAL_RTS_HANDSHAKE) {
+
+ //
+ // Well we might want to set RTS.
+ //
+ // Before we do, we need to check whether we are doing
+ // rts flow control. If we are then we need to check
+ // if then number of characters in the interrupt buffer
+ // exceeds the XoffLimit. If it does then we don't
+ // enable RTS AND we set the RXHolding to record that
+ // we are holding because of the rts.
+ //
+
+ if ((Extension->BufferSize - New.XoffLimit) >
+ Extension->CharsInInterruptBuffer) {
+
+ //
+ // However if we are already holding we don't want
+ // to turn it back on unless we exceed the Xon
+ // limit.
+ //
+
+ if (Extension->RXHolding & SERIAL_RX_RTS) {
+
+ //
+ // We can assume that its RTS line is already low.
+ //
+
+ if (Extension->CharsInInterruptBuffer >
+ (ULONG)New.XonLimit) {
+
+ SerialDbgPrintEx(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "Removing rts block of "
+ "reception for %p\n",
+ Extension->Controller);
+ Extension->RXHolding &= ~SERIAL_RX_RTS;
+ SerialSetRTS(Extension->WdfInterrupt, Extension);
+
+ }
+
+ } else {
+
+ SerialSetRTS(Extension->WdfInterrupt, Extension);
+
+ }
+
+ } else {
+
+ SerialDbgPrintEx(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "Setting rts block of reception for "
+ "%p\n", Extension->Controller);
+ Extension->RXHolding |= SERIAL_RX_RTS;
+ SerialClrRTS(Extension->WdfInterrupt, Extension);
+
+ }
+
+ } else if ((New.FlowReplace & SERIAL_RTS_MASK) ==
+ SERIAL_RTS_CONTROL) {
+
+ //
+ // Note that if we aren't currently doing rts flow control then
+ // we MIGHT have been. So even if we aren't currently doing
+ // RTS flow control, we should still check if RX is holding
+ // because of RTS. If it is, then we should clear the holding
+ // of this bit.
+ //
+
+ if (Extension->RXHolding & SERIAL_RX_RTS) {
+
+ SerialDbgPrintEx(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "Clearing rts block of reception for "
+ "%p\n", Extension->Controller);
+ Extension->RXHolding &= ~SERIAL_RX_RTS;
+
+ }
+
+ SerialSetRTS(Extension->WdfInterrupt, Extension);
+
+ } else if ((New.FlowReplace & SERIAL_RTS_MASK) ==
+ SERIAL_TRANSMIT_TOGGLE) {
+
+ //
+ // We first need to check whether reception is being held
+ // up because of previous RTS flow control. If it is then
+ // we should clear that reason in the RXHolding mask.
+ //
+
+ if (Extension->RXHolding & SERIAL_RX_RTS) {
+
+ SerialDbgPrintEx(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "TOGGLE Clearing rts block of "
+ "reception for %p\n", Extension->Controller);
+ Extension->RXHolding &= ~SERIAL_RX_RTS;
+
+ }
+
+ //
+ // We have to place the rts value into the Extension
+ // now so that the code that tests whether the
+ // rts line should be lowered will find that we
+ // are "still" doing transmit toggling. The code
+ // for lowering can be invoked later by a timer so
+ // it has to test whether it still needs to do its
+ // work.
+ //
+
+ Extension->HandFlow.FlowReplace &= ~SERIAL_RTS_MASK;
+ Extension->HandFlow.FlowReplace |= SERIAL_TRANSMIT_TOGGLE;
+
+ //
+ // The order of the tests is very important below.
+ //
+ // If there is a break then we should turn on the RTS.
+ //
+ // If there isn't a break but there are characters in
+ // the hardware, then turn on the RTS.
+ //
+ // If there are writes pending that aren't being held
+ // up, then turn on the RTS.
+ //
+
+ if ((Extension->TXHolding & SERIAL_TX_BREAK) ||
+ ((SerialProcessLSR(Extension) & (SERIAL_LSR_THRE |
+ SERIAL_LSR_TEMT)) !=
+ (SERIAL_LSR_THRE |
+ SERIAL_LSR_TEMT)) ||
+ (Extension->CurrentWriteRequest || Extension->TransmitImmediate ||
+ (!IsQueueEmpty(Extension->WriteQueue)) &&
+ (!Extension->TXHolding))) {
+
+ SerialSetRTS(Extension->WdfInterrupt, Extension);
+
+ } else {
+
+ //
+ // This routine will check to see if it is time
+ // to lower the RTS because of transmit toggle
+ // being on. If it is ok to lower it, it will,
+ // if it isn't ok, it will schedule things so
+ // that it will get lowered later.
+ //
+
+ Extension->CountOfTryingToLowerRTS++;
+ SerialPerhapsLowerRTS(Extension->WdfInterrupt, Extension);
+
+ }
+
+ } else {
+
+ //
+ // The end result here will be that RTS is cleared.
+ //
+ // We first need to check whether reception is being held
+ // up because of previous RTS flow control. If it is then
+ // we should clear that reason in the RXHolding mask.
+ //
+
+ if (Extension->RXHolding & SERIAL_RX_RTS) {
+
+ SerialDbgPrintEx(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "Clearing rts block of reception for"
+ " %p\n", Extension->Controller);
+ Extension->RXHolding &= ~SERIAL_RX_RTS;
+
+ }
+
+ SerialClrRTS(Extension->WdfInterrupt, Extension);
+
+ }
+
+ }
+
+ //
+ // We now take care of automatic receive flow control.
+ // We only do work if things have changed.
+ //
+
+ if ((!Extension->DeviceIsOpened) ||
+ ((Extension->HandFlow.FlowReplace & SERIAL_AUTO_RECEIVE) !=
+ (New.FlowReplace & SERIAL_AUTO_RECEIVE))) {
+
+ if (New.FlowReplace & SERIAL_AUTO_RECEIVE) {
+
+ //
+ // We wouldn't be here if it had been on before.
+ //
+ // We should check to see whether we exceed the turn
+ // off limits.
+ //
+ // Note that since we are following the OS/2 flow
+ // control rules we will never send an xon if
+ // when enabling xon/xoff flow control we discover that
+ // we could receive characters but we are held up do
+ // to a previous Xoff.
+ //
+
+ if ((Extension->BufferSize - New.XoffLimit) <=
+ Extension->CharsInInterruptBuffer) {
+
+ //
+ // Cause the Xoff to be sent.
+ //
+
+ Extension->RXHolding |= SERIAL_RX_XOFF;
+
+ SerialProdXonXoff(
+ Extension,
+ FALSE
+ );
+
+ }
+
+ } else {
+
+ //
+ // The app has disabled automatic receive flow control.
+ //
+ // If transmission was being held up because of
+ // an automatic receive Xoff, then we should
+ // cause an Xon to be sent.
+ //
+
+ if (Extension->RXHolding & SERIAL_RX_XOFF) {
+
+ Extension->RXHolding &= ~SERIAL_RX_XOFF;
+
+ //
+ // Cause the Xon to be sent.
+ //
+
+ SerialProdXonXoff(
+ Extension,
+ TRUE
+ );
+
+ }
+
+ }
+
+ }
+
+ //
+ // We now take care of automatic transmit flow control.
+ // We only do work if things have changed.
+ //
+
+ if ((!Extension->DeviceIsOpened) ||
+ ((Extension->HandFlow.FlowReplace & SERIAL_AUTO_TRANSMIT) !=
+ (New.FlowReplace & SERIAL_AUTO_TRANSMIT))) {
+
+ if (New.FlowReplace & SERIAL_AUTO_TRANSMIT) {
+
+ //
+ // We wouldn't be here if it had been on before.
+ //
+ // There is some belief that if autotransmit
+ // was just enabled, I should go look in what we
+ // already received, and if we find the xoff character
+ // then we should stop transmitting. I think this
+ // is an application bug. For now we just care about
+ // what we see in the future.
+ //
+
+ ;
+
+ } else {
+
+ //
+ // The app has disabled automatic transmit flow control.
+ //
+ // If transmission was being held up because of
+ // an automatic transmit Xoff, then we should
+ // cause an Xon to be sent.
+ //
+
+ if (Extension->TXHolding & SERIAL_TX_XOFF) {
+
+ Extension->TXHolding &= ~SERIAL_TX_XOFF;
+
+ //
+ // Cause the Xon to be sent.
+ //
+
+ SerialProdXonXoff(
+ Extension,
+ TRUE
+ );
+
+ }
+
+ }
+
+ }
+
+ //
+ // At this point we can simply make sure that entire
+ // handflow structure in the extension is updated.
+ //
+
+ Extension->HandFlow = New;
+
+ return FALSE;
+
+}
+
+BOOLEAN
+SerialSetHandFlow(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to set the handshake and control
+ flow in the device extension.
+
+Arguments:
+
+ Context - Pointer to a structure that contains a pointer to
+ the device extension and a pointer to a handflow
+ structure..
+
+Return Value:
+
+ This routine always returns FALSE.
+
+--*/
+
+{
+
+ PSERIAL_IOCTL_SYNC S = Context;
+ PSERIAL_DEVICE_EXTENSION Extension = S->Extension;
+ PSERIAL_HANDFLOW HandFlow = S->Data;
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ SerialSetupNewHandFlow(
+ Extension,
+ HandFlow
+ );
+
+ SerialHandleModemUpdate(
+ Extension,
+ FALSE
+ );
+
+ return FALSE;
+
+}
+
+BOOLEAN
+SerialTurnOnBreak(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This routine will turn on break in the hardware and
+ record the fact the break is on, in the extension variable
+ that holds reasons that transmission is stopped.
+
+Arguments:
+
+ Context - Really a pointer to the device extension.
+
+Return Value:
+
+ This routine always returns FALSE.
+
+--*/
+
+{
+
+ PSERIAL_DEVICE_EXTENSION Extension = Context;
+
+ UCHAR OldLineControl;
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ if ((Extension->HandFlow.FlowReplace & SERIAL_RTS_MASK) ==
+ SERIAL_TRANSMIT_TOGGLE) {
+
+ SerialSetRTS(Extension->WdfInterrupt, Extension);
+
+ }
+
+ OldLineControl = READ_LINE_CONTROL(Extension, Extension->Controller);
+
+ OldLineControl |= SERIAL_LCR_BREAK;
+
+ WRITE_LINE_CONTROL(Extension,
+ Extension->Controller,
+ OldLineControl
+ );
+
+ Extension->TXHolding |= SERIAL_TX_BREAK;
+
+ return FALSE;
+
+}
+
+BOOLEAN
+SerialTurnOffBreak(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This routine will turn off break in the hardware and
+ record the fact the break is off, in the extension variable
+ that holds reasons that transmission is stopped.
+
+Arguments:
+
+ Context - Really a pointer to the device extension.
+
+Return Value:
+
+ This routine always returns FALSE.
+
+--*/
+
+{
+
+ PSERIAL_DEVICE_EXTENSION Extension = Context;
+
+ UCHAR OldLineControl;
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ if (Extension->TXHolding & SERIAL_TX_BREAK) {
+
+ //
+ // We actually have a good reason for testing if transmission
+ // is holding instead of blindly clearing the bit.
+ //
+ // If transmission actually was holding and the result of
+ // clearing the bit is that we should restart transmission
+ // then we will poke the interrupt enable bit, which will
+ // cause an actual interrupt and transmission will then
+ // restart on its own.
+ //
+ // If transmission wasn't holding and we poked the bit
+ // then we would interrupt before a character actually made
+ // it out and we could end up over writing a character in
+ // the transmission hardware.
+
+ OldLineControl = READ_LINE_CONTROL(Extension, Extension->Controller);
+
+ OldLineControl &= ~SERIAL_LCR_BREAK;
+
+ WRITE_LINE_CONTROL(Extension,
+ Extension->Controller,
+ OldLineControl
+ );
+
+ Extension->TXHolding &= ~SERIAL_TX_BREAK;
+
+ if (!Extension->TXHolding &&
+ (Extension->TransmitImmediate ||
+ Extension->WriteLength) &&
+ Extension->HoldingEmpty) {
+
+ DISABLE_ALL_INTERRUPTS(Extension, Extension->Controller);
+ ENABLE_ALL_INTERRUPTS(Extension, Extension->Controller);
+
+ } else {
+
+ //
+ // The following routine will lower the rts if we
+ // are doing transmit toggleing and there is no
+ // reason to keep it up.
+ //
+
+ Extension->CountOfTryingToLowerRTS++;
+ SerialPerhapsLowerRTS(Extension->WdfInterrupt, Extension);
+
+ }
+
+ }
+
+ return FALSE;
+
+}
+
+BOOLEAN
+SerialPretendXoff(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to process the Ioctl that request the
+ driver to act as if an Xoff was received. Even if the
+ driver does not have automatic Xoff/Xon flowcontrol - This
+ still will stop the transmission. This is the OS/2 behavior
+ and is not well specified for Windows. Therefore we adopt
+ the OS/2 behavior.
+
+ Note: If the driver does not have automatic Xoff/Xon enabled
+ then the only way to restart transmission is for the
+ application to request we "act" as if we saw the xon.
+
+Arguments:
+
+ Context - Really a pointer to the device extension.
+
+Return Value:
+
+ This routine always returns FALSE.
+
+--*/
+
+{
+
+ PSERIAL_DEVICE_EXTENSION Extension = Context;
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ Extension->TXHolding |= SERIAL_TX_XOFF;
+
+ if ((Extension->HandFlow.FlowReplace & SERIAL_RTS_MASK) ==
+ SERIAL_TRANSMIT_TOGGLE) {
+
+ SerialInsertQueueDpc(
+ Extension->StartTimerLowerRTSDpc
+ )?Extension->CountOfTryingToLowerRTS++:0;
+
+ }
+
+ return FALSE;
+
+}
+
+BOOLEAN
+SerialPretendXon(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to process the Ioctl that request the
+ driver to act as if an Xon was received.
+
+ Note: If the driver does not have automatic Xoff/Xon enabled
+ then the only way to restart transmission is for the
+ application to request we "act" as if we saw the xon.
+
+Arguments:
+
+ Context - Really a pointer to the device extension.
+
+Return Value:
+
+ This routine always returns FALSE.
+
+--*/
+
+{
+
+ PSERIAL_DEVICE_EXTENSION Extension = Context;
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ if (Extension->TXHolding) {
+
+ //
+ // We actually have a good reason for testing if transmission
+ // is holding instead of blindly clearing the bit.
+ //
+ // If transmission actually was holding and the result of
+ // clearing the bit is that we should restart transmission
+ // then we will poke the interrupt enable bit, which will
+ // cause an actual interrupt and transmission will then
+ // restart on its own.
+ //
+ // If transmission wasn't holding and we poked the bit
+ // then we would interrupt before a character actually made
+ // it out and we could end up over writing a character in
+ // the transmission hardware.
+
+ Extension->TXHolding &= ~SERIAL_TX_XOFF;
+
+ if (!Extension->TXHolding &&
+ (Extension->TransmitImmediate ||
+ Extension->WriteLength) &&
+ Extension->HoldingEmpty) {
+
+ DISABLE_ALL_INTERRUPTS(Extension, Extension->Controller);
+ ENABLE_ALL_INTERRUPTS(Extension, Extension->Controller);
+
+ }
+
+ }
+
+ return FALSE;
+
+}
+
+VOID
+SerialHandleReducedIntBuffer(
+ IN PSERIAL_DEVICE_EXTENSION Extension
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called to handle a reduction in the number
+ of characters in the interrupt (typeahead) buffer. It
+ will check the current output flow control and re-enable transmission
+ as needed.
+
+ NOTE: This routine assumes that it is working at interrupt level.
+
+Arguments:
+
+ Extension - A pointer to the device extension.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+
+ //
+ // If we are doing receive side flow control and we are
+ // currently "holding" then because we've emptied out
+ // some characters from the interrupt buffer we need to
+ // see if we can "re-enable" reception.
+ //
+
+ if (Extension->RXHolding) {
+
+ if (Extension->CharsInInterruptBuffer <=
+ (ULONG)Extension->HandFlow.XonLimit) {
+
+ if (Extension->RXHolding & SERIAL_RX_DTR) {
+
+ Extension->RXHolding &= ~SERIAL_RX_DTR;
+ SerialSetDTR(Extension->WdfInterrupt, Extension);
+
+ }
+
+ if (Extension->RXHolding & SERIAL_RX_RTS) {
+
+ Extension->RXHolding &= ~SERIAL_RX_RTS;
+ SerialSetRTS(Extension->WdfInterrupt, Extension);
+
+ }
+
+ if (Extension->RXHolding & SERIAL_RX_XOFF) {
+
+ //
+ // Prod the transmit code to send xon.
+ //
+
+ SerialProdXonXoff(
+ Extension,
+ TRUE
+ );
+
+ }
+
+ }
+
+ }
+
+}
+
+VOID
+SerialProdXonXoff(
+ IN PSERIAL_DEVICE_EXTENSION Extension,
+ IN BOOLEAN SendXon
+ )
+
+/*++
+
+Routine Description:
+
+ This routine will set up the SendXxxxChar variables if
+ necessary and determine if we are going to be interrupting
+ because of current transmission state. It will cause an
+ interrupt to occur if neccessary, to send the xon/xoff char.
+
+ NOTE: This routine assumes that it is called at interrupt
+ level.
+
+Arguments:
+
+ Extension - A pointer to the serial device extension.
+
+ SendXon - If a character is to be send, this indicates whether
+ it should be an Xon or an Xoff.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ //
+ // We assume that if the prodding is called more than
+ // once that the last prod has set things up appropriately.
+ //
+ // We could get called before the character is sent out
+ // because the send of the character was blocked because
+ // of hardware flow control (or break).
+ //
+
+ if (!Extension->SendXonChar && !Extension->SendXoffChar
+ && Extension->HoldingEmpty) {
+
+ DISABLE_ALL_INTERRUPTS(Extension, Extension->Controller);
+ ENABLE_ALL_INTERRUPTS(Extension, Extension->Controller);
+
+ }
+
+ if (SendXon) {
+
+ Extension->SendXonChar = TRUE;
+ Extension->SendXoffChar = FALSE;
+
+ } else {
+
+ Extension->SendXonChar = FALSE;
+ Extension->SendXoffChar = TRUE;
+
+ }
+
+}
+
+ULONG
+SerialHandleModemUpdate(
+ IN PSERIAL_DEVICE_EXTENSION Extension,
+ IN BOOLEAN DoingTX
+ )
+
+/*++
+
+Routine Description:
+
+ This routine will be to check on the modem status, and
+ handle any appropriate event notification as well as
+ any flow control appropriate to modem status lines.
+
+ NOTE: This routine assumes that it is called at interrupt
+ level.
+
+Arguments:
+
+ Extension - A pointer to the serial device extension.
+
+ DoingTX - This boolean is used to indicate that this call
+ came from the transmit processing code. If this
+ is true then there is no need to cause a new interrupt
+ since the code will be trying to send the next
+ character as soon as this call finishes.
+
+Return Value:
+
+ This returns the old value of the modem status register
+ (extended into a ULONG).
+
+--*/
+
+{
+
+ //
+ // We keep this local so that after we are done
+ // examining the modem status and we've updated
+ // the transmission holding value, we know whether
+ // we've changed from needing to hold up transmission
+ // to transmission being able to proceed.
+ //
+ ULONG OldTXHolding = Extension->TXHolding;
+
+ //
+ // Holds the value in the mode status register.
+ //
+ UCHAR ModemStatus;
+ PREQUEST_CONTEXT reqContext;
+
+ ModemStatus =
+ READ_MODEM_STATUS(Extension, Extension->Controller);
+
+
+ //
+ // If we are placeing the modem status into the data stream
+ // on every change, we should do it now.
+ //
+
+ if (Extension->EscapeChar) {
+
+ if (ModemStatus & (SERIAL_MSR_DCTS |
+ SERIAL_MSR_DDSR |
+ SERIAL_MSR_TERI |
+ SERIAL_MSR_DDCD)) {
+
+ SerialPutChar(
+ Extension,
+ Extension->EscapeChar
+ );
+ SerialPutChar(
+ Extension,
+ SERIAL_LSRMST_MST
+ );
+ SerialPutChar(
+ Extension,
+ ModemStatus
+ );
+
+ }
+
+ }
+
+
+ //
+ // Take care of input flow control based on sensitivity
+ // to the DSR. This is done so that the application won't
+ // see spurious data generated by odd devices.
+ //
+ // Basically, if we are doing dsr sensitivity then the
+ // driver should only accept data when the dsr bit is
+ // set.
+ //
+
+ if (Extension->HandFlow.ControlHandShake & SERIAL_DSR_SENSITIVITY) {
+
+ if (ModemStatus & SERIAL_MSR_DSR) {
+
+ //
+ // The line is high. Simply make sure that
+ // RXHolding does't have the DSR bit.
+ //
+
+ Extension->RXHolding &= ~SERIAL_RX_DSR;
+
+ } else {
+
+ Extension->RXHolding |= SERIAL_RX_DSR;
+
+ }
+
+ } else {
+
+ //
+ // We don't have sensitivity due to DSR. Make sure we
+ // arn't holding. (We might have been, but the app just
+ // asked that we don't hold for this reason any more.)
+ //
+
+ Extension->RXHolding &= ~SERIAL_RX_DSR;
+
+ }
+
+ //
+ // Check to see if we have a wait
+ // pending on the modem status events. If we
+ // do then we schedule a dpc to satisfy
+ // that wait.
+ //
+
+ if (Extension->IsrWaitMask) {
+
+ if ((Extension->IsrWaitMask & SERIAL_EV_CTS) &&
+ (ModemStatus & SERIAL_MSR_DCTS)) {
+
+ Extension->HistoryMask |= SERIAL_EV_CTS;
+
+ }
+
+ if ((Extension->IsrWaitMask & SERIAL_EV_DSR) &&
+ (ModemStatus & SERIAL_MSR_DDSR)) {
+
+ Extension->HistoryMask |= SERIAL_EV_DSR;
+
+ }
+
+ if ((Extension->IsrWaitMask & SERIAL_EV_RING) &&
+ (ModemStatus & SERIAL_MSR_TERI)) {
+
+ Extension->HistoryMask |= SERIAL_EV_RING;
+
+ }
+
+ if ((Extension->IsrWaitMask & SERIAL_EV_RLSD) &&
+ (ModemStatus & SERIAL_MSR_DDCD)) {
+
+ Extension->HistoryMask |= SERIAL_EV_RLSD;
+
+ }
+
+ if (Extension->IrpMaskLocation &&
+ Extension->HistoryMask) {
+
+ *Extension->IrpMaskLocation =
+ Extension->HistoryMask;
+ Extension->IrpMaskLocation = NULL;
+ Extension->HistoryMask = 0;
+
+ reqContext = SerialGetRequestContext(Extension->CurrentWaitRequest);
+ reqContext->Information = sizeof(ULONG);
+ SerialInsertQueueDpc(
+ Extension->CommWaitDpc
+ );
+
+ }
+
+ }
+
+ //
+ // If the app has modem line flow control then
+ // we check to see if we have to hold up transmission.
+ //
+
+ if (Extension->HandFlow.ControlHandShake &
+ SERIAL_OUT_HANDSHAKEMASK) {
+
+ if (Extension->HandFlow.ControlHandShake &
+ SERIAL_CTS_HANDSHAKE) {
+
+ if (ModemStatus & SERIAL_MSR_CTS) {
+
+ Extension->TXHolding &= ~SERIAL_TX_CTS;
+
+ } else {
+
+ Extension->TXHolding |= SERIAL_TX_CTS;
+
+ }
+
+ } else {
+
+ Extension->TXHolding &= ~SERIAL_TX_CTS;
+
+ }
+
+ if (Extension->HandFlow.ControlHandShake &
+ SERIAL_DSR_HANDSHAKE) {
+
+ if (ModemStatus & SERIAL_MSR_DSR) {
+
+ Extension->TXHolding &= ~SERIAL_TX_DSR;
+
+ } else {
+
+ Extension->TXHolding |= SERIAL_TX_DSR;
+
+ }
+
+ } else {
+
+ Extension->TXHolding &= ~SERIAL_TX_DSR;
+
+ }
+
+ if (Extension->HandFlow.ControlHandShake &
+ SERIAL_DCD_HANDSHAKE) {
+
+ if (ModemStatus & SERIAL_MSR_DCD) {
+
+ Extension->TXHolding &= ~SERIAL_TX_DCD;
+
+ } else {
+
+ Extension->TXHolding |= SERIAL_TX_DCD;
+
+ }
+
+ } else {
+
+ Extension->TXHolding &= ~SERIAL_TX_DCD;
+
+ }
+
+ //
+ // If we hadn't been holding, and now we are then
+ // queue off a dpc that will lower the RTS line
+ // if we are doing transmit toggling.
+ //
+
+ if (!OldTXHolding && Extension->TXHolding &&
+ ((Extension->HandFlow.FlowReplace & SERIAL_RTS_MASK) ==
+ SERIAL_TRANSMIT_TOGGLE)) {
+
+ SerialInsertQueueDpc(
+ Extension->StartTimerLowerRTSDpc
+ )?Extension->CountOfTryingToLowerRTS++:0;
+
+ }
+
+ //
+ // We've done any adjusting that needed to be
+ // done to the holding mask given updates
+ // to the modem status. If the Holding mask
+ // is clear (and it wasn't clear to start)
+ // and we have "write" work to do set things
+ // up so that the transmission code gets invoked.
+ //
+
+ if (!DoingTX && OldTXHolding && !Extension->TXHolding) {
+
+ if (!Extension->TXHolding &&
+ (Extension->TransmitImmediate ||
+ Extension->WriteLength) &&
+ Extension->HoldingEmpty) {
+
+ DISABLE_ALL_INTERRUPTS(Extension, Extension->Controller);
+ ENABLE_ALL_INTERRUPTS(Extension, Extension->Controller);
+ }
+
+ }
+
+ } else {
+
+ //
+ // We need to check if transmission is holding
+ // up because of modem status lines. What
+ // could have occured is that for some strange
+ // reason, the app has asked that we no longer
+ // stop doing output flow control based on
+ // the modem status lines. If however, we
+ // *had* been held up because of the status lines
+ // then we need to clear up those reasons.
+ //
+
+ if (Extension->TXHolding & (SERIAL_TX_DCD |
+ SERIAL_TX_DSR |
+ SERIAL_TX_CTS)) {
+
+ Extension->TXHolding &= ~(SERIAL_TX_DCD |
+ SERIAL_TX_DSR |
+ SERIAL_TX_CTS);
+
+
+ if (!DoingTX && OldTXHolding && !Extension->TXHolding) {
+
+ if (!Extension->TXHolding &&
+ (Extension->TransmitImmediate ||
+ Extension->WriteLength) &&
+ Extension->HoldingEmpty) {
+
+ DISABLE_ALL_INTERRUPTS(Extension, Extension->Controller);
+ ENABLE_ALL_INTERRUPTS(Extension, Extension->Controller);
+ }
+
+ }
+
+ }
+
+ }
+
+ return ((ULONG)ModemStatus);
+}
+
+BOOLEAN
+SerialPerhapsLowerRTS(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This routine checks that the software reasons for lowering
+ the RTS lines are present. If so, it will then cause the
+ line status register to be read (and any needed processing
+ implied by the status register to be done), and if the
+ shift register is empty it will lower the line. If the
+ shift register isn't empty, this routine will queue off
+ a dpc that will start a timer, that will basically call
+ us back to try again.
+
+ NOTE: This routine assumes that it is called at interrupt
+ level.
+
+Arguments:
+
+ Context - Really a pointer to the device extension.
+
+Return Value:
+
+ Always FALSE.
+
+--*/
+
+{
+
+ PSERIAL_DEVICE_EXTENSION Extension = Context;
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ //
+ // We first need to test if we are actually still doing
+ // transmit toggle flow control. If we aren't then
+ // we have no reason to try be here.
+ //
+
+ if ((Extension->HandFlow.FlowReplace & SERIAL_RTS_MASK) ==
+ SERIAL_TRANSMIT_TOGGLE) {
+
+ //
+ // The order of the tests is very important below.
+ //
+ // If there is a break then we should leave on the RTS,
+ // because when the break is turned off, it will submit
+ // the code to shut down the RTS.
+ //
+ // If there are writes pending that aren't being held
+ // up, then leave on the RTS, because the end of the write
+ // code will cause this code to be reinvoked. If the writes
+ // are being held up, its ok to lower the RTS because the
+ // upon trying to write the first character after transmission
+ // is restarted, we will raise the RTS line.
+ //
+
+ if ((Extension->TXHolding & SERIAL_TX_BREAK) ||
+ (Extension->CurrentWriteRequest || Extension->TransmitImmediate ||
+ (!IsQueueEmpty(Extension->WriteQueue)) &&
+ (!Extension->TXHolding))) {
+
+ NOTHING;
+
+ } else {
+
+ //
+ // Looks good so far. Call the line status check and processing
+ // code, it will return the "current" line status value. If
+ // the holding and shift register are clear, lower the RTS line,
+ // if they aren't clear, queue of a dpc that will cause a timer
+ // to reinvoke us later. We do this code here because no one
+ // but this routine cares about the characters in the hardware,
+ // so no routine by this routine will bother invoking to test
+ // if the hardware is empty.
+ //
+
+ if ((SerialProcessLSR(Extension) &
+ (SERIAL_LSR_THRE | SERIAL_LSR_TEMT)) !=
+ (SERIAL_LSR_THRE | SERIAL_LSR_TEMT)) {
+
+ //
+ // Well it's not empty, try again later.
+ //
+
+ SerialInsertQueueDpc(
+ Extension->StartTimerLowerRTSDpc
+ )?Extension->CountOfTryingToLowerRTS++:0;
+
+
+ } else {
+
+ //
+ // Nothing in the hardware, Lower the RTS.
+ //
+
+ SerialClrRTS(Extension->WdfInterrupt, Extension);
+
+
+ }
+
+ }
+
+ }
+
+ //
+ // We decement the counter to indicate that we've reached
+ // the end of the execution path that is trying to push
+ // down the RTS line.
+ //
+
+ Extension->CountOfTryingToLowerRTS--;
+
+ return FALSE;
+}
+
+VOID
+SerialStartTimerLowerRTS(
+ IN WDFDPC Dpc
+ )
+
+/*++
+
+Routine Description:
+
+ This routine starts a timer that when it expires will start
+ a dpc that will check if it can lower the rts line because
+ there are no characters in the hardware.
+
+Arguments:
+
+ Dpc - Not Used.
+
+ DeferredContext - Really points to the device extension.
+
+ SystemContext1 - Not Used.
+
+ SystemContext2 - Not Used.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ LARGE_INTEGER CharTime;
+ PSERIAL_DEVICE_EXTENSION Extension = NULL;
+
+ Extension = SerialGetDeviceExtension(WdfDpcGetParentObject(Dpc));
+
+
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_IOCTLS, ">SerialStartTimerLowerRTS(%p)\n",
+ Extension);
+
+
+ //
+ // Since all the callbacks into the driver are serialized, we don't have
+ // synchronize the access to any of the Extension variables.
+ //
+
+ CharTime = SerialGetCharTime(Extension);
+
+ CharTime.QuadPart = -CharTime.QuadPart;
+
+ if (SerialSetTimer(
+ Extension->LowerRTSTimer,
+ CharTime
+ )) {
+
+ //
+ // The timer was already in the timer queue. This implies
+ // that one path of execution that was trying to lower
+ // the RTS has "died". Synchronize with the ISR so that
+ // we can lower the count.
+ //
+
+ WdfInterruptSynchronize(
+ Extension->WdfInterrupt,
+ SerialDecrementRTSCounter,
+ Extension
+ );
+
+ }
+
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_IOCTLS, "<SerialStartTimerLowerRTS\n");
+
+}
+
+VOID
+SerialInvokePerhapsLowerRTS(
+ IN WDFTIMER Timer
+ )
+
+/*++
+
+Routine Description:
+
+ This dpc routine exists solely to call the code that
+ tests if the rts line should be lowered when TRANSMIT
+ TOGGLE flow control is being used.
+
+Arguments:
+
+ WDFTIMER
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ PSERIAL_DEVICE_EXTENSION Extension = NULL;
+
+ Extension = SerialGetDeviceExtension(WdfTimerGetParentObject(Timer));
+
+ WdfInterruptSynchronize(
+ Extension->WdfInterrupt,
+ SerialPerhapsLowerRTS,
+ Extension
+ );
+
+}
+
+BOOLEAN
+SerialDecrementRTSCounter(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This routine checks that the software reasons for lowering
+ the RTS lines are present. If so, it will then cause the
+ line status register to be read (and any needed processing
+ implied by the status register to be done), and if the
+ shift register is empty it will lower the line. If the
+ shift register isn't empty, this routine will queue off
+ a dpc that will start a timer, that will basically call
+ us back to try again.
+
+ NOTE: This routine assumes that it is called at interrupt
+ level.
+
+Arguments:
+
+ Context - Really a pointer to the device extension.
+
+Return Value:
+
+ Always FALSE.
+
+--*/
+
+{
+
+ PSERIAL_DEVICE_EXTENSION Extension = Context;
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ Extension->CountOfTryingToLowerRTS--;
+
+ return FALSE;
+
+}
+
+
diff --git a/tests/projects/wdk/kmdf/serial/openclos.c b/tests/projects/wdk/kmdf/serial/openclos.c
new file mode 100644
index 000000000..477f07ac1
--- /dev/null
+++ b/tests/projects/wdk/kmdf/serial/openclos.c
@@ -0,0 +1,850 @@
+/*++
+
+Copyright (c) Microsoft Corporation
+
+Module Name:
+
+ openclos.c
+
+Abstract:
+
+ This module contains the code that is very specific to
+ opening, closing, and cleaning up in the serial driver.
+
+Environment:
+
+ Kernel mode
+
+--*/
+
+#include "precomp.h"
+
+#if defined(EVENT_TRACING)
+#include "openclos.tmh"
+#endif
+
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGESER,SerialGetCharTime)
+#pragma alloc_text(PAGESER,SerialEvtFileClose)
+#pragma alloc_text(PAGESER,SerialDrainUART)
+#pragma alloc_text(PAGESRP0,SerialEvtDeviceFileCreate)
+#pragma alloc_text(PAGESRP0,SerialCreateTimersAndDpcs)
+#endif // ALLOC_PRAGMA
+
+
+
+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;
+
+}
+
+
+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;
+}
+
+
+NTSTATUS
+SerialDeviceFileCreateWorker (
+ IN WDFDEVICE Device
+ )
+{
+ NTSTATUS status;
+ PSERIAL_DEVICE_EXTENSION extension = SerialGetDeviceExtension (Device);
+
+ //
+ // Create a buffer for the RX data when no reads are outstanding.
+ //
+
+ extension->InterruptReadBuffer = NULL;
+ extension->BufferSize = 0;
+
+ switch (MmQuerySystemSize()) {
+
+ case MmLargeSystem: {
+
+ extension->BufferSize = 4096;
+ extension->InterruptReadBuffer = ExAllocatePoolWithTag(
+ NonPagedPoolNx,
+ extension->BufferSize,
+ POOL_TAG
+ );
+
+ if (extension->InterruptReadBuffer) {
+ break;
+ }
+
+ }
+
+ case MmMediumSystem: {
+
+ extension->BufferSize = 1024;
+ extension->InterruptReadBuffer = ExAllocatePoolWithTag(
+ NonPagedPoolNx,
+ extension->BufferSize,
+ POOL_TAG
+ );
+
+ if (extension->InterruptReadBuffer) {
+ break;
+ }
+
+ }
+
+ case MmSmallSystem: {
+
+ extension->BufferSize = 128;
+ extension->InterruptReadBuffer = ExAllocatePoolWithTag(
+ NonPagedPoolNx,
+ extension->BufferSize,
+ POOL_TAG
+ );
+
+ }
+
+ }
+
+ if (!extension->InterruptReadBuffer) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ //
+ // By taking a power reference by calling WdfDeviceStopIdle, we prevent the
+ // framework from powering down our device due to idle timeout when there
+ // is an open handle. Power reference also moves the device to D0 if we are
+ // idled out. If you fail create anywhere later in this routine, do make sure
+ // drop the reference.
+ //
+ status = WdfDeviceStopIdle(Device, TRUE);
+ if (!NT_SUCCESS(status)) {
+ return status;
+ }
+
+ //
+ // wakeup is not currently enabled
+ //
+
+ extension->IsWakeEnabled = FALSE;
+
+ //
+ // On a new open we "flush" the read queue by initializing the
+ // count of characters.
+ //
+
+ extension->CharsInInterruptBuffer = 0;
+ extension->LastCharSlot = extension->InterruptReadBuffer +
+ (extension->BufferSize - 1);
+
+ extension->ReadBufferBase = extension->InterruptReadBuffer;
+ extension->CurrentCharSlot = extension->InterruptReadBuffer;
+ extension->FirstReadableChar = extension->InterruptReadBuffer;
+
+ extension->TotalCharsQueued = 0;
+
+ //
+ // We set up the default xon/xoff limits.
+ //
+
+ extension->HandFlow.XoffLimit = extension->BufferSize >> 3;
+ extension->HandFlow.XonLimit = extension->BufferSize >> 1;
+
+ extension->WmiCommData.XoffXmitThreshold = extension->HandFlow.XoffLimit;
+ extension->WmiCommData.XonXmitThreshold = extension->HandFlow.XonLimit;
+
+ extension->BufferSizePt8 = ((3*(extension->BufferSize>>2))+
+ (extension->BufferSize>>4));
+
+ //
+ // Mark the device as busy for WMI
+ //
+
+ extension->WmiCommData.IsBusy = TRUE;
+
+ extension->IrpMaskLocation = NULL;
+ extension->HistoryMask = 0;
+ extension->IsrWaitMask = 0;
+
+ extension->SendXonChar = FALSE;
+ extension->SendXoffChar = FALSE;
+
+#if !DBG
+ //
+ // Clear out the statistics.
+ //
+
+ WdfInterruptSynchronize(
+ extension->WdfInterrupt,
+ SerialClearStats,
+ extension
+ );
+#endif
+
+ //
+ // The escape char replacement must be reset upon every open.
+ //
+
+ extension->EscapeChar = 0;
+
+ //
+ // We don't want the device to be removed or stopped when there is an handle
+ //
+ // 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, FALSE);
+
+ //
+ // Synchronize with the ISR and let it know that the device
+ // has been successfully opened.
+ //
+
+ WdfInterruptSynchronize(
+ extension->WdfInterrupt,
+ SerialMarkOpen,
+ extension
+ );
+
+ return STATUS_SUCCESS;
+
+}
+
+
+VOID
+SerialEvtFileClose(
+ IN WDFFILEOBJECT FileObject
+ )
+
+/*++
+
+ EvtFileClose is called when all the handles represented by the FileObject
+ is closed and all the references to FileObject is removed. This callback
+ may get called in an arbitrary thread context instead of the thread that
+ called CloseHandle. If you want to delete any per FileObject context that
+ must be done in the context of the user thread that made the Create call,
+ you should do that in the EvtDeviceCleanp callback.
+
+Arguments:
+
+ FileObject - Pointer to fileobject that represents the open handle.
+
+Return Value:
+
+ VOID
+
+--*/
+
+{
+ PAGED_CODE();
+
+ SerialFileCloseWorker(WdfFileObjectGetDevice(FileObject));
+ return;
+}
+
+
+NTSTATUS
+SerialWdmFileClose (
+ IN WDFDEVICE Device,
+ IN PIRP Irp
+ )
+/*++
+
+Routine Description:
+
+ This is the dispatch routine for IRP_MJ_CLOSE. This is called when all the
+ handles represented by the FileObject is closed and all the references to
+ the FileObject is removed.
+
+Arguments:
+
+ DeviceObject - Pointer to the device object for this device
+ Irp - Pointer to the IRP for the current request
+
+Return Value:
+
+ NT status code
+
+--*/
+{
+ SerialFileCloseWorker(Device);
+
+ Irp->IoStatus.Information = 0L;
+ Irp->IoStatus.Status = STATUS_SUCCESS;
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+ return STATUS_SUCCESS;
+}
+
+
+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;
+
+}
+
+BOOLEAN
+SerialMarkOpen(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This routine merely sets a boolean to true to mark the fact that
+ somebody opened the device and its worthwhile to pay attention
+ to interrupts.
+
+Arguments:
+
+ Context - Really a pointer to the device extension.
+
+Return Value:
+
+ This routine always returns FALSE.
+
+--*/
+
+{
+
+ PSERIAL_DEVICE_EXTENSION extension = Context;
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ SerialReset(extension->WdfInterrupt, extension);
+
+ //
+ // Prepare for the opening by re-enabling interrupts.
+ //
+ // We do this my modifying the OUT2 line in the modem control.
+ // In PC's this bit is "anded" with the interrupt line.
+ //
+
+ WRITE_MODEM_CONTROL(extension,
+ extension->Controller,
+ (UCHAR)(READ_MODEM_CONTROL(extension, extension->Controller) | SERIAL_MCR_OUT2)
+ );
+
+ extension->DeviceIsOpened = TRUE;
+ extension->ErrorWord = 0;
+
+ return FALSE;
+
+}
+
+VOID
+SerialDrainUART(IN PSERIAL_DEVICE_EXTENSION PDevExt,
+ IN PLARGE_INTEGER PDrainTime)
+{
+ PAGED_CODE();
+
+ //
+ // Wait until all characters have been emptied out of the hardware.
+ //
+
+ while ((READ_LINE_STATUS(PDevExt, PDevExt->Controller) &
+ (SERIAL_LSR_THRE | SERIAL_LSR_TEMT))
+ != (SERIAL_LSR_THRE | SERIAL_LSR_TEMT)) {
+ KeDelayExecutionThread(KernelMode, FALSE, PDrainTime);
+ }
+}
+
+VOID
+SerialDisableUART(IN PVOID Context)
+
+/*++
+
+Routine Description:
+
+ This routine disables the UART and puts it in a "safe" state when
+ not in use (like a close or powerdown).
+
+Arguments:
+
+ Context - Really a pointer to the device extension.
+
+Return Value:
+
+ This routine always returns FALSE.
+
+--*/
+
+{
+ PSERIAL_DEVICE_EXTENSION extension = Context;
+
+ //
+ // Prepare for the closing by stopping interrupts.
+ //
+ // We do this by adjusting the OUT2 line in the modem control.
+ // In PC's this bit is "anded" with the interrupt line.
+ //
+
+ WRITE_MODEM_CONTROL(extension, extension->Controller,
+ (UCHAR)(READ_MODEM_CONTROL(extension, extension->Controller)
+ & ~SERIAL_MCR_OUT2));
+
+ if (extension->FifoPresent) {
+ WRITE_FIFO_CONTROL(extension, extension->Controller, (UCHAR)0);
+ }
+}
+
+
+
+BOOLEAN
+SerialMarkClose(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This routine merely sets a boolean to false to mark the fact that
+ somebody closed the device and it's no longer worthwhile to pay attention
+ to interrupts. It also disables the UART.
+
+Arguments:
+
+ Context - Really a pointer to the device extension.
+
+Return Value:
+
+ This routine always returns FALSE.
+
+--*/
+
+{
+
+ PSERIAL_DEVICE_EXTENSION extension = Context;
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ SerialDisableUART(Context);
+ extension->DeviceIsOpened = FALSE;
+ extension->DeviceState.Reopen = FALSE;
+
+ return FALSE;
+
+}
+
+LARGE_INTEGER
+SerialGetCharTime(
+ IN PSERIAL_DEVICE_EXTENSION Extension
+ )
+
+/*++
+
+Routine Description:
+
+ This function will return the number of 100 nanosecond intervals
+ there are in one character time (based on the present form
+ of flow control.
+
+Arguments:
+
+ Extension - Just what it says.
+
+Return Value:
+
+ 100 nanosecond intervals in a character time.
+
+--*/
+
+{
+ ULONG dataSize = 0;
+ ULONG paritySize;
+ ULONG stopSize;
+ ULONG charTime;
+ ULONG bitTime;
+ LARGE_INTEGER tmp;
+
+ PAGED_CODE();
+
+ if ((Extension->LineControl & SERIAL_DATA_MASK) == SERIAL_5_DATA) {
+ dataSize = 5;
+ } else if ((Extension->LineControl & SERIAL_DATA_MASK)
+ == SERIAL_6_DATA) {
+ dataSize = 6;
+ } else if ((Extension->LineControl & SERIAL_DATA_MASK)
+ == SERIAL_7_DATA) {
+ dataSize = 7;
+ } else if ((Extension->LineControl & SERIAL_DATA_MASK)
+ == SERIAL_8_DATA) {
+ dataSize = 8;
+ }
+
+ paritySize = 1;
+ if ((Extension->LineControl & SERIAL_PARITY_MASK)
+ == SERIAL_NONE_PARITY) {
+
+ paritySize = 0;
+
+ }
+
+ if (Extension->LineControl & SERIAL_2_STOP) {
+
+ //
+ // Even if it is 1.5, for sanities sake were going
+ // to say 2.
+ //
+
+ stopSize = 2;
+
+ } else {
+
+ stopSize = 1;
+
+ }
+
+ //
+ // First we calculate the number of 100 nanosecond intervals
+ // are in a single bit time (Approximately).
+ //
+
+ bitTime = (10000000+(Extension->CurrentBaud-1))/Extension->CurrentBaud;
+ charTime = bitTime + ((dataSize+paritySize+stopSize)*bitTime);
+
+ tmp.QuadPart = charTime;
+ return tmp;
+
+}
+
+
diff --git a/tests/projects/wdk/kmdf/serial/pnp.c b/tests/projects/wdk/kmdf/serial/pnp.c
new file mode 100644
index 000000000..8773f30ac
--- /dev/null
+++ b/tests/projects/wdk/kmdf/serial/pnp.c
@@ -0,0 +1,2804 @@
+/*++
+
+Copyright (c) 1991, 1992, 1993 - 1997 Microsoft Corporation
+
+Module Name:
+
+ pnp.c
+
+Abstract:
+
+ This module contains the code that handles the plug and play
+ IRPs for the serial driver.
+
+Environment:
+
+ Kernel mode
+
+--*/
+
+#include "precomp.h"
+#include <initguid.h>
+#include <ntddser.h>
+#include <stdlib.h>
+
+#if defined(EVENT_TRACING)
+#include "pnp.tmh"
+#endif
+
+static const PHYSICAL_ADDRESS SerialPhysicalZero = {0};
+static const SUPPORTED_BAUD_RATES SupportedBaudRates[] = {
+ {75, SERIAL_BAUD_075},
+ {110, SERIAL_BAUD_110},
+ {135, SERIAL_BAUD_134_5},
+ {150, SERIAL_BAUD_150},
+ {300, SERIAL_BAUD_300},
+ {600, SERIAL_BAUD_600},
+ {1200, SERIAL_BAUD_1200},
+ {1800, SERIAL_BAUD_1800},
+ {2400, SERIAL_BAUD_2400},
+ {4800, SERIAL_BAUD_4800},
+ {7200, SERIAL_BAUD_7200},
+ {9600, SERIAL_BAUD_9600},
+ {14400, SERIAL_BAUD_14400},
+ {19200, SERIAL_BAUD_19200},
+ {38400, SERIAL_BAUD_38400},
+ {56000, SERIAL_BAUD_56K},
+ {57600, SERIAL_BAUD_57600},
+ {115200, SERIAL_BAUD_115200},
+ {128000, SERIAL_BAUD_128K},
+ {SERIAL_BAUD_INVALID, SERIAL_BAUD_USER}
+ };
+
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGESRP0, SerialEvtDeviceAdd)
+#pragma alloc_text(PAGESRP0, SerialEvtPrepareHardware)
+#pragma alloc_text(PAGESRP0, SerialEvtReleaseHardware)
+#pragma alloc_text(PAGESRP0, SerialEvtDeviceD0ExitPreInterruptsDisabled)
+#pragma alloc_text(PAGESRP0, SerialMapHWResources)
+#pragma alloc_text(PAGESRP0, SerialUnmapHWResources)
+#pragma alloc_text(PAGESRP0, SerialEvtDeviceContextCleanup)
+#pragma alloc_text(PAGESRP0, SerialDoExternalNaming)
+#pragma alloc_text(PAGESRP0, SerialReportMaxBaudRate)
+#pragma alloc_text(PAGESRP0, SerialUndoExternalNaming)
+#pragma alloc_text(PAGESRP0, SerialInitController)
+#pragma alloc_text(PAGESRP0, SerialGetMappedAddress)
+#pragma alloc_text(PAGESRP0, SerialSetPowerPolicy)
+#pragma alloc_text(PAGESRP0, SerialReadSymName)
+
+#endif // ALLOC_PRAGMA
+
+PVOID LocalMmMapIoSpace(
+ _In_ PHYSICAL_ADDRESS PhysicalAddress,
+ _In_ SIZE_T NumberOfBytes
+ )
+{
+ typedef
+ PVOID
+ (*PFN_MM_MAP_IO_SPACE_EX) (
+ _In_ PHYSICAL_ADDRESS PhysicalAddress,
+ _In_ SIZE_T NumberOfBytes,
+ _In_ ULONG Protect
+ );
+
+ UNICODE_STRING name;
+ PFN_MM_MAP_IO_SPACE_EX pMmMapIoSpaceEx;
+
+ RtlInitUnicodeString(&name, L"MmMapIoSpaceEx");
+ pMmMapIoSpaceEx = (PFN_MM_MAP_IO_SPACE_EX) (ULONG_PTR)MmGetSystemRoutineAddress(&name);
+
+ if (pMmMapIoSpaceEx != NULL){
+ //
+ // Call WIN10 API if available
+ //
+ return pMmMapIoSpaceEx(PhysicalAddress,
+ NumberOfBytes,
+ PAGE_READWRITE | PAGE_NOCACHE);
+ }
+
+ //
+ // Supress warning that MmMapIoSpace allocates executable memory.
+ // This function is only used if the preferred API, MmMapIoSpaceEx
+ // is not present. MmMapIoSpaceEx is available starting in WIN10.
+ //
+ #pragma warning(suppress: 30029)
+ return MmMapIoSpace(PhysicalAddress, NumberOfBytes, MmNonCached);
+}
+
+NTSTATUS
+SerialEvtDeviceAdd(
+ IN WDFDRIVER Driver,
+ IN PWDFDEVICE_INIT DeviceInit
+ )
+/*++
+
+Routine Description:
+
+ EvtDeviceAdd is called by the framework in response to AddDevice
+ call from the PnP manager.
+
+
+Arguments:
+
+ Driver - Handle to a framework driver object created in DriverEntry
+
+ DeviceInit - Pointer to a framework-allocated WDFDEVICE_INIT structure.
+
+Return Value:
+
+ NTSTATUS
+
+--*/
+
+{
+ NTSTATUS status;
+ PSERIAL_DEVICE_EXTENSION pDevExt;
+ static ULONG currentInstance = 0;
+ WDF_FILEOBJECT_CONFIG fileobjectConfig;
+ WDFDEVICE device;
+ WDF_PNPPOWER_EVENT_CALLBACKS pnpPowerCallbacks;
+ WDF_OBJECT_ATTRIBUTES attributes;
+ WDF_IO_QUEUE_CONFIG queueConfig;
+ WDFQUEUE defaultqueue;
+ ULONG isMulti;
+ PULONG countSoFar;
+ WDF_INTERRUPT_CONFIG interruptConfig;
+ PSERIAL_INTERRUPT_CONTEXT interruptContext;
+ ULONG relinquishPowerPolicy;
+
+ DECLARE_UNICODE_STRING_SIZE(deviceName, DEVICE_OBJECT_NAME_LENGTH);
+
+ PAGED_CODE();
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_PNP, "-->SerialEvtDeviceAdd\n");
+
+ status = RtlUnicodeStringPrintf(&deviceName, L"%ws%u",
+ L"\\Device\\Serial",
+ currentInstance++);
+
+
+ if (!NT_SUCCESS(status)) {
+ return status;
+ }
+
+ status = WdfDeviceInitAssignName(DeviceInit,& deviceName);
+ if (!NT_SUCCESS(status)) {
+ return status;
+ }
+
+ WdfDeviceInitSetExclusive(DeviceInit, TRUE);
+ WdfDeviceInitSetDeviceType(DeviceInit, FILE_DEVICE_SERIAL_PORT);
+
+ WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, REQUEST_CONTEXT);
+
+ WdfDeviceInitSetRequestAttributes(DeviceInit, &attributes);
+
+ //
+ // Zero out the PnpPowerCallbacks structure.
+ //
+ WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpPowerCallbacks);
+
+ //
+ // Set Callbacks for any of the functions we are interested in.
+ // If no callback is set, Framework will take the default action
+ // by itself. These next two callbacks set up and tear down hardware state,
+ // specifically that which only has to be done once.
+ //
+
+ pnpPowerCallbacks.EvtDevicePrepareHardware = SerialEvtPrepareHardware;
+ pnpPowerCallbacks.EvtDeviceReleaseHardware = SerialEvtReleaseHardware;
+
+ //
+ // These two callbacks set up and tear down hardware state that must be
+ // done every time the device moves in and out of the D0-working state.
+ //
+
+ pnpPowerCallbacks.EvtDeviceD0Entry = SerialEvtDeviceD0Entry;
+ pnpPowerCallbacks.EvtDeviceD0Exit = SerialEvtDeviceD0Exit;
+
+ //
+ // Specify the callback for monitoring when the device's interrupt are
+ // enabled or about to be disabled.
+ //
+
+ pnpPowerCallbacks.EvtDeviceD0EntryPostInterruptsEnabled = SerialEvtDeviceD0EntryPostInterruptsEnabled;
+ pnpPowerCallbacks.EvtDeviceD0ExitPreInterruptsDisabled = SerialEvtDeviceD0ExitPreInterruptsDisabled;
+
+ //
+ // Register the PnP and power callbacks.
+ //
+ WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &pnpPowerCallbacks);
+
+ if ( !NT_SUCCESS(status)) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP,
+ "WdfDeviceInitSetPnpPowerEventCallbacks failed %!STATUS!\n",
+ status);
+ return status;
+ }
+
+ //
+ // Find out if we own power policy
+ //
+ SerialGetFdoRegistryKeyValue( DeviceInit,
+ L"SerialRelinquishPowerPolicy",
+ &relinquishPowerPolicy );
+
+ if(relinquishPowerPolicy) {
+ //
+ // FDO's are assumed to be power policy owner by default. So tell
+ // the framework explicitly to relinquish the power policy ownership.
+ //
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_PNP,
+ "RelinquishPowerPolicy due to registry settings\n");
+
+ WdfDeviceInitSetPowerPolicyOwnership(DeviceInit, FALSE);
+ }
+
+ //
+ // For Windows XP and below, we will register for the WDM Preprocess callback
+ // for IRP_MJ_CREATE. This is done because, the Serenum filter doesn't handle
+ // creates that are marked pending. Since framework always marks the IRP pending,
+ // we are registering this WDM preprocess handler so that we can bypass the
+ // framework and handle the create and close ourself. This workaround is need
+ // only if you intend to install the Serenum as an upper filter.
+ //
+ if (RtlIsNtDdiVersionAvailable(NTDDI_VISTA) == FALSE) {
+
+ status = WdfDeviceInitAssignWdmIrpPreprocessCallback(
+ DeviceInit,
+ SerialWdmDeviceFileCreate,
+ IRP_MJ_CREATE,
+ NULL, // pointer minor function table
+ 0); // number of entries in the table
+
+ if (!NT_SUCCESS(status)) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP,
+ "WdfDeviceInitAssignWdmIrpPreprocessCallback failed %!STATUS!\n",
+ status);
+ return status;
+ }
+
+ status = WdfDeviceInitAssignWdmIrpPreprocessCallback(
+ DeviceInit,
+ SerialWdmFileClose,
+ IRP_MJ_CLOSE,
+ NULL, // pointer minor function table
+ 0); // number of entries in the table
+
+ if (!NT_SUCCESS(status)) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP,
+ "WdfDeviceInitAssignWdmIrpPreprocessCallback failed %!STATUS!\n",
+ status);
+ return status;
+ }
+
+ } else {
+
+ //
+ // FileEvents can opt for Device level synchronization only if the ExecutionLevel
+ // of the Device is passive. Since we can't choose passive execution-level for
+ // device because we have chose to synchronize timers & dpcs with the device,
+ // we will opt out of synchonization with the device for fileobjects.
+ // Note: If the driver has to synchronize Create with the other I/O events,
+ // it can create a queue and configure-dispatch create requests to the queue.
+ //
+ WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
+ attributes.SynchronizationScope = WdfSynchronizationScopeNone;
+
+ //
+ // Set Entry points for Create and Close..
+ //
+ WDF_FILEOBJECT_CONFIG_INIT(
+ &fileobjectConfig,
+ SerialEvtDeviceFileCreate,
+ SerialEvtFileClose,
+ WDF_NO_EVENT_CALLBACK // Cleanup
+ );
+
+ WdfDeviceInitSetFileObjectConfig(
+ DeviceInit,
+ &fileobjectConfig,
+ &attributes
+ );
+ }
+
+
+ //
+ // Since framework queues doesn't handle IRP_MJ_FLUSH_BUFFERS,
+ // IRP_MJ_QUERY_INFORMATION and IRP_MJ_SET_INFORMATION requests,
+ // we will register a preprocess callback to handle them.
+ //
+ status = WdfDeviceInitAssignWdmIrpPreprocessCallback(
+ DeviceInit,
+ SerialFlush,
+ IRP_MJ_FLUSH_BUFFERS,
+ NULL, // pointer minor function table
+ 0); // number of entries in the table
+
+ if (!NT_SUCCESS(status)) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP,
+ "WdfDeviceInitAssignWdmIrpPreprocessCallback failed %!STATUS!\n",
+ status);
+ return status;
+ }
+
+ status = WdfDeviceInitAssignWdmIrpPreprocessCallback(
+ DeviceInit,
+ SerialQueryInformationFile,
+ IRP_MJ_QUERY_INFORMATION,
+ NULL, // pointer minor function table
+ 0); // number of entries in the table
+
+ if (!NT_SUCCESS(status)) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP,
+ "WdfDeviceInitAssignWdmIrpPreprocessCallback failed %!STATUS!\n",
+ status);
+ return status;
+ }
+ status = WdfDeviceInitAssignWdmIrpPreprocessCallback(
+ DeviceInit,
+ SerialSetInformationFile,
+ IRP_MJ_SET_INFORMATION,
+ NULL, // pointer minor function table
+ 0); // number of entries in the table
+
+ if (!NT_SUCCESS(status)) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP,
+ "WdfDeviceInitAssignWdmIrpPreprocessCallback failed %!STATUS!\n",
+ status);
+ return status;
+ }
+
+
+ //
+ // Create a device
+ //
+ WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE (&attributes,
+ SERIAL_DEVICE_EXTENSION);
+ //
+ // Provide a callback to cleanup the context. This will be called
+ // when the device is removed.
+ //
+ attributes.EvtCleanupCallback = SerialEvtDeviceContextCleanup;
+ //
+ // By opting for SynchronizationScopeDevice, we tell the framework to
+ // synchronize callbacks events of all the objects directly associated
+ // with the device. In this driver, we will associate queues, dpcs,
+ // and timers. By doing that we don't have to worrry about synchronizing
+ // access to device-context by Io Events, cancel-routine, timer and dpc
+ // callbacks.
+ //
+ attributes.SynchronizationScope = WdfSynchronizationScopeDevice;
+
+ status = WdfDeviceCreate(&DeviceInit, &attributes, &device);
+ if (!NT_SUCCESS(status)) {
+
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP,
+ "SerialAddDevice - WdfDeviceCreate failed %!STATUS!\n",
+ status);
+ return status;
+ }
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_PNP,
+ "Created device (%p) %wZ\n", device, &deviceName);
+
+ pDevExt = SerialGetDeviceExtension (device);
+
+ pDevExt->DriverObject = WdfDriverWdmGetDriverObject(Driver);
+
+ //
+ // This sample doesn't support multiport serial devices.
+ // Multiport devices allow other pseudo-serial devices with extra
+ // resources to specify another range of I/O ports.
+ //
+ if(!SerialGetRegistryKeyValue(device, L"MultiportDevice", &isMulti)) {
+ isMulti = 0;
+ }
+
+ if(isMulti) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP,
+ "This sample doesn't support multiport devices\n");
+ return STATUS_DEVICE_CONFIGURATION_ERROR;
+ }
+
+ //
+ // Set up the device extension.
+ //
+
+ pDevExt = SerialGetDeviceExtension (device);
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_PNP,
+ "AddDevice PDO(0x%p) FDO(0x%p), Lower(0x%p) DevExt (0x%p)\n",
+ WdfDeviceWdmGetPhysicalDevice (device),
+ WdfDeviceWdmGetDeviceObject (device),
+ WdfDeviceWdmGetAttachedDevice(device),
+ pDevExt);
+
+ pDevExt->DeviceIsOpened = FALSE;
+ pDevExt->DeviceObject = WdfDeviceWdmGetDeviceObject(device);
+ pDevExt->WdfDevice = device;
+
+ pDevExt->TxFifoAmount = driverDefaults.TxFIFODefault;
+ pDevExt->UartRemovalDetect = driverDefaults.UartRemovalDetect;
+ pDevExt->CreatedSymbolicLink = FALSE;
+ pDevExt->OwnsPowerPolicy = relinquishPowerPolicy ? FALSE : TRUE;
+
+ status = SerialSetPowerPolicy(pDevExt);
+ if(!NT_SUCCESS(status)){
+ return status;
+ }
+
+ //
+ // We create four manual queues below.
+ // Read Queue..(how about using serial queue for read). Since requests
+ // jump from queue to queue, we cannot configure the queues to receive a
+ // particular type of request. For example, some of the IOCTLs end up
+ // in read and write queue.
+ //
+ WDF_IO_QUEUE_CONFIG_INIT(&queueConfig,
+ WdfIoQueueDispatchManual);
+
+ queueConfig.EvtIoStop = SerialEvtIoStop;
+ queueConfig.EvtIoResume = SerialEvtIoResume;
+ queueConfig.EvtIoCanceledOnQueue = SerialEvtCanceledOnQueue;
+
+ status = WdfIoQueueCreate (device,
+ &queueConfig,
+ WDF_NO_OBJECT_ATTRIBUTES,
+ &pDevExt->ReadQueue
+ );
+
+ if (!NT_SUCCESS(status)) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP, " WdfIoQueueCreate for Read failed %!STATUS!\n", status);
+ return status;
+ }
+
+ //
+ // Write Queue..
+ //
+ WDF_IO_QUEUE_CONFIG_INIT(&queueConfig,
+ WdfIoQueueDispatchManual);
+
+ queueConfig.EvtIoStop = SerialEvtIoStop;
+ queueConfig.EvtIoResume = SerialEvtIoResume;
+ queueConfig.EvtIoCanceledOnQueue = SerialEvtCanceledOnQueue;
+
+ status = WdfIoQueueCreate (device,
+ &queueConfig,
+ WDF_NO_OBJECT_ATTRIBUTES,
+ &pDevExt->WriteQueue
+ );
+
+ if (!NT_SUCCESS(status)) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP, " WdfIoQueueCreate for Write failed %!STATUS!\n", status);
+ return status;
+ }
+
+ //
+ // Mask Queue...
+ //
+ WDF_IO_QUEUE_CONFIG_INIT(&queueConfig,
+ WdfIoQueueDispatchManual
+ );
+
+ queueConfig.EvtIoCanceledOnQueue = SerialEvtCanceledOnQueue;
+
+ queueConfig.EvtIoStop = SerialEvtIoStop;
+ queueConfig.EvtIoResume = SerialEvtIoResume;
+
+ status = WdfIoQueueCreate (device,
+ &queueConfig,
+ WDF_NO_OBJECT_ATTRIBUTES,
+ &pDevExt->MaskQueue
+ );
+
+ if (!NT_SUCCESS(status)) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP, " WdfIoQueueCreate for Mask failed %!STATUS!\n", status);
+ return status;
+ }
+
+ //
+ // Purge Queue..
+ //
+ WDF_IO_QUEUE_CONFIG_INIT(&queueConfig,
+ WdfIoQueueDispatchManual
+ );
+
+ queueConfig.EvtIoCanceledOnQueue = SerialEvtCanceledOnQueue;
+
+ queueConfig.EvtIoStop = SerialEvtIoStop;
+ queueConfig.EvtIoResume = SerialEvtIoResume;
+
+ status = WdfIoQueueCreate (device,
+ &queueConfig,
+ WDF_NO_OBJECT_ATTRIBUTES,
+ &pDevExt->PurgeQueue
+ );
+
+ if (!NT_SUCCESS(status)) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP, " WdfIoQueueCreate for Purge failed %!STATUS!\n", status);
+ return status;
+ }
+
+ //
+ // All the incoming I/O requests are routed to the default queue and dispatch to the
+ // appropriate callback events. These callback event will check to see if another
+ // request is currently active. If so then it will forward it to other manual queues.
+ // All the queues are auto managed by the framework in response to the PNP
+ // and Power events.
+ //
+ WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(
+ &queueConfig,
+ WdfIoQueueDispatchParallel
+ );
+ queueConfig.EvtIoRead = SerialEvtIoRead;
+ queueConfig.EvtIoWrite = SerialEvtIoWrite;
+ queueConfig.EvtIoDeviceControl = SerialEvtIoDeviceControl;
+ queueConfig.EvtIoInternalDeviceControl = SerialEvtIoInternalDeviceControl;
+ queueConfig.EvtIoCanceledOnQueue = SerialEvtCanceledOnQueue;
+
+ queueConfig.EvtIoStop = SerialEvtIoStop;
+ queueConfig.EvtIoResume = SerialEvtIoResume;
+
+ status = WdfIoQueueCreate(device,
+ &queueConfig,
+ WDF_NO_OBJECT_ATTRIBUTES,
+ &defaultqueue
+ );
+ if (!NT_SUCCESS(status)) {
+
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP, "WdfIoQueueCreate failed %!STATUS!\n", status);
+ return status;
+ }
+
+ //
+ // Create WDFINTERRUPT object. Let us leave the ShareVector to default value and
+ // let the framework decide whether to share the interrupt or not based on the
+ // ShareDisposition provided by the bus driver in the resource descriptor.
+ //
+
+ WDF_INTERRUPT_CONFIG_INIT(&interruptConfig,
+ SerialISR,
+ NULL);
+
+ interruptConfig.EvtInterruptDisable = SerialEvtInterruptDisable;
+ interruptConfig.EvtInterruptEnable = SerialEvtInterruptEnable;
+
+ WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, SERIAL_INTERRUPT_CONTEXT);
+
+ status = WdfInterruptCreate(device,
+ &interruptConfig,
+ &attributes,
+ &pDevExt->WdfInterrupt);
+
+ if (!NT_SUCCESS(status)) {
+
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP, "Couldn't create interrupt for %wZ\n",
+ &pDevExt->DeviceName);
+ return status;
+ }
+
+ //
+ // Interrupt state wait lock...
+ //
+ WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
+ attributes.ParentObject = pDevExt->WdfInterrupt;
+
+ interruptContext = SerialGetInterruptContext(pDevExt->WdfInterrupt);
+
+ status = WdfWaitLockCreate(&attributes,
+ &interruptContext->InterruptStateLock
+ );
+
+ if (!NT_SUCCESS(status)) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP, " WdfWaitLockCreate for InterruptStateLock failed %!STATUS!\n", status);
+ return status;
+ }
+
+ //
+ // Set interrupt policy
+ //
+ SerialSetInterruptPolicy(pDevExt->WdfInterrupt);
+
+ //
+ // Timers and DPCs...
+ //
+ status = SerialCreateTimersAndDpcs(pDevExt);
+ if (!NT_SUCCESS(status)) {
+
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP, "SerialCreateTimersAndDpcs failed %x\n", status);
+ return status;
+ }
+
+ //
+ // Register with WMI.
+ //
+ status = SerialWmiRegistration(device);
+ if(!NT_SUCCESS (status)) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP, "SerialWmiRegistration failed %!STATUS!\n", status);
+ return status;
+
+ }
+
+ //
+ // Upto this point, if we fail, we don't have to worry about freeing any resource because
+ // framework will free all the objects.
+ //
+ //
+ // Do the external naming.
+ //
+
+ status = SerialDoExternalNaming(pDevExt);
+ if (!NT_SUCCESS(status)) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP, "External Naming Failed - Status %!STATUS!\n",
+ status);
+ return status;
+ }
+
+ //
+ // Finally increment the global system configuration that keeps track of number of serial ports.
+ //
+ countSoFar = &IoGetConfigurationInformation()->SerialCount;
+ (*countSoFar)++;
+ pDevExt->IsSystemConfigInfoUpdated = TRUE;
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_PNP, "<--SerialEvtDeviceAdd\n");
+
+ return status;
+
+}
+#pragma warning(push)
+#pragma warning(disable:28118) // this callback will run at IRQL=PASSIVE_LEVEL
+_Use_decl_annotations_
+VOID
+SerialEvtDeviceContextCleanup (
+ WDFOBJECT Device
+ )
+/*++
+
+Routine Description:
+
+ EvtDeviceContextCleanup event callback cleans up anything done in
+ EvtDeviceAdd, except those things that are automatically cleaned
+ up by the Framework.
+
+ In a driver derived from this sample, it's quite likely that this function could
+ be deleted.
+
+Arguments:
+
+ Device - Handle to a framework device object.
+
+Return Value:
+
+ VOID
+
+--*/
+{
+ PSERIAL_DEVICE_EXTENSION deviceExtension;
+ PULONG countSoFar;
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_PNP, "--> SerialDeviceContextCleanup\n");
+
+ PAGED_CODE();
+
+ deviceExtension = SerialGetDeviceExtension (Device);
+
+ if (deviceExtension->InterruptReadBuffer != NULL) {
+ ExFreePool(deviceExtension->InterruptReadBuffer);
+ deviceExtension->InterruptReadBuffer = NULL;
+ }
+
+ //
+ // Update the global configuration count for serial device.
+ //
+ if(deviceExtension->IsSystemConfigInfoUpdated) {
+ countSoFar = &IoGetConfigurationInformation()->SerialCount;
+ (*countSoFar)--;
+ }
+
+ SerialUndoExternalNaming(deviceExtension);
+
+ return;
+}
+#pragma warning(pop) // enable 28118 again
+
+NTSTATUS
+SerialEvtPrepareHardware(
+ WDFDEVICE Device,
+ WDFCMRESLIST Resources,
+ WDFCMRESLIST ResourcesTranslated
+ )
+/*++
+
+Routine Description:
+
+ SerialEvtPrepareHardware event callback performs operations that are necessary
+ to make the device operational. The framework calls the driver's
+ SerialEvtPrepareHardware callback when the PnP manager sends an IRP_MN_START_DEVICE
+ request to the driver stack.
+
+Arguments:
+
+ Device - Handle to a framework device object.
+
+ Resources - Handle to a collection of framework resource objects.
+ This collection identifies the raw (bus-relative) hardware
+ resources that have been assigned to the device.
+
+ ResourcesTranslated - Handle to a collection of framework resource objects.
+ This collection identifies the translated (system-physical)
+ hardware resources that have been assigned to the device.
+ The resources appear from the CPU's point of view.
+ Use this list of resources to map I/O space and
+ device-accessible memory into virtual address space
+
+Return Value:
+
+ WDF status code
+
+--*/
+{
+ PSERIAL_DEVICE_EXTENSION pDevExt;
+ NTSTATUS status;
+ CONFIG_DATA config;
+ PCONFIG_DATA pConfig = &config;
+ ULONG defaultClockRate = 1843200;
+
+ PAGED_CODE();
+
+ SerialDbgPrintEx (TRACE_LEVEL_INFORMATION, DBG_PNP, "--> SerialEvtPrepareHardware\n");
+ //
+ // Get the Device Extension..
+ //
+ pDevExt = SerialGetDeviceExtension (Device);
+
+ RtlZeroMemory(pConfig, sizeof(CONFIG_DATA));
+
+ //
+ // Initialize a config data structure with default values for those that
+ // may not already be initialized.
+ //
+
+ pConfig->LogFifo = driverDefaults.LogFifoDefault;
+
+
+ //
+ // Get the hw resources for the device.
+ //
+
+ status = SerialMapHWResources(Device, Resources, ResourcesTranslated, pConfig);
+
+ if (!NT_SUCCESS(status)) {
+ goto End;
+ }
+
+ //
+ // Open the "Device Parameters" section of registry for this device and get parameters.
+ //
+
+ if(!SerialGetRegistryKeyValue (Device,
+ L"DisablePort",
+ &pConfig->DisablePort)){
+ pConfig->DisablePort = 0;
+ }
+
+ if(!SerialGetRegistryKeyValue (Device,
+ L"ForceFifoEnable",
+ &pConfig->ForceFifoEnable)){
+ pConfig->ForceFifoEnable = driverDefaults.ForceFifoEnableDefault;
+ }
+
+ if(!SerialGetRegistryKeyValue (Device,
+ L"RxFIFO",
+ &pConfig->RxFIFO)){
+ pConfig->RxFIFO = driverDefaults.RxFIFODefault;
+ }
+
+ if(!SerialGetRegistryKeyValue (Device,
+ L"TxFIFO",
+ &pConfig->TxFIFO)){
+ pConfig->TxFIFO = driverDefaults.TxFIFODefault;
+ }
+
+ if(!SerialGetRegistryKeyValue (Device,
+ L"Share System Interrupt",
+ &pConfig->PermitShare)){
+ pConfig->PermitShare = driverDefaults.PermitShareDefault;
+ }
+
+ if(!SerialGetRegistryKeyValue (Device,
+ L"ClockRate",
+ &pConfig->ClockRate)) {
+ pConfig->ClockRate = defaultClockRate;
+ }
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_PNP, "Com Port ClockRate: %x\n",
+ pConfig->ClockRate);
+
+ if(!SerialGetRegistryKeyValue(Device,
+ L"TL16C550C Auto Flow Control",
+ &pConfig->TL16C550CAFC)){
+ pConfig->TL16C550CAFC = 0;
+ }
+
+ status = SerialInitController(pDevExt, pConfig);
+
+ if (NT_SUCCESS(status)) {
+ }
+End:
+
+ SerialDbgPrintEx (TRACE_LEVEL_INFORMATION, DBG_PNP, "<-- SerialEvtPrepareHardware 0x%x\n", status);
+
+ return status;
+}
+
+NTSTATUS
+SerialEvtReleaseHardware(
+ IN WDFDEVICE Device,
+ IN WDFCMRESLIST ResourcesTranslated
+ )
+/*++
+
+Routine Description:
+
+ EvtDeviceReleaseHardware is called by the framework whenever the PnP manager
+ is revoking ownership of our resources. This may be in response to either
+ IRP_MN_STOP_DEVICE or IRP_MN_REMOVE_DEVICE. The callback is made before
+ passing down the IRP to the lower driver.
+
+ In this callback, do anything necessary to free those resources.
+ In this driver, we will not receive this callback when there is open handle to
+ the device. We explicitly tell the framework (WdfDeviceSetStaticStopRemove) to
+ fail stop and query-remove when handle is open.
+
+Arguments:
+
+ Device - Handle to a framework device object.
+
+ ResourcesTranslated - Handle to a collection of framework resource objects.
+ This collection identifies the translated (system-physical)
+ hardware resources that have been assigned to the device.
+ The resources appear from the CPU's point of view.
+ Use this list of resources to map I/O space and
+ device-accessible memory into virtual address space
+
+Return Value:
+
+ NTSTATUS - Failures will be logged, but not acted on.
+
+--*/
+{
+ PSERIAL_DEVICE_EXTENSION pDevExt;
+
+ UNREFERENCED_PARAMETER(ResourcesTranslated);
+
+ PAGED_CODE();
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_PNP,
+ "--> SerialEvtReleaseHardware\n");
+
+ pDevExt = SerialGetDeviceExtension (Device);
+
+ //
+ // Reset and put the device into a known initial state before releasing the hw resources.
+ // In this driver we can recieve this callback only when there is no handle open because
+ // we tell the framework to disable stop by calling WdfDeviceSetStaticStopRemove.
+ // Since we have already reset the device in our close handler, we don't have to
+ // do anything other than unmapping the I/O resources.
+ //
+
+ //
+ // Unmap any Memory-Mapped registers. Disconnecting from the interrupt will
+ // be done automatically by the framework.
+ //
+ SerialUnmapHWResources(pDevExt);
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_PNP,
+ "<-- SerialEvtReleaseHardware\n");
+
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+SerialEvtDeviceD0EntryPostInterruptsEnabled(
+ IN WDFDEVICE Device,
+ IN WDF_POWER_DEVICE_STATE PreviousState
+ )
+/*++
+
+Routine Description:
+
+ EvtDeviceD0EntryPostInterruptsEnabled is called by the framework after the
+ driver has enabled the device's hardware interrupts.
+
+ 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.
+
+Arguments:
+
+ Device - Handle to a framework device object.
+
+ PreviousState - A WDF_POWER_DEVICE_STATE-typed enumerator that identifies
+ the previous device power state.
+
+Return Value:
+
+ NTSTATUS - Failures will be logged, but not acted on.
+
+--*/
+{
+ PSERIAL_DEVICE_EXTENSION extension = SerialGetDeviceExtension(Device);
+ PSERIAL_INTERRUPT_CONTEXT interruptContext = SerialGetInterruptContext(extension->WdfInterrupt);
+ WDF_INTERRUPT_INFO info;
+
+ UNREFERENCED_PARAMETER(PreviousState);
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_PNP,
+ "--> SerialEvtDeviceD0EntryPostInterruptsEnabled\n");
+ //
+ // The following lines of code show how to call WdfInterruptGetInfo.
+ //
+ WDF_INTERRUPT_INFO_INIT(&info);
+ WdfInterruptGetInfo(extension->WdfInterrupt, &info);
+
+ WdfWaitLockAcquire(interruptContext->InterruptStateLock, NULL);
+ interruptContext->IsInterruptConnected = TRUE;
+ WdfWaitLockRelease(interruptContext->InterruptStateLock);
+
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+SerialEvtDeviceD0ExitPreInterruptsDisabled(
+ IN WDFDEVICE Device,
+ IN WDF_POWER_DEVICE_STATE TargetState
+ )
+/*++
+
+Routine Description:
+
+ EvtDeviceD0ExitPreInterruptsDisabled is called by the framework before the
+ driver disables the device's hardware interrupts.
+
+Arguments:
+
+ Device - Handle to a framework device object.
+
+ TargetState - A WDF_POWER_DEVICE_STATE-typed enumerator that identifies the
+ device power state that the device is about to enter.
+
+Return Value:
+
+ NTSTATUS - Failures will be logged, but not acted on.
+
+--*/
+{
+ PSERIAL_DEVICE_EXTENSION extension = SerialGetDeviceExtension(Device);
+ PSERIAL_INTERRUPT_CONTEXT interruptContext = SerialGetInterruptContext(extension->WdfInterrupt);
+
+ UNREFERENCED_PARAMETER(TargetState);
+ PAGED_CODE();
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_PNP,
+ "--> SerialEvtDeviceD0ExitPreInterruptsDisabled\n");
+
+ WdfWaitLockAcquire(interruptContext->InterruptStateLock, NULL);
+ interruptContext->IsInterruptConnected = FALSE;
+ WdfWaitLockRelease(interruptContext->InterruptStateLock);
+
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+SerialSetPowerPolicy(
+ IN PSERIAL_DEVICE_EXTENSION DeviceExtension
+ )
+{
+ WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS idleSettings;
+ //WDF_POWER_POLICY_EVENT_CALLBACKS powerPolicyCallbacks;
+ NTSTATUS status = STATUS_SUCCESS;
+ WDFDEVICE hDevice = DeviceExtension->WdfDevice;
+ ULONG powerOnClose;
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_PNP,
+ "--> SerialSetPowerPolicy\n");
+
+ PAGED_CODE();
+
+ //
+ // Find out whether we want to power down the device when there no handles open.
+ //
+ SerialGetRegistryKeyValue(hDevice, L"EnablePowerManagement", &powerOnClose);
+ DeviceExtension->RetainPowerOnClose = powerOnClose ? TRUE : FALSE;
+
+ //
+ // In some drivers, the device must be specifically programmed to enable
+ // wake signals. UARTs were designed long, long before such a concept. So
+ // this driver, which just drives UARTs, doesn't register wake arm/disarm
+ // callbacks. Arming or disarming for UARTs has to be handled by side-band
+ // code that controls hardware designed more recently. In this case, ACPI
+ // is handling it. If one were to write a driver which implemented a more
+ // modern serial device, one might need to use these callbacks.
+ //
+
+ //
+ // Init the power policy callbacks
+ //
+ //WDF_POWER_POLICY_EVENT_CALLBACKS_INIT(&powerPolicyCallbacks);
+
+ //
+ // This group of three callbacks allows this sample driver to manage
+ // arming the device for wake from the S0 state.
+ //
+
+ //powerPolicyCallbacks.EvtDeviceArmWakeFromS0 = SerialEvtDeviceWakeArmS0;
+ //powerPolicyCallbacks.EvtDeviceDisarmWakeFromS0 = SerialEvtDeviceWakeDisarmS0;
+ //powerPolicyCallbacks.EvtDeviceWakeFromS0Triggered = SerialEvtDeviceWakeTriggeredS0;
+
+ //
+ // This group of three callbacks allows the device to be armed for wake
+ // from Sx (S1, S2, S3 or S4.) Networking devices can optionally be put
+ // into a state where a packet sent to them will cause the device's wake
+ // signal to be triggered, which causes the machine to wake, moving back
+ // into the S0 state.
+ //
+
+ //powerPolicyCallbacks.EvtDeviceArmWakeFromSx = SerialEvtDeviceWakeArmSx;
+ //powerPolicyCallbacks.EvtDeviceDisarmWakeFromSx = SerialEvtDeviceWakeDisarmSx;
+ //powerPolicyCallbacks.EvtDeviceWakeFromSxTriggered = SerialEvtDeviceWakeTriggeredSx;
+
+ //
+ // Register the power policy callbacks.
+ //
+ //WdfDeviceSetPowerPolicyEventCallbacks(hDevice, &powerPolicyCallbacks);
+
+ //
+ // Init the idle policy structure. By setting IdleCannotWakeFromS0 we tell the framework
+ // to power down the device without arming for wake. The only way the device can come
+ // back to D0 is when we call WdfDeviceStopIdle in SerialEvtDeviceFileCreate.
+ // We can't choose IdleCanWakeFromS0 by default is because onboard serial ports typically
+ // don't have wake capability. If the driver is used for plugin boards that does support
+ // wait-wake, you can update the settings to match that. If MS provided modem driver
+ // is used on ports that does support wake on ring, then it will update the settings
+ // by sending an internal ioctl to us.
+ //
+ WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS_INIT(&idleSettings, IdleCannotWakeFromS0);
+ if(DeviceExtension->OwnsPowerPolicy && !DeviceExtension->RetainPowerOnClose) {
+ //
+ // Since we don't have to retain power when there are no open handles, we
+ // register for idle power management to save power. Check the use of
+ // WdfDeviceStopIdle in SerialEvtDeviceFileCreate.
+ //
+ idleSettings.UserControlOfIdleSettings = IdleAllowUserControl;
+
+ status = WdfDeviceAssignS0IdleSettings(hDevice, &idleSettings);
+ if ( !NT_SUCCESS(status)) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP,
+ "WdfDeviceSetPowerPolicyS0IdlePolicy failed %x \n", status);
+ return status;
+ }
+ }
+
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_PNP, "<-- SerialSetPowerPolicy\n");
+
+ return status;
+}
+
+UINT32
+SerialReportMaxBaudRate(ULONG Bauds)
+/*++
+
+Routine Description:
+
+ This routine returns the max baud rate given a selection of rates
+
+Arguments:
+
+ Bauds - Bit-encoded list of supported bauds
+
+
+ Return Value:
+
+ The max baud rate listed in Bauds
+
+--*/
+{
+ int i;
+
+ PAGED_CODE();
+
+ for(i=0; SupportedBaudRates[i].BaudRate != SERIAL_BAUD_INVALID; i++) {
+
+ if(Bauds & SupportedBaudRates[i].Mask) {
+ return SupportedBaudRates[i].BaudRate;
+ }
+ }
+
+ //
+ // We're in bad shape
+ //
+
+ return 0;
+}
+
+NTSTATUS
+SerialInitController(
+ IN PSERIAL_DEVICE_EXTENSION pDevExt,
+ IN PCONFIG_DATA PConfigData
+ )
+/*++
+
+Routine Description:
+
+ Really too many things to mention here. In general initializes
+ kernel synchronization structures, allocates the typeahead buffer,
+ sets up defaults, etc.
+
+Arguments:
+
+ PDevObj - Device object for the device to be started
+
+ PConfigData - Pointer to a record for a single port.
+
+Return Value:
+
+ STATUS_SUCCCESS if everything went ok. A !NT_SUCCESS status
+ otherwise.
+
+--*/
+
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ SHORT junk;
+ int i;
+
+ PAGED_CODE();
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_PNP, "--> SerialInitController for %wZ\n",
+ &pDevExt->DeviceName);
+
+ //
+ // Save the value of clock input to the part. We use this to calculate
+ // the divisor latch value. The value is in Hertz.
+ //
+
+ pDevExt->ClockRate = PConfigData->ClockRate;
+
+
+ //
+ // Save if we have to enable TI's auto flow control
+ //
+
+
+ pDevExt->TL16C550CAFC = PConfigData->TL16C550CAFC;
+
+
+ //
+ // Map the memory for the control registers for the serial device
+ // into virtual memory.
+ //
+ pDevExt->Controller =
+ SerialGetMappedAddress(PConfigData->TrController,
+ PConfigData->SpanOfController,
+ (BOOLEAN)PConfigData->AddressSpace,
+ &pDevExt->UnMapRegisters);
+
+
+ if (!pDevExt->Controller) {
+
+ SerialLogError(
+ pDevExt->DriverObject,
+ pDevExt->DeviceObject,
+ PConfigData->TrController,
+ SerialPhysicalZero,
+ 0,
+ 0,
+ 0,
+ 7,
+ STATUS_SUCCESS,
+ SERIAL_REGISTERS_NOT_MAPPED,
+ pDevExt->DeviceName.Length+sizeof(WCHAR),
+ pDevExt->DeviceName.Buffer,
+ 0,
+ NULL
+ );
+
+ SerialDbgPrintEx(TRACE_LEVEL_WARNING, DBG_PNP, "Could not map memory for device "
+ "registers for %wZ\n", &pDevExt->DeviceName);
+
+ pDevExt->UnMapRegisters = FALSE;
+ status = STATUS_NONE_MAPPED;
+ goto ExtensionCleanup;
+
+ }
+
+ pDevExt->AddressSpace = PConfigData->AddressSpace;
+ pDevExt->SpanOfController = PConfigData->SpanOfController;
+
+ //
+ // Save off the interface type and the bus number.
+ //
+
+ pDevExt->Vector = PConfigData->TrVector;
+ pDevExt->Irql = (UCHAR)PConfigData->TrIrql;
+ pDevExt->InterruptMode = PConfigData->InterruptMode;
+ pDevExt->Affinity = PConfigData->Affinity;
+
+ //
+ // If the user said to permit sharing within the device, propagate this
+ // through.
+ //
+
+ pDevExt->PermitShare = PConfigData->PermitShare;
+
+
+ //
+ // Before we test whether the port exists (which will enable the FIFO)
+ // convert the rx trigger value to what should be used in the register.
+ //
+ // If a bogus value was given - crank them down to 1.
+ //
+ // If this is a "souped up" UART with like a 64 byte FIFO, they
+ // should use the appropriate "spoofing" value to get the desired
+ // results. I.e., if on their chip 0xC0 in the FCR is for 64 bytes,
+ // they should specify 14 in the registry.
+ //
+
+ switch (PConfigData->RxFIFO) {
+
+ case 1:
+
+ pDevExt->RxFifoTrigger = SERIAL_1_BYTE_HIGH_WATER;
+ break;
+
+ case 4:
+
+ pDevExt->RxFifoTrigger = SERIAL_4_BYTE_HIGH_WATER;
+ break;
+
+ case 8:
+
+ pDevExt->RxFifoTrigger = SERIAL_8_BYTE_HIGH_WATER;
+ break;
+
+ case 14:
+
+ pDevExt->RxFifoTrigger = SERIAL_14_BYTE_HIGH_WATER;
+ break;
+
+ default:
+
+ pDevExt->RxFifoTrigger = SERIAL_1_BYTE_HIGH_WATER;
+ break;
+
+ }
+
+
+ if (PConfigData->TxFIFO < 1) {
+
+ pDevExt->TxFifoAmount = 1;
+
+ } else {
+
+ pDevExt->TxFifoAmount = PConfigData->TxFIFO;
+
+ }
+
+ if (!SerialDoesPortExist(
+ pDevExt,
+ &pDevExt->DeviceName,
+ PConfigData->ForceFifoEnable,
+ PConfigData->LogFifo
+ )) {
+
+ //
+ // We couldn't verify that there was actually a
+ // port. No need to log an error as the port exist
+ // code will log exactly why.
+ //
+
+ SerialDbgPrintEx(TRACE_LEVEL_WARNING, DBG_PNP, "DoesPortExist test failed for "
+ "%wZ\n", &pDevExt->DeviceName);
+
+ status = STATUS_NO_SUCH_DEVICE;
+ goto ExtensionCleanup;
+
+ }
+
+
+ //
+ // If the user requested that we disable the port, then
+ // do it now. Log the fact that the port has been disabled.
+ //
+
+ if (PConfigData->DisablePort) {
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_PNP, "disabled port %wZ as requested in "
+ "configuration\n", &pDevExt->DeviceName);
+
+ status = STATUS_NO_SUCH_DEVICE;
+
+ SerialLogError(
+ pDevExt->DriverObject,
+ pDevExt->DeviceObject,
+ PConfigData->TrController,
+ SerialPhysicalZero,
+ 0,
+ 0,
+ 0,
+ 57,
+ STATUS_SUCCESS,
+ SERIAL_DISABLED_PORT,
+ pDevExt->DeviceName.Length+sizeof(WCHAR),
+ pDevExt->DeviceName.Buffer,
+ 0,
+ NULL
+ );
+
+ goto ExtensionCleanup;
+
+ }
+
+
+
+ //
+ // Set up the default device control fields.
+ // Note that if the values are changed after
+ // the file is open, they do NOT revert back
+ // to the old value at file close.
+ //
+
+ pDevExt->SpecialChars.XonChar = SERIAL_DEF_XON;
+ pDevExt->SpecialChars.XoffChar = SERIAL_DEF_XOFF;
+ pDevExt->HandFlow.ControlHandShake = SERIAL_DTR_CONTROL;
+ pDevExt->HandFlow.FlowReplace = SERIAL_RTS_CONTROL;
+
+
+ //
+ // Default Line control protocol. 7E1
+ //
+ // Seven data bits.
+ // Even parity.
+ // 1 Stop bits.
+ //
+
+ pDevExt->LineControl = SERIAL_7_DATA |
+ SERIAL_EVEN_PARITY |
+ SERIAL_NONE_PARITY;
+
+ pDevExt->ValidDataMask = 0x7f;
+ pDevExt->CurrentBaud = 1200;
+
+
+ //
+ // We set up the default xon/xoff limits.
+ //
+ // This may be a bogus value. It looks like the BufferSize
+ // is not set up until the device is actually opened.
+ //
+
+ pDevExt->HandFlow.XoffLimit = pDevExt->BufferSize >> 3;
+ pDevExt->HandFlow.XonLimit = pDevExt->BufferSize >> 1;
+
+ pDevExt->BufferSizePt8 = ((3*(pDevExt->BufferSize>>2))+
+ (pDevExt->BufferSize>>4));
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_PNP, " The default interrupt read buffer size is: %d\n"
+ "------ The XoffLimit is : %d\n"
+ "------ The XonLimit is : %d\n"
+ "------ The pt 8 size is : %d\n",
+ pDevExt->BufferSize, pDevExt->HandFlow.XoffLimit,
+ pDevExt->HandFlow.XonLimit, pDevExt->BufferSizePt8);
+
+
+ //
+ // Go through all the "named" baud rates to find out which ones
+ // can be supported with this port.
+ //
+ //
+
+ pDevExt->SupportedBauds = SERIAL_BAUD_USER;
+
+
+ for(i=0; SupportedBaudRates[i].BaudRate != SERIAL_BAUD_INVALID; i++) {
+
+ if (!NT_ERROR(SerialGetDivisorFromBaud(
+ pDevExt->ClockRate,
+ (LONG)SupportedBaudRates[i].BaudRate,
+ &junk
+ ))) {
+
+ pDevExt->SupportedBauds |= SupportedBaudRates[i].Mask;
+ }
+ }
+
+
+
+
+ //
+ // Mark this device as not being opened by anyone. We keep a
+ // variable around so that spurious interrupts are easily
+ // dismissed by the ISR.
+ //
+
+ SetDeviceIsOpened(pDevExt, FALSE, FALSE);
+
+ //
+ // Store values into the extension for interval timing.
+ //
+
+ //
+ // If the interval timer is less than a second then come
+ // in with a short "polling" loop.
+ //
+ // For large (> then 2 seconds) use a 1 second poller.
+ //
+
+ pDevExt->ShortIntervalAmount.QuadPart = -1;
+ pDevExt->LongIntervalAmount.QuadPart = -10000000;
+ pDevExt->CutOverAmount.QuadPart = 200000000;
+
+ DISABLE_ALL_INTERRUPTS (pDevExt, pDevExt->Controller);
+
+ WRITE_MODEM_CONTROL(pDevExt, pDevExt->Controller, (UCHAR)0);
+
+ // make sure there is no escape character currently set
+ pDevExt->EscapeChar = 0;
+ //
+ // This should set up everything as it should be when
+ // a device is to be opened. We do need to lower the
+ // modem lines, and disable the recalcitrant fifo
+ // so that it will show up if the user boots to dos.
+ //
+
+ // __WARNING_IRQ_SET_TOO_HIGH: we are calling interrupt synchronize routine directly. Suppress it because interrupt is not connected yet.
+ // __WARNING_INVALID_PARAM_VALUE_1: Interrupt is UNREFERENCED_PARAMETER, so it can be NULL
+#pragma warning(suppress: __WARNING_IRQ_SET_TOO_HIGH; suppress: __WARNING_INVALID_PARAM_VALUE_1)
+ SerialReset(NULL, pDevExt);
+
+#pragma warning(suppress: __WARNING_IRQ_SET_TOO_HIGH; suppress: __WARNING_INVALID_PARAM_VALUE_1)
+ SerialMarkClose(NULL, pDevExt);
+
+#pragma warning(suppress: __WARNING_IRQ_SET_TOO_HIGH; suppress: __WARNING_INVALID_PARAM_VALUE_1)
+ SerialClrRTS(NULL, pDevExt);
+
+#pragma warning(suppress: __WARNING_IRQ_SET_TOO_HIGH; suppress: __WARNING_INVALID_PARAM_VALUE_1)
+ SerialClrDTR(NULL, pDevExt);
+
+ //
+ // Fill in WMI hardware data
+ //
+ pDevExt->WmiHwData.IrqNumber = pDevExt->Irql;
+ pDevExt->WmiHwData.IrqLevel = pDevExt->Irql;
+ pDevExt->WmiHwData.IrqVector = pDevExt->Vector;
+ pDevExt->WmiHwData.IrqAffinityMask = pDevExt->Affinity;
+ pDevExt->WmiHwData.InterruptType = pDevExt->InterruptMode == Latched
+ ? SERIAL_WMI_INTTYPE_LATCHED : SERIAL_WMI_INTTYPE_LEVEL;
+ pDevExt->WmiHwData.BaseIOAddress = (ULONG_PTR)pDevExt->Controller;
+
+ //
+ // Fill in WMI device state data (as defaults)
+ //
+
+ pDevExt->WmiCommData.BaudRate = pDevExt->CurrentBaud;
+ pDevExt->WmiCommData.BitsPerByte = (pDevExt->LineControl & 0x03) + 5;
+ pDevExt->WmiCommData.ParityCheckEnable = (pDevExt->LineControl & 0x08)
+ ? TRUE : FALSE;
+
+ switch (pDevExt->LineControl & SERIAL_PARITY_MASK) {
+ case SERIAL_NONE_PARITY:
+ pDevExt->WmiCommData.Parity = SERIAL_WMI_PARITY_NONE;
+ break;
+
+ case SERIAL_ODD_PARITY:
+ pDevExt->WmiCommData.Parity = SERIAL_WMI_PARITY_ODD;
+ break;
+
+ case SERIAL_EVEN_PARITY:
+ pDevExt->WmiCommData.Parity = SERIAL_WMI_PARITY_EVEN;
+ break;
+
+ case SERIAL_MARK_PARITY:
+ pDevExt->WmiCommData.Parity = SERIAL_WMI_PARITY_MARK;
+ break;
+
+ case SERIAL_SPACE_PARITY:
+ pDevExt->WmiCommData.Parity = SERIAL_WMI_PARITY_SPACE;
+ break;
+
+ default:
+ ASSERTMSG(0, "Illegal Parity setting for WMI");
+ pDevExt->WmiCommData.Parity = SERIAL_WMI_PARITY_NONE;
+ break;
+ }
+
+ pDevExt->WmiCommData.StopBits = pDevExt->LineControl & SERIAL_STOP_MASK
+ ? (pDevExt->WmiCommData.BitsPerByte == 5 ? SERIAL_WMI_STOP_1_5
+ : SERIAL_WMI_STOP_2) : SERIAL_WMI_STOP_1;
+ pDevExt->WmiCommData.XoffCharacter = pDevExt->SpecialChars.XoffChar;
+ pDevExt->WmiCommData.XoffXmitThreshold = pDevExt->HandFlow.XoffLimit;
+ pDevExt->WmiCommData.XonCharacter = pDevExt->SpecialChars.XonChar;
+ pDevExt->WmiCommData.XonXmitThreshold = pDevExt->HandFlow.XonLimit;
+ pDevExt->WmiCommData.MaximumBaudRate
+ = SerialReportMaxBaudRate(pDevExt->SupportedBauds);
+ pDevExt->WmiCommData.MaximumOutputBufferSize = (UINT32)((ULONG)-1);
+ pDevExt->WmiCommData.MaximumInputBufferSize = (UINT32)((ULONG)-1);
+ pDevExt->WmiCommData.Support16BitMode = FALSE;
+ pDevExt->WmiCommData.SupportDTRDSR = TRUE;
+ pDevExt->WmiCommData.SupportIntervalTimeouts = TRUE;
+ pDevExt->WmiCommData.SupportParityCheck = TRUE;
+ pDevExt->WmiCommData.SupportRTSCTS = TRUE;
+ pDevExt->WmiCommData.SupportXonXoff = TRUE;
+ pDevExt->WmiCommData.SettableBaudRate = TRUE;
+ pDevExt->WmiCommData.SettableDataBits = TRUE;
+ pDevExt->WmiCommData.SettableFlowControl = TRUE;
+ pDevExt->WmiCommData.SettableParity = TRUE;
+ pDevExt->WmiCommData.SettableParityCheck = TRUE;
+ pDevExt->WmiCommData.SettableStopBits = TRUE;
+ pDevExt->WmiCommData.IsBusy = FALSE;
+
+ //
+ // Common error path cleanup. If the status is
+ // bad, get rid of the device extension, device object
+ // and any memory associated with it.
+ //
+
+ExtensionCleanup: ;
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_PNP, "<-- SerialInitController %x\n", status);
+
+ return status;
+}
+
+
+NTSTATUS
+SerialMapHWResources(
+ IN WDFDEVICE Device,
+ IN WDFCMRESLIST PResList,
+ IN WDFCMRESLIST PTrResList,
+ OUT PCONFIG_DATA PConfig
+ )
+/*++
+
+Routine Description:
+
+ This routine will get the configuration information and put
+ it and the translated values into CONFIG_DATA structures.
+
+Arguments:
+
+ Device - Handle to a framework device object.
+
+ Resources - Handle to a collection of framework resource objects.
+ This collection identifies the raw (bus-relative) hardware
+ resources that have been assigned to the device.
+
+ ResourcesTranslated - Handle to a collection of framework resource objects.
+ This collection identifies the translated (system-physical)
+ hardware resources that have been assigned to the device.
+ The resources appear from the CPU's point of view.
+ Use this list of resources to map I/O space and
+ device-accessible memory into virtual address space
+
+Return Value:
+
+ STATUS_SUCCESS if consistant configuration was found - otherwise.
+ returns STATUS_SERIAL_NO_DEVICE_INITED.
+
+--*/
+
+{
+ PSERIAL_DEVICE_EXTENSION pDevExt;
+ NTSTATUS status = STATUS_SUCCESS;
+ ULONG i;
+ PCM_PARTIAL_RESOURCE_DESCRIPTOR pPartialTrResourceDesc, pPartialRawResourceDesc;
+ ULONG gotInt = 0;
+ ULONG gotIO = 0;
+ ULONG ioResIndex = 0;
+ ULONG curIoIndex = 0;
+ ULONG gotMem = 0;
+ BOOLEAN DebugPortInUse = FALSE;
+
+ PAGED_CODE();
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_PNP, "--> SerialMapHWResources\n");
+
+ //
+ // Get the DeviceExtension..
+ //
+ pDevExt = SerialGetDeviceExtension (Device);
+
+ if ((PResList == NULL) || (PTrResList == NULL)) {
+ ASSERT(PResList != NULL);
+ ASSERT(PTrResList != NULL);
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ goto End;
+ }
+
+ for (i = 0; i < WdfCmResourceListGetCount(PTrResList); i++) {
+
+ pPartialTrResourceDesc = WdfCmResourceListGetDescriptor(PTrResList, i);
+ pPartialRawResourceDesc = WdfCmResourceListGetDescriptor(PResList, i);
+
+ switch (pPartialTrResourceDesc->Type) {
+ case CmResourceTypePort:
+
+ ASSERT(!(pPartialTrResourceDesc->u.Port.Length == SERIAL_STATUS_LENGTH));
+
+ if (gotIO == 0) {
+
+ if (curIoIndex == ioResIndex) {
+
+ gotIO = 1;
+ PConfig->TrController = pPartialTrResourceDesc->u.Port.Start;
+
+ if (!PConfig->TrController.LowPart) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP, "Bogus port address %x\n",
+ PConfig->TrController.LowPart);
+ status = STATUS_DEVICE_CONFIGURATION_ERROR;
+ goto End;
+ }
+ //
+ // We need the raw address to check if the debugger is using the com port
+ //
+ PConfig->Controller = pPartialRawResourceDesc->u.Port.Start;
+ PConfig->AddressSpace = pPartialTrResourceDesc->Flags;
+ pDevExt->SerialReadUChar = SerialReadPortUChar;
+ pDevExt->SerialWriteUChar = SerialWritePortUChar;
+
+ } else {
+ curIoIndex++;
+ }
+ }
+
+ break;
+
+ //
+ // If this is 8 bytes long and we haven't found any I/O range,
+ // then this is probably a fancy-pants machine with memory replacing
+ // IO space
+ //
+ case CmResourceTypeMemory:
+
+ ASSERT(!(pPartialTrResourceDesc->u.Port.Length == SERIAL_STATUS_LENGTH));
+
+ if ((gotMem == 0) && (gotIO == 0)
+ && (pPartialTrResourceDesc->u.Memory.Length
+ == (SERIAL_REGISTER_SPAN + SERIAL_STATUS_LENGTH))) {
+ gotMem = 1;
+ PConfig->TrController = pPartialTrResourceDesc->u.Memory.Start;
+
+ if (!PConfig->TrController.LowPart) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP, "Bogus I/O memory address %x\n",
+ PConfig->TrController.LowPart);
+ status = STATUS_DEVICE_CONFIGURATION_ERROR;
+ goto End;
+ }
+
+ PConfig->Controller = pPartialRawResourceDesc->u.Memory.Start;
+ PConfig->AddressSpace = CM_RESOURCE_PORT_MEMORY;
+ PConfig->SpanOfController = SERIAL_REGISTER_SPAN;
+ pDevExt->SerialReadUChar = SerialReadRegisterUChar;
+ pDevExt->SerialWriteUChar = SerialWriteRegisterUChar;
+ }
+ break;
+
+ case CmResourceTypeInterrupt:
+ if (gotInt == 0) {
+ gotInt = 1;
+ PConfig->TrVector = pPartialTrResourceDesc->u.Interrupt.Vector;
+
+ if (!PConfig->TrVector) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP, "Bogus vector 0\n");
+ status = STATUS_DEVICE_CONFIGURATION_ERROR;
+ goto End;
+ }
+
+ if (pPartialTrResourceDesc->ShareDisposition == CmResourceShareShared) {
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_PNP, "Sharing interrupt with other devices \n");
+ } else {
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_PNP, "Interrupt is not shared with other devices\n");
+ }
+
+ PConfig->TrIrql = pPartialTrResourceDesc->u.Interrupt.Level;
+ PConfig->Affinity = pPartialTrResourceDesc->u.Interrupt.Affinity;
+ }
+ break;
+
+ default: break;
+ } // switch (pPartialTrResourceDesc->Type)
+
+ } // for (i = 0; i < WdfCollectionGetCount
+
+ if(!((gotMem || gotIO) && gotInt) )
+ {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ goto End;
+ }
+
+ //
+ // First check what type of AddressSpace this port is in. Then check
+ // if the debugger is using this port. If it is, set DebugPortInUse to TRUE.
+ //
+ if(PConfig->AddressSpace == CM_RESOURCE_PORT_MEMORY) {
+
+ PHYSICAL_ADDRESS KdComPhysical;
+
+ KdComPhysical = MmGetPhysicalAddress(*KdComPortInUse);
+
+ if(KdComPhysical.LowPart == PConfig->Controller.LowPart) {
+ DebugPortInUse = TRUE;
+ }
+
+ } else {
+ //
+ // This compare is done using **untranslated** values since that is what
+ // the kernel shoves in regardless of the architecture.
+ //
+
+ if ((*KdComPortInUse) == (ULongToPtr(PConfig->Controller.LowPart))) {
+ DebugPortInUse = TRUE;
+ }
+ }
+
+ if (DebugPortInUse) {
+
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP, "Kernel debugger is using port at "
+ "address %p\n", *KdComPortInUse);
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP, "Serial driver will not load port\n");
+
+ SerialLogError(
+ pDevExt->DriverObject,
+ NULL,
+ PConfig->TrController,
+ SerialPhysicalZero,
+ 0,
+ 0,
+ 0,
+ 3,
+ STATUS_SUCCESS,
+ SERIAL_KERNEL_DEBUGGER_ACTIVE,
+ pDevExt->DeviceName.Length+sizeof(WCHAR),
+ pDevExt->DeviceName.Buffer,
+ 0,
+ NULL
+ );
+
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ goto End;
+ }
+
+End:
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_PNP, "<-- SerialMapHWResources %x\n", status);
+
+ return status;
+}
+
+VOID
+SerialUnmapHWResources(
+ IN PSERIAL_DEVICE_EXTENSION PDevExt
+ )
+/*++
+
+Routine Description:
+
+ Releases resources (not pool) stored in the device extension.
+
+Arguments:
+
+ PDevExt - Pointer to the device extension to release resources from.
+
+Return Value:
+
+ VOID
+
+--*/
+{
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_PNP, "-->SerialUnMapResources(%p)\n",
+ PDevExt);
+ PAGED_CODE();
+
+ //
+ // If necessary, unmap the device registers.
+ //
+
+ if (PDevExt->UnMapRegisters) {
+ MmUnmapIoSpace(PDevExt->Controller, PDevExt->SpanOfController);
+ PDevExt->UnMapRegisters = FALSE;
+ }
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_PNP, "<--SerialUnMapResources\n");
+}
+
+
+NTSTATUS
+SerialReadSymName(
+ IN WDFDEVICE Device,
+ _Out_writes_bytes_(*SizeOfRegName) PWSTR RegName,
+ _Inout_ PUSHORT SizeOfRegName
+ )
+{
+ NTSTATUS status;
+ WDFKEY hKey;
+ UNICODE_STRING value;
+ UNICODE_STRING valueName;
+ USHORT requiredLength;
+
+ PAGED_CODE();
+
+ value.Buffer = RegName;
+ value.MaximumLength = *SizeOfRegName;
+ value.Length = 0;
+
+ status = WdfDeviceOpenRegistryKey(Device,
+ PLUGPLAY_REGKEY_DEVICE,
+ STANDARD_RIGHTS_ALL,
+ WDF_NO_OBJECT_ATTRIBUTES,
+ &hKey);
+
+ if (NT_SUCCESS (status)) {
+ //
+ // Fetch PortName which contains the suggested REG_SZ symbolic name.
+ //
+
+
+ RtlInitUnicodeString(&valueName, L"PortName");
+
+ status = WdfRegistryQueryUnicodeString (hKey,
+ &valueName,
+ &requiredLength,
+ &value);
+
+ if (!NT_SUCCESS (status)) {
+ //
+ // This is for PCMCIA which currently puts the name under Identifier.
+ //
+
+ RtlInitUnicodeString(&valueName, L"Identifier");
+ status = WdfRegistryQueryUnicodeString (hKey,
+ &valueName,
+ &requiredLength,
+ &value);
+
+ if (!NT_SUCCESS(status)) {
+ //
+ // Hmm. Either we have to pick a name or bail...
+ //
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP, "Getting PortName/Identifier failed - %x\n", status);
+ }
+ }
+
+ WdfRegistryClose(hKey);
+ }
+
+ if(NT_SUCCESS(status)) {
+ //
+ // NULL terminate the string and return number of characters in the string.
+ //
+ if(value.Length > *SizeOfRegName - sizeof(WCHAR)) {
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ *SizeOfRegName = value.Length;
+ RegName[*SizeOfRegName/sizeof(WCHAR)] = UNICODE_NULL;
+ }
+ return status;
+}
+
+
+NTSTATUS
+SerialDoExternalNaming(IN PSERIAL_DEVICE_EXTENSION PDevExt)
+
+/*++
+
+Routine Description:
+
+ This routine will be used to create a symbolic link
+ to the driver name in the given object directory.
+
+ It will also create an entry in the device map for
+ this device - IF we could create the symbolic link.
+
+Arguments:
+
+ Extension - Pointer to the device extension.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ WCHAR pRegName[SYMBOLIC_NAME_LENGTH];
+ USHORT nameSize = sizeof(pRegName);
+ WDFSTRING stringHandle = NULL;
+ WDF_OBJECT_ATTRIBUTES attributes;
+ DECLARE_UNICODE_STRING_SIZE(symbolicLinkName,SYMBOLIC_NAME_LENGTH ) ;
+
+ PAGED_CODE();
+
+ WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
+ attributes.ParentObject = PDevExt->WdfDevice;
+ status = WdfStringCreate(NULL, &attributes, &stringHandle);
+ if(!NT_SUCCESS(status)){
+ goto SerialDoExternalNamingError;
+ }
+
+ status = WdfDeviceRetrieveDeviceName(PDevExt->WdfDevice, stringHandle);
+ if(!NT_SUCCESS(status)){
+ goto SerialDoExternalNamingError;
+ }
+
+ //
+ // Since we are storing the buffer pointer of the string handle in our
+ // extension, we will hold onto string handle until the device is deleted.
+ //
+ WdfStringGetUnicodeString(stringHandle, &PDevExt->DeviceName);
+
+ SerialGetRegistryKeyValue(PDevExt->WdfDevice, L"SerialSkipExternalNaming", &PDevExt->SkipNaming);
+
+ if (PDevExt->SkipNaming) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP, "Skipping external naming due to registry settings\n");
+ return STATUS_SUCCESS;
+ }
+
+ status = SerialReadSymName(PDevExt->WdfDevice, pRegName, &nameSize);
+ if (!NT_SUCCESS(status)) {
+ goto SerialDoExternalNamingError;
+ }
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_PNP, "DosName is %ws\n", pRegName);
+
+ status = RtlUnicodeStringPrintf(&symbolicLinkName,
+ L"%ws%ws",
+ L"\\DosDevices\\",
+ pRegName);
+
+ if (!NT_SUCCESS(status)) {
+ goto SerialDoExternalNamingError;
+ }
+
+ status = WdfDeviceCreateSymbolicLink(PDevExt->WdfDevice, &symbolicLinkName);
+
+ if (!NT_SUCCESS(status)) {
+
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP, "Couldn't create the symbolic link for port %wZ\n", &symbolicLinkName);
+
+ goto SerialDoExternalNamingError;
+
+ }
+
+
+ PDevExt->CreatedSymbolicLink = TRUE;
+
+ status = RtlWriteRegistryValue(RTL_REGISTRY_DEVICEMAP, SERIAL_DEVICE_MAP,
+ PDevExt->DeviceName.Buffer,
+ REG_SZ,
+ pRegName,
+ nameSize + sizeof(WCHAR));
+
+ if (!NT_SUCCESS(status)) {
+
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP, "Couldn't create the device map entry\n"
+ "------- for port %ws\n", PDevExt->DeviceName.Buffer);
+
+ goto SerialDoExternalNamingError;
+ }
+
+ PDevExt->CreatedSerialCommEntry = TRUE;
+
+ //
+ // Make the device visible via a device association as well.
+ // The reference string is the eight digit device index
+ //
+ status = WdfDeviceCreateDeviceInterface(PDevExt->WdfDevice,
+ (LPGUID) &GUID_DEVINTERFACE_COMPORT,
+ NULL);
+
+ if (!NT_SUCCESS (status)) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP, "Couldn't register class association\n"
+ "for port %wZ\n", &PDevExt->DeviceName);
+
+ goto SerialDoExternalNamingError;
+ }
+
+ return status;
+
+ SerialDoExternalNamingError:;
+
+ //
+ // Clean up error conditions
+ //
+
+ PDevExt->DeviceName.Buffer = NULL;
+
+ if (PDevExt->CreatedSerialCommEntry) {
+ _Analysis_assume_(NULL != PDevExt->DeviceName.Buffer);
+ RtlDeleteRegistryValue(RTL_REGISTRY_DEVICEMAP, SERIAL_DEVICE_MAP,
+ PDevExt->DeviceName.Buffer);
+ }
+
+ if(stringHandle) {
+ WdfObjectDelete(stringHandle);
+ }
+
+ return status;
+}
+
+
+VOID
+SerialUndoExternalNaming(IN PSERIAL_DEVICE_EXTENSION Extension)
+
+/*++
+
+Routine Description:
+
+ This routine will be used to delete a symbolic link
+ to the driver name in the given object directory.
+
+ It will also delete an entry in the device map for
+ this device if the symbolic link had been created.
+
+Arguments:
+
+ Extension - Pointer to the device extension.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ NTSTATUS status;
+ PWCHAR deviceName = Extension->DeviceName.Buffer;
+
+ PAGED_CODE();
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_PNP,
+ "In SerialUndoExternalNaming for extension: "
+ "%p of port %ws\n", Extension, deviceName);
+
+ //
+ // Maybe there is nothing for us to do
+ //
+
+ if (Extension->SkipNaming) {
+ return;
+ }
+
+ //
+ // We're cleaning up here. One reason we're cleaning up
+ // is that we couldn't allocate space for the NtNameOfPort.
+ //
+
+ if ((deviceName != NULL) && Extension->CreatedSerialCommEntry) {
+
+ status = RtlDeleteRegistryValue(RTL_REGISTRY_DEVICEMAP,
+ SERIAL_DEVICE_MAP,
+ deviceName);
+ if (!NT_SUCCESS(status)) {
+
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP,
+ "Couldn't delete value entry %ws\n",
+ deviceName);
+
+ }
+ }
+}
+
+VOID
+SerialPurgePendingRequests(PSERIAL_DEVICE_EXTENSION pDevExt)
+/*++
+
+Routine Description:
+
+ This routine completes any irps pending for the passed device object.
+
+Arguments:
+
+ PDevObj - Pointer to the device object whose irps must die.
+
+Return Value:
+
+ VOID
+
+--*/
+{
+ NTSTATUS status;
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_PNP,
+ ">SerialPurgePendingRequests(%p)\n", pDevExt);
+
+ //
+ // Then cancel all the reads and writes.
+ //
+
+ SerialPurgeRequests(pDevExt->WriteQueue, &pDevExt->CurrentWriteRequest);
+
+ SerialPurgeRequests(pDevExt->ReadQueue, &pDevExt->CurrentReadRequest);
+
+ //
+ // Next get rid of purges.
+ //
+
+ SerialPurgeRequests(pDevExt->PurgeQueue, &pDevExt->CurrentPurgeRequest);
+
+ //
+ // Get rid of any mask operations.
+ //
+
+ SerialPurgeRequests( pDevExt->MaskQueue, &pDevExt->CurrentMaskRequest);
+
+ //
+ // Now get rid of pending wait mask request.
+ //
+
+ if (pDevExt->CurrentWaitRequest) {
+
+ status = SerialClearCancelRoutine(pDevExt->CurrentWaitRequest, TRUE );
+ if (NT_SUCCESS(status)) {
+
+ SerialCompleteRequest(pDevExt->CurrentWaitRequest, STATUS_CANCELLED, 0);
+ pDevExt->CurrentWaitRequest = NULL;
+
+ }
+
+ }
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_PNP, "<SerialPurgePendingRequests\n");
+}
+
+BOOLEAN
+SerialDoesPortExist(
+ IN PSERIAL_DEVICE_EXTENSION Extension,
+ IN PUNICODE_STRING InsertString,
+ IN ULONG ForceFifo,
+ IN ULONG LogFifo
+ )
+
+/*++
+
+Routine Description:
+
+ This routine examines several of what might be the serial device
+ registers. It ensures that the bits that should be zero are zero.
+
+ In addition, this routine will determine if the device supports
+ fifo's. If it does it will enable the fifo's and turn on a boolean
+ in the extension that indicates the fifo's presence.
+
+ NOTE: If there is indeed a serial port at the address specified
+ it will absolutely have interrupts inhibited upon return
+ from this routine.
+
+ NOTE: Since this routine should be called fairly early in
+ the device driver initialization, the only element
+ that needs to be filled in is the base register address.
+
+ NOTE: These tests all assume that this code is the only
+ code that is looking at these ports or this memory.
+
+ This is a not to unreasonable assumption even on
+ multiprocessor systems.
+
+Arguments:
+
+ Extension - A pointer to a serial device extension.
+ InsertString - String to place in an error log entry.
+ ForceFifo - !0 forces the fifo to be left on if found.
+ LogFifo - !0 forces a log message if fifo found.
+
+Return Value:
+
+ Will return true if the port really exists, otherwise it
+ will return false.
+
+--*/
+
+{
+
+
+ UCHAR regContents;
+ BOOLEAN returnValue = TRUE;
+ UCHAR oldIERContents;
+ UCHAR oldLCRContents;
+ USHORT value1;
+ USHORT value2;
+ KIRQL oldIrql;
+
+ //
+ // Save of the line control.
+ //
+
+ oldLCRContents = READ_LINE_CONTROL(Extension, Extension->Controller);
+
+ //
+ // Make sure that we are *aren't* accessing the divsior latch.
+ //
+
+ WRITE_LINE_CONTROL(Extension,
+ Extension->Controller,
+ (UCHAR)(oldLCRContents & ~SERIAL_LCR_DLAB)
+ );
+
+ oldIERContents = READ_INTERRUPT_ENABLE(Extension, Extension->Controller);
+
+ //
+ // Go up to power level for a very short time to prevent
+ // any interrupts from this device from coming in.
+ //
+
+ KeRaiseIrql(
+ POWER_LEVEL,
+ &oldIrql
+ );
+
+ WRITE_INTERRUPT_ENABLE(Extension,
+ Extension->Controller,
+ 0x0f
+ );
+
+ value1 = READ_INTERRUPT_ENABLE(Extension, Extension->Controller);
+ value1 = value1 << 8;
+ value1 |= READ_RECEIVE_BUFFER(Extension, Extension->Controller);
+
+ READ_DIVISOR_LATCH(Extension,
+ Extension->Controller,
+ (PSHORT) &value2
+ );
+
+ WRITE_LINE_CONTROL(Extension,
+ Extension->Controller,
+ oldLCRContents
+ );
+
+ //
+ // Put the ier back to where it was before. If we are on a
+ // level sensitive port this should prevent the interrupts
+ // from coming in. If we are on a latched, we don't care
+ // cause the interrupts generated will just get dropped.
+ //
+
+ WRITE_INTERRUPT_ENABLE(Extension,
+ Extension->Controller,
+ oldIERContents
+ );
+
+ KeLowerIrql(oldIrql);
+
+ if (value1 == value2) {
+
+ SerialLogError(
+ Extension->DeviceObject->DriverObject,
+ Extension->DeviceObject,
+ SerialPhysicalZero,
+ SerialPhysicalZero,
+ 0,
+ 0,
+ 0,
+ 62,
+ STATUS_SUCCESS,
+ SERIAL_DLAB_INVALID,
+ InsertString->Length+sizeof(WCHAR),
+ InsertString->Buffer,
+ 0,
+ NULL
+ );
+ returnValue = FALSE;
+ goto AllDone;
+
+ }
+
+ AllDone: ;
+
+
+ //
+ // If we think that there is a serial device then we determine
+ // if a fifo is present.
+ //
+
+ if (returnValue) {
+
+ //
+ // Well, we think it's a serial device. Absolutely
+ // positively, prevent interrupts from occuring.
+ //
+ // We disable all the interrupt enable bits, and
+ // push down all the lines in the modem control
+ // We only needed to push down OUT2 which in
+ // PC's must also be enabled to get an interrupt.
+ //
+
+ DISABLE_ALL_INTERRUPTS(Extension, Extension->Controller);
+
+ WRITE_MODEM_CONTROL(Extension, Extension->Controller, (UCHAR)0);
+
+ //
+ // See if this is a 16550. We do this by writing to
+ // what would be the fifo control register with a bit
+ // pattern that tells the device to enable fifo's.
+ // We then read the iterrupt Id register to see if the
+ // bit pattern is present that identifies the 16550.
+ //
+
+ WRITE_FIFO_CONTROL(Extension,
+ Extension->Controller,
+ SERIAL_FCR_ENABLE
+ );
+
+ regContents = READ_INTERRUPT_ID_REG(Extension, Extension->Controller);
+
+ if (regContents & SERIAL_IIR_FIFOS_ENABLED) {
+
+ //
+ // Save off that the device supports fifos.
+ //
+
+ Extension->FifoPresent = TRUE;
+
+ //
+ // There is a fine new "super" IO chip out there that
+ // will get stuck with a line status interrupt if you
+ // attempt to clear the fifo and enable it at the same
+ // time if data is present. The best workaround seems
+ // to be that you should turn off the fifo read a single
+ // byte, and then re-enable the fifo.
+ //
+
+ WRITE_FIFO_CONTROL(Extension,
+ Extension->Controller,
+ (UCHAR)0
+ );
+
+ READ_RECEIVE_BUFFER(Extension, Extension->Controller);
+
+ //
+ // There are fifos on this card. Set the value of the
+ // receive fifo to interrupt when 4 characters are present.
+ //
+
+ WRITE_FIFO_CONTROL(Extension, Extension->Controller,
+ (UCHAR)(SERIAL_FCR_ENABLE
+ | Extension->RxFifoTrigger
+ | SERIAL_FCR_RCVR_RESET
+ | SERIAL_FCR_TXMT_RESET));
+
+ }
+
+ //
+ // The !Extension->FifoPresent is included in the test so that
+ // broken chips like the WinBond will still work after we test
+ // for the fifo.
+ //
+
+ if (!ForceFifo || !Extension->FifoPresent) {
+
+ Extension->FifoPresent = FALSE;
+ WRITE_FIFO_CONTROL(Extension,
+ Extension->Controller,
+ (UCHAR)0
+ );
+
+ }
+
+ if (Extension->FifoPresent) {
+
+ if (LogFifo) {
+
+ SerialLogError(
+ Extension->DeviceObject->DriverObject,
+ Extension->DeviceObject,
+ SerialPhysicalZero,
+ SerialPhysicalZero,
+ 0,
+ 0,
+ 0,
+ 15,
+ STATUS_SUCCESS,
+ SERIAL_FIFO_PRESENT,
+ InsertString->Length+sizeof(WCHAR),
+ InsertString->Buffer,
+ 0,
+ NULL
+ );
+
+ }
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_PNP,
+ "Fifo's detected at port address: %p\n",
+ Extension->Controller);
+ }
+ }
+
+ return returnValue;
+}
+
+
+
+BOOLEAN
+SerialReset(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This places the hardware in a standard configuration.
+
+ NOTE: This assumes that it is called at interrupt level.
+
+
+Arguments:
+
+ Context - The device extension for serial device
+ being managed.
+
+Return Value:
+
+ Always FALSE.
+
+--*/
+
+{
+
+ PSERIAL_DEVICE_EXTENSION extension = Context;
+ UCHAR regContents;
+ UCHAR oldModemControl;
+ ULONG i;
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ //
+ // Adjust the out2 bit.
+ // This will also prevent any interrupts from occuring.
+ //
+
+ oldModemControl = READ_MODEM_CONTROL(extension, extension->Controller);
+
+ WRITE_MODEM_CONTROL(extension, extension->Controller,
+ (UCHAR)(oldModemControl & ~SERIAL_MCR_OUT2));
+
+ //
+ // Reset the fifo's if there are any.
+ //
+
+ if (extension->FifoPresent) {
+
+ //
+ // There is a fine new "super" IO chip out there that
+ // will get stuck with a line status interrupt if you
+ // attempt to clear the fifo and enable it at the same
+ // time if data is present. The best workaround seems
+ // to be that you should turn off the fifo read a single
+ // byte, and then re-enable the fifo.
+ //
+
+ WRITE_FIFO_CONTROL(extension,
+ extension->Controller,
+ (UCHAR)0
+ );
+
+ READ_RECEIVE_BUFFER(extension, extension->Controller);
+
+ WRITE_FIFO_CONTROL(extension,
+ extension->Controller,
+ (UCHAR)(SERIAL_FCR_ENABLE | extension->RxFifoTrigger |
+ SERIAL_FCR_RCVR_RESET | SERIAL_FCR_TXMT_RESET)
+ );
+
+ }
+
+ //
+ // Make sure that the line control set up correct.
+ //
+ // 1) Make sure that the Divisor latch select is set
+ // up to select the transmit and receive register.
+ //
+ // 2) Make sure that we aren't in a break state.
+ //
+
+ regContents = READ_LINE_CONTROL(extension, extension->Controller);
+ regContents &= ~(SERIAL_LCR_DLAB | SERIAL_LCR_BREAK);
+
+ WRITE_LINE_CONTROL(extension,
+ extension->Controller,
+ regContents
+ );
+
+ //
+ // Read the receive buffer until the line status is
+ // clear. (Actually give up after a 5 reads.)
+ //
+
+ for (i = 0;
+ i < 5;
+ i++
+ ) {
+ #pragma warning(disable: 4127)
+ if (IsNotNEC_98) {
+ #pragma warning(default: 4127)
+ READ_RECEIVE_BUFFER(extension, extension->Controller);
+ if (!(READ_LINE_STATUS(extension, extension->Controller) & 1)) {
+
+ break;
+
+ }
+ } else {
+ //
+ // I get incorrect data when read enpty buffer.
+ // But do not read no data! for PC98!
+ //
+ if (!(READ_LINE_STATUS(extension, extension->Controller) & 1)) {
+
+ break;
+
+ }
+ READ_RECEIVE_BUFFER(extension, extension->Controller);
+ }
+
+ }
+
+ //
+ // Read the modem status until the low 4 bits are
+ // clear. (Actually give up after a 5 reads.)
+ //
+
+ for (i = 0;
+ i < 1000;
+ i++
+ ) {
+
+ if (!(READ_MODEM_STATUS(extension, extension->Controller) & 0x0f)) {
+
+ break;
+
+ }
+
+ }
+
+ //
+ // Now we set the line control, modem control, and the
+ // baud to what they should be.
+ //
+
+ //
+ // See if we have to enable special Auto Flow Control
+ //
+
+ if (extension->TL16C550CAFC) {
+ oldModemControl = READ_MODEM_CONTROL(extension, extension->Controller);
+
+ WRITE_MODEM_CONTROL(extension, extension->Controller,
+ (UCHAR)(oldModemControl | SERIAL_MCR_TL16C550CAFE));
+ }
+
+
+
+ SerialSetLineControl(extension->WdfInterrupt, extension);
+
+ SerialSetupNewHandFlow(
+ extension,
+ &extension->HandFlow
+ );
+
+ SerialHandleModemUpdate(
+ extension,
+ FALSE
+ );
+
+ {
+ SHORT appropriateDivisor;
+ SERIAL_IOCTL_SYNC s;
+
+ SerialGetDivisorFromBaud( extension->ClockRate,
+ extension->CurrentBaud,
+ &appropriateDivisor );
+
+ s.Extension = extension;
+ s.Data = (PVOID) (ULONG_PTR) appropriateDivisor;
+ SerialSetBaud(extension->WdfInterrupt, &s);
+ }
+
+ //
+ // Enable which interrupts we want to receive.
+ //
+ // NOTE NOTE: This does not actually let interrupts
+ // occur. We must still raise the OUT2 bit in the
+ // modem control register. We will do that on open.
+ //
+
+ ENABLE_ALL_INTERRUPTS(extension, extension->Controller);
+
+ //
+ // Read the interrupt id register until the low bit is
+ // set. (Actually give up after a 5 reads.)
+ //
+
+ for (i = 0;
+ i < 5;
+ i++
+ ) {
+
+ if (READ_INTERRUPT_ID_REG(extension, extension->Controller) & 0x01) {
+
+ break;
+
+ }
+
+ }
+
+ //
+ // Now we know that nothing could be transmitting at this point
+ // so we set the HoldingEmpty indicator.
+ //
+
+ extension->HoldingEmpty = TRUE;
+
+ return FALSE;
+}
+
+
+
+PVOID
+SerialGetMappedAddress(
+ PHYSICAL_ADDRESS IoAddress,
+ ULONG NumberOfBytes,
+ ULONG AddressSpace,
+ PBOOLEAN MappedAddress
+ )
+
+/*++
+
+Routine Description:
+
+ This routine maps an IO address to system address space.
+
+Arguments:
+
+ IoAddress - base device address to be mapped.
+ NumberOfBytes - number of bytes for which address is valid.
+ AddressSpace - Denotes whether the address is in io space or memory.
+ MappedAddress - indicates whether the address was mapped.
+ This only has meaning if the address returned
+ is non-null.
+
+Return Value:
+
+ Mapped address
+
+--*/
+
+{
+ PVOID address;
+
+ PAGED_CODE();
+
+ //
+ // Map the device base address into the virtual address space
+ // if the address is in memory space.
+ //
+
+ if (!AddressSpace) {
+
+ address = LocalMmMapIoSpace(IoAddress,
+ NumberOfBytes);
+
+ *MappedAddress = (BOOLEAN)((address)?(TRUE):(FALSE));
+
+
+ } else {
+
+ address = ULongToPtr(IoAddress.LowPart);
+ *MappedAddress = FALSE;
+
+ }
+
+ return address;
+}
+
+VOID
+SerialSetInterruptPolicy(
+ _In_ WDFINTERRUPT WdfInterrupt
+ )
+/*++
+
+Routine Description:
+
+ This routine shows how to set the interrupt policy preferences.
+
+Arguments:
+
+ WdfInterrupt - Interrupt object handle.
+
+Return Value:
+
+ None
+
+--*/
+{
+ WDF_INTERRUPT_EXTENDED_POLICY policyAndGroup;
+#ifdef SERIAL_SELECT_INTERRUPT_GROUP
+ USHORT groupCount = 1;
+ USHORT group = 0;
+ UNICODE_STRING funcName;
+ PFN_KE_GET_ACTIVE_GROUP_COUNT fnKeQueryActiveGroupCount;
+ PFN_KE_QUERY_GROUP_AFFINITY fnKeQueryGroupAffinity;
+ KAFFINITY groupAffinity = (KAFFINITY)1;
+#endif
+
+ WDF_INTERRUPT_EXTENDED_POLICY_INIT(&policyAndGroup);
+ policyAndGroup.Priority = WdfIrqPriorityNormal;
+
+#ifdef SERIAL_SELECT_INTERRUPT_GROUP
+ //
+ // If OS supports groups, find how many they are.
+ //
+ RtlInitUnicodeString(&funcName, L"KeQueryActiveGroupCount");
+ fnKeQueryActiveGroupCount = (PFN_KE_GET_ACTIVE_GROUP_COUNT)
+ MmGetSystemRoutineAddress(&funcName);
+
+ if (fnKeQueryActiveGroupCount != NULL) {
+ groupCount = fnKeQueryActiveGroupCount();
+
+ //
+ // Make sure there is at least one group for the boot processor.
+ //
+ if (0 == groupCount) {
+ groupCount = 1;
+ }
+ }
+
+ if (groupCount <= SERIAL_PREFERRED_INTERRUPT_GROUP) {
+ group = groupCount - 1;
+ }
+ else {
+ group = SERIAL_PREFERRED_INTERRUPT_GROUP;
+ }
+
+ //
+ // Get the group affinity.
+ //
+ RtlInitUnicodeString(&funcName, L"KeQueryGroupAffinity");
+ fnKeQueryGroupAffinity = (PFN_KE_QUERY_GROUP_AFFINITY)
+ MmGetSystemRoutineAddress(&funcName);
+
+ if (fnKeQueryGroupAffinity != NULL) {
+ groupAffinity = fnKeQueryGroupAffinity(group);
+
+ //
+ // Active groups have at least one processor.
+ //
+ if ((KAFFINITY)0 == groupAffinity) {
+ groupAffinity = (KAFFINITY)1;
+ }
+ }
+
+ //
+ // Initialize group.
+ //
+ policyAndGroup.Policy = WdfIrqPolicySpecifiedProcessors;
+ policyAndGroup.TargetProcessorSetAndGroup.Group = group;
+ policyAndGroup.TargetProcessorSetAndGroup.Mask = groupAffinity;
+#endif
+
+ //
+ // Set interrupt policy and group preference.
+ //
+ WdfInterruptSetExtendedPolicy(WdfInterrupt, &policyAndGroup);
+}
+
diff --git a/tests/projects/wdk/kmdf/serial/power.c b/tests/projects/wdk/kmdf/serial/power.c
new file mode 100644
index 000000000..68aad3efa
--- /dev/null
+++ b/tests/projects/wdk/kmdf/serial/power.c
@@ -0,0 +1,331 @@
+/*++
+
+Copyright (c) Microsoft Corporation
+
+Module Name:
+
+ power.c
+
+Abstract:
+
+ This module contains the code that handles the power IRPs for the serial
+ driver.
+
+Environment:
+
+ Kernel mode
+
+--*/
+
+#include "precomp.h"
+
+
+#if defined(EVENT_TRACING)
+#include "power.tmh"
+#endif
+
+
+PCHAR
+DbgDevicePowerString(
+ IN WDF_POWER_DEVICE_STATE Type
+ );
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGESER,SerialEvtDeviceD0Exit)
+#pragma alloc_text(PAGESER,SerialSaveDeviceState)
+#endif // ALLOC_PRAGMA
+
+PCHAR
+DbgDevicePowerString(
+ IN WDF_POWER_DEVICE_STATE Type
+ )
+/*++
+
+Updated Routine Description:
+ DbgDevicePowerString does not change in this stage of the function driver.
+
+--*/
+{
+
+ switch (Type)
+ {
+ case WdfPowerDeviceInvalid:
+ return "WdfPowerDeviceInvalid";
+ case WdfPowerDeviceD0:
+ return "WdfPowerDeviceD0";
+ case WdfPowerDeviceD1:
+ return "WdfPowerDeviceD1";
+ case WdfPowerDeviceD2:
+ return "WdfPowerDeviceD2";
+ case WdfPowerDeviceD3:
+ return "WdfPowerDeviceD3";
+ case WdfPowerDeviceD3Final:
+ return "WdfPowerDeviceD3Final";
+ case WdfPowerDevicePrepareForHibernation:
+ return "WdfPowerDevicePrepareForHibernation";
+ case WdfPowerDeviceMaximum:
+ return "WdfPowerDeviceMaximum";
+ default:
+ return "UnKnown Device Power State";
+ }
+}
+
+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;
+}
+
+
+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");
+}
+
+
+VOID
+SetDeviceIsOpened(IN PSERIAL_DEVICE_EXTENSION PDevExt, IN BOOLEAN DeviceIsOpened, IN BOOLEAN Reopen)
+{
+
+ PDevExt->DeviceIsOpened = DeviceIsOpened;
+ PDevExt->DeviceState.Reopen = Reopen;
+
+}
+
+
+
diff --git a/tests/projects/wdk/kmdf/serial/precomp.h b/tests/projects/wdk/kmdf/serial/precomp.h
new file mode 100644
index 000000000..7c7c5d5ef
--- /dev/null
+++ b/tests/projects/wdk/kmdf/serial/precomp.h
@@ -0,0 +1,18 @@
+
+#include <stddef.h>
+#include <stdarg.h>
+#define WIN9X_COMPAT_SPINLOCK
+#include "ntddk.h"
+#include <wdf.h>
+#define NTSTRSAFE_LIB
+#include <ntstrsafe.h>
+#include "ntddser.h"
+#include <wmilib.h>
+#include <initguid.h> // required for GUID definitions
+#include <wmidata.h>
+#include "serial.h"
+#include "serialp.h"
+#include "serlog.h"
+#include "log.h"
+#include "trace.h"
+
diff --git a/tests/projects/wdk/kmdf/serial/precompsrc.c b/tests/projects/wdk/kmdf/serial/precompsrc.c
new file mode 100644
index 000000000..5944cf515
--- /dev/null
+++ b/tests/projects/wdk/kmdf/serial/precompsrc.c
@@ -0,0 +1 @@
+#include "precomp.h" \ No newline at end of file
diff --git a/tests/projects/wdk/kmdf/serial/purge.c b/tests/projects/wdk/kmdf/serial/purge.c
new file mode 100644
index 000000000..fcc32148e
--- /dev/null
+++ b/tests/projects/wdk/kmdf/serial/purge.c
@@ -0,0 +1,175 @@
+/*++
+
+Copyright (c) Microsoft Corporation
+
+Module Name:
+
+ purge.c
+
+Abstract:
+
+ This module contains the code that is very specific to purge
+ operations in the serial driver
+
+Environment:
+
+ Kernel mode
+
+--*/
+
+#include "precomp.h"
+
+#if defined(EVENT_TRACING)
+#include "purge.tmh"
+#endif
+
+
+VOID
+SerialStartPurge(
+ IN PSERIAL_DEVICE_EXTENSION Extension
+ )
+
+/*++
+
+Routine Description:
+
+ Depending on the mask in the current request, purge the interrupt
+ buffer, the read queue, or the write queue, or all of the above.
+
+Arguments:
+
+ Extension - Pointer to the device extension.
+
+Return Value:
+
+ Will return STATUS_SUCCESS always. This is reasonable
+ since the DPC completion code that calls this routine doesn't
+ care and the purge request always goes through to completion
+ once it's started.
+
+--*/
+
+{
+
+ WDFREQUEST NewRequest;
+ PREQUEST_CONTEXT reqContext;
+
+ do {
+
+ ULONG Mask;
+ reqContext = SerialGetRequestContext(Extension->CurrentPurgeRequest);
+ Mask = *((ULONG *) (reqContext->SystemBuffer));
+
+ if (Mask & SERIAL_PURGE_TXABORT) {
+
+ SerialFlushRequests(
+ Extension->WriteQueue,
+ &Extension->CurrentWriteRequest
+ );
+
+ SerialFlushRequests(
+ Extension->WriteQueue,
+ &Extension->CurrentXoffRequest
+ );
+
+ }
+
+ if (Mask & SERIAL_PURGE_RXABORT) {
+
+ SerialFlushRequests(
+ Extension->ReadQueue,
+ &Extension->CurrentReadRequest
+ );
+
+ }
+
+ if (Mask & SERIAL_PURGE_RXCLEAR) {
+
+ //
+ // Clean out the interrupt buffer.
+ //
+ // Note that we do this under protection of the
+ // the drivers control lock so that we don't hose
+ // the pointers if there is currently a read that
+ // is reading out of the buffer.
+ //
+
+
+ WdfInterruptSynchronize(
+ Extension->WdfInterrupt,
+ SerialPurgeInterruptBuff,
+ Extension
+ );
+
+ }
+
+ reqContext->Status = STATUS_SUCCESS;
+ reqContext->Information = 0;
+
+ SerialGetNextRequest(
+ &Extension->CurrentPurgeRequest,
+ Extension->PurgeQueue,
+ &NewRequest,
+ TRUE,
+ Extension
+ );
+
+ } while (NewRequest);
+
+ return;
+
+}
+
+BOOLEAN
+SerialPurgeInterruptBuff(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This routine simply resets the interrupt (typeahead) buffer.
+
+ NOTE: This routine is being called from WdfInterruptSynchronize.
+
+Arguments:
+
+ Context - Really a pointer to the device extension.
+
+Return Value:
+
+ Always false.
+
+--*/
+
+{
+
+ PSERIAL_DEVICE_EXTENSION Extension = Context;
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ //
+ // The typeahead buffer is by definition empty if there
+ // currently is a read owned by the isr.
+ //
+
+
+ if (Extension->ReadBufferBase == Extension->InterruptReadBuffer) {
+
+ Extension->CurrentCharSlot = Extension->InterruptReadBuffer;
+ Extension->FirstReadableChar = Extension->InterruptReadBuffer;
+ Extension->LastCharSlot = Extension->InterruptReadBuffer +
+ (Extension->BufferSize - 1);
+ Extension->CharsInInterruptBuffer = 0;
+
+ SerialHandleReducedIntBuffer(Extension);
+
+ }
+
+ return FALSE;
+
+}
+
+
diff --git a/tests/projects/wdk/kmdf/serial/qsfile.c b/tests/projects/wdk/kmdf/serial/qsfile.c
new file mode 100644
index 000000000..ad1604131
--- /dev/null
+++ b/tests/projects/wdk/kmdf/serial/qsfile.c
@@ -0,0 +1,180 @@
+/*++
+
+Copyright (c) 1991, 1992, 1993 - 1997 Microsoft Corporation
+
+Module Name:
+
+ qsfile.c
+
+Abstract:
+
+ This module contains the code that is very specific to query/set file
+ operations in the serial driver.
+
+Environment:
+
+ Kernel mode
+
+--*/
+
+#include "precomp.h"
+
+#if defined(EVENT_TRACING)
+#include "qsfile.tmh"
+#endif
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGESRP0,SerialQueryInformationFile)
+#pragma alloc_text(PAGESRP0,SerialSetInformationFile)
+#endif
+
+
+NTSTATUS
+SerialQueryInformationFile(
+ IN WDFDEVICE Device,
+ IN PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to query the end of file information on
+ the opened serial port. Any other file information request
+ is retured with an invalid parameter.
+
+ This routine always returns an end of file of 0.
+
+Arguments:
+
+ DeviceObject - Pointer to the device object for this device
+
+ Irp - Pointer to the IRP for the current request
+
+Return Value:
+
+ The function value is the final status of the call
+
+--*/
+
+{
+ NTSTATUS Status;
+ PIO_STACK_LOCATION IrpSp;
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_PNP, ">SerialQueryInformationFile(%p, %p)\n", Device, Irp);
+
+ PAGED_CODE();
+
+
+ IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ Irp->IoStatus.Information = 0L;
+ Status = STATUS_SUCCESS;
+
+ if (IrpSp->Parameters.QueryFile.FileInformationClass ==
+ FileStandardInformation) {
+
+ if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength <
+ sizeof(FILE_STANDARD_INFORMATION))
+ {
+ Status = STATUS_BUFFER_TOO_SMALL;
+ }
+ else
+ {
+ PFILE_STANDARD_INFORMATION Buf = Irp->AssociatedIrp.SystemBuffer;
+
+ Buf->AllocationSize.QuadPart = 0;
+ Buf->EndOfFile = Buf->AllocationSize;
+ Buf->NumberOfLinks = 0;
+ Buf->DeletePending = FALSE;
+ Buf->Directory = FALSE;
+ Irp->IoStatus.Information = sizeof(FILE_STANDARD_INFORMATION);
+ }
+
+ } else if (IrpSp->Parameters.QueryFile.FileInformationClass ==
+ FilePositionInformation) {
+
+ if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength <
+ sizeof(FILE_POSITION_INFORMATION))
+ {
+ Status = STATUS_BUFFER_TOO_SMALL;
+ }
+ else
+ {
+
+ ((PFILE_POSITION_INFORMATION)Irp->AssociatedIrp.SystemBuffer)->
+ CurrentByteOffset.QuadPart = 0;
+ Irp->IoStatus.Information = sizeof(FILE_POSITION_INFORMATION);
+ }
+
+ } else {
+ Status = STATUS_INVALID_PARAMETER;
+ }
+
+ Irp->IoStatus.Status = Status;
+
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+ return Status;
+
+}
+
+NTSTATUS
+SerialSetInformationFile(
+ IN WDFDEVICE Device,
+ IN PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to set the end of file information on
+ the opened parallel port. Any other file information request
+ is retured with an invalid parameter.
+
+ This routine always ignores the actual end of file since
+ the query information code always returns an end of file of 0.
+
+Arguments:
+
+ DeviceObject - Pointer to the device object for this device
+
+ Irp - Pointer to the IRP for the current request
+
+Return Value:
+
+The function value is the final status of the call
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ PAGED_CODE();
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_PNP, ">SerialSetInformationFile(%p, %p)\n", Device, Irp);
+
+ Irp->IoStatus.Information = 0L;
+ if ((IoGetCurrentIrpStackLocation(Irp)->
+ Parameters.SetFile.FileInformationClass ==
+ FileEndOfFileInformation) ||
+ (IoGetCurrentIrpStackLocation(Irp)->
+ Parameters.SetFile.FileInformationClass ==
+ FileAllocationInformation)) {
+
+ Status = STATUS_SUCCESS;
+
+ } else {
+
+ Status = STATUS_INVALID_PARAMETER;
+
+ }
+
+ Irp->IoStatus.Status = Status;
+
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+ return Status;
+
+}
+
diff --git a/tests/projects/wdk/kmdf/serial/read.c b/tests/projects/wdk/kmdf/serial/read.c
new file mode 100644
index 000000000..ab745fdd7
--- /dev/null
+++ b/tests/projects/wdk/kmdf/serial/read.c
@@ -0,0 +1,1748 @@
+/*++
+
+Copyright (c) Microsoft Corporation
+
+Module Name:
+
+ read.c
+
+Abstract:
+
+ This module contains the code that is very specific to read
+ operations in the serial driver
+
+Environment:
+
+ Kernel mode
+
+--*/
+
+#include "precomp.h"
+
+#if defined(EVENT_TRACING)
+#include "read.tmh"
+#endif
+
+EVT_WDF_REQUEST_CANCEL SerialCancelCurrentRead;
+
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialGrabReadFromIsr;
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialUpdateReadByIsr;
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialUpdateInterruptBuffer;
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialUpdateAndSwitchToUser;
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialUpdateAndSwitchToNew;
+
+ULONG
+SerialGetCharsFromIntBuffer(
+ PSERIAL_DEVICE_EXTENSION Extension
+ );
+
+
+NTSTATUS
+SerialResizeBuffer(
+ IN PSERIAL_DEVICE_EXTENSION Extension
+ );
+
+ULONG
+SerialMoveToNewIntBuffer(
+ PSERIAL_DEVICE_EXTENSION Extension,
+ PUCHAR NewBuffer
+ );
+
+VOID
+SerialEvtIoRead(
+ IN WDFQUEUE Queue,
+ IN WDFREQUEST Request,
+ IN size_t Length
+ )
+
+/*++
+
+Routine Description:
+
+ This is the dispatch routine for reading. It validates the parameters
+ for the read request and if all is ok then it places the request
+ on the work queue.
+
+Arguments:
+
+ Queue - Queue handle
+ Request - Handle to the read request
+ Lenght - Length of the data buffer associated with the request.
+ The default property of the queue is to not dispatch
+ zero lenght read & write requests to the driver and
+ complete is with status success. So we will never get
+ a zero length request.
+
+Return Value:
+
+
+--*/
+
+{
+
+ PSERIAL_DEVICE_EXTENSION extension;
+ NTSTATUS status;
+ WDFDEVICE hDevice;
+ WDF_REQUEST_PARAMETERS params;
+ PREQUEST_CONTEXT reqContext;
+ size_t bufLen;
+
+ hDevice = WdfIoQueueGetDevice(Queue);
+ extension = SerialGetDeviceExtension(hDevice);
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_READ,
+ ">SerialEvtIoRead(%p, 0x%I64x)\n", Request, Length);
+
+ if (SerialCompleteIfError(extension, Request) != STATUS_SUCCESS) {
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_READ, "<SerialEvtIoRead (2) %d\n", STATUS_CANCELLED);
+ return;
+
+ }
+
+ WDF_REQUEST_PARAMETERS_INIT(&params);
+
+ WdfRequestGetParameters(
+ Request,
+ &params
+ );
+
+ //
+ // Initialize the scratch area of the request.
+ //
+ reqContext = SerialGetRequestContext(Request);
+ reqContext->MajorFunction = params.Type;
+ reqContext->Length = (ULONG) Length;
+
+ status = WdfRequestRetrieveOutputBuffer (Request, Length, &reqContext->SystemBuffer, &bufLen);
+
+ if (!NT_SUCCESS (status)) {
+
+ SerialCompleteRequest(Request , status, 0);
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_READ, "<SerialEvtIoRead (5) %X\n", status);
+ return;
+ }
+
+ ASSERT(bufLen == reqContext->Length);
+
+ //
+ // Well it looks like we actually have to do some
+ // work. Put the read on the queue so that we can
+ // process it when our previous reads are done.
+ //
+ SerialStartOrQueue(extension, Request, extension->ReadQueue,
+ &extension->CurrentReadRequest, SerialStartRead);
+
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_READ, "<SerialEvtIoRead (3) %X\n", status);
+
+ return;
+
+}
+
+VOID
+SerialStartRead(
+ IN PSERIAL_DEVICE_EXTENSION Extension
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to start off any read. It initializes
+ the Iostatus fields of the request. It will set up any timers
+ that are used to control the read. It will attempt to complete
+ the read from data already in the interrupt buffer. If the
+ read can be completed quickly it will start off another if
+ necessary.
+
+Arguments:
+
+ Extension - Simply a pointer to the serial device extension.
+
+Return Value:
+
+ This routine will return the status of the first read
+ request. This is useful in that if we have a read that can
+ complete right away (AND there had been nothing in the
+ queue before it) the read could return SUCCESS and the
+ application won't have to do a wait.
+
+--*/
+
+{
+
+ SERIAL_UPDATE_CHAR updateChar;
+
+ WDFREQUEST newRequest;
+
+ BOOLEAN returnWithWhatsPresent;
+ BOOLEAN os2ssreturn;
+ BOOLEAN crunchDownToOne;
+ BOOLEAN useTotalTimer;
+ BOOLEAN useIntervalTimer;
+
+ ULONG multiplierVal = 0;
+ ULONG constantVal = 0;
+
+ LARGE_INTEGER totalTime = {0};
+
+ SERIAL_TIMEOUTS timeoutsForIrp;
+
+ PREQUEST_CONTEXT reqContext;
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_READ,
+ ">SerialStartRead(%p)\n", Extension);
+
+ updateChar.Extension = Extension;
+
+
+ do {
+
+ reqContext = SerialGetRequestContext(Extension->CurrentReadRequest);
+
+ //
+ // Check to see if this is a resize request. If it is
+ // then go to a routine that specializes in that.
+ //
+
+ if (reqContext->MajorFunction != IRP_MJ_READ) {
+
+ NTSTATUS localStatus = SerialResizeBuffer(Extension);
+ UNREFERENCED_PARAMETER(localStatus);
+ ASSERT(NT_SUCCESS(localStatus));
+
+ } else {
+
+ Extension->NumberNeededForRead = reqContext->Length;
+
+ //
+ // Calculate the timeout value needed for the
+ // request. Note that the values stored in the
+ // timeout record are in milliseconds.
+ //
+
+ useTotalTimer = FALSE;
+ returnWithWhatsPresent = FALSE;
+ os2ssreturn = FALSE;
+ crunchDownToOne = FALSE;
+ useIntervalTimer = FALSE;
+
+ //
+ //
+ // CIMEXCIMEX -- this is a lie
+ //
+ // Always initialize the timer objects so that the
+ // completion code can tell when it attempts to
+ // cancel the timers whether the timers had ever
+ // been Set.
+ //
+ // CIMEXCIMEX -- this is the truth
+ //
+ // What we want to do is just make sure the timers are
+ // cancelled to the best of our ability and move on with
+ // life.
+ //
+
+ SerialCancelTimer(Extension->ReadRequestTotalTimer, Extension);
+ SerialCancelTimer(Extension->ReadRequestIntervalTimer, Extension);
+
+ //
+ // We get the *current* timeout values to use for timing
+ // this read.
+ //
+
+
+ timeoutsForIrp = Extension->Timeouts;
+
+ //
+ // Calculate the interval timeout for the read.
+ //
+
+ if (timeoutsForIrp.ReadIntervalTimeout &&
+ (timeoutsForIrp.ReadIntervalTimeout !=
+ MAXULONG)) {
+
+ useIntervalTimer = TRUE;
+
+ Extension->IntervalTime.QuadPart =
+ UInt32x32To64(
+ timeoutsForIrp.ReadIntervalTimeout,
+ 10000
+ );
+
+
+ if (Extension->IntervalTime.QuadPart >=
+ Extension->CutOverAmount.QuadPart) {
+
+ Extension->IntervalTimeToUse =
+ &Extension->LongIntervalAmount;
+
+ } else {
+
+ Extension->IntervalTimeToUse =
+ &Extension->ShortIntervalAmount;
+
+ }
+
+ }
+
+ if (timeoutsForIrp.ReadIntervalTimeout == MAXULONG) {
+
+ //
+ // We need to do special return quickly stuff here.
+ //
+ // 1) If both constant and multiplier are
+ // 0 then we return immediately with whatever
+ // we've got, even if it was zero.
+ //
+ // 2) If constant and multiplier are not MAXULONG
+ // then return immediately if any characters
+ // are present, but if nothing is there, then
+ // use the timeouts as specified.
+ //
+ // 3) If multiplier is MAXULONG then do as in
+ // "2" but return when the first character
+ // arrives.
+ //
+
+ if (!timeoutsForIrp.ReadTotalTimeoutConstant &&
+ !timeoutsForIrp.ReadTotalTimeoutMultiplier) {
+
+ returnWithWhatsPresent = TRUE;
+
+ } else if ((timeoutsForIrp.ReadTotalTimeoutConstant != MAXULONG)
+ &&
+ (timeoutsForIrp.ReadTotalTimeoutMultiplier
+ != MAXULONG)) {
+
+ useTotalTimer = TRUE;
+ os2ssreturn = TRUE;
+ multiplierVal = timeoutsForIrp.ReadTotalTimeoutMultiplier;
+ constantVal = timeoutsForIrp.ReadTotalTimeoutConstant;
+
+ } else if ((timeoutsForIrp.ReadTotalTimeoutConstant != MAXULONG)
+ &&
+ (timeoutsForIrp.ReadTotalTimeoutMultiplier
+ == MAXULONG)) {
+
+ useTotalTimer = TRUE;
+ os2ssreturn = TRUE;
+ crunchDownToOne = TRUE;
+ multiplierVal = 0;
+ constantVal = timeoutsForIrp.ReadTotalTimeoutConstant;
+
+ }
+
+ } else {
+
+ //
+ // If both the multiplier and the constant are
+ // zero then don't do any total timeout processing.
+ //
+
+ if (timeoutsForIrp.ReadTotalTimeoutMultiplier ||
+ timeoutsForIrp.ReadTotalTimeoutConstant) {
+
+ //
+ // We have some timer values to calculate.
+ //
+
+ useTotalTimer = TRUE;
+ multiplierVal = timeoutsForIrp.ReadTotalTimeoutMultiplier;
+ constantVal = timeoutsForIrp.ReadTotalTimeoutConstant;
+
+ }
+
+ }
+
+ if (useTotalTimer) {
+
+ totalTime.QuadPart = ((LONGLONG)(UInt32x32To64(
+ Extension->NumberNeededForRead,
+ multiplierVal
+ )
+ + constantVal))
+ * -10000;
+
+ }
+
+
+ //
+ // We do this copy in the hope of getting most (if not
+ // all) of the characters out of the interrupt buffer.
+ //
+ // Note that we need to protect this operation with a
+ // spinlock since we don't want a purge to hose us.
+ //
+
+ updateChar.CharsCopied = SerialGetCharsFromIntBuffer(Extension);
+
+ //
+ // See if we have any cause to return immediately.
+ //
+
+ if (returnWithWhatsPresent || (!Extension->NumberNeededForRead) ||
+ (os2ssreturn &&
+ reqContext->Information)) {
+
+ //
+ // We got all we needed for this read.
+ // Update the number of characters in the
+ // interrupt read buffer.
+ //
+
+ WdfInterruptSynchronize(
+ Extension->WdfInterrupt,
+ SerialUpdateInterruptBuffer,
+ &updateChar
+ );
+
+ reqContext->Status = STATUS_SUCCESS;
+
+ } else {
+
+ //
+ // The request might go under control of the isr. It
+ // won't hurt to initialize the reference count
+ // right now.
+ //
+
+ SERIAL_INIT_REFERENCE(reqContext);
+
+ //
+ // If we are supposed to crunch the read down to
+ // one character, then update the read length
+ // in the request and truncate the number needed for
+ // read down to one. Note that if we are doing
+ // this crunching, then the information must be
+ // zero (or we would have completed above) and
+ // the number needed for the read must still be
+ // equal to the read length.
+ //
+
+ if (crunchDownToOne) {
+
+ ASSERT(
+ (!reqContext->Information)
+ &&
+ (Extension->NumberNeededForRead == reqContext->Length)
+ );
+
+ Extension->NumberNeededForRead = 1;
+ reqContext->Length = 1;
+
+ }
+
+ //
+ // We still need to get more characters for this read.
+ // synchronize with the isr so that we can update the
+ // number of characters and if necessary it will have the
+ // isr switch to copying into the users buffer.
+ //
+
+ WdfInterruptSynchronize(
+ Extension->WdfInterrupt,
+ SerialUpdateAndSwitchToUser,
+ &updateChar
+ );
+
+ if (!updateChar.Completed) {
+
+ SerialSetCancelRoutine(Extension->CurrentReadRequest,
+ SerialCancelCurrentRead);
+
+ //
+ // The request still isn't complete. The
+ // completion routines will end up reinvoking
+ // this routine. So we simply leave.
+ //
+ // First thought we should start off the total
+ // timer for the read and increment the reference
+ // count that the total timer has on the current
+ // request. Note that this is safe, because even if
+ // the io has been satisfied by the isr it can't
+ // complete yet because we still own the cancel
+ // spinlock.
+ //
+
+ if (useTotalTimer) {
+ BOOLEAN result;
+
+ result = SerialSetTimer(
+ Extension->ReadRequestTotalTimer,
+ totalTime
+ );
+
+ if(result == FALSE) {
+ SERIAL_SET_REFERENCE(
+ reqContext,
+ SERIAL_REF_TOTAL_TIMER
+ );
+ }
+
+ }
+
+ if (useIntervalTimer) {
+
+ BOOLEAN result;
+
+ KeQuerySystemTime(
+ &Extension->LastReadTime
+
+ );
+ result = SerialSetTimer(
+ Extension->ReadRequestIntervalTimer,
+ *Extension->IntervalTimeToUse
+ );
+
+ if(result == FALSE) {
+ SERIAL_SET_REFERENCE(
+ reqContext,
+ SERIAL_REF_INT_TIMER
+ );
+ }
+
+ }
+
+ break;
+
+ } else {
+
+ reqContext->Status = STATUS_SUCCESS;
+ }
+
+ }
+
+ }
+
+ //
+ // Well the operation is complete.
+ //
+
+ SerialGetNextRequest(&Extension->CurrentReadRequest,
+ Extension->ReadQueue,
+ &newRequest, TRUE, Extension);
+
+ } while (newRequest);
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_READ, "<SerialStartRead \n");
+
+ return;
+
+}
+
+
+VOID
+SerialCompleteRead(
+ IN WDFDPC Dpc
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is merely used to complete any read that
+ ended up being used by the Isr. It assumes that the
+ status and the information fields of the request are already
+ correctly filled in.
+
+Arguments:
+
+ Dpc - Not Used.
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ PSERIAL_DEVICE_EXTENSION extension = NULL;
+
+ extension = SerialGetDeviceExtension(WdfDpcGetParentObject(Dpc));
+
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_READ, ">SerialCompleteRead(%p)\n",
+ extension);
+
+ //
+ // We set this to indicate to the interval timer
+ // that the read has completed.
+ //
+ // Recall that the interval timer dpc can be lurking in some
+ // DPC queue.
+ //
+
+ extension->CountOnLastRead = SERIAL_COMPLETE_READ_COMPLETE;
+
+ SerialTryToCompleteCurrent(
+ extension,
+ NULL,
+ STATUS_SUCCESS,
+ &extension->CurrentReadRequest,
+ extension->ReadQueue,
+ extension->ReadRequestIntervalTimer,
+ extension->ReadRequestTotalTimer,
+ SerialStartRead,
+ SerialGetNextRequest,
+ SERIAL_REF_ISR
+ );
+
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_READ, "<SerialCompleteRead\n");
+}
+
+
+VOID
+SerialCancelCurrentRead(
+ WDFREQUEST Request
+ )
+/*++
+
+Routine Description:
+
+ This routine is used to cancel the current read.
+
+Arguments:
+
+ Device - Wdf device handle
+
+ Request - Pointer to the WDFREQUEST to be canceled.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ PSERIAL_DEVICE_EXTENSION extension = NULL;
+ WDFDEVICE device = WdfIoQueueGetDevice(WdfRequestGetIoQueue(Request));
+
+ UNREFERENCED_PARAMETER(Request);
+
+ extension = SerialGetDeviceExtension(device);
+
+ //
+ // We set this to indicate to the interval timer
+ // that the read has encountered a cancel.
+ //
+ // Recall that the interval timer dpc can be lurking in some
+ // DPC queue.
+ //
+
+ extension->CountOnLastRead = SERIAL_COMPLETE_READ_CANCEL;
+
+ SerialTryToCompleteCurrent(
+ extension,
+ SerialGrabReadFromIsr,
+ STATUS_CANCELLED,
+ &extension->CurrentReadRequest,
+ extension->ReadQueue,
+ extension->ReadRequestIntervalTimer,
+ extension->ReadRequestTotalTimer,
+ SerialStartRead,
+ SerialGetNextRequest,
+ SERIAL_REF_CANCEL
+ );
+
+}
+
+
+BOOLEAN
+SerialGrabReadFromIsr(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to grab (if possible) the request from the
+ isr. If it finds that the isr still owns the request it grabs
+ the ipr away (updating the number of characters copied into the
+ users buffer). If it grabs it away it also decrements the
+ reference count on the request since it no longer belongs to the
+ isr (and the dpc that would complete it).
+
+ NOTE: This routine assumes that if the current buffer that the
+ ISR is copying characters into is the interrupt buffer then
+ the dpc has already been queued.
+
+ NOTE: This routine is being called from WdfInterruptSynchronize.
+
+ NOTE: This routine assumes that it is called with the cancel spin
+ lock held.
+
+Arguments:
+
+ Context - Really a pointer to the device extension.
+
+Return Value:
+
+ Always false.
+
+--*/
+
+{
+
+ PSERIAL_DEVICE_EXTENSION extension = Context;
+ PREQUEST_CONTEXT reqContext;
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ reqContext = SerialGetRequestContext(extension->CurrentReadRequest);
+
+ if (extension->ReadBufferBase !=
+ extension->InterruptReadBuffer) {
+
+ //
+ // We need to set the information to the number of characters
+ // that the read wanted minus the number of characters that
+ // didn't get read into the interrupt buffer.
+ //
+
+ reqContext->Information = reqContext->Length -
+ ((extension->LastCharSlot - extension->CurrentCharSlot) + 1);
+
+ //
+ // Switch back to the interrupt buffer.
+ //
+
+ extension->ReadBufferBase = extension->InterruptReadBuffer;
+ extension->CurrentCharSlot = extension->InterruptReadBuffer;
+ extension->FirstReadableChar = extension->InterruptReadBuffer;
+ extension->LastCharSlot = extension->InterruptReadBuffer +
+ (extension->BufferSize - 1);
+ extension->CharsInInterruptBuffer = 0;
+
+ SERIAL_CLEAR_REFERENCE(
+ reqContext,
+ SERIAL_REF_ISR
+ );
+
+ }
+
+ return FALSE;
+
+}
+
+VOID
+SerialReadTimeout(
+ IN WDFTIMER Timer
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to complete a read because its total
+ timer has expired.
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ PSERIAL_DEVICE_EXTENSION extension = NULL;
+
+ extension = SerialGetDeviceExtension(WdfTimerGetParentObject(Timer));
+
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_READ, ">SerialReadTimeout(%p)\n",
+ extension);
+
+ //
+ // We set this to indicate to the interval timer
+ // that the read has completed due to total timeout.
+ //
+ // Recall that the interval timer dpc can be lurking in some
+ // DPC queue.
+ //
+
+ extension->CountOnLastRead = SERIAL_COMPLETE_READ_TOTAL;
+
+ SerialTryToCompleteCurrent(
+ extension,
+ SerialGrabReadFromIsr,
+ STATUS_TIMEOUT,
+ &extension->CurrentReadRequest,
+ extension->ReadQueue,
+ extension->ReadRequestIntervalTimer,
+ extension->ReadRequestTotalTimer,
+ SerialStartRead,
+ SerialGetNextRequest,
+ SERIAL_REF_TOTAL_TIMER
+ );
+
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_READ, "<SerialReadTimeout\n");
+}
+
+
+BOOLEAN
+SerialUpdateReadByIsr(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to update the count of characters read
+ by the isr since the last interval timer experation.
+
+ NOTE: This routine is being called from WdfInterruptSynchronize.
+
+ NOTE: This routine assumes that it is called with the cancel spin
+ lock held.
+
+Arguments:
+
+ Context - Really a pointer to the device extension.
+
+Return Value:
+
+ Always false.
+
+--*/
+
+{
+
+ PSERIAL_DEVICE_EXTENSION extension = Context;
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ extension->CountOnLastRead = extension->ReadByIsr;
+ extension->ReadByIsr = 0;
+
+ return FALSE;
+
+}
+
+
+VOID
+SerialIntervalReadTimeout(
+ IN WDFTIMER Timer
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used timeout the request if the time between
+ characters exceed the interval time. A global is kept in
+ the device extension that records the count of characters read
+ the last the last time this routine was invoked (This dpc
+ will resubmit the timer if the count has changed). If the
+ count has not changed then this routine will attempt to complete
+ the request. Note the special case of the last count being zero.
+ The timer isn't really in effect until the first character is
+ read.
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PSERIAL_DEVICE_EXTENSION extension = NULL;
+
+ extension = SerialGetDeviceExtension(WdfTimerGetParentObject(Timer));
+
+
+ //SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_READ, ">SerialIntervalReadTimeout(%p)\n",
+ // extension);
+
+ if (extension->CountOnLastRead == SERIAL_COMPLETE_READ_TOTAL) {
+
+ //
+ // This value is only set by the total
+ // timer to indicate that it has fired.
+ // If so, then we should simply try to complete.
+ //
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_INIT, "in SERIAL_COMPLETE_READ_TOTAL\n");
+
+ SerialTryToCompleteCurrent(
+ extension,
+ SerialGrabReadFromIsr,
+ STATUS_TIMEOUT,
+ &extension->CurrentReadRequest,
+ extension->ReadQueue,
+ extension->ReadRequestIntervalTimer,
+ extension->ReadRequestTotalTimer,
+ SerialStartRead,
+ SerialGetNextRequest,
+ SERIAL_REF_INT_TIMER
+ );
+
+ } else if (extension->CountOnLastRead == SERIAL_COMPLETE_READ_COMPLETE) {
+
+ //
+ // This value is only set by the regular
+ // completion routine.
+ //
+ // If so, then we should simply try to complete.
+ //
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_INIT, "in SERIAL_COMPLETE_READ_COMPLETE\n");
+
+ SerialTryToCompleteCurrent(
+ extension,
+ SerialGrabReadFromIsr,
+ STATUS_SUCCESS,
+ &extension->CurrentReadRequest,
+ extension->ReadQueue,
+ extension->ReadRequestIntervalTimer,
+ extension->ReadRequestTotalTimer,
+ SerialStartRead,
+ SerialGetNextRequest,
+ SERIAL_REF_INT_TIMER
+ );
+
+ } else if (extension->CountOnLastRead == SERIAL_COMPLETE_READ_CANCEL) {
+
+ //
+ // This value is only set by the cancel
+ // read routine.
+ //
+ // If so, then we should simply try to complete.
+ //
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_INIT, "in SERIAL_COMPLETE_READ_CANCEL\n");
+
+ SerialTryToCompleteCurrent(
+ extension,
+ SerialGrabReadFromIsr,
+ STATUS_CANCELLED,
+ &extension->CurrentReadRequest,
+ extension->ReadQueue,
+ extension->ReadRequestIntervalTimer,
+ extension->ReadRequestTotalTimer,
+ SerialStartRead,
+ SerialGetNextRequest,
+ SERIAL_REF_INT_TIMER
+ );
+
+ } else if (extension->CountOnLastRead || extension->ReadByIsr) {
+
+ //
+ // Something has happened since we last came here. We
+ // check to see if the ISR has read in any more characters.
+ // If it did then we should update the isr's read count
+ // and resubmit the timer.
+ //
+
+ if (extension->ReadByIsr) {
+
+ WdfInterruptSynchronize(
+ extension->WdfInterrupt,
+ SerialUpdateReadByIsr,
+ extension
+ );
+
+ //
+ // Save off the "last" time something was read.
+ // As we come back to this routine we will compare
+ // the current time to the "last" time. If the
+ // difference is ever larger then the interval
+ // requested by the user, then time out the request.
+ //
+
+ KeQuerySystemTime(
+ &extension->LastReadTime
+ );
+
+ SerialSetTimer(
+ extension->ReadRequestIntervalTimer,
+ *extension->IntervalTimeToUse
+ );
+
+ } else {
+
+ //
+ // Take the difference between the current time
+ // and the last time we had characters and
+ // see if it is greater then the interval time.
+ // if it is, then time out the request. Otherwise
+ // go away again for a while.
+ //
+
+ //
+ // No characters read in the interval time. Kill
+ // this read.
+ //
+
+ LARGE_INTEGER currentTime;
+
+ KeQuerySystemTime(
+ &currentTime
+ );
+
+ if ((currentTime.QuadPart - extension->LastReadTime.QuadPart) >=
+ extension->IntervalTime.QuadPart) {
+
+ SerialTryToCompleteCurrent(
+ extension,
+ SerialGrabReadFromIsr,
+ STATUS_TIMEOUT,
+ &extension->CurrentReadRequest,
+ extension->ReadQueue,
+ extension->ReadRequestIntervalTimer,
+ extension->ReadRequestTotalTimer,
+ SerialStartRead,
+ SerialGetNextRequest,
+ SERIAL_REF_INT_TIMER
+ );
+
+ } else {
+
+ SerialSetTimer(
+ extension->ReadRequestIntervalTimer,
+ *extension->IntervalTimeToUse
+ );
+
+ }
+
+
+ }
+
+ } else {
+
+ //
+ // Timer doesn't really start until the first character.
+ // So we should simply resubmit ourselves.
+ //
+
+ SerialSetTimer(
+ extension->ReadRequestIntervalTimer,
+ *extension->IntervalTimeToUse
+ );
+
+ }
+
+
+ //SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_READ, "<SerialIntervalReadTimeout\n");
+}
+
+
+ULONG
+SerialGetCharsFromIntBuffer(
+ PSERIAL_DEVICE_EXTENSION Extension
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to copy any characters out of the interrupt
+ buffer into the users buffer. It will be reading values that
+ are updated with the ISR but this is safe since this value is
+ only decremented by synchronization routines. This routine will
+ return the number of characters copied so some other routine
+ can call a synchronization routine to update what is seen at
+ interrupt level.
+
+Arguments:
+
+ Extension - A pointer to the device extension.
+
+Return Value:
+
+ The number of characters that were copied into the user
+ buffer.
+
+--*/
+
+{
+
+ //
+ // This value will be the number of characters that this
+ // routine returns. It will be the minimum of the number
+ // of characters currently in the buffer or the number of
+ // characters required for the read.
+ //
+ ULONG numberOfCharsToGet;
+
+ //
+ // This holds the number of characters between the first
+ // readable character and - the last character we will read or
+ // the real physical end of the buffer (not the last readable
+ // character).
+ //
+ ULONG firstTryNumberToGet;
+
+ PREQUEST_CONTEXT reqContext = SerialGetRequestContext(Extension->CurrentReadRequest);
+
+ //
+ // The minimum of the number of characters we need and
+ // the number of characters available
+ //
+
+ numberOfCharsToGet = Extension->CharsInInterruptBuffer;
+
+ if (numberOfCharsToGet > Extension->NumberNeededForRead) {
+
+ numberOfCharsToGet = Extension->NumberNeededForRead;
+
+ }
+
+ if (numberOfCharsToGet) {
+
+ //
+ // This will hold the number of characters between the
+ // first available character and the end of the buffer.
+ // Note that the buffer could wrap around but for the
+ // purposes of the first copy we don't care about that.
+ //
+
+ firstTryNumberToGet = (ULONG)(Extension->LastCharSlot -
+ Extension->FirstReadableChar) + 1;
+
+ if (firstTryNumberToGet > numberOfCharsToGet) {
+
+ //
+ // The characters don't wrap. Actually they may wrap but
+ // we don't care for the purposes of this read since the
+ // characters we need are available before the wrap.
+ //
+
+ RtlMoveMemory(
+ ((PUCHAR)(reqContext->SystemBuffer))
+ + (reqContext->Length - Extension->NumberNeededForRead),
+ Extension->FirstReadableChar,
+ numberOfCharsToGet
+ );
+
+ Extension->NumberNeededForRead -= numberOfCharsToGet;
+
+ //
+ // We now will move the pointer to the first character after
+ // what we just copied into the users buffer.
+ //
+ // We need to check if the stream of readable characters
+ // is wrapping around to the beginning of the buffer.
+ //
+ // Note that we may have just taken the last characters
+ // at the end of the buffer.
+ //
+
+ if ((Extension->FirstReadableChar + (numberOfCharsToGet - 1)) ==
+ Extension->LastCharSlot) {
+
+ Extension->FirstReadableChar = Extension->InterruptReadBuffer;
+
+ } else {
+
+ Extension->FirstReadableChar += numberOfCharsToGet;
+
+ }
+
+ } else {
+
+ //
+ // The characters do wrap. Get up until the end of the buffer.
+ //
+
+ RtlMoveMemory(
+ ((PUCHAR)(reqContext->SystemBuffer))
+ + (reqContext->Length - Extension->NumberNeededForRead),
+ Extension->FirstReadableChar,
+ firstTryNumberToGet
+ );
+
+ Extension->NumberNeededForRead -= firstTryNumberToGet;
+
+ //
+ // Now get the rest of the characters from the beginning of the
+ // buffer.
+ //
+
+ RtlMoveMemory(
+ ((PUCHAR)(reqContext->SystemBuffer))
+ + (reqContext->Length - Extension->NumberNeededForRead),
+ Extension->InterruptReadBuffer,
+ numberOfCharsToGet - firstTryNumberToGet
+ );
+
+ Extension->FirstReadableChar = Extension->InterruptReadBuffer +
+ (numberOfCharsToGet -
+ firstTryNumberToGet);
+
+ Extension->NumberNeededForRead -= (numberOfCharsToGet -
+ firstTryNumberToGet);
+
+ }
+
+ }
+
+ reqContext->Information += numberOfCharsToGet;
+ return numberOfCharsToGet;
+
+}
+
+
+BOOLEAN
+SerialUpdateInterruptBuffer(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to update the number of characters that
+ remain in the interrupt buffer. We need to use this routine
+ since the count could be updated during the update by execution
+ of the ISR.
+
+ NOTE: This is called by WdfInterruptSynchronize.
+
+Arguments:
+
+ Context - Points to a structure that contains a pointer to the
+ device extension and count of the number of characters
+ that we previously copied into the users buffer. The
+ structure actually has a third field that we don't
+ use in this routine.
+
+Return Value:
+
+ Always FALSE.
+
+--*/
+
+{
+
+ PSERIAL_UPDATE_CHAR update = Context;
+ PSERIAL_DEVICE_EXTENSION extension = update->Extension;
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ ASSERT(extension->CharsInInterruptBuffer >= update->CharsCopied);
+ extension->CharsInInterruptBuffer -= update->CharsCopied;
+
+ //
+ // Deal with flow control if necessary.
+ //
+
+ SerialHandleReducedIntBuffer(extension);
+
+
+ return FALSE;
+
+}
+
+
+BOOLEAN
+SerialUpdateAndSwitchToUser(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This routine gets the (hopefully) few characters that
+ remain in the interrupt buffer after the first time we tried
+ to get them out. If we still don't have enough characters
+ to satisfy the read it will then we set things up so that the
+ ISR uses the user buffer copy into.
+
+ This routine is also used to update a count that is maintained
+ by the ISR to keep track of the number of characters in its buffer.
+
+ NOTE: This is called by WdfInterruptSynchronize.
+
+Arguments:
+
+ Context - Points to a structure that contains a pointer to the
+ device extension, a count of the number of characters
+ that we previously copied into the users buffer, and
+ a boolean that we will set that defines whether we
+ switched the ISR to copy into the users buffer.
+
+Return Value:
+
+ Always FALSE.
+
+--*/
+
+{
+
+ PSERIAL_UPDATE_CHAR updateChar = Context;
+ PSERIAL_DEVICE_EXTENSION extension = updateChar->Extension;
+ PREQUEST_CONTEXT reqContext;
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ reqContext = SerialGetRequestContext(extension->CurrentReadRequest);
+
+ SerialUpdateInterruptBuffer(extension->WdfInterrupt, Context);
+
+ //
+ // There are more characters to get to satisfy this read.
+ // Copy any characters that have arrived since we got
+ // the last batch.
+ //
+
+ updateChar->CharsCopied = SerialGetCharsFromIntBuffer(extension);
+
+ SerialUpdateInterruptBuffer(extension->WdfInterrupt, Context);
+
+ //
+ // No more new characters will be "received" until we exit
+ // this routine. We again check to make sure that we
+ // haven't satisfied this read, and if we haven't we set things
+ // up so that the ISR copies into the user buffer.
+ //
+
+ if (extension->NumberNeededForRead) {
+
+ //
+ // We shouldn't be switching unless there are no
+ // characters left.
+ //
+
+ ASSERT(!extension->CharsInInterruptBuffer);
+
+ //
+ // We use the following to values to do inteval timing.
+ //
+ // CountOnLastRead is mostly used to simply prevent
+ // the interval timer from timing out before any characters
+ // are read. (Interval timing should only be effective
+ // after the first character is read.)
+ //
+ // After the first time the interval timer fires and
+ // characters have be read we will simply update with
+ // the value of ReadByIsr and then set ReadByIsr to zero.
+ // (We do that in a synchronization routine.
+ //
+ // If the interval timer dpc routine ever encounters
+ // ReadByIsr == 0 when CountOnLastRead is non-zero it
+ // will timeout the read.
+ //
+ // (Note that we have a special case of CountOnLastRead
+ // < 0. This is done by the read completion routines other
+ // than the total timeout dpc to indicate that the total
+ // timeout has expired.)
+ //
+
+ extension->CountOnLastRead = (LONG)reqContext->Information;
+
+ extension->ReadByIsr = 0;
+
+ //
+ // By compareing the read buffer base address to the
+ // the base address of the interrupt buffer the ISR
+ // can determine whether we are using the interrupt
+ // buffer or the user buffer.
+ //
+
+ extension->ReadBufferBase = reqContext->SystemBuffer;
+
+ //
+ // The current char slot is after the last copied in
+ // character. We know there is always room since we
+ // we wouldn't have gotten here if there wasn't.
+ //
+
+ extension->CurrentCharSlot = extension->ReadBufferBase +
+ reqContext->Information;
+
+ //
+ // The last position that a character can go is on the
+ // last byte of user buffer. While the actual allocated
+ // buffer space may be bigger, we know that there is at
+ // least as much as the read length.
+ //
+
+ extension->LastCharSlot = extension->ReadBufferBase +
+ (reqContext->Length - 1);
+#if 0 // We set the cancel before calling this routine in StartRead
+ //
+ // Mark the request as being in a cancelable state.
+ //
+ IoSetCancelRoutine(
+ extension->CurrentReadIrp,
+ SerialCancelCurrentRead
+ );
+
+ SERIAL_SET_REFERENCE(
+ reqContext,
+ SERIAL_REF_CANCEL
+ );
+#endif
+ //
+ // Increment the reference count twice.
+ //
+ // Once for the Isr owning the request and once
+ // because the cancel routine has a reference
+ // to it.
+ //
+
+ SERIAL_SET_REFERENCE(
+ reqContext,
+ SERIAL_REF_ISR
+ );
+
+ updateChar->Completed = FALSE;
+
+ } else {
+
+ updateChar->Completed = TRUE;
+
+ }
+
+ return FALSE;
+
+}
+//
+// We use this structure only to communicate to the synchronization
+// routine when we are switching to the resized buffer.
+//
+typedef struct _SERIAL_RESIZE_PARAMS {
+ PSERIAL_DEVICE_EXTENSION Extension;
+ PUCHAR OldBuffer;
+ PUCHAR NewBuffer;
+ ULONG NewBufferSize;
+ ULONG NumberMoved;
+ } SERIAL_RESIZE_PARAMS,*PSERIAL_RESIZE_PARAMS;
+
+
+NTSTATUS
+SerialResizeBuffer(
+ IN PSERIAL_DEVICE_EXTENSION Extension
+ )
+
+/*++
+
+Routine Description:
+
+ This routine will process the resize buffer request.
+ If size requested for the RX buffer is smaller than
+ the current buffer then we will simply return
+ STATUS_SUCCESS. (We don't want to make buffers smaller.
+ If we did that then we all of a sudden have "overrun"
+ problems to deal with as well as flow control to deal
+ with - very painful.) We ignore the TX buffer size
+ request since we don't use a TX buffer.
+
+Arguments:
+
+ Extension - Pointer to the device extension for the port.
+
+Return Value:
+
+ STATUS_SUCCESS if everything worked out ok.
+ STATUS_INSUFFICIENT_RESOURCES if we couldn't allocate the
+ memory for the buffer.
+
+--*/
+
+{
+
+ PREQUEST_CONTEXT reqContext = SerialGetRequestContext(Extension->CurrentReadRequest);
+ PSERIAL_QUEUE_SIZE rs = reqContext->SystemBuffer;
+
+ PVOID newBuffer = reqContext->Type3InputBuffer;
+
+
+ reqContext->Type3InputBuffer = NULL;
+ reqContext->Information = 0L;
+ reqContext->Status = STATUS_SUCCESS;
+
+ if (rs->InSize <= Extension->BufferSize) {
+
+ //
+ // Nothing to do. We don't make buffers smaller. Just
+ // agree with the user. We must deallocate the memory
+ // that was already allocated in the ioctl dispatch routine.
+ //
+
+ ExFreePool(newBuffer);
+
+ } else {
+
+ SERIAL_RESIZE_PARAMS rp;
+
+ //
+ // Hmmm, looks like we actually have to go
+ // through with this. We need to move all the
+ // data that is in the current buffer into this
+ // new buffer. We'll do this in two steps.
+ //
+ // First we go up to dispatch level and try to
+ // move as much as we can without stopping the
+ // ISR from running. We go up to dispatch level
+ // by acquiring the control lock. We do it at
+ // dispatch using the control lock so that:
+ //
+ // 1) We can't be context switched in the middle
+ // of the move. Our pointers into the buffer
+ // could be *VERY* stale by the time we got back.
+ //
+ // 2) We use the control lock since we don't want
+ // some pesky purge request to come along while
+ // we are trying to move.
+ //
+ // After the move, but while we still hold the control
+ // lock, we synch with the ISR and get those last
+ // (hopefully) few characters that have come in since
+ // we started the copy. We switch all of our pointers,
+ // counters, and such to point to this new buffer. NOTE:
+ // we need to be careful. If the buffer we were using
+ // was not the default one created when we initialized
+ // the device (i.e. it was created via a previous WDFREQUEST of
+ // this type), we should deallocate it.
+ //
+
+ rp.Extension = Extension;
+ rp.OldBuffer = Extension->InterruptReadBuffer;
+ rp.NewBuffer = newBuffer;
+ rp.NewBufferSize = rs->InSize;
+
+ rp.NumberMoved = SerialMoveToNewIntBuffer(
+ Extension,
+ newBuffer
+ );
+
+ WdfInterruptSynchronize(
+ Extension->WdfInterrupt,
+ SerialUpdateAndSwitchToNew,
+ &rp
+ );
+
+ //
+ // Free up the memory that the old buffer consumed.
+ //
+
+ ExFreePool(rp.OldBuffer);
+
+ }
+
+ return STATUS_SUCCESS;
+
+}
+
+
+ULONG
+SerialMoveToNewIntBuffer(
+ PSERIAL_DEVICE_EXTENSION Extension,
+ PUCHAR NewBuffer
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to copy any characters out of the interrupt
+ buffer into the "new" buffer. It will be reading values that
+ are updated with the ISR but this is safe since this value is
+ only decremented by synchronization routines. This routine will
+ return the number of characters copied so some other routine
+ can call a synchronization routine to update what is seen at
+ interrupt level.
+
+Arguments:
+
+ Extension - A pointer to the device extension.
+ NewBuffer - Where the characters are to be move to.
+
+Return Value:
+
+ The number of characters that were copied into the user
+ buffer.
+
+--*/
+
+{
+
+ ULONG numberOfCharsMoved = Extension->CharsInInterruptBuffer;
+
+
+ if (numberOfCharsMoved) {
+
+ //
+ // This holds the number of characters between the first
+ // readable character and the last character we will read or
+ // the real physical end of the buffer (not the last readable
+ // character).
+ //
+ ULONG firstTryNumberToGet = (ULONG)(Extension->LastCharSlot -
+ Extension->FirstReadableChar) + 1;
+
+ if (firstTryNumberToGet >= numberOfCharsMoved) {
+
+ //
+ // The characters don't wrap.
+ //
+
+ RtlMoveMemory(
+ NewBuffer,
+ Extension->FirstReadableChar,
+ numberOfCharsMoved
+ );
+
+ if ((Extension->FirstReadableChar+(numberOfCharsMoved-1)) ==
+ Extension->LastCharSlot) {
+
+ Extension->FirstReadableChar = Extension->InterruptReadBuffer;
+
+ } else {
+
+ Extension->FirstReadableChar += numberOfCharsMoved;
+
+ }
+
+ } else {
+
+ //
+ // The characters do wrap. Get up until the end of the buffer.
+ //
+
+ RtlMoveMemory(
+ NewBuffer,
+ Extension->FirstReadableChar,
+ firstTryNumberToGet
+ );
+
+ //
+ // Now get the rest of the characters from the beginning of the
+ // buffer.
+ //
+
+ RtlMoveMemory(
+ NewBuffer+firstTryNumberToGet,
+ Extension->InterruptReadBuffer,
+ numberOfCharsMoved - firstTryNumberToGet
+ );
+
+ Extension->FirstReadableChar = Extension->InterruptReadBuffer +
+ numberOfCharsMoved - firstTryNumberToGet;
+
+ }
+
+ }
+
+ return numberOfCharsMoved;
+
+}
+
+
+BOOLEAN
+SerialUpdateAndSwitchToNew(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This routine gets the (hopefully) few characters that
+ remain in the interrupt buffer after the first time we tried
+ to get them out.
+
+ NOTE: This is called by WdfInterruptSynchronize.
+
+Arguments:
+
+ Context - Points to a structure that contains a pointer to the
+ device extension, a pointer to the buffer we are moving
+ to, and a count of the number of characters
+ that we previously copied into the new buffer, and the
+ actual size of the new buffer.
+
+Return Value:
+
+ Always FALSE.
+
+--*/
+
+{
+
+ PSERIAL_RESIZE_PARAMS params = Context;
+ PSERIAL_DEVICE_EXTENSION extension = params->Extension;
+ ULONG tempCharsInInterruptBuffer = extension->CharsInInterruptBuffer;
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ ASSERT(extension->CharsInInterruptBuffer >= params->NumberMoved);
+
+ //
+ // We temporarily reduce the chars in interrupt buffer to
+ // "fool" the move routine. We will restore it after the
+ // move.
+ //
+
+ extension->CharsInInterruptBuffer -= params->NumberMoved;
+
+ if (extension->CharsInInterruptBuffer) {
+
+ SerialMoveToNewIntBuffer(
+ extension,
+ params->NewBuffer + params->NumberMoved
+ );
+
+ }
+
+ extension->CharsInInterruptBuffer = tempCharsInInterruptBuffer;
+
+
+ extension->LastCharSlot = params->NewBuffer + (params->NewBufferSize - 1);
+ extension->FirstReadableChar = params->NewBuffer;
+ extension->ReadBufferBase = params->NewBuffer;
+ extension->InterruptReadBuffer = params->NewBuffer;
+ extension->BufferSize = params->NewBufferSize;
+
+ //
+ // We *KNOW* that the new interrupt buffer is larger than the
+ // old buffer. We don't need to worry about it being full.
+ //
+
+ extension->CurrentCharSlot = extension->InterruptReadBuffer +
+ extension->CharsInInterruptBuffer;
+
+ //
+ // We set up the default xon/xoff limits.
+ //
+
+ extension->HandFlow.XoffLimit = extension->BufferSize >> 3;
+ extension->HandFlow.XonLimit = extension->BufferSize >> 1;
+
+ extension->WmiCommData.XoffXmitThreshold = extension->HandFlow.XoffLimit;
+ extension->WmiCommData.XonXmitThreshold = extension->HandFlow.XonLimit;
+
+ extension->BufferSizePt8 = ((3*(extension->BufferSize>>2))+
+ (extension->BufferSize>>4));
+
+ //
+ // Since we (essentially) reduced the percentage of the interrupt
+ // buffer being full, we need to handle any flow control.
+ //
+
+ SerialHandleReducedIntBuffer(extension);
+
+ return FALSE;
+
+}
+
+
diff --git a/tests/projects/wdk/kmdf/serial/registry.c b/tests/projects/wdk/kmdf/serial/registry.c
new file mode 100644
index 000000000..5ab25955a
--- /dev/null
+++ b/tests/projects/wdk/kmdf/serial/registry.c
@@ -0,0 +1,443 @@
+/*++
+
+Copyright (c) Microsoft Corporation
+
+Module Name:
+
+ registry.c
+
+Abstract:
+
+ This module contains the code that is used to get values from the
+ registry and to manipulate entries in the registry.
+
+Environment:
+
+ Kernel mode
+
+--*/
+
+#include "precomp.h"
+
+#if defined(EVENT_TRACING)
+#include "registry.tmh"
+#endif
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(INIT,SerialGetConfigDefaults)
+#pragma alloc_text(PAGESRP0,SerialGetRegistryKeyValue)
+#pragma alloc_text(PAGESRP0,SerialPutRegistryKeyValue)
+#pragma alloc_text(PAGESRP0,SerialGetFdoRegistryKeyValue)
+#endif // ALLOC_PRAGMA
+
+
+#define PARAMATER_NAME_LEN 80
+
+
+NTSTATUS
+SerialGetConfigDefaults(
+ IN PSERIAL_FIRMWARE_DATA DriverDefaultsPtr,
+ IN WDFDRIVER Driver
+ )
+
+/*++
+
+Routine Description:
+
+ This routine reads the default configuration data from the
+ registry for the serial driver.
+
+ It also builds fields in the registry for several configuration
+ options if they don't exist.
+
+Arguments:
+
+ DriverDefaultsPtr - Pointer to a structure that will contain
+ the default configuration values.
+
+ RegistryPath - points to the entry for this driver in the
+ current control set of the registry.
+
+Return Value:
+
+ STATUS_SUCCESS if we got the defaults, otherwise we failed.
+ The only way to fail this call is if the STATUS_INSUFFICIENT_RESOURCES.
+
+--*/
+
+{
+
+ NTSTATUS status = STATUS_SUCCESS; // return value
+ WDFKEY hKey;
+ DECLARE_UNICODE_STRING_SIZE(valueName,PARAMATER_NAME_LEN);
+
+ status = WdfDriverOpenParametersRegistryKey(Driver,
+ STANDARD_RIGHTS_ALL,
+ WDF_NO_OBJECT_ATTRIBUTES,
+ &hKey);
+ if (!NT_SUCCESS (status)) {
+ return status;
+ }
+
+ status = RtlUnicodeStringPrintf(&valueName,L"BreakOnEntry");
+ if (!NT_SUCCESS (status)) {
+ goto End;
+
+ }
+
+ status = WdfRegistryQueryULong (hKey,
+ &valueName,
+ &DriverDefaultsPtr->ShouldBreakOnEntry);
+
+ if (!NT_SUCCESS (status)) {
+ DriverDefaultsPtr->ShouldBreakOnEntry = 0;
+ }
+
+ status = RtlUnicodeStringPrintf(&valueName,L"DebugLevel");
+ if (!NT_SUCCESS (status)) {
+ goto End;
+ }
+
+ status = WdfRegistryQueryULong (hKey,
+ &valueName,
+ &DriverDefaultsPtr->DebugLevel);
+
+ if (!NT_SUCCESS (status)) {
+ DriverDefaultsPtr->DebugLevel = 0;
+ }
+
+
+ status = RtlUnicodeStringPrintf(&valueName,L"ForceFifoEnable");
+ if (!NT_SUCCESS (status)) {
+ goto End;
+ }
+
+ status = WdfRegistryQueryULong (hKey,
+ &valueName,
+ &DriverDefaultsPtr->ForceFifoEnableDefault);
+
+ if (!NT_SUCCESS (status)) {
+
+ //
+ // If it isn't then write out values so that it could
+ // be adjusted later.
+ //
+ DriverDefaultsPtr->ForceFifoEnableDefault = SERIAL_FORCE_FIFO_DEFAULT;
+
+ status = WdfRegistryAssignULong(hKey,
+ &valueName,
+ DriverDefaultsPtr->ForceFifoEnableDefault
+ );
+ if (!NT_SUCCESS (status)) {
+ goto End;
+ }
+
+ }
+
+ status = RtlUnicodeStringPrintf(&valueName,L"RxFIFO");
+ if (!NT_SUCCESS (status)) {
+ goto End;
+ }
+
+ status = WdfRegistryQueryULong (hKey,
+ &valueName,
+ &DriverDefaultsPtr->RxFIFODefault);
+
+ if (!NT_SUCCESS (status)) {
+
+ DriverDefaultsPtr->RxFIFODefault = SERIAL_RX_FIFO_DEFAULT;
+
+ status = WdfRegistryAssignULong(hKey,
+ &valueName,
+ DriverDefaultsPtr->RxFIFODefault
+ );
+ if (!NT_SUCCESS (status)) {
+ goto End;
+ }
+
+ }
+
+ status = RtlUnicodeStringPrintf(&valueName,L"TxFIFO");
+ if (!NT_SUCCESS (status)) {
+ goto End;
+ }
+
+ status = WdfRegistryQueryULong (hKey,
+ &valueName,
+ &DriverDefaultsPtr->TxFIFODefault);
+
+ if (!NT_SUCCESS (status)) {
+
+ DriverDefaultsPtr->TxFIFODefault = SERIAL_TX_FIFO_DEFAULT;
+
+ status = WdfRegistryAssignULong(hKey,
+ &valueName,
+ DriverDefaultsPtr->TxFIFODefault
+ );
+ if (!NT_SUCCESS (status)) {
+ goto End;
+ }
+
+ }
+
+ status = RtlUnicodeStringPrintf(&valueName,L"PermitShare");
+ if (!NT_SUCCESS (status)) {
+ goto End;
+ }
+
+ status = WdfRegistryQueryULong (hKey,
+ &valueName,
+ &DriverDefaultsPtr->PermitShareDefault);
+
+ if (!NT_SUCCESS (status)) {
+
+ DriverDefaultsPtr->PermitShareDefault = SERIAL_PERMIT_SHARE_DEFAULT;
+
+ status = WdfRegistryAssignULong(hKey,
+ &valueName,
+ DriverDefaultsPtr->PermitShareDefault
+ );
+ if (!NT_SUCCESS (status)) {
+ goto End;
+ }
+
+ }
+
+ status = RtlUnicodeStringPrintf(&valueName,L"LogFifo");
+ if (!NT_SUCCESS (status)) {
+ goto End;
+ }
+
+ status = WdfRegistryQueryULong (hKey,
+ &valueName,
+ &DriverDefaultsPtr->LogFifoDefault);
+
+ if (!NT_SUCCESS (status)) {
+
+ DriverDefaultsPtr->LogFifoDefault = SERIAL_LOG_FIFO_DEFAULT;
+
+ status = WdfRegistryAssignULong(hKey,
+ &valueName,
+ DriverDefaultsPtr->LogFifoDefault
+ );
+ if (!NT_SUCCESS (status)) {
+ goto End;
+ }
+
+ DriverDefaultsPtr->LogFifoDefault = 1;
+ }
+
+
+ status = RtlUnicodeStringPrintf(&valueName,L"UartRemovalDetect");
+ if (!NT_SUCCESS (status)) {
+ goto End;
+ }
+
+ status = WdfRegistryQueryULong (hKey,
+ &valueName,
+ &DriverDefaultsPtr->UartRemovalDetect);
+
+ if (!NT_SUCCESS (status)) {
+ DriverDefaultsPtr->UartRemovalDetect = 0;
+ }
+
+
+End:
+ WdfRegistryClose(hKey);
+ return (status);
+}
+
+BOOLEAN
+SerialGetRegistryKeyValue(
+ IN WDFDEVICE WdfDevice,
+ _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, ">SerialGetRegistryKeyValue(XXX)\n");
+
+ *Value = 0;
+
+ status = WdfDeviceOpenRegistryKey(WdfDevice,
+ 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, "<--SerialGetRegistryKeyValue %ws %d \n",
+ Name, *Value);
+
+ return retValue;
+}
+
+#define PARAMATER_NAME_LEN 80
+
+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;
+
+}
+
+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;
+}
+
+
diff --git a/tests/projects/wdk/kmdf/serial/serial.h b/tests/projects/wdk/kmdf/serial/serial.h
new file mode 100644
index 000000000..fcbf9748f
--- /dev/null
+++ b/tests/projects/wdk/kmdf/serial/serial.h
@@ -0,0 +1,1757 @@
+/*++
+
+Copyright (c) 1990, 1991, 1992, 1993 - 1997 Microsoft Corporation
+
+Module Name :
+
+ serial.h
+
+Abstract:
+
+ Type definitions and data for the serial port driver
+
+--*/
+
+#define POOL_TAG 'XMOC'
+
+
+//
+// Some default driver values. We will check the registry for
+// them first.
+//
+#define SERIAL_UNINITIALIZED_DEFAULT 1234567
+#define SERIAL_FORCE_FIFO_DEFAULT 1
+#define SERIAL_RX_FIFO_DEFAULT 8
+#define SERIAL_TX_FIFO_DEFAULT 14
+#define SERIAL_PERMIT_SHARE_DEFAULT 0
+#define SERIAL_LOG_FIFO_DEFAULT 0
+
+
+//
+// This define gives the default Object directory
+// that we should use to insert the symbolic links
+// between the NT device name and namespace used by
+// that object directory.
+#define DEFAULT_DIRECTORY L"DosDevices"
+
+//
+// For the above directory, the serial port will
+// use the following name as the suffix of the serial
+// ports for that directory. It will also append
+// a number onto the end of the name. That number
+// will start at 1.
+#define DEFAULT_SERIAL_NAME L"COM"
+//
+//
+// This define gives the default NT name for
+// for serial ports detected by the firmware.
+// This name will be appended to Device prefix
+// with a number following it. The number is
+// incremented each time encounter a serial
+// port detected by the firmware. Note that
+// on a system with multiple busses, this means
+// that the first port on a bus is not necessarily
+// \Device\Serial0.
+//
+#define DEFAULT_NT_SUFFIX L"Serial"
+#define _DRIVER_NAME_ "Serial.sys"
+
+#define DEVICE_OBJECT_NAME_LENGTH 128
+#define SYMBOLIC_NAME_LENGTH 128
+#define SERIAL_DEVICE_MAP L"SERIALCOMM"
+
+//
+// GUID_DEVINTERFACE_COMPORT is not defined in the Win2K
+// headers, so we will need this definition to avoid compilation
+// errors.
+//
+#define GUID_DEVINTERFACE_COMPORT GUID_CLASS_COMPORT
+
+//
+// This value - which could be redefined at compile
+// time, define the stride between registers
+//
+#if !defined(SERIAL_REGISTER_STRIDE)
+#define SERIAL_REGISTER_STRIDE 1
+#endif
+
+//
+// Offsets from the base register address of the
+// various registers for the 8250 family of UARTS.
+//
+#define RECEIVE_BUFFER_REGISTER ((ULONG)((0x00)*SERIAL_REGISTER_STRIDE))
+#define TRANSMIT_HOLDING_REGISTER ((ULONG)((0x00)*SERIAL_REGISTER_STRIDE))
+#define INTERRUPT_ENABLE_REGISTER ((ULONG)((0x01)*SERIAL_REGISTER_STRIDE))
+#define INTERRUPT_IDENT_REGISTER ((ULONG)((0x02)*SERIAL_REGISTER_STRIDE))
+#define FIFO_CONTROL_REGISTER ((ULONG)((0x02)*SERIAL_REGISTER_STRIDE))
+#define LINE_CONTROL_REGISTER ((ULONG)((0x03)*SERIAL_REGISTER_STRIDE))
+#define MODEM_CONTROL_REGISTER ((ULONG)((0x04)*SERIAL_REGISTER_STRIDE))
+#define LINE_STATUS_REGISTER ((ULONG)((0x05)*SERIAL_REGISTER_STRIDE))
+#define MODEM_STATUS_REGISTER ((ULONG)((0x06)*SERIAL_REGISTER_STRIDE))
+#define DIVISOR_LATCH_LSB ((ULONG)((0x00)*SERIAL_REGISTER_STRIDE))
+#define DIVISOR_LATCH_MSB ((ULONG)((0x01)*SERIAL_REGISTER_STRIDE))
+#define SERIAL_REGISTER_SPAN ((ULONG)(7*SERIAL_REGISTER_STRIDE))
+
+//
+// If we have an interrupt status register this is its assumed
+// length.
+//
+#define SERIAL_STATUS_LENGTH ((ULONG)(1*SERIAL_REGISTER_STRIDE))
+
+//
+// Bitmask definitions for accessing the 8250 device registers.
+//
+
+//
+// These bits define the number of data bits trasmitted in
+// the Serial Data Unit (SDU - Start,data, parity, and stop bits)
+//
+#define SERIAL_DATA_LENGTH_5 0x00
+#define SERIAL_DATA_LENGTH_6 0x01
+#define SERIAL_DATA_LENGTH_7 0x02
+#define SERIAL_DATA_LENGTH_8 0x03
+
+
+//
+// These masks define the interrupts that can be enabled or disabled.
+//
+//
+// This interrupt is used to notify that there is new incomming
+// data available. The SERIAL_RDA interrupt is enabled by this bit.
+//
+#define SERIAL_IER_RDA 0x01
+
+//
+// This interrupt is used to notify that there is space available
+// in the transmitter for another character. The SERIAL_THR
+// interrupt is enabled by this bit.
+//
+#define SERIAL_IER_THR 0x02
+
+//
+// This interrupt is used to notify that some sort of error occured
+// with the incomming data. The SERIAL_RLS interrupt is enabled by
+// this bit.
+#define SERIAL_IER_RLS 0x04
+
+//
+// This interrupt is used to notify that some sort of change has
+// taken place in the modem control line. The SERIAL_MS interrupt is
+// enabled by this bit.
+//
+#define SERIAL_IER_MS 0x08
+
+
+//
+// These masks define the values of the interrupt identification
+// register. The low bit must be clear in the interrupt identification
+// register for any of these interrupts to be valid. The interrupts
+// are defined in priority order, with the highest value being most
+// important. See above for a description of what each interrupt
+// implies.
+//
+#define SERIAL_IIR_RLS 0x06
+#define SERIAL_IIR_RDA 0x04
+#define SERIAL_IIR_CTI 0x0c
+#define SERIAL_IIR_THR 0x02
+#define SERIAL_IIR_MS 0x00
+
+//
+// This bit mask get the value of the high two bits of the
+// interrupt id register. If this is a 16550 class chip
+// these bits will be a one if the fifo's are enbled, otherwise
+// they will always be zero.
+//
+#define SERIAL_IIR_FIFOS_ENABLED 0xc0
+
+//
+// If the low bit is logic one in the interrupt identification register
+// this implies that *NO* interrupts are pending on the device.
+//
+#define SERIAL_IIR_NO_INTERRUPT_PENDING 0x01
+
+
+//
+// Use these bits to detect removal of serial card for Stratus implementation
+//
+#define SERIAL_IIR_MUST_BE_ZERO 0x30
+
+
+//
+// These masks define access to the fifo control register.
+//
+
+//
+// Enabling this bit in the fifo control register will turn
+// on the fifos. If the fifos are enabled then the high two
+// bits of the interrupt id register will be set to one. Note
+// that this only occurs on a 16550 class chip. If the high
+// two bits in the interrupt id register are not one then
+// we know we have a lower model chip.
+//
+//
+#define SERIAL_FCR_ENABLE ((UCHAR)0x01)
+#define SERIAL_FCR_RCVR_RESET ((UCHAR)0x02)
+#define SERIAL_FCR_TXMT_RESET ((UCHAR)0x04)
+
+//
+// This set of values define the high water marks (when the
+// interrupts trip) for the receive fifo.
+//
+#define SERIAL_1_BYTE_HIGH_WATER ((UCHAR)0x00)
+#define SERIAL_4_BYTE_HIGH_WATER ((UCHAR)0x40)
+#define SERIAL_8_BYTE_HIGH_WATER ((UCHAR)0x80)
+#define SERIAL_14_BYTE_HIGH_WATER ((UCHAR)0xc0)
+
+//
+// These masks define access to the line control register.
+//
+
+//
+// This defines the bit used to control the definition of the "first"
+// two registers for the 8250. These registers are the input/output
+// register and the interrupt enable register. When the DLAB bit is
+// enabled these registers become the least significant and most
+// significant bytes of the divisor value.
+//
+#define SERIAL_LCR_DLAB 0x80
+
+//
+// This defines the bit used to control whether the device is sending
+// a break. When this bit is set the device is sending a space (logic 0).
+//
+// Most protocols will assume that this is a hangup.
+//
+#define SERIAL_LCR_BREAK 0x40
+
+//
+// These defines are used to set the line control register.
+//
+#define SERIAL_5_DATA ((UCHAR)0x00)
+#define SERIAL_6_DATA ((UCHAR)0x01)
+#define SERIAL_7_DATA ((UCHAR)0x02)
+#define SERIAL_8_DATA ((UCHAR)0x03)
+#define SERIAL_DATA_MASK ((UCHAR)0x03)
+
+#define SERIAL_1_STOP ((UCHAR)0x00)
+#define SERIAL_1_5_STOP ((UCHAR)0x04) // Only valid for 5 data bits
+#define SERIAL_2_STOP ((UCHAR)0x04) // Not valid for 5 data bits
+#define SERIAL_STOP_MASK ((UCHAR)0x04)
+
+#define SERIAL_NONE_PARITY ((UCHAR)0x00)
+#define SERIAL_ODD_PARITY ((UCHAR)0x08)
+#define SERIAL_EVEN_PARITY ((UCHAR)0x18)
+#define SERIAL_MARK_PARITY ((UCHAR)0x28)
+#define SERIAL_SPACE_PARITY ((UCHAR)0x38)
+#define SERIAL_PARITY_MASK ((UCHAR)0x38)
+
+//
+// These masks define access the modem control register.
+//
+
+//
+// This bit controls the data terminal ready (DTR) line. When
+// this bit is set the line goes to logic 0 (which is then inverted
+// by normal hardware). This is normally used to indicate that
+// the device is available to be used. Some odd hardware
+// protocols (like the kernel debugger) use this for handshaking
+// purposes.
+//
+#define SERIAL_MCR_DTR 0x01
+
+//
+// This bit controls the ready to send (RTS) line. When this bit
+// is set the line goes to logic 0 (which is then inverted by the normal
+// hardware). This is used for hardware handshaking. It indicates that
+// the hardware is ready to send data and it is waiting for the
+// receiving end to set clear to send (CTS).
+//
+#define SERIAL_MCR_RTS 0x02
+
+//
+// This bit is used for general purpose output.
+//
+#define SERIAL_MCR_OUT1 0x04
+
+//
+// This bit is used for general purpose output.
+//
+#define SERIAL_MCR_OUT2 0x08
+
+//
+// This bit controls the loopback testing mode of the device. Basically
+// the outputs are connected to the inputs (and vice versa).
+//
+#define SERIAL_MCR_LOOP 0x10
+
+//
+// This bit enables auto flow control on a TI TL16C550C/TL16C550CI
+//
+
+#define SERIAL_MCR_TL16C550CAFE 0x20
+
+
+//
+// These masks define access to the line status register. The line
+// status register contains information about the status of data
+// transfer. The first five bits deal with receive data and the
+// last two bits deal with transmission. An interrupt is generated
+// whenever bits 1 through 4 in this register are set.
+//
+
+//
+// This bit is the data ready indicator. It is set to indicate that
+// a complete character has been received. This bit is cleared whenever
+// the receive buffer register has been read.
+//
+#define SERIAL_LSR_DR 0x01
+
+//
+// This is the overrun indicator. It is set to indicate that the receive
+// buffer register was not read befor a new character was transferred
+// into the buffer. This bit is cleared when this register is read.
+//
+#define SERIAL_LSR_OE 0x02
+
+//
+// This is the parity error indicator. It is set whenever the hardware
+// detects that the incoming serial data unit does not have the correct
+// parity as defined by the parity select in the line control register.
+// This bit is cleared by reading this register.
+//
+#define SERIAL_LSR_PE 0x04
+
+//
+// This is the framing error indicator. It is set whenever the hardware
+// detects that the incoming serial data unit does not have a valid
+// stop bit. This bit is cleared by reading this register.
+//
+#define SERIAL_LSR_FE 0x08
+
+//
+// This is the break interrupt indicator. It is set whenever the data
+// line is held to logic 0 for more than the amount of time it takes
+// to send one serial data unit. This bit is cleared whenever the
+// this register is read.
+//
+#define SERIAL_LSR_BI 0x10
+
+//
+// This is the transmit holding register empty indicator. It is set
+// to indicate that the hardware is ready to accept another character
+// for transmission. This bit is cleared whenever a character is
+// written to the transmit holding register.
+//
+#define SERIAL_LSR_THRE 0x20
+
+//
+// This bit is the transmitter empty indicator. It is set whenever the
+// transmit holding buffer is empty and the transmit shift register
+// (a non-software accessable register that is used to actually put
+// the data out on the wire) is empty. Basically this means that all
+// data has been sent. It is cleared whenever the transmit holding or
+// the shift registers contain data.
+//
+#define SERIAL_LSR_TEMT 0x40
+
+//
+// This bit indicates that there is at least one error in the fifo.
+// The bit will not be turned off until there are no more errors
+// in the fifo.
+//
+#define SERIAL_LSR_FIFOERR 0x80
+
+
+//
+// These masks are used to access the modem status register.
+// Whenever one of the first four bits in the modem status
+// register changes state a modem status interrupt is generated.
+//
+
+//
+// This bit is the delta clear to send. It is used to indicate
+// that the clear to send bit (in this register) has *changed*
+// since this register was last read by the CPU.
+//
+#define SERIAL_MSR_DCTS 0x01
+
+//
+// This bit is the delta data set ready. It is used to indicate
+// that the data set ready bit (in this register) has *changed*
+// since this register was last read by the CPU.
+//
+#define SERIAL_MSR_DDSR 0x02
+
+//
+// This is the trailing edge ring indicator. It is used to indicate
+// that the ring indicator input has changed from a low to high state.
+//
+#define SERIAL_MSR_TERI 0x04
+
+//
+// This bit is the delta data carrier detect. It is used to indicate
+// that the data carrier bit (in this register) has *changed*
+// since this register was last read by the CPU.
+//
+#define SERIAL_MSR_DDCD 0x08
+
+//
+// This bit contains the (complemented) state of the clear to send
+// (CTS) line.
+//
+#define SERIAL_MSR_CTS 0x10
+
+//
+// This bit contains the (complemented) state of the data set ready
+// (DSR) line.
+//
+#define SERIAL_MSR_DSR 0x20
+
+//
+// This bit contains the (complemented) state of the ring indicator
+// (RI) line.
+//
+#define SERIAL_MSR_RI 0x40
+
+//
+// This bit contains the (complemented) state of the data carrier detect
+// (DCD) line.
+//
+#define SERIAL_MSR_DCD 0x80
+
+//
+// This should be more than enough space to hold then
+// numeric suffix of the device name.
+//
+#define DEVICE_NAME_DELTA 20
+
+
+//
+// Up to 16 Ports Per card. However for sixteen
+// port cards the interrupt status register must me
+// the indexing kind rather then the bitmask kind.
+//
+//
+#define SERIAL_MAX_PORTS_INDEXED (16)
+#define SERIAL_MAX_PORTS_NONINDEXED (8)
+
+typedef struct _CONFIG_DATA {
+ PHYSICAL_ADDRESS Controller;
+ PHYSICAL_ADDRESS TrController;
+ ULONG SpanOfController;
+ ULONG ClockRate;
+ ULONG AddressSpace;
+ ULONG DisablePort;
+ ULONG ForceFifoEnable;
+ ULONG RxFIFO;
+ ULONG TxFIFO;
+ ULONG PermitShare;
+ ULONG PermitSystemWideShare;
+ ULONG LogFifo;
+ KINTERRUPT_MODE InterruptMode;
+ ULONG TrVector;
+ ULONG TrIrql;
+ KAFFINITY Affinity;
+ ULONG TL16C550CAFC;
+ } CONFIG_DATA,*PCONFIG_DATA;
+
+
+//
+// This structure contains configuration data, much of which
+// is read from the registry.
+//
+typedef struct _SERIAL_FIRMWARE_DATA {
+ PDRIVER_OBJECT DriverObject;
+ ULONG ControllersFound;
+ ULONG ForceFifoEnableDefault;
+ ULONG DebugLevel;
+ ULONG ShouldBreakOnEntry;
+ ULONG RxFIFODefault;
+ ULONG TxFIFODefault;
+ ULONG PermitShareDefault;
+ ULONG PermitSystemWideShare;
+ ULONG LogFifoDefault;
+ ULONG UartRemovalDetect;
+ UNICODE_STRING Directory;
+ UNICODE_STRING NtNameSuffix;
+ UNICODE_STRING DirectorySymbolicName;
+ LIST_ENTRY ConfigList;
+} SERIAL_FIRMWARE_DATA,*PSERIAL_FIRMWARE_DATA;
+
+//
+// Default xon/xoff characters.
+//
+#define SERIAL_DEF_XON 0x11
+#define SERIAL_DEF_XOFF 0x13
+
+//
+// Reasons that recption may be held up.
+//
+#define SERIAL_RX_DTR ((ULONG)0x01)
+#define SERIAL_RX_XOFF ((ULONG)0x02)
+#define SERIAL_RX_RTS ((ULONG)0x04)
+#define SERIAL_RX_DSR ((ULONG)0x08)
+
+//
+// Reasons that transmission may be held up.
+//
+#define SERIAL_TX_CTS ((ULONG)0x01)
+#define SERIAL_TX_DSR ((ULONG)0x02)
+#define SERIAL_TX_DCD ((ULONG)0x04)
+#define SERIAL_TX_XOFF ((ULONG)0x08)
+#define SERIAL_TX_BREAK ((ULONG)0x10)
+
+//
+// These values are used by the routines that can be used
+// to complete a read (other than interval timeout) to indicate
+// to the interval timeout that it should complete.
+//
+#define SERIAL_COMPLETE_READ_CANCEL ((LONG)-1)
+#define SERIAL_COMPLETE_READ_TOTAL ((LONG)-2)
+#define SERIAL_COMPLETE_READ_COMPLETE ((LONG)-3)
+
+//
+// These are default values that shouldn't appear in the registry
+//
+#define SERIAL_BAD_VALUE ((ULONG)-1)
+
+
+typedef struct _SERIAL_DEVICE_STATE {
+ //
+ // TRUE if we need to set the state to open
+ // on a powerup
+ //
+
+ BOOLEAN Reopen;
+
+ //
+ // Hardware registers
+ //
+
+ UCHAR IER;
+ // FCR is known by other values
+ UCHAR LCR;
+ UCHAR MCR;
+ // LSR is never written
+ // MSR is never written
+ // SCR is either scratch or interrupt status
+
+
+} SERIAL_DEVICE_STATE, *PSERIAL_DEVICE_STATE;
+
+
+typedef
+UCHAR
+(*PREAD_PORT_UCHAR)(
+ IN UCHAR *Register
+ );
+
+typedef
+VOID
+(*PWRITE_PORT_UCHAR)(
+ IN UCHAR *Register,
+ IN UCHAR Value
+ );
+
+typedef struct _SERIAL_DEVICE_EXTENSION {
+ //
+ // WDF device handle
+ //
+ WDFDEVICE WdfDevice;
+ //
+ // Points to the device object that contains
+ // this device extension.
+ //
+ PDEVICE_OBJECT DeviceObject;
+ //
+ // We keep a pointer around to our device name for dumps
+ // and for creating "external" symbolic links to this
+ // device.
+ //
+ UNICODE_STRING DeviceName;
+ //
+ // Pointer to the driver object
+ //
+
+ PDRIVER_OBJECT DriverObject;
+
+ //
+ // Records whether we actually created the symbolic link name
+ // at driver load time. If we didn't create it, we won't try
+ // to destroy it when we unload.
+ //
+ BOOLEAN CreatedSymbolicLink;
+
+ //
+ // Records whether we actually created an entry in SERIALCOMM
+ // at driver load time. If we didn't create it, we won't try
+ // to destroy it when the device is removed.
+ //
+ BOOLEAN CreatedSerialCommEntry;
+
+ //
+ // Did we update system count for serial ports
+ //
+ BOOLEAN IsSystemConfigInfoUpdated;
+
+ //
+ // Should we expose external interfaces?
+ //
+ ULONG SkipNaming;
+
+ //
+ // Support the TI TL16C550C and TL16C550CI auto flow control
+ //
+
+ ULONG TL16C550CAFC;
+
+ //
+ // Detect removed hardware in intterrupt routine flag
+ //
+ ULONG UartRemovalDetect;
+
+ //
+ // We keep track of whether the somebody has the device currently
+ // opened with a simple boolean. We need to know this so that
+ // spurious interrupts from the device (especially during initialization)
+ // will be ignored. This value is only accessed in the ISR and
+ // is only set via synchronization routines. We may be able
+ // to get rid of this boolean when the code is more fleshed out.
+ //
+ BOOLEAN DeviceIsOpened;
+
+ //
+ // Current state during powerdown
+ //
+
+ SERIAL_DEVICE_STATE DeviceState;
+
+ //
+ // TRUE if we own power policy
+ //
+
+ BOOLEAN OwnsPowerPolicy;
+
+ //
+ // TRUE if we should retain power on close and not aggressively
+ // reduce power consumption
+ //
+
+ BOOLEAN RetainPowerOnClose;
+
+ //
+ // Should we enable wakeup
+ //
+
+ BOOLEAN IsWakeEnabled;
+
+ //
+ // This list head is used to contain the time ordered list
+ // of read requests. Access to this list is protected by
+ // the global cancel spinlock.
+ //
+ WDFQUEUE ReadQueue;
+
+ //
+ // This list head is used to contain the time ordered list
+ // of write requests. Access to this list is protected by
+ // the global cancel spinlock.
+ //
+ WDFQUEUE WriteQueue;
+
+ //
+ // This list head is used to contain the time ordered list
+ // of set and wait mask requests. Access to this list is protected by
+ // the global cancel spinlock.
+ //
+ WDFQUEUE MaskQueue;
+
+ //
+ // Holds the serialized list of purge requests.
+ //
+ WDFQUEUE PurgeQueue;
+
+ //
+ // This points to the request that is currently being processed
+ // for the read queue. This field is initialized by the open to
+ // NULL.
+ //
+ // This value is only set at dispatch level. It may be
+ // read at interrupt level.
+ //
+ WDFREQUEST CurrentReadRequest;
+
+ //
+ // This points to the request that is currently being processed
+ // for the write queue.
+ //
+ // This value is only set at dispatch level. It may be
+ // read at interrupt level.
+ //
+ WDFREQUEST CurrentWriteRequest;
+
+ //
+ // Points to the request that is currently being processed to
+ // affect the wait mask operations.
+ //
+ WDFREQUEST CurrentMaskRequest;
+
+ //
+ // Points to the request that is currently being processed to
+ // purge the read/write queues and buffers.
+ //
+ WDFREQUEST CurrentPurgeRequest;
+
+ //
+ // Points to the current request that is waiting on a comm event.
+ //
+ WDFREQUEST CurrentWaitRequest;
+
+ //
+ // Points to the request that is being used to send an immediate
+ // character.
+ //
+ WDFREQUEST CurrentImmediateRequest;
+
+ //
+ // Points to the request that is being used to count the number
+ // of characters received after an xoff (as currently defined
+ // by the IOCTL_SERIAL_XOFF_COUNTER ioctl) is sent.
+ //
+ WDFREQUEST CurrentXoffRequest;
+
+ //
+ // The base address for the set of device registers
+ // of the serial port.
+ //
+ PUCHAR Controller;
+ //
+ // This value holds the span (in units of bytes) of the register
+ // set controlling this port. This is constant over the life
+ // of the port.
+ //
+ ULONG SpanOfController;
+
+ //
+ // Address space
+ //
+
+ ULONG AddressSpace;
+
+ PREAD_PORT_UCHAR SerialReadUChar;
+ PWRITE_PORT_UCHAR SerialWriteUChar;
+
+ //
+ // Hold the clock rate input to the serial part.
+ //
+ ULONG ClockRate;
+
+ //
+ // The number of characters to push out if a fifo is present.
+ //
+ ULONG TxFifoAmount;
+
+ //
+ // Set to indicate that it is ok to share interrupts within the device.
+ //
+ ULONG PermitShare;
+
+
+ //
+ // Points to the interrupt object for used by this device.
+ //
+ WDFINTERRUPT WdfInterrupt;
+
+ //
+ // Translated vector
+ //
+ ULONG Vector;
+ //
+ // Translated Irql
+ //
+ KIRQL Irql;
+
+ KINTERRUPT_MODE InterruptMode;
+
+ KAFFINITY Affinity;
+
+ //
+ // This value is set by the read code to hold the time value
+ // used for read interval timing. We keep it in the extension
+ // so that the interval timer dpc routine determine if the
+ // interval time has passed for the IO.
+ //
+ LARGE_INTEGER IntervalTime;
+
+ //
+ // These two values hold the "constant" time that we should use
+ // to delay for the read interval time.
+ //
+ LARGE_INTEGER ShortIntervalAmount;
+ LARGE_INTEGER LongIntervalAmount;
+
+ //
+ // This holds the value that we use to determine if we should use
+ // the long interval delay or the short interval delay.
+ //
+ LARGE_INTEGER CutOverAmount;
+
+ //
+ // This holds the system time when we last time we had
+ // checked that we had actually read characters. Used
+ // for interval timing.
+ //
+ LARGE_INTEGER LastReadTime;
+
+
+ //
+ // This points the the delta time that we should use to
+ // delay for interval timing.
+ //
+ PLARGE_INTEGER IntervalTimeToUse;
+
+
+ //
+ // Set at intialization to indicate that on the current
+ // architecture we need to unmap the base register address
+ // when we unload the driver.
+ //
+ BOOLEAN UnMapRegisters;
+
+ //
+ // Holds the number of bytes remaining in the current write
+ // request.
+ //
+ // This location is only accessed while at interrupt level.
+ //
+ ULONG WriteLength;
+
+ //
+ // Holds a pointer to the current character to be sent in
+ // the current write.
+ //
+ // This location is only accessed while at interrupt level.
+ //
+ PUCHAR WriteCurrentChar;
+
+ //
+ // This is a buffer for the read processing.
+ //
+ // The buffer works as a ring. When the character is read from
+ // the device it will be place at the end of the ring.
+ //
+ // Characters are only placed in this buffer at interrupt level
+ // although character may be read at any level. The pointers
+ // that manage this buffer may not be updated except at interrupt
+ // level.
+ //
+ PUCHAR InterruptReadBuffer;
+
+ //
+ // This is a pointer to the first character of the buffer into
+ // which the interrupt service routine is copying characters.
+ //
+ PUCHAR ReadBufferBase;
+
+ //
+ // This is a count of the number of characters in the interrupt
+ // buffer. This value is set and read at interrupt level. Note
+ // that this value is only *incremented* at interrupt level so
+ // it is safe to read it at any level. When characters are
+ // copied out of the read buffer, this count is decremented by
+ // a routine that synchronizes with the ISR.
+ //
+ ULONG CharsInInterruptBuffer;
+
+ //
+ // Points to the first available position for a newly received
+ // character. This variable is only accessed at interrupt level and
+ // buffer initialization code.
+ //
+ PUCHAR CurrentCharSlot;
+
+ //
+ // This variable is used to contain the last available position
+ // in the read buffer. It is updated at open and at interrupt
+ // level when switching between the users buffer and the interrupt
+ // buffer.
+ //
+ PUCHAR LastCharSlot;
+
+ //
+ // This marks the first character that is available to satisfy
+ // a read request. Note that while this always points to valid
+ // memory, it may not point to a character that can be sent to
+ // the user. This can occur when the buffer is empty.
+ //
+ PUCHAR FirstReadableChar;
+
+ //
+ // Pointer to the lock variable returned for this extension when
+ // locking down the driver
+ //
+ PVOID LockPtr;
+
+
+ //
+ // This variable holds the size of whatever buffer we are currently
+ // using.
+ //
+ ULONG BufferSize;
+
+ //
+ // This variable holds .8 of BufferSize. We don't want to recalculate
+ // this real often - It's needed when so that an application can be
+ // "notified" that the buffer is getting full.
+ //
+ ULONG BufferSizePt8;
+
+ //
+ // This value holds the number of characters desired for a
+ // particular read. It is initially set by read length in the
+ // WDFREQUEST. It is decremented each time more characters are placed
+ // into the "users" buffer buy the code that reads characters
+ // out of the typeahead buffer into the users buffer. If the
+ // typeahead buffer is exhausted by the read, and the reads buffer
+ // is given to the isr to fill, this value is becomes meaningless.
+ //
+ ULONG NumberNeededForRead;
+
+ //
+ // This mask will hold the bitmask sent down via the set mask
+ // ioctl. It is used by the interrupt service routine to determine
+ // if the occurence of "events" (in the serial drivers understanding
+ // of the concept of an event) should be noted.
+ //
+ ULONG IsrWaitMask;
+
+ //
+ // This mask will always be a subset of the IsrWaitMask. While
+ // at device level, if an event occurs that is "marked" as interesting
+ // in the IsrWaitMask, the driver will turn on that bit in this
+ // history mask. The driver will then look to see if there is a
+ // request waiting for an event to occur. If there is one, it
+ // will copy the value of the history mask into the wait request, zero
+ // the history mask, and complete the wait request. If there is no
+ // waiting request, the driver will be satisfied with just recording
+ // that the event occured. If a wait request should be queued,
+ // the driver will look to see if the history mask is non-zero. If
+ // it is non-zero, the driver will copy the history mask into the
+ // request, zero the history mask, and then complete the request.
+ //
+ ULONG HistoryMask;
+
+ //
+ // This is a pointer to the where the history mask should be
+ // placed when completing a wait. It is only accessed at
+ // device level.
+ //
+ // We have a pointer here to assist us to synchronize completing a wait.
+ // If this is non-zero, then we have wait outstanding, and the isr still
+ // knows about it. We make this pointer null so that the isr won't
+ // attempt to complete the wait.
+ //
+ // We still keep a pointer around to the wait request, since the actual
+ // pointer to the wait request will be used for the "common" request completion
+ // path.
+ //
+ ULONG *IrpMaskLocation;
+
+ //
+ // This mask holds all of the reason that transmission
+ // is not proceeding. Normal transmission can not occur
+ // if this is non-zero.
+ //
+ // This is only written from interrupt level.
+ // This could be (but is not) read at any level.
+ //
+ ULONG TXHolding;
+
+ //
+ // This mask holds all of the reason that reception
+ // is not proceeding. Normal reception can not occur
+ // if this is non-zero.
+ //
+ // This is only written from interrupt level.
+ // This could be (but is not) read at any level.
+ //
+ ULONG RXHolding;
+
+ //
+ // This holds the reasons that the driver thinks it is in
+ // an error state.
+ //
+ // This is only written from interrupt level.
+ // This could be (but is not) read at any level.
+ //
+ ULONG ErrorWord;
+
+ //
+ // This keeps a total of the number of characters that
+ // are in all of the "write" irps that the driver knows
+ // about. It is only accessed with the cancel spinlock
+ // held.
+ //
+ ULONG TotalCharsQueued;
+
+ //
+ // This holds a count of the number of characters read
+ // the last time the interval timer dpc fired. It
+ // is a long (rather than a ulong) since the other read
+ // completion routines use negative values to indicate
+ // to the interval timer that it should complete the read
+ // if the interval timer DPC was lurking in some DPC queue when
+ // some other way to complete occurs.
+ //
+ LONG CountOnLastRead;
+
+ //
+ // This is a count of the number of characters read by the
+ // isr routine. It is *ONLY* written at isr level. We can
+ // read it at dispatch level.
+ //
+ ULONG ReadByIsr;
+
+ //
+ // This holds the current baud rate for the device.
+ //
+ ULONG CurrentBaud;
+
+ //
+ // This is the number of characters read since the XoffCounter
+ // was started. This variable is only accessed at device level.
+ // If it is greater than zero, it implies that there is an
+ // XoffCounter ioctl in the queue.
+ //
+ LONG CountSinceXoff;
+
+ //
+ // This ulong is incremented each time something trys to start
+ // the execution path that tries to lower the RTS line when
+ // doing transmit toggling. If it "bumps" into another path
+ // (indicated by a false return value from queueing a dpc
+ // and a TRUE return value tring to start a timer) it will
+ // decrement the count. These increments and decrements
+ // are all done at device level. Note that in the case
+ // of a bump while trying to start the timer, we have to
+ // go up to device level to do the decrement.
+ //
+ ULONG CountOfTryingToLowerRTS;
+
+ //
+ // This ULONG is used to keep track of the "named" (in ntddser.h)
+ // baud rates that this particular device supports.
+ //
+ ULONG SupportedBauds;
+
+ //
+ // Holds the timeout controls for the device. This value
+ // is set by the Ioctl processing.
+ //
+ // It should only be accessed under protection of the control
+ // lock since more than one request can be in the control dispatch
+ // routine at one time.
+ //
+ SERIAL_TIMEOUTS Timeouts;
+
+ //
+ // This holds the various characters that are used
+ // for replacement on errors and also for flow control.
+ //
+ // They are only set at interrupt level.
+ //
+ SERIAL_CHARS SpecialChars;
+
+ //
+ // This structure holds the handshake and control flow
+ // settings for the serial driver.
+ //
+ // It is only set at interrupt level. It can be
+ // be read at any level with the control lock held.
+ //
+ SERIAL_HANDFLOW HandFlow;
+
+
+ //
+ // Holds performance statistics that applications can query.
+ // Reset on each open. Only set at device level.
+ //
+ SERIALPERF_STATS PerfStats;
+
+ //
+ // This holds what we beleive to be the current value of
+ // the line control register.
+ //
+ // It should only be accessed under protection of the control
+ // lock since more than one request can be in the control dispatch
+ // routine at one time.
+ //
+ UCHAR LineControl;
+
+
+ //
+ // This is only accessed at interrupt level. It keeps track
+ // of whether the holding register is empty.
+ //
+ BOOLEAN HoldingEmpty;
+
+ //
+ // This variable is only accessed at interrupt level. It
+ // indicates that we want to transmit a character immediately.
+ // That is - in front of any characters that could be transmitting
+ // from a normal write.
+ //
+ BOOLEAN TransmitImmediate;
+
+ //
+ // This variable is only accessed at interrupt level. Whenever
+ // a wait is initiated this variable is set to false.
+ // Whenever any kind of character is written it is set to true.
+ // Whenever the write queue is found to be empty the code that
+ // is processing that completing request will synchonize with the interrupt.
+ // If this synchronization code finds that the variable is true and that
+ // there is a wait on the transmit queue being empty then it is
+ // certain that the queue was emptied and that it has happened since
+ // the wait was initiated.
+ //
+ BOOLEAN EmptiedTransmit;
+
+ //
+ // We keep the following values around so that we can connect
+ // to the interrupt and report resources after the configuration
+ // record is gone.
+ //
+
+ //
+ // We hold the character that should be transmitted immediately.
+ //
+ // Note that we can't use this to determine whether there is
+ // a character to send because the character to send could be
+ // zero.
+ //
+ UCHAR ImmediateChar;
+
+ //
+ // This holds the mask that will be used to mask off unwanted
+ // data bits of the received data (valid data bits can be 5,6,7,8)
+ // The mask will normally be 0xff. This is set while the control
+ // lock is held since it wouldn't have adverse effects on the
+ // isr if it is changed in the middle of reading characters.
+ // (What it would do to the app is another question - but then
+ // the app asked the driver to do it.)
+ //
+ UCHAR ValidDataMask;
+
+ //
+ // The application can turn on a mode,via the
+ // IOCTL_SERIAL_LSRMST_INSERT ioctl, that will cause the
+ // serial driver to insert the line status or the modem
+ // status into the RX stream. The parameter with the ioctl
+ // is a pointer to a UCHAR. If the value of the UCHAR is
+ // zero, then no insertion will ever take place. If the
+ // value of the UCHAR is non-zero (and not equal to the
+ // xon/xoff characters), then the serial driver will insert.
+ //
+ UCHAR EscapeChar;
+
+ //
+ // These two booleans are used to indicate to the isr transmit
+ // code that it should send the xon or xoff character. They are
+ // only accessed at open and at interrupt level.
+ //
+ BOOLEAN SendXonChar;
+ BOOLEAN SendXoffChar;
+
+ //
+ // This boolean will be true if a 16550 is present *and* enabled.
+ //
+ BOOLEAN FifoPresent;
+
+ //
+ // This is the water mark that the rxfifo should be
+ // set to when the fifo is turned on. This is not the actual
+ // value, but the encoded value that goes into the register.
+ //
+ UCHAR RxFifoTrigger;
+
+ //
+ // This points to a DPC used to complete write requests.
+ //
+ WDFDPC CompleteWriteDpc;
+
+ //
+ // This points to a DPC used to complete read requests.
+ //
+ WDFDPC CompleteReadDpc;
+
+
+ //
+ // This dpc is fired off if a comm error occurs. It will
+ // execute a dpc routine that will cancel all pending reads
+ // and writes.
+ //
+ WDFDPC CommErrorDpc;
+
+ //
+ // 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.
+ //
+ WDFDPC CommWaitDpc;
+
+ //
+ // This dpc is fired off when the transmit immediate char
+ // character is given to the hardware. It will simply complete
+ // the request.
+ //
+ WDFDPC CompleteImmediateDpc;
+
+ //
+ // This dpc is fired off if the xoff counter actually runs down
+ // to zero.
+ //
+ WDFDPC XoffCountCompleteDpc;
+
+ //
+ // 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.
+ //
+ WDFDPC StartTimerLowerRTSDpc;
+
+ //
+ // This timer used to handle total read request timing.
+ //
+ WDFTIMER ReadRequestTotalTimer;
+
+ //
+ // This timer used to handle interval read request timing.
+ //
+ WDFTIMER ReadRequestIntervalTimer;
+
+ //
+ // This timer used to handle total write request timing.
+ //
+ WDFTIMER WriteRequestTotalTimer;
+
+ //
+ // This is timer structure used to handle total time request timing.
+ //
+ WDFTIMER ImmediateTotalTimer;
+
+ //
+ // This timer is used to timeout the xoff counter io.
+ //
+ WDFTIMER XoffCountTimer;
+
+ //
+ // This timer is used to invoke a dpc one character time
+ // after the timer is set. That dpc will be used to check
+ // whether we should lower the RTS line if we are doing
+ // transmit toggling.
+ //
+ WDFTIMER LowerRTSTimer;
+
+ //
+ // WMI Information
+ //
+
+ //
+ // WMI Comm Data
+ //
+
+ SERIAL_WMI_COMM_DATA WmiCommData;
+
+ //
+ // WMI HW Data
+ //
+
+ SERIAL_WMI_HW_DATA WmiHwData;
+
+ //
+ // WMI Performance Data
+ //
+
+ SERIAL_WMI_PERF_DATA WmiPerfData;
+
+} SERIAL_DEVICE_EXTENSION,*PSERIAL_DEVICE_EXTENSION;
+
+WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(SERIAL_DEVICE_EXTENSION,
+ SerialGetDeviceExtension)
+
+//
+// This is the scratch area for every request.
+// We will copy some of the frequently used information of the request
+// into our context area so that way we don't have to call WdfRequestGetParams
+// function everytime.
+//
+typedef struct _REQUEST_CONTEXT {
+ ULONG_PTR Information;
+ NTSTATUS Status;
+ ULONG Length;
+ PVOID RefCount;
+ PVOID SystemBuffer;
+ UCHAR MajorFunction;
+ PFN_WDF_REQUEST_CANCEL CancelRoutine;
+ BOOLEAN Cancelled;
+ PVOID Type3InputBuffer;
+ PSERIAL_DEVICE_EXTENSION Extension;
+ ULONG IoctlCode;
+ BOOLEAN MarkCancelableOnResume;
+} REQUEST_CONTEXT, *PREQUEST_CONTEXT;
+
+
+WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(REQUEST_CONTEXT,
+ SerialGetRequestContext)
+
+
+//
+// This is the Interrupt context for the Serial device. This structure is used
+// for keeping track of whether the Interrupt is connected or not.
+//
+typedef struct _SERIAL_INTERRUPT_CONTEXT {
+
+ //
+ // This boolean value indicates whether Interrupt is connected.
+ //
+ BOOLEAN IsInterruptConnected;
+
+ //
+ // This lock is used to synchronize the file close logic and
+ // the Surprise Removal logic. When a surprise remove happens,
+ // the device interrupts are disabled. When this occurs, the
+ // file close logic should not attempt to use the interrupt
+ // object.
+ //
+ WDFWAITLOCK InterruptStateLock;
+
+} SERIAL_INTERRUPT_CONTEXT, *PSERIAL_INTERRUPT_CONTEXT;
+
+WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(SERIAL_INTERRUPT_CONTEXT,
+ SerialGetInterruptContext)
+
+
+#define SERIAL_FLAGS_CLEAR 0x0L
+#define SERIAL_FLAGS_STARTED 0x1L
+#define SERIAL_FLAGS_STOPPED 0x2L
+#define SERIAL_FLAGS_BROKENHW 0x4L
+#define SERIAL_FLAGS_LEGACY_ENUMED 0x8L
+
+
+__inline
+UCHAR
+SerialReadPortUChar (
+ IN UCHAR * x
+ )
+{
+ return READ_PORT_UCHAR (x);
+}
+__inline
+VOID
+SerialWritePortUChar (
+ IN UCHAR * x,
+ IN UCHAR y
+ )
+{
+ WRITE_PORT_UCHAR (x,y);
+}
+
+__inline
+UCHAR
+SerialReadRegisterUChar (
+ IN UCHAR * x
+ )
+{
+ return READ_REGISTER_UCHAR (x);
+}
+
+__inline
+VOID
+SerialWriteRegisterUChar (
+ IN UCHAR * x,
+ IN UCHAR y
+ )
+{
+ WRITE_REGISTER_UCHAR (x,y);
+}
+
+
+
+//
+// Sets the divisor latch register. The divisor latch register
+// is used to control the baud rate of the 8250.
+//
+// As with all of these routines it is assumed that it is called
+// at a safe point to access the hardware registers. In addition
+// it also assumes that the data is correct.
+//
+// Arguments:
+//
+// BaseAddress - A pointer to the address from which the hardware
+// device registers are located.
+//
+// DesiredDivisor - The value to which the divisor latch register should
+// be set.
+//
+#define WRITE_DIVISOR_LATCH(Extension, BaseAddress,DesiredDivisor) \
+do \
+{ \
+ PUCHAR Address = BaseAddress; \
+ SHORT Divisor = DesiredDivisor; \
+ UCHAR LineControl; \
+ LineControl = Extension->SerialReadUChar(Address+LINE_CONTROL_REGISTER); \
+ Extension->SerialWriteUChar( \
+ Address+LINE_CONTROL_REGISTER, \
+ (UCHAR)(LineControl | SERIAL_LCR_DLAB) \
+ ); \
+ Extension->SerialWriteUChar( \
+ Address+DIVISOR_LATCH_LSB, \
+ (UCHAR)(Divisor & 0xff) \
+ ); \
+ Extension->SerialWriteUChar( \
+ Address+DIVISOR_LATCH_MSB, \
+ (UCHAR)((Divisor & 0xff00) >> 8) \
+ ); \
+ Extension->SerialWriteUChar( \
+ Address+LINE_CONTROL_REGISTER, \
+ LineControl \
+ ); \
+} WHILE (0)
+
+//
+// Reads the divisor latch register. The divisor latch register
+// is used to control the baud rate of the 8250.
+//
+// As with all of these routines it is assumed that it is called
+// at a safe point to access the hardware registers. In addition
+// it also assumes that the data is correct.
+//
+// Arguments:
+//
+// BaseAddress - A pointer to the address from which the hardware
+// device registers are located.
+//
+// DesiredDivisor - A pointer to the 2 byte word which will contain
+// the value of the divisor.
+//
+#define READ_DIVISOR_LATCH(Extension, BaseAddress,PDesiredDivisor) \
+do \
+{ \
+ PUCHAR Address = BaseAddress; \
+ PSHORT PDivisor = PDesiredDivisor; \
+ UCHAR LineControl; \
+ UCHAR Lsb; \
+ UCHAR Msb; \
+ LineControl = Extension->SerialReadUChar(Address+LINE_CONTROL_REGISTER); \
+ Extension->SerialWriteUChar( \
+ Address+LINE_CONTROL_REGISTER, \
+ (UCHAR)(LineControl | SERIAL_LCR_DLAB) \
+ ); \
+ Lsb = Extension->SerialReadUChar(Address+DIVISOR_LATCH_LSB); \
+ Msb = Extension->SerialReadUChar(Address+DIVISOR_LATCH_MSB); \
+ *PDivisor = Lsb; \
+ *PDivisor = *PDivisor | (((USHORT)Msb) << 8); \
+ Extension->SerialWriteUChar( \
+ Address+LINE_CONTROL_REGISTER, \
+ LineControl \
+ ); \
+} WHILE (0)
+
+//
+// This macro reads the interrupt enable register.
+//
+// Arguments:
+//
+// BaseAddress - A pointer to the address from which the hardware
+// device registers are located.
+//
+#define READ_INTERRUPT_ENABLE(Extension, BaseAddress) \
+ (Extension->SerialReadUChar((BaseAddress)+INTERRUPT_ENABLE_REGISTER))
+
+//
+// This macro writes the interrupt enable register.
+//
+// Arguments:
+//
+// BaseAddress - A pointer to the address from which the hardware
+// device registers are located.
+//
+// Values - The values to write to the interrupt enable register.
+//
+#define WRITE_INTERRUPT_ENABLE(Extension, BaseAddress,Values) \
+do \
+{ \
+ Extension->SerialWriteUChar( \
+ BaseAddress+INTERRUPT_ENABLE_REGISTER, \
+ Values \
+ ); \
+} WHILE (0)
+
+//
+// This macro disables all interrupts on the hardware.
+//
+// Arguments:
+//
+// BaseAddress - A pointer to the address from which the hardware
+// device registers are located.
+//
+//
+#define DISABLE_ALL_INTERRUPTS(Extension, BaseAddress) \
+do \
+{ \
+ WRITE_INTERRUPT_ENABLE(Extension, BaseAddress,0); \
+} WHILE (0)
+
+//
+// This macro enables all interrupts on the hardware.
+//
+// Arguments:
+//
+// BaseAddress - A pointer to the address from which the hardware
+// device registers are located.
+//
+//
+#define ENABLE_ALL_INTERRUPTS(Extension, BaseAddress) \
+do \
+{ \
+ \
+ WRITE_INTERRUPT_ENABLE( \
+ (Extension), (BaseAddress), \
+ (UCHAR)(SERIAL_IER_RDA | SERIAL_IER_THR | \
+ SERIAL_IER_RLS | SERIAL_IER_MS) \
+ ); \
+ \
+} WHILE (0)
+
+//
+// This macro reads the interrupt identification register
+//
+// Arguments:
+//
+// BaseAddress - A pointer to the address from which the hardware
+// device registers are located.
+//
+// Note that this routine potententially quites a transmitter
+// empty interrupt. This is because one way that the transmitter
+// empty interrupt is cleared is to simply read the interrupt id
+// register.
+//
+//
+#define READ_INTERRUPT_ID_REG(Extension, BaseAddress) \
+ (Extension->SerialReadUChar((BaseAddress)+INTERRUPT_IDENT_REGISTER))
+
+//
+// This macro reads the modem control register
+//
+// Arguments:
+//
+// BaseAddress - A pointer to the address from which the hardware
+// device registers are located.
+//
+//
+#define READ_MODEM_CONTROL(Extension, BaseAddress) \
+ (Extension->SerialReadUChar((BaseAddress)+MODEM_CONTROL_REGISTER))
+
+//
+// This macro reads the modem status register
+//
+// Arguments:
+//
+// BaseAddress - A pointer to the address from which the hardware
+// device registers are located.
+//
+//
+#define READ_MODEM_STATUS(Extension, BaseAddress) \
+ (Extension->SerialReadUChar((BaseAddress)+MODEM_STATUS_REGISTER))
+
+//
+// This macro reads a value out of the receive buffer
+//
+// Arguments:
+//
+// BaseAddress - A pointer to the address from which the hardware
+// device registers are located.
+//
+//
+#define READ_RECEIVE_BUFFER(Extension, BaseAddress) \
+ (Extension->SerialReadUChar((BaseAddress)+RECEIVE_BUFFER_REGISTER))
+
+//
+// This macro reads the line status register
+//
+// Arguments:
+//
+// BaseAddress - A pointer to the address from which the hardware
+// device registers are located.
+//
+//
+#define READ_LINE_STATUS(Extension, BaseAddress) \
+ (Extension->SerialReadUChar((BaseAddress)+LINE_STATUS_REGISTER))
+
+//
+// This macro writes the line control register
+//
+// Arguments:
+//
+// BaseAddress - A pointer to the address from which the hardware
+// device registers are located.
+//
+//
+#define WRITE_LINE_CONTROL(Extension, BaseAddress,NewLineControl) \
+do \
+{ \
+ Extension->SerialWriteUChar( \
+ (BaseAddress)+LINE_CONTROL_REGISTER, \
+ (NewLineControl) \
+ ); \
+} WHILE (0)
+
+//
+// This macro reads the line control register
+//
+// Arguments:
+//
+// BaseAddress - A pointer to the address from which the hardware
+// device registers are located.
+//
+//
+#define READ_LINE_CONTROL(Extension, BaseAddress) \
+ (Extension->SerialReadUChar((BaseAddress)+LINE_CONTROL_REGISTER))
+
+
+//
+// This macro writes to the transmit register
+//
+// Arguments:
+//
+// BaseAddress - A pointer to the address from which the hardware
+// device registers are located.
+//
+// TransmitChar - The character to send down the wire.
+//
+//
+#define WRITE_TRANSMIT_HOLDING(Extension, BaseAddress,TransmitChar) \
+do \
+{ \
+ Extension->SerialWriteUChar( \
+ (BaseAddress)+TRANSMIT_HOLDING_REGISTER, \
+ (TransmitChar) \
+ ); \
+} WHILE (0)
+
+//
+// This macro writes to the transmit FIFO register
+//
+// Arguments:
+//
+// BaseAddress - A pointer to the address from which the hardware
+// device registers are located.
+//
+// TransmitChars - Pointer to the characters to send down the wire.
+//
+// TxN - number of charactes to send.
+//
+//
+#define WRITE_TRANSMIT_FIFO_HOLDING(Extension, BaseAddress,TransmitChars,TxN) \
+do \
+{ \
+ WRITE_PORT_BUFFER_UCHAR( \
+ (BaseAddress)+TRANSMIT_HOLDING_REGISTER, \
+ (TransmitChars), \
+ (TxN) \
+ ); \
+} WHILE (0)
+
+//
+// This macro writes to the control register
+//
+// Arguments:
+//
+// BaseAddress - A pointer to the address from which the hardware
+// device registers are located.
+//
+// ControlValue - The value to set the fifo control register too.
+//
+//
+#define WRITE_FIFO_CONTROL(Extension, BaseAddress,ControlValue) \
+do \
+{ \
+ Extension->SerialWriteUChar( \
+ (BaseAddress)+FIFO_CONTROL_REGISTER, \
+ (ControlValue) \
+ ); \
+} WHILE (0)
+
+//
+// This macro writes to the modem control register
+//
+// Arguments:
+//
+// BaseAddress - A pointer to the address from which the hardware
+// device registers are located.
+//
+// ModemControl - The control bits to send to the modem control.
+//
+//
+#define WRITE_MODEM_CONTROL(Extension, BaseAddress,ModemControl) \
+do \
+{ \
+ Extension->SerialWriteUChar( \
+ (BaseAddress)+MODEM_CONTROL_REGISTER, \
+ (ModemControl) \
+ ); \
+} WHILE (0)
+
+#define WRITE_INTERRUPT_STATUS(Extension, BaseAddress,Status) \
+do \
+{ \
+ Extension->SerialWriteUChar(BaseAddress, Status); \
+} WHILE (0)
+
+
+//
+// This macro reads the interrupt status register
+//
+// Arguments:
+//
+// BaseAddress - A pointer to the address from which the hardware
+// device registers are located. BaseAddress is gotten
+// from PSERIAL_MULTIPORT_DISPATCH->InterruptStatus which
+// already has the complete address
+//
+// AddressSpace - Flag indicating where port is located, MMIO or IO
+// space
+//
+//
+#define READ_INTERRUPT_STATUS(Extension, BaseAddress) \
+ Extension->SerialReadUChar(BaseAddress))
+
+//
+// We use this to query into the registry as to whether we
+// should break at driver entry.
+//
+
+extern SERIAL_FIRMWARE_DATA driverDefaults;
+
+
+//
+// This is exported from the kernel. It is used to point
+// to the address that the kernel debugger is using.
+//
+
+extern PUCHAR *KdComPortInUse;
+
+
+typedef enum _SERIAL_MEM_COMPARES {
+ AddressesAreEqual,
+ AddressesOverlap,
+ AddressesAreDisjoint
+ } SERIAL_MEM_COMPARES,*PSERIAL_MEM_COMPARES;
+
+#define SERIAL_BAUD_INVALID 0xFFFFFFFF
+
+typedef struct _SUPPORTED_BAUD_RATES {
+ UINT32 BaudRate;
+ ULONG Mask;
+}SUPPORTED_BAUD_RATES;
+
diff --git a/tests/projects/wdk/kmdf/serial/serial.inx b/tests/projects/wdk/kmdf/serial/serial.inx
new file mode 100644
index 000000000..3322653b0
--- /dev/null
+++ b/tests/projects/wdk/kmdf/serial/serial.inx
Binary files differ
diff --git a/tests/projects/wdk/kmdf/serial/serial.rc b/tests/projects/wdk/kmdf/serial/serial.rc
new file mode 100644
index 000000000..9fbb59de3
--- /dev/null
+++ b/tests/projects/wdk/kmdf/serial/serial.rc
@@ -0,0 +1,14 @@
+#include <windows.h>
+
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_DRV
+#define VER_FILESUBTYPE VFT2_DRV_SYSTEM
+#define VER_FILEDESCRIPTION_STR "Serial Device Driver"
+#define VER_INTERNALNAME_STR "serial.sys"
+#define VER_ORIGINALFILENAME_STR "serial.sys"
+
+#include "common.ver"
+
+#include "serlog.rc"
+
diff --git a/tests/projects/wdk/kmdf/serial/serialp.h b/tests/projects/wdk/kmdf/serial/serialp.h
new file mode 100644
index 000000000..d363f3503
--- /dev/null
+++ b/tests/projects/wdk/kmdf/serial/serialp.h
@@ -0,0 +1,596 @@
+/*++
+
+Copyright (c) Microsoft Corporation
+
+Module Name :
+
+ serialp.h
+
+Abstract:
+
+ Prototypes and macros that are used throughout the driver.
+
+--*/
+
+//-----------------------------------------------------------------------------
+// 4127 -- Conditional Expression is Constant warning
+//-----------------------------------------------------------------------------
+#define WHILE(constant) \
+__pragma(warning(suppress: 4127)) while(constant)
+
+typedef
+VOID
+(*PSERIAL_START_ROUTINE) (
+ IN PSERIAL_DEVICE_EXTENSION
+ );
+
+typedef
+VOID
+(*PSERIAL_GET_NEXT_ROUTINE) (
+ IN WDFREQUEST *CurrentOpRequest,
+ IN WDFQUEUE QueueToProcess,
+ OUT WDFREQUEST *NewRequest,
+ IN BOOLEAN CompleteCurrent,
+ PSERIAL_DEVICE_EXTENSION Extension
+ );
+
+DRIVER_INITIALIZE DriverEntry;
+
+EVT_WDF_DRIVER_DEVICE_ADD SerialEvtDeviceAdd;
+EVT_WDF_OBJECT_CONTEXT_CLEANUP SerialEvtDriverContextCleanup;
+EVT_WDF_DEVICE_CONTEXT_CLEANUP SerialEvtDeviceContextCleanup;
+
+EVT_WDF_DEVICE_D0_ENTRY SerialEvtDeviceD0Entry;
+EVT_WDF_DEVICE_D0_EXIT SerialEvtDeviceD0Exit;
+EVT_WDF_DEVICE_D0_ENTRY_POST_INTERRUPTS_ENABLED SerialEvtDeviceD0EntryPostInterruptsEnabled;
+EVT_WDF_DEVICE_D0_EXIT_PRE_INTERRUPTS_DISABLED SerialEvtDeviceD0ExitPreInterruptsDisabled;
+EVT_WDF_DEVICE_PREPARE_HARDWARE SerialEvtPrepareHardware;
+EVT_WDF_DEVICE_RELEASE_HARDWARE SerialEvtReleaseHardware;
+
+EVT_WDF_DEVICE_FILE_CREATE SerialEvtDeviceFileCreate;
+EVT_WDF_FILE_CLOSE SerialEvtFileClose;
+
+EVT_WDF_IO_QUEUE_IO_READ SerialEvtIoRead;
+EVT_WDF_IO_QUEUE_IO_WRITE SerialEvtIoWrite;
+EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL SerialEvtIoDeviceControl;
+EVT_WDF_IO_QUEUE_IO_INTERNAL_DEVICE_CONTROL SerialEvtIoInternalDeviceControl;
+EVT_WDF_IO_QUEUE_IO_CANCELED_ON_QUEUE SerialEvtCanceledOnQueue;
+EVT_WDF_IO_QUEUE_IO_STOP SerialEvtIoStop;
+EVT_WDF_IO_QUEUE_IO_RESUME SerialEvtIoResume;
+
+EVT_WDF_INTERRUPT_ENABLE SerialEvtInterruptEnable;
+EVT_WDF_INTERRUPT_DISABLE SerialEvtInterruptDisable;
+
+EVT_WDF_DPC SerialCompleteRead;
+EVT_WDF_DPC SerialCompleteWrite;
+EVT_WDF_DPC SerialCommError;
+EVT_WDF_DPC SerialCompleteImmediate;
+EVT_WDF_DPC SerialCompleteXoff;
+EVT_WDF_DPC SerialCompleteWait;
+EVT_WDF_DPC SerialStartTimerLowerRTS;
+
+EVT_WDF_TIMER SerialReadTimeout;
+EVT_WDF_TIMER SerialIntervalReadTimeout;
+EVT_WDF_TIMER SerialWriteTimeout;
+EVT_WDF_TIMER SerialTimeoutImmediate;
+EVT_WDF_TIMER SerialTimeoutXoff;
+EVT_WDF_TIMER SerialInvokePerhapsLowerRTS;
+
+VOID
+SerialStartRead(
+ IN PSERIAL_DEVICE_EXTENSION Extension
+ );
+
+VOID
+SerialStartWrite(
+ IN PSERIAL_DEVICE_EXTENSION Extension
+ );
+
+VOID
+SerialStartMask(
+ IN PSERIAL_DEVICE_EXTENSION Extension
+ );
+
+VOID
+SerialStartImmediate(
+ IN PSERIAL_DEVICE_EXTENSION Extension
+ );
+
+VOID
+SerialStartPurge(
+ IN PSERIAL_DEVICE_EXTENSION Extension
+ );
+
+VOID
+SerialGetNextWrite(
+ IN WDFREQUEST *CurrentOpRequest,
+ IN WDFQUEUE QueueToProcess,
+ IN WDFREQUEST *NewRequest,
+ IN BOOLEAN CompleteCurrent,
+ IN PSERIAL_DEVICE_EXTENSION Extension
+ );
+
+EVT_WDFDEVICE_WDM_IRP_PREPROCESS SerialWdmDeviceFileCreate;
+EVT_WDFDEVICE_WDM_IRP_PREPROCESS SerialWdmFileClose;
+EVT_WDFDEVICE_WDM_IRP_PREPROCESS SerialFlush;
+
+EVT_WDFDEVICE_WDM_IRP_PREPROCESS SerialQueryInformationFile;
+EVT_WDFDEVICE_WDM_IRP_PREPROCESS SerialSetInformationFile;
+
+NTSTATUS
+SerialDeviceFileCreateWorker (
+ IN WDFDEVICE Device
+ );
+
+
+VOID
+SerialFileCloseWorker(
+ IN WDFDEVICE Device
+ );
+
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialProcessEmptyTransmit;
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialSetDTR;
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialClrDTR;
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialSetRTS;
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialClrRTS;
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialSetBaud;
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialSetLineControl;
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialSetHandFlow;
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialTurnOnBreak;
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialTurnOffBreak;
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialPretendXoff;
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialPretendXon;
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialReset;
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialPerhapsLowerRTS;
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialMarkOpen;
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialMarkClose;
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialGetStats;
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialClearStats;
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialSetChars;
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialSetMCRContents;
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialGetMCRContents;
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialSetFCRContents;
+
+BOOLEAN
+SerialSetupNewHandFlow(
+ IN PSERIAL_DEVICE_EXTENSION Extension,
+ IN PSERIAL_HANDFLOW NewHandFlow
+ );
+
+
+VOID
+SerialHandleReducedIntBuffer(
+ IN PSERIAL_DEVICE_EXTENSION Extension
+ );
+
+VOID
+SerialProdXonXoff(
+ IN PSERIAL_DEVICE_EXTENSION Extension,
+ IN BOOLEAN SendXon
+ );
+
+EVT_WDF_REQUEST_CANCEL SerialCancelWait;
+
+
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialPurgeInterruptBuff;
+
+VOID
+SerialPurgeRequests(
+ IN WDFQUEUE QueueToClean,
+ IN WDFREQUEST *CurrentOpRequest
+ );
+
+VOID
+SerialFlushRequests(
+ IN WDFQUEUE QueueToClean,
+ IN WDFREQUEST *CurrentOpRequest
+ );
+
+VOID
+SerialGetNextRequest(
+ IN WDFREQUEST *CurrentOpRequest,
+ IN WDFQUEUE QueueToProcess,
+ OUT WDFREQUEST *NextIrp,
+ IN BOOLEAN CompleteCurrent,
+ IN PSERIAL_DEVICE_EXTENSION extension
+ );
+
+
+VOID
+SerialTryToCompleteCurrent(
+ IN PSERIAL_DEVICE_EXTENSION Extension,
+ IN PFN_WDF_INTERRUPT_SYNCHRONIZE SynchRoutine OPTIONAL,
+ IN NTSTATUS StatusToUse,
+ IN WDFREQUEST *CurrentOpRequest,
+ IN WDFQUEUE QueueToProcess,
+ IN WDFTIMER IntervalTimer,
+ IN WDFTIMER TotalTimer,
+ IN PSERIAL_START_ROUTINE Starter,
+ IN PSERIAL_GET_NEXT_ROUTINE GetNextIrp,
+ IN LONG RefType
+ );
+
+VOID
+SerialStartOrQueue(
+ IN PSERIAL_DEVICE_EXTENSION Extension,
+ IN WDFREQUEST Request,
+ IN WDFQUEUE QueueToExamine,
+ IN WDFREQUEST *CurrentOpRequest,
+ IN PSERIAL_START_ROUTINE Starter
+ );
+
+NTSTATUS
+SerialCompleteIfError(
+ PSERIAL_DEVICE_EXTENSION extension,
+ WDFREQUEST Request
+ );
+
+ULONG
+SerialHandleModemUpdate(
+ IN PSERIAL_DEVICE_EXTENSION Extension,
+ IN BOOLEAN DoingTX
+ );
+
+
+EVT_WDF_INTERRUPT_ISR SerialISR;
+
+NTSTATUS
+SerialGetDivisorFromBaud(
+ IN ULONG ClockRate,
+ IN LONG DesiredBaud,
+ OUT PSHORT AppropriateDivisor
+ );
+
+VOID
+SerialCleanupDevice(
+ IN PSERIAL_DEVICE_EXTENSION Extension
+ );
+
+UCHAR
+SerialProcessLSR(
+ IN PSERIAL_DEVICE_EXTENSION Extension
+ );
+
+LARGE_INTEGER
+SerialGetCharTime(
+ IN PSERIAL_DEVICE_EXTENSION Extension
+ );
+
+
+VOID
+SerialPutChar(
+ IN PSERIAL_DEVICE_EXTENSION Extension,
+ IN UCHAR CharToPut
+ );
+
+NTSTATUS
+SerialGetConfigDefaults(
+ IN PSERIAL_FIRMWARE_DATA DriverDefaultsPtr,
+ IN WDFDRIVER Driver
+ );
+
+VOID
+SerialGetProperties(
+ IN PSERIAL_DEVICE_EXTENSION Extension,
+ IN PSERIAL_COMMPROP Properties
+ );
+
+VOID
+SerialLogError(
+ _In_ PDRIVER_OBJECT DriverObject,
+ _In_opt_ PDEVICE_OBJECT DeviceObject,
+ _In_ PHYSICAL_ADDRESS P1,
+ _In_ PHYSICAL_ADDRESS P2,
+ _In_ ULONG SequenceNumber,
+ _In_ UCHAR MajorFunctionCode,
+ _In_ UCHAR RetryCount,
+ _In_ ULONG UniqueErrorValue,
+ _In_ NTSTATUS FinalStatus,
+ _In_ NTSTATUS SpecificIOStatus,
+ _In_ ULONG LengthOfInsert1,
+ _In_reads_bytes_opt_(LengthOfInsert1) PWCHAR Insert1,
+ _In_ ULONG LengthOfInsert2,
+ _In_reads_bytes_opt_(LengthOfInsert2) PWCHAR Insert2
+ );
+
+NTSTATUS
+SerialMapHWResources(
+ IN WDFDEVICE Device,
+ IN WDFCMRESLIST PResList,
+ IN WDFCMRESLIST PTrResList,
+ OUT PCONFIG_DATA PConfig
+ );
+
+VOID
+SerialUnmapHWResources(
+ IN PSERIAL_DEVICE_EXTENSION PDevExt
+ );
+
+BOOLEAN
+SerialGetRegistryKeyValue (
+ IN WDFDEVICE WdfDevice,
+ _In_ PCWSTR Name,
+ OUT PULONG Value
+ );
+
+
+BOOLEAN
+SerialPutRegistryKeyValue (
+ IN WDFDEVICE WdfDevice,
+ _In_ PCWSTR Name,
+ IN ULONG Value
+ );
+
+NTSTATUS
+SerialInitController(
+ IN PSERIAL_DEVICE_EXTENSION pDevExt,
+ IN PCONFIG_DATA PConfigData
+ );
+
+BOOLEAN
+SerialCIsrSw(
+ IN WDFINTERRUPT Interrupt,
+ IN ULONG MessageID
+ );
+
+NTSTATUS
+SerialDoExternalNaming(
+ IN PSERIAL_DEVICE_EXTENSION PDevExt
+ );
+
+PVOID
+SerialGetMappedAddress(
+ PHYSICAL_ADDRESS IoAddress,
+ ULONG NumberOfBytes,
+ ULONG AddressSpace,
+ PBOOLEAN MappedAddress
+ );
+
+BOOLEAN
+SerialDoesPortExist(
+ IN PSERIAL_DEVICE_EXTENSION Extension,
+ PUNICODE_STRING InsertString,
+ IN ULONG ForceFifo,
+ IN ULONG LogFifo
+ );
+
+SERIAL_MEM_COMPARES
+SerialMemCompare(
+ IN PHYSICAL_ADDRESS A,
+ IN ULONG SpanOfA,
+ IN PHYSICAL_ADDRESS B,
+ IN ULONG SpanOfB
+ );
+
+VOID
+SerialUndoExternalNaming(
+ IN PSERIAL_DEVICE_EXTENSION Extension
+ );
+
+VOID
+SerialReleaseResources(
+ IN PSERIAL_DEVICE_EXTENSION PDevExt
+ );
+
+VOID
+SerialPurgePendingRequests(
+ PSERIAL_DEVICE_EXTENSION pDevExt
+ );
+
+VOID
+SerialDisableUART(
+ IN PVOID Context
+ );
+
+VOID
+SerialDrainUART(
+ IN PSERIAL_DEVICE_EXTENSION PDevExt,
+ IN PLARGE_INTEGER PDrainTime
+ );
+
+VOID
+SerialSaveDeviceState(
+ IN PSERIAL_DEVICE_EXTENSION PDevExt
+ );
+
+NTSTATUS
+SerialSetPowerPolicy(
+ IN PSERIAL_DEVICE_EXTENSION DeviceExtension
+ );
+
+UINT32
+SerialReportMaxBaudRate(
+ ULONG Bauds
+ );
+
+BOOLEAN
+SerialInsertQueueDpc(
+ IN WDFDPC Dpc
+ );
+
+BOOLEAN
+SerialSetTimer(
+ IN WDFTIMER Timer,
+ IN LARGE_INTEGER DueTime
+ );
+
+BOOLEAN
+SerialCancelTimer(
+ IN WDFTIMER Timer,
+ IN PSERIAL_DEVICE_EXTENSION PDevExt
+ );
+
+VOID
+SerialUnlockPages(
+ IN WDFDPC PDpc,
+ IN PVOID PDeferredContext,
+ IN PVOID PSysContext1,
+ IN PVOID PSysContext2)
+ ;
+
+VOID
+SerialMarkHardwareBroken(
+ IN PSERIAL_DEVICE_EXTENSION PDevExt
+ );
+
+VOID
+SerialDisableInterfacesResources(
+ IN PSERIAL_DEVICE_EXTENSION PDevExt,
+ IN BOOLEAN DisableUART
+ );
+
+VOID
+SerialSetDeviceFlags(
+ IN PSERIAL_DEVICE_EXTENSION PDevExt,
+ OUT PULONG PFlags,
+ IN ULONG Value,
+ IN BOOLEAN Set
+ );
+
+
+VOID
+SetDeviceIsOpened(
+ IN PSERIAL_DEVICE_EXTENSION PDevExt,
+ IN BOOLEAN DeviceIsOpened,
+ IN BOOLEAN Reopen
+ );
+
+BOOLEAN
+IsQueueEmpty(
+ IN WDFQUEUE Queue
+ );
+
+NTSTATUS
+SerialCreateTimersAndDpcs(
+ IN PSERIAL_DEVICE_EXTENSION PDevExt
+ );
+
+VOID
+SerialDrainTimersAndDpcs(
+ IN PSERIAL_DEVICE_EXTENSION PDevExt
+ );
+
+VOID
+SerialSetCancelRoutine(
+ IN WDFREQUEST Request,
+ IN PFN_WDF_REQUEST_CANCEL CancelRoutine
+ );
+
+NTSTATUS
+SerialClearCancelRoutine(
+ IN WDFREQUEST Request,
+ IN BOOLEAN ClearReference
+ );
+
+NTSTATUS
+SerialWmiRegistration(
+ WDFDEVICE Device
+ );
+
+NTSTATUS
+SerialReadSymName(
+ IN WDFDEVICE Device,
+ _Out_writes_bytes_(*SizeOfRegName) PWSTR RegName,
+ _Inout_ PUSHORT SizeOfRegName
+ );
+
+VOID
+SerialCompleteRequest(
+ IN WDFREQUEST Request,
+ IN NTSTATUS Status,
+ IN ULONG_PTR Info
+ );
+
+BOOLEAN
+SerialGetFdoRegistryKeyValue(
+ IN PWDFDEVICE_INIT DeviceInit,
+ _In_ PCWSTR Name,
+ OUT PULONG Value
+ );
+
+VOID
+SerialSetInterruptPolicy(
+ _In_ WDFINTERRUPT WdfInterrupt
+ );
+
+typedef struct _SERIAL_UPDATE_CHAR {
+ PSERIAL_DEVICE_EXTENSION Extension;
+ ULONG CharsCopied;
+ BOOLEAN Completed;
+ } SERIAL_UPDATE_CHAR,*PSERIAL_UPDATE_CHAR;
+
+//
+// The following simple structure is used to send a pointer
+// the device extension and an ioctl specific pointer
+// to data.
+//
+typedef struct _SERIAL_IOCTL_SYNC {
+ PSERIAL_DEVICE_EXTENSION Extension;
+ PVOID Data;
+ } SERIAL_IOCTL_SYNC,*PSERIAL_IOCTL_SYNC;
+
+
+//
+// The following three macros are used to initialize, set
+// and clear references in IRPs that are used by
+// this driver. The reference is stored in the fourth
+// argument of the request, which is never used by any operation
+// accepted by this driver.
+//
+
+#define SERIAL_REF_ISR (0x00000001)
+#define SERIAL_REF_CANCEL (0x00000002)
+#define SERIAL_REF_TOTAL_TIMER (0x00000004)
+#define SERIAL_REF_INT_TIMER (0x00000008)
+#define SERIAL_REF_XOFF_REF (0x00000010)
+
+
+#define SERIAL_INIT_REFERENCE(ReqContext) { \
+ (ReqContext)->RefCount = NULL; \
+ }
+
+#define SERIAL_SET_REFERENCE(ReqContext, RefType) \
+ do { \
+ LONG _refType = (RefType); \
+ PULONG_PTR _arg4 = (PVOID)&(ReqContext)->RefCount; \
+ ASSERT(!(*_arg4 & _refType)); \
+ *_arg4 |= _refType; \
+ } WHILE (0)
+
+#define SERIAL_CLEAR_REFERENCE(ReqContext, RefType) \
+ do { \
+ LONG _refType = (RefType); \
+ PULONG_PTR _arg4 = (PVOID)&(ReqContext)->RefCount; \
+ ASSERT(*_arg4 & _refType); \
+ *_arg4 &= ~_refType; \
+ } WHILE (0)
+
+#define SERIAL_REFERENCE_COUNT(ReqContext) \
+ ((ULONG_PTR)(((ReqContext)->RefCount)))
+
+#define SERIAL_TEST_REFERENCE(ReqContext, RefType) ((ULONG_PTR)ReqContext ->RefCount & RefType)
+
+//
+// Prototypes and defines to handle processor groups.
+//
+typedef
+USHORT
+(*PFN_KE_GET_ACTIVE_GROUP_COUNT)(
+ VOID
+ );
+
+typedef
+KAFFINITY
+(*PFN_KE_QUERY_GROUP_AFFINITY) (
+ _In_ USHORT GroupNumber
+ );
+
+//
+// Force the serial interrupt to run on the last interrupt group.
+//
+//#define SERIAL_SELECT_INTERRUPT_GROUP 1
+#define SERIAL_LAST_INTERRUPT_GROUP 0xFFFF
+#define SERIAL_PREFERRED_INTERRUPT_GROUP SERIAL_LAST_INTERRUPT_GROUP
+
+
+
diff --git a/tests/projects/wdk/kmdf/serial/serlog.mc b/tests/projects/wdk/kmdf/serial/serlog.mc
new file mode 100644
index 000000000..ee6935b67
--- /dev/null
+++ b/tests/projects/wdk/kmdf/serial/serlog.mc
@@ -0,0 +1,290 @@
+;/*++ BUILD Version: 0001 // Increment this if a change has global effects
+;
+;Copyright (c) 1992, 1993 Microsoft Corporation
+;
+;Module Name:
+;
+; ntiologc.h
+;
+;Abstract:
+;
+; Constant definitions for the I/O error code log values.
+;
+;--*/
+;
+;#ifndef _SERLOG_
+;#define _SERLOG_
+;
+;//
+;// Status values are 32 bit values layed out as follows:
+;//
+;// 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
+;// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
+;// +---+-+-------------------------+-------------------------------+
+;// |Sev|C| Facility | Code |
+;// +---+-+-------------------------+-------------------------------+
+;//
+;// where
+;//
+;// Sev - is the severity code
+;//
+;// 00 - Success
+;// 01 - Informational
+;// 10 - Warning
+;// 11 - Error
+;//
+;// C - is the Customer code flag
+;//
+;// Facility - is the facility code
+;//
+;// Code - is the facility's status code
+;//
+;
+MessageIdTypedef=NTSTATUS
+
+SeverityNames=(Success=0x0:STATUS_SEVERITY_SUCCESS
+ Informational=0x1:STATUS_SEVERITY_INFORMATIONAL
+ Warning=0x2:STATUS_SEVERITY_WARNING
+ Error=0x3:STATUS_SEVERITY_ERROR
+ )
+
+FacilityNames=(System=0x0
+ RpcRuntime=0x2:FACILITY_RPC_RUNTIME
+ RpcStubs=0x3:FACILITY_RPC_STUBS
+ Io=0x4:FACILITY_IO_ERROR_CODE
+ Serial=0x6:FACILITY_SERIAL_ERROR_CODE
+ )
+
+
+MessageId=0x0001 Facility=Serial Severity=Informational SymbolicName=SERIAL_KERNEL_DEBUGGER_ACTIVE
+Language=English
+The kernel debugger is already using %2.
+.
+
+MessageId=0x0002 Facility=Serial Severity=Informational SymbolicName=SERIAL_FIFO_PRESENT
+Language=English
+While validating that %2 was really a serial port, a fifo was detected. The fifo will be used.
+.
+
+MessageId=0x0003 Facility=Serial Severity=Informational SymbolicName=SERIAL_USER_OVERRIDE
+Language=English
+User configuration data for parameter %2 overriding firmware configuration data.
+.
+
+MessageId=0x0004 Facility=Serial Severity=Warning SymbolicName=SERIAL_NO_SYMLINK_CREATED
+Language=English
+Unable to create the symbolic link for %2.
+.
+
+MessageId=0x0005 Facility=Serial Severity=Warning SymbolicName=SERIAL_NO_DEVICE_MAP_CREATED
+Language=English
+Unable to create the device map entry for %2.
+.
+
+MessageId=0x0006 Facility=Serial Severity=Warning SymbolicName=SERIAL_NO_DEVICE_MAP_DELETED
+Language=English
+Unable to delete the device map entry for %2.
+.
+
+MessageId=0x0007 Facility=Serial Severity=Error SymbolicName=SERIAL_UNREPORTED_IRQL_CONFLICT
+Language=English
+Another driver on the system, which did not report its resources, has already claimed the interrupt used by %2.
+.
+
+MessageId=0x0008 Facility=Serial Severity=Error SymbolicName=SERIAL_INSUFFICIENT_RESOURCES
+Language=English
+Not enough resources were available for the driver.
+.
+
+MessageId=0x0009 Facility=Serial Severity=Error SymbolicName=SERIAL_UNSUPPORTED_CLOCK_RATE
+Language=English
+The baud clock rate configuration is not supported on device %2.
+.
+
+MessageId=0x000A Facility=Serial Severity=Error SymbolicName=SERIAL_REGISTERS_NOT_MAPPED
+Language=English
+The hardware locations for %2 could not be translated to something the memory management system could understand.
+.
+
+MessageId=0x000B Facility=Serial Severity=Error SymbolicName=SERIAL_RESOURCE_CONFLICT
+Language=English
+The hardware resources for %2 are already in use by another device.
+.
+
+MessageId=0x000C Facility=Serial Severity=Error SymbolicName=SERIAL_NO_BUFFER_ALLOCATED
+Language=English
+No memory could be allocated in which to place new data for %2.
+.
+
+MessageId=0x000D Facility=Serial Severity=Error SymbolicName=SERIAL_IER_INVALID
+Language=English
+While validating that %2 was really a serial port, the interrupt enable register contained enabled bits in a must be zero bitfield.
+The device is assumed not to be a serial port and will be deleted.
+.
+
+MessageId=0x000E Facility=Serial Severity=Error SymbolicName=SERIAL_MCR_INVALID
+Language=English
+While validating that %2 was really a serial port, the modem control register contained enabled bits in a must be zero bitfield.
+The device is assumed not to be a serial port and will be deleted.
+.
+
+MessageId=0x000F Facility=Serial Severity=Error SymbolicName=SERIAL_IIR_INVALID
+Language=English
+While validating that %2 was really a serial port, the interrupt id register contained enabled bits in a must be zero bitfield.
+The device is assumed not to be a serial port and will be deleted.
+.
+
+MessageId=0x0010 Facility=Serial Severity=Error SymbolicName=SERIAL_DL_INVALID
+Language=English
+While validating that %2 was really a serial port, the baud rate register could not be set consistantly.
+The device is assumed not to be a serial port and will be deleted.
+.
+
+MessageId=0x0011 Facility=Serial Severity=Error SymbolicName=SERIAL_NOT_ENOUGH_CONFIG_INFO
+Language=English
+Some firmware configuration information was incomplete.
+.
+
+MessageId=0x0012 Facility=Serial Severity=Error SymbolicName=SERIAL_NO_PARAMETERS_INFO
+Language=English
+No Parameters subkey was found for user defined data. This is odd, and it also means no user configuration can be found.
+.
+
+MessageId=0x0013 Facility=Serial Severity=Error SymbolicName=SERIAL_UNABLE_TO_ACCESS_CONFIG
+Language=English
+Specific user configuration data is unretrievable.
+.
+
+MessageId=0x0014 Facility=Serial Severity=Error SymbolicName=SERIAL_INVALID_PORT_INDEX
+Language=English
+On parameter %2 which indicates a multiport card, must have a port index specified greater than 0.
+.
+
+MessageId=0x0015 Facility=Serial Severity=Error SymbolicName=SERIAL_PORT_INDEX_TOO_HIGH
+Language=English
+On parameter %2 which indicates a multiport card, the port index for the multiport card is too large.
+.
+
+MessageId=0x0016 Facility=Serial Severity=Error SymbolicName=SERIAL_UNKNOWN_BUS
+Language=English
+The bus type for %2 is not recognizable.
+.
+
+MessageId=0x0017 Facility=Serial Severity=Error SymbolicName=SERIAL_BUS_NOT_PRESENT
+Language=English
+The bus type for %2 is not available on this computer.
+.
+
+MessageId=0x0018 Facility=Serial Severity=Error SymbolicName=SERIAL_BUS_INTERRUPT_CONFLICT
+Language=English
+The bus specified for %2 does not support the specified method of interrupt.
+.
+
+MessageId=0x0019 Facility=Serial Severity=Error SymbolicName=SERIAL_INVALID_USER_CONFIG
+Language=English
+User configuration for parameter %2 must have %3.
+.
+
+MessageId=0x001A Facility=Serial Severity=Error SymbolicName=SERIAL_DEVICE_TOO_HIGH
+Language=English
+The user specified port for %2 is way too high in physical memory.
+.
+
+MessageId=0x001B Facility=Serial Severity=Error SymbolicName=SERIAL_STATUS_TOO_HIGH
+Language=English
+The status port for %2 is way too high in physical memory.
+.
+
+MessageId=0x001C Facility=Serial Severity=Error SymbolicName=SERIAL_STATUS_CONTROL_CONFLICT
+Language=English
+The status port for %2 overlaps the control registers for the device.
+.
+
+MessageId=0x001D Facility=Serial Severity=Error SymbolicName=SERIAL_CONTROL_OVERLAP
+Language=English
+The control registers for %2 overlaps with the %3 control registers.
+.
+
+MessageId=0x001E Facility=Serial Severity=Error SymbolicName=SERIAL_STATUS_OVERLAP
+Language=English
+The status register for %2 overlaps the %3 control registers.
+.
+
+MessageId=0x001F Facility=Serial Severity=Error SymbolicName=SERIAL_STATUS_STATUS_OVERLAP
+Language=English
+The status register for %2 overlaps with the %3 status register.
+.
+
+MessageId=0x0020 Facility=Serial Severity=Error SymbolicName=SERIAL_CONTROL_STATUS_OVERLAP
+Language=English
+The control registers for %2 overlaps the %3 status register.
+.
+
+MessageId=0x0021 Facility=Serial Severity=Error SymbolicName=SERIAL_MULTI_INTERRUPT_CONFLICT
+Language=English
+Two ports, %2 and %3, on a single multiport card can't have two different interrupts.
+.
+
+MessageId=0x0022 Facility=Serial Severity=Informational SymbolicName=SERIAL_DISABLED_PORT
+Language=English
+Disabling %2 as requested by the configuration data.
+.
+
+MessageId=0x0023 Facility=Serial Severity=Error SymbolicName=SERIAL_GARBLED_PARAMETER
+Language=English
+Parameter %2 data is unretrievable from the registry.
+.
+
+MessageId=0x0024 Facility=Serial Severity=Error SymbolicName=SERIAL_DLAB_INVALID
+Language=English
+While validating that %2 was really a serial port, the contents of the divisor latch register was identical to the interrupt enable and the receive registers.
+The device is assumed not to be a serial port and will be deleted.
+.
+
+MessageId=0x0025 Facility=Serial Severity=Error SymbolicName=SERIAL_NO_TRANSLATE_PORT
+Language=English
+Could not translate the user reported I/O port for %2.
+.
+
+MessageId=0x0026 Facility=Serial Severity=Error SymbolicName=SERIAL_NO_GET_INTERRUPT
+Language=English
+Could not get the user reported interrupt for %2 from the HAL.
+.
+
+MessageId=0x0027 Facility=Serial Severity=Error SymbolicName=SERIAL_NO_TRANSLATE_ISR
+Language=English
+Could not translate the user reported Interrupt Status Register for %2.
+.
+
+MessageId=0x0028 Facility=Serial Severity=Error SymbolicName=SERIAL_NO_DEVICE_REPORT
+Language=English
+Could not report the discovered legacy device %2 to the IO subsystem.
+.
+
+MessageId=0x0029 Facility=Serial Severity=Error SymbolicName=SERIAL_REGISTRY_WRITE_FAILED
+Language=English
+Error writing to the registry.
+.
+
+MessageId=0x002A Facility=Serial Severity=Warning SymbolicName=SERIAL_MOUSE_CONFLICT_IRQ
+Language=English
+There is a serial mouse using the same interrupt as %2. Therefore, %2 will not be started.
+.
+
+MessageId=0x002B Facility=Serial Severity=Warning SymbolicName=SERIAL_MOUSE_ON_PORT
+Language=English
+There was a serial mouse found on %2. Therefore, %2 will be assigned to the mouse.
+.
+
+MessageId=0x002C Facility=Serial Severity=Error SymbolicName=SERIAL_NO_DEVICE_REPORT_RES
+Language=English
+Could not report device %2 to IO subsystem due to a resource conflict.
+.
+
+MessageId=0x002D Facility=Serial Severity=Error SymbolicName=SERIAL_HARDWARE_FAILURE
+Language=English
+The serial driver detected a hardware failure on device %2 and will disable this device.
+.
+
+;#endif /* _NTIOLOGC_ */
+
diff --git a/tests/projects/wdk/kmdf/serial/trace.h b/tests/projects/wdk/kmdf/serial/trace.h
new file mode 100644
index 000000000..5bd9d50ca
--- /dev/null
+++ b/tests/projects/wdk/kmdf/serial/trace.h
@@ -0,0 +1,118 @@
+/*++
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+ THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+ KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+ PURPOSE.
+
+Module Name:
+
+ TRACE.h
+
+Abstract:
+
+ Header file for the debug tracing related function defintions and macros.
+
+Environment:
+
+ Kernel mode
+
+--*/
+
+#include <evntrace.h> // For TRACE_LEVEL definitions
+
+#if !defined(EVENT_TRACING)
+
+//
+// TODO: These defines are missing in evntrace.h
+// in some DDK build environments (XP).
+//
+#if !defined(TRACE_LEVEL_NONE)
+ #define TRACE_LEVEL_NONE 0
+ #define TRACE_LEVEL_CRITICAL 1
+ #define TRACE_LEVEL_FATAL 1
+ #define TRACE_LEVEL_ERROR 2
+ #define TRACE_LEVEL_WARNING 3
+ #define TRACE_LEVEL_INFORMATION 4
+ #define TRACE_LEVEL_VERBOSE 5
+ #define TRACE_LEVEL_RESERVED6 6
+ #define TRACE_LEVEL_RESERVED7 7
+ #define TRACE_LEVEL_RESERVED8 8
+ #define TRACE_LEVEL_RESERVED9 9
+#endif
+
+
+//
+// Define Debug Flags
+//
+#define DBG_INIT 0x00000001
+#define DBG_PNP 0x00000002
+#define DBG_POWER 0x00000004
+#define DBG_WMI 0x00000008
+#define DBG_CREATE_CLOSE 0x00000010
+#define DBG_IOCTLS 0x00000020
+#define DBG_WRITE 0x00000040
+#define DBG_READ 0x00000080
+#define DBG_DPC 0x00000100
+#define DBG_INTERRUPT 0x00000200
+#define DBG_LOCKS 0x00000400
+#define DBG_QUEUEING 0x00000800
+#define DBG_HW_ACCESS 0x00001000
+
+VOID
+TraceEvents (
+ IN ULONG DebugPrintLevel,
+ IN ULONG DebugPrintFlag,
+ IN PCCHAR DebugMessage,
+ ...
+ );
+
+#define WPP_INIT_TRACING(DriverObject, RegistryPath)
+#define WPP_CLEANUP(DriverObject)
+
+#else
+//
+// If software tracing is defined in the sources file..
+// WPP_DEFINE_CONTROL_GUID specifies the GUID used for this driver.
+// *** REPLACE THE GUID WITH YOUR OWN UNIQUE ID ***
+// WPP_DEFINE_BIT allows setting debug bit masks to selectively print.
+// The names defined in the WPP_DEFINE_BIT call define the actual names
+// that are used to control the level of tracing for the control guid
+// specified.
+//
+// Name of the logger is Serial and the guid is
+// {F3A79AB6-9827-4419-9465-45CF949EF659}
+// (0xf3a79ab6, 0x9827, 0x4419, 0x94, 0x65, 0x45, 0xcf, 0x94, 0x9e, 0xf6, 0x59);
+//
+
+#define WPP_CHECK_FOR_NULL_STRING //to prevent exceptions due to NULL strings
+
+#define WPP_CONTROL_GUIDS \
+ WPP_DEFINE_CONTROL_GUID(SerialTraceGuid,(bc6c9364,fc67,42c5,acf7,abed3b12ecc6), \
+ WPP_DEFINE_BIT(DBG_INIT) /* bit 0 = 0x00000001 */ \
+ WPP_DEFINE_BIT(DBG_PNP) /* bit 1 = 0x00000002 */ \
+ WPP_DEFINE_BIT(DBG_POWER) /* bit 2 = 0x00000004 */ \
+ WPP_DEFINE_BIT(DBG_WMI) /* bit 3 = 0x00000008 */ \
+ WPP_DEFINE_BIT(DBG_CREATE_CLOSE) /* bit 4 = 0x00000010 */ \
+ WPP_DEFINE_BIT(DBG_IOCTLS) /* bit 5 = 0x00000020 */ \
+ WPP_DEFINE_BIT(DBG_WRITE) /* bit 6 = 0x00000040 */ \
+ WPP_DEFINE_BIT(DBG_READ) /* bit 7 = 0x00000080 */ \
+ WPP_DEFINE_BIT(DBG_DPC) /* bit 8 = 0x00000100 */ \
+ WPP_DEFINE_BIT(DBG_INTERRUPT) /* bit 9 = 0x00000200 */ \
+ WPP_DEFINE_BIT(DBG_LOCKS) /* bit 10 = 0x00000400 */ \
+ WPP_DEFINE_BIT(DBG_QUEUEING) /* bit 11 = 0x00000800 */ \
+ WPP_DEFINE_BIT(DBG_HW_ACCESS) /* bit 12 = 0x00001000 */ \
+ /* You can have up to 32 defines. If you want more than that,\
+ you have to provide another trace control GUID */\
+ )
+
+
+#define WPP_LEVEL_FLAGS_LOGGER(lvl,flags) WPP_LEVEL_LOGGER(flags)
+#define WPP_LEVEL_FLAGS_ENABLED(lvl, flags) (WPP_LEVEL_ENABLED(flags) && WPP_CONTROL(WPP_BIT_ ## flags).Level >= lvl)
+
+
+#endif
+
+
diff --git a/tests/projects/wdk/kmdf/serial/utils.c b/tests/projects/wdk/kmdf/serial/utils.c
new file mode 100644
index 000000000..a84136efe
--- /dev/null
+++ b/tests/projects/wdk/kmdf/serial/utils.c
@@ -0,0 +1,1946 @@
+/*++
+
+Copyright (c) Microsoft Corporation
+
+Module Name:
+
+ utils.c
+
+Abstract:
+
+ This module contains code that perform queueing and completion
+ manipulation on requests. Also module generic functions such
+ as error logging.
+
+Environment:
+
+ Kernel mode
+
+--*/
+
+#include "precomp.h"
+
+#if defined(EVENT_TRACING)
+#include "utils.tmh"
+#endif
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGESRP0,SerialMemCompare)
+#pragma alloc_text(PAGESRP0,SerialLogError)
+#pragma alloc_text(PAGESRP0,SerialMarkHardwareBroken)
+#endif // ALLOC_PRAGMA
+
+
+VOID
+SerialRundownIrpRefs(
+ IN WDFREQUEST *CurrentOpRequest,
+ IN WDFTIMER IntervalTimer,
+ IN WDFTIMER TotalTimer,
+ IN PSERIAL_DEVICE_EXTENSION PDevExt,
+ IN LONG RefType
+ );
+
+static const PHYSICAL_ADDRESS SerialPhysicalZero = {0};
+
+VOID
+SerialPurgeRequests(
+ IN WDFQUEUE QueueToClean,
+ IN WDFREQUEST *CurrentOpRequest
+ )
+
+/*++
+
+Routine Description:
+
+ This function is used to cancel all queued and the current irps
+ for reads or for writes. Called at DPC level.
+
+Arguments:
+
+ QueueToClean - A pointer to the queue which we're going to clean out.
+
+ CurrentOpRequest - Pointer to a pointer to the current request.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS status;
+ PREQUEST_CONTEXT reqContext;
+
+ WdfIoQueuePurge(QueueToClean, WDF_NO_EVENT_CALLBACK, WDF_NO_CONTEXT);
+
+ //
+ // The queue is clean. Now go after the current if
+ // it's there.
+ //
+
+ if (*CurrentOpRequest) {
+
+ PFN_WDF_REQUEST_CANCEL CancelRoutine;
+
+ reqContext = SerialGetRequestContext(*CurrentOpRequest);
+ CancelRoutine = reqContext->CancelRoutine;
+ //
+ // Clear the common cancel routine but don't clear the reference because the
+ // request specific cancel routine called below will clear the reference.
+ //
+ status = SerialClearCancelRoutine(*CurrentOpRequest, FALSE);
+ if (NT_SUCCESS(status)) {
+ //
+ // Let us just call the CancelRoutine to start the next request.
+ //
+ if(CancelRoutine) {
+ CancelRoutine(*CurrentOpRequest);
+ }
+ }
+ }
+}
+
+VOID
+SerialFlushRequests(
+ IN WDFQUEUE QueueToClean,
+ IN WDFREQUEST *CurrentOpRequest
+ )
+
+/*++
+
+Routine Description:
+
+ This function is used to cancel all queued and the current irps
+ for reads or for writes. Called at DPC level.
+
+Arguments:
+
+ QueueToClean - A pointer to the queue which we're going to clean out.
+
+ CurrentOpRequest - Pointer to a pointer to the current request.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ SerialPurgeRequests(QueueToClean, CurrentOpRequest);
+
+ //
+ // Since purge puts the queue state to fail requests, we have to explicitly
+ // change the queue state to accept requests.
+ //
+ WdfIoQueueStart(QueueToClean);
+
+}
+
+
+VOID
+SerialGetNextRequest(
+ IN WDFREQUEST * CurrentOpRequest,
+ IN WDFQUEUE QueueToProcess,
+ OUT WDFREQUEST * NextRequest,
+ IN BOOLEAN CompleteCurrent,
+ IN PSERIAL_DEVICE_EXTENSION Extension
+ )
+
+/*++
+
+Routine Description:
+
+ This function is used to make the head of the particular
+ queue the current request. It also completes the what
+ was the old current request if desired.
+
+Arguments:
+
+ CurrentOpRequest - Pointer to a pointer to the currently active
+ request for the particular work list. Note that
+ this item is not actually part of the list.
+
+ QueueToProcess - The list to pull the new item off of.
+
+ NextIrp - The next Request to process. Note that CurrentOpRequest
+ will be set to this value under protection of the
+ cancel spin lock. However, if *NextIrp is NULL when
+ this routine returns, it is not necessaryly true the
+ what is pointed to by CurrentOpRequest will also be NULL.
+ The reason for this is that if the queue is empty
+ when we hold the cancel spin lock, a new request may come
+ in immediately after we release the lock.
+
+ CompleteCurrent - If TRUE then this routine will complete the
+ request pointed to by the pointer argument
+ CurrentOpRequest.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ WDFREQUEST oldRequest = NULL;
+ PREQUEST_CONTEXT reqContext;
+ NTSTATUS status;
+
+ UNREFERENCED_PARAMETER(Extension);
+
+ oldRequest = *CurrentOpRequest;
+ *CurrentOpRequest = NULL;
+
+ //
+ // Check to see if there is a new request to start up.
+ //
+
+ status = WdfIoQueueRetrieveNextRequest(
+ QueueToProcess,
+ CurrentOpRequest
+ );
+
+ if(!NT_SUCCESS(status)) {
+ ASSERTMSG("WdfIoQueueRetrieveNextRequest failed",
+ status == STATUS_NO_MORE_ENTRIES);
+ }
+
+ *NextRequest = *CurrentOpRequest;
+
+ if (CompleteCurrent) {
+
+ if (oldRequest) {
+
+ reqContext = SerialGetRequestContext(oldRequest);
+
+ SerialCompleteRequest(oldRequest,
+ reqContext->Status,
+ reqContext->Information);
+ }
+ }
+}
+
+VOID
+SerialTryToCompleteCurrent(
+ IN PSERIAL_DEVICE_EXTENSION Extension,
+ IN PFN_WDF_INTERRUPT_SYNCHRONIZE SynchRoutine OPTIONAL,
+ IN NTSTATUS StatusToUse,
+ IN WDFREQUEST *CurrentOpRequest,
+ IN WDFQUEUE QueueToProcess OPTIONAL,
+ IN WDFTIMER IntervalTimer OPTIONAL,
+ IN WDFTIMER TotalTimer OPTIONAL,
+ IN PSERIAL_START_ROUTINE Starter OPTIONAL,
+ IN PSERIAL_GET_NEXT_ROUTINE GetNextRequest OPTIONAL,
+ IN LONG RefType
+ )
+
+/*++
+
+Routine Description:
+
+ This routine attempts to remove all of the reasons there are
+ references on the current read/write. If everything can be completed
+ it will complete this read/write and try to start another.
+
+ NOTE: This routine assumes that it is called with the cancel
+ spinlock held.
+
+Arguments:
+
+ Extension - Simply a pointer to the device extension.
+
+ SynchRoutine - A routine that will synchronize with the isr
+ and attempt to remove the knowledge of the
+ current request from the isr. NOTE: This pointer
+ can be null.
+
+ IrqlForRelease - This routine is called with the cancel spinlock held.
+ This is the irql that was current when the cancel
+ spinlock was acquired.
+
+ StatusToUse - The request's status field will be set to this value, if
+ this routine can complete the request.
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PREQUEST_CONTEXT reqContext;
+
+ ASSERTMSG("SerialTryToCompleteCurrent: CurrentOpRequest is NULL", *CurrentOpRequest);
+
+ reqContext = SerialGetRequestContext(*CurrentOpRequest);
+
+ if(RefType == SERIAL_REF_ISR || RefType == SERIAL_REF_XOFF_REF) {
+ //
+ // We can decrement the reference to "remove" the fact
+ // that the caller no longer will be accessing this request.
+ //
+
+ SERIAL_CLEAR_REFERENCE(
+ reqContext,
+ RefType
+ );
+ }
+
+ if (SynchRoutine) {
+
+ WdfInterruptSynchronize(
+ Extension->WdfInterrupt,
+ SynchRoutine,
+ Extension
+ );
+
+ }
+
+ //
+ // Try to run down all other references to this request.
+ //
+
+ SerialRundownIrpRefs(
+ CurrentOpRequest,
+ IntervalTimer,
+ TotalTimer,
+ Extension,
+ RefType
+ );
+
+ if(StatusToUse == STATUS_CANCELLED) {
+ //
+ // This function is called from a cancelroutine. So mark
+ // the request as cancelled. We need to do this because
+ // we may not complete the request below if somebody
+ // else has a reference to it.
+ // This state variable was added to avoid calling
+ // WdfRequestMarkCancelable second time on a request that
+ // has cancelled but wasn't completed in the cancel routine.
+ //
+ reqContext->Cancelled = TRUE;
+ }
+
+ //
+ // See if the ref count is zero after trying to complete everybody else.
+ //
+
+ if (!SERIAL_REFERENCE_COUNT(reqContext)) {
+
+ WDFREQUEST newRequest;
+
+
+ //
+ // The ref count was zero so we should complete this
+ // request.
+ //
+ // The following call will also cause the current request to be
+ // completed.
+ //
+
+ reqContext->Status = StatusToUse;
+
+ if (StatusToUse == STATUS_CANCELLED) {
+
+ reqContext->Information = 0;
+
+ }
+
+ if (GetNextRequest) {
+
+ GetNextRequest(
+ CurrentOpRequest,
+ QueueToProcess,
+ &newRequest,
+ TRUE,
+ Extension
+ );
+
+ if (newRequest) {
+
+ Starter(Extension);
+
+ }
+
+ } else {
+
+ WDFREQUEST oldRequest = *CurrentOpRequest;
+
+ //
+ // There was no get next routine. We will simply complete
+ // the request. We should make sure that we null out the
+ // pointer to the pointer to this request.
+ //
+
+ *CurrentOpRequest = NULL;
+
+ SerialCompleteRequest(oldRequest,
+ reqContext->Status,
+ reqContext->Information);
+ }
+
+ } else {
+
+
+ }
+
+}
+
+
+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
+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
+SerialRundownIrpRefs(
+ IN WDFREQUEST *CurrentOpRequest,
+ IN WDFTIMER IntervalTimer OPTIONAL,
+ IN WDFTIMER TotalTimer OPTIONAL,
+ IN PSERIAL_DEVICE_EXTENSION PDevExt,
+ IN LONG RefType
+ )
+
+/*++
+
+Routine Description:
+
+ This routine runs through the various items that *could*
+ have a reference to the current read/write. It try's to remove
+ the reason. If it does succeed in removing the reason it
+ will decrement the reference count on the request.
+
+ NOTE: This routine assumes that it is called with the cancel
+ spin lock held.
+
+Arguments:
+
+ CurrentOpRequest - Pointer to a pointer to current request for the
+ particular operation.
+
+ IntervalTimer - Pointer to the interval timer for the operation.
+ NOTE: This could be null.
+
+ TotalTimer - Pointer to the total timer for the operation.
+ NOTE: This could be null.
+
+ PDevExt - Pointer to device extension
+
+Return Value:
+
+ None.
+
+--*/
+
+
+{
+ PREQUEST_CONTEXT reqContext;
+ WDFREQUEST request = *CurrentOpRequest;
+
+ reqContext = SerialGetRequestContext(request);
+
+ if(RefType == SERIAL_REF_CANCEL) {
+ //
+ // Caller is a cancel routine. So just clear the reference.
+ //
+ SERIAL_CLEAR_REFERENCE( reqContext, SERIAL_REF_CANCEL );
+ reqContext->CancelRoutine = NULL;
+
+ } else {
+ //
+ // Try to clear the cancelable state.
+ //
+ SerialClearCancelRoutine(request, TRUE);
+ }
+ if (IntervalTimer) {
+
+ //
+ // Try to cancel the operations interval timer. If the operation
+ // returns true then the timer did have a reference to the
+ // request. Since we've canceled this timer that reference is
+ // no longer valid and we can decrement the reference count.
+ //
+ // If the cancel returns false then this means either of two things:
+ //
+ // a) The timer has already fired.
+ //
+ // b) There never was an interval timer.
+ //
+ // In the case of "b" there is no need to decrement the reference
+ // count since the "timer" never had a reference to it.
+ //
+ // In the case of "a", then the timer itself will be coming
+ // along and decrement it's reference. Note that the caller
+ // of this routine might actually be the this timer, so
+ // decrement the reference.
+ //
+
+ if (SerialCancelTimer(IntervalTimer, PDevExt)) {
+
+ SERIAL_CLEAR_REFERENCE(
+ reqContext,
+ SERIAL_REF_INT_TIMER
+ );
+
+ } else if(RefType == SERIAL_REF_INT_TIMER) { // caller is the timer
+
+ SERIAL_CLEAR_REFERENCE(
+ reqContext,
+ SERIAL_REF_INT_TIMER
+ );
+ }
+
+ }
+
+ if (TotalTimer) {
+
+ //
+ // Try to cancel the operations total timer. If the operation
+ // returns true then the timer did have a reference to the
+ // request. Since we've canceled this timer that reference is
+ // no longer valid and we can decrement the reference count.
+ //
+ // If the cancel returns false then this means either of two things:
+ //
+ // a) The timer has already fired.
+ //
+ // b) There never was an total timer.
+ //
+ // In the case of "b" there is no need to decrement the reference
+ // count since the "timer" never had a reference to it.
+ //
+ // In the case of "a", then the timer itself will be coming
+ // along and decrement it's reference. Note that the caller
+ // of this routine might actually be the this timer, so
+ // decrement the reference.
+ //
+
+ if (SerialCancelTimer(TotalTimer, PDevExt)) {
+
+ SERIAL_CLEAR_REFERENCE(
+ reqContext,
+ SERIAL_REF_TOTAL_TIMER
+ );
+
+ } else if(RefType == SERIAL_REF_TOTAL_TIMER) { // caller is the timer
+
+ SERIAL_CLEAR_REFERENCE(
+ reqContext,
+ SERIAL_REF_TOTAL_TIMER
+ );
+ }
+ }
+}
+
+
+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;
+ }
+}
+
+VOID
+SerialEvtCanceledOnQueue(
+ IN WDFQUEUE Queue,
+ IN WDFREQUEST Request
+ )
+
+/*++
+
+Routine Description:
+
+ Called when the request is cancelled while it's waiting
+ on the queue. This callback is used instead of EvtCleanupCallback
+ on the request because this one will be called with the
+ presentation lock held.
+
+
+Arguments:
+
+ Queue - Queue in which the request currently waiting
+ Request - Request being cancelled
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PSERIAL_DEVICE_EXTENSION extension = NULL;
+ PREQUEST_CONTEXT reqContext;
+
+ UNREFERENCED_PARAMETER(Queue);
+
+ reqContext = SerialGetRequestContext(Request);
+
+ extension = reqContext->Extension;
+
+ //
+ // If this is a write request then take the amount of characters
+ // to write and subtract it from the count of characters to write.
+ //
+
+ if (reqContext->MajorFunction == IRP_MJ_WRITE) {
+
+ extension->TotalCharsQueued -= reqContext->Length;
+
+ } else if (reqContext->MajorFunction == IRP_MJ_DEVICE_CONTROL) {
+
+ //
+ // If it's an immediate then we need to decrement the
+ // count of chars queued. If it's a resize then we
+ // need to deallocate the pool that we're passing on
+ // to the "resizing" routine.
+ //
+
+ if (( reqContext->IoctlCode == IOCTL_SERIAL_IMMEDIATE_CHAR) ||
+ (reqContext->IoctlCode == IOCTL_SERIAL_XOFF_COUNTER)) {
+
+ extension->TotalCharsQueued--;
+
+ } else if (reqContext->IoctlCode == IOCTL_SERIAL_SET_QUEUE_SIZE) {
+
+ //
+ // We shoved the pointer to the memory into the
+ // the type 3 buffer pointer which we KNOW we
+ // never use.
+ //
+
+ ASSERT(reqContext->Type3InputBuffer);
+
+ ExFreePool(reqContext->Type3InputBuffer);
+
+ reqContext->Type3InputBuffer = NULL;
+
+ }
+
+ }
+
+ SerialCompleteRequest(Request, WdfRequestGetStatus(Request), 0);
+}
+
+
+NTSTATUS
+SerialCompleteIfError(
+ PSERIAL_DEVICE_EXTENSION extension,
+ WDFREQUEST Request
+ )
+
+/*++
+
+Routine Description:
+
+ If the current request is not an IOCTL_SERIAL_GET_COMMSTATUS request and
+ there is an error and the application requested abort on errors,
+ then cancel the request.
+
+Arguments:
+
+ extension - Pointer to the device context
+
+ Request - Pointer to the WDFREQUEST to test.
+
+Return Value:
+
+ STATUS_SUCCESS or STATUS_CANCELLED.
+
+--*/
+
+{
+
+ WDF_REQUEST_PARAMETERS params;
+ NTSTATUS status = STATUS_SUCCESS;
+
+ if ((extension->HandFlow.ControlHandShake &
+ SERIAL_ERROR_ABORT) && extension->ErrorWord) {
+
+ WDF_REQUEST_PARAMETERS_INIT(&params);
+
+ WdfRequestGetParameters(
+ Request,
+ &params
+ );
+
+
+ //
+ // There is a current error in the driver. No requests should
+ // come through except for the GET_COMMSTATUS.
+ //
+
+ if ((params.Type != WdfRequestTypeDeviceControl) ||
+ (params.Parameters.DeviceIoControl.IoControlCode != IOCTL_SERIAL_GET_COMMSTATUS)) {
+ status = STATUS_CANCELLED;
+ SerialCompleteRequest(Request, status, 0);
+ }
+
+ }
+
+ return status;
+
+}
+
+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;
+}
+
+
+
+
+BOOLEAN
+SerialInsertQueueDpc(IN WDFDPC PDpc)
+/*++
+
+Routine Description:
+
+ This function must be called to queue DPC's for the serial driver.
+
+Arguments:
+
+ PDpc - Pointer to the Dpc object
+
+Return Value:
+
+ Kicks up return value from KeInsertQueueDpc()
+
+--*/
+{
+ //
+ // If the specified DPC object is not currently in the queue, WdfDpcEnqueue
+ // queues the DPC and returns TRUE.
+ //
+
+ return WdfDpcEnqueue(PDpc);
+}
+
+
+
+BOOLEAN
+SerialSetTimer(IN WDFTIMER Timer, IN LARGE_INTEGER DueTime)
+/*++
+
+Routine Description:
+
+ This function must be called to set timers for the serial driver.
+
+Arguments:
+
+ Timer - pointer to timer dispatcher object
+
+ DueTime - time at which the timer should expire
+
+
+Return Value:
+
+ Kicks up return value from KeSetTimerEx()
+
+--*/
+{
+ BOOLEAN result;
+ //
+ // If the timer object was already in the system timer queue, WdfTimerStart returns TRUE
+ //
+ result = WdfTimerStart(Timer, DueTime.QuadPart);
+
+ return result;
+
+}
+
+
+VOID
+SerialDrainTimersAndDpcs(
+ IN PSERIAL_DEVICE_EXTENSION PDevExt
+ )
+/*++
+
+Routine Description:
+
+ This function cancels all the timers and Dpcs and waits for them
+ to run to completion if they are already fired.
+
+Arguments:
+
+ PDevExt - Pointer to the device extension for the device that needs to
+ set a timer
+
+Return Value:
+
+--*/
+{
+ WdfTimerStop(PDevExt->ReadRequestTotalTimer, TRUE);
+
+ WdfTimerStop(PDevExt->ReadRequestIntervalTimer, TRUE);
+
+ WdfTimerStop(PDevExt->WriteRequestTotalTimer, TRUE);
+
+ WdfTimerStop(PDevExt->ImmediateTotalTimer, TRUE);
+
+ WdfTimerStop(PDevExt->XoffCountTimer, TRUE);
+
+ WdfTimerStop(PDevExt->LowerRTSTimer, TRUE);
+
+ WdfDpcCancel(PDevExt->CompleteWriteDpc, TRUE);
+
+ WdfDpcCancel(PDevExt->CompleteReadDpc, TRUE);
+
+ WdfDpcCancel(PDevExt->CommErrorDpc, TRUE);
+
+ WdfDpcCancel(PDevExt->CompleteImmediateDpc, TRUE);
+
+ WdfDpcCancel(PDevExt->CommWaitDpc, TRUE);
+
+ WdfDpcCancel(PDevExt->XoffCountCompleteDpc, TRUE);
+
+ WdfDpcCancel(PDevExt->StartTimerLowerRTSDpc, TRUE);
+
+ return;
+}
+
+
+
+BOOLEAN
+SerialCancelTimer(
+ IN WDFTIMER Timer,
+ IN PSERIAL_DEVICE_EXTENSION PDevExt
+ )
+/*++
+
+Routine Description:
+
+ This function must be called to cancel timers for the serial driver.
+
+Arguments:
+
+ Timer - pointer to timer dispatcher object
+
+ PDevExt - Pointer to the device extension for the device that needs to
+ set a timer
+
+Return Value:
+
+ True if timer was cancelled
+
+--*/
+{
+ UNREFERENCED_PARAMETER(PDevExt);
+
+ return WdfTimerStop(Timer, FALSE);
+}
+
+SERIAL_MEM_COMPARES
+SerialMemCompare(
+ IN PHYSICAL_ADDRESS A,
+ IN ULONG SpanOfA,
+ IN PHYSICAL_ADDRESS B,
+ IN ULONG SpanOfB
+ )
+/*++
+
+Routine Description:
+
+ Compare two phsical address.
+
+Arguments:
+
+ A - One half of the comparison.
+
+ SpanOfA - In units of bytes, the span of A.
+
+ B - One half of the comparison.
+
+ SpanOfB - In units of bytes, the span of B.
+
+
+Return Value:
+
+ The result of the comparison.
+
+--*/
+{
+ LARGE_INTEGER a;
+ LARGE_INTEGER b;
+
+ LARGE_INTEGER lower;
+ ULONG lowerSpan;
+ LARGE_INTEGER higher;
+
+ PAGED_CODE();
+
+ a = A;
+ b = B;
+
+ if (a.QuadPart == b.QuadPart) {
+
+ return AddressesAreEqual;
+
+ }
+
+ if (a.QuadPart > b.QuadPart) {
+
+ higher = a;
+ lower = b;
+ lowerSpan = SpanOfB;
+
+ } else {
+
+ higher = b;
+ lower = a;
+ lowerSpan = SpanOfA;
+
+ }
+
+ if ((higher.QuadPart - lower.QuadPart) >= lowerSpan) {
+
+ return AddressesAreDisjoint;
+
+ }
+
+ return AddressesOverlap;
+
+}
+
+
+VOID
+SerialLogError(
+ _In_ PDRIVER_OBJECT DriverObject,
+ _In_opt_ PDEVICE_OBJECT DeviceObject,
+ _In_ PHYSICAL_ADDRESS P1,
+ _In_ PHYSICAL_ADDRESS P2,
+ _In_ ULONG SequenceNumber,
+ _In_ UCHAR MajorFunctionCode,
+ _In_ UCHAR RetryCount,
+ _In_ ULONG UniqueErrorValue,
+ _In_ NTSTATUS FinalStatus,
+ _In_ NTSTATUS SpecificIOStatus,
+ _In_ ULONG LengthOfInsert1,
+ _In_reads_bytes_opt_(LengthOfInsert1) PWCHAR Insert1,
+ _In_ ULONG LengthOfInsert2,
+ _In_reads_bytes_opt_(LengthOfInsert2) PWCHAR Insert2
+ )
+/*++
+
+Routine Description:
+
+ This routine allocates an error log entry, copies the supplied data
+ to it, and requests that it be written to the error log file.
+
+Arguments:
+
+ DriverObject - A pointer to the driver object for the device.
+
+ DeviceObject - A pointer to the device object associated with the
+ device that had the error, early in initialization, one may not
+ yet exist.
+
+ P1,P2 - If phyical addresses for the controller ports involved
+ with the error are available, put them through as dump data.
+
+ SequenceNumber - A ulong value that is unique to an WDFREQUEST over the
+ life of the request in this driver - 0 generally means an error not
+ associated with an request.
+
+ MajorFunctionCode - If there is an error associated with the request,
+ this is the major function code of that request.
+
+ RetryCount - The number of times a particular operation has been
+ retried.
+
+ UniqueErrorValue - A unique long word that identifies the particular
+ call to this function.
+
+ FinalStatus - The final status given to the request that was associated
+ with this error. If this log entry is being made during one of
+ the retries this value will be STATUS_SUCCESS.
+
+ SpecificIOStatus - The IO status for a particular error.
+
+ LengthOfInsert1 - The length in bytes (including the terminating NULL)
+ of the first insertion string.
+
+ Insert1 - The first insertion string.
+
+ LengthOfInsert2 - The length in bytes (including the terminating NULL)
+ of the second insertion string. NOTE, there must
+ be a first insertion string for their to be
+ a second insertion string.
+
+ Insert2 - The second insertion string.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PIO_ERROR_LOG_PACKET errorLogEntry;
+
+ PVOID objectToUse;
+ SHORT dumpToAllocate = 0;
+ PUCHAR ptrToFirstInsert;
+ PUCHAR ptrToSecondInsert;
+
+ PAGED_CODE();
+
+ if (Insert1 == NULL) {
+ LengthOfInsert1 = 0;
+ }
+
+ if (Insert2 == NULL) {
+ LengthOfInsert2 = 0;
+ }
+
+
+ if (ARGUMENT_PRESENT(DeviceObject)) {
+
+ objectToUse = DeviceObject;
+
+ } else {
+
+ objectToUse = DriverObject;
+
+ }
+
+ if (SerialMemCompare(
+ P1,
+ (ULONG)1,
+ SerialPhysicalZero,
+ (ULONG)1
+ ) != AddressesAreEqual) {
+
+ dumpToAllocate = (SHORT)sizeof(PHYSICAL_ADDRESS);
+
+ }
+
+ if (SerialMemCompare(
+ P2,
+ (ULONG)1,
+ SerialPhysicalZero,
+ (ULONG)1
+ ) != AddressesAreEqual) {
+
+ dumpToAllocate += (SHORT)sizeof(PHYSICAL_ADDRESS);
+
+ }
+
+ errorLogEntry = IoAllocateErrorLogEntry(
+ objectToUse,
+ (UCHAR)(sizeof(IO_ERROR_LOG_PACKET) +
+ dumpToAllocate
+ + LengthOfInsert1 +
+ LengthOfInsert2)
+ );
+
+ if ( errorLogEntry != NULL ) {
+
+ errorLogEntry->ErrorCode = SpecificIOStatus;
+ errorLogEntry->SequenceNumber = SequenceNumber;
+ errorLogEntry->MajorFunctionCode = MajorFunctionCode;
+ errorLogEntry->RetryCount = RetryCount;
+ errorLogEntry->UniqueErrorValue = UniqueErrorValue;
+ errorLogEntry->FinalStatus = FinalStatus;
+ errorLogEntry->DumpDataSize = dumpToAllocate;
+
+ if (dumpToAllocate) {
+
+ RtlCopyMemory(
+ &errorLogEntry->DumpData[0],
+ &P1,
+ sizeof(PHYSICAL_ADDRESS)
+ );
+
+ if (dumpToAllocate > sizeof(PHYSICAL_ADDRESS)) {
+
+ RtlCopyMemory(
+ ((PUCHAR)&errorLogEntry->DumpData[0])
+ +sizeof(PHYSICAL_ADDRESS),
+ &P2,
+ sizeof(PHYSICAL_ADDRESS)
+ );
+
+ ptrToFirstInsert =
+ ((PUCHAR)&errorLogEntry->DumpData[0])+(2*sizeof(PHYSICAL_ADDRESS));
+
+ } else {
+
+ ptrToFirstInsert =
+ ((PUCHAR)&errorLogEntry->DumpData[0])+sizeof(PHYSICAL_ADDRESS);
+
+
+ }
+
+ } else {
+
+ ptrToFirstInsert = (PUCHAR)&errorLogEntry->DumpData[0];
+
+ }
+
+ ptrToSecondInsert = ptrToFirstInsert + LengthOfInsert1;
+
+ if (LengthOfInsert1) {
+
+ errorLogEntry->NumberOfStrings = 1;
+ errorLogEntry->StringOffset = (USHORT)(ptrToFirstInsert -
+ (PUCHAR)errorLogEntry);
+ RtlCopyMemory(
+ ptrToFirstInsert,
+ Insert1,
+ LengthOfInsert1
+ );
+
+ if (LengthOfInsert2) {
+
+ errorLogEntry->NumberOfStrings = 2;
+ RtlCopyMemory(
+ ptrToSecondInsert,
+ Insert2,
+ LengthOfInsert2
+ );
+
+ }
+
+ }
+
+ IoWriteErrorLogEntry(errorLogEntry);
+
+ }
+
+}
+
+VOID
+SerialMarkHardwareBroken(IN PSERIAL_DEVICE_EXTENSION PDevExt)
+/*++
+
+Routine Description:
+
+ Marks a UART as broken. This causes the driver stack to stop accepting
+ requests and eventually be removed.
+
+Arguments:
+ PDevExt - Device extension attached to PDevObj
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PAGED_CODE();
+
+ //
+ // Write a log entry
+ //
+
+ SerialLogError(PDevExt->DriverObject, NULL, SerialPhysicalZero,
+ SerialPhysicalZero, 0, 0, 0, 88, STATUS_SUCCESS,
+ SERIAL_HARDWARE_FAILURE, PDevExt->DeviceName.Length
+ + sizeof(WCHAR), PDevExt->DeviceName.Buffer, 0, NULL);
+
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_INIT, "Device is broken. Request a restart...\n");
+ WdfDeviceSetFailed(PDevExt->WdfDevice, WdfDeviceFailedAttemptRestart);
+}
+
+NTSTATUS
+SerialGetDivisorFromBaud(
+ IN ULONG ClockRate,
+ IN LONG DesiredBaud,
+ OUT PSHORT AppropriateDivisor
+ )
+
+/*++
+
+Routine Description:
+
+ This routine will determine a divisor based on an unvalidated
+ baud rate.
+
+Arguments:
+
+ ClockRate - The clock input to the controller.
+
+ DesiredBaud - The baud rate for whose divisor we seek.
+
+ AppropriateDivisor - Given that the DesiredBaud is valid, the
+ LONG pointed to by this parameter will be set to the appropriate
+ value. NOTE: The long is undefined if the DesiredBaud is not
+ supported.
+
+Return Value:
+
+ This function will return STATUS_SUCCESS if the baud is supported.
+ If the value is not supported it will return a status such that
+ NT_ERROR(Status) == FALSE.
+
+--*/
+
+{
+
+ NTSTATUS status = STATUS_SUCCESS;
+ SHORT calculatedDivisor;
+ ULONG denominator;
+ ULONG remainder;
+
+ //
+ // Allow up to a 1 percent error
+ //
+
+ ULONG maxRemain18 = 18432;
+ ULONG maxRemain30 = 30720;
+ ULONG maxRemain42 = 42336;
+ ULONG maxRemain80 = 80000;
+ ULONG maxRemain;
+
+
+
+ //
+ // Reject any non-positive bauds.
+ //
+
+ denominator = DesiredBaud*(ULONG)16;
+
+ if (DesiredBaud <= 0) {
+
+ *AppropriateDivisor = -1;
+
+ } else if ((LONG)denominator < DesiredBaud) {
+
+ //
+ // If the desired baud was so huge that it cause the denominator
+ // calculation to wrap, don't support it.
+ //
+
+ *AppropriateDivisor = -1;
+
+ } else {
+
+ if (ClockRate == 1843200) {
+ maxRemain = maxRemain18;
+ } else if (ClockRate == 3072000) {
+ maxRemain = maxRemain30;
+ } else if (ClockRate == 4233600) {
+ maxRemain = maxRemain42;
+ } else {
+ maxRemain = maxRemain80;
+ }
+
+ calculatedDivisor = (SHORT)(ClockRate / denominator);
+ remainder = ClockRate % denominator;
+
+ //
+ // Round up.
+ //
+
+ if (((remainder*2) > ClockRate) && (DesiredBaud != 110)) {
+
+ calculatedDivisor++;
+ }
+
+
+ //
+ // Only let the remainder calculations effect us if
+ // the baud rate is > 9600.
+ //
+
+ if (DesiredBaud >= 9600) {
+
+ //
+ // If the remainder is less than the maximum remainder (wrt
+ // the ClockRate) or the remainder + the maximum remainder is
+ // greater than or equal to the ClockRate then assume that the
+ // baud is ok.
+ //
+
+ if ((remainder >= maxRemain) && ((remainder+maxRemain) < ClockRate)) {
+ calculatedDivisor = -1;
+ }
+
+ }
+
+ //
+ // Don't support a baud that causes the denominator to
+ // be larger than the clock.
+ //
+
+ if (denominator > ClockRate) {
+
+ calculatedDivisor = -1;
+
+ }
+
+ //
+ // Ok, Now do some special casing so that things can actually continue
+ // working on all platforms.
+ //
+
+ if (ClockRate == 1843200) {
+
+ if (DesiredBaud == 56000) {
+ calculatedDivisor = 2;
+ }
+
+ } else if (ClockRate == 3072000) {
+
+ if (DesiredBaud == 14400) {
+ calculatedDivisor = 13;
+ }
+
+ } else if (ClockRate == 4233600) {
+
+ if (DesiredBaud == 9600) {
+ calculatedDivisor = 28;
+ } else if (DesiredBaud == 14400) {
+ calculatedDivisor = 18;
+ } else if (DesiredBaud == 19200) {
+ calculatedDivisor = 14;
+ } else if (DesiredBaud == 38400) {
+ calculatedDivisor = 7;
+ } else if (DesiredBaud == 56000) {
+ calculatedDivisor = 5;
+ }
+
+ } else if (ClockRate == 8000000) {
+
+ if (DesiredBaud == 14400) {
+ calculatedDivisor = 35;
+ } else if (DesiredBaud == 56000) {
+ calculatedDivisor = 9;
+ }
+
+ }
+
+ *AppropriateDivisor = calculatedDivisor;
+
+ }
+
+
+ if (*AppropriateDivisor == -1) {
+
+ status = STATUS_INVALID_PARAMETER;
+
+ }
+
+ return status;
+
+}
+
+
+BOOLEAN
+IsQueueEmpty(
+ IN WDFQUEUE Queue
+ )
+{
+ WDF_IO_QUEUE_STATE queueStatus;
+
+ queueStatus = WdfIoQueueGetState( Queue, NULL, NULL );
+
+ return (WDF_IO_QUEUE_IDLE(queueStatus)) ? TRUE : FALSE;
+}
+
+VOID
+SerialSetCancelRoutine(
+ IN WDFREQUEST Request,
+ IN PFN_WDF_REQUEST_CANCEL CancelRoutine)
+{
+ PREQUEST_CONTEXT reqContext = SerialGetRequestContext(Request);
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_IOCTLS,
+ "-->SerialSetCancelRoutine %p \n", Request);
+
+ WdfRequestMarkCancelable(Request, CancelRoutine);
+ SERIAL_SET_REFERENCE(reqContext, SERIAL_REF_CANCEL);
+ reqContext->CancelRoutine = CancelRoutine;
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_IOCTLS,
+ "<-- SerialSetCancelRoutine \n");
+
+ return;
+}
+
+NTSTATUS
+SerialClearCancelRoutine(
+ IN WDFREQUEST Request,
+ IN BOOLEAN ClearReference
+ )
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ PREQUEST_CONTEXT reqContext = SerialGetRequestContext(Request);
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_IOCTLS,
+ "-->SerialClearCancelRoutine %p %x\n",
+ Request, ClearReference);
+
+ if(SERIAL_TEST_REFERENCE(reqContext, SERIAL_REF_CANCEL))
+ {
+ status = WdfRequestUnmarkCancelable(Request);
+ if (NT_SUCCESS(status)) {
+
+ reqContext->CancelRoutine = NULL;
+ if(ClearReference) {
+
+ SERIAL_CLEAR_REFERENCE( reqContext, SERIAL_REF_CANCEL );
+
+ }
+ } else {
+ ASSERT(status == STATUS_CANCELLED);
+ }
+ }
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_IOCTLS,
+ "-->SerialClearCancelRoutine %p\n", Request);
+
+ return status;
+}
+
+
+VOID
+SerialCompleteRequest(
+ IN WDFREQUEST Request,
+ IN NTSTATUS Status,
+ IN ULONG_PTR Info
+ )
+{
+ PREQUEST_CONTEXT reqContext;
+
+ reqContext = SerialGetRequestContext(Request);
+
+ ASSERT(reqContext->RefCount == 0);
+
+ SerialDbgPrintEx(TRACE_LEVEL_VERBOSE, DBG_PNP,
+ "Complete Request: %p %X 0x%I64x\n",
+ (Request), (Status), (Info));
+
+ WdfRequestCompleteWithInformation((Request), (Status), (Info));
+
+}
+
+
diff --git a/tests/projects/wdk/kmdf/serial/waitmask.c b/tests/projects/wdk/kmdf/serial/waitmask.c
new file mode 100644
index 000000000..c3139679e
--- /dev/null
+++ b/tests/projects/wdk/kmdf/serial/waitmask.c
@@ -0,0 +1,574 @@
+/*++
+
+Copyright (c) Microsoft Corporation
+
+Module Name:
+
+ waitmask.c
+
+Abstract:
+
+ This module contains the code that is very specific to get/set/wait
+ on event mask operations in the serial driver
+
+Environment:
+
+ Kernel mode
+
+--*/
+
+#include "precomp.h"
+
+#if defined(EVENT_TRACING)
+#include "waitmask.tmh"
+#endif
+
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialGrabWaitFromIsr;
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialGiveWaitToIsr;
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialFinishOldWait;
+
+
+VOID
+SerialStartMask(
+ IN PSERIAL_DEVICE_EXTENSION Extension
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to process the set mask and wait
+ mask ioctls. Calls to this routine are serialized by
+ placing irps in the list under the protection of the
+ cancel spin lock.
+
+Arguments:
+
+ Extension - A pointer to the serial device extension.
+
+Return Value:
+
+ Will return pending for everything put the first
+ request that we actually process. Even in that
+ case it will return pending unless it can complete
+ it right away.
+
+
+--*/
+
+{
+
+
+ WDFREQUEST NewRequest;
+ PREQUEST_CONTEXT reqContext;
+ WDF_REQUEST_PARAMETERS params;
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_IOCTLS,
+ "In SerialStartMask\n");
+
+ ASSERT(Extension->CurrentMaskRequest);
+
+
+ do {
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_IOCTLS,
+ "STARTMASK - CurrentMaskRequest: %p\n",
+ Extension->CurrentMaskRequest);
+
+ WDF_REQUEST_PARAMETERS_INIT(&params);
+
+ WdfRequestGetParameters(
+ Extension->CurrentMaskRequest,
+ &params
+ );
+
+
+ reqContext = SerialGetRequestContext(Extension->CurrentMaskRequest);
+
+ ASSERT((params.Parameters.DeviceIoControl.IoControlCode ==
+ IOCTL_SERIAL_WAIT_ON_MASK) ||
+ (params.Parameters.DeviceIoControl.IoControlCode ==
+ IOCTL_SERIAL_SET_WAIT_MASK));
+
+ if (params.Parameters.DeviceIoControl.IoControlCode ==
+ IOCTL_SERIAL_SET_WAIT_MASK) {
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_IOCTLS,
+ "SERIAL - %p is a SETMASK request\n",
+ Extension->CurrentMaskRequest);
+
+ //
+ // Complete the old wait if there is one.
+ //
+
+ WdfInterruptSynchronize(
+ Extension->WdfInterrupt,
+ SerialFinishOldWait,
+ Extension
+ );
+
+ //
+ // Any current waits should be on its way to completion
+ // at this point. There certainly shouldn't be any
+ // request mask location.
+ //
+
+ ASSERT(!Extension->IrpMaskLocation);
+
+ reqContext->Status = STATUS_SUCCESS;
+
+ //
+ // The following call will also cause the current
+ // call to be completed.
+ //
+
+ SerialGetNextRequest(
+ &Extension->CurrentMaskRequest,
+ Extension->MaskQueue,
+ &NewRequest,
+ TRUE,
+ Extension
+ );
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_IOCTLS,
+ "Perhaps another mask request was found in "
+ "the queue\n"
+ "------- %p/%p <- values should be the same\n",
+ Extension->CurrentMaskRequest, NewRequest);
+
+
+ } else {
+
+ //
+ // First make sure that we have a non-zero mask.
+ // If the app queues a wait on a zero mask it can't
+ // be statisfied so it makes no sense to start it.
+ //
+
+ if ((!Extension->IsrWaitMask) || (Extension->CurrentWaitRequest)) {
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_IOCTLS,
+ "WaitIrp is invalid\n"
+ "------- IsrWaitMask: %x\n"
+ "------- CurrentWaitRequest: %p\n",
+ Extension->IsrWaitMask,
+ Extension->CurrentWaitRequest);
+
+ reqContext->Status = STATUS_INVALID_PARAMETER;
+
+ SerialGetNextRequest(&Extension->CurrentMaskRequest,
+ Extension->MaskQueue, &NewRequest, TRUE,
+ Extension);
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_IOCTLS,
+ "Perhaps another mask request was found "
+ "in the queue\n"
+ "------- %p/%p <- values should be the same\n",
+ Extension->CurrentMaskRequest,NewRequest);
+
+ } else {
+
+ //
+ // Make the current mask request the current wait request and
+ // get a new current mask request. Note that when we get
+ // the new current mask request we DO NOT complete the
+ // old current mask request (which is now the current wait
+ // request.
+ //
+ // Then under the protection of the cancel spin lock
+ // we check to see if the current wait request needs to
+ // be canceled
+ //
+
+ SERIAL_INIT_REFERENCE(reqContext);
+
+ SerialSetCancelRoutine(Extension->CurrentMaskRequest,
+ SerialCancelWait);
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_IOCTLS,
+ "%p will become the current "
+ "wait request\n",
+ Extension->CurrentMaskRequest);
+ //
+ // There should never be a mask location when
+ // there isn't a current wait request. At this point
+ // there shouldn't be a current wait request also.
+ //
+
+ ASSERT(!Extension->IrpMaskLocation);
+ ASSERT(!Extension->CurrentWaitRequest);
+
+ Extension->CurrentWaitRequest = Extension->CurrentMaskRequest;
+
+ WdfInterruptSynchronize(
+ Extension->WdfInterrupt,
+ SerialGiveWaitToIsr,
+ Extension
+ );
+
+ //
+ // Since it isn't really the mask request anymore,
+ // null out that pointer.
+ //
+ Extension->CurrentMaskRequest = NULL;
+
+ //
+ // This will release the cancel spinlock for us
+ //
+
+ SerialGetNextRequest(&Extension->CurrentMaskRequest,
+ Extension->MaskQueue, &NewRequest,
+ FALSE, Extension);
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_IOCTLS,
+ "Perhaps another mask request was "
+ "found in the queue\n"
+ "------- %p/%p <- values should be the "
+ "same\n", Extension->CurrentMaskRequest,
+ NewRequest);
+ }
+
+ }
+
+ } while (NewRequest);
+
+ return;
+
+}
+
+BOOLEAN
+SerialGrabWaitFromIsr(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This routine will check to see if the ISR still knows about
+ a wait request by checking to see if the IrpMaskLocation is non-null.
+ If it is then it will zero the Irpmasklocation (which in effect
+ grabs the request away from the isr). This routine is only called
+ buy the cancel code for the wait.
+
+ NOTE: This is called by WdfInterruptSynchronize.
+
+Arguments:
+
+ Context - A pointer to the device extension
+
+Return Value:
+
+ Always FALSE.
+
+--*/
+
+{
+ PSERIAL_DEVICE_EXTENSION Extension = Context;
+
+ PREQUEST_CONTEXT reqContext;
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ reqContext = SerialGetRequestContext(Extension->CurrentWaitRequest);
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_IOCTLS,
+ "In SerialGrabWaitFromIsr\n");
+
+ if (Extension->IrpMaskLocation) {
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_IOCTLS,
+ "The isr still owns the request %p, mask "
+ "location is %p\n"
+ "------- and system buffer is %p\n",
+ Extension->CurrentWaitRequest,Extension->IrpMaskLocation,
+ reqContext->SystemBuffer);
+
+ //
+ // The isr still "owns" the request.
+ //
+
+ *Extension->IrpMaskLocation = 0;
+ Extension->IrpMaskLocation = NULL;
+
+ reqContext->Information = sizeof(ULONG);
+
+ //
+ // Since the isr no longer references the request we need to
+ // decrement the reference count.
+ //
+
+ SERIAL_CLEAR_REFERENCE(
+ reqContext,
+ SERIAL_REF_ISR
+ );
+
+ }
+
+ return FALSE;
+}
+
+BOOLEAN
+SerialGiveWaitToIsr(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This routine simply sets a variable in the device extension
+ so that the isr knows that we have a wait request.
+
+ NOTE: This is called by WdfInterruptSynchronize.
+
+ NOTE: This routine assumes that it is called with the
+ cancel spinlock held.
+
+Arguments:
+
+ Context - Simply a pointer to the device extension.
+
+Return Value:
+
+ Always FALSE.
+
+--*/
+
+{
+
+ PSERIAL_DEVICE_EXTENSION Extension = Context;
+
+ PREQUEST_CONTEXT reqContext;
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ reqContext = SerialGetRequestContext(Extension->CurrentWaitRequest);
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_IOCTLS,
+ "In SerialGiveWaitToIsr\n");
+ //
+ // There certainly shouldn't be a current mask location at
+ // this point since we have a new current wait request.
+ //
+
+ ASSERT(!Extension->IrpMaskLocation);
+
+ //
+ // The isr may or may not actually reference this request. It
+ // won't if the wait can be satisfied immediately. However,
+ // since it will then go through the normal completion sequence,
+ // we need to have an incremented reference count anyway.
+ //
+
+ SERIAL_SET_REFERENCE(
+ reqContext,
+ SERIAL_REF_ISR
+ );
+
+ if (!Extension->HistoryMask) {
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_IOCTLS,
+ "No events occured prior to the wait call"
+ "\n");
+
+ //
+ // Although this wait might not be for empty transmit
+ // queue, it doesn't hurt anything to set it to false.
+ //
+
+ Extension->EmptiedTransmit = FALSE;
+
+ //
+ // Record where the "completion mask" should be set.
+ //
+
+ Extension->IrpMaskLocation = reqContext->SystemBuffer;
+ SerialDbgPrintEx( TRACE_LEVEL_INFORMATION, DBG_IOCTLS,
+ "The isr owns the request %p, mask location is "
+ "%p\n"
+ "------- and system buffer is %p\n",
+ Extension->CurrentWaitRequest,Extension->IrpMaskLocation,
+ reqContext->SystemBuffer);
+
+ } else {
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_IOCTLS,
+ "%x occurred prior to the wait - starting "
+ "the\n"
+ "------- completion code for %p\n",
+ Extension->HistoryMask,Extension->CurrentWaitRequest);
+
+ *((ULONG *)reqContext->SystemBuffer) =
+ Extension->HistoryMask;
+ Extension->HistoryMask = 0;
+ reqContext->Information = sizeof(ULONG);
+ reqContext->Status = STATUS_SUCCESS;
+
+ SerialInsertQueueDpc(Extension->CommWaitDpc);
+
+ }
+
+ return FALSE;
+}
+
+BOOLEAN
+SerialFinishOldWait(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This routine will check to see if the ISR still knows about
+ a wait request by checking to see if the Irpmasklocation is non-null.
+ If it is then it will zero the Irpmasklocation (which in effect
+ grabs the request away from the isr). This routine is only called
+ buy the cancel code for the wait.
+
+ NOTE: This is called by WdfInterruptSynchronize.
+
+Arguments:
+
+ Context - A pointer to the device extension
+
+Return Value:
+
+ Always FALSE.
+
+--*/
+
+{
+ PSERIAL_DEVICE_EXTENSION Extension = Context;
+
+ PREQUEST_CONTEXT reqContext = NULL;
+ PREQUEST_CONTEXT reqContextMask;
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ reqContextMask = SerialGetRequestContext(Extension->CurrentMaskRequest);
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_IOCTLS,
+ "In SerialFinishOldWait\n");
+
+ if (Extension->IrpMaskLocation) {
+
+ reqContext = SerialGetRequestContext(Extension->CurrentWaitRequest);
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_IOCTLS,
+ "The isr still owns the request %p, mask "
+ "location is %p\n"
+ "------- and system buffer is %p\n",
+ Extension->CurrentWaitRequest,Extension->IrpMaskLocation,
+ reqContext->SystemBuffer);
+ //
+ // The isr still "owns" the request.
+ //
+
+ *Extension->IrpMaskLocation = 0;
+ Extension->IrpMaskLocation = NULL;
+
+ reqContext->Information = sizeof(ULONG);
+
+ //
+ // We don't decrement the reference since the completion routine
+ // will do that.
+ //
+
+ SerialInsertQueueDpc(Extension->CommWaitDpc);
+
+ }
+
+ //
+ // Don't wipe out any historical data we are still interested in.
+ //
+
+ Extension->HistoryMask &= *((ULONG *)reqContextMask->SystemBuffer);
+
+ Extension->IsrWaitMask = *((ULONG *)reqContextMask->SystemBuffer);
+ SerialDbgPrintEx( TRACE_LEVEL_INFORMATION, DBG_IOCTLS,
+ "Set mask location of %p, in request %p, with "
+ "system buffer of %p\n",
+ Extension->IrpMaskLocation, Extension->CurrentMaskRequest,
+ reqContextMask->SystemBuffer);
+ return FALSE;
+}
+
+VOID
+SerialCancelWait(
+ IN WDFREQUEST Request
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to cancel a request that is waiting on
+ a comm event.
+
+Arguments:
+
+ Device - Wdf handle for the device
+
+ Request - Pointer to the WDFREQUEST for the current request
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ PSERIAL_DEVICE_EXTENSION Extension;
+ WDFDEVICE device = WdfIoQueueGetDevice(WdfRequestGetIoQueue(Request));
+
+ UNREFERENCED_PARAMETER(Request);
+
+ Extension = SerialGetDeviceExtension(device);
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_IOCTLS,
+ "Canceling wait for request %p\n",
+ Extension->CurrentWaitRequest);
+
+ SerialTryToCompleteCurrent(Extension,
+ SerialGrabWaitFromIsr,
+ STATUS_CANCELLED,
+ &Extension->CurrentWaitRequest,
+ NULL, NULL, NULL,
+ NULL, NULL, SERIAL_REF_CANCEL);
+
+}
+
+
+VOID
+SerialCompleteWait(
+ IN WDFDPC Dpc
+ )
+
+{
+
+ PSERIAL_DEVICE_EXTENSION Extension = NULL;
+
+ Extension = SerialGetDeviceExtension(WdfDpcGetParentObject(Dpc));
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_IOCTLS,
+ ">SerialCompleteWait(%p)\n",
+ Extension);
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_IOCTLS,
+ "Completing wait for request %p\n",
+ Extension->CurrentWaitRequest);
+
+ SerialTryToCompleteCurrent(Extension, NULL, STATUS_SUCCESS,
+ &Extension->CurrentWaitRequest, NULL, NULL, NULL,
+ NULL, NULL, SERIAL_REF_ISR);
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_IOCTLS,
+ "<SerialCompleteWait\n");
+}
+
+
diff --git a/tests/projects/wdk/kmdf/serial/wmi.c b/tests/projects/wdk/kmdf/serial/wmi.c
new file mode 100644
index 000000000..0076f4337
--- /dev/null
+++ b/tests/projects/wdk/kmdf/serial/wmi.c
@@ -0,0 +1,295 @@
+/*++
+
+Copyright (c) 1997 Microsoft Corporation
+
+Module Name:
+
+ wmi.c
+
+Abstract:
+
+ This module contains the code that handles the wmi IRPs for the
+ serial driver.
+
+Environment:
+
+ Kernel mode
+
+--*/
+
+#include "precomp.h"
+#include <wmistr.h>
+
+#if defined(EVENT_TRACING)
+#include "wmi.tmh"
+#endif
+
+EVT_WDF_WMI_INSTANCE_QUERY_INSTANCE EvtWmiQueryPortName;
+EVT_WDF_WMI_INSTANCE_QUERY_INSTANCE EvtWmiQueryPortCommData;
+EVT_WDF_WMI_INSTANCE_QUERY_INSTANCE EvtWmiQueryPortHWData;
+EVT_WDF_WMI_INSTANCE_QUERY_INSTANCE EvtWmiQueryPortPerfData;
+EVT_WDF_WMI_INSTANCE_QUERY_INSTANCE EvtWmiQueryPortPropData;
+
+NTSTATUS
+SerialWmiRegisterInstance(
+ WDFDEVICE Device,
+ const GUID* Guid,
+ ULONG MinInstanceBufferSize,
+ PFN_WDF_WMI_INSTANCE_QUERY_INSTANCE EvtWmiInstanceQueryInstance
+ );
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGESRP0, SerialWmiRegistration)
+#pragma alloc_text(PAGESRP0, SerialWmiRegisterInstance)
+#pragma alloc_text(PAGESRP0, EvtWmiQueryPortName)
+#pragma alloc_text(PAGESRP0, EvtWmiQueryPortCommData)
+#pragma alloc_text(PAGESRP0, EvtWmiQueryPortHWData)
+#pragma alloc_text(PAGESRP0, EvtWmiQueryPortPerfData)
+#pragma alloc_text(PAGESRP0, EvtWmiQueryPortPropData)
+#endif
+
+NTSTATUS
+SerialWmiRegisterInstance(
+ WDFDEVICE Device,
+ const GUID* Guid,
+ ULONG MinInstanceBufferSize,
+ PFN_WDF_WMI_INSTANCE_QUERY_INSTANCE EvtWmiInstanceQueryInstance
+ )
+{
+ WDF_WMI_PROVIDER_CONFIG providerConfig;
+ WDF_WMI_INSTANCE_CONFIG instanceConfig;
+
+ PAGED_CODE();
+
+ //
+ // Create and register WMI providers and instances blocks
+ //
+ WDF_WMI_PROVIDER_CONFIG_INIT(&providerConfig, Guid);
+ providerConfig.MinInstanceBufferSize = MinInstanceBufferSize;
+
+ WDF_WMI_INSTANCE_CONFIG_INIT_PROVIDER_CONFIG(&instanceConfig, &providerConfig);
+ instanceConfig.Register = TRUE;
+ instanceConfig.EvtWmiInstanceQueryInstance = EvtWmiInstanceQueryInstance;
+
+ return WdfWmiInstanceCreate(Device,
+ &instanceConfig,
+ WDF_NO_OBJECT_ATTRIBUTES,
+ WDF_NO_HANDLE);
+}
+
+NTSTATUS
+SerialWmiRegistration(
+ WDFDEVICE Device
+)
+/*++
+Routine Description
+
+ Registers with WMI as a data provider for this
+ instance of the device
+
+--*/
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ PSERIAL_DEVICE_EXTENSION pDevExt;
+
+ PAGED_CODE();
+
+ pDevExt = SerialGetDeviceExtension (Device);
+
+ //
+ // Fill in wmi perf data (all zero's)
+ //
+ RtlZeroMemory(&pDevExt->WmiPerfData, sizeof(pDevExt->WmiPerfData));
+
+ status = SerialWmiRegisterInstance(Device,
+ &MSSerial_PortName_GUID,
+ 0,
+ EvtWmiQueryPortName);
+ if (!NT_SUCCESS(status)) {
+ return status;
+ }
+
+ status = SerialWmiRegisterInstance(Device,
+ &MSSerial_CommInfo_GUID,
+ sizeof(SERIAL_WMI_COMM_DATA),
+ EvtWmiQueryPortCommData);
+ if (!NT_SUCCESS(status)) {
+ return status;
+ }
+
+ status = SerialWmiRegisterInstance(Device,
+ &MSSerial_HardwareConfiguration_GUID,
+ sizeof(SERIAL_WMI_HW_DATA),
+ EvtWmiQueryPortHWData);
+ if (!NT_SUCCESS(status)) {
+ return status;
+ }
+
+ status = SerialWmiRegisterInstance(Device,
+ &MSSerial_PerformanceInformation_GUID,
+ sizeof(SERIAL_WMI_PERF_DATA),
+ EvtWmiQueryPortPerfData);
+ if (!NT_SUCCESS(status)) {
+ return status;
+ }
+
+ status = SerialWmiRegisterInstance(Device,
+ &MSSerial_CommProperties_GUID,
+ sizeof(SERIAL_COMMPROP) + sizeof(ULONG),
+ EvtWmiQueryPortPropData);
+
+ if (!NT_SUCCESS(status)) {
+ return status;
+ }
+
+ return status;
+}
+
+//
+// WMI Call back functions
+//
+
+NTSTATUS
+EvtWmiQueryPortName(
+ IN WDFWMIINSTANCE WmiInstance,
+ IN ULONG OutBufferSize,
+ IN PVOID OutBuffer,
+ OUT PULONG BufferUsed
+ )
+{
+ WDFDEVICE device;
+ WCHAR pRegName[SYMBOLIC_NAME_LENGTH];
+ UNICODE_STRING string;
+ USHORT nameSize = sizeof(pRegName);
+ NTSTATUS status;
+
+ PAGED_CODE();
+
+ device = WdfWmiInstanceGetDevice(WmiInstance);
+
+ status = SerialReadSymName(device, pRegName, &nameSize);
+ if (!NT_SUCCESS(status)) {
+ return status;
+ }
+
+ RtlInitUnicodeString(&string, pRegName);
+
+ return WDF_WMI_BUFFER_APPEND_STRING(OutBuffer,
+ OutBufferSize,
+ &string,
+ BufferUsed);
+}
+
+NTSTATUS
+EvtWmiQueryPortCommData(
+ IN WDFWMIINSTANCE WmiInstance,
+ IN ULONG OutBufferSize,
+ IN PVOID OutBuffer,
+ OUT PULONG BufferUsed
+ )
+{
+ PSERIAL_DEVICE_EXTENSION pDevExt;
+
+ UNREFERENCED_PARAMETER(OutBufferSize);
+
+ PAGED_CODE();
+
+ pDevExt = SerialGetDeviceExtension (WdfWmiInstanceGetDevice(WmiInstance));
+
+ *BufferUsed = sizeof(SERIAL_WMI_COMM_DATA);
+
+ if (OutBufferSize < *BufferUsed) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ *(PSERIAL_WMI_COMM_DATA)OutBuffer = pDevExt->WmiCommData;
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS
+EvtWmiQueryPortHWData(
+ IN WDFWMIINSTANCE WmiInstance,
+ IN ULONG OutBufferSize,
+ IN PVOID OutBuffer,
+ OUT PULONG BufferUsed
+ )
+{
+ PSERIAL_DEVICE_EXTENSION pDevExt;
+
+ UNREFERENCED_PARAMETER(OutBufferSize);
+
+ PAGED_CODE();
+
+ pDevExt = SerialGetDeviceExtension (WdfWmiInstanceGetDevice(WmiInstance));
+
+ *BufferUsed = sizeof(SERIAL_WMI_HW_DATA);
+
+ if (OutBufferSize < *BufferUsed) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ *(PSERIAL_WMI_HW_DATA)OutBuffer = pDevExt->WmiHwData;
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS
+EvtWmiQueryPortPerfData(
+ IN WDFWMIINSTANCE WmiInstance,
+ IN ULONG OutBufferSize,
+ IN PVOID OutBuffer,
+ OUT PULONG BufferUsed
+ )
+{
+ PSERIAL_DEVICE_EXTENSION pDevExt;
+
+ UNREFERENCED_PARAMETER(OutBufferSize);
+
+ PAGED_CODE();
+
+ pDevExt = SerialGetDeviceExtension (WdfWmiInstanceGetDevice(WmiInstance));
+
+ *BufferUsed = sizeof(SERIAL_WMI_PERF_DATA);
+
+ if (OutBufferSize < *BufferUsed) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ *(PSERIAL_WMI_PERF_DATA)OutBuffer = pDevExt->WmiPerfData;
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS
+EvtWmiQueryPortPropData(
+ IN WDFWMIINSTANCE WmiInstance,
+ IN ULONG OutBufferSize,
+ IN PVOID OutBuffer,
+ OUT PULONG BufferUsed
+ )
+{
+ PSERIAL_DEVICE_EXTENSION pDevExt;
+
+ UNREFERENCED_PARAMETER(OutBufferSize);
+
+ PAGED_CODE();
+
+ pDevExt = SerialGetDeviceExtension (WdfWmiInstanceGetDevice(WmiInstance));
+
+ *BufferUsed = sizeof(SERIAL_COMMPROP) + sizeof(ULONG);
+
+ if (OutBufferSize < *BufferUsed) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ SerialGetProperties(
+ pDevExt,
+ (PSERIAL_COMMPROP)OutBuffer
+ );
+
+ *((PULONG)(((PSERIAL_COMMPROP)OutBuffer)->ProvChar)) = 0;
+
+ return STATUS_SUCCESS;
+}
+
diff --git a/tests/projects/wdk/kmdf/serial/write.c b/tests/projects/wdk/kmdf/serial/write.c
new file mode 100644
index 000000000..c67c062b4
--- /dev/null
+++ b/tests/projects/wdk/kmdf/serial/write.c
@@ -0,0 +1,1195 @@
+/*++
+
+Copyright (c) Microsoft Corporation
+
+Module Name:
+
+ write.c
+
+Abstract:
+
+ This module contains the code that is very specific to write
+ operations in the serial driver
+
+Environment:
+
+ Kernel mode
+
+--*/
+
+#include "precomp.h"
+
+#if defined(EVENT_TRACING)
+#include "write.tmh"
+#endif
+
+EVT_WDF_REQUEST_CANCEL SerialCancelCurrentWrite;
+EVT_WDF_REQUEST_CANCEL SerialCancelCurrentXoff;
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialGiveWriteToIsr;
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialGiveXoffToIsr;
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialGrabWriteFromIsr;
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialGrabXoffFromIsr;
+
+
+VOID
+SerialEvtIoWrite(
+ IN WDFQUEUE Queue,
+ IN WDFREQUEST Request,
+ IN size_t Length
+ )
+
+/*++
+
+Routine Description:
+
+ This is the dispatch routine for write. It validates the parameters
+ for the write request and if all is ok then it places the request
+ on the work queue.
+
+Arguments:
+
+ Queue - Handle to the framework queue object that is associated
+ with the I/O request.
+ Request - Pointer to the WDFREQUEST for the current request
+
+ Length - Length of the IO operation
+ The default property of the queue is to not dispatch
+ zero lenght read & write requests to the driver and
+ complete is with status success. So we will never get
+ a zero length request.
+
+Return Value:
+
+--*/
+
+{
+
+ PSERIAL_DEVICE_EXTENSION extension;
+ NTSTATUS status;
+ WDFDEVICE hDevice;
+ WDF_REQUEST_PARAMETERS params;
+ PREQUEST_CONTEXT reqContext;
+ size_t bufLen;
+
+ hDevice = WdfIoQueueGetDevice(Queue);
+ extension = SerialGetDeviceExtension(hDevice);
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_WRITE,
+ ">SerialEvtIoWrite(%p, 0x%I64x)\n", Request, Length);
+
+ if (SerialCompleteIfError(extension, Request) != STATUS_SUCCESS) {
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_WRITE, "<SerialEvtIoWrite (2) %d\n", STATUS_CANCELLED);
+ return;
+
+ }
+
+
+ WDF_REQUEST_PARAMETERS_INIT(&params);
+
+ WdfRequestGetParameters(
+ Request,
+ &params
+ );
+
+ //
+ // Initialize the scratch area of the request.
+ //
+ reqContext = SerialGetRequestContext(Request);
+ reqContext->MajorFunction = params.Type;
+ reqContext->Length = (ULONG) Length;
+
+ status = WdfRequestRetrieveInputBuffer (Request, Length, &reqContext->SystemBuffer, &bufLen);
+
+ if (!NT_SUCCESS (status)) {
+
+ SerialCompleteRequest(Request , status, 0);
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_WRITE, "<SerialEvtIoWrite (4) %X\n", status);
+ return;
+ }
+
+ SerialStartOrQueue(extension, Request, extension->WriteQueue,
+ &extension->CurrentWriteRequest,
+ SerialStartWrite);
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_WRITE, "<SerialEvtIoWrite (5) %X\n", status);
+
+ return ;
+
+}
+
+VOID
+SerialStartWrite(
+ IN PSERIAL_DEVICE_EXTENSION Extension
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to start off any write. It initializes
+ the Iostatus fields of the request. It will set up any timers
+ that are used to control the write.
+
+Arguments:
+
+ Extension - Points to the serial device extension
+
+Return Value:
+
+--*/
+
+{
+
+ LARGE_INTEGER TotalTime;
+ BOOLEAN UseATimer;
+ SERIAL_TIMEOUTS Timeouts;
+ PREQUEST_CONTEXT reqContext;
+ PREQUEST_CONTEXT reqContextXoff;
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_WRITE,
+ ">SerialStartWrite(%p)\n", Extension);
+
+ TotalTime.QuadPart = 0;
+
+ do {
+
+ reqContext = SerialGetRequestContext(Extension->CurrentWriteRequest);
+
+ //
+ // If there is an xoff counter then complete it.
+ //
+
+ //
+ // We see if there is a actually an Xoff counter request.
+ //
+ // If there is, we put the write request back on the head
+ // of the write list. We then complete the xoff counter.
+ // The xoff counter completing code will actually make the
+ // xoff counter back into the current write request, and
+ // in the course of completing the xoff (which is now
+ // the current write) we will restart this request.
+ //
+
+ if (Extension->CurrentXoffRequest) {
+
+ reqContextXoff =
+ SerialGetRequestContext(Extension->CurrentXoffRequest);
+
+ if (SERIAL_REFERENCE_COUNT(reqContextXoff)) {
+
+ //
+ // The reference count is non-zero. This implies that
+ // the xoff request has not made it through the completion
+ // path yet. We will increment the reference count
+ // and attempt to complete it ourseleves.
+ //
+
+ SERIAL_SET_REFERENCE(
+ reqContextXoff,
+ SERIAL_REF_XOFF_REF
+ );
+
+ reqContextXoff->Information = 0;
+
+ //
+ // The following call will actually release the
+ // cancel spin lock.
+ //
+
+ SerialTryToCompleteCurrent(
+ Extension,
+ SerialGrabXoffFromIsr,
+ STATUS_SERIAL_MORE_WRITES,
+ &Extension->CurrentXoffRequest,
+ NULL,
+ NULL,
+ Extension->XoffCountTimer,
+ NULL,
+ NULL,
+ SERIAL_REF_XOFF_REF
+ );
+
+ } else {
+
+ //
+ // The request is well on its way to being finished.
+ // We can let the regular completion code do the
+ // work. Just release the spin lock.
+ //
+
+ }
+
+ }
+
+ UseATimer = FALSE;
+
+ //
+ // 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.
+ //
+ // Take care, we might have an xoff counter masquerading
+ // as a write.
+ //
+
+ TotalTime.QuadPart =
+ ((LONGLONG)((UInt32x32To64(
+ (reqContext->MajorFunction == IRP_MJ_WRITE)?
+ (reqContext->Length) : (1),
+ Timeouts.WriteTotalTimeoutMultiplier
+ )
+ + Timeouts.WriteTotalTimeoutConstant)))
+ * -10000;
+
+ }
+
+ //
+ // The request may be going to the isr shortly. Now
+ // is a good time to initialize its reference counts.
+ //
+
+ 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->CurrentWriteRequest,
+ SerialCancelCurrentWrite);
+
+ if (UseATimer) {
+ BOOLEAN result;
+
+ result = SerialSetTimer(
+ Extension->WriteRequestTotalTimer,
+ TotalTime
+ );
+ if(result == FALSE) {
+ //
+ // This timer now has a reference to the request.
+ //
+
+ SERIAL_SET_REFERENCE( reqContext, SERIAL_REF_TOTAL_TIMER );
+ }
+ }
+
+ WdfInterruptSynchronize(
+ Extension->WdfInterrupt,
+ SerialGiveWriteToIsr,
+ Extension
+ );
+
+ } WHILE (FALSE);
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_WRITE, "<SerialStartWrite \n");
+
+ return;
+}
+
+
+VOID
+SerialGetNextWrite(
+ IN WDFREQUEST *CurrentOpRequest,
+ IN WDFQUEUE QueueToProcess,
+ IN WDFREQUEST *NewRequest,
+ IN BOOLEAN CompleteCurrent,
+ PSERIAL_DEVICE_EXTENSION Extension
+ )
+
+/*++
+
+Routine Description:
+
+ This routine completes the old write as well as getting
+ a pointer to the next write.
+
+ The reason that we have have pointers to the current write
+ queue as well as the current write request is so that this
+ routine may be used in the common completion code for
+ read and write.
+
+Arguments:
+
+ CurrentOpRequest - Pointer to the pointer that points to the
+ current write request.
+
+ QueueToProcess - Pointer to the write queue.
+
+ NewRequest - A pointer to a pointer to the request that will be the
+ current request. Note that this could end up pointing
+ to a null pointer. This does NOT necessaryly mean
+ that there is no current write. What could occur
+ is that while the cancel lock is held the write
+ queue ended up being empty, but as soon as we release
+ the cancel spin lock a new request came in from
+ SerialStartWrite.
+
+ CompleteCurrent - Flag indicates whether the CurrentOpRequest should
+ be completed.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PREQUEST_CONTEXT reqContext;
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_WRITE, ">SerialGetNextWrite\n");
+
+
+ do {
+
+ reqContext = SerialGetRequestContext(*CurrentOpRequest);
+
+ //
+ // We could be completing a flush.
+ //
+
+ if (reqContext->MajorFunction == IRP_MJ_WRITE) {
+
+ ASSERT(Extension->TotalCharsQueued >= reqContext->Length);
+
+ Extension->TotalCharsQueued -= reqContext->Length;
+
+ } else if (reqContext->MajorFunction == IRP_MJ_DEVICE_CONTROL) {
+
+ WDFREQUEST request = *CurrentOpRequest;
+ PSERIAL_XOFF_COUNTER Xc;
+
+ Xc = reqContext->SystemBuffer;
+
+ //
+ // We should never have a xoff counter when we
+ // get to this point.
+ //
+
+ ASSERT(!Extension->CurrentXoffRequest);
+
+ //
+ // This could only be a xoff counter masquerading as
+ // a write request.
+ //
+
+ Extension->TotalCharsQueued--;
+
+ //
+ // Check to see of the xoff request has been set with success.
+ // This means that the write completed normally. If that
+ // is the case, and it hasn't been set to cancel in the
+ // meanwhile, then go on and make it the CurrentXoffRequest.
+ //
+
+ if (reqContext->Status != STATUS_SUCCESS || reqContext->Cancelled) {
+
+ // TODO: I see Xoff request getting abandoned due to loss of
+ // Total timer - SERIAL_REF_TOTAL_TIMER
+ //
+ // Oh well, we can just finish it off.
+ //
+ NOTHING;
+
+ } else {
+
+ SerialSetCancelRoutine(request, SerialCancelCurrentXoff);
+
+ //
+ // We don't want to complete the current request now. This
+ // will now get completed by the Xoff counter code.
+ //
+
+ CompleteCurrent = FALSE;
+
+ //
+ // Give the counter to the isr.
+ //
+
+ Extension->CurrentXoffRequest = request;
+ WdfInterruptSynchronize(
+ Extension->WdfInterrupt,
+ SerialGiveXoffToIsr,
+ Extension
+ );
+
+ //
+ // Start the timer for the counter and increment
+ // the reference count since the timer has a
+ // reference to the request.
+ //
+
+ if (Xc->Timeout) {
+
+ LARGE_INTEGER delta;
+ BOOLEAN result;
+
+ delta.QuadPart = -((LONGLONG)UInt32x32To64(
+ 1000,
+ Xc->Timeout
+ ));
+
+ result = SerialSetTimer(
+ Extension->XoffCountTimer,
+ delta
+
+ );
+ if(result == FALSE) {
+ SERIAL_SET_REFERENCE(
+ reqContext,
+ SERIAL_REF_TOTAL_TIMER
+ );
+ }
+ }
+
+ }
+
+
+ }
+
+ //
+ // Note that the following call will (probably) also cause
+ // the current request to be completed.
+ //
+
+ SerialGetNextRequest(
+ CurrentOpRequest,
+ QueueToProcess,
+ NewRequest,
+ CompleteCurrent,
+ Extension
+ );
+
+ if (!*NewRequest) {
+
+
+ WdfInterruptSynchronize(
+ Extension->WdfInterrupt,
+ SerialProcessEmptyTransmit,
+ Extension
+ );
+
+ break;
+
+ } else if (SerialGetRequestContext(*NewRequest)->MajorFunction
+ == IRP_MJ_FLUSH_BUFFERS) {
+
+ //
+ // If we encounter a flush request we just want to get
+ // the next request and complete the flush.
+ //
+ // Note that if NewRequest is non-null then it is also
+ // equal to CurrentWriteRequest.
+ //
+
+
+ ASSERT((*NewRequest) == (*CurrentOpRequest));
+ SerialGetRequestContext(*NewRequest)->Status = STATUS_SUCCESS;
+
+ } else {
+
+ break;
+
+ }
+
+ } WHILE (TRUE);
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_WRITE, "<SerialGetNextWrite\n");
+
+}
+
+
+VOID
+SerialCompleteWrite(
+ IN WDFDPC Dpc
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is merely used to complete any write. It
+ assumes that the status and the information fields of
+ the request are already correctly filled in.
+
+Arguments:
+
+ Dpc - Not Used.
+
+ DeferredContext - Really points to the device extension.
+
+ SystemContext1 - Not Used.
+
+ SystemContext2 - Not Used.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ PSERIAL_DEVICE_EXTENSION Extension = NULL;
+
+ Extension = SerialGetDeviceExtension(WdfDpcGetParentObject(Dpc));
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_WRITE, ">SerialCompleteWrite(%p)\n",
+ Extension);
+
+
+ SerialTryToCompleteCurrent(Extension, NULL, STATUS_SUCCESS,
+ &Extension->CurrentWriteRequest,
+ Extension->WriteQueue, NULL,
+ Extension->WriteRequestTotalTimer,
+ SerialStartWrite, SerialGetNextWrite,
+ SERIAL_REF_ISR);
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_WRITE, "<SerialCompleteWrite\n");
+
+}
+
+
+BOOLEAN
+SerialProcessEmptyTransmit(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to determine if conditions are appropriate
+ to satisfy a wait for transmit empty event, and if so to complete
+ the request that is waiting for that event. It also call the code
+ that checks to see if we should lower the RTS line if we are
+ doing transmit toggling.
+
+ NOTE: This routine is called by WdfInterruptSynchronize.
+
+ NOTE: This routine assumes that it is called with the cancel
+ spinlock held.
+
+Arguments:
+
+ Context - Really a pointer to the device extension.
+
+Return Value:
+
+ This routine always returns FALSE.
+
+--*/
+
+{
+
+ PSERIAL_DEVICE_EXTENSION Extension = Context;
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ if (Extension->IsrWaitMask && (Extension->IsrWaitMask & SERIAL_EV_TXEMPTY) &&
+ Extension->EmptiedTransmit && (!Extension->TransmitImmediate) &&
+ (!Extension->CurrentWriteRequest) && IsQueueEmpty(Extension->WriteQueue)) {
+
+ Extension->HistoryMask |= SERIAL_EV_TXEMPTY;
+ if (Extension->IrpMaskLocation) {
+
+ *Extension->IrpMaskLocation = Extension->HistoryMask;
+ Extension->IrpMaskLocation = NULL;
+ Extension->HistoryMask = 0;
+
+ SerialGetRequestContext(Extension->CurrentWaitRequest)->Information = sizeof(ULONG);
+ SerialInsertQueueDpc(
+ Extension->CommWaitDpc
+ );
+
+ }
+
+ Extension->CountOfTryingToLowerRTS++;
+ SerialPerhapsLowerRTS(Extension->WdfInterrupt, Extension);
+
+ }
+
+ return FALSE;
+
+}
+
+
+BOOLEAN
+SerialGiveWriteToIsr(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ Try to start off the write by slipping it in behind
+ a transmit immediate char, or if that isn't available
+ and the transmit holding register is empty, "tickle"
+ the UART into interrupting with a transmit buffer
+ empty.
+
+ NOTE: This routine is called by WdfInterruptSynchronize.
+
+ NOTE: This routine assumes that it is called with the
+ cancel spin lock held.
+
+Arguments:
+
+ Context - Really a pointer to the device extension.
+
+Return Value:
+
+ This routine always returns FALSE.
+
+--*/
+
+{
+
+ PSERIAL_DEVICE_EXTENSION Extension = Context;
+
+ //
+ // The current stack location. This contains all of the
+ // information we need to process this particular request.
+ //
+ PREQUEST_CONTEXT reqContext;
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ reqContext = SerialGetRequestContext(Extension->CurrentWriteRequest);
+
+ //
+ // We might have a xoff counter request masquerading as a
+ // write. The length of these requests will always be one
+ // and we can get a pointer to the actual character from
+ // the data supplied by the user.
+ //
+
+ if (reqContext->MajorFunction == IRP_MJ_WRITE) {
+
+ Extension->WriteLength = reqContext->Length;
+ Extension->WriteCurrentChar = reqContext->SystemBuffer;
+
+ } else {
+
+ Extension->WriteLength = 1;
+ Extension->WriteCurrentChar =
+ ((PUCHAR)reqContext->SystemBuffer) +
+ FIELD_OFFSET(
+ SERIAL_XOFF_COUNTER,
+ XoffChar
+ );
+
+ }
+
+ //
+ // The isr now has a reference to the request.
+ //
+
+ SERIAL_SET_REFERENCE(
+ reqContext,
+ SERIAL_REF_ISR
+ );
+
+ //
+ // Check first to see if an immediate char is transmitting.
+ // If it is then we'll just slip in behind it when its
+ // done.
+ //
+
+ if (!Extension->TransmitImmediate) {
+
+ //
+ // If there is no immediate char transmitting then we
+ // will "re-enable" the transmit holding register empty
+ // interrupt. The 8250 family of devices will always
+ // signal a transmit holding register empty interrupt
+ // *ANY* time this bit is set to one. By doing things
+ // this way we can simply use the normal interrupt code
+ // to start off this write.
+ //
+ // We've been keeping track of whether the transmit holding
+ // register is empty so it we only need to do this
+ // if the register is empty.
+ //
+
+ if (Extension->HoldingEmpty) {
+
+ DISABLE_ALL_INTERRUPTS(Extension, Extension->Controller);
+ ENABLE_ALL_INTERRUPTS(Extension, Extension->Controller);
+
+ }
+
+ }
+
+ //
+ // The rts line may already be up from previous writes,
+ // however, it won't take much additional time to turn
+ // on the RTS line if we are doing transmit toggling.
+ //
+
+ if ((Extension->HandFlow.FlowReplace & SERIAL_RTS_MASK) ==
+ SERIAL_TRANSMIT_TOGGLE) {
+
+ SerialSetRTS(Extension->WdfInterrupt, Extension);
+
+ }
+
+ return FALSE;
+
+}
+
+
+VOID
+SerialCancelCurrentWrite(
+ IN WDFREQUEST Request
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to cancel the current write.
+
+Arguments:
+
+ Device - Wdf handle for the device
+
+ Request - Pointer to the WDFREQUEST to be canceled.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ PSERIAL_DEVICE_EXTENSION Extension;
+ WDFDEVICE device = WdfIoQueueGetDevice(WdfRequestGetIoQueue(Request));
+
+ UNREFERENCED_PARAMETER(Request);
+
+ Extension = SerialGetDeviceExtension(device);
+
+ SerialTryToCompleteCurrent(
+ Extension,
+ SerialGrabWriteFromIsr,
+ STATUS_CANCELLED,
+ &Extension->CurrentWriteRequest,
+ Extension->WriteQueue,
+ NULL,
+ Extension->WriteRequestTotalTimer,
+ SerialStartWrite,
+ SerialGetNextWrite,
+ SERIAL_REF_CANCEL
+ );
+
+}
+
+
+VOID
+SerialWriteTimeout(
+ IN WDFTIMER Timer
+ )
+
+/*++
+
+Routine Description:
+
+ This routine will try to timeout the current write.
+
+Arguments:
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ PSERIAL_DEVICE_EXTENSION Extension = NULL;
+
+ Extension = SerialGetDeviceExtension(WdfTimerGetParentObject(Timer));
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_WRITE, ">SerialWriteTimeout(%p)\n",
+ Extension);
+
+ SerialTryToCompleteCurrent(Extension, SerialGrabWriteFromIsr,
+ STATUS_TIMEOUT, &Extension->CurrentWriteRequest,
+ Extension->WriteQueue, NULL,
+ Extension->WriteRequestTotalTimer,
+ SerialStartWrite, SerialGetNextWrite,
+ SERIAL_REF_TOTAL_TIMER);
+
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_WRITE, "<SerialWriteTimeout\n");
+}
+
+
+BOOLEAN
+SerialGrabWriteFromIsr(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+
+ This routine is used to grab the current request, which could be timing
+ out or canceling, from the ISR
+
+ NOTE: This routine is being called from WdfInterruptSynchronize.
+
+ NOTE: This routine assumes that the cancel spin lock is held
+ when this routine is called.
+
+Arguments:
+
+ Context - Really a pointer to the device extension.
+
+Return Value:
+
+ Always false.
+
+--*/
+
+{
+
+ PSERIAL_DEVICE_EXTENSION Extension = Context;
+
+ PREQUEST_CONTEXT reqContext;
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ reqContext = SerialGetRequestContext(Extension->CurrentWriteRequest);
+
+ //
+ // Check if the write length is non-zero. If it is non-zero
+ // then the ISR still owns the request. We calculate the the number
+ // of characters written and update the information field of the
+ // request with the characters written. We then clear the write length
+ // the isr sees.
+ //
+
+ if (Extension->WriteLength) {
+
+ //
+ // We could have an xoff counter masquerading as a
+ // write request. If so, don't update the write length.
+ //
+
+ if (reqContext->MajorFunction == IRP_MJ_WRITE) {
+
+ reqContext->Information = reqContext->Length -Extension->WriteLength;
+
+ } else {
+
+ reqContext->Information = 0;
+
+ }
+
+ //
+ // Since the isr no longer references this request, we can
+ // decrement it's reference count.
+ //
+
+ SERIAL_CLEAR_REFERENCE(
+ reqContext,
+ SERIAL_REF_ISR
+ );
+
+ Extension->WriteLength = 0;
+
+ }
+
+ return FALSE;
+
+}
+
+
+BOOLEAN
+SerialGrabXoffFromIsr(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to grab an xoff counter request from the
+ isr when it is no longer masquerading as a write request. This
+ routine is called by the cancel and timeout code for the
+ xoff counter ioctl.
+
+
+ NOTE: This routine is being called from WdfInterruptSynchronize.
+
+ NOTE: This routine assumes that the cancel spin lock is held
+ when this routine is called.
+
+Arguments:
+
+ Context - Really a pointer to the device extension.
+
+Return Value:
+
+ Always false.
+
+--*/
+
+{
+
+ PSERIAL_DEVICE_EXTENSION Extension = Context;
+
+ PREQUEST_CONTEXT reqContext;
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ reqContext = SerialGetRequestContext(Extension->CurrentXoffRequest);
+
+ if (Extension->CountSinceXoff) {
+
+ //
+ // This is only non-zero when there actually is a Xoff ioctl
+ // counting down.
+ //
+
+ Extension->CountSinceXoff = 0;
+
+ //
+ // We decrement the count since the isr no longer owns
+ // the request.
+ //
+
+ SERIAL_CLEAR_REFERENCE(
+ reqContext,
+ SERIAL_REF_ISR
+ );
+
+ }
+
+ return FALSE;
+
+}
+
+
+VOID
+SerialCompleteXoff(
+ IN WDFDPC Dpc
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is merely used to truely complete an xoff counter request. It
+ assumes that the status and the information fields of the request are
+ already correctly filled in.
+
+Arguments:
+
+ Dpc - Not Used.
+
+ DeferredContext - Really points to the device extension.
+
+ SystemContext1 - Not Used.
+
+ SystemContext2 - Not Used.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ PSERIAL_DEVICE_EXTENSION Extension = NULL;
+
+ Extension = SerialGetDeviceExtension(WdfDpcGetParentObject(Dpc));
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_WRITE, ">SerialCompleteXoff(%p)\n",
+ Extension);
+
+
+ SerialTryToCompleteCurrent(Extension, NULL, STATUS_SUCCESS,
+ &Extension->CurrentXoffRequest, NULL, NULL,
+ Extension->XoffCountTimer, NULL, NULL,
+ SERIAL_REF_ISR);
+
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_WRITE, "<SerialCompleteXoff\n");
+
+}
+
+
+VOID
+SerialTimeoutXoff(
+ IN WDFTIMER Timer
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is merely used to truely complete an xoff counter request,
+ if its timer has run out.
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ PSERIAL_DEVICE_EXTENSION Extension = NULL;
+
+ Extension = SerialGetDeviceExtension(WdfTimerGetParentObject(Timer));
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_WRITE, ">SerialTimeoutXoff(%p)\n", Extension);
+
+ SerialTryToCompleteCurrent(Extension, SerialGrabXoffFromIsr,
+ STATUS_SERIAL_COUNTER_TIMEOUT,
+ &Extension->CurrentXoffRequest, NULL, NULL, NULL,
+ NULL, NULL, SERIAL_REF_TOTAL_TIMER);
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_WRITE, "<SerialTimeoutXoff\n");
+}
+
+
+VOID
+SerialCancelCurrentXoff(
+ IN WDFREQUEST Request
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to cancel the current write.
+
+Arguments:
+
+ Device - Wdf handle for the device
+
+ Request - Pointer to the WDFREQUEST to be canceled.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ PSERIAL_DEVICE_EXTENSION Extension;
+ WDFDEVICE device = WdfIoQueueGetDevice(WdfRequestGetIoQueue(Request));
+
+ UNREFERENCED_PARAMETER(Request);
+
+ Extension = SerialGetDeviceExtension(device);
+
+ SerialTryToCompleteCurrent(
+ Extension,
+ SerialGrabXoffFromIsr,
+ STATUS_CANCELLED,
+ &Extension->CurrentXoffRequest,
+ NULL,
+ NULL,
+ Extension->XoffCountTimer,
+ NULL,
+ NULL,
+ SERIAL_REF_CANCEL
+ );
+
+}
+
+
+BOOLEAN
+SerialGiveXoffToIsr(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+
+ This routine starts off the xoff counter. It merely
+ has to set the xoff count and increment the reference
+ count to denote that the isr has a reference to the request.
+
+ NOTE: This routine is called by WdfInterruptSynchronize.
+
+ NOTE: This routine assumes that it is called with the
+ cancel spin lock held.
+
+Arguments:
+
+ Context - Really a pointer to the device extension.
+
+Return Value:
+
+ This routine always returns FALSE.
+
+--*/
+
+{
+
+ PSERIAL_DEVICE_EXTENSION Extension = Context;
+ PREQUEST_CONTEXT reqContext;
+ PSERIAL_XOFF_COUNTER Xc = NULL;
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ reqContext = SerialGetRequestContext(Extension->CurrentXoffRequest);
+ Xc = reqContext->SystemBuffer;
+
+ //
+ // The current stack location. This contains all of the
+ // information we need to process this particular request.
+ //
+
+ ASSERT(Extension->CurrentXoffRequest);
+ Extension->CountSinceXoff = Xc->Counter;
+
+ //
+ // The isr now has a reference to the request.
+ //
+
+ SERIAL_SET_REFERENCE(
+ reqContext,
+ SERIAL_REF_ISR
+ );
+
+ return FALSE;
+
+}
+
+
diff --git a/tests/projects/wdk/kmdf/serial/xmake.lua b/tests/projects/wdk/kmdf/serial/xmake.lua
new file mode 100644
index 000000000..49bf2215e
--- /dev/null
+++ b/tests/projects/wdk/kmdf/serial/xmake.lua
@@ -0,0 +1,23 @@
+
+-- add modes: debug and release
+add_rules("mode.debug", "mode.release")
+
+-- add include directories
+add_includedirs(".")
+
+-- add target
+target("serial")
+
+ -- add rules
+ add_rules("wdk.kmdf.driver")
+
+ -- add flags for rule: wdk.tracewpp
+ add_values("wdk.tracewpp.flags", "-func:SerialDbgPrintEx(LEVEL,FLAGS,MSG,...)")
+
+ -- add header file name for rule: wdk.mc
+ add_values("wdk.mc.header", "serlog.h")
+
+ -- add files
+ add_files("*.c", {rule = "wdk.tracewpp"})
+ add_files("*.mc", "*.rc", "*.inx")
+
diff --git a/xmake/rules/wdk/inf/xmake.lua b/xmake/rules/wdk/inf/xmake.lua
index 3cec2660b..d612e51ea 100644
--- a/xmake/rules/wdk/inf/xmake.lua
+++ b/xmake/rules/wdk/inf/xmake.lua
@@ -22,14 +22,14 @@
-- @file xmake.lua
--
--- define rule: *.inf
-rule("wdk.inf")
+-- define rule: *.mc
+rule("wdk.mc")
-- add rule: wdk environment
add_deps("wdk.env")
-- set extensions
- set_extensions(".inf", ".inx")
+ set_extensions(".mc")
-- on load
on_load(function (target)
@@ -40,50 +40,81 @@ rule("wdk.inf")
-- get arch
local arch = assert(config.arch(), "arch not found!")
- -- get stampinf
- local stampinf = path.join(target:data("wdk").bindir, arch, is_host("windows") and "stampinf.exe" or "stampinf")
- assert(stampinf and os.isexec(stampinf), "stampinf not found!")
+ -- get mc
+ local mc = path.join(target:data("wdk").bindir, arch, is_host("windows") and "mc.exe" or "mc")
+ assert(mc and os.isexec(mc), "mc not found!")
- -- save uic
- target:data_set("wdk.stampinf", stampinf)
+ -- save mc
+ target:data_set("wdk.mc", mc)
+
+ -- save output directory
+ target:data_set("wdk.mc.outputdir", path.join(config.buildir(), ".wdk", "mc", config.get("mode") or "generic", config.get("arch") or os.arch(), target:name()))
end)
- -- on build file
- on_build_file(function (target, sourcefile, opt)
+ -- before build file
+ before_build_file(function (target, sourcefile, opt)
-- imports
import("core.base.option")
import("core.project.depend")
- -- the target file
- local targetfile = path.join(target:targetdir(), path.basename(sourcefile) .. ".inf")
+ -- get mc
+ local mc = target:data("wdk.mc")
+
+ -- get output directory
+ local outputdir = target:data("wdk.mc.outputdir")
+
+ -- init args
+ local args = {}
+ local flags = target:values("wdk.mc.flags")
+ if flags then
+ table.join2(args, flags)
+ end
+ table.insert(args, "-h")
+ table.insert(args, outputdir)
+ table.insert(args, "-r")
+ table.insert(args, outputdir)
+
+ -- add header file
+ local header = target:values("wdk.mc.header")
+ local headerfile = header and path.join(outputdir, header) or nil
+ if headerfile then
+ table.insert(args, "-z")
+ table.insert(args, path.basename(headerfile))
+ target:data_add("wdk.cleanfiles", headerfile)
+ else
+ headerfile = path.join(outputdir, path.basename(sourcefile) .. ".h")
+ end
+
+ -- add source file
+ table.insert(args, sourcefile)
- -- add clean files
- target:data_add("wdk.cleanfiles", targetfile)
+ -- add includedirs
+ target:add("includedirs", outputdir)
-- need build this object?
- local dependfile = target:dependfile(targetfile)
+ local dependfile = target:dependfile(headerfile)
local dependinfo = option.get("rebuild") and {} or (depend.load(dependfile) or {})
- if not depend.is_changed(dependinfo, {lastmtime = os.mtime(targetfile)}) then
+ if not depend.is_changed(dependinfo, {lastmtime = os.mtime(headerfile), values = args}) then
return
end
-- trace progress info
if option.get("verbose") then
- cprint("${green}[%02d%%]:${dim} compiling.wdk.inf %s", opt.progress, sourcefile)
+ cprint("${green}[%02d%%]:${dim} compiling.wdk.mc %s", opt.progress, sourcefile)
else
- cprint("${green}[%02d%%]:${clear} compiling.wdk.inf %s", opt.progress, sourcefile)
+ cprint("${green}[%02d%%]:${clear} compiling.wdk.mc %s", opt.progress, sourcefile)
end
- -- get stampinf
- local stampinf = target:data("wdk.stampinf")
-
- -- update the timestamp
- os.cp(sourcefile, targetfile)
- os.vrunv(stampinf, {"-d", "*", "-a", is_arch("x64") and "arm64" or "x86", "-v", "*", "-f", targetfile}, {wildcards = false})
+ -- do message compile
+ if not os.isdir(outputdir) then
+ os.mkdir(outputdir)
+ end
+ os.vrunv(mc, args)
-- update files and values to the dependent file
- dependinfo.files = {sourcefile}
+ dependinfo.files = {sourcefile}
+ dependinfo.values = args
depend.save(dependinfo, dependfile)
end)
diff --git a/xmake/rules/wdk/mc/xmake.lua b/xmake/rules/wdk/mc/xmake.lua
new file mode 100644
index 000000000..3765bf151
--- /dev/null
+++ b/xmake/rules/wdk/mc/xmake.lua
@@ -0,0 +1,120 @@
+--!A cross-platform build utility based on Lua
+--
+-- Licensed to the Apache Software Foundation (ASF) under one
+-- or more contributor license agreements. See the NOTICE file
+-- distributed with this work for additional information
+-- regarding copyright ownership. The ASF licenses this file
+-- to you under the Apache License, Version 2.0 (the
+-- "License"); you may not use this file except in compliance
+-- with the License. You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+-- Copyright (C) 2015 - 2018, TBOOX Open Source Group.
+--
+-- @author ruki
+-- @file xmake.lua
+--
+
+-- define rule: *.mc
+rule("wdk.mc")
+
+ -- add rule: wdk environment
+ add_deps("wdk.env")
+
+ -- set extensions
+ set_extensions(".mc")
+
+ -- on load
+ on_load(function (target)
+
+ -- imports
+ import("core.project.config")
+
+ -- get arch
+ local arch = assert(config.arch(), "arch not found!")
+
+ -- get mc
+ local mc = path.join(target:data("wdk").bindir, arch, is_host("windows") and "mc.exe" or "mc")
+ assert(mc and os.isexec(mc), "mc not found!")
+
+ -- save mc
+ target:data_set("wdk.mc", mc)
+
+ -- save output directory
+ target:data_set("wdk.mc.outputdir", path.join(config.buildir(), ".wdk", "mc", config.get("mode") or "generic", config.get("arch") or os.arch(), target:name()))
+ end)
+
+ -- before build file
+ before_build_file(function (target, sourcefile, opt)
+
+ -- imports
+ import("core.base.option")
+ import("core.project.depend")
+
+ -- get mc
+ local mc = target:data("wdk.mc")
+
+ -- get output directory
+ local outputdir = target:data("wdk.mc.outputdir")
+
+ -- init args
+ local args = {}
+ local flags = target:values("wdk.mc.flags")
+ if flags then
+ table.join2(args, flags)
+ end
+ table.insert(args, "-h")
+ table.insert(args, outputdir)
+ table.insert(args, "-r")
+ table.insert(args, outputdir)
+ table.insert(args, sourcefile)
+
+ -- add includedirs
+ target:add("includedirs", outputdir)
+
+ -- add header file
+ local header = target:values("wdk.mc.header")
+ local headerfile = header and path.join(outputdir, header) or nil
+ if headerfile then
+ table.insert(args, "-z")
+ table.insert(args, path.basename(headerfile))
+ table.insert(args, "-e")
+ table.insert(args, path.extension(headerfile))
+ target:data_add("wdk.cleanfiles", headerfile)
+ else
+ headerfile = path.join(outputdir, path.basename(sourcefile) .. ".h")
+ end
+
+ -- need build this object?
+ local dependfile = target:dependfile(headerfile)
+ local dependinfo = option.get("rebuild") and {} or (depend.load(dependfile) or {})
+ if not depend.is_changed(dependinfo, {lastmtime = os.mtime(headerfile), values = args}) then
+ return
+ end
+
+ -- trace progress info
+ if option.get("verbose") then
+ cprint("${green}[%02d%%]:${dim} compiling.wdk.mc %s", opt.progress, sourcefile)
+ else
+ cprint("${green}[%02d%%]:${clear} compiling.wdk.mc %s", opt.progress, sourcefile)
+ end
+
+ -- do message compile
+ if not os.isdir(outputdir) then
+ os.mkdir(outputdir)
+ end
+ os.vrunv(mc, args)
+
+ -- update files and values to the dependent file
+ dependinfo.files = {sourcefile}
+ dependinfo.values = args
+ depend.save(dependinfo, dependfile)
+ end)
+
diff --git a/xmake/rules/wdk/xmake.lua b/xmake/rules/wdk/xmake.lua
index 402cd1c64..931803f44 100644
--- a/xmake/rules/wdk/xmake.lua
+++ b/xmake/rules/wdk/xmake.lua
@@ -26,7 +26,7 @@
rule("wdk.umdf.driver")
-- add rules
- add_deps("wdk.inf", "wdk.man")
+ add_deps("wdk.inf", "wdk.man", "wdk.mc")
-- on load
on_load(function (target)
@@ -37,7 +37,7 @@ rule("wdk.umdf.driver")
rule("wdk.umdf.binary")
-- add rules
- add_deps("wdk.inf", "wdk.man")
+ add_deps("wdk.inf", "wdk.man", "wdk.mc")
-- on load
on_load(function (target)
@@ -48,7 +48,7 @@ rule("wdk.umdf.binary")
rule("wdk.kmdf.driver")
-- add rules
- add_deps("wdk.inf", "wdk.man")
+ add_deps("wdk.inf", "wdk.man", "wdk.mc")
-- on load
on_load(function (target)
@@ -59,7 +59,7 @@ rule("wdk.kmdf.driver")
rule("wdk.kmdf.binary")
-- add rules
- add_deps("wdk.inf", "wdk.man")
+ add_deps("wdk.inf", "wdk.man", "wdk.mc")
-- on load
on_load(function (target)