Sample Code
Windows Driver Samples/ Virtual serial driver sample/ C++/ queue.cpp/
/*++ 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(); 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_SET_QUEUE_SIZE: case IOCTL_SERIAL_SET_DTR: case IOCTL_SERIAL_SET_WAIT_MASK: 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(¤tCharacter, 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; }
Our Services
-
What our customers say about us?
Read our customer testimonials to find out why our clients keep returning for their projects.
View Testimonials