summaryrefslogtreecommitdiff
path: root/serial/VirtualSerial/queue.cpp
diff options
context:
space:
mode:
authorDave Wilson <[email protected]>2015-03-17 19:50:07 -0700
committerDave Wilson <[email protected]>2015-03-17 19:50:07 -0700
commit97cf5197cf5b882b2c689d8dc2b555f2edf8f418 (patch)
tree46f3701832d70b420eb0fc0eb93261f9da45db3f /serial/VirtualSerial/queue.cpp
parentef1905bf1e8825bb31120dfb27e0daf3154d859a (diff)
Initial publish
Diffstat (limited to 'serial/VirtualSerial/queue.cpp')
-rw-r--r--serial/VirtualSerial/queue.cpp1188
1 files changed, 1188 insertions, 0 deletions
diff --git a/serial/VirtualSerial/queue.cpp b/serial/VirtualSerial/queue.cpp
new file mode 100644
index 00000000..5a7b5bad
--- /dev/null
+++ b/serial/VirtualSerial/queue.cpp
@@ -0,0 +1,1188 @@
+/*++
+
+Copyright (c) Microsoft Corporation, All Rights Reserved
+
+Module Name:
+
+ queue.cpp
+
+Abstract:
+
+ This file implements the I/O queue interface and performs
+ the read/write/ioctl operations.
+
+Environment:
+
+ Windows User-Mode Driver Framework (WUDF)
+
+--*/
+
+
+#include "internal.h"
+
+//
+// IUnknown implementation
+//
+
+//
+// Queue destructor.
+// Free up the buffer, wait for thread to terminate and
+// delete critical section.
+//
+
+
+CMyQueue::~CMyQueue(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+
+ IUnknown implementation of Release
+
+Arguments:
+
+
+Return Value:
+
+--*/
+{
+ WUDF_TEST_DRIVER_ASSERT(m_Device);
+
+ m_Device->Release();
+}
+
+
+//
+// Initialize
+HRESULT
+CMyQueue::CreateInstance(
+ _In_ CMyDevice *pDevice,
+ _In_ IWDFDevice *FxDevice,
+ _Out_ PCMyQueue *Queue
+ )
+/*++
+
+Routine Description:
+
+
+ CreateInstance creates an instance of the queue object.
+
+Arguments:
+
+ ppUkwn - OUT parameter is an IUnknown interface to the queue object
+
+Return Value:
+
+ HRESULT indicating success or failure
+
+--*/
+{
+ CMyQueue *pMyQueue = new CMyQueue(pDevice);
+ HRESULT hr;
+
+ if (pMyQueue == NULL) {
+ return E_OUTOFMEMORY;
+ }
+
+ hr = pMyQueue->Initialize(FxDevice);
+
+ if (SUCCEEDED(hr))
+ {
+ *Queue = pMyQueue;
+ }
+ else
+ {
+ pMyQueue->Release();
+ }
+ return hr;
+}
+
+HRESULT
+CMyQueue::Initialize(
+ _In_ IWDFDevice *FxDevice
+ )
+{
+ IWDFIoQueue *fxQueue;
+ IUnknown *unknown = NULL;
+ HRESULT hr;
+
+ //
+ // Initialize ring buffer
+ //
+
+ hr = m_RingBuffer.Initialize(DATA_BUFFER_SIZE);
+
+ if (FAILED(hr))
+ {
+ goto Exit;
+ }
+
+ unknown = QueryIUnknown();
+
+ //
+ // Create the default queue
+ //
+
+ {
+ hr = FxDevice->CreateIoQueue(unknown,
+ TRUE,
+ WdfIoQueueDispatchParallel,
+ TRUE,
+ FALSE,
+ &fxQueue);
+ }
+
+ if (FAILED(hr))
+ {
+ goto Exit;
+ }
+
+ m_FxQueue = fxQueue;
+
+ fxQueue->Release();
+
+ //
+ // Create a manual queue to hold pending read requests. By keeping
+ // them in the queue, framework takes care of cancelling them if the app
+ // exits
+ //
+
+ {
+ hr = FxDevice->CreateIoQueue(NULL,
+ FALSE,
+ WdfIoQueueDispatchManual,
+ TRUE,
+ FALSE,
+ &fxQueue);
+ }
+
+ if (FAILED(hr))
+ {
+ goto Exit;
+ }
+
+ m_FxReadQueue = fxQueue;
+
+ fxQueue->Release();
+
+ //
+ // Create a manual queue to hold pending ioctl wait-on-mask requests.
+ //
+
+ {
+ hr = FxDevice->CreateIoQueue(NULL,
+ FALSE,
+ WdfIoQueueDispatchManual,
+ TRUE,
+ FALSE,
+ &fxQueue);
+ }
+
+ if (FAILED(hr))
+ {
+ goto Exit;
+ }
+
+ m_FxWaitMaskQueue = fxQueue;
+
+ fxQueue->Release();
+
+Exit:
+ SAFE_RELEASE(unknown);
+ return hr;
+}
+
+HRESULT
+STDMETHODCALLTYPE
+CMyQueue::QueryInterface(
+ _In_ REFIID InterfaceId,
+ _Out_ PVOID *Object
+ )
+/*++
+
+Routine Description:
+
+
+ Query Interface
+
+Arguments:
+
+ Follows COM specifications
+
+Return Value:
+
+ HRESULT indicating success or failure
+
+--*/
+{
+ HRESULT hr;
+
+
+ if (IsEqualIID(InterfaceId, __uuidof(IQueueCallbackWrite))) {
+ *Object = QueryIQueueCallbackWrite();
+ hr = S_OK;
+ } else if (IsEqualIID(InterfaceId, __uuidof(IQueueCallbackRead))) {
+ *Object = QueryIQueueCallbackRead();
+ hr = S_OK;
+ } else if (IsEqualIID(InterfaceId, __uuidof(IQueueCallbackDeviceIoControl))) {
+ *Object = QueryIQueueCallbackDeviceIoControl();
+ hr = S_OK;
+ } else {
+ hr = CUnknown::QueryInterface(InterfaceId, Object);
+ }
+
+ return hr;
+}
+
+VOID
+STDMETHODCALLTYPE
+CMyQueue::OnDeviceIoControl(
+ _In_ IWDFIoQueue *pWdfQueue,
+ _In_ IWDFIoRequest *pWdfRequest,
+ _In_ ULONG ControlCode,
+ _In_ SIZE_T InputBufferSizeInBytes,
+ _In_ SIZE_T OutputBufferSizeInBytes
+ )
+/*++
+
+Routine Description:
+
+
+ DeviceIoControl dispatch routine
+
+Arguments:
+
+ pWdfQueue - Framework Queue instance
+ pWdfRequest - Framework Request instance
+ ControlCode - IO Control Code
+ InputBufferSizeInBytes - Length of input buffer
+ OutputBufferSizeInBytes - Length of output buffer
+
+ Always succeeds DeviceIoIoctl
+Return Value:
+
+ VOID
+
+--*/
+{
+ UNREFERENCED_PARAMETER(OutputBufferSizeInBytes);
+ UNREFERENCED_PARAMETER(InputBufferSizeInBytes);
+ UNREFERENCED_PARAMETER(pWdfQueue);
+
+ HRESULT hr = S_OK;
+ SIZE_T reqCompletionInfo = 0;
+ IWDFMemory *inputMemory = NULL;
+ IWDFMemory *outputMemory = NULL;
+ UINT i;
+
+ WUDF_TEST_DRIVER_ASSERT(pWdfRequest);
+ WUDF_TEST_DRIVER_ASSERT(m_Device);
+
+ switch (ControlCode)
+ {
+ case IOCTL_SERIAL_SET_BAUD_RATE:
+ {
+ //
+ // This is a driver for a virtual serial port. Since there is no
+ // actual hardware, we just store the baud rate and don't do
+ // anything with it.
+ //
+ SERIAL_BAUD_RATE baudRateBuffer = {0};
+
+ pWdfRequest->GetInputMemory(&inputMemory);
+ if (NULL == inputMemory)
+ {
+ hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ hr = inputMemory->CopyToBuffer(0,
+ (void*) &baudRateBuffer,
+ sizeof(SERIAL_BAUD_RATE));
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ m_Device->SetBaudRate(baudRateBuffer.BaudRate);
+ }
+
+ break;
+ }
+ case IOCTL_SERIAL_GET_BAUD_RATE:
+ {
+ SERIAL_BAUD_RATE baudRateBuffer = {0};
+
+ baudRateBuffer.BaudRate = m_Device->GetBaudRate();
+
+ pWdfRequest->GetOutputMemory(&outputMemory);
+ if (NULL == outputMemory)
+ {
+ hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ hr = outputMemory->CopyFromBuffer(0,
+ (void*) &baudRateBuffer,
+ sizeof(SERIAL_BAUD_RATE));
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ reqCompletionInfo = sizeof(SERIAL_BAUD_RATE);
+ }
+
+ break;
+ }
+ case IOCTL_SERIAL_SET_MODEM_CONTROL:
+ {
+ //
+ // This is a driver for a virtual serial port. Since there is no
+ // actual hardware, we just store the modem control register
+ // configuration and don't do anything with it.
+ //
+ ULONG *pModemControlRegister = NULL;
+
+ pWdfRequest->GetInputMemory(&inputMemory);
+ if (NULL == inputMemory)
+ {
+ hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ pModemControlRegister = m_Device->GetModemControlRegisterPtr();
+ WUDF_TEST_DRIVER_ASSERT(pModemControlRegister);
+
+ hr = inputMemory->CopyToBuffer(0,
+ (void*) pModemControlRegister,
+ sizeof(ULONG));
+ }
+
+ break;
+ }
+ case IOCTL_SERIAL_GET_MODEM_CONTROL:
+ {
+ ULONG *pModemControlRegister = NULL;
+
+ pWdfRequest->GetOutputMemory(&outputMemory);
+ if (NULL == outputMemory)
+ {
+ hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ pModemControlRegister = m_Device->GetModemControlRegisterPtr();
+ WUDF_TEST_DRIVER_ASSERT(pModemControlRegister);
+
+ hr = outputMemory->CopyFromBuffer(0,
+ (void*) pModemControlRegister,
+ sizeof(ULONG));
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ reqCompletionInfo = sizeof(ULONG);
+ }
+
+ break;
+ }
+ case IOCTL_SERIAL_SET_FIFO_CONTROL:
+ {
+ //
+ // This is a driver for a virtual serial port. Since there is no
+ // actual hardware, we just store the FIFO control register
+ // configuration and don't do anything with it.
+ //
+ ULONG *pFifoControlRegister = NULL;
+
+ pWdfRequest->GetInputMemory(&inputMemory);
+ if (NULL == inputMemory)
+ {
+ hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ pFifoControlRegister = m_Device->GetFifoControlRegisterPtr();
+
+ hr = inputMemory->CopyToBuffer(0,
+ (void*) pFifoControlRegister,
+ sizeof(ULONG));
+ }
+
+ break;
+ }
+ case IOCTL_SERIAL_GET_LINE_CONTROL:
+ {
+ ULONG *pLineControlRegister = NULL;
+ SERIAL_LINE_CONTROL lineControl = {0};
+ ULONG lineControlSnapshot;
+
+ pLineControlRegister = m_Device->GetLineControlRegisterPtr();
+ WUDF_TEST_DRIVER_ASSERT(pLineControlRegister);
+
+ //
+ // Take a snapshot of the line control register variable
+ //
+ lineControlSnapshot = *pLineControlRegister;
+
+ //
+ // Decode the word length
+ //
+ if ((lineControlSnapshot & SERIAL_DATA_MASK) == SERIAL_5_DATA)
+ {
+ lineControl.WordLength = 5;
+ }
+ else if ((lineControlSnapshot & SERIAL_DATA_MASK) == SERIAL_6_DATA)
+ {
+ lineControl.WordLength = 6;
+ }
+ else if ((lineControlSnapshot & SERIAL_DATA_MASK) == SERIAL_7_DATA)
+ {
+ lineControl.WordLength = 7;
+ }
+ else if ((lineControlSnapshot & SERIAL_DATA_MASK) == SERIAL_8_DATA)
+ {
+ lineControl.WordLength = 8;
+ }
+
+ //
+ // Decode the parity
+ //
+ if ((lineControlSnapshot & SERIAL_PARITY_MASK) == SERIAL_NONE_PARITY)
+ {
+ lineControl.Parity = NO_PARITY;
+ }
+ else if ((lineControlSnapshot & SERIAL_PARITY_MASK) == SERIAL_ODD_PARITY)
+ {
+ lineControl.Parity = ODD_PARITY;
+ }
+ else if ((lineControlSnapshot & SERIAL_PARITY_MASK) == SERIAL_EVEN_PARITY)
+ {
+ lineControl.Parity = EVEN_PARITY;
+ }
+ else if ((lineControlSnapshot & SERIAL_PARITY_MASK) == SERIAL_MARK_PARITY)
+ {
+ lineControl.Parity = MARK_PARITY;
+ }
+ else if ((lineControlSnapshot & SERIAL_PARITY_MASK) == SERIAL_SPACE_PARITY)
+ {
+ lineControl.Parity = SPACE_PARITY;
+ }
+
+ //
+ // Decode the length of the stop bit
+ //
+ if (lineControlSnapshot & SERIAL_2_STOP)
+ {
+ if (lineControl.WordLength == 5)
+ {
+ lineControl.StopBits = STOP_BITS_1_5;
+ }
+ else
+ {
+ lineControl.StopBits = STOP_BITS_2;
+ }
+ }
+ else
+ {
+ lineControl.StopBits = STOP_BIT_1;
+ }
+
+ //
+ // Copy the information that was decoded to the caller's buffer
+ //
+ pWdfRequest->GetOutputMemory(&outputMemory);
+ if (NULL == outputMemory)
+ {
+ hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ hr = outputMemory->CopyFromBuffer(0,
+ (void*) &lineControl,
+ sizeof(SERIAL_LINE_CONTROL));
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ reqCompletionInfo = sizeof(SERIAL_LINE_CONTROL);
+ }
+
+ break;
+ }
+ case IOCTL_SERIAL_SET_LINE_CONTROL:
+ {
+ ULONG *pLineControlRegister = NULL;
+ SERIAL_LINE_CONTROL lineControl = {0};
+ UCHAR lineControlData = 0;
+ UCHAR lineControlStop = 0;
+ UCHAR lineControlParity = 0;
+ ULONG lineControlSnapshot;
+ ULONG lineControlNew;
+ ULONG lineControlPrevious;
+
+ pLineControlRegister = m_Device->GetLineControlRegisterPtr();
+ WUDF_TEST_DRIVER_ASSERT(pLineControlRegister);
+
+ //
+ // This is a driver for a virtual serial port. Since there is no
+ // actual hardware, we just store the line control register
+ // configuration and don't do anything with it.
+ //
+ pWdfRequest->GetInputMemory(&inputMemory);
+ if (NULL == inputMemory)
+ {
+ hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ hr = inputMemory->CopyToBuffer(0,
+ (void*) &lineControl,
+ sizeof(SERIAL_LINE_CONTROL));
+ }
+
+ //
+ // Bits 0 and 1 of the line control register
+ //
+ if (SUCCEEDED(hr))
+ {
+ switch (lineControl.WordLength)
+ {
+ case 5:
+ lineControlData = SERIAL_5_DATA;
+ m_Device->SetValidDataMask(0x1f);
+ break;
+
+ case 6:
+ lineControlData = SERIAL_6_DATA;
+ m_Device->SetValidDataMask(0x3f);
+ break;
+
+ case 7:
+ lineControlData = SERIAL_7_DATA;
+ m_Device->SetValidDataMask(0x7f);
+ break;
+
+ case 8:
+ lineControlData = SERIAL_8_DATA;
+ m_Device->SetValidDataMask(0xff);
+ break;
+
+ default:
+ hr = E_INVALIDARG;
+ }
+ }
+
+ //
+ // Bit 2 of the line control register
+ //
+ if (SUCCEEDED(hr))
+ {
+ switch (lineControl.StopBits)
+ {
+ case STOP_BIT_1:
+ lineControlStop = SERIAL_1_STOP;
+ break;
+
+ case STOP_BITS_1_5:
+ if (lineControlData != SERIAL_5_DATA)
+ {
+ hr = E_INVALIDARG;
+ break;
+ }
+ lineControlStop = SERIAL_1_5_STOP;
+ break;
+
+ case STOP_BITS_2:
+ if (lineControlData == SERIAL_5_DATA)
+ {
+ hr = E_INVALIDARG;
+ break;
+ }
+ lineControlStop = SERIAL_2_STOP;
+ break;
+
+ default:
+ hr = E_INVALIDARG;
+ }
+ }
+
+ //
+ // Bits 3, 4 and 5 of the line control register
+ //
+ if (SUCCEEDED(hr))
+ {
+ switch (lineControl.Parity)
+ {
+ case NO_PARITY:
+ lineControlParity = SERIAL_NONE_PARITY;
+ break;
+
+ case EVEN_PARITY:
+ lineControlParity = SERIAL_EVEN_PARITY;
+ break;
+
+ case ODD_PARITY:
+ lineControlParity = SERIAL_ODD_PARITY;
+ break;
+
+ case SPACE_PARITY:
+ lineControlParity = SERIAL_SPACE_PARITY;
+ break;
+
+ case MARK_PARITY:
+ lineControlParity = SERIAL_MARK_PARITY;
+ break;
+
+ default:
+ hr = E_INVALIDARG;
+ }
+ }
+
+ //
+ // Update our line control register variable atomically
+ //
+ i=0;
+ do
+ {
+ i++;
+ if ((i & 0xf) == 0)
+ {
+ //
+ // We've been spinning in a loop for a while trying to
+ // update the line control register variable atomically.
+ // Yield the CPU for other threads for a while.
+ //
+ SwitchToThread();
+ }
+
+ lineControlSnapshot = *pLineControlRegister;
+
+ lineControlNew = (lineControlSnapshot & SERIAL_LCR_BREAK) |
+ (lineControlData |
+ lineControlParity |
+ lineControlStop);
+
+ lineControlPrevious = InterlockedCompareExchange((LONG *) pLineControlRegister,
+ lineControlNew,
+ lineControlSnapshot);
+
+ } while (lineControlPrevious != lineControlSnapshot);
+
+ break;
+ }
+ case IOCTL_SERIAL_GET_TIMEOUTS:
+ {
+ SERIAL_TIMEOUTS timeoutValues = {0};
+
+ pWdfRequest->GetOutputMemory(&outputMemory);
+ if (NULL == outputMemory)
+ {
+ hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ m_Device->GetTimeouts(&timeoutValues);
+
+ hr = outputMemory->CopyFromBuffer(0,
+ (void*) &timeoutValues,
+ sizeof(timeoutValues));
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ reqCompletionInfo = sizeof(SERIAL_TIMEOUTS);
+ }
+
+ break;
+ }
+ case IOCTL_SERIAL_SET_TIMEOUTS:
+ {
+ SERIAL_TIMEOUTS timeoutValues = {0};
+
+ pWdfRequest->GetInputMemory(&inputMemory);
+ if (NULL == inputMemory)
+ {
+ hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ hr = inputMemory->CopyToBuffer(0,
+ (void*) &timeoutValues,
+ sizeof(timeoutValues));
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ if ((timeoutValues.ReadIntervalTimeout == MAXULONG) &&
+ (timeoutValues.ReadTotalTimeoutMultiplier == MAXULONG) &&
+ (timeoutValues.ReadTotalTimeoutConstant == MAXULONG))
+ {
+ hr = E_INVALIDARG;
+ }
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ m_Device->SetTimeouts(timeoutValues);
+ }
+
+ break;
+ }
+ case IOCTL_SERIAL_WAIT_ON_MASK:
+ {
+ //
+ // NOTE: the contract is that this ioctl should be marked pending
+ // and not to be completed until some wait event happens. Therefore
+ // it is incorrect for the driver to complete the ioctl right away,
+ // no matter whether success or failure is returned. In either case
+ // most likely the app will send down another iocl of wait-on-mask
+ // in a tight loop, and that gets completed again, and again.
+ // The end result will be high CPU utilization in task manager.
+ //
+ // The correct way would be to mark the ioctl as pending, or as in
+ // WDF world, keep it in a manual queue. Since this is a driver for
+ // a virtual serial port and there is no actual hardware, there will
+ // be no wait event happening. The ioctl stays in the queue until
+ // the calling app decides to no longer wait for it by means of
+ // sending down a set-wait-mask request.
+ //
+
+ //
+ // At most one pending wait-on-mask request is expected
+ //
+ IWDFIoRequest *pSavedRequest;
+ hr = m_FxWaitMaskQueue->RetrieveNextRequest(&pSavedRequest);
+ if (SUCCEEDED(hr))
+ {
+ pSavedRequest->Complete(E_FAIL);
+
+ //
+ // RetrieveNextRequest from a manual queue increments the reference
+ // counter by 1. We need to decrement it, otherwise the request will
+ // not be released and there will be an object leak.
+ //
+ SAFE_RELEASE(pSavedRequest);
+ }
+
+ //
+ // Keep the request in a manual queue and the framework will take
+ // care of cancelling them when the app exits.
+ //
+ pWdfRequest->ForwardToIoQueue(m_FxWaitMaskQueue);
+
+ //
+ // Instead of "break" out of the switch statement and complete the
+ // request at the end of this function, use "return" directly.
+ //
+ return;
+ }
+ case IOCTL_SERIAL_SET_WAIT_MASK:
+ {
+ //
+ // NOTE: the contract says a set-wait-mask will cause any pending
+ // wait-on-mask to complete with STATUS_SUCCESS and the output wait
+ // event mask is set to zero. This is also the way for app to break
+ // out of the loop of sending down wait-on-mask
+ //
+
+ IWDFIoRequest *pSavedRequest;
+ hr = m_FxWaitMaskQueue->RetrieveNextRequest(&pSavedRequest);
+ if (SUCCEEDED(hr))
+ {
+ pSavedRequest->GetOutputMemory(&outputMemory);
+ if (NULL == outputMemory)
+ {
+ hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ ULONG eventMask = 0;
+
+ hr = outputMemory->CopyFromBuffer(0,
+ (void*) &eventMask,
+ sizeof(eventMask));
+
+ pSavedRequest->CompleteWithInformation(hr, sizeof(eventMask));
+ }
+ else
+ {
+ pSavedRequest->Complete(hr);
+ }
+
+ SAFE_RELEASE(pSavedRequest);
+
+ //
+ // outputMemory will be released at the end of the function
+ //
+ }
+
+ //
+ // NOTE: The application expects STATUS_SUCCESS for these IOCTLs.
+ //
+ hr = S_OK;
+
+ break;
+ }
+ case IOCTL_SERIAL_SET_QUEUE_SIZE:
+ case IOCTL_SERIAL_SET_DTR:
+ case IOCTL_SERIAL_SET_RTS:
+ case IOCTL_SERIAL_CLR_RTS:
+ case IOCTL_SERIAL_SET_XON:
+ case IOCTL_SERIAL_SET_XOFF:
+ case IOCTL_SERIAL_SET_CHARS:
+ case IOCTL_SERIAL_GET_CHARS:
+ case IOCTL_SERIAL_GET_HANDFLOW:
+ case IOCTL_SERIAL_SET_HANDFLOW:
+ case IOCTL_SERIAL_RESET_DEVICE:
+ {
+ //
+ // NOTE: The application expects STATUS_SUCCESS for these IOCTLs.
+ // so don't merge this with default.
+ //
+ break;
+ }
+
+ default:
+ {
+ hr = E_INVALIDARG;
+ }
+ }
+
+ //
+ // clean up
+ //
+ if (inputMemory)
+ {
+ inputMemory->Release();
+ }
+
+ if (outputMemory)
+ {
+ outputMemory->Release();
+ }
+
+ //
+ // complete the request
+ //
+ pWdfRequest->CompleteWithInformation(hr, reqCompletionInfo);
+
+ return;
+}
+
+VOID
+STDMETHODCALLTYPE
+CMyQueue::OnWrite(
+ _In_ IWDFIoQueue *pWdfQueue,
+ _In_ IWDFIoRequest *pWdfRequest,
+ _In_ SIZE_T BytesToWrite
+ )
+/*++
+
+Routine Description:
+
+
+ Write dispatch routine
+ IQueueCallbackWrite
+
+Arguments:
+
+ pWdfQueue - Framework Queue instance
+ pWdfRequest - Framework Request instance
+ BytesToWrite - Length of bytes in the write buffer
+
+ Allocate and copy data to local buffer
+Return Value:
+
+ VOID
+
+--*/
+{
+ IWDFMemory* pRequestMemory = NULL;
+ IWDFIoRequest* pSavedRequest = NULL;
+ SIZE_T availableData = 0;
+ SIZE_T savedRequestBufferSize = 0;
+ HRESULT hr = S_OK;
+
+ UNREFERENCED_PARAMETER(pWdfQueue);
+
+ //
+ // Get memory object
+ //
+
+ pWdfRequest->GetInputMemory(&pRequestMemory);
+
+ //
+ // Process input
+ //
+
+ ProcessWriteBytes((PUCHAR)pRequestMemory->GetDataBuffer(NULL), BytesToWrite);
+
+ //
+ // Release memory object and complete request
+ //
+ SAFE_RELEASE(pRequestMemory);
+ pWdfRequest->CompleteWithInformation(hr, BytesToWrite);
+
+ //
+ // Get the amount of data available in the ring buffer
+ //
+ m_RingBuffer.GetAvailableData(&availableData);
+
+ if (availableData > 0)
+ {
+ //
+ // Continue with the next request, if there is one pending
+ //
+ hr = m_FxReadQueue->RetrieveNextRequest(&pSavedRequest);
+ if ((pSavedRequest == NULL) || (FAILED(hr)))
+ {
+ goto Exit;
+ }
+
+ pSavedRequest->GetReadParameters(&savedRequestBufferSize, NULL, NULL);
+
+ OnRead(m_FxQueue,
+ pSavedRequest,
+ savedRequestBufferSize);
+
+ //
+ // RetrieveNextRequest from a manual queue increments the reference
+ // counter by 1. We need to decrement it, otherwise the request will
+ // not be released and there will be an object leak.
+ //
+ SAFE_RELEASE(pSavedRequest);
+ }
+
+Exit:
+ return;
+}
+
+VOID
+STDMETHODCALLTYPE
+CMyQueue::OnRead(
+ _In_ IWDFIoQueue *pWdfQueue,
+ _In_ IWDFIoRequest *pWdfRequest,
+ _In_ SIZE_T SizeInBytes
+ )
+/*++
+
+Routine Description:
+
+
+ Read dispatch routine
+ IQueueCallbackRead
+
+Arguments:
+
+ pWdfQueue - Framework Queue instance
+ pWdfRequest - Framework Request instance
+ SizeInBytes - Length of bytes in the read buffer
+
+ Copy available data into the read buffer
+Return Value:
+
+ VOID
+
+--*/
+{
+ IWDFMemory* pRequestMemory = NULL;
+ SIZE_T BytesCopied = 0;
+ HRESULT hr;
+
+ UNREFERENCED_PARAMETER(pWdfQueue);
+
+ //
+ // Get memory object
+ //
+ pWdfRequest->GetOutputMemory(&pRequestMemory);
+
+ hr = m_RingBuffer.Read((PBYTE)pRequestMemory->GetDataBuffer(NULL),
+ SizeInBytes,
+ &BytesCopied);
+
+ //
+ // Release memory object.
+ //
+ SAFE_RELEASE(pRequestMemory);
+
+ if (FAILED(hr))
+ {
+ //
+ // Error reading buffer
+ //
+ pWdfRequest->Complete(hr);
+ goto Exit;
+ }
+
+ if (BytesCopied > 0)
+ {
+ //
+ // Data was read from buffer succesfully
+ //
+ pWdfRequest->CompleteWithInformation(hr, BytesCopied);
+ }
+ else
+ {
+ //
+ // No data to read. Queue the request for later processing.
+ //
+ pWdfRequest->ForwardToIoQueue(m_FxReadQueue);
+ }
+
+Exit:
+ return;
+}
+
+VOID
+CMyQueue::ProcessWriteBytes(
+ _In_reads_bytes_(Length) PUCHAR Characters,
+ _In_ SIZE_T Length
+ )
+/*++
+Routine Description:
+
+ This function is called when the framework receives IRP_MJ_WRITE
+ requests from the system. The write event handler(FmEvtIoWrite) calls ProcessWriteBytes.
+ It parses the Characters passed in and looks for the for sequences "AT" -ok ,
+ "ATA" --CONNECT, ATD<number> -- CONNECT and sets the state of the device appropriately.
+ These bytes are placed in the read Buffer to be processed later since this device
+ works in a loopback fashion.
+
+
+Arguments:
+
+ Characters - Pointer to the write IRP's system buffer.
+
+ 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:
+
+ VOID
+
+--*/
+
+{
+
+ UCHAR currentCharacter;
+ UCHAR connectString[] = "\r\nCONNECT\r\n";
+ UCHAR connectStringCch = ARRAY_SIZE(connectString) - 1;
+ UCHAR okString[] = "\r\nOK\r\n";
+ UCHAR okStringCch = ARRAY_SIZE(okString) - 1;
+
+ while (Length != 0) {
+
+ currentCharacter = *(Characters++);
+ Length--;
+
+ if(currentCharacter == '\0')
+ {
+ continue;
+ }
+
+ m_RingBuffer.Write(&currentCharacter, sizeof(currentCharacter));
+
+ switch (m_CommandMatchState) {
+ case COMMAND_MATCH_STATE_IDLE:
+ if ((currentCharacter == 'a') || (currentCharacter == 'A'))
+ {
+ //
+ // got an A
+ //
+ m_CommandMatchState=COMMAND_MATCH_STATE_GOT_A;
+ m_ConnectCommand=FALSE;
+ m_IgnoreNextChar=FALSE;
+ }
+
+ break;
+
+ case COMMAND_MATCH_STATE_GOT_A:
+ if ((currentCharacter == 't') || (currentCharacter == 'T'))
+ {
+ //
+ // got a T
+ //
+ m_CommandMatchState=COMMAND_MATCH_STATE_GOT_T;
+ }
+ else
+ {
+ m_CommandMatchState=COMMAND_MATCH_STATE_IDLE;
+ }
+
+ break;
+
+ case COMMAND_MATCH_STATE_GOT_T:
+
+ if (!m_IgnoreNextChar)
+ {
+ //
+ // the last char was not a special char
+ // check for CONNECT command
+ //
+ if ((currentCharacter == 'A') || (currentCharacter == 'a'))
+ {
+
+ m_ConnectCommand=TRUE;
+ }
+
+ if ((currentCharacter == 'D') || (currentCharacter == 'd'))
+ {
+
+ m_ConnectCommand=TRUE;
+ }
+ }
+
+ m_IgnoreNextChar=TRUE;
+
+ if (currentCharacter == '\r')
+ {
+ //
+ // got a CR, send a response to the command
+ //
+ m_CommandMatchState = COMMAND_MATCH_STATE_IDLE;
+
+ if (m_ConnectCommand)
+ {
+ //
+ // place <cr><lf>CONNECT<cr><lf> in the buffer
+ //
+ m_RingBuffer.Write(connectString, connectStringCch);
+
+ //
+ // connected now raise CD
+ //
+ m_CurrentlyConnected = TRUE;
+ m_ConnectionStateChanged = TRUE;
+
+ }
+ else
+ {
+ //
+ // place <cr><lf>OK<cr><lf> in the buffer
+ //
+ m_RingBuffer.Write(okString, okStringCch);
+ }
+ }
+
+ break;
+ default:
+ break;
+ }
+ }
+ return;
+}