diff --git a/.gitignore b/.gitignore
index 05fdc97..14a3fc9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,6 +10,7 @@
*.symvers
*.cache.mk
+*.aps
*.obj
*.pdb
*.idb
@@ -28,5 +29,7 @@
[Ll]ogs/
[Dd]ebug/
[Rr]elease/
+[Dd]ebug_EXE/
+[Rr]elease_EXE/
RCa*
\ No newline at end of file
diff --git a/src/windows/tools/qcdev/qcdev.sln b/src/windows/tools/qcdev/qcdev.sln
new file mode 100644
index 0000000..b230194
--- /dev/null
+++ b/src/windows/tools/qcdev/qcdev.sln
@@ -0,0 +1,25 @@
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.31321.278
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qcdev", "qcdev.vcxproj", "{351DA988-07E9-4A1B-9ADC-5DECFE5493C6}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Release|Win32 = Release|Win32
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {351DA988-07E9-4A1B-9ADC-5DECFE5493C6}.Debug|Win32.ActiveCfg = Debug|Win32
+ {351DA988-07E9-4A1B-9ADC-5DECFE5493C6}.Debug|Win32.Build.0 = Debug|Win32
+ {351DA988-07E9-4A1B-9ADC-5DECFE5493C6}.Release|Win32.ActiveCfg = Release|Win32
+ {351DA988-07E9-4A1B-9ADC-5DECFE5493C6}.Release|Win32.Build.0 = Release|Win32
+ {351DA988-07E9-4A1B-9ADC-5DECFE5493C6}.Release|Win32.Deploy.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {54B448FC-3AF0-4EF5-A89A-BB6B10639B0B}
+ EndGlobalSection
+EndGlobal
diff --git a/src/windows/tools/qcdev/qcdev.vcxproj b/src/windows/tools/qcdev/qcdev.vcxproj
new file mode 100644
index 0000000..c52bdaa
--- /dev/null
+++ b/src/windows/tools/qcdev/qcdev.vcxproj
@@ -0,0 +1,88 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+
+ {351DA988-07E9-4A1B-9ADC-5DECFE5493C6}
+ Win32Proj
+ qcdev
+ 10.0
+ qcdev
+
+
+
+ DynamicLibrary
+ true
+ v142
+ Unicode
+
+
+ DynamicLibrary
+ false
+ v142
+ Unicode
+
+
+
+
+
+
+
+
+
+ true
+
+
+ false
+
+
+
+ Level3
+ WIN32;_DEBUG;_WINDOWS;_USRDLL;QCDEVLIB_EXPORTS;TOOLS_TARGET_WINDOWS;%(PreprocessorDefinitions)
+
+
+ Windows
+ true
+ setupapi.lib;Shlwapi.lib;Cfgmgr32.lib;%(AdditionalDependencies)
+
+
+
+
+ Level3
+ true
+ WIN32;NDEBUG;_WINDOWS;_USRDLL;QCDEVLIB_EXPORTS;%(PreprocessorDefinitions)
+
+
+ Windows
+ DebugFull
+ true
+ true
+ setupapi.lib;Shlwapi.lib;Cfgmgr32.lib;%(AdditionalDependencies)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/windows/tools/qcdev/qdaio.cpp b/src/windows/tools/qcdev/qdaio.cpp
new file mode 100644
index 0000000..98714dc
--- /dev/null
+++ b/src/windows/tools/qcdev/qdaio.cpp
@@ -0,0 +1,1130 @@
+/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*
+
+ Q D A I O . C P P
+
+GENERAL DESCRIPTION
+ This file implements asynchronous I/O operations for Qualcomm USB device
+ communication, including device open/close, send/receive, and I/O thread
+ management.
+
+ Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ SPDX-License-Identifier: BSD-3-Clause
+
+*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/
+
+#include "qdpublic.h"
+#include "scandev.h"
+#include "qdaio.h"
+#include
+
+using namespace QDAIO;
+using namespace QcDevice;
+
+namespace QDAIO
+{
+ QDAIODEV AioDevice[QDA_MAX_DEV];
+ LONG DevInitialized = 0;
+ CRITICAL_SECTION MasterLock;
+
+ // namespace // unnamed
+ // {
+ VOID InitDevExtension(PQDAIODEV IoDev)
+ {
+ PQDAIODEV_EXTENSION pDevExt = (PQDAIODEV_EXTENSION)IoDev->DevExtension;
+ PQDAIO_THREAD_CONTEXT pThreadContext;
+ PAIO_ITEM pIoItem;
+ int ioType;
+ int ioItemIdx;
+
+ // initialize extension
+
+ // QIO_RX (0) QIO_TX (1)
+ for (ioType = 0; ioType < 2; ioType++)
+ {
+ // RX/TX idle queues
+ InitializeListHead(&(pDevExt->IoItemIdleQueue[ioType]));
+ pIoItem = pDevExt->IoPool[ioType] = new (std::nothrow) AIO_ITEM[IoDev->IoItemPoolSize];
+ if (pIoItem != NULL)
+ {
+ for (ioItemIdx = 0; ioItemIdx < IoDev->IoItemPoolSize; ioItemIdx++)
+ {
+ pIoItem->State = IO_ITEM_IDLE;
+ pIoItem->Index = ioItemIdx;
+ ZeroMemory(&(pIoItem->OverlappedContext), sizeof(OVERLAPPED));
+ pIoItem->OverlappedContext.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+ InsertTailList(&(pDevExt->IoItemIdleQueue[ioType]), &(pIoItem->List));
+ pIoItem++;
+ }
+ }
+ InitializeCriticalSection(&(pDevExt->IoPoolLock[ioType]));
+
+ // RX/TX queues
+ InitializeListHead(&(pDevExt->DispatchQueue[ioType]));
+ InitializeListHead(&(pDevExt->PendingQueue[ioType]));
+ InitializeListHead(&(pDevExt->CompletionQueue[ioType]));
+
+ // RX/TX thread handles
+ pDevExt->IoThreadHandle[ioType] = INVALID_HANDLE_VALUE;
+ pDevExt->CompletionThreadHandle[ioType] = INVALID_HANDLE_VALUE;
+
+ // RX/TX thread events
+ pDevExt->IoThreadStartedEvt[ioType] = CreateEvent(NULL, FALSE, FALSE, NULL);
+ pDevExt->IoThreadTerminatedEvt[ioType] = CreateEvent(NULL, FALSE, FALSE, NULL);
+ pDevExt->CompletionThreadStartedEvt[ioType] = CreateEvent(NULL, FALSE, FALSE, NULL);
+ pDevExt->CompletionThreadTerminatedEvt[ioType] = CreateEvent(NULL, FALSE, FALSE, NULL);
+ pDevExt->CancelCompletionThreadEvt[ioType] = CreateEvent(NULL, FALSE, FALSE, NULL);
+
+ // RX/TX action events
+ pDevExt->DispatchEvt[ioType] = CreateEvent(NULL, FALSE, FALSE, NULL);
+ pDevExt->CompletionEvt[ioType] = CreateEvent(NULL, FALSE, FALSE, NULL);
+ pDevExt->CancelEvt[ioType] = CreateEvent(NULL, FALSE, FALSE, NULL);
+
+ // Locks
+ InitializeCriticalSection(&(pDevExt->IoLock[ioType]));
+ InitializeCriticalSection(&(pDevExt->CompletionLock[ioType]));
+
+ // thread context
+ pThreadContext = &(pDevExt->ThreadContext[ioType]);
+ pThreadContext->Dev = IoDev;
+ pThreadContext->IoType = ioType;
+
+ // stats
+ pDevExt->OutstandingIo[ioType] = 0;
+ }
+ } // InitDevExtension
+
+ VOID ResetDevExtension(PQDAIODEV IoDev)
+ {
+ PQDAIODEV_EXTENSION pDevExt = (PQDAIODEV_EXTENSION)IoDev->DevExtension;
+ int i;
+
+ QCD_Printf("QDAIO::ResetDevExtension 0x%p\n", pDevExt);
+
+ for (i = 0; i < 2; i++)
+ {
+ // RX/TX thread handles
+ pDevExt->IoThreadHandle[i] = INVALID_HANDLE_VALUE;
+ pDevExt->CompletionThreadHandle[i] = INVALID_HANDLE_VALUE;
+ }
+ } // ResetDevExtension
+
+ // RX/TX threads should be cancelled before calling this
+ VOID PurgeDevExtension(PQDAIODEV Dev)
+ {
+ PAIO_ITEM pIoItem;
+ int ioItemIdx;
+ PQDAIODEV_EXTENSION pDevExt = (PQDAIODEV_EXTENSION)Dev->DevExtension;
+ int ioType;
+
+ QCD_Printf("QDAIO::PurgeDevExtension[%02d] 0x%p\n", Dev->AppHandle, pDevExt);
+
+ EnterCriticalSection(&MasterLock);
+
+ Dev->DevHandle = INVALID_HANDLE_VALUE;
+
+ for (ioType = 0; ioType < 2; ioType++)
+ {
+ pIoItem = pDevExt->IoPool[ioType];
+ for (ioItemIdx = 0; ioItemIdx < Dev->IoItemPoolSize; ioItemIdx++)
+ {
+ CloseHandle(pIoItem->OverlappedContext.hEvent);
+ }
+ CloseHandle(pDevExt->DispatchEvt[ioType]);
+ CloseHandle(pDevExt->CompletionEvt[ioType]);
+ CloseHandle(pDevExt->CancelEvt[ioType]);
+ CloseHandle(pDevExt->IoThreadStartedEvt[ioType]);
+ CloseHandle(pDevExt->IoThreadTerminatedEvt[ioType]);
+ CloseHandle(pDevExt->CompletionThreadStartedEvt[ioType]);
+ CloseHandle(pDevExt->CompletionThreadTerminatedEvt[ioType]);
+ CloseHandle(pDevExt->CancelCompletionThreadEvt[ioType]);
+ pDevExt->IoThreadHandle[ioType] = INVALID_HANDLE_VALUE;
+ pDevExt->CompletionThreadHandle[ioType] = INVALID_HANDLE_VALUE;
+ DeleteCriticalSection(&(pDevExt->IoLock[ioType]));
+ DeleteCriticalSection(&(pDevExt->CompletionLock[ioType]));
+ DeleteCriticalSection(&(pDevExt->IoPoolLock[ioType]));
+ delete[] pDevExt->IoPool[ioType];
+ }
+ LeaveCriticalSection(&MasterLock);
+ } // PurgeDevExtension
+
+ // To cancel all if Context == NULL
+ int FindAndCancel(PQDAIODEV Dev, int IoType, PVOID UserContext)
+ {
+ PQDAIODEV pIoDev = Dev;
+ PQDAIODEV_EXTENSION pDevExt = (PQDAIODEV_EXTENSION)pIoDev->DevExtension;
+ PAIO_ITEM item;
+ PCRITICAL_SECTION ioLock;
+ PLIST_ENTRY dispatchQueue;
+ PLIST_ENTRY pendingQueue;
+ PLIST_ENTRY peekItem;
+ HANDLE dispatchEvt;
+ HANDLE completionEvt;
+ int itemFound = 0;
+
+ QCD_Printf("QDAIO::FindAndCancel IoDev 0x%p Ext 0x%p Ctxt 0x%p\n", pIoDev->DevExtension, UserContext);
+
+ if (IoType < 0 || IoType > 1)
+ {
+ return itemFound;
+ }
+
+ if (UserContext == NULL)
+ {
+ // cancel all
+ CancelIoEx(pIoDev->DevHandle, NULL);
+ itemFound = 1;
+ return itemFound;
+ }
+
+ ioLock = &(pDevExt->IoLock[IoType]);
+ dispatchQueue = &(pDevExt->DispatchQueue[IoType]);
+ pendingQueue = &(pDevExt->PendingQueue[IoType]);
+ dispatchEvt = pDevExt->DispatchEvt[IoType];
+ completionEvt = pDevExt->CompletionEvt[IoType];
+
+ EnterCriticalSection(ioLock);
+ if (!IsListEmpty(dispatchQueue))
+ {
+ // find item and en-queue to CancelQueue
+ peekItem = dispatchQueue->Flink;
+ while (peekItem != dispatchQueue)
+ {
+ item = CONTAINING_RECORD(peekItem, AIO_ITEM, List);
+ peekItem = peekItem->Flink;
+ if (item->UserContext == UserContext)
+ {
+ itemFound = 1;
+ item->Status = QDAIO_STATUS_CANCELLED;
+ item->IoSize = 0;
+ CancelIoEx(pIoDev->DevHandle, &item->OverlappedContext);
+ break;
+ }
+ } // while
+ } // if
+ LeaveCriticalSection(ioLock);
+
+ return itemFound;
+ } // FindAndCancel
+
+ DWORD WINAPI IoThread(PVOID Context)
+ {
+ PQDAIO_THREAD_CONTEXT ioContext = (PQDAIO_THREAD_CONTEXT)Context;
+ PQDAIODEV pIoDev;
+ PQDAIODEV_EXTENSION pDevExt;
+ PAIO_ITEM ioItem;
+ PLIST_ENTRY dispatchQueue, completionQueue;
+ HANDLE dispatchEvt;
+ HANDLE completionEvt;
+ PCRITICAL_SECTION ioLock;
+ PCRITICAL_SECTION completionLock;
+ PLIST_ENTRY pEntry;
+ DWORD waitIdx;
+ BOOL bNotDone = TRUE;
+
+ pIoDev = ioContext->Dev;
+ pDevExt = (PQDAIODEV_EXTENSION)pIoDev->DevExtension;
+
+ if ((ioContext->IoType != QIO_RX) && (ioContext->IoType != QIO_TX))
+ {
+ QCD_Printf("QDAIO::IoThread: invalid IoType %d\n", ioContext->IoType);
+ return 0;
+ }
+
+ QCD_Printf("-->QDAIO::IoThread: Dev[%d] 0x%p IoType %d\n", pIoDev->AppHandle, pIoDev, ioContext->IoType);
+
+ ioLock = &(pDevExt->IoLock[ioContext->IoType]);
+ completionLock = &pDevExt->CompletionLock[ioContext->IoType];
+ dispatchEvt = pDevExt->DispatchEvt[ioContext->IoType];
+ completionEvt = pDevExt->CompletionEvt[ioContext->IoType];
+ dispatchQueue = &(pDevExt->DispatchQueue[ioContext->IoType]);
+ completionQueue = &(pDevExt->CompletionQueue[ioContext->IoType]);
+
+ SetEvent(pDevExt->IoThreadStartedEvt[ioContext->IoType]);
+
+ while (bNotDone == TRUE)
+ {
+ DWORD waitState;
+ BOOL bWait;
+
+ EnterCriticalSection(ioLock);
+
+ if ((pIoDev->DevState == QDAIO_DEV_STATE_CLOSE) && (IsListEmpty(dispatchQueue)))
+ {
+ LeaveCriticalSection(ioLock);
+ bNotDone = FALSE; // to exit thread
+ continue;
+ }
+
+ if ((IsListEmpty(dispatchQueue)) && (pIoDev->DevState == QDAIO_DEV_STATE_OPEN))
+ {
+ bWait = TRUE;
+ }
+ else
+ {
+ bWait = FALSE;
+ }
+ LeaveCriticalSection(ioLock);
+
+ if (bWait == TRUE)
+ {
+ // QCD_Printf("QDAIO::IoThread[%d]: wait 1s for dispatch\n", ioContext->IoType); // LOG
+ waitState = WaitForSingleObject(dispatchEvt, 1000); // 1s timeout
+ ResetEvent(dispatchEvt);
+ if (waitState == WAIT_OBJECT_0)
+ {
+ // QCD_Printf("QDAIO::IoThread[%d]: req in queue\n", ioContext->IoType); // LOG
+ }
+ else
+ {
+ QCD_Printf("QDAIO::IoThread[%d]: Warning: WAIT 0x%x\n", ioContext->IoType, waitState);
+ continue;
+ }
+ }
+
+ EnterCriticalSection(ioLock);
+
+ while (!IsListEmpty(dispatchQueue))
+ {
+ // dequeue the head item to wait for
+ pEntry = RemoveHeadList(dispatchQueue);
+ ioItem = CONTAINING_RECORD(pEntry, AIO_ITEM, List);
+ ioItem->State = IO_ITEM_PENDING;
+ LeaveCriticalSection(ioLock);
+
+ waitIdx = WAIT_TIMEOUT;
+ while (waitIdx == WAIT_TIMEOUT)
+ {
+ waitIdx = WaitForSingleObject(ioItem->OverlappedContext.hEvent, 1000); // 1s timeout
+ // QCD_Printf("QDAIO::IoThread[%d]: wait IO[%d] 0x%p\n", ioContext->IoType, ioItem->Index, ioItem);
+ if (waitIdx == WAIT_TIMEOUT)
+ {
+ QCD_Printf("QDAIO::IoThread[%d]: timeout, Idx %d\n", ioContext->IoType, ioItem->Index);
+ }
+ }
+ if (waitIdx == WAIT_OBJECT_0)
+ {
+ // QCD_Printf("QDAIO::IoThread[%d]: IO to be back\n", ioContext->IoType); // LOG
+ }
+ else
+ {
+ QCD_Printf("QDAIO::IoThread[%d]: IO anomaly 0x%x\n", ioContext->IoType, waitIdx);
+ }
+
+ if (TRUE)
+ {
+ DWORD lastErr;
+ BOOLEAN ioResult;
+
+ // QCD_Printf("QDAIO::IoThread[%d]: wait OV 0x%p\n", ioContext->IoType, &(ioItem->OverlappedContext)); // LOG
+ ioResult = GetOverlappedResult
+ (
+ pIoDev->DevHandle,
+ &(ioItem->OverlappedContext),
+ &(ioItem->IoSize),
+ TRUE // no return until operaqtion completes
+ );
+ lastErr = GetLastError();
+
+ if (ioResult == TRUE)
+ {
+ // QCD_Printf("IoThread: OV success, 0x%p\n", &(ioItem->OverlappedContext)); // LOG
+ ioItem->Status = QDAIO_STATUS_SUCCESS;
+ ioItem->State = IO_ITEM_COMPLETED;
+ }
+ else if (lastErr == ERROR_IO_PENDING)
+ {
+ QCD_Printf("IoThread: OV 0x%p still pending after wait???\n", &(ioItem->OverlappedContext));
+ waitIdx = WAIT_TIMEOUT;
+ continue;
+ }
+ else
+ {
+ // QCD_Printf("IoThread: OV 0x%p failed\n", &(ioItem->OverlappedContext));
+ ioItem->Status = QDAIO_STATUS_FAILURE;
+ ioItem->State = IO_ITEM_COMPLETED;
+ QCD_Printf("QDAIO::IoThread[%d]: failure, DevIdx %02ld [%03d] Outstanding %d err %d\n",
+ ioContext->IoType, pIoDev->AppHandle, ioItem->Index,
+ (pDevExt->OutstandingIo[ioContext->IoType] - 1), lastErr);
+ }
+ if (ioItem->State == IO_ITEM_COMPLETED)
+ {
+ InterlockedDecrement(&(pDevExt->OutstandingIo[ioContext->IoType]));
+ // enqueue to completionQueue
+ EnterCriticalSection(completionLock);
+ InsertTailList(completionQueue, &(ioItem->List));
+ LeaveCriticalSection(completionLock);
+ SetEvent(completionEvt);
+ }
+ }
+ EnterCriticalSection(ioLock);
+ } // while -- DispatchQueue
+ LeaveCriticalSection(ioLock);
+ } // while (bNotDone == TRUE)
+
+ SetEvent(pDevExt->IoThreadTerminatedEvt[ioContext->IoType]);
+
+ QCD_Printf("<--QDAIO::IoThread: Dev[%d] 0x%p IoType %d\n", pIoDev->AppHandle, pIoDev, ioContext->IoType);
+
+ return 0;
+ } // Function -- IoThread
+
+ BOOL WINAPI StartIoThread(PQDAIODEV IoDev)
+ {
+ BOOL bResult = TRUE;
+ PQDAIODEV_EXTENSION pDevExt;
+ PQDAIO_THREAD_CONTEXT pRx, pTx;
+ DWORD waitIdx;
+
+ pDevExt = (PQDAIODEV_EXTENSION)IoDev->DevExtension;
+ pRx = &(pDevExt->ThreadContext[QIO_RX]);
+ pTx = &(pDevExt->ThreadContext[QIO_TX]);
+
+ QCD_Printf("-->StartIoThread: 0x%p\n", IoDev);
+
+ pDevExt->IoThreadHandle[QIO_RX] = ::CreateThread(NULL, 0, IoThread, (LPVOID)pRx, NULL, 0);
+ if (pDevExt->IoThreadHandle[QIO_RX] != INVALID_HANDLE_VALUE)
+ {
+ waitIdx = WaitForSingleObject(pDevExt->IoThreadStartedEvt[QIO_RX], 500);
+ ResetEvent(pDevExt->IoThreadStartedEvt[QIO_RX]);
+ QCD_Printf("QDAIO::StartIoThread: RX 0x%x\n", waitIdx);
+
+ pDevExt->IoThreadHandle[QIO_TX] = ::CreateThread(NULL, 0, IoThread, (LPVOID)pTx, NULL, 0);
+ if (pDevExt->IoThreadHandle[QIO_TX] != INVALID_HANDLE_VALUE)
+ {
+ waitIdx = WaitForSingleObject(pDevExt->IoThreadStartedEvt[QIO_TX], 500);
+ ResetEvent(pDevExt->IoThreadStartedEvt[QIO_TX]);
+ QCD_Printf("QDAIO::StartIoThread: TX 0x%x\n", waitIdx);
+ }
+ else
+ {
+ bResult = FALSE;
+ QCD_Printf("<--StartIoThread: TX thread creation failure, dev 0x%p\n", IoDev);
+ }
+ }
+ else
+ {
+ bResult = FALSE;
+ QCD_Printf("<--StartIoThread: RX thread creation failure, dev 0x%p\n", IoDev);
+ }
+
+ return bResult;
+ } // StartIoThread
+
+ VOID WINAPI CancelIoThread(PQDAIODEV IoDev)
+ {
+ PQDAIODEV_EXTENSION pDevExt;
+ DWORD waitIdx;
+
+ pDevExt = (PQDAIODEV_EXTENSION)IoDev->DevExtension;
+
+ // Cancel RX
+ QCD_Printf("CancelIoThread: wait RX... Dev 0x%p\n", IoDev);
+ SetEvent(pDevExt->DispatchEvt[QIO_RX]); // wake up
+ waitIdx = WaitForSingleObject(pDevExt->IoThreadTerminatedEvt[QIO_RX], 2000);
+ QCD_Printf("QDAIO::CancelIoThread: RX 0x%x\n", waitIdx);
+
+ // Cancel TX
+ QCD_Printf("CancelIoThread: wait TX... Dev 0x%p\n", IoDev);
+ SetEvent(pDevExt->DispatchEvt[QIO_TX]); // wake up
+ waitIdx = WaitForSingleObject(pDevExt->IoThreadTerminatedEvt[QIO_TX], 2000);
+ QCD_Printf("QDAIO::CancelIoThread: TX 0x%x\n", waitIdx);
+ } // CancelIoThread
+
+ PAIO_ITEM GetIoItem(PQDAIODEV Dev, int IoType)
+ {
+ PQDAIODEV_EXTENSION pDevExt;
+ PAIO_ITEM pItem = NULL;
+ PLIST_ENTRY pEntry;
+
+ pDevExt = (PQDAIODEV_EXTENSION)Dev->DevExtension;
+
+ EnterCriticalSection(&(pDevExt->IoPoolLock[IoType]));
+
+ if (!IsListEmpty(&(pDevExt->IoItemIdleQueue[IoType])))
+ {
+ pEntry = RemoveHeadList(&(pDevExt->IoItemIdleQueue[IoType]));
+ pItem = CONTAINING_RECORD(pEntry, AIO_ITEM, List);
+ }
+
+ LeaveCriticalSection(&(pDevExt->IoPoolLock[IoType]));
+
+ return pItem;
+ } // Function -- GetIoItem
+
+ VOID RecycleIoItem(PQDAIODEV Dev, PAIO_ITEM IoItem, int IoType)
+ {
+ PQDAIODEV_EXTENSION pDevExt;
+
+ pDevExt = (PQDAIODEV_EXTENSION)Dev->DevExtension;
+
+ IoItem->Buf = NULL;
+ IoItem->IoSize = 0;
+ IoItem->IoCallback = NULL;
+ IoItem->UserContext = NULL;
+ IoItem->Status = 0;
+
+ IoItem->OverlappedContext.Internal = 0;
+ IoItem->OverlappedContext.InternalHigh = 0;
+ IoItem->OverlappedContext.Pointer = 0;
+ IoItem->OverlappedContext.Offset = 0;
+ IoItem->OverlappedContext.OffsetHigh = 0;
+
+ EnterCriticalSection(&(pDevExt->IoPoolLock[IoType]));
+ IoItem->State = IO_ITEM_IDLE;
+ InsertTailList(&(pDevExt->IoItemIdleQueue[IoType]), &(IoItem->List));
+ LeaveCriticalSection(&(pDevExt->IoPoolLock[IoType]));
+ } // Function -- RecycleIoItem
+
+ VOID WINAPI PurgeCompletionQueue(PQDAIO_THREAD_CONTEXT pContext)
+ {
+ PQDAIODEV pIoDev;
+ PQDAIODEV_EXTENSION pDevExt;
+ PCRITICAL_SECTION pCompLock;
+ PLIST_ENTRY completionQueue, pEntry;
+ PAIO_ITEM ioItem;
+
+ pIoDev = pContext->Dev;
+ pDevExt = (PQDAIODEV_EXTENSION)pIoDev->DevExtension;
+ pCompLock = &(pDevExt->CompletionLock[pContext->IoType]);
+ completionQueue = &(pDevExt->CompletionQueue[pContext->IoType]);
+
+ EnterCriticalSection(pCompLock);
+
+ while (!IsListEmpty(completionQueue))
+ {
+ pEntry = RemoveHeadList(completionQueue);
+ LeaveCriticalSection(pCompLock);
+
+ ioItem = CONTAINING_RECORD(pEntry, AIO_ITEM, List);
+ ioItem->IoCallback
+ (
+ pIoDev->AppHandle, ioItem->UserContext,
+ ioItem->Status, ioItem->IoSize
+ );
+ RecycleIoItem(pIoDev, ioItem, pContext->IoType);
+
+ EnterCriticalSection(pCompLock);
+ }
+
+ LeaveCriticalSection(pCompLock);
+
+ } // PurgeCompletionQueue
+
+ DWORD WINAPI CompletionThread(PVOID Context)
+ {
+ PQDAIO_THREAD_CONTEXT pContext = (PQDAIO_THREAD_CONTEXT)Context;
+ PQDAIODEV pIoDev;
+ PQDAIODEV_EXTENSION pDevExt;
+ PCRITICAL_SECTION pCompLock;
+ PLIST_ENTRY completionQueue;
+ BOOL notDone = TRUE;
+ HANDLE hEvents[2];
+ DWORD waitStatus;
+
+ pIoDev = pContext->Dev;
+ pDevExt = (PQDAIODEV_EXTENSION)pIoDev->DevExtension;
+ pCompLock = &(pDevExt->CompletionLock[pContext->IoType]);
+ completionQueue = &(pDevExt->CompletionQueue[pContext->IoType]);
+ hEvents[0] = pDevExt->CancelCompletionThreadEvt[pContext->IoType];
+ hEvents[1] = pDevExt->CompletionEvt[pContext->IoType];
+
+ SetEvent(pDevExt->CompletionThreadStartedEvt[pContext->IoType]);
+
+ while (notDone == TRUE)
+ {
+ // wait for events
+ waitStatus = WaitForMultipleObjects(2, hEvents, FALSE, INFINITE);
+ switch (waitStatus)
+ {
+ case WAIT_OBJECT_0: // cancel
+ {
+ QCD_Printf("QDAIO::CompletionThread: app %d CxlEvt\n", pIoDev->AppHandle);
+ ResetEvent(hEvents[0]);
+ // purge completionQueue
+ PurgeCompletionQueue(pContext);
+ notDone = FALSE;
+ break;
+ }
+ case WAIT_OBJECT_0 + 1: // completion
+ {
+ // QCD_Printf("QDAIO::CompletionThread: app %d CompletionEvt\n", pIoDev->AppHandle); // LOG
+ ResetEvent(hEvents[1]);
+ PurgeCompletionQueue(pContext);
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ } // switch
+ } // while (notDone == TRUE)
+
+ PurgeCompletionQueue(pContext); // final cleanup
+ SetEvent(pDevExt->CompletionThreadTerminatedEvt[pContext->IoType]);
+ return 0;
+ } // CompletionThread
+
+ BOOL WINAPI StartCompletionThread(PQDAIODEV IoDev)
+ {
+ BOOL bResult = TRUE;
+ PQDAIODEV_EXTENSION pDevExt;
+ PQDAIO_THREAD_CONTEXT pRx, pTx;
+ DWORD waitIdx;
+
+ pDevExt = (PQDAIODEV_EXTENSION)IoDev->DevExtension;
+ pRx = &(pDevExt->ThreadContext[QIO_RX]);
+ pTx = &(pDevExt->ThreadContext[QIO_TX]);
+
+ QCD_Printf("-->QDAIO::StartCompletionThread: Dev 0x%p\n", IoDev);
+
+ pDevExt->CompletionThreadHandle[QIO_RX] = ::CreateThread(NULL, 0, CompletionThread, (LPVOID)pRx, NULL, 0);
+ if (pDevExt->CompletionThreadHandle[QIO_RX] != INVALID_HANDLE_VALUE)
+ {
+ waitIdx = WaitForSingleObject(pDevExt->CompletionThreadStartedEvt[QIO_RX], 500);
+ ResetEvent(pDevExt->CompletionThreadStartedEvt[QIO_RX]);
+ QCD_Printf("QDAIO::StartCompletionThread: RX 0x%x\n", waitIdx);
+
+ pDevExt->CompletionThreadHandle[QIO_TX] = ::CreateThread(NULL, 0, CompletionThread, (LPVOID)pTx, NULL, 0);
+ if (pDevExt->CompletionThreadHandle[QIO_TX] != INVALID_HANDLE_VALUE)
+ {
+ waitIdx = WaitForSingleObject(pDevExt->CompletionThreadStartedEvt[QIO_TX], 500);
+ ResetEvent(pDevExt->CompletionThreadStartedEvt[QIO_TX]);
+ QCD_Printf("QDAIO::StartCompletionThread: TX 0x%x\n", waitIdx);
+ }
+ else
+ {
+ QCD_Printf("QDAIO::StartCompletionThread: TX thread creation failure, Dev 0x%p\n", IoDev);
+ bResult = FALSE;
+ }
+ }
+ else
+ {
+ QCD_Printf("QDAIO::StartCompletionThread: RX thread creation failure, Dev 0x%p\n", IoDev);
+ bResult = FALSE;
+ }
+ QCD_Printf("<--QDAIO::StartCompletionThread: Dev 0x%p\n", IoDev);
+
+ return bResult;
+ } // StartCompletionThread
+
+ VOID WINAPI CancelCompletionThread(PQDAIODEV IoDev)
+ {
+ PQDAIODEV_EXTENSION pDevExt;
+ DWORD waitIdx;
+
+ pDevExt = (PQDAIODEV_EXTENSION)IoDev->DevExtension;
+
+ // Cancel RX
+ SetEvent(pDevExt->CancelCompletionThreadEvt[QIO_RX]);
+ waitIdx = WaitForSingleObject(pDevExt->CompletionThreadTerminatedEvt[QIO_RX], 2000);
+ QCD_Printf("QDAIO::CancelCompletionThread: RX 0x%x\n", waitIdx);
+
+ // Cancel TX
+ SetEvent(pDevExt->CancelCompletionThreadEvt[QIO_TX]);
+ waitIdx = WaitForSingleObject(pDevExt->CompletionThreadTerminatedEvt[QIO_TX], 2000);
+ QCD_Printf("QDAIO::CancelCompletionThread: TX 0x%x\n", waitIdx);
+
+ } // CancelCompletionThread
+
+ int WINAPI ConfigureCommChannel(UCHAR DevType, HANDLE DeviceHandle)
+ {
+ int retVal = 0;
+
+ if (DevType == QC_DEV_TYPE_PORTS)
+ {
+ DCB dcb;
+ COMMTIMEOUTS commTimeouts;
+
+ dcb.DCBlength = sizeof(DCB);
+ if (GetCommState(DeviceHandle, &dcb) == TRUE)
+ {
+ //dcb.BaudRate = 3000000;
+ dcb.BaudRate = 4800;
+ dcb.ByteSize = 8;
+ dcb.Parity = NOPARITY;
+ dcb.StopBits = ONESTOPBIT;
+ if (SetCommState(DeviceHandle, &dcb) == FALSE)
+ {
+ QCD_Printf("[Type_%d] SetCommState failed on handle 0x%x, error 0x%x\n",
+ DevType, DeviceHandle, GetLastError());
+ retVal = 1;
+ }
+ else
+ {
+ QCD_Printf("[Type_%d] SetCommState done", DevType);
+ }
+ }
+ else
+ {
+ QCD_Printf("[Type_%d] GetCommState failed on handle 0x%x, error 0x%x\n",
+ DevType, DeviceHandle, GetLastError());
+ retVal = 2;
+ }
+
+ commTimeouts.ReadIntervalTimeout = MAXDWORD;
+ commTimeouts.ReadTotalTimeoutMultiplier = 0;
+ commTimeouts.ReadTotalTimeoutConstant = 2000;
+ commTimeouts.WriteTotalTimeoutMultiplier = 0;
+ commTimeouts.WriteTotalTimeoutConstant = 3000;
+ if (SetCommTimeouts(DeviceHandle, &commTimeouts) == false)
+ {
+ QCD_Printf("[Type_%d] SetCommTimeouts failed on handle 0x%x Error 0x%x\n",
+ DevType, DeviceHandle, GetLastError());
+ retVal = 3;
+ }
+ else
+ {
+ QCD_Printf("[Type_%d] SetCommTimeouts done", DevType);
+ }
+ }
+ return retVal;
+ } // ConfigureCommChannel
+
+ // } // namespace // unnamed
+} // namespace QDAIO
+
+QCDEVLIB_API BOOL QDAIO::Initialize(VOID)
+{
+ LONG i;
+
+ if (InterlockedIncrement(&DevInitialized) > 1)
+ {
+ InterlockedDecrement(&DevInitialized);
+ return FALSE;
+ }
+
+ InitializeCriticalSection(&MasterLock);
+ for (i = 0; i < QDA_MAX_DEV; i++)
+ {
+ AioDevice[i].AppHandle = i; // psudo handle
+ AioDevice[i].DevState = QDAIO_DEV_STATE_CLOSE;
+ AioDevice[i].IoItemPoolSize = QDAIO_IO_POOL_SIZE; // TODO: seetable in the future
+
+ AioDevice[i].DevExtension = new (std::nothrow) QDAIODEV_EXTENSION;
+ if (AioDevice[i].DevExtension == NULL)
+ {
+ QCD_Printf("QDAIO::Initialize: error - no mem at idx %d\n", i);
+ QDAIO::Cleanup();
+ return FALSE;
+ }
+ InitDevExtension(&AioDevice[i]);
+ }
+ QCD_Printf("<--QDAIO::Initialize -- InitDevExtension (%d)\n", i);
+ return TRUE;
+} // Initialize
+
+QCDEVLIB_API VOID QDAIO::Cleanup(VOID)
+{
+ PQDAIODEV_EXTENSION pDevExt;
+ LONG devIdx;
+
+ // TODO: make sure all devices are closed and extension purged
+ for (devIdx = 0; devIdx < QDA_MAX_DEV; devIdx++)
+ {
+ pDevExt = (PQDAIODEV_EXTENSION)AioDevice[devIdx].DevExtension;
+ if (pDevExt != NULL)
+ {
+ PurgeDevExtension(&AioDevice[devIdx]);
+ delete AioDevice[devIdx].DevExtension;
+ AioDevice[devIdx].DevExtension = NULL;
+ }
+ else
+ {
+ // NULL is set when Initialize fails to allocate memory
+ break;
+ }
+ }
+ DeleteCriticalSection(&MasterLock);
+ QCD_Printf("<--QDAIO::Cleanup -- PurgeDevExtension (%d)\n", devIdx);
+}
+
+QCDEVLIB_API LONG QDAIO::OpenDevice(PVOID DeviceName)
+{
+ LONG i, appHdl = -1;
+ PQDAIODEV pIoDev = NULL;
+ PQDAIODEV_EXTENSION pDevExt = NULL;
+ HANDLE hDevice = INVALID_HANDLE_VALUE; // -1
+
+ if (DeviceName == NULL)
+ {
+ return QDAIO_STATUS_INVALID_DEVICE;
+ }
+
+ EnterCriticalSection(&MasterLock);
+ if (DevInitialized == 0)
+ {
+ LeaveCriticalSection(&MasterLock);
+ return QDAIO_STATUS_DEVICE_NOT_INIT;
+ }
+
+ // locate a free slot, start with index 1 to avoid conflict with QDAIO_STATUS_SUCCESS
+ for (i = 1; i < QDA_MAX_DEV; i++)
+ {
+ if (AioDevice[i].DevState == QDAIO_DEV_STATE_CLOSE)
+ {
+ appHdl = i;
+ pIoDev = (PQDAIODEV) & (AioDevice[i]);
+ pDevExt = (PQDAIODEV_EXTENSION)pIoDev->DevExtension;
+ break;
+ }
+ }
+ LeaveCriticalSection(&MasterLock);
+
+ if ((appHdl >= QDA_MAX_DEV) || (appHdl < 0))
+ {
+ return QDAIO_STATUS_BAD_HANDLE;
+ }
+ if (pIoDev == NULL || pDevExt == NULL)
+ {
+ return QDAIO_STATUS_NO_DEVICE;
+ }
+
+ // initialize extension
+ ResetDevExtension(pIoDev);
+
+ hDevice = ::CreateFileA
+ (
+ (PCHAR)DeviceName,
+ GENERIC_WRITE | GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, // async operation
+ NULL
+ );
+ if (hDevice == INVALID_HANDLE_VALUE)
+ {
+ QCD_Printf("QDAIO::OpenDevice: Error 0x%x <%s>\n", GetLastError(), (PCHAR)DeviceName);
+ return QDAIO_STATUS_HANDLE_FAILURE;
+ }
+ else
+ {
+ pIoDev->DevHandle = hDevice;
+ ConfigureCommChannel(QC_DEV_TYPE_PORTS, hDevice);
+ QCD_Printf("QDAIO::OpenDevice: Handle 0x%x/0x%x\n", appHdl, hDevice);
+
+ pIoDev->DevState = QDAIO_DEV_STATE_OPEN;
+ StartIoThread(pIoDev);
+ StartCompletionThread(pIoDev);
+ }
+
+ return appHdl;
+} // OpenDevice
+
+
+QCDEVLIB_API LONG QDAIO::OpenDevice(PVOID DeviceName, DWORD Baudrate, BOOL isLegacyTimeoutConfig)
+{
+ LONG i, appHdl = -1;
+ PQDAIODEV pIoDev = NULL;
+ PQDAIODEV_EXTENSION pDevExt = NULL;
+ HANDLE hDevice = INVALID_HANDLE_VALUE; // -1
+
+ if (DeviceName == NULL)
+ {
+ return QDAIO_STATUS_INVALID_DEVICE;
+ }
+
+ EnterCriticalSection(&MasterLock);
+ if (DevInitialized == 0)
+ {
+ LeaveCriticalSection(&MasterLock);
+ return QDAIO_STATUS_DEVICE_NOT_INIT;
+ }
+
+ // locate a free slot, start with index 1 to avoid conflict with QDAIO_STATUS_SUCCESS
+ for (i = 1; i < QDA_MAX_DEV; i++)
+ {
+ if (AioDevice[i].DevState == QDAIO_DEV_STATE_CLOSE)
+ {
+ appHdl = i;
+ pIoDev = (PQDAIODEV) & (AioDevice[i]);
+ pDevExt = (PQDAIODEV_EXTENSION)pIoDev->DevExtension;
+ break;
+ }
+ }
+ LeaveCriticalSection(&MasterLock);
+
+ if ((appHdl >= QDA_MAX_DEV) || (appHdl < 0))
+ {
+ return QDAIO_STATUS_BAD_HANDLE;
+ }
+ if (pIoDev == NULL || pDevExt == NULL)
+ {
+ return QDAIO_STATUS_NO_DEVICE;
+ }
+
+ // initialize extension
+ ResetDevExtension(pIoDev);
+
+ hDevice = QcDevice::OpenDevice(DeviceName, Baudrate, isLegacyTimeoutConfig);
+
+ if (hDevice == INVALID_HANDLE_VALUE)
+ {
+ QCD_Printf("QDAIO::OpenDevice: Error 0x%x <%s>\n", GetLastError(), (PCHAR)DeviceName);
+ return QDAIO_STATUS_HANDLE_FAILURE;
+ }
+ else
+ {
+ pIoDev->DevHandle = hDevice;
+ ConfigureCommChannel(QC_DEV_TYPE_PORTS, hDevice);
+ QCD_Printf("QDAIO::OpenDevice: Handle 0x%x/0x%x\n", appHdl, hDevice);
+
+ pIoDev->DevState = QDAIO_DEV_STATE_OPEN;
+ StartIoThread(pIoDev);
+ StartCompletionThread(pIoDev);
+ }
+
+ return appHdl;
+} // openDevice
+
+QCDEVLIB_API LONG QDAIO::CloseDevice(LONG AppHandle)
+{
+ PQDAIODEV pIoDev;
+
+ if ((AppHandle >= QDA_MAX_DEV) || (AppHandle < 0))
+ {
+ QCD_Printf("QDAIO::CloseDevice: Invalid appHdl %d\n", AppHandle);
+ return QDAIO_STATUS_BAD_HANDLE;
+ }
+
+ pIoDev = &(AioDevice[AppHandle]);
+
+ if (pIoDev->DevState != QDAIO_DEV_STATE_OPEN)
+ {
+ QCD_Printf("QDAIO::CloseDevice: dev handle 0x%x not open\n", AppHandle);
+ return QDAIO_STATUS_NO_DEVICE;
+ }
+
+ QCD_Printf("QDAIO::CloseDevice: handle 0x%x/0x%x\n", AppHandle, pIoDev->DevHandle);
+
+ pIoDev->DevState = QDAIO_DEV_STATE_CLOSE;
+
+ CloseHandle(pIoDev->DevHandle);
+
+ CancelIoThread(pIoDev);
+ CancelCompletionThread(pIoDev);
+
+ ResetDevExtension(pIoDev);
+
+ return QDAIO_STATUS_SUCCESS;
+} // CloseDevice
+
+QCDEVLIB_API LONG QDAIO::Send(LONG AppHandle, PCHAR Buffer, ULONG Length, PAIO_CONTEXT Context)
+{
+ PAIO_ITEM ioItem;
+ BOOL bResult = FALSE;
+ DWORD numBytesSent = 0;
+ PQDAIODEV pIoDev;
+ PQDAIODEV_EXTENSION pDevExt;
+
+ if ((AppHandle >= QDA_MAX_DEV) || (AppHandle < 0))
+ {
+ QCD_Printf("QDAIO::Send: Invalid appHdl %d\n", AppHandle);
+ return QDAIO_STATUS_BAD_HANDLE;
+ }
+
+ pIoDev = &(AioDevice[AppHandle]);
+
+ if (pIoDev->DevExtension == NULL)
+ {
+ QCD_Printf("QDAIO::Send: non-exist appHdl %d\n", AppHandle);
+ return QDAIO_STATUS_NO_DEVICE;
+ }
+ pDevExt = (PQDAIODEV_EXTENSION)pIoDev->DevExtension;
+
+ if (pIoDev->DevState != QDAIO_DEV_STATE_OPEN)
+ {
+ QCD_Printf("QDAIO::Send: dev not open: appHdl %d\n", AppHandle);
+ return QDAIO_STATUS_FAILURE;
+ }
+
+ ioItem = GetIoItem(pIoDev, QIO_TX);
+ if (ioItem == NULL)
+ {
+ QCD_Printf("QDAIO::Send: out of memory for appHdl 0x%x\n", AppHandle);
+ return QDAIO_STATUS_NO_MEMORY;
+ }
+
+ // en-queue request
+ ioItem->Buf = Buffer;
+ ioItem->IoSize = Length;;
+ ioItem->UserContext = (PVOID)Context;
+ ioItem->IoCallback = Context->Callback;
+ if (ioItem->OverlappedContext.hEvent == NULL)
+ {
+ // TODO: do we need to call CB?
+ QCD_Printf("QDAIO::Send: event error %u\n", GetLastError());
+ RecycleIoItem(pIoDev, ioItem, QIO_TX);
+ return QDAIO_STATUS_FAILURE;
+ }
+
+ bResult = ::WriteFile
+ (
+ pIoDev->DevHandle,
+ Buffer,
+ Length,
+ &numBytesSent,
+ &(ioItem->OverlappedContext)
+ );
+
+ EnterCriticalSection(&(pDevExt->IoLock[QIO_TX]));
+ InsertTailList(&(pDevExt->DispatchQueue[QIO_TX]), &(ioItem->List));
+ SetEvent(pDevExt->DispatchEvt[QIO_TX]);
+ LeaveCriticalSection(&(pDevExt->IoLock[QIO_TX]));
+ InterlockedIncrement(&(pDevExt->OutstandingIo[QIO_TX]));
+
+ // QCD_Printf("<--QDAIO::Send[%d] Ctx 0x%p OV 0x%p bResult %d \n", AppHandle, Context, &(ioItem->OverlappedContext), bResult); // LOG
+
+ return QDAIO_STATUS_PENDING;
+
+} // Send
+
+QCDEVLIB_API LONG QDAIO::Read(LONG AppHandle, PCHAR Buffer, ULONG Length, PAIO_CONTEXT Context)
+{
+ PAIO_ITEM ioItem;
+ BOOL bResult = FALSE;
+ DWORD numBytesRead = 0;
+ PQDAIODEV pIoDev;
+ PQDAIODEV_EXTENSION pDevExt;
+
+ if ((AppHandle >= QDA_MAX_DEV) || (AppHandle < 0))
+ {
+ QCD_Printf("QDAIO::Read: Invalid appHdl %d\n", AppHandle);
+ return QDAIO_STATUS_BAD_HANDLE;
+ }
+
+ pIoDev = &(AioDevice[AppHandle]);
+
+ if (pIoDev->DevExtension == NULL)
+ {
+ QCD_Printf("QDAIO::Read: non-exist appHdl %d\n", AppHandle);
+ return QDAIO_STATUS_NO_DEVICE;
+ }
+ pDevExt = (PQDAIODEV_EXTENSION)pIoDev->DevExtension;
+
+ if (pIoDev->DevState != QDAIO_DEV_STATE_OPEN)
+ {
+ QCD_Printf("QDAIO::Read: dev not open: appHdl %d\n", AppHandle);
+ return QDAIO_STATUS_FAILURE;
+ }
+
+ ioItem = GetIoItem(pIoDev, QIO_RX);
+ if (ioItem == NULL)
+ {
+ QCD_Printf("QDAIO::Read: out of memory for appHdl %d\n", AppHandle);
+ return QDAIO_STATUS_NO_MEMORY;
+ }
+
+ // en-queue request
+ ioItem->Buf = Buffer;
+ ioItem->IoSize = Length;
+ ioItem->UserContext = (PVOID)Context;
+ ioItem->IoCallback = Context->Callback;
+ if (ioItem->OverlappedContext.hEvent == NULL)
+ {
+ QCD_Printf("QDAIO::Read: event error %u\n", GetLastError());
+ RecycleIoItem(pIoDev, ioItem, QIO_RX);
+ return QDAIO_STATUS_FAILURE;
+ }
+
+ bResult = ::ReadFile
+ (
+ pIoDev->DevHandle,
+ Buffer,
+ Length,
+ &numBytesRead,
+ &(ioItem->OverlappedContext)
+ );
+
+ EnterCriticalSection(&(pDevExt->IoLock[QIO_RX]));
+ InsertTailList(&(pDevExt->DispatchQueue[QIO_RX]), &(ioItem->List));
+ SetEvent(pDevExt->DispatchEvt[QIO_RX]);
+ LeaveCriticalSection(&(pDevExt->IoLock[QIO_RX]));
+ InterlockedIncrement(&(pDevExt->OutstandingIo[QIO_RX]));
+
+ // QCD_Printf("<--QDAIO::Read[%d--%d] Len %d Context 0x%p/0x%p OV 0x%p, bResult %d\n",
+ // AppHandle, ioItem->Index, Length, Context, ioItem, &(ioItem->OverlappedContext), bResult); // LOG
+ return QDAIO_STATUS_PENDING;
+} // Read
+
+// cancell all RX/TX if Context == NULL
+QCDEVLIB_API LONG QDAIO::Cancel(LONG AppHandle, PAIO_CONTEXT Context)
+{
+ PQDAIODEV pIoDev;
+
+ if ((AppHandle >= QDA_MAX_DEV) || (AppHandle < 0))
+ {
+ QCD_Printf("QDAIO::Cancel: Invalid appHdl 0x%x\n", AppHandle);
+ return QDAIO_STATUS_BAD_HANDLE;
+ }
+
+ pIoDev = &(AioDevice[AppHandle]);
+
+ if (pIoDev->DevExtension == NULL)
+ {
+ QCD_Printf("QDAIO::Cancel: non-exist appHdl 0x%x\n", AppHandle);
+ return QDAIO_STATUS_NO_DEVICE;
+ }
+
+ if (pIoDev->DevState != QDAIO_DEV_STATE_OPEN)
+ {
+ QCD_Printf("QDAIO::Cancel: dev not open, appHdl 0x%x\n", AppHandle);
+ return QDAIO_STATUS_FAILURE;
+ }
+
+ QCD_Printf("QDAIO::Cancel[%d]: 0x%p\n", AppHandle, Context);
+
+ if (FindAndCancel(pIoDev, QIO_RX, Context) == 0)
+ {
+ FindAndCancel(pIoDev, QIO_TX, Context);
+ }
+
+ return QDAIO_STATUS_SUCCESS; // success from CancelIoEx
+} // Cancel
+
+QCDEVLIB_API BOOL QDAIO::SetSessionTotal(LONG AppHandle, LONGLONG SessionTotal)
+{
+ PQDAIODEV pIoDev;
+ DWORD bytesReturned = 0;
+
+ if ((AppHandle >= QDA_MAX_DEV) || (AppHandle < 0))
+ {
+ QCD_Printf("QDAIO::SetSessionTotal: Invalid appHdl 0x%x\n", AppHandle);
+ return FALSE;
+ }
+
+ pIoDev = &(AioDevice[AppHandle]);
+
+ if (pIoDev->DevExtension == NULL)
+ {
+ QCD_Printf("QDAIO::SetSessionTotal: non-exist appHdl 0x%x\n", AppHandle);
+ return FALSE;
+ }
+
+ if (pIoDev->DevState != QDAIO_DEV_STATE_OPEN)
+ {
+ QCD_Printf("QDAIO::SetSessionTotal: dev not open, appHdl 0x%x\n", AppHandle);
+ return FALSE;
+ }
+
+ QCD_Printf("QDAIO::SetSessionTotal[%d]\n", AppHandle);
+
+ if (DeviceIoControl(
+ pIoDev->DevHandle,
+ IOCTL_QCUSB_SET_SESSION_TOTAL,
+ (LPVOID)&SessionTotal,
+ (DWORD)sizeof(LONGLONG),
+ (LPVOID)NULL,
+ (DWORD)0,
+ &bytesReturned,
+ NULL
+ ) == FALSE)
+ {
+ QCD_Printf("QDAIO::SetSessionTotal: failure 0x%x\n", GetLastError());
+ return FALSE;
+ }
+
+ QCD_Printf("QDAIO::SetSessionTotal: %I64d bytes of session total (%u bytes)\n", SessionTotal, bytesReturned);
+
+ return TRUE;
+} // SetSessionTotal
diff --git a/src/windows/tools/qcdev/qdaio.h b/src/windows/tools/qcdev/qdaio.h
new file mode 100644
index 0000000..508ea12
--- /dev/null
+++ b/src/windows/tools/qcdev/qdaio.h
@@ -0,0 +1,162 @@
+/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*
+
+ Q D A I O . H
+
+GENERAL DESCRIPTION
+ This file defines data structures and function prototypes for the
+ asynchronous I/O module used in Qualcomm USB device communication.
+
+ Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ SPDX-License-Identifier: BSD-3-Clause
+
+*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/
+
+#ifndef QDAIO_H
+#define QDAIO_H
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include "qdpublic.h"
+#include "utils.h"
+
+#define QDA_MAX_DEV 16
+#define QDAIO_IO_POOL_SIZE 100
+#define BAIL_OUT_THRESHOLD 5
+
+#define QIO_RX 0
+#define QIO_TX 1
+
+typedef enum _QDAIO_DEV_STATE
+{
+ QDAIO_DEV_STATE_UNINIT = 0,
+ QDAIO_DEV_STATE_INIT = 1,
+ QDAIO_DEV_STATE_OPEN = 2,
+ QDAIO_DEV_STATE_CLOSE = 3
+} QDAIO_DEV_STATE;
+
+typedef enum _AIO_ITEM_STATE
+{
+ IO_ITEM_IDLE = 0,
+ IO_ITEM_PENDING,
+ IO_ITEM_COMPLETED
+} AIO_ITEM_STATE;
+
+// AIO status codes
+typedef enum _QDAIO_STATUS
+{
+ QDAIO_STATUS_SUCCESS = 0,
+ QDAIO_STATUS_FAILURE = -1,
+ QDAIO_STATUS_INVALID_DEVICE = -2,
+ QDAIO_STATUS_DEVICE_NOT_INIT = -3,
+ QDAIO_STATUS_NO_DEVICE = -4,
+ QDAIO_STATUS_HANDLE_FAILURE = -5,
+ QDAIO_STATUS_BAD_HANDLE = -6,
+ QDAIO_STATUS_NO_MEMORY = -7,
+ QDAIO_STATUS_PENDING = -8,
+ QDAIO_STATUS_CANCELLED = -9
+} QDAIO_STATUS;
+
+typedef VOID(_stdcall *AIO_CALLBACK)(LONG AppHdl, PVOID Context, ULONG Status, ULONG IoSize);
+
+typedef struct _AIO_CONTEXT
+{
+ PVOID UserData;
+ PVOID UserData2;
+ AIO_CALLBACK Callback;
+ int Index;
+ LIST_ENTRY List;
+} AIO_CONTEXT, *PAIO_CONTEXT;
+
+typedef struct _AIO_ITEM
+{
+ LIST_ENTRY List;
+ PVOID Buf;
+ DWORD IoSize;
+ OVERLAPPED OverlappedContext;
+ AIO_CALLBACK IoCallback;
+ PVOID UserContext;
+ AIO_ITEM_STATE State;
+ int Index;
+ DWORD Status; // system-specific error code (such as Windows error code)
+} AIO_ITEM, *PAIO_ITEM;
+
+typedef struct _QDAIODEV
+{
+ PVOID DevExtension; // point to QDAIODEV_EXTENTION
+ LONG AppHandle; // index
+ HANDLE DevHandle;
+ LONG IoItemPoolSize;
+ QDAIO_DEV_STATE DevState;
+} QDAIODEV, *PQDAIODEV;
+
+typedef struct _QDAIO_THREAD_CONTEXT
+{
+ PQDAIODEV Dev;
+ int IoType; // RX -- 0; TX -- 1
+} QDAIO_THREAD_CONTEXT, *PQDAIO_THREAD_CONTEXT;
+
+typedef struct _QDAIODEV_EXTENTION
+{
+ // RX/TX item pool
+ PAIO_ITEM IoPool[2];
+ LIST_ENTRY IoItemIdleQueue[2];
+ CRITICAL_SECTION IoPoolLock[2];
+
+ // RX/TX queues
+ LIST_ENTRY DispatchQueue[2];
+ LIST_ENTRY PendingQueue[2];
+ LIST_ENTRY CompletionQueue[2];
+
+ // RX/TX thread handle
+ HANDLE IoThreadHandle[2];
+ HANDLE CompletionThreadHandle[2];
+
+ // Rx/TX thread events
+ HANDLE IoThreadStartedEvt[2];
+ HANDLE IoThreadTerminatedEvt[2];
+ HANDLE CompletionThreadStartedEvt[2];
+ HANDLE CompletionThreadTerminatedEvt[2];
+ HANDLE CancelCompletionThreadEvt[2];
+
+ // RX/TX action events
+ HANDLE DispatchEvt[2];
+ HANDLE CompletionEvt[2];
+ HANDLE CancelEvt[2];
+
+ // RX/TX thread context
+ QDAIO_THREAD_CONTEXT ThreadContext[2];
+
+ // Locks
+ CRITICAL_SECTION IoLock[2];
+ CRITICAL_SECTION CompletionLock[2];
+ LONG OutstandingIo[2];
+} QDAIODEV_EXTENSION, *PQDAIODEV_EXTENSION;
+
+#define QCOMSER_IOCTL_INDEX 2048
+/* Make the following code as 3339 - session total bytes */
+#define IOCTL_QCUSB_SET_SESSION_TOTAL CTL_CODE(FILE_DEVICE_UNKNOWN, \
+ QCOMSER_IOCTL_INDEX+31, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+namespace QDAIO
+{
+ extern QDAIODEV AioDevice[QDA_MAX_DEV];
+ QCDEVLIB_API BOOL Initialize(VOID);
+ QCDEVLIB_API VOID Cleanup(VOID);
+
+ // following APIs return handle or QDAIO_STATUS on failure
+ QCDEVLIB_API LONG OpenDevice(PVOID DeviceName);
+ QCDEVLIB_API LONG OpenDevice(PVOID DeviceName, DWORD Baudrate = DEV_DEFAULT_BAUD_RATE, BOOL isLegacyTimeoutConfig = false);
+ QCDEVLIB_API LONG CloseDevice(LONG AppHandle);
+ QCDEVLIB_API LONG Send(LONG AppHandle, PCHAR Buffer, ULONG Length, PAIO_CONTEXT Context);
+ QCDEVLIB_API LONG Read(LONG AppHandle, PCHAR Buffer, ULONG Length, PAIO_CONTEXT Context);
+ QCDEVLIB_API LONG Cancel(LONG AppHandle, PAIO_CONTEXT Context);
+ QCDEVLIB_API BOOL SetSessionTotal(LONG AppHandle, LONGLONG SessionTotal); // Golden-Gate specific
+} // QDAIO
+
+#endif
diff --git a/src/windows/tools/qcdev/qddll.cpp b/src/windows/tools/qcdev/qddll.cpp
new file mode 100644
index 0000000..8e43cd4
--- /dev/null
+++ b/src/windows/tools/qcdev/qddll.cpp
@@ -0,0 +1,150 @@
+/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*
+
+ Q D D L L . C P P
+
+GENERAL DESCRIPTION
+ This file implements DLL entry points and exported COM registration
+ functions for the Qualcomm device configuration library (qcdev).
+
+ Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ SPDX-License-Identifier: BSD-3-Clause
+
+*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/
+
+#include "qddll.h"
+
+QCDEVLIB_API HRESULT __cdecl DllRegisterServer(void)
+{
+ return S_OK;
+}
+
+QCDEVLIB_API HRESULT __cdecl DllUnregisterServer(void)
+{
+ return S_OK;
+}
+
+BOOL WINAPI DllMain
+(
+ HINSTANCE hinstDLL, // DLL module handle
+ DWORD fdwReason, // calling reason
+ LPVOID lpReserved
+)
+{
+ // printf("-->DllMain\n");
+ // Perform actions based on the reason for calling.
+ switch (fdwReason)
+ {
+ case DLL_PROCESS_ATTACH:
+ // printf("DllMain: DLL_PROCESS_ATTACH\n");
+ DisableThreadLibraryCalls(hinstDLL);
+ break;
+
+ case DLL_THREAD_ATTACH:
+ // printf("DllMain: DLL_THREAD_ATTACH\n");
+ break;
+
+ case DLL_THREAD_DETACH:
+ // printf("DllMain: DLL_THREAD_DETACH\n");
+ break;
+
+ case DLL_PROCESS_DETACH:
+ // printf("DllMain: DLL_PROCESS_DETACH\n");
+ break;
+ }
+ // printf("<--DllMain\n");
+ return TRUE;
+}
+
+QCDEVLIB_API VOID __cdecl QDDLL_SetLoggingCallback(QCD_LOGGING_CALLBACK Cb)
+{
+ QcDevice::SetLoggingCallback(Cb);
+}
+
+QCDEVLIB_API VOID __cdecl QDDLL_SetDeviceChangeCallback(DEVICECHANGE_CALLBACK Cb)
+{
+ QcDevice::SetDeviceChangeCallback(Cb);
+}
+
+QCDEVLIB_API VOID __cdecl QDDLL_SetDeviceChangeCallbackWithContext(DEVICECHANGE_CALLBACK Cb, PVOID AppContext)
+{
+ QcDevice::SetDeviceChangeCallback(Cb, AppContext);
+}
+
+QCDEVLIB_API VOID __cdecl QDDLL_SetDeviceChangeCallback_N(DEVICECHANGE_CALLBACK_N Cb)
+{
+ QcDevice::SetDeviceChangeCallback(Cb);
+}
+
+QCDEVLIB_API VOID __cdecl QDDLL_SetFeature(PVOID Features)
+{
+ PDEV_FEATURE_SETTING myst = (PDEV_FEATURE_SETTING)Features;
+
+ QcDevice::QCD_Printf("-->QDDLL_SetFeature: 0x%x, 0x%x, 0x%x\n", myst->Version, myst->Settings, myst->DeviceClass);
+ QcDevice::SetFeature(Features);
+ QcDevice::QCD_Printf("<--QDDLL_SetFeature\n");
+}
+
+QCDEVLIB_API VOID __cdecl QDDLL_StartDeviceMonitor(VOID)
+{
+ QcDevice::StartDeviceMonitor();
+}
+
+QCDEVLIB_API VOID __cdecl QDDLL_StopDeviceMonitor(VOID)
+{
+ QcDevice::StopDeviceMonitor();
+}
+
+QCDEVLIB_API ULONG __cdecl QDDLL_GetDevice(PVOID DevInfo)
+{
+ return QcDevice::GetDevice(DevInfo);
+}
+
+QCDEVLIB_API PCHAR __cdecl QDDLL_GetPortName(PVOID DeviceName)
+{
+ return QcDevice::GetPortName(DeviceName);
+}
+
+QCDEVLIB_API PCHAR __cdecl QDDLL_LibVersion(PVOID)
+{
+ return QcDevice::LibVersion();
+}
+
+// ************** EXPERIMENTAL APIs *******************
+
+QCDEVLIB_API ULONG __cdecl QDDLL_GetDeviceList(PVOID Buffer, ULONG BufferSize, PULONG ActualSize)
+{
+ return QcDevice::GetDeviceList(Buffer, BufferSize, ActualSize);
+}
+
+QCDEVLIB_API HANDLE __cdecl QDDLL_OpenDevice(PVOID DeviceName)
+{
+ return QcDevice::OpenDevice(DeviceName);
+}
+
+QCDEVLIB_API VOID __cdecl QDDLL_CloseDevice(HANDLE hDevice)
+{
+ QcDevice::CloseDevice(hDevice);
+}
+
+QCDEVLIB_API BOOL __cdecl QDDLL_ReadFromDevice
+(
+ HANDLE hDevice,
+ PVOID RxBuffer,
+ DWORD NumBytesToRead,
+ LPDWORD NumBytesReturned
+)
+{
+ return QcDevice::ReadFromDevice(hDevice, RxBuffer, NumBytesToRead, NumBytesReturned);
+}
+
+QCDEVLIB_API BOOL __cdecl QDDLL_SendToDevice
+(
+ HANDLE hDevice,
+ PVOID TxBuffer,
+ DWORD NumBytesToSend,
+ LPDWORD NumBytesSent
+)
+{
+ return QcDevice::SendToDevice(hDevice, TxBuffer, NumBytesToSend, NumBytesSent);
+}
+// ************** END OF EXPERIMENTAL APIs *******************
diff --git a/src/windows/tools/qcdev/qddll.h b/src/windows/tools/qcdev/qddll.h
new file mode 100644
index 0000000..a68d024
--- /dev/null
+++ b/src/windows/tools/qcdev/qddll.h
@@ -0,0 +1,65 @@
+/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*
+
+ Q D D L L . H
+
+GENERAL DESCRIPTION
+ This file defines the DLL export/import macros and exported function
+ prototypes for the Qualcomm device configuration library (qcdev).
+
+ Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ SPDX-License-Identifier: BSD-3-Clause
+
+*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/
+
+#ifndef QDDLL_H
+#define QDDLL_H
+
+#include "qdpublic.h"
+
+#ifdef QCDEVLIB_EXPORTS
+#define QCDEVLIB_API __declspec(dllexport)
+#else
+#define QCDEVLIB_API __declspec(dllimport)
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ QCDEVLIB_API HRESULT __cdecl DllRegisterServer(void);
+ QCDEVLIB_API HRESULT __cdecl DllUnregisterServer(void);
+ QCDEVLIB_API VOID __cdecl QDDLL_SetLoggingCallback(QCD_LOGGING_CALLBACK Cb);
+ QCDEVLIB_API VOID __cdecl QDDLL_SetDeviceChangeCallback(DEVICECHANGE_CALLBACK Cb);
+ QCDEVLIB_API VOID __cdecl QDDLL_SetDeviceChangeCallbackWithContext(DEVICECHANGE_CALLBACK Cb, PVOID AppContext);
+ QCDEVLIB_API VOID __cdecl QDDLL_SetDeviceChangeCallback_N(DEVICECHANGE_CALLBACK_N Cb);
+ QCDEVLIB_API VOID __cdecl QDDLL_SetFeature(PVOID Features);
+ QCDEVLIB_API VOID __cdecl QDDLL_StartDeviceMonitor(VOID);
+ QCDEVLIB_API VOID __cdecl QDDLL_StopDeviceMonitor(VOID);
+ QCDEVLIB_API ULONG __cdecl QDDLL_GetDevice(PVOID DevInfo);
+ QCDEVLIB_API PCHAR __cdecl QDDLL_GetPortName(PVOID DeviceName);
+ QCDEVLIB_API PCHAR __cdecl QDDLL_LibVersion(PVOID);
+ QCDEVLIB_API ULONG __cdecl QDDLL_GetDeviceList(PVOID Buffer, ULONG BufferSize, PULONG ActualSize);
+ QCDEVLIB_API HANDLE __cdecl QDDLL_OpenDevice(PVOID DeviceName);
+ QCDEVLIB_API VOID __cdecl QDDLL_CloseDevice(HANDLE hDevice);
+
+ QCDEVLIB_API BOOL __cdecl QDDLL_ReadFromDevice
+ (
+ HANDLE hDevice,
+ PVOID RxBuffer,
+ DWORD NumBytesToRead,
+ LPDWORD NumBytesReturned
+ );
+
+ QCDEVLIB_API BOOL __cdecl QDDLL_SendToDevice
+ (
+ HANDLE hDevice,
+ PVOID TxBuffer,
+ DWORD NumBytesToSend,
+ LPDWORD NumBytesSent
+ );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // QDDLL_H
diff --git a/src/windows/tools/qcdev/qdpublic.h b/src/windows/tools/qcdev/qdpublic.h
new file mode 100644
index 0000000..f6425ea
--- /dev/null
+++ b/src/windows/tools/qcdev/qdpublic.h
@@ -0,0 +1,180 @@
+/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*
+
+ Q D P U B L I C . H
+
+GENERAL DESCRIPTION
+ This file defines the public data types, constants, callback
+ typedefs, and the QcDevice class interface for the Qualcomm
+ device configuration library (qcdev).
+
+ Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ SPDX-License-Identifier: BSD-3-Clause
+
+*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/
+
+#ifndef QDPUB_H
+#define QDPUB_H
+
+#include
+
+#ifdef QCDEVLIB_EXPORTS
+#define QCDEVLIB_API __declspec(dllexport)
+#else
+#define QCDEVLIB_API __declspec(dllimport)
+#endif
+
+// constants for the Flag in callback
+#define QC_DEV_TYPE_NONE 0x00
+#define QC_DEV_TYPE_NET 0x01
+#define QC_DEV_TYPE_PORTS 0x02
+#define QC_DEV_TYPE_USB 0x03
+#define QC_DEV_TYPE_MDM 0x04
+#define QC_DEV_TYPE_ADB 0x05
+#define QC_DEV_TYPE_MBIM 0x06
+#define QC_DEV_TYPE_RNDIS 0x07
+
+#define QC_DEV_STATE_DEPARTURE 0x00
+#define QC_DEV_STATE_ARRIVAL 0x01
+
+#define QC_FLAG_MASK_QC_DRIVER 0x0000000F
+#define QC_FLAG_MASK_DEV_STATE 0x000000F0
+#define QC_FLAG_MASK_DEV_TYPE 0x0000FF00
+#define QC_FLAG_MASK_DEV_BUS 0x000F0000
+#define QC_FLAG_MASK_DEV_PROTOCOL 0x000000FF
+#define QC_FLAG_MASK_DEV_PROTOCOL_CATEGORY 0x000000F0
+
+#define QC_DEV_BUS_TYPE_NONE 0 // none
+#define QC_DEV_BUS_TYPE_USB 1 // USB
+#define QC_DEV_BUS_TYPE_PCI 2 // PCI
+#define QC_DEV_BUS_TYPE_PCIE 3 // QC_BUS
+
+// configurable settings
+#define DEV_FEATURE_INCLUDE_NONE_QC_PORTS 0x00000001
+#define DEV_FEATURE_SCAN_USB_WITH_VID 0x00010000
+
+#define DEV_CLASS_NET 0x00000001
+#define DEV_CLASS_PORTS 0x00000002
+#define DEV_CLASS_USB 0x00000004
+#define DEV_CLASS_MDM 0x00000008
+#define DEV_CLASS_ADB 0x00000010
+
+// USB Protocol Number Assignment
+#define DEV_PROTOCOL_UNKNOWN 0x00
+#define DEV_PROTOCOL_SAHARA 0x10
+#define DEV_PROTOCOL_SAHARA_PBL_EMERGENCY_DOWNLOAD 0x11
+#define DEV_PROTOCOL_SAHARA_PBL_FLASHLESS_BOOT 0x12
+#define DEV_PROTOCOL_SAHARA_SBL_XBL_RAMDUMP 0x13
+#define DEV_PROTOCOL_SAHARA_TN_APPS_Remote_EFS 0x14
+#define DEV_PROTOCOL_FIREHOSE 0x20
+#define DEV_PROTOCOL_DIAG 0x30
+#define DEV_PROTOCOL_DUN 0x40
+#define DEV_PROTOCOL_RMNET 0x50
+#define DEV_PROTOCOL_NMEA 0x60
+#define DEV_PROTOCOL_QDSS 0x70
+#define DEV_PROTOCOL_ADPL 0x80
+#define DEV_PROTOCOL_RESERVED 0xFF
+#define DEV_PROTOCOL_MAJOR_REVISION_MASK 0xF0
+#define DEV_DEFAULT_BAUD_RATE 38400
+
+#pragma pack(push, 4)
+
+typedef struct _DEV_FEATURE_SETTING
+{
+ ULONG Version; // 1
+ ULONG Settings; // OR'ed feature masks
+ ULONG DeviceClass; // 0 - all classes
+ PTSTR VID;
+} DEV_FEATURE_SETTING, *PDEV_FEATURE_SETTING;
+
+typedef struct _CB_PARAMS
+{
+ PWCHAR DevDesc; // device description for display in unicode
+ PCHAR DevName; // device name for I/O (normally the symbolic name)
+ PWCHAR IfName; // interface name for network adapter
+ PWCHAR Loc; // device's location on its bus
+ PWCHAR DevPath; // device's connection path
+ PWCHAR SerNum; // serial number from device
+ PWCHAR SerNumMsm; // serial number from device
+ ULONG Mtu; // MTU for network connection (data call)
+ ULONG Flag; // flag to indicate device type, state, and availability of QC driver support
+ ULONG Protocol; // protocol code (low 8 bits) of the USB function, 0 means unknown protocol
+ PWCHAR HwId; // device hardware ID
+ PWCHAR ParentDev; // name of parent device if present
+} CB_PARAMS, *PCB_PARAMS;
+
+typedef enum _DEV_INFO_ERRNO
+{
+ DEV_INFO_OK = 0,
+ DEV_INFO_DEV_DESC_LEN = 1,
+ DEV_INFO_DEV_NAME_LEN = 2,
+ DEV_INFO_IF_NAME_LEN = 3,
+ DEV_INFO_LOC_LEN = 4,
+ DEV_INFO_SER_NUM_LEN = 5,
+ DEV_INFO_END = 255
+} DEV_INFO_ERRNO;
+
+typedef struct _DEV_PARAMS_N
+{
+ PVOID DevDesc; // device description for display in unicode
+ ULONG DevDescBufLen; // in bytes
+ PVOID DevName; // device name for I/O (normally the symbolic name), ANSI string
+ ULONG DevnameBufLen; // in bytes
+ PVOID IfName; // interface name for network adapter, in unicode
+ ULONG IfNameBufLen; // in bytes
+ PVOID Loc; // device's location on its bus, in unicode
+ ULONG LocBufLen; // in bytes
+ PVOID DevPath; // device's connection path
+ ULONG DevPathBufLen; // in bytes
+ PVOID SerNum; // serial number from device, in unicode
+ ULONG SerNumBufLen; // in bytes
+ PVOID SerNumMsm; // serial number from device, in unicode
+ ULONG SerNumMsmBufLen; // in bytes
+ ULONG Mtu; // MTU for network connection (data call)
+ ULONG Flag; // flag to indicate device type, state, and availability of QC driver support
+ ULONG Protocol; // protocol code of the USB function, 0 means unknown protocol
+} DEV_PARAMS_N, *PDEV_PARAMS_N;
+
+#pragma pack(pop)
+
+// Device change callback
+typedef VOID(_stdcall *DEVICECHANGE_CALLBACK)(PCB_PARAMS CbParams, PVOID *Context);
+typedef VOID(_stdcall *DEVICECHANGE_CALLBACK_N)(VOID);
+
+// Logging callback
+typedef VOID(_stdcall *QCD_LOGGING_CALLBACK)(PCHAR Message); // NULL-terminated ANSI string
+
+// APIs for device monitor/update
+namespace QcDevice
+{
+ QCDEVLIB_API VOID _cdecl QCD_Printf(const char *Format, ...);
+ QCDEVLIB_API VOID SetLoggingCallback(QCD_LOGGING_CALLBACK Cb);
+ QCDEVLIB_API VOID SetDeviceChangeCallback(DEVICECHANGE_CALLBACK Cb);
+ QCDEVLIB_API VOID SetDeviceChangeCallback(DEVICECHANGE_CALLBACK Cb, PVOID AppContext);
+ QCDEVLIB_API VOID SetDeviceChangeCallback(DEVICECHANGE_CALLBACK_N Cb);
+ QCDEVLIB_API VOID SetFeature(PVOID Features);
+ QCDEVLIB_API VOID StartDeviceMonitor(VOID);
+ QCDEVLIB_API VOID StopDeviceMonitor(VOID);
+ QCDEVLIB_API ULONG GetDevice(PVOID UserBuffer);
+ QCDEVLIB_API PCHAR GetPortName(PVOID DeviveName);
+ QCDEVLIB_API PCHAR LibVersion(VOID);
+ QCDEVLIB_API BOOL FindParent(PVOID HwInstanceId, PVOID ParentDev, PVOID PotentialSerNum);
+ QCDEVLIB_API ULONG GetDeviceList(PVOID Buffer, ULONG BufferSize, PULONG ActualSize);
+ QCDEVLIB_API HANDLE OpenDevice(PVOID DeviceName, DWORD Baudrate = DEV_DEFAULT_BAUD_RATE, BOOL isLegacyTimeoutConfig = false);
+ QCDEVLIB_API VOID CloseDevice(HANDLE hDevice);
+ QCDEVLIB_API BOOL ReadFromDevice
+ (
+ HANDLE hDevice,
+ PVOID RxBuffer,
+ DWORD NumBytesToRead,
+ LPDWORD NumBytesReturned
+ );
+ QCDEVLIB_API BOOL SendToDevice
+ (
+ HANDLE hDevice,
+ PVOID TxBuffer,
+ DWORD NumBytesToSend,
+ LPDWORD NumBytesSent
+ );
+}
+
+#endif // QDPUB_H
diff --git a/src/windows/tools/qcdev/resource.h b/src/windows/tools/qcdev/resource.h
new file mode 100644
index 0000000..7f9c754
--- /dev/null
+++ b/src/windows/tools/qcdev/resource.h
@@ -0,0 +1,17 @@
+// Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+// SPDX-License-Identifier: BSD-3-Clause
+
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by Resource.rc
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 101
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1001
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/src/windows/tools/qcdev/resource.rc b/src/windows/tools/qcdev/resource.rc
new file mode 100644
index 0000000..301ef7d
--- /dev/null
+++ b/src/windows/tools/qcdev/resource.rc
@@ -0,0 +1,99 @@
+// Microsoft Visual C++ generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "winres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (United States) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+ "#include ""winres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 1,0,0,0
+ PRODUCTVERSION 1,0,0,0
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x40004L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "CompanyName", "Qualcomm Technologies, Inc."
+ VALUE "FileDescription", "Qualcomm USB Device Utility"
+ VALUE "FileVersion", "1.0.0.0"
+ VALUE "InternalName", "qcdev"
+ VALUE "LegalCopyright", "Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries."
+ VALUE "OriginalFilename", "qcdev.dll"
+ VALUE "ProductName", "Qualcomm USB Device Utility"
+ VALUE "ProductVersion", "1.0.0.0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
+
+#endif // English (United States) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/src/windows/tools/qcdev/scandev.cpp b/src/windows/tools/qcdev/scandev.cpp
new file mode 100644
index 0000000..d056274
--- /dev/null
+++ b/src/windows/tools/qcdev/scandev.cpp
@@ -0,0 +1,2578 @@
+/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*
+
+ S C A N D E V . C P P
+
+GENERAL DESCRIPTION
+ This file implements device scanning, enumeration, and monitoring
+ functions for Qualcomm USB devices on Windows.
+
+ Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ SPDX-License-Identifier: BSD-3-Clause
+
+*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/
+
+#include "scandev.h"
+#include "utils.h"
+
+using namespace QcDevice;
+
+namespace QcDevice
+{
+ namespace
+ {
+ PVOID CallerContext = NULL;
+ INTERNAL_DEV_FEATURE_SETTING FeatureSetting;
+ BOOL bFeatureSet = FALSE;
+ DEVICECHANGE_CALLBACK fNotifyCb = NULL;
+ DEVICECHANGE_CALLBACK_N fNotifyCb_N = NULL;
+ QCD_LOGGING_CALLBACK fLogger = NULL;
+ BOOL bMonitorRunning = FALSE;
+ HANDLE hStopMonitorEvt, hMonitorStartedEvt;
+ HANDLE hAnnouncementEvt, hAnnouncementExitEvt;
+ HANDLE hMonitorThread, hAnnouncementThread = 0;
+ HANDLE hRegChangeEvt[QC_REG_MAX];
+ HKEY hSysKey[QC_REG_MAX];
+ LONG lInOperation = 0;
+ LONG lInitialized = 0;
+ CRITICAL_SECTION opLock, notifyLock;
+ LIST_ENTRY ArrivalList, FreeList, NewArrivalList;
+ LIST_ENTRY NotifyFreeList, AnnouncementList;
+ PQC_NOTIFICATION_STORE NotifyStore;
+
+ VOID InitializeLists(VOID)
+ {
+ int i;
+ PQC_DEV_ITEM pItem;
+
+ InitializeListHead(&ArrivalList);
+ InitializeListHead(&FreeList);
+ InitializeListHead(&NewArrivalList);
+ InitializeListHead(&NotifyFreeList);
+ InitializeListHead(&AnnouncementList);
+ for (i = 0; i < MAX_NUM_DEV; i++)
+ {
+ pItem = (PQC_DEV_ITEM)malloc(sizeof(QC_DEV_ITEM));
+ if (pItem == NULL)
+ {
+ break;
+ }
+ pItem->Info.Type = QC_DEV_TYPE_NONE;
+ pItem->Info.Flag = DEV_FLAG_NONE;
+ pItem->Info.IsQCDriver = 0;
+ pItem->UserContext = NULL;
+ ZeroMemory(pItem->DevDesc, QC_MAX_VALUE_NAME);
+ ZeroMemory(pItem->DevDescA, QC_MAX_VALUE_NAME);
+ ZeroMemory(pItem->DevNameW, QC_MAX_VALUE_NAME);
+ ZeroMemory(pItem->DevNameA, QC_MAX_VALUE_NAME);
+ ZeroMemory(pItem->HwId, QC_MAX_VALUE_NAME);
+ ZeroMemory(pItem->ParentDev, QC_MAX_VALUE_NAME);
+ ZeroMemory(pItem->InterfaceName, QC_MAX_VALUE_NAME);
+ ZeroMemory(pItem->SerNum, 256);
+ ZeroMemory(pItem->SerNumMsm, 256);
+ InsertTailList(&FreeList, &(pItem->List));
+ }
+ NotifyStore = (PQC_NOTIFICATION_STORE)malloc(sizeof(QC_NOTIFICATION_STORE) * 512);
+ if (NotifyStore == NULL)
+ {
+ QCD_Printf("Failed to alloc notification store\n");
+ }
+ else
+ {
+ for (i = 0; i < 512; i++)
+ {
+ InitializeListHead(&(NotifyStore[i].DevItemChain));
+ InsertTailList(&NotifyFreeList, &(NotifyStore[i].List));
+ }
+ }
+ } // InitializeLists
+
+ VOID CleanupList(PLIST_ENTRY ItemList)
+ {
+ PLIST_ENTRY pEntry;
+ PQC_DEV_ITEM pItem;
+
+ EnterCriticalSection(&opLock);
+ while (!IsListEmpty(ItemList))
+ {
+ pEntry = RemoveHeadList(ItemList);
+ pItem = CONTAINING_RECORD(pEntry, QC_DEV_ITEM, List);
+ free(pItem);
+ }
+ LeaveCriticalSection(&opLock);
+ } // CleanupList
+
+ VOID TryToAnnounce(PLIST_ENTRY ActiveList, PLIST_ENTRY NewArrival)
+ {
+ PQC_DEV_ITEM pActive, pArrival;
+ PLIST_ENTRY headOfActive, peekActive;
+ PLIST_ENTRY headOfArrival, peekArrival;
+ PLIST_ENTRY headOfStore;
+ PQC_NOTIFICATION_STORE storeBranch = NULL;
+
+ // NOTE: ActiveList is the ArrivalList
+
+ EnterCriticalSection(&opLock);
+
+ if ((!IsListEmpty(ActiveList)) && (!IsListEmpty(NewArrival)))
+ {
+ headOfActive = ActiveList;
+ peekActive = headOfActive->Flink;
+ while (peekActive != headOfActive)
+ {
+ pActive = CONTAINING_RECORD(peekActive, QC_DEV_ITEM, List);
+ peekActive = peekActive->Flink;
+
+ headOfArrival = NewArrival;
+ peekArrival = headOfArrival->Flink;
+ while (peekArrival != headOfArrival)
+ {
+ pArrival = CONTAINING_RECORD(peekArrival, QC_DEV_ITEM, List);
+ peekArrival = peekArrival->Flink;
+ if ((StrCmp((LPCWSTR)pActive->DevDesc, (LPCWSTR)pArrival->DevDesc) == 0) &&
+ (StrCmp((LPCWSTR)pActive->DevNameW, (LPCWSTR)pArrival->DevNameW) == 0))
+ {
+ if (StrCmp((LPCWSTR)pActive->SerNum, (LPCWSTR)pArrival->SerNum) == 0)
+ {
+ pActive->Info.Flag = pArrival->Info.Flag = DEV_FLAG_ARRIVAL;
+ }
+ }
+ } // while
+ } // while
+ } // if
+
+ // Get a storeBranch for devices to be announced
+ {
+ EnterCriticalSection(¬ifyLock);
+ if (!IsListEmpty(&NotifyFreeList))
+ {
+ headOfStore = RemoveHeadList(&NotifyFreeList);
+ storeBranch = CONTAINING_RECORD(headOfStore, QC_NOTIFICATION_STORE, List);
+ }
+ LeaveCriticalSection(¬ifyLock);
+ }
+
+ // New arrivals are Flag==0 on NewArrival
+ while (!IsListEmpty(NewArrival))
+ {
+ headOfArrival = RemoveHeadList(NewArrival);
+ pArrival = CONTAINING_RECORD(headOfArrival, QC_DEV_ITEM, List);
+ if (pArrival->Info.Flag == DEV_FLAG_NONE)
+ {
+ PQC_DEV_ITEM pDevice;
+
+ pDevice = (PQC_DEV_ITEM)malloc(sizeof(QC_DEV_ITEM));
+ pArrival->Info.Flag = DEV_FLAG_ARRIVAL;
+ if ((storeBranch == NULL) || (hAnnouncementThread == 0) || (pDevice == NULL))
+ {
+ if (fNotifyCb != NULL)
+ {
+ pArrival->CbParams.DevDesc = (PWCHAR)pArrival->DevDesc;
+ pArrival->CbParams.DevName = (PCHAR)pArrival->DevNameA;
+ pArrival->CbParams.IfName = (PWCHAR)pArrival->InterfaceName;
+ pArrival->CbParams.Loc = (PWCHAR)pArrival->Location;
+ pArrival->CbParams.DevPath = (PWCHAR)pArrival->DevPath;
+ pArrival->CbParams.SerNum = (PWCHAR)pArrival->SerNum;
+ pArrival->CbParams.SerNumMsm = (PWCHAR)pArrival->SerNumMsm;
+ pArrival->CbParams.HwId = (PWCHAR)pArrival->HwId;
+ pArrival->CbParams.ParentDev = (PWCHAR)pArrival->ParentDev;
+ pArrival->CbParams.Flag = ((ULONG)pArrival->Info.Type << 8) |
+ ((ULONG)1 << 4) |
+ ((ULONG)pArrival->BusType << 16) |
+ (ULONG)pArrival->Info.IsQCDriver;
+ if (CallerContext != NULL)
+ {
+ pArrival->UserContext = CallerContext;
+ }
+ fNotifyCb(&(pArrival->CbParams), &pArrival->UserContext);
+ QCD_Printf(" HWID [%ws]\n", pArrival->HwId);
+ }
+ }
+ else // send to announcement thread
+ {
+ CopyMemory((PVOID)pDevice, (PVOID)pArrival, sizeof(QC_DEV_ITEM));
+ pDevice->Context = (PVOID)pArrival;
+ InsertTailList(&(storeBranch->DevItemChain), &pDevice->List);
+ }
+ InsertTailList(&ArrivalList, &pArrival->List); // ArrivalList is ActiveList
+ }
+ else
+ {
+ ZeroMemory(pArrival, sizeof(QC_DEV_ITEM));
+ InsertTailList(&FreeList, &pArrival->List);
+ }
+ } // while
+
+ // Those departed are Intersection==0 on ActiveList
+ // Now, this list also has new arrivals marked with DEV_FLAG_ARRIVAL
+ if (!IsListEmpty(ActiveList))
+ {
+ headOfActive = ActiveList;
+ peekActive = headOfActive->Flink;
+ while (peekActive != headOfActive)
+ {
+ pActive = CONTAINING_RECORD(peekActive, QC_DEV_ITEM, List);
+ peekActive = peekActive->Flink;
+ if (pActive->Info.Flag == DEV_FLAG_NONE)
+ {
+ PQC_DEV_ITEM pDevice;
+
+ pDevice = (PQC_DEV_ITEM)malloc(sizeof(QC_DEV_ITEM));
+ RemoveEntryList(&pActive->List);
+ if ((storeBranch == NULL) || (hAnnouncementThread == 0) || (pDevice == NULL))
+ {
+ if (fNotifyCb != NULL)
+ {
+ pActive->CbParams.DevDesc = (PWCHAR)pActive->DevDesc;
+ pActive->CbParams.DevName = (PCHAR)pActive->DevNameA;
+ pActive->CbParams.IfName = (PWCHAR)pActive->InterfaceName;
+ pActive->CbParams.Loc = (PWCHAR)pActive->Location;
+ pActive->CbParams.DevPath = (PWCHAR)pActive->DevPath;
+ pActive->CbParams.SerNum = (PWCHAR)pActive->SerNum;
+ pActive->CbParams.SerNumMsm = (PWCHAR)pActive->SerNumMsm;
+ pActive->CbParams.HwId = (PWCHAR)pActive->HwId;
+ pActive->CbParams.ParentDev = (PWCHAR)pActive->ParentDev;
+ pActive->CbParams.Flag = ((ULONG)pActive->Info.Type << 8) |
+ ((ULONG)pActive->BusType << 16) |
+ pActive->Info.IsQCDriver;
+
+ if (CallerContext != NULL)
+ {
+ pActive->UserContext = CallerContext;
+ }
+ fNotifyCb
+ (
+ &(pActive->CbParams),
+ &pActive->UserContext
+ );
+ QCD_Printf(" HWID [%ws]\n", pActive->HwId);
+ }
+ }
+ else
+ {
+ pActive->Info.Flag = DEV_FLAG_DEPARTURE;
+ CopyMemory((PVOID)pDevice, (PVOID)pActive, sizeof(QC_DEV_ITEM));
+ InsertTailList(&(storeBranch->DevItemChain), &pDevice->List);
+ }
+ ZeroMemory(pActive, sizeof(QC_DEV_ITEM));
+ InsertTailList(&FreeList, &pActive->List);
+ }
+ else
+ {
+ pActive->Info.Flag = DEV_FLAG_NONE; // reset
+ }
+ } // while
+ } // if
+
+ LeaveCriticalSection(&opLock);
+
+ // put records to NotifyStore
+ if (storeBranch != NULL)
+ {
+ EnterCriticalSection(¬ifyLock);
+ if (!IsListEmpty(&(storeBranch->DevItemChain)))
+ {
+ InsertTailList(&AnnouncementList, &(storeBranch->List));
+ SetEvent(hAnnouncementEvt);
+ }
+ else
+ {
+ InsertHeadList(&NotifyFreeList, &(storeBranch->List));
+ }
+ LeaveCriticalSection(¬ifyLock);
+ }
+ } // TryToAnnounce
+
+ VOID MakeAnnouncement(VOID)
+ {
+ PQC_NOTIFICATION_STORE storeBranch;
+ PQC_DEV_ITEM devItem;
+ PLIST_ENTRY headOfList;
+
+ QCD_Printf("Announcing device:\n");
+ EnterCriticalSection(¬ifyLock);
+ while (!IsListEmpty(&AnnouncementList))
+ {
+ headOfList = RemoveHeadList(&AnnouncementList);
+ storeBranch = CONTAINING_RECORD(headOfList, QC_NOTIFICATION_STORE, List);
+ LeaveCriticalSection(¬ifyLock);
+
+ while (!IsListEmpty(&(storeBranch->DevItemChain)))
+ {
+ headOfList = RemoveHeadList(&(storeBranch->DevItemChain));
+ devItem = CONTAINING_RECORD(headOfList, QC_DEV_ITEM, List);
+ if (fNotifyCb != NULL)
+ {
+ if (devItem->Info.Flag == DEV_FLAG_ARRIVAL)
+ {
+ PQC_DEV_ITEM context = (PQC_DEV_ITEM)(devItem->Context);
+
+ devItem->CbParams.DevDesc = (PWCHAR)devItem->DevDesc;
+ devItem->CbParams.DevName = (PCHAR)devItem->DevNameA;
+ devItem->CbParams.IfName = (PWCHAR)devItem->InterfaceName;
+ devItem->CbParams.Loc = (PWCHAR)devItem->Location;
+ devItem->CbParams.DevPath = (PWCHAR)devItem->DevPath;
+ devItem->CbParams.SerNum = (PWCHAR)devItem->SerNum;
+ devItem->CbParams.SerNumMsm = (PWCHAR)devItem->SerNumMsm;
+ devItem->CbParams.HwId = (PWCHAR)devItem->HwId;
+ devItem->CbParams.ParentDev = (PWCHAR)devItem->ParentDev;
+ devItem->CbParams.Flag = ((ULONG)devItem->Info.Type << 8) |
+ ((ULONG)devItem->BusType << 16) |
+ ((ULONG)1 << 4) |
+ (ULONG)devItem->Info.IsQCDriver;
+ QCD_Printf("ARRIVAL: <%ws> pItem 0x%p - 0x%p - 0x%p\n", (PWCHAR)devItem->DevDesc,
+ devItem, devItem->Context, &(devItem->CbParams));
+ if (CallerContext != NULL)
+ {
+ devItem->UserContext = CallerContext;
+ }
+ fNotifyCb
+ (
+ &(devItem->CbParams),
+ &devItem->UserContext
+ );
+ context->UserContext = devItem->UserContext; // save user context ID
+ QCD_Printf(" HWID [%ws]\n", devItem->HwId);
+ }
+ else // DEV_FLAG_DEPARTURE
+ {
+ devItem->CbParams.DevDesc = (PWCHAR)devItem->DevDesc;
+ devItem->CbParams.DevName = (PCHAR)devItem->DevNameA;
+ devItem->CbParams.IfName = (PWCHAR)devItem->InterfaceName;
+ devItem->CbParams.Loc = (PWCHAR)devItem->Location;
+ devItem->CbParams.DevPath = (PWCHAR)devItem->DevPath;
+ devItem->CbParams.SerNum = (PWCHAR)devItem->SerNum;
+ devItem->CbParams.SerNumMsm = (PWCHAR)devItem->SerNumMsm;
+ devItem->CbParams.HwId = (PWCHAR)devItem->HwId;
+ devItem->CbParams.ParentDev = (PWCHAR)devItem->ParentDev;
+ devItem->CbParams.Flag = ((ULONG)devItem->Info.Type << 8) |
+ ((ULONG)devItem->BusType << 16) |
+ (ULONG)devItem->Info.IsQCDriver;
+ QCD_Printf("DEPARTURE: <%ws>\n", (PWCHAR)devItem->DevDesc);
+
+ if (CallerContext != NULL)
+ {
+ devItem->UserContext = CallerContext;
+ }
+ fNotifyCb
+ (
+ &(devItem->CbParams),
+ &devItem->UserContext
+ );
+ QCD_Printf(" HWID [%ws]\n", devItem->HwId);
+ }
+ }
+ free(devItem);
+ }
+ EnterCriticalSection(¬ifyLock);
+ InsertTailList(&NotifyFreeList, &(storeBranch->List)); // recycle
+ }
+ LeaveCriticalSection(¬ifyLock);
+ } // MakeAnnouncement
+
+ VOID MakeAnnouncement_N(VOID)
+ {
+ PQC_NOTIFICATION_STORE storeBranch;
+ PQC_DEV_ITEM devItem;
+ PLIST_ENTRY headOfList;
+
+ QCD_Printf("Announcing device:\n");
+ EnterCriticalSection(¬ifyLock);
+ while (!IsListEmpty(&AnnouncementList))
+ {
+ headOfList = RemoveHeadList(&AnnouncementList);
+ storeBranch = CONTAINING_RECORD(headOfList, QC_NOTIFICATION_STORE, List);
+ LeaveCriticalSection(¬ifyLock);
+
+ while (!IsListEmpty(&(storeBranch->DevItemChain)))
+ {
+ headOfList = RemoveHeadList(&(storeBranch->DevItemChain));
+ devItem = CONTAINING_RECORD(headOfList, QC_DEV_ITEM, List);
+ if (fNotifyCb != NULL)
+ {
+ if (devItem->Info.Flag == DEV_FLAG_ARRIVAL)
+ {
+ PQC_DEV_ITEM context = (PQC_DEV_ITEM)(devItem->Context);
+
+ devItem->CbParams.DevDesc = (PWCHAR)devItem->DevDesc;
+ devItem->CbParams.DevName = (PCHAR)devItem->DevNameA;
+ devItem->CbParams.IfName = (PWCHAR)devItem->InterfaceName;
+ devItem->CbParams.Loc = (PWCHAR)devItem->Location;
+ devItem->CbParams.DevPath = (PWCHAR)devItem->DevPath;
+ devItem->CbParams.SerNum = (PWCHAR)devItem->SerNum;
+ devItem->CbParams.SerNumMsm = (PWCHAR)devItem->SerNumMsm;
+ devItem->CbParams.HwId = (PWCHAR)devItem->HwId;
+ devItem->CbParams.ParentDev = (PWCHAR)devItem->ParentDev;
+ devItem->CbParams.Flag = (((ULONG)devItem->Info.Type << 8) |
+ ((ULONG)1 << 4) |
+ (ULONG)devItem->Info.IsQCDriver);
+ QCD_Printf("ARRIVAL: <%ws>\n", (PWCHAR)devItem->DevDesc);
+ if (CallerContext != NULL)
+ {
+ devItem->UserContext = CallerContext;
+ }
+ fNotifyCb
+ (
+ &(devItem->CbParams),
+ &devItem->UserContext
+ );
+ context->UserContext = devItem->UserContext; // save user context ID
+ QCD_Printf(" HWID [%ws]\n", devItem->HwId);
+ }
+ else // DEV_FLAG_DEPARTURE
+ {
+ devItem->CbParams.DevDesc = (PWCHAR)devItem->DevDesc;
+ devItem->CbParams.DevName = (PCHAR)devItem->DevNameA;
+ devItem->CbParams.IfName = (PWCHAR)devItem->InterfaceName;
+ devItem->CbParams.Loc = (PWCHAR)devItem->Location;
+ devItem->CbParams.DevPath = (PWCHAR)devItem->DevPath;
+ devItem->CbParams.SerNum = (PWCHAR)devItem->SerNum;
+ devItem->CbParams.SerNumMsm = (PWCHAR)devItem->SerNumMsm;
+ devItem->CbParams.HwId = (PWCHAR)devItem->HwId;
+ devItem->CbParams.ParentDev = (PWCHAR)devItem->ParentDev;
+ devItem->CbParams.Flag = (((ULONG)devItem->Info.Type << 8) | (ULONG)devItem->Info.IsQCDriver);
+ QCD_Printf("DEPARTURE: <%ws>\n", (PWCHAR)devItem->DevDesc);
+ if (CallerContext != NULL)
+ {
+ devItem->UserContext = CallerContext;
+ }
+ fNotifyCb
+ (
+ &(devItem->CbParams),
+ &devItem->UserContext
+ );
+ QCD_Printf(" HWID [%ws]\n", devItem->HwId);
+ }
+ }
+ free(devItem);
+ }
+ EnterCriticalSection(¬ifyLock);
+ InsertTailList(&NotifyFreeList, &(storeBranch->List)); // recycle
+ }
+ LeaveCriticalSection(¬ifyLock);
+ } // MakeAnnouncement_N
+
+ DWORD WINAPI AnnouncementThread(PVOID Context)
+ {
+ DWORD status = WAIT_OBJECT_0;
+
+ while (bMonitorRunning == TRUE) // set in StartDeviceMonitor()
+ {
+ status = WaitForSingleObject(hAnnouncementEvt, 500);
+ if (status == WAIT_OBJECT_0)
+ {
+ if (fNotifyCb_N != NULL)
+ {
+ fNotifyCb_N();
+ }
+ else
+ {
+ MakeAnnouncement();
+ }
+ }
+ }
+ SetEvent(hAnnouncementExitEvt);
+ return 0;
+ } // AnnouncementThread
+
+ BOOL MonitorDeviceChange(VOID)
+ {
+ static WCHAR keyPath[REG_HW_ID_SIZE];
+ LONG entries, result;
+ static DWORD status = WAIT_OBJECT_0;
+ DWORD retCode;
+
+ // QCD_Printf("-->MonitorDeviceChange\n");
+
+ // Make sure events are created only once
+ if ((entries = InterlockedIncrement(&lInitialized)) == 1)
+ {
+ int i;
+
+ for (i = QC_REG_DEVMAP; i < QC_REG_MAX; i++)
+ {
+ hRegChangeEvt[i] = CreateEvent(NULL, FALSE, FALSE, NULL);
+ if (hRegChangeEvt[i] == NULL)
+ {
+ QCD_Printf("CreateEvent failed: 0x%x\n", GetLastError());
+ }
+ else if (i == QC_REG_DEVMAP)
+ {
+ // trigger an immediate 1st-time scan later in this function
+ SetEvent(hRegChangeEvt[i]);
+ }
+ }
+
+ // not useful because ADB driver does not make reg change on arrival/departure
+ StringCchCopy(keyPath, MAX_PATH, TEXT(QC_REG_SW_KEY_ADB));
+ retCode = RegOpenKeyEx
+ (
+ HKEY_LOCAL_MACHINE,
+ keyPath,
+ 0,
+ KEY_READ,
+ &hSysKey[QC_REG_ADB]
+ );
+ if (retCode != ERROR_SUCCESS)
+ {
+ hSysKey[QC_REG_ADB] = 0;
+ QCD_Printf("Error: failed to open reg for SW_ADB\n");
+ }
+
+ StringCchCopy(keyPath, MAX_PATH, TEXT(QC_REG_SW_KEY_USB));
+ retCode = RegOpenKeyEx
+ (
+ HKEY_LOCAL_MACHINE,
+ keyPath,
+ 0,
+ KEY_READ,
+ &hSysKey[QC_REG_USB]
+ );
+ if (retCode != ERROR_SUCCESS)
+ {
+ hSysKey[QC_REG_USB] = 0;
+ QCD_Printf("Error: failed to open reg for SW_USB\n");
+ }
+
+ StringCchCopy(keyPath, MAX_PATH, TEXT(QC_REG_KEY_DEVMAP));
+ retCode = RegOpenKeyEx
+ (
+ HKEY_LOCAL_MACHINE,
+ keyPath,
+ 0,
+ KEY_READ,
+ &hSysKey[QC_REG_DEVMAP]
+ );
+ if (retCode != ERROR_SUCCESS)
+ {
+ hSysKey[QC_REG_DEVMAP] = 0;
+ QCD_Printf("Error: failed to open reg for DEVMAP\n");
+ }
+
+ StringCchCopy(keyPath, MAX_PATH, TEXT(QC_REG_SW_KEY_MDM));
+ retCode = RegOpenKeyEx
+ (
+ HKEY_LOCAL_MACHINE,
+ keyPath,
+ 0,
+ KEY_READ,
+ &hSysKey[QC_REG_MDM]
+ );
+ if (retCode != ERROR_SUCCESS)
+ {
+ hSysKey[QC_REG_MDM] = 0;
+ QCD_Printf("Error: failed to open reg for MDM\n");
+ }
+
+ StringCchCopy(keyPath, MAX_PATH, TEXT(QC_REG_SW_KEY_NET));
+ retCode = RegOpenKeyEx
+ (
+ HKEY_LOCAL_MACHINE,
+ keyPath,
+ 0,
+ KEY_READ,
+ &hSysKey[QC_REG_NET]
+ );
+ if (retCode != ERROR_SUCCESS)
+ {
+ hSysKey[QC_REG_NET] = 0;
+ QCD_Printf("Error: failed to open reg for NET\n");
+ }
+
+ StringCchCopy(keyPath, MAX_PATH, TEXT(QC_REG_SW_KEY_PORTS));
+ retCode = RegOpenKeyEx
+ (
+ HKEY_LOCAL_MACHINE,
+ keyPath,
+ 0,
+ KEY_READ,
+ &hSysKey[QC_REG_PORTS]
+ );
+ if (retCode != ERROR_SUCCESS)
+ {
+ hSysKey[QC_REG_PORTS] = 0;
+ QCD_Printf("Error: failed to open reg for PORTS\n");
+ }
+
+ }
+ else if (entries > 1)
+ {
+ InterlockedDecrement(&lInitialized);
+ }
+
+ if (status != WAIT_TIMEOUT)
+ {
+ int i;
+
+ for (i = QC_REG_DEVMAP; i < QC_REG_MAX; i++)
+ {
+ if (hSysKey[i] != 0)
+ {
+ result = RegNotifyChangeKeyValue
+ (
+ hSysKey[i],
+ TRUE,
+ REG_NOTIFY_CHANGE_LAST_SET,
+ hRegChangeEvt[i],
+ TRUE
+ );
+ if (result != ERROR_SUCCESS)
+ {
+ QCD_Printf("Failed to monitor reg location %d - 0x%x\n", i, GetLastError());
+ }
+ }
+ else
+ {
+ QCD_Printf("Skip to monitor reg location %d\n", i);
+ }
+ }
+ status = WaitForMultipleObjects(QC_REG_MAX, hRegChangeEvt, FALSE, FeatureSetting.TimerInterval);
+ // QCD_Printf("Reg change detected: 0x%x\n", status);
+ }
+ else
+ {
+ SetEvent(hRegChangeEvt[0]); // scan on timer
+ status = WaitForMultipleObjects(QC_REG_MAX, hRegChangeEvt, FALSE, FeatureSetting.TimerInterval);
+ // QCD_Printf("timer off: 0x%x\n", status);
+ }
+
+ // QCD_Printf("<--MonitorDeviceChange: ST 0x%x\n", status);
+
+ return status; // WAIT_OBJECT_0);
+
+ } // MonitorDeviceChange
+
+ PBYTE GetPortNameFromHwKey(PTSTR LocationPath)
+ {
+ WCHAR keyPath[REG_HW_ID_SIZE];
+ static BYTE regValue[QC_MAX_VALUE_NAME];
+ DWORD regLen = QC_MAX_VALUE_NAME;
+ HKEY hHwKey;
+ DWORD retCode;
+
+ StringCchCopy(keyPath, MAX_PATH, TEXT(QC_REG_HW_KEY_PREF));
+ StringCchCat(keyPath, MAX_PATH, LocationPath);
+ StringCchCat(keyPath, MAX_PATH, TEXT(QC_REG_HW_KEY_PARAM));
+
+ retCode = RegOpenKeyEx
+ (
+ HKEY_LOCAL_MACHINE,
+ keyPath,
+ 0,
+ KEY_READ,
+ &hHwKey
+ );
+
+ if (retCode == ERROR_SUCCESS)
+ {
+ retCode = RegQueryValueEx
+ (
+ hHwKey,
+ TEXT(DEV_PORT_NAME),
+ NULL, // reserved
+ NULL, // returned type
+ (LPBYTE)regValue,
+ ®Len
+ );
+ RegCloseKey(hHwKey);
+
+ if (retCode == ERROR_SUCCESS)
+ {
+ return (PBYTE)regValue;
+ }
+ }
+ return NULL;
+ } // GetPortNameFromHwKey
+
+ VOID GetNetInterfaceName(PTSTR NetCfgInstanceId, PTSTR IfName)
+ {
+ HKEY hTestKey;
+ CHAR fullKeyName[QC_MAX_VALUE_NAME];
+
+ StringCchCopy((PTSTR)fullKeyName, QC_MAX_VALUE_NAME / 2, TEXT(QC_NET_CONNECTION_REG_KEY));
+ StringCchCat((PTSTR)fullKeyName, QC_MAX_VALUE_NAME / 2, NetCfgInstanceId);
+ StringCchCat((PTSTR)fullKeyName, QC_MAX_VALUE_NAME / 2, TEXT("\\"));
+ StringCchCat((PTSTR)fullKeyName, QC_MAX_VALUE_NAME / 2, TEXT("Connection"));
+
+ if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, (PTSTR)fullKeyName, 0, KEY_READ, &hTestKey) == ERROR_SUCCESS)
+ {
+ DWORD retCode;
+ DWORD valueNameLen = QC_MAX_VALUE_NAME / 2;
+
+ retCode = RegQueryValueEx
+ (
+ hTestKey,
+ TEXT("Name"),
+ NULL, // reserved
+ NULL, // returned type
+ (LPBYTE)IfName,
+ &valueNameLen
+ );
+
+ if (retCode == ERROR_SUCCESS)
+ {
+ // QCD_Printf("GetNetInterfaceNames: InterfaceName: [%ws]\n", IfName);
+ }
+ else
+ {
+ QCD_Printf("GetNetInterfaceNames: error 0x%x\n", GetLastError());
+ }
+
+ RegCloseKey(hTestKey);
+ }
+ else
+ {
+ QCD_Printf("GetNetInterfaceName: faild to open registry 0x%x\n", GetLastError());
+ }
+ } // GetNetInterfaceName
+
+ void MatchCaseSerNum(PTSTR InstanceId, PTSTR pSerNumPos, PVOID SerNum)
+ {
+ //Store serial number in instance id as serial number
+ StringCchCopy((PTSTR)SerNum, 128, pSerNumPos);
+
+ HKEY hSwKey;
+ WCHAR keyPath[MAX_PATH];
+ CHAR matchingDeviceId[QC_MAX_VALUE_NAME];
+
+ StringCchCopy(keyPath, MAX_PATH, TEXT(QC_REG_HW_KEY_PREF));
+ ZeroMemory(matchingDeviceId, QC_MAX_VALUE_NAME);
+ StringCchCopyN((PTSTR)matchingDeviceId, QC_MAX_VALUE_NAME / 2, InstanceId, lstrlen(InstanceId) - lstrlen(pSerNumPos));
+ StringCchCat(keyPath, MAX_PATH, (PTSTR)matchingDeviceId);
+
+ DWORD retCode = RegOpenKeyEx
+ (
+ HKEY_LOCAL_MACHINE,
+ keyPath,
+ 0,
+ KEY_READ,
+ &hSwKey
+ );
+ if (retCode == ERROR_SUCCESS)
+ {
+ WCHAR keyName[256];//Max key name is 256 WORD
+ DWORD keyLength = 512;//Max key name is 512 CHAR
+ WCHAR matchingSerNum[128];//Serial Number is 128 WORD
+ WCHAR orgSerNumUpper[128];//Serial Number is 128 WORD
+ StringCchCopy(orgSerNumUpper, 128, pSerNumPos);
+ _wcsupr_s(orgSerNumUpper, 128);
+ DWORD index = 0;
+ //Go through the enum key
+ while (RegEnumKeyEx(hSwKey, index, keyName, &keyLength, NULL, NULL, NULL, NULL) == ERROR_SUCCESS)
+ {
+ //Compare and see if the subkey match the instand id in Capital
+ StringCchCopy(matchingSerNum, 128, keyName);
+ _wcsupr_s(matchingSerNum, 128);
+ if (0 == StrCmp(matchingSerNum, orgSerNumUpper))
+ {
+ //Store key name as serial number
+ StringCchCopy((PTSTR)SerNum, 128, keyName);
+ break;
+ }
+ index++;
+ keyLength = 512;
+ ZeroMemory(keyName, keyLength);
+ }
+ RegCloseKey(hSwKey);
+ }
+ }
+
+ PBYTE ValidateDevice
+ (
+ PTSTR DriverKey,
+ PVOID InstanceId,
+ PVOID InterfaceName, // optional, interface name of a NIC
+ PVOID SerNum,
+ PVOID SerNumMsm,
+ PVOID ParentDev,
+ PULONG Protocol,
+ PLONG Mtu,
+ int DevType,
+ BOOL *IsActive,
+ BOOL *IsQCDriver,
+ ULONG DevStatus
+ )
+ {
+ WCHAR keyPath[REG_HW_ID_SIZE];
+ static BYTE regValue[QC_MAX_VALUE_NAME];
+ DWORD regLen = QC_MAX_VALUE_NAME;
+ HKEY hSwKey;
+ BOOL bResult = FALSE;
+ DWORD retCode;
+
+ StringCchCopy(keyPath, MAX_PATH, TEXT(QC_REG_SW_KEY));
+ StringCchCat(keyPath, MAX_PATH, DriverKey);
+
+ // QCD_Printf("ValidateDevice: DriverKey <%ws>\n", keyPath);
+
+ // Open device software key
+ retCode = RegOpenKeyEx
+ (
+ HKEY_LOCAL_MACHINE,
+ keyPath,
+ 0,
+ KEY_READ,
+ &hSwKey
+ );
+
+ if (retCode == ERROR_SUCCESS)
+ {
+ retCode = RegQueryValueEx
+ (
+ hSwKey,
+ TEXT(QC_SPEC_STAMP),
+ NULL, // reserved
+ NULL, // returned type
+ (LPBYTE)regValue,
+ ®Len
+ );
+ if (retCode == ERROR_SUCCESS)
+ {
+ DWORD v = *((DWORD *)regValue);
+ // QCD_Printf(" QC_SPEC_STAMP: %u for <%ws>\n", v, keyPath);
+
+ if (v == 1)
+ {
+ *IsActive = TRUE;
+ FeatureSetting.TimerInterval = 1000; // INFINITE; // ADB device needs timer
+ }
+ else
+ {
+ *IsActive = FALSE;
+ }
+
+ if ((DevStatus & DN_STARTED) != 0)
+ {
+ *IsActive = TRUE; // Best Effort -- let system devnode status make final call but may not be reliable
+ }
+ *IsQCDriver = TRUE;
+ }
+ else
+ {
+ // not a QC driver
+ *IsActive = TRUE;
+ *IsQCDriver = FALSE;
+ }
+
+ regLen = 256;
+ retCode = RegQueryValueEx
+ (
+ hSwKey,
+ TEXT(QC_SPEC_SERNUM),
+ NULL, // reserved
+ NULL, // returned type
+ (LPBYTE)SerNum,
+ ®Len
+ );
+ if (retCode != ERROR_SUCCESS)
+ {
+ // or we use InstanceId instead for USB single function device (for Fastboot)
+ if ((InstanceId != NULL) &&
+ (StrStrW((PTSTR)InstanceId, TEXT(BUS_TEST_USB)) != NULL) && // is a USB device
+ (StrStrW((PTSTR)InstanceId, TEXT("&MI_")) == NULL)) // is not a composite device
+ {
+ PTSTR p = (PTSTR)InstanceId, p1;
+
+ while (p1 = StrStrW(p, TEXT("\\")))
+ {
+ p = ++p1;
+ }
+
+ if (StrStrW(p, TEXT("&")) != NULL)
+ {
+ ZeroMemory(SerNum, 256);
+ QCD_Printf("ValidateDevice: no SerNum retrieved from <%ws>\n", p);
+ }
+ else
+ {
+ MatchCaseSerNum((PTSTR)InstanceId, p, SerNum);
+ QCD_Printf("ValidateDevice: use <%ws> as SerNum\n", (PTSTR)SerNum);
+ }
+ }
+ else
+ {
+ ZeroMemory(SerNum, 256);
+ }
+ }
+
+ regLen = 256;
+ retCode = RegQueryValueEx
+ (
+ hSwKey,
+ TEXT(QC_SPEC_SERNUM_MSM),
+ NULL, // reserved
+ NULL, // returned type
+ (LPBYTE)SerNumMsm,
+ ®Len
+ );
+ if (retCode != ERROR_SUCCESS)
+ {
+ ZeroMemory(SerNumMsm, 256);
+ }
+
+ // retrieve protocol code
+ regLen = sizeof(ULONG);
+ retCode = RegQueryValueEx
+ (
+ hSwKey,
+ TEXT(QC_SPEC_PROTOC),
+ NULL, // reserved
+ NULL, // returned type
+ (LPBYTE)Protocol,
+ ®Len
+ );
+ if (retCode != ERROR_SUCCESS)
+ {
+ *Protocol = 0;
+ }
+
+ // retrieve parent device name
+ regLen = 256;
+ retCode = RegQueryValueEx
+ (
+ hSwKey,
+ TEXT(QC_SPEC_PARENT_DEV),
+ NULL, // reserved
+ NULL, // returned type
+ (LPBYTE)ParentDev,
+ ®Len
+ );
+ if (retCode != ERROR_SUCCESS)
+ {
+ ZeroMemory(ParentDev, 256);
+ }
+
+ // Retrieve the symbolic link name
+ keyPath[0] = 0;
+ regLen = QC_MAX_VALUE_NAME;
+ if ((DevType == QC_DEV_TYPE_PORTS) || (DevType == QC_DEV_TYPE_MDM))
+ {
+ if (*IsQCDriver == TRUE)
+ {
+ retCode = RegQueryValueEx
+ (
+ hSwKey,
+ TEXT(QC_SPEC),
+ NULL, // reserved
+ NULL, // returned type
+ (LPBYTE)regValue,
+ ®Len
+ );
+ }
+ else if ((InstanceId != NULL) && ((FeatureSetting.User.Settings & DEV_FEATURE_INCLUDE_NONE_QC_PORTS) != 0))
+ {
+ LPBYTE p;
+
+ p = GetPortNameFromHwKey((PTSTR)InstanceId);
+ if (p != NULL)
+ {
+ StringCchCopy((PTSTR)regValue, QC_MAX_VALUE_NAME / 2, (PTSTR)p);
+ retCode = ERROR_SUCCESS;
+ }
+ else
+ {
+ retCode = ERROR_FILE_NOT_FOUND;
+ }
+ }
+ else
+ {
+ retCode = ERROR_FILE_NOT_FOUND;
+ }
+ }
+ else if (DevType == QC_DEV_TYPE_NET)
+ {
+ retCode = RegQueryValueEx
+ (
+ hSwKey,
+ TEXT("NetCfgInstanceId"),
+ NULL, // reserved
+ NULL, // returned type
+ (LPBYTE)regValue,
+ ®Len
+ );
+ if (InterfaceName != NULL)
+ {
+ GetNetInterfaceName((PTSTR)regValue, (PTSTR)InterfaceName);
+ }
+
+ regLen = sizeof(LONG);
+ retCode = RegQueryValueEx
+ (
+ hSwKey,
+ TEXT(QC_SPEC_MTU),
+ NULL, // reserved
+ NULL, // returned type
+ (LPBYTE)Mtu,
+ ®Len
+ );
+ if (retCode != ERROR_SUCCESS)
+ {
+ *Mtu = 0;
+ }
+ regLen = QC_MAX_VALUE_NAME;
+ retCode = RegQueryValueEx
+ (
+ hSwKey,
+ TEXT(QC_SPEC_NET),
+ NULL, // reserved
+ NULL, // returned type
+ (LPBYTE)regValue,
+ ®Len
+ );
+ if (retCode == ERROR_SUCCESS)
+ {
+ *IsActive = TRUE;
+ }
+ else
+ {
+ if ((InstanceId != NULL) &&
+ (StrStrW((PTSTR)InstanceId, TEXT("VID_05C6")) != NULL) && // is a QC USB device
+ (StrStrW((PTSTR)InstanceId, TEXT("&MI_")) != NULL)) // is a composite device
+ {
+ *IsActive = TRUE;
+ }
+ else
+ {
+ *IsActive = FALSE;
+ // QCD_Printf("ValidateDevice: RemoveDevice <%ws>\n", InstanceId);
+ }
+ }
+
+ // Inspect modem SSR
+ if (*IsQCDriver == TRUE)
+ {
+ DWORD ssrRetCode;
+ BYTE ssrValue[QC_MAX_VALUE_NAME];
+
+ DWORD ssrLen = QC_MAX_VALUE_NAME;
+ ssrRetCode = RegQueryValueEx
+ (
+ hSwKey,
+ TEXT(QC_SPEC_SSR),
+ NULL, // reserved
+ NULL, // returned type
+ (LPBYTE)ssrValue,
+ &ssrLen
+ );
+ if (ssrRetCode == ERROR_SUCCESS)
+ {
+ DWORD v = *((DWORD *)ssrValue);
+
+ if (v == 1)
+ {
+ *IsActive = FALSE;
+ QCD_Printf("ValidateDevice: SSR detected for <%ws>\n", InstanceId);
+ }
+ else
+ {
+ // *isActive should remain what has been set so far
+ QCD_Printf("ValidateDevice: SSR changed for <%ws> IsActive %d/%d\n", InstanceId, *IsActive, v);
+ }
+ }
+ else
+ {
+ // *isActive should remain what has been set so far
+ // QCD_Printf("ValidateDevice: SSR does not exist for <%ws> IsActive %d\n", InstanceId, *IsActive);
+ }
+ }
+ }
+ else if (DevType == QC_DEV_TYPE_USB)
+ {
+ // for QDSS/DPL online, use FriendlyName, so return NULL
+ retCode = ERROR_FILE_NOT_FOUND; // just assign an error code
+ }
+ else
+ {
+ retCode = ERROR_FILE_NOT_FOUND; // just assign an error code
+ }
+
+ if (retCode == ERROR_SUCCESS)
+ {
+ bResult = TRUE;
+ }
+ }
+ RegCloseKey(hSwKey);
+
+ if (bResult == TRUE)
+ {
+ PBYTE pDevNameString = (PBYTE)regValue;
+
+ if (DevType == QC_DEV_TYPE_NET)
+ {
+ PCHAR pStart, pEnd;
+
+ pStart = (PCHAR)regValue;
+ pEnd = pStart + regLen;
+ while (pEnd > pStart)
+ {
+ if (*pEnd != 0x5C) // look for '\'
+ {
+ pEnd--;
+ }
+ else
+ {
+ pEnd += 2;
+ pDevNameString = (PBYTE)pEnd;
+ break;
+ }
+ }
+ }
+ return pDevNameString;
+ }
+
+ return NULL;
+ } // ValidateDevice
+
+ BOOL IsHexNumber(PVOID NumBuffer)
+ {
+ PCHAR p = (PCHAR)NumBuffer;
+ BOOL retVal = TRUE;
+
+ while ((*p != 0) && (*(p + 1) == 0))
+ {
+ if ((*p >= '0' && *p <= '9') ||
+ (*p >= 'A' && *p <= 'F') ||
+ (*p >= 'a' && *p <= 'f'))
+ {
+ // QCD_Printf("examine: %02X/%c\n", *p, *p);
+ }
+ else
+ {
+ retVal = FALSE;
+ break;
+ }
+ p += 2;
+ }
+ return (p == NumBuffer ? FALSE : retVal);
+ } // IsHexNumber
+
+ BOOL FindParent(PVOID HwInstanceId, PVOID ParentDev, PVOID PotentialSerNum)
+ {
+ WCHAR keyPath[REG_HW_ID_SIZE];
+ CHAR container[256], parentContainer[256];
+ DWORD containerLen = 256;
+ HKEY hHwKey;
+ DWORD retVal;
+ BOOL result = FALSE;
+
+ StringCchCopy(keyPath, MAX_PATH, TEXT(QC_REG_HW_KEY_PREF));
+ StringCchCat(keyPath, MAX_PATH, (PTSTR)HwInstanceId);
+
+ // QCD_Printf("FindParent: path <%ws>\n", keyPath);
+
+ // Open instance and retrieve ContainerID
+ if (RegOpenKeyExW
+ (
+ HKEY_LOCAL_MACHINE,
+ keyPath,
+ 0,
+ KEY_READ,
+ &hHwKey
+ ) == ERROR_SUCCESS
+ )
+ {
+ ZeroMemory(container, containerLen);
+ retVal = RegQueryValueExW
+ (
+ hHwKey,
+ TEXT("ContainerID"),
+ NULL,
+ NULL,
+ (LPBYTE)container,
+ &containerLen
+ );
+ RegCloseKey(hHwKey);
+
+ if (retVal == ERROR_SUCCESS)
+ {
+ PWSTR pEnd;
+ LSTATUS openSt;
+
+ // QCD_Printf("FindParent: Container <%ws>\n", (PTSTR)container);
+
+ // revise keyPath
+ if ((pEnd = StrStrW((PTSTR)keyPath, TEXT("Enum\\USB\\VID_"))) != NULL)
+ {
+ pEnd += 26;
+ *pEnd = 0;
+ // QCD_Printf("FindParent: Rev-path <%ws>\n", keyPath);
+ openSt = RegOpenKeyExW(HKEY_LOCAL_MACHINE, keyPath, KEY_ENUMERATE_SUB_KEYS, KEY_READ, &hHwKey);
+ if (openSt == ERROR_SUCCESS)
+ {
+ DWORD idx = 0;
+ CHAR subKey[512];
+ DWORD subKeyLen = 512;
+
+ while (TRUE)
+ {
+ ZeroMemory(subKey, containerLen);
+ subKeyLen = 512;
+ retVal = RegEnumKeyEx
+ (
+ hHwKey, idx,
+ (LPWSTR)subKey, &subKeyLen,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+ );
+ if (retVal == ERROR_SUCCESS)
+ {
+ HKEY hSubKey;
+
+ // open the subkey
+ // QCD_Printf("FindParent: iteration %d <%ws> ret %d\n", idx, (LPWSTR)subKey, retVal);
+ openSt = RegOpenKeyExW(hHwKey, (LPWSTR)subKey, 0, KEY_READ, &hSubKey);
+ if (openSt == ERROR_SUCCESS)
+ {
+ containerLen = 256;
+ ZeroMemory(parentContainer, containerLen);
+ retVal = RegQueryValueExW
+ (
+ hSubKey, TEXT("ContainerID"), NULL, NULL,
+ (LPBYTE)parentContainer, &containerLen
+ );
+ if (retVal == ERROR_SUCCESS)
+ {
+ // QCD_Printf("FindParent: ParentContainer <%ws>\n", parentContainer);
+ if (StrCmpW((PCWSTR)parentContainer, (PCWSTR)container) == 0)
+ {
+ DWORD devLen = 256;
+
+ retVal = RegQueryValueExW
+ (
+ hSubKey, TEXT("FriendlyName"), NULL, NULL,
+ (LPBYTE)ParentDev, &devLen
+ );
+ if (retVal == ERROR_SUCCESS)
+ {
+ QCD_Printf("FindParent: Parent found <%ws> SubKey <%ws>\n", (PTSTR)ParentDev, subKey);
+ if (IsHexNumber((PVOID)subKey) == TRUE)
+ {
+ StringCchCopy((PTSTR)PotentialSerNum, 128, (PTSTR)subKey);
+ }
+ result = TRUE;
+ }
+ else
+ {
+ QCD_Printf("FindParent: FriendlyName failure 0x%x\n", GetLastError());
+ }
+ }
+ }
+ RegCloseKey(hSubKey);
+ }
+ }
+ else
+ {
+ // until retVal == ERROR_NO_MORE_ITEMS (259)
+ QCD_Printf("FindParent: Rev-path <%ws> iteration %d error 0x%x Ret %d\n", keyPath, idx, GetLastError(), retVal);
+ break; // out of the loop
+ }
+ idx++;
+ } // while
+
+ RegCloseKey(hHwKey);
+ }
+ }
+ }
+ }
+
+ return result;
+ } // FindParent
+
+ VOID ScanDevices(VOID)
+ {
+ HDEVINFO devInfoHandle = INVALID_HANDLE_VALUE;
+ SP_DEVINFO_DATA devInfoData;
+ DWORD memberIdx = 0;
+ CHAR devClass[REG_HW_ID_SIZE];
+ CHAR driverKey[REG_HW_ID_SIZE];
+ CHAR hwId[QC_MAX_VALUE_NAME];
+ CHAR comptId[QC_MAX_VALUE_NAME];
+ CHAR instanceId[512];
+ CHAR potentialSerNum[256];
+ CHAR serNum[256];
+ CHAR serNumMsm[256];
+ CHAR parentDev[256];
+ CHAR ifName[QC_MAX_VALUE_NAME];
+ CHAR friendlyName[QC_MAX_VALUE_NAME];
+ CHAR location[QC_MAX_VALUE_NAME];
+ CHAR devPath[QC_MAX_VALUE_NAME];
+ LONG mtu;
+ ULONG funcProtocol;
+ DWORD requiredSize;
+ BOOL bResult, bHwIdOK;
+ BOOL bMatch, bExclude;
+ UCHAR devType = QC_DEV_TYPE_NONE, busType = QC_DEV_BUS_TYPE_NONE;
+ PBYTE pDevName = NULL;
+ BOOL bActive = FALSE, bQCDriver = FALSE, bInstanceIdOK = FALSE;
+ PQC_DEV_ITEM pItem = NULL;
+ PTSTR pSwIdx = NULL;
+ BOOL bAdbDetected;
+ CONFIGRET cmRet;
+ ULONG devStatus, problemNum;
+
+
+ devInfoHandle = SetupDiGetClassDevsEx
+ (
+ NULL,
+ NULL, // TEXT("USB")
+ NULL,
+ (DIGCF_PRESENT | DIGCF_ALLCLASSES),
+ NULL,
+ NULL, // Machine,
+ NULL
+ );
+ if (devInfoHandle == INVALID_HANDLE_VALUE)
+ {
+ QCD_Printf("SetupDiGetClassDevsEx returned no handle\n");
+ return;
+ }
+
+ devInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
+ while (SetupDiEnumDeviceInfo(devInfoHandle, memberIdx, &devInfoData) == TRUE)
+ {
+ busType = QC_DEV_BUS_TYPE_NONE;
+ pDevName = NULL;
+ bInstanceIdOK = FALSE;
+ bHwIdOK = FALSE;
+ bMatch = bExclude = FALSE;
+ ZeroMemory(devClass, REG_HW_ID_SIZE);
+ ZeroMemory(driverKey, REG_HW_ID_SIZE);
+ ZeroMemory(hwId, QC_MAX_VALUE_NAME);
+ ZeroMemory(instanceId, 512);
+ ZeroMemory(ifName, QC_MAX_VALUE_NAME);
+ ZeroMemory(friendlyName, QC_MAX_VALUE_NAME);
+ ZeroMemory(location, QC_MAX_VALUE_NAME);
+ ZeroMemory(devPath, QC_MAX_VALUE_NAME);
+ ZeroMemory(potentialSerNum, 256);
+ ZeroMemory(serNum, 256);
+ ZeroMemory(serNumMsm, 256);
+ ZeroMemory(parentDev, 256);
+ bAdbDetected = FALSE;
+ devType = QC_DEV_TYPE_NONE;
+ pSwIdx = NULL;
+ devStatus = problemNum = 0;
+
+ bResult = SetupDiGetDeviceRegistryProperty
+ (
+ devInfoHandle,
+ &devInfoData,
+ SPDRP_CLASS,
+ NULL,
+ (LPBYTE)devClass,
+ REG_HW_ID_SIZE,
+ &requiredSize
+ );
+ if (bResult == FALSE)
+ {
+ // QCD_Printf("No class info returned\n");
+ memberIdx++;
+ continue;
+ }
+ else
+ {
+ CharUpper((PTSTR)devClass);
+ if (((FeatureSetting.User.DeviceClass & DEV_CLASS_NET) != 0) ||
+ ((FeatureSetting.User.DeviceClass & DEV_CLASS_MDM) != 0) ||
+ ((FeatureSetting.User.DeviceClass & DEV_CLASS_PORTS) != 0) ||
+ ((FeatureSetting.User.DeviceClass & DEV_CLASS_USB) != 0) ||
+ ((FeatureSetting.User.DeviceClass & DEV_CLASS_ADB) != 0))
+ {
+ // QCD_Printf("Inspecting DevClass <%ws>\n", (PTSTR)devClass);
+ if (StrStrW((PTSTR)devClass, TEXT(D_CLASS_NET)) != NULL)
+ {
+ // QCD_Printf("NET class <%ws>\n", (PTSTR)devClass);
+ devType = QC_DEV_TYPE_NET;
+ bMatch = TRUE;
+ }
+ else if (StrStrW((PTSTR)devClass, TEXT(D_CLASS_PORTS)) != NULL)
+ {
+ // QCD_Printf("PORTS class <%ws>\n", (PTSTR)devClass);
+ devType = QC_DEV_TYPE_PORTS;
+ bMatch = TRUE;
+ }
+ else if (StrStrW((PTSTR)devClass, TEXT(D_CLASS_MODEM)) != NULL)
+ {
+ // QCD_Printf("MDM class <%ws>\n", (PTSTR)devClass);
+ devType = QC_DEV_TYPE_MDM;
+ bMatch = TRUE;
+ }
+ else if (StrStrW((PTSTR)devClass, TEXT(D_CLASS_ADB)) != NULL)
+ {
+ QCD_Printf("ADB class <%ws>\n", (PTSTR)devClass);
+ devType = QC_DEV_TYPE_ADB;
+ bAdbDetected = TRUE;
+ bMatch = TRUE;
+ }
+ else if (StrStrW((PTSTR)devClass, TEXT(D_CLASS_USBDEV)) != NULL)
+ {
+ QCD_Printf("USBDEV class <%ws>\n", (PTSTR)devClass);
+ devType = QC_DEV_TYPE_USB;
+ bMatch = TRUE;
+ }
+ }
+
+ bResult = SetupDiGetDeviceRegistryProperty
+ (
+ devInfoHandle,
+ &devInfoData,
+ SPDRP_HARDWAREID,
+ NULL,
+ (LPBYTE)hwId,
+ QC_MAX_VALUE_NAME,
+ &requiredSize
+ );
+ if (bResult == TRUE)
+ {
+ bHwIdOK = TRUE;
+ }
+
+ bResult = SetupDiGetDeviceRegistryProperty
+ (
+ devInfoHandle,
+ &devInfoData,
+ SPDRP_COMPATIBLEIDS,
+ NULL,
+ (LPBYTE)comptId,
+ QC_MAX_VALUE_NAME,
+ &requiredSize
+ );
+
+ if (bResult == TRUE)
+ {
+ // MBIM
+ if (StrStrW((PTSTR)comptId, TEXT("USB\\Class_02&SubClass_0e")) != NULL)
+ {
+ devType = QC_DEV_TYPE_MBIM;
+ }
+ // RNDIS
+ else if (StrStrW((PTSTR)comptId, TEXT("USB\\MS_COMP_RNDIS")) != NULL)
+ {
+ devType = QC_DEV_TYPE_RNDIS;
+ }
+ else if (StrStrW((PTSTR)comptId, TEXT("USB\\Class_EF&SubClass_04&Prot_01")) != NULL)
+ {
+ devType = QC_DEV_TYPE_RNDIS;
+ }
+ }
+
+ // filter by USB VID
+ if (bMatch == TRUE)
+ {
+ if ((FeatureSetting.User.Settings & DEV_FEATURE_SCAN_USB_WITH_VID) != 0)
+ {
+ if (StrStrW((PTSTR)hwId, (PTSTR)TEXT(BUS_TEST_USB)) != NULL)
+ {
+ if (StrStrW((PTSTR)hwId, (PTSTR)FeatureSetting.VID) != NULL)
+ {
+ // match VID
+ QCD_Printf("USB VID matched <%ws>\n", (PTSTR)hwId);
+ }
+ else
+ {
+ bMatch = FALSE;
+ }
+ }
+ }
+ // QCD_Printf("Matched class <%ws>... devType %d\n", (PTSTR)devClass, devType);
+ }
+ }
+
+ if (bMatch == FALSE)
+ {
+ memberIdx++;
+ continue;
+ }
+ else
+ {
+ cmRet = CM_Get_DevNode_Status(&devStatus, &problemNum, devInfoData.DevInst, 0);
+ if (cmRet == CR_SUCCESS)
+ {
+ // if (StrStrW((PTSTR)hwId, TEXT("VID_05C6")) != NULL)
+ // {
+ // QCD_Printf("\tCM_Get_DevNode_Status 0x%x HWID <%ws>\n", devStatus, (PTSTR)hwId);
+ // }
+ if ((devStatus & DN_STARTED) == 0)
+ {
+ QCD_Printf("\tDevice NOT started.\n");
+ memberIdx++;
+ continue;
+ }
+ else
+ {
+ // QCD_Printf("\tDevice started.\n");
+
+ // if (StrStrW((PTSTR)hwId, TEXT("VID_05C6")) != NULL) // limit to QC device for debugging
+ if (StrStrW((PTSTR)hwId, TEXT("USB")) != NULL) // only re-test USB device
+ {
+ // test again after 100ms
+ Sleep(100);
+ devStatus = 0;
+ cmRet = CM_Get_DevNode_Status(&devStatus, &problemNum, devInfoData.DevInst, 0);
+ // QCD_Printf("\t2ND: CM_Get_DevNode_Status 0x%x HWID <%ws>\n", devStatus, (PTSTR)hwId);
+ if ((devStatus & DN_STARTED) == 0)
+ {
+ QCD_Printf("\t2ND: Device NOT started.\n");
+ }
+ } // if
+ }
+ }
+ else
+ {
+ QCD_Printf("\tCM_Get_DevNode_Status failed 0x%x HWID <%ws>\n", cmRet, (PTSTR)hwId);
+ memberIdx++;
+ continue;
+ }
+ }
+
+ // retrieve driver/software key
+ bResult = SetupDiGetDeviceRegistryProperty
+ (
+ devInfoHandle,
+ &devInfoData,
+ SPDRP_DRIVER,
+ NULL,
+ (LPBYTE)driverKey,
+ REG_HW_ID_SIZE,
+ &requiredSize
+ );
+ if (bResult == FALSE)
+ {
+ // QCD_Printf("No driver info returned\n");
+ memberIdx++;
+ continue;
+ }
+ else
+ {
+ pSwIdx = StrStrW((PTSTR)driverKey, TEXT("\\"));
+ if (pSwIdx != NULL)
+ {
+ pSwIdx++;
+ // QCD_Printf("driver key <%ws> IDX <%ws>\n", (PWSTR)driverKey, pSwIdx);
+ }
+ }
+
+ // retrieve deivce name for display
+ bResult = SetupDiGetDeviceRegistryProperty
+ (
+ devInfoHandle,
+ &devInfoData,
+ SPDRP_FRIENDLYNAME,
+ NULL,
+ (LPBYTE)friendlyName,
+ QC_MAX_VALUE_NAME,
+ &requiredSize
+ );
+ if (bResult == FALSE)
+ {
+ SetupDiGetDeviceRegistryProperty
+ (
+ devInfoHandle,
+ &devInfoData,
+ SPDRP_DEVICEDESC,
+ NULL,
+ (LPBYTE)friendlyName,
+ QC_MAX_VALUE_NAME,
+ &requiredSize
+ );
+
+ // QCD_Printf("<%ws> class <%ws> devType %d ADB %d\n", friendlyName, (PTSTR)devClass, devType, bAdbDetected);
+
+ if (bAdbDetected == TRUE)
+ {
+ CHAR custName[QC_MAX_VALUE_NAME];
+ // TODO: for Android, set friendly name
+ StringCchCopy((PTSTR)custName, QC_MAX_VALUE_NAME / 2, (PTSTR)friendlyName);
+ StringCchCat((PTSTR)custName, QC_MAX_VALUE_NAME / 2, TEXT(" ("));
+ StringCchCat((PTSTR)custName, QC_MAX_VALUE_NAME / 2, (PTSTR)pSwIdx);
+ StringCchCat((PTSTR)custName, QC_MAX_VALUE_NAME / 2, TEXT(")"));
+
+ bResult = SetupDiSetDeviceRegistryProperty
+ (
+ devInfoHandle,
+ &devInfoData,
+ SPDRP_FRIENDLYNAME,
+ (LPBYTE)custName,
+ QC_MAX_VALUE_NAME
+ );
+ if (bResult == TRUE)
+ {
+ QCD_Printf("ADB custName <%ws>\n", (PTSTR)custName);
+ StringCchCopy((PTSTR)friendlyName, QC_MAX_VALUE_NAME / 2, (PTSTR)custName);
+ }
+ }
+ }
+
+#ifdef QC_LPC_SUPPORT
+ // LPC_TEST -- add filters
+ if ((StrStrW((PTSTR)friendlyName, TEXT("LPC Device")) == NULL) &&
+ (StrStrW((PTSTR)friendlyName, TEXT("HS-USB Diagnostics")) == NULL))
+ {
+ // QCD_Printf("Skipping <%ws>\n", friendlyName);
+ memberIdx++;
+ continue;
+ }
+#endif
+
+ if ((StrStrW((PTSTR)friendlyName, TEXT("Qualcomm")) != NULL) ||
+ (StrStrW((PTSTR)friendlyName, TEXT("QDSS")) != NULL))
+ {
+ QCD_Printf("<%ws> class <%ws> devType %d ADB %d\n", friendlyName, (PTSTR)devClass, devType, bAdbDetected);
+ if (cmRet == CR_SUCCESS)
+ {
+ if ((devStatus & DN_STARTED) == 0)
+ {
+ QCD_Printf("\tDevice NOT started.\n");
+ }
+ else
+ {
+ QCD_Printf("\tDevice started.\n");
+ }
+ }
+ else
+ {
+ QCD_Printf("\tQDSSDPL: CM_Get_DevNode_Status failed 0x%x\n", cmRet);
+ }
+ }
+
+ bResult = SetupDiGetDeviceRegistryProperty
+ (
+ devInfoHandle,
+ &devInfoData,
+ SPDRP_LOCATION_INFORMATION,
+ NULL,
+ (LPBYTE)location,
+ QC_MAX_VALUE_NAME,
+ &requiredSize
+ );
+
+ if (bResult == FALSE)
+ {
+ if (StrStrW((PTSTR)hwId, (PTSTR)TEXT(BUS_TEST_USB)) != NULL)
+ {
+ QCD_Printf("\tLOCATION_INFORMATION failure: 0x%x\n", GetLastError());
+ }
+ }
+
+ bResult = SetupDiGetDeviceRegistryProperty
+ (
+ devInfoHandle,
+ &devInfoData,
+ SPDRP_LOCATION_PATHS,
+ NULL,
+ (LPBYTE)devPath,
+ QC_MAX_VALUE_NAME,
+ &requiredSize
+ );
+ if (bResult == FALSE)
+ {
+ if (StrStrW((PTSTR)hwId, (PTSTR)TEXT(BUS_TEST_USB)) != NULL)
+ {
+ QCD_Printf("\tLOCATION_PATHS failure: 0x%x\n", GetLastError());
+ }
+ }
+
+ // if location is not available, use devPath
+ if ((location[0] == 0) && (location[1] == 0))
+ {
+ CopyMemory((PVOID)location, (PVOID)devPath, QC_MAX_VALUE_NAME);
+ }
+
+ // Get a dev item
+ EnterCriticalSection(&opLock);
+ if (!IsListEmpty(&FreeList))
+ {
+ PLIST_ENTRY pEntry;
+
+ pEntry = RemoveHeadList(&FreeList);
+ pItem = CONTAINING_RECORD(pEntry, QC_DEV_ITEM, List);
+ LeaveCriticalSection(&opLock);
+ pItem->Info.Type = devType;
+ }
+ else
+ {
+ QCD_Printf("CRITICAL: no pItem for dev\n");
+ LeaveCriticalSection(&opLock);
+ pItem = NULL;
+ }
+
+ requiredSize = 0;
+ bInstanceIdOK = SetupDiGetDeviceInstanceId
+ (
+ devInfoHandle,
+ &devInfoData,
+ (PWSTR)instanceId,
+ 256,
+ &requiredSize
+ );
+
+ if (bInstanceIdOK == TRUE)
+ {
+ if (StrStrW((PTSTR)instanceId, TEXT(BUS_TEST_USB)) != NULL)
+ {
+ busType = QC_DEV_BUS_TYPE_USB;
+ }
+ else if (StrStrW((PTSTR)instanceId, TEXT(BUS_TEST_PCI)) != NULL)
+ {
+ busType = QC_DEV_BUS_TYPE_PCI;
+ }
+ else if (StrStrW((PTSTR)instanceId, TEXT(BUS_TEST_PCIE)) != NULL)
+ {
+ busType = QC_DEV_BUS_TYPE_PCIE;
+ }
+ // QCD_Printf("ScanDevices: <%ws> BusType (%d) <%ws>\n", (PWSTR)instanceId, busType, (PTSTR)friendlyName);
+ }
+
+ if ((bInstanceIdOK == TRUE) && ((devType == QC_DEV_TYPE_PORTS) || (devType == QC_DEV_TYPE_MDM)))
+ {
+
+ pDevName = ValidateDevice
+ (
+ (PTSTR)driverKey,
+ (PVOID)instanceId,
+ (PVOID)ifName,
+ (PVOID)serNum,
+ (PVOID)serNumMsm,
+ (PVOID)parentDev,
+ &funcProtocol,
+ &mtu,
+ devType,
+ &bActive,
+ &bQCDriver,
+ devStatus
+ );
+ }
+ else
+ {
+ pDevName = ValidateDevice
+ (
+ (PTSTR)driverKey,
+ (PVOID)instanceId, // NULL,
+ (PVOID)ifName,
+ (PVOID)serNum,
+ (PVOID)serNumMsm,
+ (PVOID)parentDev,
+ &funcProtocol,
+ &mtu,
+ devType,
+ &bActive,
+ &bQCDriver,
+ devStatus
+ );
+ }
+
+ if (bActive == TRUE)
+ {
+ if (pItem != NULL)
+ {
+ size_t rtnBytes;
+
+ pItem->Info.Type = devType;
+ pItem->Info.Flag = DEV_FLAG_NONE;
+ pItem->Info.IsQCDriver = (UCHAR)bQCDriver;
+ pItem->CbParams.Protocol = funcProtocol;
+ pItem->CbParams.Mtu = (ULONG)mtu;
+ pItem->BusType = busType;
+ StringCchCopy((PTSTR)pItem->DevDesc, QC_MAX_VALUE_NAME / 2, (PTSTR)friendlyName);
+ wcstombs_s(&rtnBytes, pItem->DevDescA, QC_MAX_VALUE_NAME, (PWCHAR)pItem->DevDesc, _TRUNCATE);
+
+ StringCchCopy((PTSTR)pItem->DevNameW, QC_MAX_VALUE_NAME / 2, TEXT(QC_DEV_PREFIX));
+ if (pDevName != NULL)
+ {
+ StringCchCat((PTSTR)pItem->DevNameW, QC_MAX_VALUE_NAME / 2, (PTSTR)pDevName);
+ }
+ else
+ {
+ // QDSS/DPL - use friendly name as device name
+ StringCchCat((PTSTR)pItem->DevNameW, QC_MAX_VALUE_NAME / 2, (PTSTR)friendlyName);
+ }
+ wcstombs_s(&rtnBytes, pItem->DevNameA, QC_MAX_VALUE_NAME, (PWCHAR)pItem->DevNameW, _TRUNCATE);
+ if (bHwIdOK == TRUE)
+ {
+ StringCchCopy((PTSTR)pItem->HwId, QC_MAX_VALUE_NAME / 2, (PTSTR)hwId);
+ }
+ if (devType == QC_DEV_TYPE_NET)
+ {
+ StringCchCopy((PTSTR)pItem->InterfaceName, QC_MAX_VALUE_NAME / 2, (PTSTR)ifName);
+ }
+ StringCchCopy((PTSTR)pItem->Location, QC_MAX_VALUE_NAME / 2, (PTSTR)location);
+ StringCchCopy((PTSTR)pItem->DevPath, QC_MAX_VALUE_NAME / 2, (PTSTR)devPath);
+ StringCchCopy((PTSTR)pItem->SerNum, 128, (PTSTR)serNum);
+ StringCchCopy((PTSTR)pItem->SerNumMsm, 128, (PTSTR)serNumMsm);
+ // if there's no parent name, use the DevDesc
+ if ((parentDev[0] == 0) && (parentDev[1] == 0))
+ {
+ QCD_Printf(" To find parent for <%ws> <%ws>\n", (PTSTR)pItem->DevNameW, (PTSTR)pItem->DevDesc);
+ if (FALSE == FindParent((PVOID)instanceId, (PVOID)parentDev, (PVOID)potentialSerNum))
+ {
+ StringCchCopy((PTSTR)pItem->ParentDev, 128, (PTSTR)friendlyName);
+ }
+ else
+ {
+ StringCchCopy((PTSTR)pItem->ParentDev, 128, (PTSTR)parentDev);
+ if ((serNum[0] == 0) && (serNum[1] == 0))
+ {
+ // StringCchCopy((PTSTR)pItem->SerNum, 128, (PTSTR)potentialSerNum);
+ QCD_Printf("Ignore potential SerNum from parent for <%ws>\n", (PTSTR)pItem->DevDesc);
+
+ }
+ }
+ }
+ else
+ {
+ StringCchCopy((PTSTR)pItem->ParentDev, 128, (PTSTR)parentDev);
+ }
+ InsertTailList(&NewArrivalList, &pItem->List);
+ // QCD_Printf("Dev Added: <%ws> HWID <%ws> INST <%ws> 0x%p\n", (PTSTR)pItem->DevDesc, (PTSTR)hwId, (PWSTR)instanceId, pItem);
+ }
+ } // if
+ else
+ {
+ // QCD_Printf("Dev failed to be added: <%ws>\n", (PTSTR)pItem->DevDesc);
+ EnterCriticalSection(&opLock);
+ InsertHeadList(&FreeList, &pItem->List);
+ LeaveCriticalSection(&opLock);
+ }
+
+ memberIdx++;
+ } // while
+
+ if (devInfoHandle != INVALID_HANDLE_VALUE)
+ {
+ SetupDiDestroyDeviceInfoList(devInfoHandle);
+ }
+
+ return;
+
+ } // ScanDevices
+
+ DWORD WINAPI RunDevMonitor(PVOID Context)
+ {
+ DWORD status = WAIT_OBJECT_0;
+ static BOOL bScannedAlready = FALSE;
+ DWORD tid;
+
+ while (bMonitorRunning == TRUE) // set in StartDeviceMonitor()
+ {
+ if (bScannedAlready == FALSE)
+ {
+ bScannedAlready = TRUE;
+ hAnnouncementThread = ::CreateThread(NULL, 0, AnnouncementThread, NULL, 0, &tid);
+ SetEvent(hMonitorStartedEvt);
+ }
+ // if (status == WAIT_OBJECT_0)
+ if (status < QC_REG_MAX)
+ {
+ // QCD_Printf("scanning with alert %u...\n", status);
+ ScanDevices();
+ TryToAnnounce(&ArrivalList, &NewArrivalList);
+ }
+ status = MonitorDeviceChange();
+ }
+ SetEvent(hAnnouncementEvt); // last announcement to clean up queues
+ WaitForSingleObject(hAnnouncementExitEvt, 1000);
+ CleanupList(&ArrivalList);
+ CleanupList(&NewArrivalList);
+ CleanupList(&FreeList);
+ if (NotifyStore != NULL)
+ {
+ free(NotifyStore);
+ }
+ QCD_Printf("exiting device monitor...\n");
+ SetEvent(hStopMonitorEvt); // signal after cleaning up
+ bScannedAlready = FALSE;
+
+ return 0;
+ } // RunDevMonitor
+
+ } // anonymous namespace
+
+ VOID DisplayDevices(VOID)
+ {
+ PQC_DEV_ITEM pDevInfo;
+ PLIST_ENTRY headOfArrival, peekArrival;
+
+ QCD_Printf("================ DEVICES =================\n");
+
+ EnterCriticalSection(&opLock);
+
+ if (!IsListEmpty(&ArrivalList))
+ {
+ headOfArrival = &ArrivalList;
+ peekArrival = headOfArrival->Flink;
+ while (peekArrival != headOfArrival)
+ {
+ pDevInfo = CONTAINING_RECORD(peekArrival, QC_DEV_ITEM, List);
+ peekArrival = peekArrival->Flink;
+ QCD_Printf("[Type_%d] <%ws>\n", pDevInfo->Info.Type, pDevInfo->DevDesc);
+ QCD_Printf(" Dev-to-open <%ws>\n", pDevInfo->DevNameW);
+ }
+ }
+
+ LeaveCriticalSection(&opLock);
+ } // DisplayDevices
+
+ // ConfigureCommChannel: Initial implementation for quick validation
+ // Configure communication channel, error codes to be defined later
+ int ConfigureCommChannel(UCHAR DevType, HANDLE DeviceHandle, DWORD Baudrate, BOOL IsLegacyTimeoutConfig)
+ {
+ int retVal = 0;
+
+ if (DevType == QC_DEV_TYPE_PORTS)
+ {
+ DCB dcb;
+ COMMTIMEOUTS commTimeouts;
+
+ dcb.DCBlength = sizeof(DCB);
+ if (GetCommState(DeviceHandle, &dcb) == TRUE)
+ {
+ dcb.BaudRate = Baudrate;
+ dcb.ByteSize = 8;
+ dcb.Parity = NOPARITY;
+ dcb.StopBits = ONESTOPBIT;
+ if (SetCommState(DeviceHandle, &dcb) == FALSE)
+ {
+ QCD_Printf("[Type_%d] SetCommState failed on handle 0x%x, error 0x%x\n",
+ DevType, DeviceHandle, GetLastError());
+ retVal = 1;
+ }
+ else
+ {
+ QCD_Printf("[Type_%d] SetCommState done", DevType);
+ }
+ }
+ else
+ {
+ QCD_Printf("[Type_%d] GetCommState failed on handle 0x%x, error 0x%x\n",
+ DevType, DeviceHandle, GetLastError());
+ retVal = 2;
+ }
+
+ if (IsLegacyTimeoutConfig)
+ {
+ commTimeouts.ReadIntervalTimeout = 20;
+ commTimeouts.ReadTotalTimeoutMultiplier = 0;
+ commTimeouts.ReadTotalTimeoutConstant = 100;
+ commTimeouts.WriteTotalTimeoutMultiplier = 1;
+ commTimeouts.WriteTotalTimeoutConstant = 10;
+ }
+ else
+ {
+ commTimeouts.ReadIntervalTimeout = MAXDWORD;
+ commTimeouts.ReadTotalTimeoutMultiplier = MAXDWORD;
+ commTimeouts.ReadTotalTimeoutConstant = 100;
+ commTimeouts.WriteTotalTimeoutMultiplier = 0;
+ commTimeouts.WriteTotalTimeoutConstant = 2000;
+ }
+
+ if (SetCommTimeouts(DeviceHandle, &commTimeouts) == false)
+ {
+ QCD_Printf("[Type_%d] SetCommTimeouts failed on handle 0x%x Error 0x%x\n",
+ DevType, DeviceHandle, GetLastError());
+ retVal = 3;
+ }
+ else
+ {
+ QCD_Printf("[Type_%d] SetCommTimeouts done", DevType);
+ }
+ }
+ return retVal;
+ } // ConfigureCommChannel
+} // QcDevice
+
+// ----------- PUBLIC APIs -------------
+
+QCDEVLIB_API VOID QcDevice::StartDeviceMonitor(VOID)
+{
+ DWORD tid;
+
+ if (InterlockedIncrement(&lInOperation) > 1)
+ {
+ InterlockedDecrement(&lInOperation);
+ return;
+ }
+
+ if (bMonitorRunning == FALSE)
+ {
+ if (bFeatureSet == FALSE)
+ {
+ FeatureSetting.User.Version = 1;
+ FeatureSetting.User.Settings = DEV_FEATURE_INCLUDE_NONE_QC_PORTS;
+ FeatureSetting.User.DeviceClass = (DEV_CLASS_NET | DEV_CLASS_PORTS | DEV_CLASS_USB);
+ FeatureSetting.TimerInterval = 1000; // INFINITE; // ADB device needs timer
+ ZeroMemory(FeatureSetting.VID, QC_MAX_VALUE_NAME);
+ }
+ InitializeCriticalSection(&opLock);
+ InitializeCriticalSection(¬ifyLock);
+ hStopMonitorEvt = CreateEvent(NULL, FALSE, FALSE, NULL);
+ hMonitorStartedEvt = CreateEvent(NULL, FALSE, FALSE, NULL);
+ hAnnouncementEvt = CreateEvent(NULL, FALSE, FALSE, NULL);
+ hAnnouncementExitEvt = CreateEvent(NULL, FALSE, FALSE, NULL);
+ InitializeLists();
+ bMonitorRunning = TRUE;
+ hMonitorThread = ::CreateThread(NULL, 0, RunDevMonitor, NULL, 0, &tid);
+ if (hMonitorThread == NULL)
+ {
+ bMonitorRunning = FALSE;
+ }
+ WaitForSingleObject(hMonitorStartedEvt, 5000);
+ CloseHandle(hMonitorStartedEvt); // dispose
+ }
+
+ InterlockedDecrement(&lInOperation);
+
+ return;
+} // StartDeviceMonitor
+
+QCDEVLIB_API VOID QcDevice::StopDeviceMonitor(VOID)
+{
+ int i;
+
+ bMonitorRunning = FALSE;
+ SetEvent(hRegChangeEvt[0]);
+ WaitForSingleObject(hStopMonitorEvt, 10000);
+ if (hAnnouncementThread != 0)
+ {
+ CloseHandle(hAnnouncementThread);
+ hAnnouncementThread = 0;
+ }
+ if (hMonitorThread != 0)
+ {
+ CloseHandle(hMonitorThread);
+ hMonitorThread = 0;
+ }
+ CloseHandle(hAnnouncementEvt);
+ CloseHandle(hAnnouncementExitEvt);
+ CloseHandle(hStopMonitorEvt);
+
+ for (i = QC_REG_DEVMAP; i < QC_REG_MAX; i++)
+ {
+ CloseHandle(hRegChangeEvt[i]);
+ CloseHandle(hSysKey[i]);
+ }
+ DeleteCriticalSection(&opLock);
+ DeleteCriticalSection(¬ifyLock);
+ lInitialized = 0;
+} // StopDeviceMonitor
+
+// =================== Removal Notification ====================
+QCDEVLIB_API VOID QcDevice::SetDeviceChangeCallback(DEVICECHANGE_CALLBACK Cb)
+{
+ fNotifyCb = Cb;
+ CallerContext = NULL;
+} // SetDeviceChangeCallback
+
+QCDEVLIB_API VOID QcDevice::SetDeviceChangeCallback(DEVICECHANGE_CALLBACK Cb, PVOID AppContext)
+{
+ fNotifyCb = Cb;
+ CallerContext = AppContext;
+} // SetDeviceChangeCallback
+
+QCDEVLIB_API VOID QcDevice::SetDeviceChangeCallback(DEVICECHANGE_CALLBACK_N Cb)
+{
+ fNotifyCb_N = Cb;
+} // SetDeviceChangeCallback
+
+QCDEVLIB_API VOID QcDevice::SetLoggingCallback(QCD_LOGGING_CALLBACK Cb)
+{
+ fLogger = Cb;
+} // SetLoggingCallback
+
+QCDEVLIB_API VOID QcDevice::SetFeature(PVOID Settings)
+{
+ PDEV_FEATURE_SETTING pFeature = (PDEV_FEATURE_SETTING)Settings;
+
+ if (pFeature->Version != 1)
+ {
+ return;
+ }
+ FeatureSetting.User.Version = pFeature->Version;
+ FeatureSetting.User.Settings = pFeature->Settings;
+ FeatureSetting.User.DeviceClass = pFeature->DeviceClass;
+ ZeroMemory(FeatureSetting.VID, QC_MAX_VALUE_NAME);
+
+ FeatureSetting.TimerInterval = 1000; // INFINITE; // 500; // ADB device needs timer
+
+ bFeatureSet = TRUE;
+
+ if ((FeatureSetting.User.Settings & DEV_FEATURE_SCAN_USB_WITH_VID) != 0)
+ {
+ if (pFeature->VID != NULL)
+ {
+ StringCchCopy((PTSTR)FeatureSetting.VID, QC_MAX_VALUE_NAME / 2, (PTSTR)pFeature->VID);
+ }
+ }
+
+} // SetFeature
+
+QCDEVLIB_API ULONG QcDevice::GetDevice(PVOID UserBuffer)
+{
+ PDEV_PARAMS_N DevInfo = (PDEV_PARAMS_N)UserBuffer;
+ PQC_NOTIFICATION_STORE storeBranch;
+ PQC_DEV_ITEM devItem;
+ PLIST_ENTRY headOfList;
+ ULONG infoFilled = 0, returnVal = DEV_INFO_OK;
+
+ QCD_Printf("-->GetDevice (%d, %d, %d, %d, %d)\n",
+ DevInfo->DevDescBufLen, DevInfo->DevnameBufLen, DevInfo->IfNameBufLen,
+ DevInfo->LocBufLen, DevInfo->SerNumBufLen);
+
+ EnterCriticalSection(¬ifyLock);
+ if (!IsListEmpty(&AnnouncementList))
+ {
+ headOfList = RemoveHeadList(&AnnouncementList);
+ storeBranch = CONTAINING_RECORD(headOfList, QC_NOTIFICATION_STORE, List);
+ LeaveCriticalSection(¬ifyLock);
+
+ while (!IsListEmpty(&(storeBranch->DevItemChain)))
+ {
+ headOfList = RemoveHeadList(&(storeBranch->DevItemChain));
+ devItem = CONTAINING_RECORD(headOfList, QC_DEV_ITEM, List);
+
+ if (DevInfo->DevDescBufLen > 0)
+ {
+ ZeroMemory(DevInfo->DevDesc, DevInfo->DevDescBufLen);
+ StringCchCopy((PTSTR)DevInfo->DevDesc, DevInfo->DevDescBufLen / 2, (PTSTR)devItem->DevDesc);
+ }
+ else
+ {
+ returnVal = DEV_INFO_DEV_DESC_LEN;
+ break;
+ }
+ if (DevInfo->DevnameBufLen > 0)
+ {
+ ZeroMemory(DevInfo->DevName, DevInfo->DevnameBufLen);
+ StringCchCopy((PTSTR)DevInfo->DevName, DevInfo->DevnameBufLen, (PTSTR)devItem->DevNameA);
+ }
+ else
+ {
+ returnVal = DEV_INFO_DEV_NAME_LEN;
+ break;
+ }
+ if (DevInfo->IfNameBufLen > 0)
+ {
+ ZeroMemory(DevInfo->IfName, DevInfo->IfNameBufLen);
+ StringCchCopy((PTSTR)DevInfo->IfName, DevInfo->IfNameBufLen / 2, (PTSTR)devItem->InterfaceName);
+ }
+ else
+ {
+ returnVal = DEV_INFO_IF_NAME_LEN;
+ break;
+ }
+ if (DevInfo->LocBufLen > 0)
+ {
+ ZeroMemory(DevInfo->Loc, DevInfo->LocBufLen);
+ StringCchCopy((PTSTR)DevInfo->Loc, DevInfo->LocBufLen / 2, (PTSTR)devItem->Location);
+ }
+ if (DevInfo->DevPathBufLen > 0)
+ {
+ ZeroMemory(DevInfo->DevPath, DevInfo->DevPathBufLen);
+ StringCchCopy((PTSTR)DevInfo->DevPath, DevInfo->DevPathBufLen / 2, (PTSTR)devItem->DevPath);
+ }
+ else
+ {
+ returnVal = DEV_INFO_LOC_LEN;
+ break;
+ }
+ if (DevInfo->SerNumBufLen > 0)
+ {
+ ZeroMemory(DevInfo->SerNum, DevInfo->SerNumBufLen);
+ StringCchCopy((PTSTR)DevInfo->SerNum, DevInfo->SerNumBufLen / 2, (PTSTR)devItem->SerNum);
+ }
+ if (DevInfo->SerNumMsmBufLen > 0)
+ {
+ ZeroMemory(DevInfo->SerNumMsm, DevInfo->SerNumMsmBufLen);
+ StringCchCopy((PTSTR)DevInfo->SerNumMsm, DevInfo->SerNumMsmBufLen / 2, (PTSTR)devItem->SerNumMsm);
+ }
+ else
+ {
+ returnVal = DEV_INFO_SER_NUM_LEN;
+ break;
+ }
+ DevInfo->Mtu = 0; // not used
+
+ if (devItem->Info.Flag == DEV_FLAG_ARRIVAL)
+ {
+ DevInfo->Flag = (((ULONG)devItem->Info.Type << 8) | ((ULONG)1 << 4) |
+ (ULONG)devItem->Info.IsQCDriver);
+ }
+ else
+ {
+ DevInfo->Flag = (((ULONG)devItem->Info.Type << 8) | (ULONG)devItem->Info.IsQCDriver);
+ }
+
+ free(devItem);
+ infoFilled = 1;
+ break;
+ }
+ if ((infoFilled == 0) && (returnVal != DEV_INFO_OK))
+ {
+ QCD_Printf("GetDevice: error %d, restore the device info for future retrieval\n", returnVal);
+ InsertHeadList(&(storeBranch->DevItemChain), &(devItem->List)); // re-store
+ }
+
+ EnterCriticalSection(¬ifyLock);
+ if (IsListEmpty(&(storeBranch->DevItemChain)))
+ {
+ QCD_Printf("GetDevice: recycle storeBranch\n");
+ InsertTailList(&NotifyFreeList, &(storeBranch->List)); // recycle
+ }
+ else
+ {
+ QCD_Printf("GetDevice: restore storeBranch\n");
+ InsertHeadList(&AnnouncementList, &(storeBranch->List)); // restore
+ }
+ } // if
+ LeaveCriticalSection(¬ifyLock);
+
+ if ((returnVal == DEV_INFO_OK) && (infoFilled == 0))
+ {
+ QCD_Printf("GetDevice: no more device\n");
+ returnVal = DEV_INFO_END;
+ }
+
+ QCD_Printf("<--GetDevice (ST %d, filled %d Flag 0x%x)\n", returnVal, infoFilled, DevInfo->Flag);
+ return returnVal;
+} // GetDevice
+
+QCDEVLIB_API PCHAR QcDevice::GetPortName(PVOID DeviceName)
+{
+ size_t length;
+ PCHAR p;
+
+ p = (PCHAR)DeviceName;
+ length = strlen(p);
+ p += length;
+ while (p > DeviceName)
+ {
+ if (*p == 0x5C) // '\'
+ {
+ break;
+ }
+ p--;
+ }
+
+ if (p == DeviceName)
+ {
+ return NULL;
+ }
+
+ return (p + 1);
+} // GetPortName
+
+QCDEVLIB_API PCHAR QcDevice::LibVersion(VOID)
+{
+ return "1.0.0.0";
+}
+
+QCDEVLIB_API VOID _cdecl QcDevice::QCD_Printf(const char *Format, ...)
+{
+#define DBG_MSG_MAX_SZ 1024
+#define MSG_LEN_MAX (DBG_MSG_MAX_SZ - 50)
+ va_list arguments;
+ CHAR msgBuffer[DBG_MSG_MAX_SZ], *pBuf;
+ SYSTEMTIME lt;
+
+ // log time
+ GetLocalTime(<);
+ _snprintf_s((PCHAR)msgBuffer, DBG_MSG_MAX_SZ, _TRUNCATE, "%s-[%02d:%02d:%02d.%03d] ",
+ LibVersion(), lt.wHour, lt.wMinute, lt.wSecond, lt.wMilliseconds);
+ pBuf = (PCHAR)msgBuffer + strlen(msgBuffer);
+
+ // log data
+ va_start(arguments, Format);
+ _vsnprintf_s((PCHAR)pBuf, MSG_LEN_MAX, _TRUNCATE, Format, arguments);
+ va_end(arguments);
+
+ if (fLogger != NULL)
+ {
+ fLogger((PCHAR)msgBuffer);
+ }
+ else
+ {
+#ifdef _DEBUG
+#ifdef TOOLS_TARGET_WINDOWS
+ OutputDebugStringA((PCHAR)msgBuffer);
+#else
+ Lang::cout << msgBuffer << std::endl;
+#endif
+#endif
+
+ }
+} // QCD_Printf
+
+QCDEVLIB_API ULONG QcDevice::GetDeviceList(PVOID Buffer, ULONG BufferSize, PULONG ActualSize)
+{
+ PQC_DEV_ITEM pDevInfo;
+ PLIST_ENTRY headOfArrival, peekArrival;
+ PCHAR pDest;
+ size_t remainingSpace, nameLen;
+ ULONG numOfDevices = 0;
+
+ remainingSpace = BufferSize;
+ pDest = (PCHAR)Buffer;
+ *ActualSize = 0;
+
+ EnterCriticalSection(&opLock);
+
+ ZeroMemory(Buffer, BufferSize);
+ if (!IsListEmpty(&ArrivalList))
+ {
+ headOfArrival = &ArrivalList;
+ peekArrival = headOfArrival->Flink;
+ while (peekArrival != headOfArrival)
+ {
+ pDevInfo = CONTAINING_RECORD(peekArrival, QC_DEV_ITEM, List);
+ peekArrival = peekArrival->Flink;
+
+ // copy DevDescA to the supplied buffer
+ nameLen = strlen(pDevInfo->DevDescA);
+ if (0 != strncpy_s(pDest, remainingSpace, pDevInfo->DevDescA, nameLen))
+ {
+ QCD_Printf("GetDeviceList error\n");
+ break;
+ }
+ else
+ {
+ pDest += (nameLen + 1); // including the NULL
+ remainingSpace -= (nameLen + 1);
+ *ActualSize += (ULONG)(nameLen + 1);
+ numOfDevices++;
+ }
+ }
+ }
+
+ LeaveCriticalSection(&opLock);
+
+ return numOfDevices;
+
+} // GetDeviceList
+
+QCDEVLIB_API HANDLE QcDevice::OpenDevice(PVOID DeviceName, DWORD Baudrate, BOOL IslegacyTimeoutConfig)
+{
+ PQC_DEV_ITEM pDevInfo;
+ PLIST_ENTRY headOfArrival, peekArrival;
+ PVOID pDevName = NULL;
+ HANDLE hDevice = INVALID_HANDLE_VALUE; // -1
+ CHAR usbDev[256];
+
+ QCD_Printf("-->QcDevice::OpenDevice: <%s>\n", DeviceName);
+
+ if ((StrStrA((PCHAR)DeviceName, "QDSS") == NULL) && (StrStrA((PCHAR)DeviceName, "DPL") == NULL))
+ {
+ EnterCriticalSection(&opLock);
+ if (!IsListEmpty(&ArrivalList))
+ {
+ headOfArrival = &ArrivalList;
+ peekArrival = headOfArrival->Flink;
+ while (peekArrival != headOfArrival)
+ {
+ pDevInfo = CONTAINING_RECORD(peekArrival, QC_DEV_ITEM, List);
+ peekArrival = peekArrival->Flink;
+ // QCD_Printf("-->QcDevice::OpenDevice:comparing <%s> <%s>\n", (PCHAR)DeviceName, (PCHAR)pDevInfo->DevDescA);
+ if (StrCmpA((PCHAR)DeviceName, (PCHAR)pDevInfo->DevDescA) == 0)
+ {
+ pDevName = (PVOID)pDevInfo->DevNameA;
+ break;
+ }
+ }
+ }
+ LeaveCriticalSection(&opLock);
+ }
+ else // QDSS or DPL case
+ {
+ strncpy_s(usbDev, sizeof(usbDev), "\\\\.\\", _TRUNCATE);
+ strncat_s(usbDev, sizeof(usbDev), (PCHAR)DeviceName, _TRUNCATE);
+ pDevName = usbDev;
+ }
+
+ // pDevName = DeviceName; // short-cut for testing
+
+ if (pDevName != NULL)
+ {
+ hDevice = ::CreateFileA
+ (
+ (PCHAR)pDevName,
+ GENERIC_WRITE | GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, // async operation
+ NULL
+ );
+
+ if (hDevice == INVALID_HANDLE_VALUE)
+ {
+ QCD_Printf("QcDevice::OpenDevice: Error 0x%x\n, attempt read only", GetLastError());
+ }
+ ConfigureCommChannel(QC_DEV_TYPE_PORTS, hDevice, Baudrate, IslegacyTimeoutConfig);
+ }
+ else
+ {
+ QCD_Printf("QcDevice::OpenDevice: cannot find device <%s>\n", (PCHAR)DeviceName);
+ }
+
+ QCD_Printf("<--QcDevice::OpenDevice: <%s> 0x%x\n", DeviceName, hDevice);
+ return hDevice;
+} // OpenDevice
+
+QCDEVLIB_API VOID QcDevice::CloseDevice(HANDLE hDevice)
+{
+ QCD_Printf("QcDevice::CloseDevice: 0x%x\n", hDevice);
+
+ ::CloseHandle(hDevice);
+} // CloseDevice
+
+QCDEVLIB_API BOOL QcDevice::ReadFromDevice
+(
+ HANDLE hDevice,
+ PVOID RxBuffer,
+ DWORD NumBytesToRead,
+ LPDWORD NumBytesReturned
+)
+{
+ OVERLAPPED ov;
+ BOOL bResult = FALSE;
+ DWORD dwStatus = NO_ERROR;
+
+ QCD_Printf("-->QcDevice::ReadFromDevice: 0x%x bufferSize %d bytes\n", hDevice, NumBytesToRead);
+
+ ZeroMemory(&ov, sizeof(ov));
+ ov.Offset = 0;
+ ov.OffsetHigh = 0;
+ ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (ov.hEvent == NULL)
+ {
+ QCD_Printf("QcDevice::ReadFromDevice event error %u\n", GetLastError());
+ return bResult;
+ }
+ *NumBytesReturned = 0;
+
+ bResult = ::ReadFile
+ (
+ hDevice,
+ RxBuffer,
+ NumBytesToRead,
+ NumBytesReturned,
+ &ov
+ );
+
+ if (bResult == TRUE)
+ {
+ if (ov.hEvent != NULL)
+ {
+ CloseHandle(ov.hEvent);
+ }
+ return bResult;
+ }
+ else
+ {
+ dwStatus = GetLastError();
+
+ if (ERROR_IO_PENDING != dwStatus)
+ {
+ QCD_Printf("QcDevice::ReadFromDevice failure, error %u\n", dwStatus);
+ }
+ else
+ {
+ bResult = GetOverlappedResult
+ (
+ hDevice,
+ &ov,
+ NumBytesReturned,
+ TRUE // no return until operaqtion completes
+ );
+
+ if (bResult == FALSE)
+ {
+ dwStatus = GetLastError();
+ QCD_Printf("QcDevice::ReadFromDevice/GetOverlappedResult failure %u\n", dwStatus);
+ }
+ }
+ }
+
+ if (ov.hEvent != NULL)
+ {
+ CloseHandle(ov.hEvent);
+ }
+ QCD_Printf("<--QcDevice::ReadFromDevice: 0x%x read %d bytes (result %d)\n", hDevice, *NumBytesReturned, bResult);
+
+ return bResult;
+} // ReadFromDevice
+
+QCDEVLIB_API BOOL QcDevice::SendToDevice
+(
+ HANDLE hDevice,
+ PVOID TxBuffer,
+ DWORD NumBytesToSend,
+ LPDWORD NumBytesSent
+)
+{
+ BOOL bResult = FALSE;
+ OVERLAPPED ov;
+ DWORD dwStatus = NO_ERROR;
+
+ QCD_Printf("-->QcDevice::SendToDevice: 0x%x %d bytes\n", hDevice, NumBytesToSend);
+
+ ZeroMemory(&ov, sizeof(ov));
+ ov.Offset = 0;
+ ov.OffsetHigh = 0;
+ ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (ov.hEvent == NULL)
+ {
+ QCD_Printf("Read error, event error %u\n", GetLastError());
+ return bResult;
+ }
+
+ *NumBytesSent = 0;
+
+ if (NumBytesToSend != 0)
+ {
+ bResult = ::WriteFile
+ (
+ hDevice,
+ TxBuffer,
+ NumBytesToSend,
+ NumBytesSent,
+ &ov
+ );
+ if (bResult == FALSE)
+ {
+ dwStatus = GetLastError();
+
+ if (ERROR_IO_PENDING != dwStatus)
+ {
+ QCD_Printf("QcDevice::SendToDevice-0 error %u\n", dwStatus);
+ }
+ else
+ {
+ bResult = GetOverlappedResult
+ (
+ hDevice,
+ &ov,
+ NumBytesSent,
+ TRUE // no return until operaqtion completes
+ );
+
+ if (bResult == FALSE)
+ {
+ dwStatus = GetLastError();
+ QCD_Printf("QcDevice::SendToDevice-1 %u\n", dwStatus);
+ }
+ }
+ }
+ }
+ else
+ {
+ QCD_Printf("QcDevice::SendToDevice - nothing to send\n");
+ }
+
+ if (ov.hEvent != NULL)
+ {
+ CloseHandle(ov.hEvent);
+ }
+
+ QCD_Printf("-->QcDevice::SendToDevice: 0x%x sent %d bytes (result %d)\n", hDevice, *NumBytesSent, bResult);
+ return bResult;
+} // SendToDevice
+
diff --git a/src/windows/tools/qcdev/scandev.h b/src/windows/tools/qcdev/scandev.h
new file mode 100644
index 0000000..b0d3370
--- /dev/null
+++ b/src/windows/tools/qcdev/scandev.h
@@ -0,0 +1,120 @@
+/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*
+
+ S C A N D E V . H
+
+GENERAL DESCRIPTION
+ This file defines data structures and function prototypes for device
+ scanning, enumeration, and monitoring of Qualcomm USB devices.
+
+ Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ SPDX-License-Identifier: BSD-3-Clause
+
+*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/
+
+#ifndef SCANDEV_H
+#define SCANDEV_H
+
+#include
+#include
+#include
+#include
+#include
+
+#include "qdpublic.h"
+
+typedef enum _QC_REG_LOC
+{
+ QC_REG_DEVMAP = 0,
+ QC_REG_USB = 1,
+ QC_REG_MDM = 2,
+ QC_REG_NET = 3,
+ QC_REG_PORTS = 4,
+ QC_REG_ADB = 5,
+ // QC_REG_HW_USB = 6,
+ QC_REG_MAX
+} QC_REG_LOC;
+
+#define QC_REG_KEY_DEVMAP "HARDWARE\\DEVICEMAP\\SERIALCOMM"
+#define QC_REG_SW_KEY "SYSTEM\\CurrentControlSet\\Control\\Class\\"
+#define QC_REG_SW_KEY_ADB "SYSTEM\\CurrentControlSet\\Control\\Class\\{3f966bd9-fa04-4ec5-991c-d326973b5128}"
+#define QC_REG_SW_KEY_USB "SYSTEM\\CurrentControlSet\\Control\\Class\\{88BAE032-5A81-49f0-BC3D-A4FF138216D6}"
+#define QC_REG_SW_KEY_MDM "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E96D-E325-11CE-BFC1-08002BE10318}"
+#define QC_REG_SW_KEY_NET "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}"
+#define QC_REG_SW_KEY_PORTS "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E978-E325-11CE-BFC1-08002BE10318}"
+#define QC_REG_HW_KEY_USB "SYSTEM\\CurrentControlSet\\Enum\\USB\\"
+#define QC_REG_HW_KEY_PREF "SYSTEM\\CurrentControlSet\\Enum\\"
+#define QC_REG_HW_KEY_PARAM "\\Device Parameters"
+#define QC_NET_CONNECTION_REG_KEY "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}\\"
+
+#define QC_DEV_PREFIX "\\\\.\\"
+#define DEV_PORT_NAME "PortName"
+#define QC_SPEC "AssignedPortForQCDevice"
+#define QC_SPEC_NET "QCDeviceControlFile"
+#define QC_SPEC_STAMP "QCDeviceStamp"
+#define QC_SPEC_MTU "QCAdvertisedMtu"
+#define QC_SPEC_SERNUM "QCDeviceSerialNumber"
+#define QC_SPEC_SERNUM_MSM "QCDeviceMsmSerialNumber"
+#define QC_SPEC_PROTOC "QCDeviceProtocol"
+#define QC_SPEC_PARENT_DEV "QCDeviceParent"
+#define QC_SPEC_SSR "QCDeviceSSR"
+#define QC_MAX_VALUE_NAME 1024
+#define REG_HW_ID_SIZE 2048
+
+#define D_CLASS_MODEM "MODEM"
+#define D_CLASS_PORTS "PORTS"
+#define D_CLASS_NET "NET"
+#define D_CLASS_USB "USB"
+#define D_CLASS_USBDEV "USBDEVICE"
+#define D_CLASS_ADB "ANDROIDUSBDEVICECLASS"
+
+#define BUS_TEST_USB "USB\\"
+#define BUS_TEST_PCI "PCI\\"
+#define BUS_TEST_ACPI "ACPI\\"
+#define BUS_TEST_PCIE "QC_BUS\\"
+
+#define MAX_NUM_DEV 512
+#define DEV_FLAG_NONE 0
+#define DEV_FLAG_DEPARTURE 1
+#define DEV_FLAG_ARRIVAL 2
+
+typedef struct _INTERNAL_DEV_FEATURE_SETTING
+{
+ DEV_FEATURE_SETTING User;
+ DWORD TimerInterval; // [20..5000]
+ CHAR VID[QC_MAX_VALUE_NAME]; // VID
+} INTERNAL_DEV_FEATURE_SETTING, *PINTERNAL_DEV_FEATURE_SETTING;
+
+typedef struct _QC_DEV_ITEM
+{
+ LIST_ENTRY List;
+ struct
+ {
+ UCHAR Type;
+ UCHAR Flag;
+ UCHAR IsQCDriver;
+ UCHAR Reserved;
+ } Info;
+ PVOID Context;
+ PVOID UserContext;
+ CB_PARAMS CbParams;
+ CHAR DevDesc[QC_MAX_VALUE_NAME]; // name for display
+ CHAR DevDescA[QC_MAX_VALUE_NAME]; // name for display
+ CHAR DevNameW[QC_MAX_VALUE_NAME]; // device name for communication (open/close)
+ CHAR DevNameA[QC_MAX_VALUE_NAME]; // device name for communication (open/close)
+ CHAR HwId[QC_MAX_VALUE_NAME]; // HW IDs
+ CHAR ParentDev[QC_MAX_VALUE_NAME];// parent device name
+ CHAR Location[QC_MAX_VALUE_NAME]; // Location on the bus
+ CHAR DevPath[QC_MAX_VALUE_NAME]; // device connection path
+ CHAR InterfaceName[QC_MAX_VALUE_NAME]; // Network interface name of a NIC
+ CHAR SerNum[256];
+ CHAR SerNumMsm[256];
+ UCHAR BusType;
+} QC_DEV_ITEM, *PQC_DEV_ITEM;
+
+typedef struct _QC_NOTIFICATION_STORE
+{
+ LIST_ENTRY List;
+ LIST_ENTRY DevItemChain;
+} QC_NOTIFICATION_STORE, *PQC_NOTIFICATION_STORE;
+
+#endif // SCANDEV_H
diff --git a/src/windows/tools/qcdev/utils.h b/src/windows/tools/qcdev/utils.h
new file mode 100644
index 0000000..94b2917
--- /dev/null
+++ b/src/windows/tools/qcdev/utils.h
@@ -0,0 +1,91 @@
+/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*
+
+ U T I L S . H
+
+GENERAL DESCRIPTION
+ This file provides inline utility functions (linked list operations)
+ for the Qualcomm USB device library.
+
+ Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ SPDX-License-Identifier: BSD-3-Clause
+
+*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/
+
+#ifndef UTILS_H
+#define UTILS_H
+
+VOID FORCEINLINE
+InitializeListHead(IN PLIST_ENTRY ListHead)
+{
+ ListHead->Flink = ListHead->Blink = ListHead;
+}
+
+BOOL FORCEINLINE
+IsListEmpty(IN PLIST_ENTRY ListHead)
+{
+ return (ListHead->Flink == ListHead);
+}
+
+VOID FORCEINLINE
+RemoveEntryList(IN PLIST_ENTRY Entry)
+{
+ PLIST_ENTRY Blink;
+ PLIST_ENTRY Flink;
+
+ Flink = Entry->Flink;
+ Blink = Entry->Blink;
+ Blink->Flink = Flink;
+ Flink->Blink = Blink;
+}
+
+PLIST_ENTRY FORCEINLINE
+RemoveHeadList(IN PLIST_ENTRY ListHead)
+{
+ PLIST_ENTRY Flink;
+ PLIST_ENTRY Entry;
+
+ Entry = ListHead->Flink;
+ Flink = Entry->Flink;
+ ListHead->Flink = Flink;
+ Flink->Blink = ListHead;
+ return Entry;
+}
+
+PLIST_ENTRY FORCEINLINE
+RemoveTailList(IN PLIST_ENTRY ListHead)
+{
+ PLIST_ENTRY Blink;
+ PLIST_ENTRY Entry;
+
+ Entry = ListHead->Blink;
+ Blink = Entry->Blink;
+ ListHead->Blink = Blink;
+ Blink->Flink = ListHead;
+ return Entry;
+}
+
+VOID FORCEINLINE
+InsertTailList(IN PLIST_ENTRY ListHead, IN PLIST_ENTRY Entry)
+{
+ PLIST_ENTRY Blink;
+
+ Blink = ListHead->Blink;
+ Entry->Flink = ListHead;
+ Entry->Blink = Blink;
+ Blink->Flink = Entry;
+ ListHead->Blink = Entry;
+}
+
+VOID FORCEINLINE
+InsertHeadList(IN PLIST_ENTRY ListHead, IN PLIST_ENTRY Entry)
+{
+ PLIST_ENTRY Flink;
+
+ Flink = ListHead->Flink;
+ Entry->Flink = Flink;
+ Entry->Blink = ListHead;
+ Flink->Blink = Entry;
+ ListHead->Flink = Entry;
+}
+
+#endif // UTILS_H
diff --git a/src/windows/tools/qdcfg/api.h b/src/windows/tools/qdcfg/api.h
new file mode 100644
index 0000000..872eed7
--- /dev/null
+++ b/src/windows/tools/qdcfg/api.h
@@ -0,0 +1,110 @@
+/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*
+
+ A P I . H
+
+GENERAL DESCRIPTION
+ This file defines IOCTL codes, constants, and function prototypes
+ for the qdcfg driver configuration API.
+
+ Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ SPDX-License-Identifier: BSD-3-Clause
+
+*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/
+
+#ifndef API_H
+#define API_H
+
+#include
+
+typedef enum _QMI_SERVICE_TYPE
+{
+ QMUX_TYPE_CTL = 0x00,
+ QMUX_TYPE_WDS = 0x01,
+ QMUX_TYPE_DMS = 0x02,
+ QMUX_TYPE_NAS = 0x03,
+ QMUX_TYPE_QOS = 0x04,
+ QMUX_TYPE_WMS = 0x05,
+ QMUX_TYPE_PDS = 0x06,
+ QMUX_TYPE_MAX,
+ QMUX_TYPE_ALL = 0xFF
+} QMI_SERVICE_TYPE;
+
+#define SERVICE_FILE_BUF_LEN 256
+#define QMUX_NUM_THREADS 3
+#define QMUX_MAX_DATA_LEN 2048
+#define QMUX_MAX_CMD_LEN 2048
+
+// User-defined IOCTL code range: 2048-4095
+#define QCDEV_IOCTL_INDEX 2048
+#define QCOMSER_IOCTL_INDEX 2048
+#define QCDEV_DUPLICATED_NOTIFICATION_REQ 0x00000002L
+
+#define IOCTL_QCDEV_WAIT_NOTIFY CTL_CODE(FILE_DEVICE_UNKNOWN, \
+ QCDEV_IOCTL_INDEX+1, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+/* Make the following code as 3338 - USB debug mask */
+#define IOCTL_QCDEV_SET_DBG_UMSK CTL_CODE(FILE_DEVICE_UNKNOWN, \
+ QCDEV_IOCTL_INDEX+1290, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+/* Make the following code as 3339 - MP debug mask */
+#define IOCTL_QCDEV_SET_DBG_MMSK CTL_CODE(FILE_DEVICE_UNKNOWN, \
+ QCDEV_IOCTL_INDEX+1291, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+/* Make the following code as 3340 - MP debug mask */
+#define IOCTL_QCDEV_GET_SERVICE_FILE CTL_CODE(FILE_DEVICE_UNKNOWN, \
+ QCDEV_IOCTL_INDEX+1292, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+/* Make the following code as 3343 - QMI Client Id */
+#define IOCTL_QCDEV_QMI_GET_CLIENT_ID CTL_CODE(FILE_DEVICE_UNKNOWN, \
+ QCDEV_IOCTL_INDEX+1295, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define IOCTL_QCDEV_RESUME_DL CTL_CODE(FILE_DEVICE_UNKNOWN, \
+ QCDEV_IOCTL_INDEX+25, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define IOCTL_QCDEV_MEDIA_CONNECT CTL_CODE(FILE_DEVICE_UNKNOWN, \
+ QCDEV_IOCTL_INDEX+26, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define IOCTL_QCDEV_MEDIA_DISCONNECT CTL_CODE(FILE_DEVICE_UNKNOWN, \
+ QCDEV_IOCTL_INDEX+27, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+/* Make the following code as 3338 - USB debug mask */
+#define IOCTL_QCUSB_SET_DBG_UMSK CTL_CODE(FILE_DEVICE_UNKNOWN, \
+ QCOMSER_IOCTL_INDEX+30, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define IOCTL_QCDEV_PAUSE_QMAP_DL CTL_CODE(FILE_DEVICE_UNKNOWN, \
+ QCDEV_IOCTL_INDEX+28, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define IOCTL_QCDEV_RESUME_QMAP_DL CTL_CODE(FILE_DEVICE_UNKNOWN, \
+ QCDEV_IOCTL_INDEX+29, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+typedef VOID(_stdcall *NOTIFICATION_CALLBACK)(HANDLE, ULONG);
+
+typedef struct _NOTIFICATION_CONTEXT
+{
+ HANDLE ServiceHandle;
+ NOTIFICATION_CALLBACK CallBack;
+} NOTIFICATION_CONTEXT, *PNOTIFICATION_CONTEXT;
+
+#endif // API_H
diff --git a/src/windows/tools/qdcfg/main.c b/src/windows/tools/qdcfg/main.c
new file mode 100644
index 0000000..3d2b12d
--- /dev/null
+++ b/src/windows/tools/qdcfg/main.c
@@ -0,0 +1,751 @@
+/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*
+
+ M A I N . C
+
+GENERAL DESCRIPTION
+ This file implements the main entry point and command-line interface for
+ the qdcfg driver configuration utility.
+
+ Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ SPDX-License-Identifier: BSD-3-Clause
+
+*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/
+
+#include "main.h"
+#include "reg.h"
+#include "trace.h"
+#include "../../qcversion.h"
+
+CMD_LINE_ARGS cmdArgs;
+
+#define NUM_SUPPORTED_ENTRIES 59
+
+CMD_COMMAND SupportedCommands[] =
+{
+ {"QCDriverDebugMask", "0: off(default), 1 : on\n" \
+ " [opt Flags(default 0x7fffffff)]\n" \
+ " [opt Level(default 0xff)]\n" \
+ " [opt MaxFile in MB(default 100, 0: unlimited)]", TRUE, QC_DEV_TYPE2_NONE, 0, ""},
+ {"MPNumClients", "0x10 (default) to 0xFF", FALSE, QC_DEV_TYPE2_NET, 0x10, ""},
+ {"MPReconfigDelay", "0x1F4 (default), range from 0xC8 to 0x1388 in ms", TRUE, QC_DEV_TYPE2_NET, 0x1F4, ""},
+ {"QCDevDisableQoS", "0: Enable(default)\n" \
+ " 1: Default Flow Only\n" \
+ " 2: No QoS\n" \
+ " 3: More UL buffers, no QoS", TRUE, QC_DEV_TYPE2_NET, 0, ""},
+ {"QCDevDisableIPMode", "0: Enable(default) 1: Disable", TRUE, QC_DEV_TYPE2_NET, 0, ""},
+ {"QCDriverTLPSize", "UL aggregation size, 0 to 0x10000 (default 0x8000)", TRUE, QC_DEV_TYPE2_NET, 0x8000, ""},
+ {"QCMPEnableTLP", "0: Disable 1: Enable(default)", TRUE, QC_DEV_TYPE2_NET, 1, ""},
+ {"QCDeviceFunction", "0 (default) 1: bi-directional GPS", FALSE, QC_DEV_TYPE2_PORTS | QC_DEV_TYPE2_USB, 0, ""},
+ {"QCRuntimeDeviceClass", "1: CDMA(default) 2: GSM/UMTS/LTE", TRUE, QC_DEV_TYPE2_NET, 1, ""},
+ {"QCDriverTxBuffers", "4 (default), range from 0x2 to 0xF", FALSE, QC_DEV_TYPE2_NONE, 4, ""},
+ {"QMICtrlCid", "0 (default) to 255", FALSE, QC_DEV_TYPE2_NET, 0, ""},
+ {"QMIFuzzingOn", "0 (default) to 1", FALSE, QC_DEV_TYPE2_NET, 0, ""},
+ {"QCMPEnableMBIMUL", "0: Disable 1: Enable(default)", TRUE, QC_DEV_TYPE2_NET, 1, ""},
+ {"QCMPEnableMBIMDL", "0: Disable 1: Enable(default)", TRUE, QC_DEV_TYPE2_NET, 1, ""},
+ {"QCIgnoreErrors", "0 (default) to 0xFFFFFFFF", TRUE, QC_DEV_TYPE2_NET, 0, ""},
+ {"QCDevDLPausePktCount", "1 to 0xFFFFFFFF, 0xC8 is default", TRUE, QC_DEV_TYPE2_NET, 0xC8, ""},
+ {"DLResume", "any value including 0", TRUE, QC_DEV_TYPE2_NET, -1, ""},
+ {"QCDriverMTUSize", "1000 to 1500, 1500 is default", TRUE, QC_DEV_TYPE2_NET, 1500, ""},
+ {"QCDriverTransmitTimer", "1 to 1000, 2 is default", TRUE, QC_DEV_TYPE2_NET, 2, ""},
+ {"QCDriverAggregationEnabled", "0:Disable(default) 1:Enable", TRUE, QC_DEV_TYPE2_NET, 0, ""},
+ {"QCDriverAggregationTime", "1000 to 5000, 2000 is default", TRUE, QC_DEV_TYPE2_PORTS, 2000, ""},
+ {"QCDriverAggregationSize", "1024*4 to 1024*30, 1024*20 is default", TRUE, QC_DEV_TYPE2_PORTS, 1024 * 20, ""},
+ {"QCDriverResolutionTime", "1 to 5, 1 by default", FALSE, QC_DEV_TYPE2_PORTS, 1, ""},
+ {"QCMPEnableQCDualIpFc", "0: Disable(Default) 1: Enable", TRUE, QC_DEV_TYPE2_NET, 0, ""},
+ {"QCDriverTLPMaxPackets", "0 to 1024, 0 is default", TRUE, QC_DEV_TYPE2_NET, 0, ""},
+ {"QCDriverDeregister", "0: Disable(default) 1: Enable", TRUE, QC_DEV_TYPE2_NET, 0, ""},
+ {"QCDriverDisableTimerResolution", "0(default) 1: Disable", FALSE, QC_DEV_TYPE2_NET, 0, ""},
+ {"QCDriverThreadPriority", "1 to 32, 26 is default", TRUE, QC_DEV_TYPE2_NET, 26, ""},
+ {"QCDriverL2Buffers", "2 to 100, 8 by default", TRUE, QC_DEV_TYPE2_NET, 8, ""},
+ {"QCMPEnableDLTLP", "0: Disable 1: Enable(default)", TRUE, QC_DEV_TYPE2_NET, 1, ""},
+ {"QCMPEnableQMAPV1", "0: Disable 1: Enable(default)", TRUE, QC_DEV_TYPE2_NET, 1, ""},
+ {"QCMPEnableQMAPV2", "0: Disable 1: Enable(default)", TRUE, QC_DEV_TYPE2_NET, 1, ""},
+ {"QCMPEnableQMAPV3", "0: Disable 1: Enable(default)", TRUE, QC_DEV_TYPE2_NET, 1, ""},
+ {"QCMPEnableQMAPV4", "0: Disable 1: Enable(default)", TRUE, QC_DEV_TYPE2_NET, 1, ""},
+ {"QCMPDisableQMI", "0: Disable(default) 1: Enable", TRUE, QC_DEV_TYPE2_NET, 1, ""},
+ {"QCDriverMediaConnect", "IP address to be set Statically, \n" \
+ " For Ex. 0xAB30A8C0 for IP addr 192.168.48.171", FALSE, QC_DEV_TYPE2_NET, -1, ""},
+ {"QCDriverMediaDisconnect", "any value including 0", FALSE, QC_DEV_TYPE2_NET, -1, ""},
+ {"QCDriverQMAPDLPause", "any value including 0", FALSE, QC_DEV_TYPE2_NET, -1, ""},
+ {"QCDriverQMAPDLResume", "any value including 0", FALSE, QC_DEV_TYPE2_NET, -1, ""},
+ {"QCMPNDPSignature", "0 to 0xFFFFFFFF (default 0x00535049)", TRUE, QC_DEV_TYPE2_NET, 0x00535049, ""},
+ {"QCMPMuxId", "0 (default) to 255", TRUE, QC_DEV_TYPE2_NET, 0, ""},
+ {"QCDriverDLAggregationSize", "0 (default) to 1024*64", TRUE, QC_DEV_TYPE2_NET, 0, ""},
+ {"QCDriverDLMaxPackets", "0 (default) to 1024", TRUE, QC_DEV_TYPE2_NET, 0, ""},
+ {"QCDriverEnableMBIM", "0: Disable 1: Enable(default)", TRUE, QC_DEV_TYPE2_NET, 1, ""},
+ {"QCDeviceMuxEnable", "0: Disable 1: Enable(default)", TRUE, QC_DEV_TYPE2_FLT, 1, ""},
+ {"QCDeviceStartIf", "0 (default) to 255", TRUE, QC_DEV_TYPE2_FLT, 0, ""},
+ {"QCDeviceNumIf", "1 (default) to 32", TRUE, QC_DEV_TYPE2_FLT, 1, ""},
+ {"QCDeviceNumMuxIf", "7 (default) to 32", TRUE, QC_DEV_TYPE2_FLT, 7, ""},
+ {"QCDriverNumTLPBuffers", "2 to 64, 10 is default", TRUE, QC_DEV_TYPE2_NET, 10, ""},
+ {"QCMPQMAPDLMinPadding", "0 (default) to 64", TRUE, QC_DEV_TYPE2_NET, 0, ""},
+ {"MPMaxDataReceives", "1 to 2000, 500 is default", TRUE, QC_DEV_TYPE2_NET, 500, ""},
+ {"QCMPBindIFId", "0 (default) to 255", FALSE, QC_DEV_TYPE2_NET, 0, ""},
+ {"QCMPBindEPType", "0 (default) to 255", TRUE, QC_DEV_TYPE2_NET, 0, ""},
+ {"QCMPDisableQMAPFC", "0: Disable(default) 1: Enable", TRUE, QC_DEV_TYPE2_NET, 0, ""},
+ {"QCMPMaxPendingQMIReqs", "3 to 0xFFFF, 12 is default", TRUE, QC_DEV_TYPE2_NET, 12, ""},
+ {"QCMPFakeDeviceIMSI", "0: Disable(default) 1: Enable", TRUE, QC_DEV_TYPE2_NET, 0, ""},
+ {"QCMPFakeDeviceICCID", "0: Disable(default) 1: Enable", TRUE, QC_DEV_TYPE2_NET, 0, ""},
+ {"QCMPEnableData5G", "0: Disable(default) 1: Enable", FALSE, QC_DEV_TYPE2_NET, 0, ""},
+ {"QCDeviceZLPEnabled", "0: Disable 1: Enable(default)", TRUE, QC_DEV_TYPE2_PORTS, 1, ""}
+};
+
+enum QC_ERR_CODE
+{
+ QC_NO_ERROR = 0,
+ QC_ERR_DEV_NAME,
+ QC_ERR_ENTRY_NAME_NO_SUPPORT,
+ QC_ERR_ENTRY_NAME,
+ QC_ERR_VALUE,
+ QC_ERR_UNKNOWN_ARG,
+ QC_ERR_INVALID_CMD
+};
+
+char *QcErrorMessage[] =
+{
+ "no error", // NO_ERROR
+ "valid device name must be supplied", // ERR_DEV_NAME
+ "registry entry name not supported", // ERR_DEV_NAME_NO_SUPPORT
+ "valid registry entry name must be supplied", // ERR_ENTRY_NAME
+ "entry value must be supplied to the -s option", // ERR_VALUE
+ "unknown arg", // ERR_UNKNOWN_ARG
+ "invalid command" // ERR_INVALID_CMD
+};
+
+BOOLEAN ValidateEntryName(PCHAR EntryName)
+{
+ int i;
+ BOOLEAN match = FALSE;
+
+ for (i = 0; i < NUM_SUPPORTED_ENTRIES; i++)
+ {
+ if (strcmp(SupportedCommands[i].entry, EntryName) == 0)
+ {
+ match = TRUE;
+ break;
+ }
+ }
+
+ return match;
+} // ValidateEntryName
+
+VOID DisplaySupportedEntryNames(VOID)
+{
+ int i;
+
+ printfd("\n %-28s\t%s\n", "Supported Reg Entry", "Value");
+
+ for (i = 0; i < NUM_SUPPORTED_ENTRIES; i++)
+ {
+ printfd(" %-28s %s\n", SupportedCommands[i].entry, SupportedCommands[i].usage);
+ }
+}
+
+UINT getSupportedEntries(void)
+{
+ return NUM_SUPPORTED_ENTRIES;
+}
+
+int getEntryLen(UINT index)
+{
+ if (index < NUM_SUPPORTED_ENTRIES)
+ {
+ return strlen(SupportedCommands[index].entry) + 1;
+ }
+ return -1;
+}
+
+int getUsageLen(UINT index)
+{
+ if (index < NUM_SUPPORTED_ENTRIES)
+ {
+ return strlen(SupportedCommands[index].usage) + 1;
+ }
+ return -1;
+}
+
+int getCommand(CMD_COMMAND *buffer, UINT index)
+{
+ size_t len = getEntryLen(index);
+ if (buffer->entry != NULL)
+ {
+ strncpy_s(buffer->entry, len + 1, SupportedCommands[index].entry, len);
+ len = getUsageLen(index);
+ if (buffer->usage != NULL)
+ {
+ strncpy_s(buffer->usage, len + 1, SupportedCommands[index].usage, len);
+ // TODO: copy friendly usage
+ // buffer->intro = SupportedCommands[index].intro;
+ }
+ buffer->defaultVal = SupportedCommands[index].defaultVal;
+ buffer->devType = SupportedCommands[index].devType;
+ buffer->isExposed = SupportedCommands[index].isExposed;
+ return 0;
+ }
+ return ERROR_INSUFFICIENT_BUFFER;
+}
+
+int executeCommand(char *entry, char action, char **params, int paramLen, BOOL device, char *deviceName)
+{
+ if (entry == NULL || strlen(entry) == 0)
+ {
+ return QC_ERR_ENTRY_NAME;
+ }
+ if (action != 's' && action != 'g')
+ {
+ return QC_ERR_INVALID_CMD;
+ }
+ for (int i = 0; i < NUM_SUPPORTED_ENTRIES; i++)
+ {
+ if (strcmp(entry, SupportedCommands[i].entry) == 0)
+ {
+ int argc = 2;
+ if (action == 's')
+ {
+ argc += paramLen;
+ }
+ if (device == TRUE)
+ {
+ argc += 2;
+ }
+ LPTSTR *argv = (LPTSTR *)malloc(sizeof(LPTSTR *) * argc);
+ if (argv == NULL)
+ {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+ LPTSTR *iter = argv;
+ if (action == 's')
+ {
+ *iter++ = "-s";
+ }
+ else
+ {
+ *iter++ = "-g";
+ }
+ *iter++ = entry;
+ if (action == 's')
+ {
+ for (int j = 0; j < paramLen; j++)
+ {
+ *iter++ = params[j];
+ }
+ }
+ if (device == TRUE)
+ {
+ *iter++ = "-n";
+ *iter = deviceName;
+ }
+
+ return processCommand(argc, argv);
+ }
+ }
+ return QC_ERR_ENTRY_NAME_NO_SUPPORT;
+}
+
+void setPrintDelegate(PRINTF_DELEGATE printer)
+{
+ hidden_printer = printer;
+}
+
+void printfd(const char *const format, ...)
+{
+ va_list args;
+ va_start(args, format);
+ if (hidden_printer == NULL)
+ {
+ vprintf(format, args);
+ }
+ else
+ {
+ char buffer[2048];
+ vsnprintf(buffer, 2048, format, args);
+ hidden_printer(buffer);
+ }
+ va_end(args);
+}
+
+int processCommand(int argc, LPTSTR argv[])
+{
+ PCHAR p;
+
+ ZeroMemory(&cmdArgs, sizeof(CMD_LINE_ARGS));
+ int ret = GetCommandLineArguments(argc, argv, &cmdArgs);
+ if (ret == -1)
+ {
+ return 0; // config global DebugMask or query usage, ignore other errors
+ }
+ if (ret != 0)
+ {
+ return ret;
+ }
+
+ p = cmdArgs.NIC_Name;
+ if ((*p == 'c' || *p == 'C') ||
+ (*(p + 1) == 'o' || *(p + 1) == 'O') ||
+ (*(p + 2) == 'm' || *(p + 2) == 'M'))
+ {
+ cmdArgs.IsCOM = TRUE;
+ }
+ else
+ {
+ cmdArgs.IsCOM = FALSE;
+ }
+
+ printfd("------------ Input --------------\n");
+ printfd("Dev Name: <%s>\n", cmdArgs.NIC_Name);
+ printfd("Debug : <0x%x>\n", cmdArgs.DebugMask);
+ printfd("IsCOM : %s\n", (cmdArgs.IsCOM == TRUE ? "TRUE" : "FALSE"));
+ printfd("Entry : <%s>\n", cmdArgs.EntryName);
+ if (cmdArgs.DebugFlags != 0)
+ {
+ printfd("Mask : <0x%x>\n", cmdArgs.DebugFlags);
+ }
+ if (cmdArgs.DebugLevel != 0)
+ {
+ printfd("Level : <0x%x>\n", cmdArgs.DebugLevel);
+ }
+ if (cmdArgs.DebugMaxFile != 0)
+ {
+ printfd("MaxFile : <%d>\n", cmdArgs.DebugMaxFile);
+ }
+ printfd("Value : <0x%x>\n", cmdArgs.EntryValue);
+ printfd("Action : %d\n", cmdArgs.Action);
+ printfd("\n------------ Output -------------\n");
+
+ if (cmdArgs.EntryName[0] != 0)
+ {
+ if (FALSE == ValidateEntryName(cmdArgs.EntryName))
+ {
+ Usage(QC_ERR_ENTRY_NAME_NO_SUPPORT, cmdArgs.EntryName);
+ return QC_ERR_ENTRY_NAME_NO_SUPPORT;
+ }
+ }
+
+ if (strcmp(cmdArgs.EntryName, "DLResume") == 0)
+ {
+ cmdArgs.Action = 2; // not setting registry
+ }
+
+ if (strcmp(cmdArgs.EntryName, "QCDriverMediaConnect") == 0)
+ {
+ cmdArgs.Action = 2; // not setting registry
+ }
+
+ if (strcmp(cmdArgs.EntryName, "QCDriverMediaDisconnect") == 0)
+ {
+ cmdArgs.Action = 2; // not setting registry
+ }
+
+ if (strcmp(cmdArgs.EntryName, "QCDriverQMAPDLPause") == 0)
+ {
+ cmdArgs.Action = 2; // not setting registry
+ }
+
+ if (strcmp(cmdArgs.EntryName, "QCDriverQMAPDLResume") == 0)
+ {
+ cmdArgs.Action = 2; // not setting registry
+ }
+
+ ProcessRegistryEntry(&cmdArgs);
+ printfd("\n");
+
+ return 0;
+} // main
+
+int GetCommandLineArguments(int argc, char *argv[], PCMD_LINE_ARGS Args)
+{
+ int i;
+
+ Args->EntryName[0] = 0;
+ Args->Action = -1;
+
+ for (i = 0; i < argc; i++)
+ {
+ if (argv[i][0] == '-' && argv[i][1] != '\0' && argv[i][2] == '\0')
+ {
+ switch (argv[i][1])
+ {
+ case 'h':
+ Usage(QC_NO_ERROR, NULL);
+ return -1;
+ case 'n':
+ if (i == argc - 1)
+ {
+ Usage(QC_ERR_DEV_NAME, NULL);
+ return QC_ERR_DEV_NAME;
+ }
+ sprintf_s(Args->NIC_Name, sizeof(Args->NIC_Name), "%s", argv[++i]);
+ break;
+ case 'g':
+ case 's':
+ {
+ if (i == argc - 1)
+ {
+ Usage(QC_ERR_ENTRY_NAME, NULL);
+ return QC_ERR_ENTRY_NAME;
+ }
+ if (argv[i][1] == 'g')
+ {
+ Args->Action = 0;
+ }
+ else
+ {
+ Args->Action = 1;
+ }
+ sprintf_s(Args->EntryName, sizeof(Args->EntryName), "%s", argv[++i]);
+
+ // examine EntryValue
+ if ((Args->Action == 1) && (i == argc - 1))
+ {
+ Usage(QC_ERR_VALUE, NULL);
+ return QC_ERR_VALUE;
+ }
+ if ((Args->Action == 0) && (i == argc - 1)) // no EntryValue
+ {
+ Args->EntryValue = 0;
+ break;
+ }
+
+ if (argv[i + 1][0] == '-' && argv[i + 1][1] != '\0') // another arg
+ {
+ Args->EntryValue = 0;
+ break;
+ }
+ else
+ {
+ // get EntryValue
+ PCHAR p, p1;
+
+ p = argv[++i];
+ if (strlen(p) == 0)
+ {
+ Usage(QC_ERR_VALUE, NULL);
+ return QC_ERR_VALUE;
+ }
+ if (strlen(p) > 2)
+ {
+ if (*p == '0')
+ {
+ p1 = p + 1;
+ if (*p1 == 'x' || *p1 == 'X')
+ {
+ p = p1 + 1;
+ }
+ }
+ }
+
+ if (strcmp(Args->EntryName, "QCDevicePidList") == 0)
+ {
+ sscanf_s(p, "%s", Args->EntryValueStr, (unsigned)sizeof(Args->EntryValueStr));
+ Args->EntryValueisStr = 0x01;
+ }
+
+ sscanf_s(p, "%x", &(Args->EntryValue));
+
+ // parse optional parameters
+ // for now, only optional params are for entry QCDriverDebugMask
+ // e.g. qdcfg -s QCDriverDebugMask 1 0x7fffffff 0xff 0
+ if ((strcmp(Args->EntryName, "QCDriverDebugMask") == 0) && (Args->EntryValue != 0))
+ {
+ Args->DebugMaxFile = MAX_FILE_SIZE;
+ if ((i != argc - 1) && (argv[i + 1][0] != '-')) // mask
+ {
+ p = argv[++i];
+ sscanf_s(p, "%x", &(Args->DebugFlags));
+
+ if ((i != argc - 1) && (argv[i + 1][0] != '-')) // level
+ {
+ p = argv[++i];
+ sscanf_s(p, "%x", &(Args->DebugLevel));
+ }
+
+ if ((i != argc - 1) && (argv[i + 1][0] != '-')) // maxfile
+ {
+ p = argv[++i];
+ sscanf_s(p, "%d", &(Args->DebugMaxFile));
+ }
+ }
+ }
+ }
+ break;
+ }
+ default:
+ Usage(QC_ERR_UNKNOWN_ARG, argv[i]);
+ return QC_ERR_UNKNOWN_ARG;
+ } // switch
+ }
+ else
+ {
+ Usage(QC_ERR_INVALID_CMD, NULL);
+ if ((strcmp(cmdArgs.EntryName, "QCDriverDebugMask") == 0) && (cmdArgs.Action == 1))
+ {
+ return -1; // ignore optional params for QCDriverDebugMask
+ }
+ else
+ {
+ return QC_ERR_INVALID_CMD;
+ }
+ }
+ }
+
+ // Check required arguments
+ if (Args->NIC_Name[0] == 0)
+ {
+ Usage(QC_ERR_DEV_NAME, NULL);
+ if ((strcmp(cmdArgs.EntryName, "QCDriverDebugMask") == 0) && (cmdArgs.Action == 1))
+ {
+ return -1;
+ }
+ return QC_ERR_DEV_NAME;
+ }
+ return QC_NO_ERROR;
+} // GetCommandLineArguments
+
+void Usage(int ErrorCode, PCHAR Info)
+{
+ char *usage = "\n"
+ "Qualcomm Driver Configuration Tool\n"
+ " Version 4.4 Copyright(c) Qualcomm Inc. 2011. All rights reserved.\n\n"
+ " Usage:\n"
+ " qdcfg <-n Device_Name> [- [EntryValue] [OptParams]] [-h]\n\n"
+ " Options:\n"
+ " -n: the exact name of the device in Device Manager\n"
+ " -g: get EntryValue of EntryName\n"
+ " -s: set EntryValue (default 0) in HEX to EntryName + optional params\n"
+ " -h: help\n\n"
+ " Example:\n"
+ " qdcfg -g QCDevDisableQoS -n \"Qualcomm Wireless Ethernet Adapter 7001\"\n"
+ " qdcfg -s QCDevDisableQoS 2 -n \"Qualcomm Wireless HS-USB Ethernet Adapter 9001\"\n"
+ " qdcfg -s QCDriverDebugMask 1 -n \"Qualcomm USB Modem 3197\"\n"
+ " qdcfg -s QCDriverDebugMask 1 -n \"Qualcomm HS-USB Modem 9001\"\n"
+ " qdcfg -s QCDriverDebugMask 1 -n \"Qualcomm Diagnostics Interface 3197 (COM5)\"\n"
+ " qdcfg -s QCDriverDebugMask 1 -n \"Qualcomm HS-USB Diagnostics 9001 (COM5)\"\n"
+ " qdcfg -s QCDriverDebugMask 1 -n \"Qualcomm WinMobile Diagnostics 3200 (COM5)\"\n"
+ " qdcfg -s QCDriverDebugMask 1 0x03ffffff 0xff 0 -n \"Qualcomm NMEA Device (COM5)\"\n"
+ " qdcfg -s QCDeviceFunction 1 -n \"Qualcomm HS-USB NMEA 9001 (COM5)\"\n"
+ " qdcfg -g QCDeviceFunction -n \"Qualcomm WinMobile NMEA 3200 (COM5)\"\n";
+
+
+ if ((strcmp(cmdArgs.EntryName, "QCDriverDebugMask") == 0) && (cmdArgs.Action == 1))
+ {
+ if (cmdArgs.EntryValue == 0)
+ {
+ StopTracing();
+ StopAutoLogger();
+ }
+ else
+ {
+ StartTracing(cmdArgs.DebugFlags, cmdArgs.DebugLevel, cmdArgs.DebugMaxFile);
+ StartAutoLogger(cmdArgs.DebugFlags, cmdArgs.DebugLevel, cmdArgs.DebugMaxFile);
+ }
+ }
+ else
+ {
+ printfd("%s", usage);
+ DisplaySupportedEntryNames();
+
+ if (ErrorCode != NO_ERROR)
+ {
+ if (Info == NULL)
+ {
+ printfd("\n Error: %s\n", QcErrorMessage[ErrorCode]);
+ }
+ else
+ {
+ printfd("\n Error: %s: <%s>\n", QcErrorMessage[ErrorCode], Info);
+ }
+ }
+ }
+} // Usage
+
+VOID ProcessRegistryEntry(CMD_LINE_ARGS *Args)
+{
+ char controlFileName[SERVICE_FILE_BUF_LEN];
+ char deviceName[SERVICE_FILE_BUF_LEN];
+ HANDLE hDevice;
+ DWORD bytesReturned = 0;
+
+ ZeroMemory(controlFileName, SERVICE_FILE_BUF_LEN);
+ QCWWAN_GetControlFileName(Args->NIC_Name, controlFileName);
+
+ // Also set at run-time if driver is running
+ if (strlen(controlFileName) > 0)
+ {
+ printfd("NIC control file name <%s>\n", controlFileName);
+
+ sprintf_s(deviceName, sizeof(deviceName), "\\\\.\\%s", controlFileName);
+
+ hDevice = CreateFile
+ (
+ deviceName,
+ GENERIC_WRITE | GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL
+ );
+
+ printfd("\n");
+
+ if (hDevice == INVALID_HANDLE_VALUE)
+ {
+ if ((cmdArgs.Action == 1) || (cmdArgs.Action == 2))
+ {
+ printfd("Error: couldn't open device, registry set statically.\n");
+ printfd("Please re-connect device for changes to take effect.");
+ }
+ return;
+ }
+
+ if (strcmp(cmdArgs.EntryName, "QCDriverMediaConnect") == 0)
+ {
+ DeviceIoControl(
+ hDevice,
+ IOCTL_QCDEV_MEDIA_CONNECT,
+ (LPVOID) & (Args->EntryValue),
+ (DWORD)sizeof(ULONG),
+ (LPVOID)NULL,
+ (DWORD)0,
+ &bytesReturned,
+ NULL
+ );
+ CloseHandle(hDevice);
+ return;
+ }
+
+ if (strcmp(cmdArgs.EntryName, "QCDriverMediaDisconnect") == 0)
+ {
+ DeviceIoControl(
+ hDevice,
+ IOCTL_QCDEV_MEDIA_DISCONNECT,
+ (LPVOID) & (Args->EntryValue),
+ (DWORD)sizeof(ULONG),
+ (LPVOID)NULL,
+ (DWORD)0,
+ &bytesReturned,
+ NULL
+ );
+ CloseHandle(hDevice);
+ return;
+ }
+
+ if (strcmp(cmdArgs.EntryName, "QCDriverQMAPDLPause") == 0)
+ {
+ DeviceIoControl(
+ hDevice,
+ IOCTL_QCDEV_PAUSE_QMAP_DL,
+ (LPVOID) & (Args->EntryValue),
+ (DWORD)sizeof(ULONG),
+ (LPVOID)NULL,
+ (DWORD)0,
+ &bytesReturned,
+ NULL
+ );
+ CloseHandle(hDevice);
+ return;
+ }
+
+ if (strcmp(cmdArgs.EntryName, "QCDriverQMAPDLResume") == 0)
+ {
+ DeviceIoControl(
+ hDevice,
+ IOCTL_QCDEV_RESUME_QMAP_DL,
+ (LPVOID) & (Args->EntryValue),
+ (DWORD)sizeof(ULONG),
+ (LPVOID)NULL,
+ (DWORD)0,
+ &bytesReturned,
+ NULL
+ );
+ CloseHandle(hDevice);
+ return;
+ }
+
+ if (strcmp(cmdArgs.EntryName, "DLResume") == 0)
+ {
+ DeviceIoControl(
+ hDevice,
+ IOCTL_QCDEV_RESUME_DL,
+ (LPVOID) & (Args->EntryValue),
+ (DWORD)sizeof(ULONG),
+ (LPVOID)NULL,
+ (DWORD)0,
+ &bytesReturned,
+ NULL
+ );
+ CloseHandle(hDevice);
+ return;
+ }
+
+ if ((strcmp(cmdArgs.EntryName, "QCDriverDebugMask") != 0) ||
+ (cmdArgs.Action != 1))
+ {
+ CloseHandle(hDevice);
+ return;
+ }
+
+ Args->DebugMask = Args->EntryValue;
+
+ if (DeviceIoControl(
+ hDevice,
+ IOCTL_QCDEV_SET_DBG_UMSK,
+ (LPVOID) & (Args->DebugMask),
+ (DWORD)sizeof(ULONG),
+ (LPVOID)NULL,
+ (DWORD)0,
+ &bytesReturned,
+ NULL
+ )
+ )
+ {
+ printfd("Registry set successfully at run-time: 0x%x\n", Args->DebugMask);
+ }
+ else
+ {
+ if (DeviceIoControl(
+ hDevice,
+ IOCTL_QCUSB_SET_DBG_UMSK,
+ (LPVOID) & (Args->DebugMask),
+ (DWORD)sizeof(ULONG),
+ (LPVOID)NULL,
+ (DWORD)0,
+ &bytesReturned,
+ NULL
+ )
+ )
+ {
+ printfd("Registry set successfully at run-time: 0x%x\n", Args->DebugMask);
+ }
+ else
+ {
+ printfd("Error: Registry couldn't be set at run-time, please power cycle device. %d\n", GetLastError());
+ }
+ }
+
+ CloseHandle(hDevice);
+ }
+ else
+ {
+ if (cmdArgs.Action == 1)
+ {
+ printfd("If successful, please re-connect device for changes to take effect.\n");
+ }
+ }
+} // ProcessRegistryEntry
+
+const char *getQUDVersionNum()
+{
+#ifdef QC_USB_DRIVERS_PRODUCT_VERSION
+ return QCSTR(QC_USB_DRIVERS_PRODUCT_VERSION);
+#else
+ return "1.00.00.0";
+#endif // DEBUG
+}
+
+#ifdef QDCFG_EXE
+int main(int argc, char *argv[])
+{
+ return processCommand(argc - 1, argv + 1);
+}
+#endif // QDCFG_EXE
diff --git a/src/windows/tools/qdcfg/main.h b/src/windows/tools/qdcfg/main.h
new file mode 100644
index 0000000..ff4e98e
--- /dev/null
+++ b/src/windows/tools/qdcfg/main.h
@@ -0,0 +1,77 @@
+/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*
+
+ M A I N . H
+
+GENERAL DESCRIPTION
+ This file defines data structures, macros, and function prototypes
+ for the qdcfg driver configuration utility.
+
+ Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ SPDX-License-Identifier: BSD-3-Clause
+
+*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/
+
+#ifndef MAIN_H
+#define MAIN_H
+
+#include
+#include
+#include
+#include
+#include "api.h"
+
+/* Left-shifted flag of dev type in device change callback */
+#define QC_DEV_TYPE2_NONE 0x00
+#define QC_DEV_TYPE2_NET 0x01 /* Net -> DevFlag.DEV_TYPE_NET */
+#define QC_DEV_TYPE2_PORTS 0x02 /* Diag -> DevFlag.DEV_TYPE_PORTS */
+#define QC_DEV_TYPE2_USB 0x04 /* QDSS/DPL -> DevFlag.DEV_TYPE_USB */
+#define QC_DEV_TYPE2_FLT 0x08 /* Filter -> DevFlag.DEV_TYPE_FILT */
+#define QC_DEV_TYPE2_ALL 0xff
+
+typedef struct _CMD_COMMAND
+{
+ char* entry; // "QCDriverDebugMask"
+ char* usage; // "0x10 (default) to 0xFF"
+ int isExposed;
+ BYTE devType;
+ int defaultVal;
+ char* intro; // friendly usage
+} CMD_COMMAND;
+
+typedef struct _CMD_LINE_ARGS
+{
+ int DebugMask;
+ char NIC_Name[SERVICE_FILE_BUF_LEN];
+ BOOL IsCOM;
+
+ char EntryName[128];
+ int EntryValue;
+ int Action; // 0: get; 1: set; 2: runtime only
+ int DebugFlags;
+ int DebugLevel;
+ int DebugMaxFile;
+ int EntryValueisStr;
+ char EntryValueStr[1024];
+} CMD_LINE_ARGS, *PCMD_LINE_ARGS;
+
+// Function Prototypes
+int GetCommandLineArguments(int argc, char *argv[], PCMD_LINE_ARGS Args);
+void Usage(INT, PCHAR);
+VOID ProcessRegistryEntry(CMD_LINE_ARGS* Args);
+
+// Driver Configuration APIs
+__declspec(dllexport) UINT getSupportedEntries(void);
+__declspec(dllexport) int getEntryLen(UINT index);
+__declspec(dllexport) int getUsageLen(UINT index);
+__declspec(dllexport) int getCommand(CMD_COMMAND* buffer, UINT index);
+__declspec(dllexport) int processCommand(int argc, LPTSTR argv[]);
+__declspec(dllexport) int executeCommand(char* entry, char action, char** params, int paramLen, BOOL device, char* deviceName);
+__declspec(dllexport) const char* getQUDVersionNum();
+
+// Delegate version of printf
+typedef int (_cdecl* PRINTF_DELEGATE) (char* message);
+static PRINTF_DELEGATE hidden_printer = NULL;
+__declspec(dllexport) void setPrintDelegate(PRINTF_DELEGATE printer);
+void printfd(const char* const format, ...);
+
+#endif // MAIN_H
diff --git a/src/windows/tools/qdcfg/qdcfg.sln b/src/windows/tools/qdcfg/qdcfg.sln
new file mode 100644
index 0000000..7e77e9e
--- /dev/null
+++ b/src/windows/tools/qdcfg/qdcfg.sln
@@ -0,0 +1,31 @@
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.32630.194
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qdcfg", "qdcfg.vcxproj", "{9130613B-D0F3-E81D-DD33-A6DF52E015FC}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug_EXE|Win32 = Debug_EXE|Win32
+ Debug|Win32 = Debug|Win32
+ Release_EXE|Win32 = Release_EXE|Win32
+ Release|Win32 = Release|Win32
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {9130613B-D0F3-E81D-DD33-A6DF52E015FC}.Debug_EXE|Win32.ActiveCfg = Debug_EXE|Win32
+ {9130613B-D0F3-E81D-DD33-A6DF52E015FC}.Debug_EXE|Win32.Build.0 = Debug_EXE|Win32
+ {9130613B-D0F3-E81D-DD33-A6DF52E015FC}.Debug|Win32.ActiveCfg = Debug|Win32
+ {9130613B-D0F3-E81D-DD33-A6DF52E015FC}.Debug|Win32.Build.0 = Debug|Win32
+ {9130613B-D0F3-E81D-DD33-A6DF52E015FC}.Release_EXE|Win32.ActiveCfg = Release_EXE|Win32
+ {9130613B-D0F3-E81D-DD33-A6DF52E015FC}.Release_EXE|Win32.Build.0 = Release_EXE|Win32
+ {9130613B-D0F3-E81D-DD33-A6DF52E015FC}.Release|Win32.ActiveCfg = Release|Win32
+ {9130613B-D0F3-E81D-DD33-A6DF52E015FC}.Release|Win32.Build.0 = Release|Win32
+ {9130613B-D0F3-E81D-DD33-A6DF52E015FC}.Release|Win32.Deploy.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {2C2C1340-8000-45E7-A499-F5386B9342AB}
+ EndGlobalSection
+EndGlobal
diff --git a/src/windows/tools/qdcfg/qdcfg.vcxproj b/src/windows/tools/qdcfg/qdcfg.vcxproj
new file mode 100644
index 0000000..af81fd4
--- /dev/null
+++ b/src/windows/tools/qdcfg/qdcfg.vcxproj
@@ -0,0 +1,192 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Debug_EXE
+ Win32
+
+
+ Release
+ Win32
+
+
+ Release_EXE
+ Win32
+
+
+
+ qdcfg
+ {9130613B-D0F3-E81D-DD33-A6DF52E015FC}
+ 10.0
+
+
+
+ DynamicLibrary
+ v142
+
+
+ Application
+ v142
+
+
+ DynamicLibrary
+ v142
+
+
+ Application
+ v142
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ false
+
+
+ false
+
+
+ true
+
+
+ true
+
+
+
+ MultiThreaded
+ OnlyExplicitInline
+ MaxSpeed
+ true
+ Level4
+ WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
+
+
+ .\Release\qdcfg.tlb
+
+
+ 0x0409
+ NDEBUG;%(PreprocessorDefinitions)
+
+
+ true
+ .\Release\qdcfg.bsc
+
+
+ true
+ Console
+ odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+
+
+
+
+ MultiThreaded
+ OnlyExplicitInline
+ MaxSpeed
+ true
+ Level4
+ WIN32;NDEBUG;_CONSOLE;QDCFG_EXE;%(PreprocessorDefinitions)
+
+
+ .\Release_EXE\qdcfg.tlb
+
+
+ 0x0409
+ NDEBUG;%(PreprocessorDefinitions)
+
+
+ true
+ .\Release_EXE\qdcfg.bsc
+
+
+ true
+ Console
+ odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+
+
+
+
+ MultiThreadedDebug
+ Default
+ Disabled
+ true
+ Level4
+ WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)
+
+
+ .\Debug\qdcfg.tlb
+
+
+ 0x0409
+ _DEBUG;%(PreprocessorDefinitions)
+
+
+ true
+ .\Debug\qdcfg.bsc
+
+
+ true
+ Console
+ odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+
+
+
+
+ MultiThreadedDebug
+ Default
+ Disabled
+ true
+ Level4
+ WIN32;_DEBUG;_CONSOLE;QDCFG_EXE;%(PreprocessorDefinitions)
+
+
+ .\Debug_EXE\qdcfg.tlb
+
+
+ 0x0409
+ _DEBUG;%(PreprocessorDefinitions)
+
+
+ true
+ .\Debug_EXE\qdcfg.bsc
+
+
+ true
+ Console
+ odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/windows/tools/qdcfg/reg.c b/src/windows/tools/qdcfg/reg.c
new file mode 100644
index 0000000..2ced878
--- /dev/null
+++ b/src/windows/tools/qdcfg/reg.c
@@ -0,0 +1,745 @@
+/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*
+
+ R E G . C
+
+GENERAL DESCRIPTION
+ This file implements registry access functions for querying and
+ configuring Qualcomm USB driver settings.
+
+ Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ SPDX-License-Identifier: BSD-3-Clause
+
+*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/
+
+#include "main.h"
+#include "reg.h"
+
+extern CMD_LINE_ARGS cmdArgs;
+
+BOOL QueryKey
+(
+ HKEY hKey,
+ PCHAR DeviceFriendlyName,
+ PCHAR ControlFileName,
+ PCHAR FullKeyName
+)
+{
+ TCHAR subKey[MAX_KEY_LENGTH];
+ DWORD nameLen;
+ TCHAR className[MAX_PATH] = TEXT("");
+ DWORD classNameLen = MAX_PATH;
+ DWORD numSubKeys = 0, subKeyMaxLen;
+ DWORD classMaxLen;
+ DWORD numKeyValues, valueMaxLen, valueDataMaxLen;
+ DWORD securityDescLen;
+ FILETIME lastWriteTime;
+ char fullKeyName[MAX_KEY_LENGTH];
+ DWORD i, retCode;
+
+ // Get the class name and the value count.
+ retCode = RegQueryInfoKey
+ (
+ hKey,
+ className,
+ &classNameLen,
+ NULL,
+ &numSubKeys,
+ &subKeyMaxLen,
+ &classMaxLen,
+ &numKeyValues,
+ &valueMaxLen,
+ &valueDataMaxLen,
+ &securityDescLen,
+ &lastWriteTime
+ );
+
+ // Enumerate the subkeys, until RegEnumKeyEx fails.
+
+ if (numSubKeys)
+ {
+ for (i = 0; i < numSubKeys; i++)
+ {
+ nameLen = MAX_KEY_LENGTH;
+ retCode = RegEnumKeyEx
+ (
+ hKey, i,
+ subKey,
+ &nameLen,
+ NULL,
+ NULL,
+ NULL,
+ &lastWriteTime
+ );
+ if (retCode == ERROR_SUCCESS)
+ {
+ BOOL result;
+
+ // _tprintf(TEXT("C-(%d) %s\n"), i+1, subKey);
+ sprintf_s(fullKeyName, sizeof(fullKeyName), "%s\\%s", FullKeyName, subKey);
+ result = FindDeviceInstance
+ (
+ fullKeyName,
+ DeviceFriendlyName,
+ ControlFileName
+ );
+ if (result == TRUE)
+ {
+ printfd("Device HW Key <%s>\n", fullKeyName);
+ if ((strcmp(cmdArgs.EntryName, "QCDeviceMuxEnable") == 0) ||
+ (strcmp(cmdArgs.EntryName, "QCDeviceStartIf") == 0) ||
+ (strcmp(cmdArgs.EntryName, "QCDeviceNumIf") == 0) ||
+ (strcmp(cmdArgs.EntryName, "QCDeviceNumMuxIf") == 0))
+ {
+ }
+ else
+ {
+ return TRUE;
+ }
+ }
+ }
+ }
+ }
+
+ return FALSE;
+} // QueryKey
+
+BOOL FindDeviceInstance
+(
+ PTCHAR InstanceKey,
+ PCHAR DeviceFriendlyName,
+ PCHAR ControlFileName
+)
+{
+ HKEY hTestKey;
+ BOOL ret = FALSE;
+
+ if (RegOpenKeyEx
+ (
+ HKEY_LOCAL_MACHINE,
+ InstanceKey,
+ 0,
+ KEY_READ,
+ &hTestKey
+ ) == ERROR_SUCCESS
+ )
+ {
+ ret = QCWWAN_GetEntryValue
+ (
+ hTestKey,
+ DeviceFriendlyName,
+ "QCDeviceControlFile",
+ ControlFileName
+ );
+
+ RegCloseKey(hTestKey);
+ }
+
+ return ret;
+}
+
+BOOL QCWWAN_GetEntryValue
+(
+ HKEY hKey,
+ PCHAR DeviceFriendlyName,
+ PCHAR EntryName,
+ PCHAR ControlFileName
+)
+{
+ DWORD retCode = ERROR_SUCCESS;
+ TCHAR valueName[MAX_VALUE_NAME], swKey[MAX_VALUE_NAME];
+ DWORD valueNameLen = MAX_VALUE_NAME;
+ BOOL instanceFound = FALSE;
+ HKEY hSwKey;
+
+ valueName[0] = 0;
+
+ // first get device friendly name
+ retCode = RegQueryValueEx
+ (
+ hKey,
+ "FriendlyName", // "DriverDesc", value name
+ NULL, // reserved
+ NULL, // returned type
+ (LPBYTE)valueName,
+ &valueNameLen
+ );
+
+ if (retCode == ERROR_SUCCESS)
+ {
+ valueName[valueNameLen] = 0;
+
+ if (strcmp(valueName, DeviceFriendlyName) == 0)
+ {
+ instanceFound = TRUE;
+ }
+ // printfd("D-FriendlyName-%d <%s>\n", instanceFound, valueName);
+ }
+ else
+ {
+ // no FriendlyName, get DeviceDesc
+ valueName[0] = 0;
+ valueNameLen = MAX_VALUE_NAME;
+ retCode = RegQueryValueEx
+ (
+ hKey,
+ "DeviceDesc", // value name
+ NULL, // reserved
+ NULL, // returned type
+ (LPBYTE)valueName,
+ &valueNameLen
+ );
+ if (retCode == ERROR_SUCCESS)
+ {
+ int nameLen = strlen(DeviceFriendlyName) + 1; // include NULL to match valueNameLen
+ PCHAR p;
+
+ valueName[valueNameLen] = 0;
+ if (valueNameLen >= (DWORD)nameLen)
+ {
+ p = valueName + valueNameLen - nameLen; // for VISTA -- only compare later part
+ if (strcmp(p, DeviceFriendlyName) == 0)
+ {
+ instanceFound = TRUE;
+ }
+ }
+ // printfd(" D-DeviceDesc:reg-%d (%uB) <%s>\n", instanceFound, strlen(valueName), valueName);
+ // printfd(" D-DeviceDesc:frn-%d (%uB) <%s>\n", instanceFound, strlen(DeviceFriendlyName), DeviceFriendlyName);
+ }
+ }
+
+ if (instanceFound == TRUE)
+ {
+ // Get "Driver" instance path
+ valueName[0] = 0;
+ valueNameLen = MAX_VALUE_NAME;
+ retCode = RegQueryValueEx
+ (
+ hKey,
+ "Driver", // value name
+ NULL, // reserved
+ NULL, // returned type
+ (LPBYTE)valueName,
+ &valueNameLen
+ );
+ if (retCode == ERROR_SUCCESS)
+ {
+ // Construct device software key
+ valueName[valueNameLen] = 0;
+ sprintf_s(swKey, sizeof(swKey), "%s\\%s", QCNET_REG_SW_KEY, valueName);
+
+ printfd("Device SW Key <%s>\n", swKey);
+
+ // Open device software key
+ retCode = RegOpenKeyEx
+ (
+ HKEY_LOCAL_MACHINE,
+ swKey,
+ 0,
+ (KEY_READ | KEY_WRITE),
+ &hSwKey
+ );
+
+ if (retCode == ERROR_SUCCESS)
+ {
+ // Retrieve the control file name
+ valueName[0] = 0;
+ valueNameLen = MAX_VALUE_NAME;
+ retCode = RegQueryValueEx
+ (
+ hSwKey,
+ EntryName, // value name
+ NULL, // reserved
+ NULL, // returned type
+ (LPBYTE)valueName,
+ &valueNameLen
+ );
+
+ // Check the other port control object
+ if (retCode != ERROR_SUCCESS)
+ {
+ retCode = RegQueryValueEx
+ (
+ hSwKey,
+ "AssignedPortForQCDevice", // value name
+ NULL, // reserved
+ NULL, // returned type
+ (LPBYTE)valueName,
+ &valueNameLen
+ );
+ }
+ if (retCode == ERROR_SUCCESS)
+ {
+ PCHAR p = (PCHAR)valueName + valueNameLen;
+
+ valueName[valueNameLen] = 0;
+
+ while ((p > valueName) && (*p != '\\'))
+ {
+ p--;
+ }
+ if (*p == '\\') p++;
+ strcpy_s(ControlFileName, SERVICE_FILE_BUF_LEN, p);
+ }
+
+ // set/get arbitrary entry under SW key
+ if (cmdArgs.EntryName[0] != 0)
+ {
+ // set
+ if (cmdArgs.Action == 1)
+ {
+ if (cmdArgs.EntryValueisStr == 0x01)
+ {
+ retCode = RegSetValueEx
+ (
+ hSwKey,
+ cmdArgs.EntryName,
+ 0, // reserved
+ REG_SZ, // type
+ (LPBYTE)&cmdArgs.EntryValueStr,
+ strlen(cmdArgs.EntryValueStr) + 1
+ );
+ if (retCode == ERROR_SUCCESS)
+ {
+ printfd("Entry <%s> set to %s\n", cmdArgs.EntryName, cmdArgs.EntryValueStr);
+ }
+ else
+ {
+ printfd("Error: Entry <%s> failure: %u\n", cmdArgs.EntryName, retCode);
+ }
+ }
+ else
+ {
+ retCode = RegSetValueEx
+ (
+ hSwKey,
+ cmdArgs.EntryName,
+ 0, // reserved
+ REG_DWORD, // type
+ (LPBYTE)&cmdArgs.EntryValue,
+ sizeof(DWORD)
+ );
+ if (retCode == ERROR_SUCCESS)
+ {
+ printfd("Entry <%s> set to 0x%x\n", cmdArgs.EntryName, cmdArgs.EntryValue);
+ }
+ else
+ {
+ printfd("Error: Entry <%s> failure: %u\n", cmdArgs.EntryName, retCode);
+ }
+ }
+ }
+ else if (cmdArgs.Action == 0)
+ {
+ // get
+ valueName[0] = 0;
+ valueNameLen = MAX_VALUE_NAME;
+ retCode = RegQueryValueEx
+ (
+ hSwKey,
+ cmdArgs.EntryName, // value name
+ NULL, // reserved
+ NULL, // returned type
+ (LPBYTE)valueName,
+ &valueNameLen
+ );
+
+ if (retCode == ERROR_SUCCESS)
+ {
+ valueName[valueNameLen] = 0;
+
+ printfd("Entry <%s> has value of 0x%x\n", cmdArgs.EntryName, *((DWORD *)valueName));
+ }
+ else
+ {
+ printfd("Error: Entry <%s> does not exist\n", cmdArgs.EntryName);
+ }
+ }
+ }
+
+ RegCloseKey(hSwKey);
+ return TRUE;
+ } // if (retCode == ERROR_SUCCESS)
+ } // if (retCode == ERROR_SUCCESS)
+ else
+ {
+ printfd("Error: cannot get device software key, retCode %u\n", retCode);
+ }
+ } // if (instanceFound == TRUE)
+
+ return FALSE;
+}
+
+BOOL QCWWAN_GetControlFileName
+(
+ PCHAR DeviceFriendlyName,
+ PCHAR ControlFileName
+)
+{
+ HKEY hTestKey;
+
+ if (RegOpenKeyEx
+ (
+ HKEY_LOCAL_MACHINE,
+ TEXT(QCNET_REG_HW_KEY),
+ 0,
+ KEY_READ,
+ &hTestKey
+ ) == ERROR_SUCCESS
+ )
+ {
+ TCHAR subKey[MAX_KEY_LENGTH];
+ DWORD nameLen;
+ TCHAR className[MAX_PATH] = TEXT("");
+ DWORD classNameLen = MAX_PATH;
+ DWORD numSubKeys = 0, subKeyMaxLen;
+ DWORD classMaxLen;
+ DWORD numKeyValues, valueMaxLen, valueDataMaxLen;
+ DWORD securityDescLen;
+ FILETIME lastWriteTime;
+ DWORD i, retCode;
+
+ // Get the class name and the value count.
+ retCode = RegQueryInfoKey
+ (
+ hTestKey,
+ className,
+ &classNameLen,
+ NULL,
+ &numSubKeys,
+ &subKeyMaxLen,
+ &classMaxLen,
+ &numKeyValues,
+ &valueMaxLen,
+ &valueDataMaxLen,
+ &securityDescLen,
+ &lastWriteTime
+ );
+
+ // Enumerate the subkeys, until RegEnumKeyEx fails.
+ if (numSubKeys)
+ {
+ for (i = 0; i < numSubKeys; i++)
+ {
+ nameLen = MAX_KEY_LENGTH;
+ retCode = RegEnumKeyEx
+ (
+ hTestKey, i,
+ subKey,
+ &nameLen,
+ NULL,
+ NULL,
+ NULL,
+ &lastWriteTime
+ );
+ if (retCode == ERROR_SUCCESS)
+ {
+ BOOL result;
+
+ result = QueryUSBDeviceKeys
+ (
+ subKey,
+ DeviceFriendlyName,
+ ControlFileName
+ );
+ if (result == TRUE)
+ {
+ printfd("QueryKey: TRUE\n");
+ return TRUE;
+ }
+ }
+ }
+ }
+
+ RegCloseKey(hTestKey);
+ if ((strcmp(cmdArgs.EntryName, "QCDeviceMuxEnable") == 0) ||
+ (strcmp(cmdArgs.EntryName, "QCDeviceStartIf") == 0) ||
+ (strcmp(cmdArgs.EntryName, "QCDeviceNumIf") == 0) ||
+ (strcmp(cmdArgs.EntryName, "QCDeviceNumMuxIf") == 0))
+ {
+ }
+ else
+ {
+ printfd("Error: Device <%s> does not exist\n", DeviceFriendlyName);
+ }
+
+ return retCode;
+ }
+
+ printfd("Error: Fail to access registry\n");
+
+ return FALSE;
+}
+
+BOOL QueryUSBDeviceKeys
+(
+ PTCHAR InstanceKey,
+ PCHAR DeviceFriendlyName,
+ PCHAR ControlFileName
+)
+{
+ HKEY hTestKey;
+ char fullKeyName[MAX_KEY_LENGTH];
+ BOOL ret = FALSE;
+
+ sprintf_s(fullKeyName, sizeof(fullKeyName), "%s\\%s", QCNET_REG_HW_KEY, InstanceKey);
+
+ // printfd("B-full key name: [%s]\n", fullKeyName);
+
+ if (RegOpenKeyEx
+ (
+ HKEY_LOCAL_MACHINE,
+ fullKeyName,
+ 0,
+ KEY_READ,
+ &hTestKey
+ ) == ERROR_SUCCESS
+ )
+ {
+ ret = QueryKey(hTestKey, DeviceFriendlyName, ControlFileName, fullKeyName);
+ RegCloseKey(hTestKey);
+
+ return ret;
+ }
+ else
+ {
+ printfd("Error: couldn't open registry\n");
+ }
+
+ return FALSE;
+} // QueryUSBDeviceKeys
+
+ULONG InspectLogging(PCHAR deviceFriendlyName, PULONG errorCode)
+{
+ // return 0 if logging is disabled, 1 otherwise
+ if (deviceFriendlyName == NULL)
+ {
+ *errorCode = ERROR_INVALID_PARAMETER;
+ return 0;
+ }
+
+ DWORD entryValue = 0;
+ *errorCode = InspectEntry(deviceFriendlyName, "QCDriverDebugMask", &entryValue);
+ if (*errorCode)
+ {
+ return 0;
+ }
+ else
+ {
+ return entryValue;
+ }
+}
+
+ULONG InspectEntry(PCHAR deviceFriendlyName, PCHAR entryName, PDWORD entryValue)
+{
+ // open parent regkey for all usb devices
+ HKEY hKey;
+ ULONG errorCode = RegOpenKeyEx
+ (
+ HKEY_LOCAL_MACHINE,
+ TEXT(QCNET_REG_HW_KEY),
+ 0,
+ KEY_READ,
+ &hKey
+ );
+
+ if (errorCode == ERROR_SUCCESS)
+ {
+ // query # of USB hwkeys
+ DWORD numSubKeys;
+ errorCode = RegQueryInfoKey
+ (
+ hKey,
+ NULL,
+ NULL,
+ NULL,
+ &numSubKeys,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+ );
+
+ if (errorCode == ERROR_SUCCESS)
+ {
+ DWORD nameLen;
+ TCHAR keyNameBuffer[MAX_KEY_LENGTH];
+
+ for (DWORD i = 0; i < numSubKeys; i++)
+ {
+ nameLen = MAX_KEY_LENGTH;
+
+ if (RegEnumKeyEx
+ (
+ hKey, i,
+ keyNameBuffer,
+ &nameLen,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+ ) == ERROR_SUCCESS)
+ {
+ errorCode = QueryUSBDeviceKeys2
+ (
+ keyNameBuffer,
+ deviceFriendlyName,
+ keyNameBuffer,
+ MAX_KEY_LENGTH
+ );
+
+ if (errorCode == ERROR_SUCCESS)
+ {
+ // QC Device found, query the corresponding swkey
+ TCHAR InstanceKey[MAX_KEY_LENGTH];
+ HKEY swKey;
+
+ sprintf_s(InstanceKey, sizeof(InstanceKey), "%s\\%s", QCNET_REG_SW_KEY, keyNameBuffer);
+ errorCode = RegOpenKeyEx
+ (
+ HKEY_LOCAL_MACHINE,
+ InstanceKey,
+ 0,
+ KEY_READ,
+ &swKey
+ );
+
+ DWORD valueLen = sizeof(DWORD);
+ if (errorCode == ERROR_SUCCESS)
+ {
+ errorCode = RegQueryValueEx
+ (
+ swKey,
+ entryName,
+ NULL,
+ NULL,
+ (LPBYTE)entryValue,
+ &valueLen
+ );
+
+ return errorCode;
+ }
+ else
+ {
+ printfd("Error: couldn't open registry\n");
+ return errorCode;
+ }
+ }
+ }
+ }
+ errorCode = ERROR_FILE_NOT_FOUND;
+ }
+ RegCloseKey(hKey);
+ }
+ return errorCode;
+}
+
+ULONG QueryUSBDeviceKeys2(PCHAR rootKey, PCHAR deviceFriendlyName, PCHAR swKeyBuffer, SIZE_T swKeyBufferSize)
+{
+ // return ERROR_SUCCESS if a name-matched QC device hwKey is found, store the driver instance into buffer
+ // otherwise a System Error Code will be returned, buffer is untouched
+
+ TCHAR fullKeyName[MAX_KEY_LENGTH];
+ HKEY subKey;
+
+ sprintf_s(fullKeyName, sizeof(fullKeyName), "%s\\%s", QCNET_REG_HW_KEY, rootKey);
+
+ ULONG ret = RegOpenKeyEx
+ (
+ HKEY_LOCAL_MACHINE,
+ fullKeyName,
+ 0,
+ KEY_READ,
+ &subKey
+ );
+
+ DWORD numSubKeys;
+ if (ret == ERROR_SUCCESS)
+ {
+ ret = RegQueryInfoKey
+ (
+ subKey,
+ NULL,
+ NULL,
+ NULL,
+ &numSubKeys,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+ );
+
+ if (ret == ERROR_SUCCESS)
+ {
+ DWORD nameLen;
+ TCHAR subKeyName[MAX_KEY_LENGTH];
+
+ for (DWORD i = 0; i < numSubKeys; i++)
+ {
+ nameLen = MAX_KEY_LENGTH;
+ ret = RegEnumKeyEx
+ (
+ subKey, i,
+ subKeyName,
+ &nameLen,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+ );
+
+ if (ret == ERROR_SUCCESS)
+ {
+ HKEY qcKey;
+ ret = RegOpenKeyEx
+ (
+ subKey,
+ subKeyName,
+ 0,
+ KEY_READ,
+ &qcKey
+ );
+
+ if (ret == ERROR_SUCCESS)
+ {
+ DWORD valueBufferSize = strlen(deviceFriendlyName) + 1;
+ PTCHAR valueBuffer = (PTCHAR)malloc(valueBufferSize); // buffer for qc device friendly name
+
+ if (valueBuffer == NULL)
+ {
+ RegCloseKey(subKey);
+ RegCloseKey(qcKey);
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ ret = RegQueryValueEx(qcKey, "FriendlyName", NULL, NULL, (LPBYTE)valueBuffer, &valueBufferSize);
+
+ if (ret == ERROR_SUCCESS)
+ {
+ if (strcmp(valueBuffer, deviceFriendlyName) == 0) // device found, retrive and copy the swkey
+ {
+ RegCloseKey(subKey);
+ DWORD swKeyBufLen = (DWORD)swKeyBufferSize;
+ ret = RegQueryValueEx(qcKey, "Driver", NULL, NULL, (LPBYTE)swKeyBuffer, &swKeyBufLen);
+ RegCloseKey(qcKey);
+ free(valueBuffer);
+ return ret;
+ }
+ }
+ free(valueBuffer);
+ RegCloseKey(qcKey);
+ }
+ }
+ }
+ ret = ERROR_FILE_NOT_FOUND;
+ }
+ RegCloseKey(subKey);
+ }
+
+ return ret;
+}
diff --git a/src/windows/tools/qdcfg/reg.h b/src/windows/tools/qdcfg/reg.h
new file mode 100644
index 0000000..f365133
--- /dev/null
+++ b/src/windows/tools/qdcfg/reg.h
@@ -0,0 +1,74 @@
+/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*
+
+ R E G . H
+
+GENERAL DESCRIPTION
+ This file defines constants and function prototypes for registry
+ access used by the qdcfg driver configuration utility.
+
+ Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ SPDX-License-Identifier: BSD-3-Clause
+
+*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/
+
+#ifndef REG_H
+#define REG_H
+
+#define QCNET_REG_HW_KEY "SYSTEM\\CurrentControlSet\\Enum\\USB"
+#define QCNET_REG_SW_KEY "SYSTEM\\CurrentControlSet\\Control\\Class"
+
+#define MAX_KEY_LENGTH 255
+#define MAX_VALUE_NAME 6144
+
+BOOL QueryKey
+(
+ HKEY hKey,
+ PCHAR DeviceFriendlyName,
+ PCHAR ControlFileName,
+ PCHAR FullKeyName
+);
+
+BOOL FindDeviceInstance
+(
+ PTCHAR InstanceKey,
+ PCHAR DeviceFriendlyName,
+ PCHAR ControlFileName
+);
+
+BOOL QCWWAN_GetEntryValue
+(
+ HKEY hKey,
+ PCHAR DeviceFriendlyName,
+ PCHAR EntryName,
+ PCHAR ControlFileName
+);
+
+BOOL QCWWAN_GetControlFileName
+(
+ PCHAR DeviceFriendlyName,
+ PCHAR ControlFileName
+);
+
+BOOL QueryUSBDeviceKeys
+(
+ PTCHAR InstanceKey,
+ PCHAR DeviceFriendlyName,
+ PCHAR ControlFileName
+);
+
+__declspec(dllexport) ULONG InspectLogging
+(
+ PCHAR deviceFriendlyName,
+ PULONG errorCode
+);
+
+__declspec(dllexport) ULONG InspectEntry
+(
+ PCHAR deviceFriendlyName,
+ PCHAR entryName,
+ PDWORD entryValue
+);
+
+ULONG QueryUSBDeviceKeys2(PCHAR rootKey, PCHAR deviceFriendlyName, PCHAR swKeyBuffer, SIZE_T swKeyBufferSize);
+
+#endif // REG_H
diff --git a/src/windows/tools/qdcfg/trace.c b/src/windows/tools/qdcfg/trace.c
new file mode 100644
index 0000000..acd8637
--- /dev/null
+++ b/src/windows/tools/qdcfg/trace.c
@@ -0,0 +1,571 @@
+/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*
+
+ T R A C E . C
+
+GENERAL DESCRIPTION
+ This file implements ETW trace logging management (start/stop tracing
+ and AutoLogger configuration) for Qualcomm USB drivers.
+
+ Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ SPDX-License-Identifier: BSD-3-Clause
+
+*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/
+
+#include
+#include
+#include "reg.h"
+#include "trace.h"
+
+extern void printfd(const char *const format, ...);
+
+// Default Guid
+static const GUID ProviderGuid =
+{0x5F02CA82, 0xB28B, 0x4a95, { 0xBA, 0x2C, 0xFB, 0xED, 0x52, 0xDD, 0xD3, 0xDC }};
+
+typedef struct
+{
+ char *Name;
+ char DataType; // REG_DWORD or REG_SZ
+ union
+ {
+ long LongData;
+ char *StringData;
+ } Data;
+} RegEntry, *pRegEntry;
+
+
+// Registry settings for AUTOLOGGER_REG_KEY
+#define INDEX_MAX_FILE 0
+
+RegEntry RegAutoLoggerSession[] =
+{
+ {"MaxFileSize", REG_DWORD, MAX_FILE_SIZE}, // must be index INDEX_MAX_FILE
+ {"ClockType", REG_DWORD, CLOCKTYPE},
+ {"FlushTimer", REG_DWORD, 0},
+ {"LogFileMode", REG_DWORD, LOG_FILE_MODE},
+ {"Start", REG_DWORD, 1},
+ {"FileName", REG_SZ, (long)AUTOLOGGER_LOGFILE_PATH},
+ {"Guid", REG_SZ, (long)AUTOLOGGER_GUID}
+};
+
+RegEntry RegAutoLoggerSessionOff[] =
+{
+ {"Start", REG_DWORD, 0}
+};
+
+// Registry settings for AUTOLOGGER_REG_GUID_KEY
+#define INDEX_FLAGS 0
+#define INDEX_LEVEL 1
+
+RegEntry RegAutoLoggerSessionGuid[] =
+{
+ {"EnableFlags", REG_DWORD, DEFAULT_FLAGS}, // must be index INDEX_FLAGS
+ {"EnableLevel", REG_DWORD, DEFAULT_LEVEL}, // must be index INDEX_LEVEL
+ {"Enabled", REG_DWORD, 1},
+};
+
+// Registry settings for GLOBALLOGGER_REG_KEY
+RegEntry RegGlobalLoggerSession[] =
+{
+ {"FileMax", REG_DWORD, MAX_FILE_SIZE}, // must be index INDEX_MAX_FILE
+ {"Start", REG_DWORD, 1},
+ {"FileName", REG_SZ, (long)GLOBALLOGGER_LOGFILE_PATH},
+};
+
+RegEntry RegGlobalLoggerSessionOff[] =
+{
+ {"Start", REG_DWORD, 0}
+};
+
+
+// Registry settings for GLOBALLOGGER_REG_GUID_KEY
+RegEntry RegGlobalLoggerSessionGuid[] =
+{
+ {"Flags", REG_DWORD, DEFAULT_FLAGS}, // must be index INDEX_FLAGS
+ {"Level", REG_DWORD, DEFAULT_LEVEL}, // must be index INDEX_LEVEL
+ {"FlushTimer", REG_DWORD, 0},
+};
+
+
+static void DisplayStatus(LONG status, char *msg)
+{
+ if (ERROR_SUCCESS != status)
+ {
+ LPVOID lpBuffer;
+ FormatMessage(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL,
+ status,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPSTR)&lpBuffer,
+ 100,
+ NULL);
+ printfd("Failed at %s with error %lu: %s\n", msg ? msg : "unknown task", status, lpBuffer); fflush(stdout);
+ LocalFree(lpBuffer);
+ }
+ else
+ {
+ printfd("Succeeded in %s.\n", msg); fflush(stdout);
+ }
+}
+
+static LONG AddToReg
+(
+ char *pszRegKey,
+ pRegEntry pReg,
+ int size
+)
+{
+ HKEY hTestKey;
+ pRegEntry pEntry;
+ LONG retCode = 0;
+ int i, numEntries = size / sizeof(RegEntry);
+
+ // create or open reg key
+ retCode = RegCreateKeyEx(
+ HKEY_LOCAL_MACHINE,
+ pszRegKey,
+ 0,
+ 0,
+ REG_OPTION_NON_VOLATILE,
+ KEY_WRITE,
+ NULL,
+ &hTestKey,
+ NULL);
+
+ if (retCode != ERROR_SUCCESS)
+ {
+ return retCode;
+ }
+
+ // add registry items to above reg key
+ for (i = 0, pEntry = pReg; i < numEntries; i++, pEntry++)
+ {
+ LPBYTE pData;
+ DWORD len;
+
+ if (pEntry->DataType == REG_SZ)
+ {
+ pData = (LPBYTE)pEntry->Data.StringData;
+ len = strlen(pEntry->Data.StringData);
+ }
+ else
+ {
+ pData = (LPBYTE)&pEntry->Data.LongData;
+ len = sizeof(pEntry->Data.LongData);
+ }
+ retCode = RegSetValueEx(
+ hTestKey,
+ pEntry->Name,
+ 0,
+ pEntry->DataType,
+ (LPBYTE)pData,
+ len);
+
+ if (retCode != ERROR_SUCCESS)
+ {
+ break;
+ }
+ }
+ RegCloseKey(hTestKey);
+
+ return retCode;
+}
+
+
+static void SetAutoLogger
+(
+ BOOL bOn,
+ ULONG Flags,
+ ULONG Level,
+ ULONG MaxFile
+)
+{
+ LONG retCode;
+ BOOL bIsVistaOrLater = TRUE;
+
+ if (bOn)
+ {
+ if (bIsVistaOrLater)
+ {
+ if (Flags != 0 || Level != 0)
+ {
+ RegAutoLoggerSession[INDEX_MAX_FILE].Data.LongData = MaxFile;
+ RegAutoLoggerSessionGuid[INDEX_FLAGS].Data.LongData = Flags;
+ RegAutoLoggerSessionGuid[INDEX_LEVEL].Data.LongData = Level;
+ }
+ retCode = AddToReg(AUTOLOGGER_REG_KEY, RegAutoLoggerSession, sizeof(RegAutoLoggerSession));
+ if (retCode == ERROR_SUCCESS)
+ {
+ retCode = AddToReg(AUTOLOGGER_REG_GUID_KEY, RegAutoLoggerSessionGuid, sizeof(RegAutoLoggerSessionGuid));
+ DisplayStatus(retCode, "configuring autologger for reboot"); fflush(stdout);
+ if (retCode == ERROR_SUCCESS)
+ {
+ printfd("Autologger file path: %s\n", AUTOLOGGER_LOGFILE_PATH); fflush(stdout);
+ }
+ }
+ }
+ else
+ {
+ if (Flags != 0 || Level != 0)
+ {
+ RegGlobalLoggerSession[INDEX_MAX_FILE].Data.LongData = MaxFile;
+ RegGlobalLoggerSessionGuid[INDEX_FLAGS].Data.LongData = Flags;
+ RegGlobalLoggerSessionGuid[INDEX_LEVEL].Data.LongData = Level;
+ }
+ retCode = AddToReg(GLOBALLOGGER_REG_KEY, RegGlobalLoggerSession, sizeof(RegGlobalLoggerSession));
+ if (retCode == ERROR_SUCCESS)
+ {
+ retCode = AddToReg(GLOBALLOGGER_REG_GUID_KEY, RegGlobalLoggerSessionGuid, sizeof(RegGlobalLoggerSessionGuid));
+ }
+ DisplayStatus(retCode, "configuring globallogger for reboot");
+ if (retCode == ERROR_SUCCESS)
+ {
+ printfd("Globallogger file path: %s\n", GLOBALLOGGER_LOGFILE_PATH);
+ }
+ }
+ if (MaxFile == 0)
+ {
+ printfd("You have set an unlimited log file size. ");
+ }
+ printfd("Please remember to disable logging when done.\n");
+ }
+ else
+ {
+ if (bIsVistaOrLater)
+ {
+ retCode = AddToReg(AUTOLOGGER_REG_KEY, RegAutoLoggerSessionOff, sizeof(RegAutoLoggerSessionOff));
+ DisplayStatus(retCode, "disabling autologger for reboot");
+ }
+ else
+ {
+ retCode = AddToReg(GLOBALLOGGER_REG_KEY, RegGlobalLoggerSessionOff, sizeof(RegGlobalLoggerSessionOff));
+ DisplayStatus(retCode, "disabling globallogger for reboot");
+ }
+ }
+} // SetAutoLogger
+
+
+BOOL IsRegistered(void)
+{
+ ULONG status = ERROR_SUCCESS;
+ PTRACE_GUID_PROPERTIES pProviderProperties = NULL; // Buffer that contains the block of property structures
+ PTRACE_GUID_PROPERTIES *pProviders = NULL; // Array of pointers to property structures in ProviderProperties
+ PTRACE_GUID_PROPERTIES *pTemp = NULL;
+ ULONG RegisteredProviderCount = 0; // Actual number of providers registered on the computer
+ ULONG ProviderCount = 0;
+ ULONG BufferSize = 0;
+ BOOL Registered = FALSE;
+
+ // EnumerateTraceGuids requires a valid pointer. Create a dummy
+ // allocation, so that you can get the actual allocation size.
+
+ pProviders = (PTRACE_GUID_PROPERTIES *)malloc(sizeof(PTRACE_GUID_PROPERTIES));
+
+ if (NULL == pProviders)
+ {
+ goto cleanup;
+ }
+
+ // Pass zero for the array size to get the number of registered providers
+ // for this snapshot. Then allocate memory for the block of structures
+ // and the array of pointers.
+
+ status = EnumerateTraceGuids(pProviders, 1, &RegisteredProviderCount);
+
+ if (ERROR_MORE_DATA == status)
+ {
+ USHORT i;
+
+ ProviderCount = RegisteredProviderCount;
+
+ BufferSize = sizeof(TRACE_GUID_PROPERTIES) * RegisteredProviderCount;
+
+ pProviderProperties = (PTRACE_GUID_PROPERTIES)malloc(BufferSize);
+
+ if (NULL == pProviderProperties)
+ {
+ goto cleanup;
+ }
+
+ ZeroMemory(pProviderProperties, BufferSize);
+
+ pTemp = (PTRACE_GUID_PROPERTIES *)realloc(pProviders, RegisteredProviderCount * sizeof(PTRACE_GUID_PROPERTIES));
+
+ if (NULL == pTemp)
+ {
+ goto cleanup;
+ }
+
+ pProviders = pTemp;
+ pTemp = NULL;
+
+ for (i = 0; i < RegisteredProviderCount; i++)
+ {
+ pProviders[i] = &pProviderProperties[i];
+ }
+ status = EnumerateTraceGuids(pProviders, ProviderCount, &RegisteredProviderCount);
+
+ if (ERROR_SUCCESS == status || ERROR_MORE_DATA == status)
+ {
+ for (i = 0; i < RegisteredProviderCount; i++)
+ {
+ if (IsEqualGUID(&pProviders[i]->Guid, &ProviderGuid) == 1)
+ {
+ Registered = TRUE;
+ break;
+ }
+ }
+ }
+ else
+ {
+ goto cleanup;
+ }
+ }
+
+cleanup:
+
+ if (pProviders)
+ {
+ free(pProviders);
+ }
+
+ if (pProviderProperties)
+ {
+ free(pProviderProperties);
+ }
+ return Registered;
+}
+
+void StartTracing
+(
+ ULONG Flags,
+ ULONGLONG Level,
+ ULONG MaxFile
+)
+{
+ LONG retCode;
+ TRACEHANDLE SessionHandle = 0;
+ EVENT_TRACE_PROPERTIES *pSessionProperties = NULL;
+ ULONG BufferSize = 0;
+
+ if (Flags == 0)
+ {
+ Flags = DEFAULT_FLAGS;
+ }
+ if (Level == 0)
+ {
+ Level = DEFAULT_LEVEL;
+ }
+
+ // Allocate memory for the session properties. The memory must
+ // be large enough to include the log file name and session name,
+ // which get appended to the end of the session properties structure.
+
+ BufferSize = sizeof(EVENT_TRACE_PROPERTIES) + sizeof(LOGFILE_PATH) + sizeof(LOGSESSION_NAME);
+ pSessionProperties = (EVENT_TRACE_PROPERTIES *)malloc(BufferSize);
+ if (NULL == pSessionProperties)
+ {
+ printfd("Failed to start tracing due to insufficient memory.\n", BufferSize);
+ return;
+ }
+
+ // Set the session properties. You only append the log file name
+ // to the properties structure; the StartTrace function appends
+ // the session name for you.
+
+ ZeroMemory(pSessionProperties, BufferSize);
+ pSessionProperties->Wnode.BufferSize = BufferSize;
+ pSessionProperties->Wnode.Flags = WNODE_FLAG_TRACED_GUID;
+ pSessionProperties->Wnode.ClientContext = CLOCKTYPE;
+ pSessionProperties->LogFileMode = LOG_FILE_MODE;
+ pSessionProperties->MaximumFileSize = MaxFile;
+ pSessionProperties->LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES);
+ pSessionProperties->LogFileNameOffset = sizeof(EVENT_TRACE_PROPERTIES) + sizeof(LOGSESSION_NAME);
+ strncpy_s(((char *)pSessionProperties + pSessionProperties->LogFileNameOffset), sizeof(LOGFILE_PATH), LOGFILE_PATH, sizeof(LOGFILE_PATH) - 1);
+
+ retCode = StartTrace((PTRACEHANDLE)&SessionHandle, LOGSESSION_NAME, pSessionProperties);
+ // if session was already started, report so and allow user to change level/flags
+ if (retCode == ERROR_ALREADY_EXISTS)
+ {
+ printfd("Session already in progress.\n"); fflush(stdout);
+ }
+ else if (retCode != ERROR_SUCCESS)
+ {
+ DisplayStatus(retCode, "starting trace session"); fflush(stdout);
+ }
+
+ // Enable the providers that you want to log events to your session.
+ if (retCode == ERROR_SUCCESS)
+ {
+ retCode = EnableTrace(
+ TRUE,
+ Flags,
+ (UCHAR)Level,
+ (LPCGUID)&ProviderGuid,
+ SessionHandle);
+
+ DisplayStatus(retCode, "enabling trace session"); fflush(stdout);
+ SetLastError(ERROR_SUCCESS);
+ }
+ if (retCode == ERROR_SUCCESS)
+ {
+ printfd("Log file path: %s\n", LOGFILE_PATH); fflush(stdout);
+ }
+ if (pSessionProperties)
+ {
+ free(pSessionProperties);
+ }
+} // StartTracing
+
+ULONG InspectTracing(void)
+{
+ // return ERROR_WMI_INSTANCE_NOT_FOUND if tracking is not running, ERROR_SUCCESS otherwise
+ ULONG BufferSize = sizeof(EVENT_TRACE_PROPERTIES) + sizeof(LOGFILE_PATH) + sizeof(LOGSESSION_NAME);
+ PEVENT_TRACE_PROPERTIES pSessionProperties = (PEVENT_TRACE_PROPERTIES)malloc(BufferSize);
+
+ if (NULL == pSessionProperties)
+ {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ ZeroMemory(pSessionProperties, BufferSize);
+ pSessionProperties->Wnode.BufferSize = BufferSize;
+ pSessionProperties->LogFileMode = LOG_FILE_MODE;
+ pSessionProperties->LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES);
+ pSessionProperties->LogFileNameOffset = sizeof(EVENT_TRACE_PROPERTIES) + sizeof(LOGSESSION_NAME);
+
+ ULONG ret = ControlTrace(
+ 0,
+ LOGSESSION_NAME,
+ pSessionProperties,
+ EVENT_TRACE_CONTROL_QUERY
+ );
+
+ free(pSessionProperties);
+ return ret;
+}
+
+ULONG GetTracingConfig(TracingConfig *config)
+{
+ HKEY hKey;
+
+ ULONG ret = RegOpenKeyExA(
+ HKEY_LOCAL_MACHINE,
+ AUTOLOGGER_REG_KEY,
+ 0,
+ KEY_READ,
+ &hKey
+ );
+
+ if (ret == ERROR_SUCCESS)
+ {
+ ULONG maxFileSize;
+ ULONG enableFlags;
+ ULONG enableLevel;
+ DWORD size = sizeof(ULONG);
+
+ ret = RegQueryValueEx(hKey, "MaxFileSize", NULL, NULL, (LPBYTE)&maxFileSize, &size);
+ if (ret == ERROR_SUCCESS)
+ {
+ HKEY providerKey;
+
+ ret = RegOpenKeyExA(
+ hKey,
+ "{"DRIVERS_GUID"}",
+ 0,
+ KEY_READ,
+ &providerKey
+ );
+ if (ret == ERROR_SUCCESS)
+ {
+ ret = RegQueryValueEx(providerKey, "EnableFlags", NULL, NULL, (LPBYTE)&enableFlags, &size);
+ if (ret == ERROR_SUCCESS)
+ {
+ ret = RegQueryValueEx(providerKey, "EnableLevel", NULL, NULL, (LPBYTE)&enableLevel, &size);
+ if (ret == ERROR_SUCCESS)
+ {
+ config->MaxFile = maxFileSize;
+ config->Flags = enableFlags;
+ config->Level = enableLevel;
+ }
+ }
+ RegCloseKey(providerKey);
+ }
+ }
+ RegCloseKey(hKey);
+ }
+
+ return ret;
+}
+
+#define MAX_SESSION_NAME 1024
+#define MAX_LOG_NAME 1024
+
+void StopTracing(void)
+{
+ LONG retCode = ERROR_SUCCESS;
+ EVENT_TRACE_PROPERTIES *pSessionProperties = NULL;
+ ULONG BufferSize = 0;
+ BOOL bIsVistaOrLater = TRUE;
+
+ BufferSize = sizeof(EVENT_TRACE_PROPERTIES) + MAX_SESSION_NAME + MAX_LOG_NAME;
+ pSessionProperties = (EVENT_TRACE_PROPERTIES *)malloc(BufferSize);
+ if (NULL == pSessionProperties)
+ {
+ printfd("Failed at stopping trace and autologger sessions due to insufficient memory.\n");
+ return;
+ }
+
+ ZeroMemory(pSessionProperties, BufferSize);
+ pSessionProperties->Wnode.BufferSize = BufferSize;
+ pSessionProperties->Wnode.Guid = ProviderGuid;
+
+ pSessionProperties->LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES);
+ pSessionProperties->LogFileNameOffset = sizeof(EVENT_TRACE_PROPERTIES) + MAX_LOG_NAME;
+
+ retCode = ControlTrace((TRACEHANDLE)NULL, LOGSESSION_NAME, pSessionProperties, EVENT_TRACE_CONTROL_STOP);
+ // don't bother reporting since the tracing session wasn't running
+ if (retCode != ERROR_WMI_INSTANCE_NOT_FOUND)
+ {
+ DisplayStatus(retCode, "stopping trace session");
+ }
+
+ if (bIsVistaOrLater)
+ {
+ retCode = ControlTrace((TRACEHANDLE)NULL, AUTOLOGGER_NAME, pSessionProperties, EVENT_TRACE_CONTROL_STOP);
+ // don't bother reporting since the autologger session wasn't running
+ if (retCode != ERROR_WMI_INSTANCE_NOT_FOUND)
+ {
+ DisplayStatus(retCode, "stopping autologger session");
+ }
+ }
+
+ if (pSessionProperties)
+ {
+ free(pSessionProperties);
+ }
+}
+
+void StartAutoLogger
+(
+ ULONG Flags,
+ ULONG Level,
+ ULONG MaxFile
+)
+{
+ SetAutoLogger(TRUE, Flags, Level, MaxFile);
+}
+
+
+void StopAutoLogger(void)
+{
+ SetAutoLogger(FALSE, 0, 0, 0);
+}
+
+void ParseLogsMsg(void)
+{
+ DisplayStatus(ERROR_SUCCESS, "Log Parsing");
+}
diff --git a/src/windows/tools/qdcfg/trace.h b/src/windows/tools/qdcfg/trace.h
new file mode 100644
index 0000000..b9f1322
--- /dev/null
+++ b/src/windows/tools/qdcfg/trace.h
@@ -0,0 +1,80 @@
+/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*
+
+ T R A C E . H
+
+GENERAL DESCRIPTION
+ This file defines constants, macros, and function prototypes for
+ ETW trace logging management for Qualcomm USB drivers.
+
+ Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ SPDX-License-Identifier: BSD-3-Clause
+
+*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/
+
+#ifndef TRACE_H
+#define TRACE_H
+
+#include
+
+#define DRIVERS_GUID "5F02CA82-B28B-4a95-BA2C-FBED52DDD3DC"
+
+// globallogger for xp only
+#define GLOBALLOGGER_REG_KEY "SYSTEM\\CurrentControlSet\\Control\\WMI\\GlobalLogger\\"
+#define GLOBALLOGGER_REG_GUID_KEY GLOBALLOGGER_REG_KEY \
+ DRIVERS_GUID
+
+// autologger for vista and later
+#define AUTOLOGGER_REG_KEY "SYSTEM\\CurrentControlSet\\Control\\WMI\\Autologger\\QcDriverSession\\"
+#define AUTOLOGGER_REG_GUID_KEY AUTOLOGGER_REG_KEY \
+ "{" \
+ DRIVERS_GUID \
+ "}"
+
+#define AUTOLOGGER_GUID "{C1514B62-EB15-4FF2-9475-5D0E63383ADC}"
+
+#define LOGFILE_PATH "C:\\QCDriverLog.etl"
+#define AUTOLOGGER_LOGFILE_PATH "C:\\QCDriverAutoLog.etl"
+#define GLOBALLOGGER_LOGFILE_PATH "C:\\QCDriverGlobalLog.etl"
+
+#define LOGSESSION_NAME "QCDriver"
+#define AUTOLOGGER_NAME "QCDriverSession"
+
+#define DEFAULT_FLAGS 0x7fffffff
+#define DEFAULT_LEVEL 0xff
+#define CLOCKTYPE 1
+#define LOG_FILE_MODE EVENT_TRACE_FILE_MODE_CIRCULAR
+#define MAX_FILE_SIZE 100
+#define FLUSH_TIMER 0
+
+typedef struct tracingConfig
+{
+ ULONG Flags;
+ ULONG Level;
+ ULONG MaxFile;
+} TracingConfig;
+
+void StartTracing
+(
+ ULONG Flags,
+ ULONGLONG Level,
+ ULONG MaxFile
+);
+
+__declspec(dllexport) ULONG InspectTracing(void);
+
+__declspec(dllexport) ULONG GetTracingConfig(TracingConfig *);
+
+void StopTracing(void);
+
+void StartAutoLogger
+(
+ ULONG Flags,
+ ULONG Level,
+ ULONG MaxFile
+);
+
+void StopAutoLogger(void);
+
+BOOL IsRegistered(void);
+
+#endif
diff --git a/src/windows/tools/qdclr/infdev.cpp b/src/windows/tools/qdclr/infdev.cpp
new file mode 100644
index 0000000..e16d861
--- /dev/null
+++ b/src/windows/tools/qdclr/infdev.cpp
@@ -0,0 +1,613 @@
+/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*
+
+ I N F D E V. C P P
+
+GENERAL DESCRIPTION
+ Scan and remove VID_05C6 related devnodes and inf files from system
+
+ Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ SPDX-License-Identifier: BSD-3-Clause
+
+*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/
+
+#include "infdev.h"
+
+// Global Variables
+PUCHAR gFileDataRaw;
+EXEC_MODE gExecutionMode;
+
+void print_timestamp(PCHAR text, bool NewLine)
+{
+ SYSTEMTIME lt;
+ GetLocalTime(<);
+ printf("[%02d:%02d:%02d.%03d] %s\n",
+ lt.wHour, lt.wMinute, lt.wSecond, lt.wMilliseconds, text);
+ if (NewLine == TRUE)
+ {
+ printf("\n");
+ }
+}
+
+int remove_drivers(EXEC_MODE mode)
+{
+ print_timestamp((PCHAR)"=== Start Scanning Driver Store ===", false);
+ if (mode == EXEC_MODE::PREVIEW)
+ {
+ printf(" Mode: Preview\n\n");
+ }
+ else
+ {
+ printf(" Mode: Removal\n\n");
+ }
+ gExecutionMode = mode;
+ gFileDataRaw = (PUCHAR)malloc(INF_SIZE_MAX);
+ if (gFileDataRaw == NULL)
+ {
+ printf(" ERROR: no memory for gFileDataRaw\n");
+ return EXIT_FAILURE;
+ }
+
+ // 1. remove devnode
+ ScanAndRemoveDevice(TEXT(MATCH_VID));
+
+ // 2. remove INF
+ ScanAndRemoveInf(INF_INSTALL_PATH);
+
+ free(gFileDataRaw);
+
+ DeleteFile(TEXT("C:\\Windows\\system32\\drivers\\qcusbfilter.sys"));
+ DeleteFile(TEXT("C:\\Windows\\system32\\drivers\\qcusbwwan.sys"));
+ DeleteFile(TEXT("C:\\Windows\\system32\\drivers\\qcusbnet.sys"));
+ DeleteFile(TEXT("C:\\Windows\\system32\\drivers\\qcusbser.sys"));
+ DeleteFile(TEXT("C:\\Windows\\system32\\drivers\\qdbusb.sys"));
+ DeleteFile(TEXT("C:\\Windows\\system32\\drivers\\qcwdfserial.sys"));
+
+ // Trigger re-enumeration to refresh device tree
+ DEVINST devRoot;
+ if (CM_Locate_DevNode(&devRoot, NULL, CM_LOCATE_DEVNODE_NORMAL) == CR_SUCCESS)
+ {
+ CM_Reenumerate_DevNode(devRoot, CM_REENUMERATE_NORMAL);
+ printf("\n Scanning for hardware changes...\n");
+ }
+
+ print_timestamp((PCHAR)"=== End of Scanning ===", true);
+ return EXIT_SUCCESS;
+}
+
+VOID ScanAndRemoveInf(LPCTSTR InfPath)
+{
+ WIN32_FIND_DATA fileData;
+ HANDLE fileHandle;
+ BOOL notDone = TRUE;
+ WCHAR fullPath[MAX_PATH];
+ WCHAR searchPath[MAX_PATH];
+ LARGE_INTEGER infSize;
+ ULONG dataSize;
+
+ StringCchCopy(searchPath, MAX_PATH, InfPath);
+ StringCchCat(searchPath, MAX_PATH, INT_OEM_NAMING);
+
+ printf(" INF Scan Path: <%ws>\n", InfPath);
+
+ fileHandle = FindFirstFile(searchPath, &fileData);
+ if (fileHandle == INVALID_HANDLE_VALUE)
+ {
+ printf("FindFirstFile failure\n");
+ return;
+ }
+
+ while (notDone == TRUE)
+ {
+ if ((fileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
+ {
+ // got a file
+ StringCchCopy(fullPath, MAX_PATH, InfPath);
+ StringCchCat(fullPath, MAX_PATH, TEXT("\\"));
+ infSize.LowPart = fileData.nFileSizeLow;
+ infSize.HighPart = fileData.nFileSizeHigh;
+ StringCchCat(fullPath, MAX_PATH, fileData.cFileName);
+ if (infSize.QuadPart > (INF_SIZE_MAX - 2))
+ {
+ MatchingInf(fullPath, fileData.cFileName, (INF_SIZE_MAX - 2));
+ }
+ else
+ {
+ dataSize = (ULONG)infSize.QuadPart;
+ MatchingInf(fullPath, fileData.cFileName, dataSize);
+ }
+ }
+ notDone = FindNextFile(fileHandle, &fileData);
+ }
+
+ FindClose(fileHandle);
+}
+
+BOOL MatchingInf(PCTSTR InfFullPath, PCTSTR InfFileName, ULONG DataSize)
+{
+ HINF myHandle;
+ BOOL bResult = TRUE;
+ BOOL matchFound = FALSE;
+ DWORD bytesRead;
+ BOOL notAscii = TRUE;
+ PCHAR matchType = (PCHAR)"A";
+
+ myHandle = CreateFile
+ (
+ InfFullPath,
+ GENERIC_READ,
+ FILE_SHARE_READ,
+ NULL,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL
+ );
+ if (myHandle == INVALID_HANDLE_VALUE)
+ {
+ printf(" Error: SetupOpenInfFile <%ws> 0x%X\n", InfFullPath, GetLastError());
+ return FALSE;
+ }
+
+ ZeroMemory(gFileDataRaw, INF_SIZE_MAX);
+
+ bResult = ReadFile
+ (
+ myHandle,
+ (PVOID)gFileDataRaw,
+ DataSize,
+ &bytesRead,
+ NULL
+ );
+ if (bResult == FALSE)
+ {
+ printf(" Error: Failed to read <%ws>\n", InfFullPath);
+ CloseHandle(myHandle);
+ return FALSE;
+ }
+
+ gFileDataRaw[bytesRead] = 0;
+ gFileDataRaw[bytesRead + 1] = 0;
+
+ // simple detection of encoding format
+ if (gFileDataRaw[0] == 0xEF || gFileDataRaw[0] == 0xFE || gFileDataRaw[0] == 0xFF ||
+ gFileDataRaw[0] == 0x00)
+ {
+ notAscii = TRUE;
+ }
+ if (gFileDataRaw[0] < 0x80 && gFileDataRaw[1] < 0x80)
+ {
+ notAscii = FALSE;
+ }
+
+ // Filter based on VID/PID to avoid removal of commercial GOBI devices
+ // For more aggressive removal based on VID only, comment out the PID lines
+ if (notAscii == FALSE)
+ {
+ // traditional ASCII file
+ if (strstr((char *)gFileDataRaw, (char *)MATCH_VID))
+ {
+ if ((strstr((char *)gFileDataRaw, (char *)MATCH_PID) != NULL) ||
+ (strstr((char *)gFileDataRaw, (char *)MATCH_PID2) != NULL) ||
+ (strstr((char *)gFileDataRaw, (char *)MATCH_PID3) != NULL))
+ {
+ matchType = (PCHAR)"A";
+ matchFound = TRUE;
+ }
+ else
+ {
+ printf("(A) Candidate but not to be removed: <%ws>\n", InfFullPath);
+ }
+ }
+
+ }
+ else
+ {
+ if (StrStrW((PTSTR)gFileDataRaw, TEXT(MATCH_VID)) != NULL)
+ {
+ if ((StrStrW((PTSTR)gFileDataRaw, TEXT(MATCH_PID)) != NULL) ||
+ (StrStrW((PTSTR)gFileDataRaw, TEXT(MATCH_PID2)) != NULL) ||
+ (StrStrW((PTSTR)gFileDataRaw, TEXT(MATCH_PID3)) != NULL))
+ {
+ matchType = (PCHAR)"W";
+ matchFound = TRUE;
+ }
+ else
+ {
+ printf("(W) Candidate but not to be removed: <%ws>\n", InfFullPath);
+ }
+ }
+ }
+
+ CloseHandle(myHandle);
+
+ // Confirm INF candidate
+ if (matchFound == TRUE)
+ {
+ matchFound = ConfirmInfFile(InfFullPath);
+ }
+
+ if (matchFound == TRUE)
+ {
+ RemoveInfFile(InfFileName, matchType);
+ }
+
+ return matchFound;
+}
+
+BOOL ConfirmInfFile(PCTSTR InfFullPath)
+{
+ UINT errLine = 0;
+ HINF myHandle;
+ INFCONTEXT infCtxt;
+ BOOL bResult = TRUE;
+ WCHAR lineText[LINE_LEN_MAX];
+ DWORD requiredSize = 0;
+ BOOL matchConfirmed = FALSE;
+
+ myHandle = SetupOpenInfFile(InfFullPath, NULL, INF_STYLE_WIN4, &errLine);
+ if (myHandle == INVALID_HANDLE_VALUE)
+ {
+ printf(" Error: SetupOpenInfFile <%ws> 0x%X\n", InfFullPath, GetLastError());
+ return FALSE;
+ }
+
+ bResult = SetupFindFirstLine(myHandle, TEXT("Version"), TEXT("Class"), &infCtxt);
+ if (bResult == FALSE)
+ {
+ printf(" Error: SetFindFirstLine <%ws> 0x%X\n", InfFullPath, GetLastError());
+ SetupCloseInfFile(myHandle);
+ return FALSE;
+ }
+
+ bResult = SetupGetLineText(&infCtxt, NULL, NULL, NULL, lineText, LINE_LEN_MAX, &requiredSize);
+ if (bResult == TRUE)
+ {
+ if (DeviceMatch(lineText, requiredSize) == TRUE)
+ {
+ matchConfirmed = TRUE;
+ }
+ }
+ else
+ {
+ printf(" Error: SetupGetLineText <%ws> 0x%X\n", InfFullPath, GetLastError());
+ }
+
+ SetupCloseInfFile(myHandle);
+
+ return matchConfirmed;
+}
+
+BOOL DeviceMatch(PTSTR InfText, DWORD TextSize)
+{
+ DWORD actualSize = sizeof(WCHAR) * TextSize;
+ BOOL matchFound = FALSE;
+
+ if (actualSize > LINE_LEN_MAX)
+ {
+ printf("DeviceMatch: line too long (%uB/%uB)\n", TextSize, LINE_LEN_MAX);
+ return FALSE;
+ }
+
+ if (StrStrW(InfText, TEXT("Modem")) != NULL) // MODEM
+ {
+ matchFound = TRUE;
+ }
+ else if (StrStrW(InfText, TEXT("Net")) != NULL) // NET ADAPTR
+ {
+ matchFound = TRUE;
+ }
+ else if (StrStrW(InfText, TEXT("Ports")) != NULL) // SER PORT
+ {
+ matchFound = TRUE;
+ }
+ else if (StrStrW(InfText, TEXT("USB")) != NULL) // QDSS, DPL, FILTER
+ {
+ matchFound = TRUE;
+ }
+ else if (StrStrW(InfText, TEXT("AndroidUsbDeviceClass")) != NULL) // QCADB
+ {
+ matchFound = TRUE;
+ }
+
+ return matchFound;
+}
+
+VOID RemoveInfFile(PCTSTR InfFileName, PCHAR Type)
+{
+ WCHAR cmdLineW[MAX_PATH];
+ PROCESS_INFORMATION processInfo;
+ STARTUPINFO startupInfo;
+ BOOL result;
+ DWORD exitCode;
+
+ StringCchCopy(cmdLineW, MAX_PATH, INF_REMOVE_COMMAND);
+ StringCchCat(cmdLineW, MAX_PATH, InfFileName);
+
+ if (gExecutionMode == EXEC_MODE::PREVIEW)
+ {
+ printf("(%s) %ws\n", Type, cmdLineW);
+ }
+ else
+ {
+ printf("\nDeleting %ws ...\n", InfFileName);
+
+ memset(&processInfo, 0, sizeof(processInfo));
+ memset(&startupInfo, 0, sizeof(startupInfo));
+ startupInfo.cb = sizeof(startupInfo);
+
+ result = CreateProcess
+ (
+ NULL,
+ cmdLineW,
+ NULL,
+ NULL,
+ FALSE,
+ NORMAL_PRIORITY_CLASS,
+ NULL,
+ NULL,
+ &startupInfo,
+ &processInfo
+ );
+
+ if (result == FALSE)
+ {
+ printf(" Error: CreateProcess failure 0x%X\n", GetLastError());
+ }
+
+ WaitForSingleObject(processInfo.hProcess, INFINITE);
+ GetExitCodeProcess(processInfo.hProcess, &exitCode);
+ CloseHandle(processInfo.hProcess);
+ CloseHandle(processInfo.hThread);
+ }
+}
+
+// ================ Removal of DevNode ================
+VOID ScanAndRemoveDevice(LPCTSTR HwId)
+{
+ HDEVINFO devInfoHandle = INVALID_HANDLE_VALUE;
+ SP_DEVINFO_DATA devInfoData;
+ DWORD memberIdx = 0;
+ CHAR hardwareIds[REG_HW_ID_SIZE];
+ CHAR compatibleIds[REG_HW_ID_SIZE];
+ CHAR friendlyName[REG_HW_ID_SIZE];
+ DWORD requiredSize;
+ BOOL bResult;
+ BOOL bMatch, bExclude;
+ DWORD errorCode;
+
+ devInfoHandle = SetupDiGetClassDevsEx
+ (
+ NULL,
+ NULL, // TEXT("USB")
+ NULL,
+ DIGCF_ALLCLASSES,
+ NULL,
+ NULL, // Machine,
+ NULL
+ );
+ if (devInfoHandle == INVALID_HANDLE_VALUE)
+ {
+ return;
+ }
+
+ devInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
+ while (SetupDiEnumDeviceInfo(devInfoHandle, memberIdx, &devInfoData) == TRUE)
+ {
+ bMatch = bExclude = FALSE;
+ ZeroMemory(hardwareIds, REG_HW_ID_SIZE);
+ ZeroMemory(compatibleIds, REG_HW_ID_SIZE);
+ ZeroMemory(friendlyName, REG_HW_ID_SIZE);
+
+ bResult = SetupDiGetDeviceRegistryProperty
+ (
+ devInfoHandle,
+ &devInfoData,
+ SPDRP_FRIENDLYNAME,
+ NULL,
+ (LPBYTE)friendlyName,
+ REG_HW_ID_SIZE,
+ &requiredSize
+ );
+ if (bResult == FALSE)
+ {
+ SetupDiGetDeviceRegistryProperty
+ (
+ devInfoHandle,
+ &devInfoData,
+ SPDRP_DEVICEDESC,
+ NULL,
+ (LPBYTE)friendlyName,
+ REG_HW_ID_SIZE,
+ &requiredSize
+ );
+ }
+
+ bResult = SetupDiGetDeviceRegistryProperty
+ (
+ devInfoHandle,
+ &devInfoData,
+ SPDRP_HARDWAREID,
+ NULL,
+ (LPBYTE)hardwareIds,
+ REG_HW_ID_SIZE,
+ &requiredSize
+ );
+ if (bResult == FALSE)
+ {
+ errorCode = GetLastError();
+ if (errorCode != ERROR_INVALID_DATA)
+ {
+ printf(" Error: SetupDiGetDeviceRegistryProperty: hwid (0x%X) reqSZ %d\n",
+ GetLastError(), requiredSize);
+ }
+ }
+ else
+ {
+ CharUpper((PTSTR)hardwareIds);
+
+ // matching
+ if (StrStrW((PTSTR)hardwareIds, HwId) != NULL)
+ {
+ printf("HWID <%ws>\n", (PTSTR)hardwareIds);
+ bMatch = TRUE;
+ }
+
+ // exclusion
+ if (StrStrW((PTSTR)hardwareIds, TEXT(EXCLUDE_PID)) != NULL)
+ {
+ bExclude = TRUE;
+ }
+ else if (StrStrW((PTSTR)hardwareIds, TEXT(EXCLUDE_PID2)) != NULL)
+ {
+ bExclude = TRUE;
+ }
+ else if (StrStrW((PTSTR)hardwareIds, TEXT(EXCLUDE_PID3)) != NULL)
+ {
+ bExclude = TRUE;
+ }
+ else if (StrStrW((PTSTR)hardwareIds, TEXT(EXCLUDE_PID4)) != NULL)
+ {
+ bExclude = TRUE;
+ }
+ else if (StrStrW((PTSTR)hardwareIds, TEXT(EXCLUDE_PID5)) != NULL)
+ {
+ bExclude = TRUE;
+ }
+ }
+
+ if (bMatch == FALSE)
+ {
+ bResult = SetupDiGetDeviceRegistryProperty
+ (
+ devInfoHandle,
+ &devInfoData,
+ SPDRP_COMPATIBLEIDS,
+ NULL,
+ (LPBYTE)compatibleIds,
+ REG_HW_ID_SIZE,
+ &requiredSize
+ );
+ if (bResult == FALSE)
+ {
+ errorCode = GetLastError();
+ if (errorCode != ERROR_INVALID_DATA)
+ {
+ printf(" Error: SetupDiGetDeviceRegistryProperty: cpid (0x%X) reqSZ %d\n",
+ GetLastError(), requiredSize);
+ }
+ }
+ else
+ {
+ CharUpper((PTSTR)compatibleIds);
+
+ // matching
+ if (StrStrW((PTSTR)compatibleIds, HwId) != NULL)
+ {
+ printf("COMPAT ID <%ws>\n", (PTSTR)hardwareIds);
+ bMatch = TRUE;
+ }
+ }
+ }
+
+ if (bMatch == TRUE)
+ {
+ printf(" <%ws>\n", (PTSTR)friendlyName);
+ if (gExecutionMode == EXEC_MODE::PREVIEW)
+ {
+ if (bExclude == FALSE)
+ {
+ printf(" ^ to be removed ^\n");
+ }
+ else
+ {
+ printf(" ^ candidate but not to be removed ^\n");
+ }
+ }
+ else
+ {
+ if (bExclude == FALSE)
+ {
+ RemoveDevice(devInfoHandle, &devInfoData);
+ }
+ else
+ {
+ printf(" ^ candidate but not to be removed ^\n");
+ }
+ }
+ }
+ memberIdx++;
+ }
+
+ if (devInfoHandle != INVALID_HANDLE_VALUE)
+ {
+ SetupDiDestroyDeviceInfoList(devInfoHandle);
+ }
+}
+
+BOOL RemoveDevice(HDEVINFO DevInfoHandle, PSP_DEVINFO_DATA DevInfoData)
+{
+ SP_REMOVEDEVICE_PARAMS removeDevParams;
+ SP_DEVINSTALL_PARAMS devInstallParams;
+ BOOL bResult = FALSE;
+
+ removeDevParams.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER);
+ removeDevParams.Scope = DI_REMOVEDEVICE_GLOBAL;
+ removeDevParams.ClassInstallHeader.InstallFunction = DIF_REMOVE;
+ removeDevParams.HwProfile = 0;
+
+ bResult = SetupDiSetClassInstallParams
+ (
+ DevInfoHandle,
+ DevInfoData,
+ &removeDevParams.ClassInstallHeader,
+ sizeof(SP_REMOVEDEVICE_PARAMS)
+ );
+ if (bResult == TRUE)
+ {
+ bResult = SetupDiCallClassInstaller(DIF_REMOVE, DevInfoHandle, DevInfoData);
+ if (bResult == FALSE)
+ {
+ DWORD err = GetLastError();
+ if (err == ERROR_IN_WOW64)
+ {
+ printf(" Error: executable might not work on 64-bit OS, please run 64-bit executable\n");
+ }
+ else
+ {
+ printf(" Error: SetupDiCallClassInstaller failure 0x%X\n", err);
+ }
+ }
+ }
+ else
+ {
+ printf(" Error: SetupDiSetClassInstallParams failure\n");
+ }
+
+ if (bResult == FALSE)
+ {
+ printf(" Error: failed to remove device\n");
+ }
+ else
+ {
+ devInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
+ bResult = SetupDiGetDeviceInstallParams
+ (
+ DevInfoHandle,
+ DevInfoData,
+ &devInstallParams
+ );
+ if (bResult == TRUE)
+ {
+ if (devInstallParams.Flags & (DI_NEEDRESTART | DI_NEEDREBOOT))
+ {
+ bResult = TRUE;
+ printf(" Device instance removed successfully, please reboot system\n");
+ }
+ else
+ {
+ bResult = TRUE;
+ printf(" Device instance removed successfully, no reboot needed\n");
+ }
+ }
+ }
+ return bResult;
+}
diff --git a/src/windows/tools/qdclr/infdev.h b/src/windows/tools/qdclr/infdev.h
new file mode 100644
index 0000000..db44472
--- /dev/null
+++ b/src/windows/tools/qdclr/infdev.h
@@ -0,0 +1,67 @@
+/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*
+
+ I N F D E V. H
+
+GENERAL DESCRIPTION
+ Scan and remove VID_05C6 related devnodes and inf files from system
+
+ Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ SPDX-License-Identifier: BSD-3-Clause
+
+*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/
+#ifndef INFDEV_H
+#define INFDEV_H
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define INT_OEM_NAMING L"\\oem*.inf"
+#define INF_INSTALL_PATH L"C:\\Windows\\Inf"
+
+#ifdef _WIN64
+#define INF_REMOVE_COMMAND L"C:\\Windows\\System32\\pnputil.exe -f -d "
+#else
+#define INF_REMOVE_COMMAND L"C:\\Windows\\Sysnative\\pnputil.exe -f -d "
+#endif
+
+enum class EXEC_MODE
+{
+ PREVIEW,
+ REMOVE_OEM,
+ REMOVE_ALL
+};
+
+#define INF_SIZE_MAX (1024*1024)
+#define LINE_LEN_MAX 2048
+#define REG_HW_ID_SIZE 2048
+
+#define MATCH_VID "VID_05C6"
+#define MATCH_PID "PID_9001"
+#define MATCH_PID2 "PID_9049"
+#define MATCH_PID3 "PID_9025"
+#define EXCLUDE_PID "PID_9204"
+#define EXCLUDE_PID2 "PID_9205"
+#define EXCLUDE_PID3 "PID_9301"
+#define EXCLUDE_PID4 "PID_9302"
+#define EXCLUDE_PID5 "PID_9303"
+
+// Help functions
+void print_timestamp(PCHAR Text, bool NewLine);
+int remove_drivers(EXEC_MODE);
+
+// INF removal
+VOID ScanAndRemoveInf(LPCTSTR InfPath);
+BOOL MatchingInf(PCTSTR InfFullPath, PCTSTR InfFileName, ULONG DataSize);
+BOOL ConfirmInfFile(PCTSTR InfFullPath);
+BOOL DeviceMatch(PTSTR InfText, DWORD TextSize);
+VOID RemoveInfFile(PCTSTR InfFileName, PCHAR Type);
+
+// Dev node removal
+VOID ScanAndRemoveDevice(LPCTSTR HwId);
+BOOL RemoveDevice(HDEVINFO DevInfoHandle, PSP_DEVINFO_DATA DevInfoData);
+
+#endif // INFDEV_H
diff --git a/src/windows/tools/qdclr/qdclr.cpp b/src/windows/tools/qdclr/qdclr.cpp
new file mode 100644
index 0000000..751b38e
--- /dev/null
+++ b/src/windows/tools/qdclr/qdclr.cpp
@@ -0,0 +1,21 @@
+/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*
+
+ M A I N . C P P
+
+GENERAL DESCRIPTION
+ This file implements device scanning, enumeration, and monitoring
+ functions for Qualcomm USB devices on Windows.
+
+ Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ SPDX-License-Identifier: BSD-3-Clause
+
+*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/
+
+#include "infdev.h"
+
+int main()
+{
+ remove_drivers(EXEC_MODE::REMOVE_OEM);
+ printf("Qualcomm USB Kernel Driver Cleanup Utility\n");
+ printf("Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.\n");
+}
diff --git a/src/windows/tools/qdclr/qdclr.sln b/src/windows/tools/qdclr/qdclr.sln
new file mode 100644
index 0000000..7fe2242
--- /dev/null
+++ b/src/windows/tools/qdclr/qdclr.sln
@@ -0,0 +1,37 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 15
+VisualStudioVersion = 15.0.28307.572
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qdclr", "qdclr.vcxproj", "{2C667F8B-9223-44AA-8D21-4BE25CC72898}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|ARM64 = Debug|ARM64
+ Debug|x64 = Debug|x64
+ Debug|x86 = Debug|x86
+ Release|ARM64 = Release|ARM64
+ Release|x64 = Release|x64
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {2C667F8B-9223-44AA-8D21-4BE25CC72898}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {2C667F8B-9223-44AA-8D21-4BE25CC72898}.Debug|ARM64.Build.0 = Debug|ARM64
+ {2C667F8B-9223-44AA-8D21-4BE25CC72898}.Debug|x64.ActiveCfg = Debug|x64
+ {2C667F8B-9223-44AA-8D21-4BE25CC72898}.Debug|x64.Build.0 = Debug|x64
+ {2C667F8B-9223-44AA-8D21-4BE25CC72898}.Debug|x86.ActiveCfg = Debug|Win32
+ {2C667F8B-9223-44AA-8D21-4BE25CC72898}.Debug|x86.Build.0 = Debug|Win32
+ {2C667F8B-9223-44AA-8D21-4BE25CC72898}.Release|ARM64.ActiveCfg = Release|ARM64
+ {2C667F8B-9223-44AA-8D21-4BE25CC72898}.Release|ARM64.Build.0 = Release|ARM64
+ {2C667F8B-9223-44AA-8D21-4BE25CC72898}.Release|x64.ActiveCfg = Release|x64
+ {2C667F8B-9223-44AA-8D21-4BE25CC72898}.Release|x64.Build.0 = Release|x64
+ {2C667F8B-9223-44AA-8D21-4BE25CC72898}.Release|x86.ActiveCfg = Release|Win32
+ {2C667F8B-9223-44AA-8D21-4BE25CC72898}.Release|x86.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {C234D27E-BE04-4254-B50D-A4673BB3A28C}
+ EndGlobalSection
+EndGlobal
diff --git a/src/windows/tools/qdclr/qdclr.vcxproj b/src/windows/tools/qdclr/qdclr.vcxproj
new file mode 100644
index 0000000..cbfa4f5
--- /dev/null
+++ b/src/windows/tools/qdclr/qdclr.vcxproj
@@ -0,0 +1,170 @@
+
+
+
+
+ Debug
+ ARM64
+
+
+ Debug
+ Win32
+
+
+ Release
+ ARM64
+
+
+ Release
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ 15.0
+ {2C667F8B-9223-44AA-8D21-4BE25CC72898}
+ Win32Proj
+ qdclr
+ 10.0
+
+
+
+ Application
+ v142
+ true
+ Unicode
+
+
+ Application
+ v142
+ Unicode
+
+
+ Application
+ v142
+ true
+ Unicode
+
+
+ Application
+ v142
+ true
+ Unicode
+
+
+ Application
+ v142
+ Unicode
+
+
+ Application
+ v142
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ $(SolutionDir)$(Platform)\$(Configuration)\
+ $(Platform)\$(Configuration)\
+
+
+ $(SolutionDir)$(Platform)\$(Configuration)\
+ $(Platform)\$(Configuration)\
+
+
+
+ Level4
+ WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)
+
+
+ user32.lib;cfgmgr32.lib;setupapi.lib;Shlwapi.lib;%(AdditionalDependencies)
+ Console
+
+
+
+
+ Level4
+ _WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)
+
+
+ user32.lib;cfgmgr32.lib;setupapi.lib;Shlwapi.lib;%(AdditionalDependencies)
+ Console
+
+
+
+
+ Level4
+ _WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)
+
+
+ user32.lib;cfgmgr32.lib;setupapi.lib;Shlwapi.lib;%(AdditionalDependencies)
+ Console
+
+
+
+
+ Level4
+ WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
+
+
+ Console
+ user32.lib;cfgmgr32.lib;setupapi.lib;Shlwapi.lib;%(AdditionalDependencies)
+ $(OutDir)$(TargetName)$(TargetExt)
+
+
+
+
+ Level4
+ _WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
+
+
+ Console
+ user32.lib;cfgmgr32.lib;setupapi.lib;Shlwapi.lib;%(AdditionalDependencies)
+ $(OutDir)$(TargetName)$(TargetExt)
+
+
+
+
+ Level4
+ _WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
+
+
+ Console
+ user32.lib;cfgmgr32.lib;setupapi.lib;Shlwapi.lib;%(AdditionalDependencies)
+ $(OutDir)$(TargetName)$(TargetExt)
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/windows/tools/wwansvc/api.c b/src/windows/tools/wwansvc/api.c
new file mode 100644
index 0000000..585debf
--- /dev/null
+++ b/src/windows/tools/wwansvc/api.c
@@ -0,0 +1,369 @@
+/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*
+
+ A P I . C
+
+GENERAL DESCRIPTION
+ This file implements the IOCTL-based communication API for the
+ Qualcomm MTU configuration service (qcmtusvc).
+
+ Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ SPDX-License-Identifier: BSD-3-Clause
+
+*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/
+
+#include "api.h"
+#include "reg.h"
+#include "utils.h"
+
+static CRITICAL_SECTION TidLock;
+
+VOID QCWWAN_InitializeResources(VOID)
+{
+ InitializeCriticalSection(&TidLock);
+} // QCWWAN_Initialize
+
+VOID QCWWAN_ReleaseResources(VOID)
+{
+ DeleteCriticalSection(&TidLock);
+} // QCWWAN_ReleaseResources
+
+HANDLE QCWWAN_OpenService(PCHAR DeviceFriendlyName, UCHAR ServiceType)
+{
+ HANDLE hServiceDevice, hDevice;
+ char controlFileName[SERVICE_FILE_BUF_LEN];
+ char serviceFileName[SERVICE_FILE_BUF_LEN];
+ char tmpName[SERVICE_FILE_BUF_LEN];
+ BOOL bResult;
+
+ // Init service file name buffer
+ ZeroMemory(serviceFileName, SERVICE_FILE_BUF_LEN);
+ ZeroMemory(controlFileName, SERVICE_FILE_BUF_LEN);
+ ZeroMemory(tmpName, SERVICE_FILE_BUF_LEN);
+
+ bResult = QCWWAN_GetControlFileName
+ (
+ DeviceFriendlyName,
+ tmpName
+ );
+
+ if (bResult == FALSE)
+ {
+ printf("Error: couldn't retrieve control file, error %u\n", GetLastError());
+ return INVALID_HANDLE_VALUE;
+ }
+
+ sprintf(controlFileName, "\\\\.\\%s", tmpName);
+
+ hDevice = CreateFile
+ (
+ controlFileName,
+ GENERIC_WRITE | GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL
+ );
+
+ if (hDevice == INVALID_HANDLE_VALUE)
+ {
+ printf("ERROR opening control file <%s>: INVALID_HANDLE_VALUE (error code %u)\n",
+ controlFileName, GetLastError());
+ return INVALID_HANDLE_VALUE;
+ }
+
+ // Get QMUX service file
+ ZeroMemory(tmpName, SERVICE_FILE_BUF_LEN);
+ bResult = GetServiceFile(hDevice, tmpName, ServiceType);
+ CloseHandle(hDevice); // close control file handle
+
+ if (bResult == FALSE)
+ {
+ printf("ERROR: cannot get service file.\n");
+ return INVALID_HANDLE_VALUE;
+ }
+
+ sprintf(serviceFileName, "\\\\.\\%s", tmpName);
+
+ // 2. Conduct read and write
+ hServiceDevice = CreateFile
+ (
+ serviceFileName,
+ GENERIC_WRITE | GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
+ NULL
+ );
+ if (hServiceDevice == INVALID_HANDLE_VALUE)
+ {
+ printf("ERROR opening service <%s>: INVALID_HANDLE_VALUE (error code 0x%x)\n",
+ serviceFileName, GetLastError());
+ return INVALID_HANDLE_VALUE;
+ }
+
+ return hServiceDevice;
+} // QCWWAN_OpenService
+
+BOOL QCWWAN_CloseService
+(
+ HANDLE ServiceHandle
+)
+{
+ return CloseHandle(ServiceHandle);
+} // QCWWAN_CloseService
+
+BOOL QCWWAN_Cleanup
+(
+ HANDLE ServiceHandle
+)
+{
+ return CancelIo(ServiceHandle);
+} // QCWWAN_Cleanup
+
+HANDLE QCWWAN_RegisterDeviceNotification
+(
+ HANDLE ServiceHandle,
+ NOTIFICATION_CALLBACK CallBack,
+ PVOID Context
+)
+{
+ HANDLE hNotificationThreadHandle;
+ PNOTIFICATION_CONTEXT context;
+ DWORD tid;
+
+ if (CallBack == NULL)
+ {
+ printf("Error: Unable to register notification (no callback)\n");
+ return INVALID_HANDLE_VALUE;
+ }
+
+ context = (PNOTIFICATION_CONTEXT)malloc(sizeof(NOTIFICATION_CONTEXT));
+ if (context == NULL)
+ {
+ printf("Error: NOTIFICATION_CONTEXT memory allocation failure.\n");
+ return INVALID_HANDLE_VALUE;
+ }
+
+ context->ServiceHandle = ServiceHandle;
+ context->CallBack = CallBack;
+ context->Context = Context;
+
+ hNotificationThreadHandle = CreateThread
+ (
+ NULL,
+ 0,
+ RegisterNotification,
+ (LPVOID)context,
+ 0,
+ &tid
+ );
+
+ if (hNotificationThreadHandle == INVALID_HANDLE_VALUE)
+ {
+ printf("Error: Unable to register notification, error %d\n", GetLastError());
+ free(context);
+ }
+
+ return hNotificationThreadHandle;
+} // QCWWAN_RegisterDeviceNotification
+
+BOOL QCWWAN_ReadRaw
+(
+ HANDLE ServiceHandle,
+ PCHAR ReceiveBuffer,
+ ULONG ReceiveBufferSize,
+ PULONG BytesRead
+)
+{
+ OVERLAPPED ov;
+ BOOL bResult = FALSE;
+ DWORD dwStatus = NO_ERROR;
+
+ ZeroMemory(&ov, sizeof(ov));
+ ov.Offset = 0;
+ ov.OffsetHigh = 0;
+ ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (ov.hEvent == NULL)
+ {
+ printf("Read failure, event error %u\n", GetLastError());
+ return bResult;
+ }
+
+ *BytesRead = 0;
+
+ bResult = ReadFile
+ (
+ ServiceHandle,
+ ReceiveBuffer,
+ ReceiveBufferSize,
+ BytesRead,
+ &ov
+ );
+
+ if (bResult == TRUE)
+ {
+ return bResult;
+ }
+ else
+ {
+ dwStatus = GetLastError();
+
+ if (ERROR_IO_PENDING != dwStatus)
+ {
+ printf("Read failure, error %u\n", dwStatus);
+ }
+ else
+ {
+ bResult = GetOverlappedResult
+ (
+ ServiceHandle,
+ &ov,
+ BytesRead,
+ TRUE // no return until operaqtion completes
+ );
+
+ if (bResult == FALSE)
+ {
+ dwStatus = GetLastError();
+ printf("Read/GetOverlappedResult failure, error %u\n", dwStatus);
+ }
+ }
+ }
+
+ if (ov.hEvent != NULL)
+ {
+ CloseHandle(ov.hEvent);
+ }
+
+ return bResult;
+
+} // QCWWAN_ReadRaw
+
+BOOL QCWWAN_SendRaw
+(
+ HANDLE ServiceHandle,
+ PCHAR SendBuffer,
+ ULONG SendLength,
+ PULONG BytesWritten
+)
+{
+ BOOL bResult = FALSE;
+ OVERLAPPED ov;
+ DWORD dwStatus = NO_ERROR;
+
+ ZeroMemory(&ov, sizeof(ov));
+ ov.Offset = 0;
+ ov.OffsetHigh = 0;
+ ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (ov.hEvent == NULL)
+ {
+ printf("Read error, event error %u\n", GetLastError());
+ return bResult;
+ }
+
+ *BytesWritten = 0;
+
+ // send message
+ if ((SendBuffer != NULL) && (SendLength != 0))
+ {
+#ifdef MY_UNIT_TEST
+ return 0;
+#endif // MY_UNIT_TEST
+
+ bResult = WriteFile
+ (
+ ServiceHandle,
+ SendBuffer,
+ SendLength,
+ BytesWritten,
+ &ov
+ );
+
+ if (bResult == FALSE)
+ {
+ dwStatus = GetLastError();
+
+ if (ERROR_IO_PENDING != dwStatus)
+ {
+ printf("write error, error %u\n", dwStatus);
+ }
+ else
+ {
+ bResult = GetOverlappedResult
+ (
+ ServiceHandle,
+ &ov,
+ BytesWritten,
+ TRUE // no return until operaqtion completes
+ );
+
+ if (bResult == FALSE)
+ {
+ dwStatus = GetLastError();
+ printf("Write error, error %u\n", dwStatus);
+ }
+ }
+ }
+ }
+ else
+ {
+ printf("Write error: cannot get message to send.\n");
+ }
+
+ if (ov.hEvent != NULL)
+ {
+ CloseHandle(ov.hEvent);
+ }
+
+ return bResult;
+} // QCWWAN_SendRaw
+
+USHORT QCWWAN_GenerateTransactionId(VOID)
+{
+ static USHORT tid = 0;
+ USHORT retVal;
+
+ EnterCriticalSection(&TidLock);
+
+ if (++tid == 0)
+ {
+ tid = 1;
+ }
+ retVal = tid;
+
+ LeaveCriticalSection(&TidLock);
+
+ return retVal;
+} // QCWWAN_GenerateTransactionId
+
+BOOL WINAPI QCWWAN_GetClientId
+(
+ HANDLE ServiceHandle,
+ PUCHAR ClientId
+)
+{
+ DWORD bytesReturned = 0;
+ UCHAR cid;
+
+ if (
+ DeviceIoControl
+ (
+ ServiceHandle,
+ IOCTL_QCDEV_QMI_GET_CLIENT_ID,
+ NULL,
+ 0,
+ (LPVOID)&cid,
+ sizeof(UCHAR),
+ &bytesReturned,
+ NULL
+ )
+ )
+ {
+ *ClientId = cid;
+ return TRUE;
+ }
+
+ return FALSE;
+} // QCWWAN_GetClientId
diff --git a/src/windows/tools/wwansvc/api.h b/src/windows/tools/wwansvc/api.h
new file mode 100644
index 0000000..999bc36
--- /dev/null
+++ b/src/windows/tools/wwansvc/api.h
@@ -0,0 +1,164 @@
+/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*
+
+ A P I . H
+
+GENERAL DESCRIPTION
+ This file defines IOCTL codes, QMI message structures, and function
+ prototypes for the Qualcomm MTU configuration service API.
+
+ Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ SPDX-License-Identifier: BSD-3-Clause
+
+*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/
+
+#ifndef API_H
+#define API_H
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+typedef enum _QMI_SERVICE_TYPE
+{
+ QMUX_TYPE_CTL = 0x00,
+ QMUX_TYPE_WDS = 0x01,
+ QMUX_TYPE_DMS = 0x02,
+ QMUX_TYPE_NAS = 0x03,
+ QMUX_TYPE_QOS = 0x04,
+ QMUX_TYPE_MAX,
+ QMUX_TYPE_ALL = 0xFF
+} QMI_SERVICE_TYPE;
+
+#define SERVICE_FILE_BUF_LEN 256
+#define QMUX_NUM_THREADS 3
+#define QMUX_MAX_DATA_LEN 2048
+#define QMUX_MAX_CMD_LEN 2048
+
+// User-defined IOCTL code range: 2048-4095
+#define QCDEV_IOCTL_INDEX 2048
+#define QCDEV_DUPLICATED_NOTIFICATION_REQ 0x00000002L
+
+#define IOCTL_QCDEV_WAIT_NOTIFY CTL_CODE(FILE_DEVICE_UNKNOWN, \
+ QCDEV_IOCTL_INDEX+1, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+/* Make the following code as 3338 - USB debug mask */
+#define IOCTL_QCDEV_SET_DBG_UMSK CTL_CODE(FILE_DEVICE_UNKNOWN, \
+ QCDEV_IOCTL_INDEX+1290, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+/* Make the following code as 3339 - MP debug mask */
+#define IOCTL_QCDEV_SET_DBG_MMSK CTL_CODE(FILE_DEVICE_UNKNOWN, \
+ QCDEV_IOCTL_INDEX+1291, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+/* Make the following code as 3340 - MP debug mask */
+#define IOCTL_QCDEV_GET_SERVICE_FILE CTL_CODE(FILE_DEVICE_UNKNOWN, \
+ QCDEV_IOCTL_INDEX+1292, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+/* Make the following code as 3343 - QMI Client Id */
+#define IOCTL_QCDEV_QMI_GET_CLIENT_ID CTL_CODE(FILE_DEVICE_UNKNOWN, \
+ QCDEV_IOCTL_INDEX+1295, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+/* Make the following code as XXX - MTU size */
+#define IOCTL_QCDEV_IPV4_MTU_NOTIFY CTL_CODE(FILE_DEVICE_UNKNOWN, \
+ QCDEV_IOCTL_INDEX+50, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+/* Make the following code as XXX - MTU size */
+#define IOCTL_QCDEV_IPV6_MTU_NOTIFY CTL_CODE(FILE_DEVICE_UNKNOWN, \
+ QCDEV_IOCTL_INDEX+51, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+typedef VOID(_stdcall *NOTIFICATION_CALLBACK)(HANDLE, ULONG, PVOID);
+
+typedef struct _NOTIFICATION_CONTEXT
+{
+ HANDLE ServiceHandle;
+ NOTIFICATION_CALLBACK CallBack;
+ PVOID Context;
+} NOTIFICATION_CONTEXT, *PNOTIFICATION_CONTEXT;
+
+// Function Prototypes
+
+VOID QCWWAN_InitializeResources(VOID);
+
+VOID QCWWAN_ReleaseResources(VOID);
+
+HANDLE QCWWAN_OpenService
+(
+ IN PCHAR DeviceFriendlyName,
+ IN UCHAR ServiceType
+);
+
+BOOL QCWWAN_CloseService
+(
+ IN HANDLE ServiceHandle
+);
+
+BOOL QCWWAN_Cleanup
+(
+ IN HANDLE ServiceHandle
+);
+
+HANDLE QCWWAN_RegisterDeviceNotification
+(
+ HANDLE ServiceHandle,
+ NOTIFICATION_CALLBACK CallBack,
+ PVOID Context
+);
+
+BOOL QCWWAN_ReadRaw
+(
+ IN HANDLE ServiceHandle,
+ OUT PCHAR ReceiveBuffer,
+ IN ULONG ReceiveBufferSize,
+ OUT PULONG BytesRead
+);
+
+BOOL QCWWAN_SendRaw
+(
+ IN HANDLE ServiceHandle,
+ IN PCHAR SendBuffer,
+ IN ULONG SendLength,
+ OUT PULONG BytesWritten
+);
+
+BOOL QCWWAN_TextMessageToBinary
+(
+ IN PCHAR Text,
+ OUT PCHAR BinaryBuffer,
+ IN ULONG BinaryBufferSize,
+ OUT PULONG ReturnedBinaryLength
+);
+
+BOOL QCWWAN_BinaryMessageToText
+(
+ IN HANDLE ServiceHandle,
+ IN PCHAR BinaryMessage,
+ IN ULONG BinaryLength,
+ OUT PCHAR TextBuffer,
+ IN ULONG TextBufferSize
+);
+
+USHORT QCWWAN_GenerateTransactionId(VOID);
+
+BOOL WINAPI QCWWAN_GetClientId
+(
+ IN HANDLE ServiceHandle,
+ OUT PUCHAR ClientId
+);
+
+#endif // API_H
diff --git a/src/windows/tools/wwansvc/devmon.c b/src/windows/tools/wwansvc/devmon.c
new file mode 100644
index 0000000..13c50f9
--- /dev/null
+++ b/src/windows/tools/wwansvc/devmon.c
@@ -0,0 +1,1178 @@
+/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*
+
+ D E V M O N . C
+
+GENERAL DESCRIPTION
+ This file implements device monitoring, enumeration, and NIC
+ management for the Qualcomm MTU configuration service.
+
+ Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ SPDX-License-Identifier: BSD-3-Clause
+
+*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/
+
+#include
+#include
+#include
+#include
+#include "api.h"
+#include "main.h"
+#include "utils.h"
+#include "reg.h"
+#include "devmon.h"
+
+#define DEVMN_MON_REG_KEY "HARDWARE\\DEVICEMAP\\SERIALCOMM"
+#define DEVMON_NET_CONNECTION_REG_KEY "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}"
+
+HANDLE RegMonChangeEvent;
+HANDLE RegMonExitEvent;
+DEVMN_NIC_RECORD MtuNicRecord[DEVMN_NUM_NIC]; // this is temporary
+DEVMN_NIC_RECORD MtuNicActiveRecord[DEVMN_NUM_NIC];
+PDEVMN_NIC_RECORD NicRecHead, NicRecTail;
+static int LastNicIdx;
+
+#define DEVMN_DEVMON_UNIT_TEST
+
+typedef struct _DIDLG_DATA
+{
+ HDEVINFO DeviceInfoSet;
+ HDEVNOTIFY hDevNotify;
+ GUID InterfaceClassGuid;
+} DIDLG_DATA, *PDIDLG_DATA;
+
+typedef struct _QCMTU_MONITOR_ST
+{
+ HANDLE StartupEvent;
+ DWORD StartupWaitTime;
+ UCHAR State;
+} QCMTU_MONITOR_ST, *PQCMTU_MONITOR_ST;
+
+VOID DisplayNicList(VOID)
+{
+ int i;
+
+ if (AppDebugMode == FALSE)
+ {
+ return;
+ }
+
+ QCMTU_Print("-->DisplayNicList\n");
+
+ for (i = 0; i < DEVMN_NUM_NIC; ++i)
+ {
+ if ((MtuNicRecord[i].FriendlyName[0] != 0) && (FALSE == IsInActiveList(MtuNicRecord[i].FriendlyName)))
+ {
+ QCMTU_Print("NIC[%u] FN: <%s>\n", i, MtuNicRecord[i].FriendlyName);
+ QCMTU_Print("NIC[%u] CN: <%s>\n", i, MtuNicRecord[i].ControlFileName);
+ QCMTU_Print("NIC[%u] ID: <%s>\n", i, MtuNicRecord[i].InterfaceId);
+ QCMTU_Print("NIC[%u] IF: <%s>\n", i, MtuNicRecord[i].InterfaceName);
+
+ // test code
+ if (FALSE)
+ {
+ QC_MTU mtu;
+
+ mtu.IpVer = 4;
+ mtu.MTU = 1400;
+
+ QCMTU_SetMTU(&mtu, MtuNicRecord[i].InterfaceName);
+ }
+ }
+ else
+ {
+ // break;
+ }
+ }
+
+ QCMTU_Print("<--DisplayNicList\n");
+}
+
+VOID DisplayActiveList(VOID)
+{
+ PDEVMN_NIC_RECORD item;
+
+ if (AppDebugMode == FALSE)
+ {
+ return;
+ }
+ QCMTU_RequestLock();
+
+ item = NicRecHead;
+ while (item != NULL)
+ {
+ QCMTU_Print("ActiveNIC[%u] FN: <%s>\n", item->Index, item->FriendlyName);
+ item = item->Next;
+ }
+ if (NicRecTail != NULL)
+ {
+ QCMTU_Print("ActiveNIC-Tail[%u] FN: <%s>\n", NicRecTail->Index, NicRecTail->FriendlyName);
+ }
+
+ QCMTU_ReleaseLock();
+
+} // DisplayActiveList
+
+VOID InitializeNicRecords(VOID)
+{
+ ZeroMemory(MtuNicRecord, sizeof(DEVMN_NIC_RECORD) * DEVMN_NUM_NIC);
+ ZeroMemory(MtuNicActiveRecord, sizeof(DEVMN_NIC_RECORD) * DEVMN_NUM_NIC);
+} // InitializeNicRecords
+
+VOID DEVMN_DeviceMonitor(BOOL AppMode)
+{
+ HANDLE hDeviceMonitorHandle;
+ DWORD tid;
+ DEV_BROADCAST_DEVICEINTERFACE notificationFilter;
+ QCMTU_MONITOR_ST startupState;
+
+ QCMTU_Print("Start MTU device monitor thread\n");
+
+ InitializeNicRecords();
+ startupState.StartupEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ startupState.StartupWaitTime = 10000; // 10s
+ startupState.State = 0;
+ if (startupState.StartupEvent == NULL)
+ {
+ QCMTU_Print("Unable to create startup event: %d\n", GetLastError());
+ return;
+ }
+
+ hDeviceMonitorHandle = CreateThread
+ (
+ NULL,
+ 0,
+ DEVMN_DevMonThread,
+ (LPVOID)&startupState,
+ 0,
+ &tid
+ );
+ if (hDeviceMonitorHandle == INVALID_HANDLE_VALUE)
+ {
+ QCMTU_Print("Unable to launch device monitor thread, error %d\n", GetLastError());
+ printf("Error: Unable to launch, possibly another instance already running\n");
+ return;
+ }
+
+ WaitForSingleObject(startupState.StartupEvent, startupState.StartupWaitTime);
+ QCMTU_Print("Startup event signaled: %d\n", startupState.State);
+ if (startupState.State != 0)
+ {
+ QCMTU_Print("Unable to launch device monitor thread\n");
+ CloseHandle(startupState.StartupEvent);
+ return;
+ }
+ CloseHandle(startupState.StartupEvent);
+
+ if (AppMode == TRUE)
+ {
+ QCMTU_Print("App mode, create window\n");
+
+ AppModuleHandle = GetModuleHandle(NULL);
+ if (AppModuleHandle == NULL)
+ {
+ QCMTU_Print("App mode, GetModuleHandle failure %d\n", GetLastError());
+ }
+ AppWndClass.cbSize = sizeof(WNDCLASSEX);
+ AppWndClass.style = CS_HREDRAW | CS_VREDRAW;
+ AppWndClass.lpfnWndProc = (WNDPROC)MyWindowProc;
+ AppWndClass.cbClsExtra = 0;
+ AppWndClass.cbWndExtra = 0;
+ AppWndClass.hInstance = AppModuleHandle;
+ AppWndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION); // NULL;
+ AppWndClass.hCursor = LoadCursor(NULL, IDC_ARROW); // NULL;
+ AppWndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); // NULL;
+ AppWndClass.lpszMenuName = TEXT("GenericMenu"); // NULL;
+ AppWndClass.lpszClassName = AppClassName;
+ AppWndClass.hIconSm = NULL;
+
+ if (RegisterClassEx(&AppWndClass) == 0)
+ {
+ QCMTU_Print("App mode, RegisterClass failure %d\n", GetLastError());
+ }
+
+ AppWinHandle = CreateWindowEx
+ (
+ WS_EX_NOACTIVATE,
+ AppClassName,
+ AppTitle,
+ WS_MINIMIZE, // WS_OVERLAPPEDWINDOW,
+ CW_USEDEFAULT,
+ CW_USEDEFAULT,
+ CW_USEDEFAULT,
+ CW_USEDEFAULT,
+ NULL,
+ NULL,
+ AppModuleHandle,
+ NULL
+ );
+ if (AppWinHandle == NULL)
+ {
+ QCMTU_Print("App mode, CreateWindow failure %d\n", GetLastError());
+ }
+ else
+ {
+ // ShowWindow(AppWinHandle, SW_HIDE);
+ }
+ }
+ else
+ {
+ // Register device notification
+ MyDeviceGuid = GUID_QCMTU_NIC;
+ ZeroMemory(¬ificationFilter, sizeof(notificationFilter));
+ notificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
+ notificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
+ // notificationFilter.dbcc_classguid = MyDeviceGuid;
+ memcpy(&(NotificationFilter.dbcc_classguid), &(GUID_QCMTU_NIC), sizeof(struct _GUID));
+
+ DevNotificationHandle = RegisterDeviceNotification
+ (
+ ServiceStateHandle,
+ ¬ificationFilter,
+ DEVICE_NOTIFY_SERVICE_HANDLE
+ );
+ if (DevNotificationHandle == NULL)
+ {
+ QCMTU_Print("Unable to register device notification, error %d\n", GetLastError());
+ SetEvent(RegMonExitEvent);
+ goto WaitExit;
+ }
+ }
+
+ // we terminate the service from console with ESC in application mode
+ if (AppMode == TRUE)
+ {
+ int keyPressed = 0;
+
+ while (keyPressed != KEY_PRESSED_ESC)
+ {
+ keyPressed = _getch();
+ }
+ SetEvent(RegMonExitEvent);
+ }
+
+WaitExit:
+
+ WaitForSingleObject(hDeviceMonitorHandle, INFINITE);
+ UnregisterDeviceNotification(hDeviceMonitorHandle);
+ CloseHandle(hDeviceMonitorHandle);
+ QCMTU_Print("Device monitor thread terminated\n");
+
+ return;
+} // DEVMN_DeviceMonitor
+
+DWORD WINAPI DEVMN_DevMonThread(PVOID Context)
+{
+ PQCMTU_MONITOR_ST startupSt;
+ HKEY hTestKey;
+ BOOL ret = FALSE, notDone = TRUE;
+ HANDLE waitHandle[2 * DEVMN_NUM_NIC];
+ int waitCount = DEVMON_EVENT_COUNT;
+ BOOL firstRun = TRUE, bRegChanged = FALSE;
+
+ startupSt = (PQCMTU_MONITOR_ST)Context;
+ NicRecHead = NicRecTail = NULL;
+ RegMonChangeEvent = CreateEvent(NULL, TRUE, FALSE, NULL); // signaled to scan reg
+ if (INVALID_HANDLE_VALUE == RegMonChangeEvent)
+ {
+ QCMTU_Print("Error: cannot create reg mon event\n");
+ startupSt->State = 1;
+ SetEvent(startupSt->StartupEvent);
+ return 1;
+ }
+ RegMonExitEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ if (INVALID_HANDLE_VALUE == RegMonExitEvent)
+ {
+ QCMTU_Print("Error: cannot create reg exit event\n");
+ CloseHandle(RegMonChangeEvent);
+ startupSt->State = 1;
+ SetEvent(startupSt->StartupEvent);
+ return 1;
+ }
+ waitHandle[DEVMON_EVENT_REG_CHANGE] = RegMonChangeEvent;
+ waitHandle[DEVMON_EVENT_EXIT] = RegMonExitEvent;
+
+ if (RegOpenKeyEx
+ (
+ HKEY_LOCAL_MACHINE,
+ DEVMN_MON_REG_KEY,
+ 0,
+ KEY_READ,
+ &hTestKey
+ ) != ERROR_SUCCESS
+ )
+ {
+
+ QCMTU_Print("Error: cannot open reg key for monitoring\n");
+ CloseHandle(RegMonChangeEvent);
+ CloseHandle(RegMonExitEvent);
+ startupSt->State = 1;
+ SetEvent(startupSt->StartupEvent);
+
+ return ret;
+ }
+
+ if (QCMTU_InitializeLock() == NULL)
+ {
+ QCMTU_Print("Error: cannot initialize lock\n");
+ CloseHandle(RegMonChangeEvent);
+ CloseHandle(RegMonExitEvent);
+ CloseHandle(hTestKey);
+ startupSt->State = 1;
+ SetEvent(startupSt->StartupEvent);
+ return 1;
+ }
+
+ if (QCMTU_RunSingleInstance() == FALSE)
+ {
+ startupSt->State = 1;
+ SetEvent(startupSt->StartupEvent);
+ return 1;
+ }
+
+ startupSt->State = 0;
+ SetEvent(startupSt->StartupEvent);
+
+ ZeroMemory(MtuNicActiveRecord, sizeof(DEVMN_NIC_RECORD) * DEVMN_NUM_NIC);
+
+ while (notDone == TRUE)
+ {
+ DWORD waitIdx, waitInterval = DEVMON_SCAN_INTERVAL;
+
+ RegNotifyChangeKeyValue
+ (
+ (HKEY)hTestKey,
+ TRUE,
+ REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_LAST_SET,
+ RegMonChangeEvent,
+ TRUE
+ );
+
+ if (firstRun == TRUE)
+ {
+ // need to scan the reg the first time we run the service
+ SetEvent(RegMonChangeEvent);
+ firstRun = FALSE;
+ }
+
+ // If all handles are not closed, wait
+ waitIdx = WaitForMultipleObjects
+ (
+ waitCount,
+ waitHandle,
+ FALSE,
+ waitInterval // INFINITE
+ );
+ waitIdx -= WAIT_OBJECT_0;
+
+ switch (waitIdx)
+ {
+ case DEVMON_EVENT_EXIT:
+ {
+ // terminate all MtuThreads
+ QCMTU_Print("DevMon_THREAD_EXIT\n");
+ ResetEvent(waitHandle[waitIdx]);
+ notDone = FALSE;
+ TerminateAllMtuThreads();
+ break;
+ }
+ case DEVMON_EVENT_REG_CHANGE:
+ {
+ QCMTU_Print("DevMon_THREAD_REG_CHANGE\n");
+
+ ResetEvent(waitHandle[waitIdx]);
+ bRegChanged = TRUE;
+
+ /***
+ // examine the NICs
+ ZeroMemory(MtuNicRecord, sizeof(DEVMN_NIC_RECORD)*DEVMN_NUM_NIC);
+ LastNicIdx = 0;
+ GetNicList();
+ DisplayNicList();
+ SpawnMtuService();
+ ***/
+
+ break;
+ }
+ case WAIT_TIMEOUT:
+ {
+ // if (bRegChanged == TRUE) // comment out to reply on periodic reg scan (not reg change)
+ {
+ QCMTU_Print("DevMon_WAIT_TIMEOUT: scanning system...\n");
+
+ bRegChanged = FALSE;
+
+ // examine the NICs 1s after registry change to consolidate multiple reg changes
+ ZeroMemory(MtuNicRecord, sizeof(DEVMN_NIC_RECORD) * DEVMN_NUM_NIC);
+ LastNicIdx = 0;
+ GetNicList();
+ QCMTU_Print("Scanned system, %d total adapters\n", LastNicIdx);
+ DisplayNicList();
+ SpawnMtuService();
+ }
+ break;
+ }
+ default:
+ {
+ QCMTU_Print("DevMon_EVENT_DEFAULT: %d\n", waitIdx);
+ ResetEvent(waitHandle[waitIdx]);
+ break;
+ }
+ } // switch
+ } // while
+
+ CloseHandle(RegMonChangeEvent);
+ CloseHandle(RegMonExitEvent);
+ RegCloseKey(hTestKey);
+ QCMTU_DeleteLock();
+ QCMTU_CloseRunningInstance();
+
+ QCMTU_Print("Reg monitor thread exit\n");
+ return 0;
+} // DEVMN_DevMonThread
+
+VOID GetNicList(VOID)
+{
+ HKEY hTestKey;
+
+ if (RegOpenKeyEx
+ (
+ HKEY_LOCAL_MACHINE,
+ TEXT(QCNET_REG_HW_KEY),
+ 0,
+ KEY_READ,
+ &hTestKey
+ ) == ERROR_SUCCESS
+ )
+ {
+ TCHAR subKey[MAX_KEY_LENGTH];
+ DWORD nameLen;
+ TCHAR className[MAX_PATH] = TEXT("");
+ DWORD classNameLen = MAX_PATH;
+ DWORD numSubKeys = 0;
+ DWORD subKeyMaxLen;
+ DWORD classMaxLen;
+ DWORD numKeyValues;
+ DWORD valueMaxLen;
+ DWORD valueDataMaxLen;
+ DWORD securityDescLen;
+ FILETIME lastWriteTime;
+ DWORD i, retCode;
+
+ // retrieve class name
+ retCode = RegQueryInfoKey
+ (
+ hTestKey,
+ className,
+ &classNameLen,
+ NULL,
+ &numSubKeys,
+ &subKeyMaxLen,
+ &classMaxLen,
+ &numKeyValues,
+ &valueMaxLen,
+ &valueDataMaxLen,
+ &securityDescLen,
+ &lastWriteTime
+ );
+
+ // go through subkeys
+ if (numSubKeys)
+ {
+ for (i = 0; i < numSubKeys; i++)
+ {
+ nameLen = MAX_KEY_LENGTH;
+ retCode = RegEnumKeyEx
+ (
+ hTestKey, i,
+ subKey,
+ &nameLen,
+ NULL,
+ NULL,
+ NULL,
+ &lastWriteTime
+ );
+ if (retCode == ERROR_SUCCESS)
+ {
+ BOOL result;
+
+ // _tprintf(TEXT("A-(%d) %s\n"), i+1, subKey);
+ result = QueryNicSwKeys(subKey);
+ if (result == TRUE)
+ {
+ // printf("QueryKey: TRUE\n");
+ return;
+ }
+ }
+ }
+ }
+
+ RegCloseKey(hTestKey);
+
+ GetNetInterfaceName();
+
+ return;
+ }
+
+ return;
+} // GetNicList
+
+BOOL QueryNicSwKeys(PTCHAR InstanceKey)
+{
+ HKEY hTestKey;
+ char fullKeyName[MAX_KEY_LENGTH];
+ BOOL ret = FALSE;
+
+ sprintf(fullKeyName, "%s\\%s", QCNET_REG_HW_KEY, TEXT(InstanceKey));
+
+ // QCMTU_Print("-->QueryNicSwKeys: full key name: [%s]\n", fullKeyName);
+
+ if (RegOpenKeyEx
+ (
+ HKEY_LOCAL_MACHINE,
+ fullKeyName,
+ 0,
+ KEY_READ,
+ &hTestKey
+ ) == ERROR_SUCCESS
+ )
+ {
+ ret = QueryInstance(hTestKey, fullKeyName);
+ RegCloseKey(hTestKey);
+
+ return ret;
+ }
+
+ // QCMTU_Print("<--QueryNicSwKeys: full key name: [%s]\n", fullKeyName);
+
+ return FALSE;
+} // QueryNicSwKeys
+
+BOOL QueryInstance
+(
+ HKEY hKey,
+ PCHAR FullKeyName
+)
+{
+ TCHAR subKey[MAX_KEY_LENGTH];
+ DWORD nameLen;
+ TCHAR className[MAX_PATH] = TEXT("");
+ DWORD classNameLen = MAX_PATH;
+ DWORD numSubKeys = 0;
+ DWORD subKeyMaxLen;
+ DWORD classMaxLen;
+ DWORD numKeyValues;
+ DWORD valueMaxLen;
+ DWORD valueDataMaxLen;
+ DWORD securityDescLen;
+ FILETIME lastWriteTime;
+ char fullKeyName[MAX_KEY_LENGTH];
+
+ DWORD i, retCode;
+
+ // QCMTU_Print("-->QueryInstance: full key name: [%s]\n", FullKeyName);
+
+ // Get the class name and the value count.
+ retCode = RegQueryInfoKey
+ (
+ hKey,
+ className,
+ &classNameLen,
+ NULL,
+ &numSubKeys,
+ &subKeyMaxLen,
+ &classMaxLen,
+ &numKeyValues,
+ &valueMaxLen,
+ &valueDataMaxLen,
+ &securityDescLen,
+ &lastWriteTime
+ );
+
+ // Enumerate the subkeys, until RegEnumKeyEx fails.
+ if (numSubKeys)
+ {
+ for (i = 0; i < numSubKeys; i++)
+ {
+ nameLen = MAX_KEY_LENGTH;
+ retCode = RegEnumKeyEx
+ (
+ hKey, i,
+ subKey,
+ &nameLen,
+ NULL,
+ NULL,
+ NULL,
+ &lastWriteTime
+ );
+ if (retCode == ERROR_SUCCESS)
+ {
+ BOOL result;
+
+ // _tprintf(TEXT("C-(%d) %s\n"), i+1, subKey);
+ sprintf(fullKeyName, "%s\\%s", FullKeyName, TEXT(subKey));
+ result = GetDeviceInstance(fullKeyName);
+ if (result == TRUE)
+ {
+ // QCMTU_Print("-->QueryInstance: TRUE\n");
+ return TRUE;
+ }
+ }
+ }
+ }
+
+ // QCMTU_Print("<--QueryInstance: FALSE\n");
+
+ return FALSE;
+} // QueryInstance
+
+BOOL GetDeviceInstance(PTCHAR InstanceKey)
+{
+ HKEY hTestKey;
+ BOOL ret = FALSE;
+
+ if (RegOpenKeyEx
+ (
+ HKEY_LOCAL_MACHINE,
+ InstanceKey,
+ 0,
+ KEY_READ,
+ &hTestKey
+ ) == ERROR_SUCCESS
+ )
+ {
+ ret = GetInstanceNames
+ (
+ hTestKey,
+ "QCDeviceControlFile"
+ );
+
+ RegCloseKey(hTestKey);
+ }
+
+ return ret;
+}
+
+BOOL GetInstanceNames
+(
+ HKEY hKey,
+ PCHAR EntryName // "QCDeviceControlFile"
+)
+{
+ DWORD retCode = ERROR_SUCCESS;
+ TCHAR valueName[MAX_VALUE_NAME];
+ TCHAR swKeyName[MAX_VALUE_NAME];
+ DWORD valueNameLen = MAX_VALUE_NAME;
+ BOOL instanceFound = FALSE;
+ HKEY hSwKey;
+ CHAR friendlyName[MAX_VALUE_NAME];
+ CHAR controlFileName[MAX_VALUE_NAME];
+
+ valueName[0] = 0;
+
+ // first get device friendly name
+ retCode = RegQueryValueEx
+ (
+ hKey,
+ "FriendlyName", // "DriverDesc", // value name
+ NULL, // reserved
+ NULL, // returned type
+ valueName,
+ &valueNameLen
+ );
+
+ if (retCode == ERROR_SUCCESS)
+ {
+ valueName[valueNameLen] = 0;
+ instanceFound = TRUE;
+ // QCMTU_Print("GetInstanceNames: FriendlyName: [%s]\n", valueName);
+ }
+ else
+ {
+ // no FriendlyName, get DeviceDesc
+ valueName[0] = 0;
+ valueNameLen = MAX_VALUE_NAME;
+ retCode = RegQueryValueEx
+ (
+ hKey,
+ "DeviceDesc", // value name
+ NULL, // reserved
+ NULL, // returned type
+ valueName,
+ &valueNameLen
+ );
+ if (retCode == ERROR_SUCCESS)
+ {
+ valueName[valueNameLen] = 0;
+
+ instanceFound = TRUE;
+ // QCMTU_Print("GetInstanceNames: desc-%d: [%s]\n", instanceFound, valueName);
+ }
+ }
+
+ if (instanceFound == TRUE)
+ {
+ PCHAR p = (PCHAR)valueName + valueNameLen;
+
+ while ((p > valueName) && (*p != ';'))
+ {
+ p--;
+ };
+ if (*p == ';') p++;
+ strcpy(friendlyName, p);
+
+ // Get "Driver" instance path
+ valueName[0] = 0;
+ valueNameLen = MAX_VALUE_NAME;
+ retCode = RegQueryValueEx
+ (
+ hKey,
+ "Driver", // value name
+ NULL, // reserved
+ NULL, // returned type
+ valueName,
+ &valueNameLen
+ );
+ if (retCode == ERROR_SUCCESS)
+ {
+ // Construct device software key
+ valueName[valueNameLen] = 0;
+ sprintf(swKeyName, "%s\\%s", QCNET_REG_SW_KEY, TEXT(valueName));
+
+ // printf("Got SW Key <%s>\n", swKeyName);
+
+ // Open device software key
+ retCode = RegOpenKeyEx
+ (
+ HKEY_LOCAL_MACHINE,
+ swKeyName,
+ 0,
+ KEY_READ,
+ &hSwKey
+ );
+ if (retCode == ERROR_SUCCESS)
+ {
+ // Retrieve the control file name
+ valueName[0] = 0;
+ valueNameLen = MAX_VALUE_NAME;
+ retCode = RegQueryValueEx
+ (
+ hSwKey,
+ EntryName, // value name
+ NULL, // reserved
+ NULL, // returned type
+ valueName,
+ &valueNameLen
+ );
+
+ if (retCode == ERROR_SUCCESS)
+ {
+ PCHAR p = (PCHAR)valueName + valueNameLen;
+
+ valueName[valueNameLen] = 0;
+
+ while ((p > valueName) && (*p != '\\'))
+ {
+ p--;
+ }
+ if (*p == '\\') p++;
+ strcpy(controlFileName, p);
+
+ // record the names
+ strcpy(MtuNicRecord[LastNicIdx].FriendlyName, friendlyName);
+ strcpy(MtuNicRecord[LastNicIdx].ControlFileName, controlFileName);
+
+ // QCMTU_Print("GetInstanceNames: FriendlyName: [%s]\n", friendlyName); // DEBUG
+ // QCMTU_Print("GetInstanceNames: QCDeviceControlFile: [%s]\n", controlFileName); // DEBUG
+
+ // Get NIC GUID
+ valueName[0] = 0;
+ valueNameLen = MAX_VALUE_NAME;
+ retCode = RegQueryValueEx
+ (
+ hSwKey,
+ "NetCfgInstanceId",
+ NULL, // reserved
+ NULL, // returned type
+ valueName,
+ &valueNameLen
+ );
+
+ if (retCode == ERROR_SUCCESS)
+ {
+ valueName[valueNameLen] = 0;
+ strcpy(MtuNicRecord[LastNicIdx].InterfaceId, valueName);
+ // QCMTU_Print("GetInstanceNames: InterfaceId: [%s]\n", valueName); // DEBUG
+ }
+ else
+ {
+ MtuNicRecord[LastNicIdx].InterfaceId[0] = 0;
+ }
+
+ ++LastNicIdx;
+ if (LastNicIdx >= DEVMN_NUM_NIC)
+ {
+ QCMTU_Print("WARNING: max num adapters reached!\n");
+ LastNicIdx = DEVMN_NUM_NIC - 1;
+ }
+ RegCloseKey(hSwKey);
+ return FALSE; // TRUE;
+ }
+ else
+ {
+ // QCMTU_Print("GetInstanceNames: skip: [%s]\n", friendlyName);
+ }
+
+ RegCloseKey(hSwKey);
+ } // if (retCode == ERROR_SUCCESS)
+ } // if (retCode == ERROR_SUCCESS)
+ else
+ {
+ // QCMTU_Print("GetInstanceNames: cannot get device software key, retCode %u\n", retCode);
+ }
+ } // if (instanceFound == TRUE)
+
+ return FALSE;
+} // GetInstanceNames
+
+int SpawnMtuService(VOID)
+{
+ // Compare new NIC list and active list
+ int i;
+ PDEVMN_NIC_RECORD activeNic;
+
+ if (AppDebugMode == TRUE)
+ {
+ QCMTU_Print("-->SpawnMtuService\n");
+ }
+
+ for (i = 0; i < DEVMN_NUM_NIC; ++i)
+ {
+ if (MtuNicRecord[i].FriendlyName[0] != 0)
+ {
+ if (FALSE == IsInActiveList(MtuNicRecord[i].FriendlyName))
+ {
+ // add to active list
+ activeNic = AddNicToActiveList(i);
+ if (activeNic != NULL)
+ {
+ if (QMICL_StartMTUService(activeNic) == TRUE)
+ {
+ QCMTU_Print("SpawnMtuService: Started MTU service for <%s><%s>\n",
+ activeNic->FriendlyName, activeNic->InterfaceName);
+ }
+ else
+ {
+ DEVMN_RemoveActiveNic(activeNic);
+ DisplayActiveList();
+ if (AppDebugMode == TRUE)
+ {
+ QCMTU_Print("SpawnMtuService: Cannot start MTU service for <%s>\n", MtuNicRecord[i].FriendlyName);
+ }
+ }
+ }
+ else
+ {
+ QCMTU_Print("SpawnMtuService: AddNicToActiveList failure <%s>\n", MtuNicRecord[i].FriendlyName);
+ }
+ }
+ else
+ {
+ if (AppDebugMode == TRUE)
+ {
+ QCMTU_Print("SpawnMtuService: NIC already active <%s>\n", MtuNicRecord[i].FriendlyName);
+ }
+ }
+ }
+ else
+ {
+ if (AppDebugMode == TRUE)
+ {
+ QCMTU_Print("SpawnMtuService: no more NIC\n");
+ }
+ break;
+ }
+ }
+
+ if (AppDebugMode == TRUE)
+ {
+ QCMTU_Print("<--SpawnMtuService\n");
+ }
+ return 0;
+} // SpawnMtuService
+
+BOOL IsInActiveList(PCHAR NicFriendlyName)
+{
+ PDEVMN_NIC_RECORD item;
+ BOOL inList = FALSE;
+
+ QCMTU_RequestLock();
+
+ item = NicRecHead;
+ while (item != NULL)
+ {
+ if (strcmp(item->FriendlyName, NicFriendlyName) == 0)
+ {
+ inList = TRUE;
+ break;
+ }
+ item = item->Next;
+ }
+
+ QCMTU_ReleaseLock();
+
+ return inList;
+} // IsInActiveList
+
+PDEVMN_NIC_RECORD AddNicToActiveList(int NicIdx)
+{
+ int i, idx = -1;
+
+ QCMTU_RequestLock();
+
+ // find available place holder
+ for (i = 0; i < DEVMN_NUM_NIC; ++i)
+ {
+ if (MtuNicActiveRecord[i].InUse == FALSE)
+ {
+ idx = i;
+ break;
+ }
+ }
+
+ if (idx < 0)
+ {
+ QCMTU_Print("AddNicToActiveList: Error: no place holder for MTU rec\n");
+ QCMTU_ReleaseLock();
+ return NULL;
+ }
+
+
+ if (NicRecHead == NULL)
+ {
+ NicRecHead = NicRecTail = &MtuNicActiveRecord[idx];
+ }
+ else
+ {
+ NicRecTail->Next = &MtuNicActiveRecord[idx];
+ NicRecTail = NicRecTail->Next;
+ }
+ NicRecTail->Next = NULL;
+
+ strcpy(NicRecTail->FriendlyName, MtuNicRecord[NicIdx].FriendlyName);
+ strcpy(NicRecTail->ControlFileName, MtuNicRecord[NicIdx].ControlFileName);
+ strcpy(NicRecTail->InterfaceId, MtuNicRecord[NicIdx].InterfaceId);
+ strcpy(NicRecTail->InterfaceName, MtuNicRecord[NicIdx].InterfaceName);
+ NicRecTail->Index = idx; // same as its array index
+ NicRecTail->InUse = TRUE;
+
+ QCMTU_ReleaseLock();
+
+ return NicRecTail;
+
+} // AddNicToActiveList
+
+BOOL DEVMN_RemoveActiveNic(PVOID Context)
+{
+ PDEVMN_NIC_RECORD item, prev;
+ BOOL result = FALSE;
+
+ if (AppDebugMode == TRUE)
+ {
+ QCMTU_Print("-->DEVMN_RemoveActiveNic: Remove MTU client (0x%p)\n", Context);
+ }
+
+ QCMTU_RequestLock();
+
+ item = prev = NicRecHead;
+
+ if (item == NULL)
+ {
+ QCMTU_Print("<--DEVMN_RemoveActiveNic: Error: empty active NIC list (0x%p)\n", Context);
+
+ QCMTU_ReleaseLock();
+
+ return result;
+ }
+
+
+ // if it's head
+ if (item == Context)
+ {
+ if (AppDebugMode == TRUE)
+ {
+ QCMTU_Print("<--DEVMN_RemoveActiveNic-1: Remove MTU client for <%s>\n", prev->FriendlyName);
+ }
+ item = item->Next;
+ NicRecHead = item;
+ if (NicRecHead == NULL) // empty list
+ {
+ NicRecTail = NULL;
+ }
+ DEVMN_ResetNicItem(prev);
+
+ QCMTU_ReleaseLock();
+ return TRUE;
+ }
+
+ item = item->Next;
+ while (item != NULL)
+ {
+ if (item == Context)
+ {
+ if (AppDebugMode == TRUE)
+ {
+ QCMTU_Print("<--DEVMN_RemoveActiveNic-2: Remove MTU client for <%s>\n", item->FriendlyName);
+ }
+ result = TRUE;
+ if (item == NicRecTail)
+ {
+ NicRecTail = prev;
+ }
+ prev->Next = item->Next;
+ DEVMN_ResetNicItem(item);
+ break;
+ }
+ item = item->Next;
+ prev = prev->Next;
+ }
+
+ QCMTU_ReleaseLock();
+
+ if (AppDebugMode == TRUE)
+ {
+ QCMTU_Print("<--DEVMN_RemoveActiveNic: Remove MTU client (0x%p) result=%u\n", Context, result);
+ }
+ return result;
+} // DEVMN_RemoveActiveNic
+
+VOID TerminateAllMtuThreads(VOID)
+{
+ PDEVMN_NIC_RECORD item;
+
+ QCMTU_RequestLock();
+
+ while (item = NicRecHead)
+ {
+ NicRecHead = NicRecHead->Next; // de-queue item
+
+ QCMTU_ReleaseLock();
+
+ QCMTU_Print("TerminateAllMtuThreads: Terminate MTU client for <%s>\n", item->FriendlyName);
+ SetEvent(item->Event[QMICL_EVENT_TERMINATE]);
+ if (item->MtuThreadHandle != INVALID_HANDLE_VALUE)
+ {
+ QCMTU_Print("TerminateAllMtuThreads: Wait for MTU main thread termination...\n");
+ WaitForSingleObject(item->MtuThreadHandle, INFINITE);
+ item->MtuThreadHandle = INVALID_HANDLE_VALUE;
+ QCMTU_Print("TerminateAllMtuThreads: MTU client terminated for thread 0x%x\n", item->MtuThreadHandle);
+ }
+
+ QCMTU_RequestLock();
+
+ DEVMN_ResetNicItem(item);
+ }
+
+ QCMTU_ReleaseLock();
+
+ return;
+} // TerminateAllMtuThreads
+
+VOID DEVMN_ResetNicItem(PDEVMN_NIC_RECORD Item)
+{
+ int i;
+
+ if (AppDebugMode == TRUE)
+ {
+ QCMTU_Print("-->_ResetNicItem: NIC[%d]\n", Item->Index);
+ }
+ Item->Next = NULL;
+ Item->FriendlyName[0] = 0;
+ Item->ControlFileName[0] = 0;
+ Item->InterfaceId[0] = 0;
+ Item->InterfaceName[0] = 0;
+ Item->InUse = FALSE;
+
+ Item->MtuServiceHandle = INVALID_HANDLE_VALUE;
+ Item->MtuThreadHandle = INVALID_HANDLE_VALUE;
+ for (i = 0; i < QMICL_MANAGING_THREAD_COUNT; i++)
+ {
+ Item->ManagedThread[i] = INVALID_HANDLE_VALUE;
+ }
+ for (i = 0; i < QMICL_EVENT_COUNT; i++)
+ {
+ if (Item->Event[i] != NULL)
+ {
+ CloseHandle(Item->Event[i]);
+ Item->Event[i] = NULL;
+ }
+ }
+ if (AppDebugMode == TRUE)
+ {
+ QCMTU_Print("<--_ResetNicItem: NIC[%d]\n", Item->Index);
+ }
+} // DEVMN_ResetNicItem
+
+// This function must be called after GetNicList()
+VOID GetNetInterfaceName(VOID)
+{
+ HKEY hTestKey;
+ char fullKeyName[MAX_KEY_LENGTH];
+ BOOL ret = FALSE;
+ int i;
+
+ // QCMTU_Print("-->GetNetInterfaceName\n");
+
+ for (i = 0; i < DEVMN_NUM_NIC; ++i)
+ {
+ if (MtuNicRecord[i].InterfaceId[0] == 0)
+ {
+ continue;
+ }
+
+ sprintf(fullKeyName, "%s\\%s\\Connection", DEVMON_NET_CONNECTION_REG_KEY, MtuNicRecord[i].InterfaceId);
+
+ if (RegOpenKeyEx
+ (
+ HKEY_LOCAL_MACHINE,
+ fullKeyName,
+ 0,
+ KEY_READ,
+ &hTestKey
+ ) == ERROR_SUCCESS
+ )
+ {
+ TCHAR valueName[MAX_VALUE_NAME];
+ DWORD valueNameLen;
+ DWORD retCode;
+
+ valueName[0] = 0;
+ valueNameLen = MAX_VALUE_NAME;
+ retCode = RegQueryValueEx
+ (
+ hTestKey,
+ "Name",
+ NULL, // reserved
+ NULL, // returned type
+ valueName,
+ &valueNameLen
+ );
+
+ if (retCode == ERROR_SUCCESS)
+ {
+ valueName[valueNameLen] = 0;
+ strcpy(MtuNicRecord[i].InterfaceName, valueName);
+ // QCMTU_Print("GetInterfaceNames: InterfaceName: [%s]\n", valueName);
+ }
+ else
+ {
+ QCMTU_Print("GetInterfaceNames: error for [%s]\n", fullKeyName);
+ }
+
+ RegCloseKey(hTestKey);
+ }
+ } // for
+
+ QCMTU_Print("<--GetNetInterfaceName\n");
+
+ return;
+} // GetNetInterfaceName
diff --git a/src/windows/tools/wwansvc/devmon.h b/src/windows/tools/wwansvc/devmon.h
new file mode 100644
index 0000000..0131d60
--- /dev/null
+++ b/src/windows/tools/wwansvc/devmon.h
@@ -0,0 +1,61 @@
+/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*
+
+ D E V M O N . H
+
+GENERAL DESCRIPTION
+ This file defines constants and function prototypes for device
+ monitoring and NIC management in the Qualcomm MTU configuration service.
+
+ Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ SPDX-License-Identifier: BSD-3-Clause
+
+*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/
+
+#ifndef DEVMON_H
+#define DEVMON_H
+
+#include "qmicl.h"
+
+#define DEVMON_EVENT_REG_CHANGE 0
+#define DEVMON_EVENT_EXIT 1
+#define DEVMON_EVENT_COUNT 2
+
+#define DEVMON_SCAN_INTERVAL 1000
+
+VOID DEVMN_DeviceMonitor(BOOL AppMode);
+
+DWORD WINAPI DEVMN_DevMonThread(PVOID);
+
+VOID GetNicList(VOID);
+
+BOOL QueryNicSwKeys(PTCHAR InstanceKey);
+
+BOOL QueryInstance
+(
+ HKEY hKey,
+ PCHAR FullKeyName
+);
+
+BOOL GetDeviceInstance(PTCHAR InstanceKey);
+
+BOOL GetInstanceNames
+(
+ HKEY hKey,
+ PCHAR EntryName
+);
+
+int SpawnMtuService(VOID);
+
+BOOL IsInActiveList(PCHAR NicFriendlyName);
+
+PDEVMN_NIC_RECORD AddNicToActiveList(int NicIdx);
+
+BOOL DEVMN_RemoveActiveNic(PVOID Context);
+
+VOID TerminateAllMtuThreads(VOID);
+
+VOID DEVMN_ResetNicItem(PDEVMN_NIC_RECORD Item);
+
+VOID GetNetInterfaceName(VOID);
+
+#endif // DEVMON_H
diff --git a/src/windows/tools/wwansvc/main.c b/src/windows/tools/wwansvc/main.c
new file mode 100644
index 0000000..56fe36d
--- /dev/null
+++ b/src/windows/tools/wwansvc/main.c
@@ -0,0 +1,664 @@
+/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*
+
+ M A I N . C
+
+GENERAL DESCRIPTION
+ This file implements the main entry point and Windows service control
+ logic for the Qualcomm MTU configuration service (qcmtusvc).
+
+ Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ SPDX-License-Identifier: BSD-3-Clause
+
+*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/
+
+#include "main.h"
+#include "utils.h"
+#include "devmon.h"
+
+#define HANDLER_EX
+
+#define QCMTU_SERVICE_NAME TEXT("qcmtusvc")
+#define QCMTU_SERVICE_DISPLAY_NAME TEXT("Qualcomm MTU Service")
+
+GUID GUID_QCMTU_NIC = {
+ 0x2c7089aa, 0x2e0e, 0x11d1, {0xb1, 0x14, 0x00, 0xc0, 0x4f, 0xc2, 0xaa, 0xe4}}; // MODEM
+ // 0x4d36e972, 0xe325, 0x11ce, {0xbf, 0xc1, 0x08, 0x00, 0x2b, 0xe1, 0x03, 0x18}}; // NET
+ // 0x86e0d1e0, 0x8089, 0x11d0, 0x9c, 0xe4, 0x08, 0x00, 0x3e, 0x30, 0x1f, 0x73}; // PORTS
+
+HDEVNOTIFY DevNotificationHandle = NULL;
+DEV_BROADCAST_DEVICEINTERFACE NotificationFilter;
+GUID MyDeviceGuid;
+extern HANDLE RegMonExitEvent;
+HANDLE MtuInstance = NULL;
+
+// for app mode
+HINSTANCE AppModuleHandle;
+HWND AppWinHandle;
+WNDCLASSEX AppWndClass;
+TCHAR *AppClassName = TEXT("QCMTU");
+TCHAR *AppTitle = TEXT("QCMTU");
+BOOL AppDebugMode = FALSE;
+
+// for service mode
+SERVICE_STATUS ServiceState;
+SERVICE_STATUS_HANDLE ServiceStateHandle = NULL;
+HANDLE NicLock;
+
+HANDLE QCMTU_InitializeLock(VOID)
+{
+ NicLock = CreateMutex(NULL, FALSE, NULL);
+ return NicLock;
+} // QCMTU_InitializeLock
+
+VOID QCMTU_DeleteLock(VOID)
+{
+ CloseHandle(NicLock);
+} // QCMTU_DeleteLock
+
+VOID QCMTU_RequestLock(VOID)
+{
+ WaitForSingleObject(NicLock, INFINITE);
+} // QCMTU_RequestLock
+
+VOID QCMTU_ReleaseLock(VOID)
+{
+ ReleaseMutex(NicLock);
+} // QCMTU_ReleaseLock
+
+void Usage(char *AppName)
+{
+ char info[1024];
+
+ sprintf(info, "\t========================================================\n");
+ sprintf(info + strlen(info), "\tUsage: %s [install | uninstall | app | /?]\n", AppName);
+ sprintf(info + strlen(info), "\t Options:\n");
+ sprintf(info + strlen(info), "\t install - to install the service\n");
+ sprintf(info + strlen(info), "\t - after installation, start service with \"net start qcmtusvc\"\n");
+ sprintf(info + strlen(info), "\t uninstall - to uninstall the service\n");
+ sprintf(info + strlen(info), "\t app - to run in application/console mode\n");
+ sprintf(info + strlen(info), "\t /? - to print this usage info\n\n");
+ sprintf(info + strlen(info), "\t *IMPORTANT*: run from a Cmd Prompt launched \"As Admin\"\n\n");
+ sprintf(info + strlen(info), "\t Copyright (c) 2013, Qualcomm Inc. All rights reserved.\n");
+ sprintf(info + strlen(info), "\t========================================================\n\n");
+
+ QCMTU_Print("%s", info);
+ printf("%s", info);
+}
+
+int __cdecl main(int argc, LPTSTR argv[])
+{
+ SERVICE_TABLE_ENTRY svcTbl[] =
+ {
+ {QCMTU_SERVICE_NAME, (LPSERVICE_MAIN_FUNCTION)QCMTU_ServiceMain},
+ {NULL, NULL}
+ };
+
+ if (argc > 2)
+ {
+ Usage(argv[0]);
+ return 1;
+ }
+
+ if (argc == 1)
+ {
+ // Run in service mode
+ BOOL ret = StartServiceCtrlDispatcher(svcTbl); // calls DEVMN_DeviceMonitor(FALSE)
+ if (ret == FALSE)
+ {
+ QCMTU_Print("%s start error %u\n", QCMTU_SERVICE_DISPLAY_NAME, GetLastError());
+ QCMTU_Print("To run in console mode, use: %s app\n", argv[0]);
+ QCMTU_Print("To run in service mode, install it and try: net start %s\n", QCMTU_SERVICE_NAME);
+ printf("%s start error %u\n", QCMTU_SERVICE_DISPLAY_NAME, GetLastError());
+ printf("To run in console mode, use: %s app\n", argv[0]);
+ printf("To run in service mode, install it and try: net start %s\n", QCMTU_SERVICE_NAME);
+ Usage(argv[0]);
+ }
+ }
+ else
+ {
+ // may run in service mode
+ if (strstr(argv[1], "/") || strstr(argv[1], "?"))
+ {
+ Usage(argv[0]);
+ return 0;
+ }
+ else if (strcmp(argv[1], "app") == 0)
+ {
+ printf("%s running in application/console mode.\n", QCMTU_SERVICE_DISPLAY_NAME);
+ printf("Press ESC to exit with device disconnected\n");
+ QCMTU_Print("%s running in application/console mode.\n", QCMTU_SERVICE_DISPLAY_NAME);
+ QCMTU_Print("Press ESC to exit with device disconnected\n");
+
+ DEVMN_DeviceMonitor(TRUE);
+ }
+ else if (strcmp(argv[1], "app_debug") == 0)
+ {
+ printf("%s running in application/console DEBUG mode\n", QCMTU_SERVICE_DISPLAY_NAME);
+ printf("Press ESC to exit with device disconnected\n");
+ QCMTU_Print("%s running in application/console DEBUG mode\n", QCMTU_SERVICE_DISPLAY_NAME);
+ QCMTU_Print("Press ESC to exit with device disconnected\n");
+
+ AppDebugMode = TRUE;
+ DEVMN_DeviceMonitor(TRUE);
+ }
+ else if (strcmp(argv[1], "install") == 0)
+ {
+ QCMTU_InstallService();
+ }
+ else if (strcmp(argv[1], "uninstall") == 0)
+ {
+ QCMTU_UninstallService();
+ }
+ else
+ {
+ Usage(argv[0]);
+ return 1;
+ }
+ }
+
+ return 0;
+} // main
+
+VOID QCMTU_InstallService(void)
+{
+ SC_HANDLE serviceControlManager, hService;
+ TCHAR modulePath[_MAX_PATH + 1];
+ DWORD size, rtnSize;
+
+ QCMTU_Print("Installing: %s\n", QCMTU_SERVICE_DISPLAY_NAME);
+
+ serviceControlManager = OpenSCManager(0, 0, SC_MANAGER_CREATE_SERVICE);
+ // serviceControlManager = OpenSCManager(0, 0, SERVICE_ALL_ACCESS);
+
+ size = sizeof(modulePath) / sizeof(modulePath[0]);
+
+ if (serviceControlManager != NULL)
+ {
+ rtnSize = GetModuleFileName(0, modulePath, size);
+ QCMTU_Print("Installing Module Path: <%s>\n", modulePath);
+
+ if ((rtnSize > 0) && (rtnSize < size))
+ {
+ hService = CreateService
+ (
+ serviceControlManager,
+ QCMTU_SERVICE_NAME,
+ QCMTU_SERVICE_DISPLAY_NAME,
+ SERVICE_ALL_ACCESS, // desired access
+ SERVICE_WIN32_OWN_PROCESS, // service type
+ SERVICE_AUTO_START, // SERVICE_DEMAND_START, // start type
+ SERVICE_ERROR_NORMAL, // error control
+ modulePath,
+ 0, // load order group
+ 0, // tag id
+ 0, // dependencies
+ 0, // service start name
+ 0 // password
+ );
+ if (hService != NULL)
+ {
+ CloseServiceHandle(hService);
+ QCMTU_Print("Installed: %s\n", QCMTU_SERVICE_DISPLAY_NAME);
+ }
+ else
+ {
+ QCMTU_Print("CreateService failure: <%s> error %d\n", QCMTU_SERVICE_DISPLAY_NAME, GetLastError());
+ }
+ }
+ else
+ {
+ QCMTU_Print("Error: failed to obtain service path.\n");
+ }
+
+ CloseServiceHandle(serviceControlManager);
+ }
+ else
+ {
+ QCMTU_Print("OpenSCManager failure: <%s> error %d\n", QCMTU_SERVICE_DISPLAY_NAME, GetLastError());
+ }
+} // QCMTU_InstallService
+
+VOID QCMTU_UninstallService(void)
+{
+ SC_HANDLE serviceControlManager, hService;
+ SERVICE_STATUS status;
+
+ QCMTU_Print("Uninstalling: %s\n", QCMTU_SERVICE_DISPLAY_NAME);
+
+ serviceControlManager = OpenSCManager(0, 0, SC_MANAGER_CONNECT);
+
+ if (serviceControlManager != NULL)
+ {
+ hService = OpenService
+ (
+ serviceControlManager,
+ QCMTU_SERVICE_NAME,
+ SERVICE_QUERY_STATUS | DELETE
+ );
+ if (hService != NULL)
+ {
+ if (QueryServiceStatus(hService, &status))
+ {
+ if (status.dwCurrentState == SERVICE_STOPPED)
+ {
+ DeleteService(hService);
+ QCMTU_Print("Uninstalled: %s\n", QCMTU_SERVICE_DISPLAY_NAME);
+ }
+ }
+
+ CloseServiceHandle(hService);
+ }
+
+ CloseServiceHandle(serviceControlManager);
+ }
+} // QCMTU_UninstallService
+
+VOID WINAPI QCMTU_ServiceMain(DWORD Argc, LPTSTR *Argv)
+{
+ QCMTU_Print("Starting: %s\n", QCMTU_SERVICE_DISPLAY_NAME);
+
+ // Init service state
+ ServiceState.dwServiceType = SERVICE_WIN32;
+ ServiceState.dwCurrentState = SERVICE_STOPPED;
+ ServiceState.dwControlsAccepted = 0;
+ ServiceState.dwWin32ExitCode = NO_ERROR;
+ ServiceState.dwServiceSpecificExitCode = NO_ERROR;
+ ServiceState.dwCheckPoint = 0;
+ ServiceState.dwWaitHint = 0;
+
+#ifdef HANDLER_EX
+ ServiceStateHandle = RegisterServiceCtrlHandlerEx
+ (
+ QCMTU_SERVICE_NAME,
+ (LPHANDLER_FUNCTION_EX)QCMTU_ServiceControlHandlerEx,
+ NULL
+ );
+#else
+ ServiceStateHandle = RegisterServiceCtrlHandler
+ (
+ QCMTU_SERVICE_NAME,
+ (LPHANDLER_FUNCTION)QCMTU_ServiceControlHandler
+ );
+#endif // HANDLER_EX
+
+ if (ServiceStateHandle != NULL)
+ {
+ // starting service
+ ServiceState.dwCurrentState = SERVICE_START_PENDING;
+ SetServiceStatus(ServiceStateHandle, &ServiceState);
+
+ // run service
+ ServiceState.dwControlsAccepted |= (SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN);
+ ServiceState.dwCurrentState = SERVICE_RUNNING;
+ SetServiceStatus(ServiceStateHandle, &ServiceState);
+
+ // run in service mode
+ QCMTU_Print("%s running in service mode.\n", QCMTU_SERVICE_DISPLAY_NAME);
+ DEVMN_DeviceMonitor(FALSE);
+ QCMTU_Print("Terminated: %s\n", QCMTU_SERVICE_DISPLAY_NAME);
+
+ // service to be stopped
+ ServiceState.dwCurrentState = SERVICE_STOP_PENDING;
+ SetServiceStatus(ServiceStateHandle, &ServiceState);
+
+ // service stopped
+ ServiceState.dwControlsAccepted &= ~(SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN);
+ ServiceState.dwCurrentState = SERVICE_STOPPED;
+ SetServiceStatus(ServiceStateHandle, &ServiceState);
+ }
+ else
+ {
+ QCMTU_Print("Service failure: %s\n", QCMTU_SERVICE_DISPLAY_NAME);
+ }
+
+ QCMTU_Print("ServiceMain Terminated: %s\n", QCMTU_SERVICE_DISPLAY_NAME);
+
+} // QCMTU_ServiceMain
+
+#ifdef HANDLER_EX
+DWORD WINAPI QCMTU_ServiceControlHandlerEx
+(
+ DWORD ControlCode,
+ DWORD EventType,
+ LPVOID EventData,
+ LPVOID Context
+)
+#else
+VOID WINAPI QCMTU_ServiceControlHandler
+(
+ DWORD ControlCode
+)
+#endif
+{
+ DWORD rtnCode = ERROR_CALL_NOT_IMPLEMENTED;
+
+ QCMTU_Print("%s QCMTU_ServiceControlHandler\n", QCMTU_SERVICE_DISPLAY_NAME);
+
+ switch (ControlCode)
+ {
+ case SERVICE_CONTROL_INTERROGATE:
+ {
+ QCMTU_Print("<%s> SERVICE_CONTROL_INTERROGATE\n", QCMTU_SERVICE_NAME);
+ rtnCode = NO_ERROR;
+ break;
+ }
+ // case SERVICE_CONTROL_PRESHUTDOWN:
+ case SERVICE_CONTROL_SHUTDOWN:
+ {
+ QCMTU_Print("<%s> SERVICE_CONTROL_SHUTDOWN\n", QCMTU_SERVICE_NAME);
+ ServiceState.dwCurrentState = SERVICE_STOP_PENDING;
+ SetServiceStatus(ServiceStateHandle, &ServiceState);
+ SetEvent(RegMonExitEvent);
+#ifdef HANDLER_EX
+ return NO_ERROR;
+#else
+ return;
+#endif
+ }
+ case SERVICE_CONTROL_STOP:
+ {
+ QCMTU_Print("<%s> SERVICE_CONTROL_STOP\n", QCMTU_SERVICE_NAME);
+ ServiceState.dwCurrentState = SERVICE_STOP_PENDING;
+ SetServiceStatus(ServiceStateHandle, &ServiceState);
+ SetEvent(RegMonExitEvent);
+#ifdef HANDLER_EX
+ return NO_ERROR;
+#else
+ return;
+#endif
+ }
+ case SERVICE_CONTROL_PAUSE:
+ {
+ QCMTU_Print("<%s> SERVICE_CONTROL_PAUSE\n", QCMTU_SERVICE_NAME);
+ break;
+ }
+ case SERVICE_CONTROL_CONTINUE:
+ {
+ QCMTU_Print("<%s> SERVICE_CONTROL_CONTINUE\n", QCMTU_SERVICE_NAME);
+ break;
+ }
+ case SERVICE_CONTROL_POWEREVENT:
+ {
+ QCMTU_Print("<%s> SERVICE_CONTROL_POWEREVENT\n", QCMTU_SERVICE_NAME);
+ break;
+ }
+#ifdef HANDLER_EX
+ case SERVICE_CONTROL_DEVICEEVENT:
+ {
+ switch (EventType)
+ {
+ case DBT_DEVICEARRIVAL:
+ {
+ QCMTU_Print("<%s> SERVICE_CONTROL_DEVICEEVENT/DBT_DEVICEARRIVAL\n", QCMTU_SERVICE_NAME);
+ break;
+ }
+ case DBT_DEVICEREMOVECOMPLETE:
+ {
+ QCMTU_Print("<%s> SERVICE_CONTROL_DEVICEEVENT/DBT_DEVICEREMOVECOMPLETE\n", QCMTU_SERVICE_NAME);
+ break;
+ }
+ case DBT_DEVICEQUERYREMOVE:
+ {
+ QCMTU_Print("<%s> SERVICE_CONTROL_DEVICEEVENT/DBT_DEVICEQUERYREMOVE\n", QCMTU_SERVICE_NAME);
+ break;
+ }
+ case DBT_DEVICEQUERYREMOVEFAILED:
+ {
+ QCMTU_Print("<%s> SERVICE_CONTROL_DEVICEEVENT/DBT_DEVICEQUERYREMOVEFAILED\n", QCMTU_SERVICE_NAME);
+ break;
+ }
+ case DBT_DEVICEREMOVEPENDING:
+ {
+ QCMTU_Print("<%s> SERVICE_CONTROL_DEVICEEVENT/DBT_DEVICEREMOVEPENDING\n", QCMTU_SERVICE_NAME);
+ break;
+ }
+ case DBT_CUSTOMEVENT:
+ {
+ QCMTU_Print("<%s> SERVICE_CONTROL_DEVICEEVENT/DBT_CUSTOMEVENT\n", QCMTU_SERVICE_NAME);
+ break;
+ }
+ default:
+ {
+ QCMTU_Print("<%s> SERVICE_CONTROL_DEVICEEVENT/default\n", QCMTU_SERVICE_NAME);
+ break;
+ }
+ }
+ }
+#endif // HANDLER_EX
+ default:
+ {
+ QCMTU_Print("<%s> Control code not supported 0x%x\n",
+ QCMTU_SERVICE_NAME, ControlCode);
+
+ if ((ControlCode >= 128) && (ControlCode <= 255))
+ // user defined control code
+ break;
+ else
+ // unrecognised control code
+ break;
+ }
+ }
+
+ SetServiceStatus(ServiceStateHandle, &ServiceState);
+
+#ifdef HANDLER_EX
+ return rtnCode;
+#else
+ return;
+#endif
+} // QCMTU_ServiceControlHandler
+
+LRESULT MyWindowProc
+(
+ HWND Wnd,
+ UINT Message,
+ WPARAM WParam,
+ LPARAM LParam
+)
+{
+ switch (Message)
+ {
+ case WM_CREATE:
+ case WM_INITDIALOG:
+ {
+ QCMTU_Print("<%s> WM_CREAT/WM_INITDIALOG\n", AppClassName);
+
+ MyDeviceGuid = GUID_QCMTU_NIC;
+
+ // register device notification
+ ZeroMemory(&NotificationFilter, sizeof(NotificationFilter));
+ NotificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
+ NotificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
+ // NotificationFilter.dbcc_classguid = GUID_QCMTU_NIC; // MyDeviceGuid;
+ memcpy(&NotificationFilter.dbcc_classguid, &GUID_QCMTU_NIC, sizeof(GUID));
+
+ DevNotificationHandle = RegisterDeviceNotification
+ (
+ Wnd,
+ &NotificationFilter,
+ 0 // DEVICE_NOTIFY_WINDOW_HANDLE
+ );
+ if (DevNotificationHandle == NULL)
+ {
+ QCMTU_Print("MyWindowProc: register device notification, error %d\n", GetLastError());
+ SetEvent(RegMonExitEvent);
+ }
+ else
+ {
+ return TRUE;
+ }
+
+ break;
+ }
+ case WM_DEVICECHANGE:
+ {
+ switch (WParam)
+ {
+ QCMTU_Print("<%s> WM_DEVICECHANGE\n", AppClassName);
+
+ case DBT_DEVICEARRIVAL:
+ {
+ QCMTU_Print("<%s> WM_DEVICECHANGE/DBT_DEVICEARRIVAL\n", AppClassName);
+ break;
+ }
+ case DBT_DEVICEREMOVECOMPLETE:
+ {
+ QCMTU_Print("<%s> WM_DEVICECHANGE/DBT_DEVICEREMOVECOMPLETE\n", AppClassName);
+ break;
+ }
+ case DBT_DEVICEQUERYREMOVE:
+ {
+ QCMTU_Print("<%s> WM_DEVICECHANGE/DBT_DEVICEQUERYREMOVE\n", AppClassName);
+ break;
+ }
+ case DBT_DEVICEQUERYREMOVEFAILED:
+ {
+ QCMTU_Print("<%s> WM_DEVICECHANGE/DBT_DEVICEQUERYREMOVEFAILED\n", AppClassName);
+ break;
+ }
+ case DBT_DEVICEREMOVEPENDING:
+ {
+ QCMTU_Print("<%s> WM_DEVICECHANGE/DBT_DEVICEREMOVEPENDING\n", AppClassName);
+ break;
+ }
+ case DBT_CUSTOMEVENT:
+ {
+ QCMTU_Print("<%s> WM_DEVICECHANGE/DBT_CUSTOMEVENT\n", AppClassName);
+ break;
+ }
+ default:
+ {
+ QCMTU_Print("<%s> WM_DEVICECHANGE/default\n", AppClassName);
+ break;
+ }
+ }
+ return 0;
+ }
+ case WM_POWERBROADCAST:
+ {
+ QCMTU_Print("<%s> WM_POWERBROADCAST\n", AppClassName);
+ return 0;
+ }
+ case WM_CLOSE:
+ {
+ QCMTU_Print("<%s> WM_CLOSE\n", AppClassName);
+
+ if (DevNotificationHandle != NULL)
+ {
+ UnregisterDeviceNotification(DevNotificationHandle);
+ }
+ return DefWindowProc(Wnd, Message, WParam, LParam);
+ }
+ case WM_DESTROY:
+ {
+ QCMTU_Print("<%s> WM_DESTROY\n", AppClassName);
+
+ PostQuitMessage(0);
+ return 0;
+ }
+ }
+ return DefWindowProc(Wnd, Message, WParam, LParam);
+} // MyWindowProc
+
+BOOL QCMTU_SetMTU(PQC_MTU Mtu, PCHAR InterfaceName)
+{
+ CHAR cmdLine[256];
+ PCHAR ipver;
+ PROCESS_INFORMATION processInfo;
+ STARTUPINFO startupInfo;
+ BOOL rtnVal;
+ DWORD exitCode;
+
+ if (Mtu->IpVer == 4)
+ {
+ ipver = "ipv4";
+ }
+ else if (Mtu->IpVer == 6)
+ {
+ ipver = "ipv6";
+ }
+ else
+ {
+ QCMTU_Print("QCMTU_SetMTU: error: invalid IP version\n");
+ return FALSE;
+ }
+
+ // minimum MTU value required by Microsoft Windows
+ if (Mtu->MTU < QCMTU_MIN_MTU)
+ {
+ Mtu->MTU = QCMTU_MIN_MTU;
+ }
+
+ sprintf(cmdLine, "C:\\Windows\\system32\\netsh.exe interface %s set interface interface=\"%s\" mtu=%u",
+ ipver, InterfaceName, Mtu->MTU);
+
+ QCMTU_Print("QCMTU_SetMTU: calling <%s>\n", cmdLine);
+
+ // set MTU
+ memset(&processInfo, 0, sizeof(processInfo));
+ memset(&startupInfo, 0, sizeof(startupInfo));
+ startupInfo.cb = sizeof(startupInfo);
+
+ rtnVal = CreateProcess
+ (
+ NULL, // L"C:\\Windows\\netsh.exe",
+ cmdLine,
+ NULL,
+ NULL,
+ FALSE,
+ NORMAL_PRIORITY_CLASS,
+ NULL,
+ NULL,
+ &startupInfo,
+ &processInfo
+ );
+
+ if (rtnVal == FALSE)
+ {
+ QCMTU_Print("QCMTU_SetMTU: CreateProcess failure\n");
+ }
+
+ WaitForSingleObject(processInfo.hProcess, INFINITE);
+ GetExitCodeProcess(processInfo.hProcess, &exitCode);
+ CloseHandle(processInfo.hProcess);
+ CloseHandle(processInfo.hThread);
+
+ QCMTU_Print("QCMTU_SetMTU: exit 0x%X\n", exitCode);
+
+ return rtnVal;
+
+} // QCMTU_SetMTU
+
+BOOLEAN QCMTU_RunSingleInstance(VOID)
+{
+ DWORD errNo;
+
+ MtuInstance = CreateEvent(NULL, FALSE, FALSE, "Global\\QualcommMTUSvc2013");
+ errNo = GetLastError();
+
+ if (MtuInstance == NULL)
+ {
+ QCMTU_Print("CreateEvent error [%d]\n", errNo);
+ return FALSE;
+ }
+ else if (errNo == ERROR_ALREADY_EXISTS)
+ {
+ QCMTU_Print("Error: Another instance of the service is running\n");
+ CloseHandle(MtuInstance);
+ MtuInstance = NULL;
+ return FALSE;
+ }
+
+ QCMTU_Print("MTU instance running...\n");
+
+ return TRUE;
+} // QCMTU_RunSingleInstance
+
+VOID QCMTU_CloseRunningInstance(VOID)
+{
+ if (MtuInstance != NULL)
+ {
+ CloseHandle(MtuInstance);
+ MtuInstance = NULL;
+ }
+} // QCMTU_CloseRunningInstance
diff --git a/src/windows/tools/wwansvc/main.h b/src/windows/tools/wwansvc/main.h
new file mode 100644
index 0000000..554819f
--- /dev/null
+++ b/src/windows/tools/wwansvc/main.h
@@ -0,0 +1,124 @@
+/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*
+
+ M A I N . H
+
+GENERAL DESCRIPTION
+ This file defines data structures, constants, and function prototypes
+ for the Qualcomm MTU configuration service (qcmtusvc).
+
+ Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ SPDX-License-Identifier: BSD-3-Clause
+
+*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/
+
+#ifndef MAIN_H
+#define MAIN_H
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include "api.h"
+
+// #include
+// DEFINE_GUID(GUID_QCMTU_NIC, 0x2c7089aa, 0x2e0e, 0x11d1, 0xb1, 0x14, 0x00, 0xc0, 0x4f, 0xc2, 0xaa, 0xe4);
+// DEFINE_GUID(GUID_QCMTU_NIC, 0x86e0d1e0L, 0x8089, 0x11d0, 0x9c, 0xe4, 0x08, 0x00, 0x3e, 0x30, 0x1f, 0x73);
+
+extern GUID GUID_QCMTU_NIC;
+
+extern HANDLE MtuInstance;
+extern GUID MyDeviceGuid;
+extern HINSTANCE AppModuleHandle;
+extern HWND AppWinHandle;
+extern WNDCLASSEX AppWndClass;
+extern TCHAR *AppClassName;
+extern TCHAR *AppTitle;
+extern DEV_BROADCAST_DEVICEINTERFACE NotificationFilter;
+extern SERVICE_STATUS_HANDLE ServiceStateHandle;
+extern HDEVNOTIFY DevNotificationHandle;
+extern BOOL AppDebugMode;
+
+#define DEVMN_NUM_NIC 1600
+#define QCMTU_MIN_MTU 576
+#define QCMTU_MAX_MTU (64*1024)
+
+#define QMICL_RM_NOTIFY_THREAD_IDX 0
+#define QMICL_V4_MTU_NOTIFY_THREAD_IDX 1
+#define QMICL_V6_MTU_NOTIFY_THREAD_IDX 2
+#define QMICL_MANAGING_THREAD_COUNT 3
+
+#define QMICL_EVENT_START 0
+#define QMICL_EVENT_STOP 1
+#define QMICL_EVENT_TERMINATE 2
+#define QMICL_EVENT_IPV4_MTU 3
+#define QMICL_EVENT_IPV6_MTU 4
+#define QMICL_EVENT_COUNT 5
+
+typedef struct _QC_MTU
+{
+ UCHAR IpVer;
+ ULONG MTU;
+} QC_MTU, *PQC_MTU;
+
+typedef struct _DEVMN_NIC_RECORD
+{
+ struct _DEVMN_NIC_RECORD *Next;
+
+ // NIC info
+ CHAR FriendlyName[256];
+ CHAR ControlFileName[256];
+ CHAR InterfaceId[60]; // NIC GUID
+ CHAR InterfaceName[256];
+
+ // thread context
+ HANDLE MtuServiceHandle; // control file handle
+ HANDLE MtuThreadHandle; // main thread handle
+ HANDLE ManagedThread[QMICL_MANAGING_THREAD_COUNT]; // notification thread handles
+ HANDLE Event[QMICL_EVENT_COUNT];
+ OVERLAPPED Overlapped; // structure for read
+
+ int Index;
+ BOOL InUse;
+} DEVMN_NIC_RECORD, *PDEVMN_NIC_RECORD;
+
+// Function Prototypes
+
+HANDLE QCMTU_InitializeLock(VOID);
+VOID QCMTU_DeleteLock(VOID);
+VOID QCMTU_RequestLock(VOID);
+VOID QCMTU_ReleaseLock(VOID);
+
+VOID QCMTU_InstallService(void);
+VOID QCMTU_UninstallService(void);
+VOID WINAPI QCMTU_ServiceMain(DWORD Argc, LPTSTR *Argv);
+DWORD WINAPI QCMTU_ServiceControlHandlerEx
+(
+ DWORD ControlCode,
+ DWORD EventType,
+ LPVOID EventData,
+ LPVOID Context
+);
+VOID WINAPI QCMTU_ServiceControlHandler
+(
+ DWORD ControlCode
+);
+
+LRESULT MyWindowProc
+(
+ HWND Wnd,
+ UINT Message,
+ WPARAM WParam,
+ LPARAM LParam
+);
+
+BOOL QCMTU_SetMTU(PQC_MTU Mtu, PCHAR InterfaceName);
+
+BOOLEAN QCMTU_RunSingleInstance(VOID);
+
+VOID QCMTU_CloseRunningInstance(VOID);
+
+#endif // MAIN_H
diff --git a/src/windows/tools/wwansvc/qcmtusvc.sln b/src/windows/tools/wwansvc/qcmtusvc.sln
new file mode 100644
index 0000000..aa75fc0
--- /dev/null
+++ b/src/windows/tools/wwansvc/qcmtusvc.sln
@@ -0,0 +1,19 @@
+Microsoft Visual Studio Solution File, Format Version 11.00
+# Visual Studio 2010
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qcmtusvc", "qcmtusvc.vcxproj", "{7D888E32-457F-4E3F-A294-E9B7015AD7D0}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Release|Win32 = Release|Win32
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {7D888E32-457F-4E3F-A294-E9B7015AD7D0}.Debug|Win32.ActiveCfg = Debug|Win32
+ {7D888E32-457F-4E3F-A294-E9B7015AD7D0}.Debug|Win32.Build.0 = Debug|Win32
+ {7D888E32-457F-4E3F-A294-E9B7015AD7D0}.Release|Win32.ActiveCfg = Release|Win32
+ {7D888E32-457F-4E3F-A294-E9B7015AD7D0}.Release|Win32.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/src/windows/tools/wwansvc/qcmtusvc.vcxproj b/src/windows/tools/wwansvc/qcmtusvc.vcxproj
new file mode 100644
index 0000000..091508f
--- /dev/null
+++ b/src/windows/tools/wwansvc/qcmtusvc.vcxproj
@@ -0,0 +1,89 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+
+ {7D888E32-457F-4E3F-A294-E9B7015AD7D0}
+ Win32Proj
+
+
+
+ MultiByte
+ v142
+
+
+ v142
+ MultiByte
+ Application
+
+
+
+
+
+
+
+
+
+ <_ProjectFileVersion>10.0.30319.1
+ false
+
+
+
+ WIN32;_DEBUG;_CONSOLE;QC_SERVICE;%(PreprocessorDefinitions)
+ Async
+ MultiThreadedDebug
+ Level3
+
+
+ Setupapi.lib;Version.lib;libcmtd.lib;Advapi32.lib;ws2_32.lib;user32.lib;gdi32.lib;%(AdditionalDependencies)
+ Console
+ MachineX86
+ true
+
+
+
+
+ WIN32;NDEBUG;_CONSOLE;QC_SERVICE;%(PreprocessorDefinitions)
+ Async
+ MultiThreaded
+ Level2
+
+
+ Setupapi.lib;Version.lib;libcmt.lib;Advapi32.lib;ws2_32.lib;user32.lib;gdi32.lib;%(AdditionalDependencies)
+ Console
+ true
+ true
+ MachineX86
+ DebugFull
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/windows/tools/wwansvc/qmicl.c b/src/windows/tools/wwansvc/qmicl.c
new file mode 100644
index 0000000..0b51a42
--- /dev/null
+++ b/src/windows/tools/wwansvc/qmicl.c
@@ -0,0 +1,690 @@
+/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*
+
+ Q M I C L . C
+
+GENERAL DESCRIPTION
+ This file implements the QMI client logic for MTU notification
+ handling in the Qualcomm MTU configuration service.
+
+ Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ SPDX-License-Identifier: BSD-3-Clause
+
+*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/
+
+#include
+#include "main.h"
+#include "qmicl.h"
+#include "api.h"
+#include "utils.h"
+#include "reg.h"
+#include "devmon.h"
+
+// define invalid MTU value which is greater thann QCMTU_MAX_MTU
+#define QCDEV_INVALID_MTU 0x80000000
+
+VOID QMICL_SetDefaultMTU(PDEVMN_NIC_RECORD NicInfo)
+{
+ QC_MTU mtuInfo;
+
+ QCMTU_Print("-->QMICL_SetDefaultMTU for <%s>\n", NicInfo->FriendlyName);
+
+ mtuInfo.MTU = 1500;
+
+ mtuInfo.IpVer = 4;
+ QCMTU_SetMTU(&mtuInfo, NicInfo->InterfaceName);
+
+ mtuInfo.IpVer = 6;
+ QCMTU_SetMTU(&mtuInfo, NicInfo->InterfaceName);
+
+ QCMTU_Print("<--QMICL_SetDefaultMTU for <%s>\n", NicInfo->FriendlyName);
+} // QMICL_SetDefaultMTU
+
+BOOL QMICL_StartMTUService(PVOID NicInfo)
+{
+ PDEVMN_NIC_RECORD activeNic = (PDEVMN_NIC_RECORD)NicInfo;
+ HANDLE hServiceDevice = INVALID_HANDLE_VALUE;
+ char controlFileName[SERVICE_FILE_BUF_LEN];
+
+ if (AppDebugMode == TRUE)
+ {
+ QCMTU_Print("-->QMICL_StartMTUService: Client[%d] 0x%p <%s>\n", activeNic->Index, activeNic, activeNic->FriendlyName);
+ }
+
+ if (CreateEventsForMtuThread(activeNic) == FALSE)
+ {
+ QCMTU_Print("Error: events creation failure for <%d>\n", activeNic->FriendlyName);
+ return FALSE;
+ }
+
+ sprintf(controlFileName, "\\\\.\\%s", activeNic->ControlFileName);
+
+ hServiceDevice = CreateFile
+ (
+ controlFileName,
+ GENERIC_WRITE | GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL,
+ OPEN_EXISTING,
+ (FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED),
+ NULL
+ );
+
+ if (hServiceDevice == INVALID_HANDLE_VALUE)
+ {
+ CloseEventsForMtuThread(activeNic);
+ if (AppDebugMode == TRUE)
+ {
+ QCMTU_Print("<--QMICL_StartMTUService: NIC access failure <%s>\n", activeNic->FriendlyName);
+ }
+ return FALSE;
+ }
+ QCMTU_Print("QMICL_StartMTUService: control handle 0x%x\n", hServiceDevice);
+
+ activeNic->MtuServiceHandle = hServiceDevice;
+
+ if (StartMtuThread(activeNic) == INVALID_HANDLE_VALUE)
+ {
+ CloseEventsForMtuThread(activeNic);
+ QCMTU_Print("<--QMICL_StartMTUService: cannot create NIC thread\n");
+ return FALSE;
+ }
+
+ if (AppDebugMode == TRUE)
+ {
+ QCMTU_Print("<--QMICL_StartMTUService: Client[%d] 0x%p <%s>\n", activeNic->Index, activeNic, activeNic->FriendlyName);
+ }
+
+ return TRUE;
+} // QMICL_StartMTUService
+
+VOID MyNotificationCallback
+(
+ HANDLE ServiceHandle,
+ ULONG Mask,
+ PDEVMN_NIC_RECORD Context
+)
+{
+ QCMTU_Print("MyNotificationCallback: Client 0x%p mask 0x%x\n", Context, Mask);
+ // if (Mask & 0x00000001)
+ {
+ SetEvent(Context->Event[QMICL_EVENT_TERMINATE]);
+ }
+} // MyNotificationCallback
+
+HANDLE WINAPI StartMtuThread(PVOID NicInfo)
+{
+ PDEVMN_NIC_RECORD context = (PDEVMN_NIC_RECORD)NicInfo;
+ HANDLE hNotificationThreadHandle = 0;
+ HANDLE hV4MtuNotificationThreadHandle = 0;
+ HANDLE hV6MtuNotificationThreadHandle = 0;
+ HANDLE hMainThreadHandle = 0;
+ DWORD tid1;
+
+ // Before register MTU notification, set default MTU
+ QMICL_SetDefaultMTU(NicInfo);
+
+ // Register MTU notifications
+ QCMTU_Print("Start V4 MTU notification thread with handle 0x%x\n", context->MtuServiceHandle);
+ hV4MtuNotificationThreadHandle = QMICL_RegisterMtuNotification
+ (
+ context->MtuServiceHandle,
+ 4,
+ (NOTIFICATION_CALLBACK)MyIPV4MtuNotificationCallback,
+ context
+ );
+ if (hV4MtuNotificationThreadHandle == INVALID_HANDLE_VALUE)
+ {
+ QCMTU_Print("Unable to register IPV4 MTU notification, error %d\n", GetLastError());
+ }
+ else
+ {
+ QCMTU_Print("V4 MTU notification thread handle 0x%x\n", hV4MtuNotificationThreadHandle);
+ }
+
+ QCMTU_Print("Start V6 MTU notification thread with handle 0x%x\n", context->MtuServiceHandle);
+ hV6MtuNotificationThreadHandle = QMICL_RegisterMtuNotification
+ (
+ context->MtuServiceHandle,
+ 6,
+ (NOTIFICATION_CALLBACK)MyIPV6MtuNotificationCallback,
+ context
+ );
+ if (hV6MtuNotificationThreadHandle == INVALID_HANDLE_VALUE)
+ {
+ QCMTU_Print("Unable to register IPV6 MTU notification, error %d\n", GetLastError());
+ }
+ else
+ {
+ QCMTU_Print("V6 MTU notification thread handle 0x%x\n", hV6MtuNotificationThreadHandle);
+ }
+
+ // --- Last, we register the removal notification
+ QCMTU_Print("Start removal notification thread with handle 0x%x\n", context->MtuServiceHandle);
+
+ hNotificationThreadHandle = QCWWAN_RegisterDeviceNotification
+ (
+ context->MtuServiceHandle,
+ (NOTIFICATION_CALLBACK)MyNotificationCallback,
+ context
+ );
+ if (hNotificationThreadHandle == INVALID_HANDLE_VALUE)
+ {
+ QCMTU_Print("Unable to register notification, error %d\n", GetLastError());
+ // return INVALID_HANDLE_VALUE;
+ }
+ else
+ {
+ QCMTU_Print("Removal notification thread handle 0x%x\n", hNotificationThreadHandle);
+ }
+
+ // --- start MTU main thread
+ QCMTU_Print("Start main thread for <%s>\n", context->FriendlyName);
+ hMainThreadHandle = CreateThread
+ (
+ NULL,
+ 0,
+ QMICL_MainThread,
+ (LPVOID)context,
+ 0,
+ &tid1
+ );
+ if (hMainThreadHandle == INVALID_HANDLE_VALUE)
+ {
+ QCMTU_Print("Unable to launch MTU thread, error %d\n", GetLastError());
+ CloseHandle(hNotificationThreadHandle);
+ if (hV4MtuNotificationThreadHandle != INVALID_HANDLE_VALUE)
+ {
+ CloseHandle(hV4MtuNotificationThreadHandle);
+ }
+ if (hV6MtuNotificationThreadHandle != INVALID_HANDLE_VALUE)
+ {
+ CloseHandle(hV6MtuNotificationThreadHandle);
+ }
+ }
+ else
+ {
+ context->ManagedThread[QMICL_RM_NOTIFY_THREAD_IDX] = hNotificationThreadHandle;
+ context->ManagedThread[QMICL_V4_MTU_NOTIFY_THREAD_IDX] = hV4MtuNotificationThreadHandle;
+ context->ManagedThread[QMICL_V6_MTU_NOTIFY_THREAD_IDX] = hV6MtuNotificationThreadHandle;
+ context->MtuThreadHandle = hMainThreadHandle;
+ }
+
+ return hMainThreadHandle;
+
+} // StartMtuThread
+
+// MTU Main Thread
+DWORD WINAPI QMICL_MainThread(LPVOID Context)
+{
+ PDEVMN_NIC_RECORD context;
+ int cnt = 0;
+ DWORD dwStatus = NO_ERROR;
+ BOOL stopped = FALSE, terminated = FALSE;
+ DWORD waitIdx, waitTime = 1000;
+ HANDLE hMtuNotificationThreadHandle = 0;
+
+ context = (PDEVMN_NIC_RECORD)Context;
+
+ while (terminated == FALSE)
+ {
+ // wait for events
+ waitIdx = WaitForMultipleObjects
+ (
+ QMICL_EVENT_COUNT,
+ context->Event,
+ FALSE,
+ waitTime
+ );
+
+ if (waitIdx != WAIT_TIMEOUT)
+ {
+ waitIdx -= WAIT_OBJECT_0;
+ }
+
+ // process events
+ switch (waitIdx)
+ {
+ case QMICL_EVENT_START:
+ {
+ QCMTU_Print("<%u> QMICL_EVENT_START\n", context->Index);
+ ResetEvent(context->Event[QMICL_EVENT_START]);
+ stopped = FALSE;
+
+ // TODO: register notifications
+
+ break;
+ }
+ case QMICL_EVENT_STOP:
+ {
+ QCMTU_Print("<%u> QMICL_EVENT_STOP\n", context->Index);
+ ResetEvent(context->Event[QMICL_EVENT_STOP]);
+ stopped = TRUE;
+
+#if(_WIN32_WINNT >= 0x0600)
+ CancelIoEx(context->MtuServiceHandle, NULL);
+#else
+ CancelIo(context->MtuServiceHandle);
+#endif
+
+ break;
+ }
+ case QMICL_EVENT_TERMINATE:
+ {
+ DWORD timeoutVal;
+
+ QCMTU_Print("QMICL_EVENT_TERMINATE\n");
+
+ ResetEvent(context->Event[QMICL_EVENT_TERMINATE]);
+
+ stopped = TRUE;
+ terminated = TRUE;
+
+ /***
+ #if(_WIN32_WINNT >= 0x0600)
+ CancelIoEx(context->MtuServiceHandle, NULL);
+ #else
+ CancelIo(context->MtuServiceHandle);
+ #endif
+ ***/
+
+ // close control file
+ CloseHandle(context->MtuServiceHandle);
+
+ // Wait for all managed threads to terminate
+ QCMTU_Print("QMICL_EVENT_TERMINATE: Wait for all managed threads\n");
+ timeoutVal = 5000; // 5 seconds
+ WaitForMultipleObjects
+ (
+ QMICL_MANAGING_THREAD_COUNT,
+ context->ManagedThread,
+ TRUE,
+ INFINITE // timeoutVal
+ );
+ QCMTU_Print("QMICL_EVENT_TERMINATE: all managed threads exit\n");
+
+ // close thread handle
+ if (context->ManagedThread[QMICL_RM_NOTIFY_THREAD_IDX] != INVALID_HANDLE_VALUE)
+ {
+ CloseHandle(context->ManagedThread[QMICL_RM_NOTIFY_THREAD_IDX]);
+ }
+ if (context->ManagedThread[QMICL_V4_MTU_NOTIFY_THREAD_IDX] != INVALID_HANDLE_VALUE)
+ {
+ CloseHandle(context->ManagedThread[QMICL_V4_MTU_NOTIFY_THREAD_IDX]);
+ }
+ if (context->ManagedThread[QMICL_V6_MTU_NOTIFY_THREAD_IDX] != INVALID_HANDLE_VALUE)
+ {
+ CloseHandle(context->ManagedThread[QMICL_V6_MTU_NOTIFY_THREAD_IDX]);
+ }
+
+ DEVMN_RemoveActiveNic(context);
+
+ break;
+ }
+ case QMICL_EVENT_IPV4_MTU:
+ {
+ QCMTU_Print("QMICL_EVENT_IPV4_MTU for <%s>\n", context->InterfaceName);
+ ResetEvent(context->Event[QMICL_EVENT_IPV4_MTU]);
+
+ if ((terminated == TRUE) || (stopped == TRUE))
+ {
+ break;
+ }
+ else
+ {
+ // register for MTU notification again
+ HANDLE tHandle;
+
+ tHandle = QMICL_RegisterMtuNotification
+ (
+ context->MtuServiceHandle,
+ 4,
+ (NOTIFICATION_CALLBACK)MyIPV4MtuNotificationCallback,
+ context
+ );
+ if (tHandle == INVALID_HANDLE_VALUE)
+ {
+ QCMTU_Print("QMICL_EVENT_IPV4_MTU: Unable to register IPV4 MTU notification, error %d\n", GetLastError());
+ }
+ context->ManagedThread[QMICL_V4_MTU_NOTIFY_THREAD_IDX] = tHandle;
+ }
+
+ break;
+ }
+
+ case QMICL_EVENT_IPV6_MTU:
+ {
+ QCMTU_Print("QMICL_EVENT_IPV6_MTU for <%s>\n", context->InterfaceName);
+ ResetEvent(context->Event[QMICL_EVENT_IPV6_MTU]);
+
+ if ((terminated == TRUE) || (stopped == TRUE))
+ {
+ break;
+ }
+ else
+ {
+ // register for MTU notification again
+ HANDLE tHandle;
+
+ tHandle = QMICL_RegisterMtuNotification
+ (
+ context->MtuServiceHandle,
+ 6,
+ (NOTIFICATION_CALLBACK)MyIPV6MtuNotificationCallback,
+ context
+ );
+ if (tHandle == INVALID_HANDLE_VALUE)
+ {
+ QCMTU_Print("QMICL_EVENT_IPV6_MTU: Unable to register IPV6 MTU notification, error %d\n", GetLastError());
+ }
+ context->ManagedThread[QMICL_V6_MTU_NOTIFY_THREAD_IDX] = tHandle;
+ }
+
+ break;
+ }
+
+ case WAIT_TIMEOUT:
+ {
+ // QCMTU_Print("<%u> WAIT_TIMEOUT\n", context->Index);
+ break;
+ }
+ default:
+ {
+ QCMTU_Print("<%u> UNKNOWN EVENT - %u\n", context->Index, waitIdx);
+ break;
+ }
+ } // switch
+ } // while (terminated == FALSE)
+
+ QCWWAN_CloseService(context->MtuServiceHandle);
+ CloseEventsForMtuThread(context);
+
+ QCMTU_Print("QMICL thread exits <%s>\n", context->FriendlyName);
+
+ return 0;
+} // QMICL_MainThread
+
+BOOL CreateEventsForMtuThread(PDEVMN_NIC_RECORD Context)
+{
+ int i;
+ BOOL result = TRUE;
+
+ if (AppDebugMode == TRUE)
+ {
+ QCMTU_Print("--><%u> CreateEventsForMtuThread\n", Context->Index);
+ }
+
+ QCMTU_RequestLock();
+
+ for (i = QMICL_EVENT_START; i < QMICL_EVENT_COUNT; ++i)
+ {
+ Context->Event[i] = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (Context->Event[i] == NULL)
+ {
+ result = FALSE;
+ break;
+ }
+ ResetEvent(Context->Event[i]);
+ }
+
+ if (result == TRUE)
+ {
+ Context->InUse = TRUE;
+ QCMTU_ReleaseLock();
+
+ if (AppDebugMode == TRUE)
+ {
+ QCMTU_Print("<--<%u> CreateEventsForMtuThread: TRUE (%d)\n", Context->Index, i);
+ }
+
+ return result;
+ }
+
+ // failed, clean up
+ CloseEventsForMtuThread(Context);
+
+ QCMTU_ReleaseLock();
+
+ if (AppDebugMode == TRUE)
+ {
+ QCMTU_Print("<--<%u> CreateEventsForMtuThread: failure\n", Context->Index);
+ }
+ return result;
+} // CreateEventsForMtuThread
+
+VOID CloseEventsForMtuThread(PDEVMN_NIC_RECORD Context)
+{
+ int i;
+
+ QCMTU_RequestLock();
+
+ for (i = QMICL_EVENT_START; i < QMICL_EVENT_COUNT; ++i)
+ {
+ if (Context->Event[i] != NULL)
+ {
+ CloseHandle(Context->Event[i]);
+ Context->Event[i] = NULL;
+ }
+ }
+
+ Context->InUse = FALSE;
+
+ QCMTU_ReleaseLock();
+
+} // CloseEventsForMtuThread
+
+VOID MyIPV4MtuNotificationCallback
+(
+ HANDLE ServiceHandle,
+ ULONG MtuSize,
+ PDEVMN_NIC_RECORD Context
+)
+{
+ QC_MTU mtuInfo;
+
+ mtuInfo.IpVer = 4;
+ mtuInfo.MTU = (USHORT)MtuSize;
+
+ QCMTU_Print("MyIPV4MtuNotificationCallback: IPV4 MTU 0x%x(%d) for 0x%p <%s>\n", MtuSize, mtuInfo.MTU, Context, Context->InterfaceName);
+
+ // set MTU, QCDEV_INVALID_MTU > QCMTU_MAX_MTU
+ if ((MtuSize != 0) && (MtuSize < QCMTU_MAX_MTU))
+ {
+ QCMTU_SetMTU(&mtuInfo, Context->InterfaceName);
+ }
+
+ if (MtuSize < QCMTU_MAX_MTU)
+ {
+ SetEvent(Context->Event[QMICL_EVENT_IPV4_MTU]);
+ }
+
+} // MyIPV4NotificationCallback
+
+VOID MyIPV6MtuNotificationCallback
+(
+ HANDLE ServiceHandle,
+ ULONG MtuSize,
+ PDEVMN_NIC_RECORD Context
+)
+{
+ QC_MTU mtuInfo;
+
+ mtuInfo.IpVer = 6;
+ mtuInfo.MTU = (USHORT)MtuSize;
+
+ QCMTU_Print("MyIPV6MtuNotificationCallback: IPV6 MTU 0x%x(%d) for 0x%p <%s>\n", MtuSize, mtuInfo.MTU, Context, Context->InterfaceName);
+
+ // set MTU, QCDEV_INVALID_MTU > QCMTU_MAX_MTU
+ if ((MtuSize != 0) && (MtuSize < QCMTU_MAX_MTU))
+ {
+ QCMTU_SetMTU(&mtuInfo, Context->InterfaceName);
+ }
+
+ if (MtuSize < QCMTU_MAX_MTU)
+ {
+ SetEvent(Context->Event[QMICL_EVENT_IPV6_MTU]);
+ }
+
+} // MyIPV6NotificationCallback
+
+HANDLE QMICL_RegisterMtuNotification
+(
+ HANDLE ServiceHandle,
+ UCHAR IpVersion,
+ NOTIFICATION_CALLBACK CallBack,
+ PVOID Context
+)
+{
+ HANDLE hNotificationThreadHandle;
+ PMTU_NOTIFICATION_CONTEXT context;
+ DWORD tid;
+
+ if (CallBack == NULL)
+ {
+ QCMTU_Print("Error: Unable to register MTU notification (no callback)\n");
+ return INVALID_HANDLE_VALUE;
+ }
+
+ context = (PMTU_NOTIFICATION_CONTEXT)malloc(sizeof(MTU_NOTIFICATION_CONTEXT));
+ if (context == NULL)
+ {
+ QCMTU_Print("Error: NOTIFICATION_CONTEXT memory allocation failure.\n");
+ return INVALID_HANDLE_VALUE;
+ }
+
+ context->ServiceHandle = ServiceHandle;
+ context->IpVersion = IpVersion;
+ context->CallBack = CallBack;
+ context->Context = Context;
+
+ hNotificationThreadHandle = CreateThread
+ (
+ NULL,
+ 0,
+ RegisterMtuNotification,
+ (LPVOID)context,
+ 0,
+ &tid
+ );
+
+ if (hNotificationThreadHandle == INVALID_HANDLE_VALUE)
+ {
+ QCMTU_Print("Error: Unable to register notification, error %d\n", GetLastError());
+ free(context);
+ }
+
+ return hNotificationThreadHandle;
+} // QMICL_RegisterMtuNotification
+
+DWORD WINAPI RegisterMtuNotification(PVOID Context)
+{
+ ULONG event, rtnBytes = 0;
+ OVERLAPPED ov;
+ BOOL bResult;
+ DWORD dwStatus = NO_ERROR, dwReturnBytes = 0;
+ PMTU_NOTIFICATION_CONTEXT ctxt = (PMTU_NOTIFICATION_CONTEXT)Context;
+ UCHAR ipver;
+
+ QCMTU_Print("-->RegisterMtuNotification for IPV%d, handle 0x%x\n", ctxt->IpVersion, ctxt->ServiceHandle);
+
+ ipver = ctxt->IpVersion;
+
+ ZeroMemory(&ov, sizeof(ov));
+ ov.Offset = 0;
+ ov.OffsetHigh = 0;
+ ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (ov.hEvent == NULL)
+ {
+ QCMTU_Print("RegisterMtuNotification failure, evt error %u\n", GetLastError());
+ ctxt->CallBack(ctxt->ServiceHandle, QCDEV_INVALID_MTU, ctxt->Context);
+ free(ctxt);
+ return 0;
+ }
+
+ QCMTU_Print("RegisterMtuNotification: calling DeviceIoControl for IPV%d (handle 0x%x)\n", ctxt->IpVersion, ctxt->ServiceHandle);
+
+ if (ctxt->IpVersion == 4)
+ {
+ bResult = DeviceIoControl
+ (
+ ctxt->ServiceHandle,
+ IOCTL_QCDEV_IPV4_MTU_NOTIFY,
+ NULL,
+ 0,
+ &event,
+ sizeof(event),
+ &rtnBytes,
+ &ov
+ );
+ }
+ else if (ctxt->IpVersion == 6)
+ {
+ bResult = DeviceIoControl
+ (
+ ctxt->ServiceHandle,
+ IOCTL_QCDEV_IPV6_MTU_NOTIFY,
+ NULL,
+ 0,
+ &event,
+ sizeof(event),
+ &rtnBytes,
+ &ov
+ );
+ }
+ else
+ {
+ QCMTU_Print("RegisterMtuNotification: failure, wrong IP version %d\n", ctxt->IpVersion);
+ ctxt->CallBack(ctxt->ServiceHandle, QCDEV_INVALID_MTU, ctxt->Context);
+ free(ctxt);
+ return 0;
+ }
+
+ QCMTU_Print("RegisterMtuNotification: post-DeviceIoControl for IPV%d\n", ctxt->IpVersion);
+
+ if (bResult == TRUE)
+ {
+ ctxt->CallBack(ctxt->ServiceHandle, event, ctxt->Context);
+ }
+ else
+ {
+ dwStatus = GetLastError();
+
+ if (ERROR_IO_PENDING != dwStatus)
+ {
+ QCMTU_Print("RegisterMtuNotification failure (IPV%d), error %u\n", ctxt->IpVersion, dwStatus);
+ ctxt->CallBack(ctxt->ServiceHandle, QCDEV_INVALID_MTU, ctxt->Context);
+ }
+ else
+ {
+ bResult = GetOverlappedResult
+ (
+ ctxt->ServiceHandle,
+ &ov,
+ &dwReturnBytes,
+ TRUE // no return until operaqtion completes
+ );
+
+ if (bResult == FALSE)
+ {
+ dwStatus = GetLastError();
+ QCMTU_Print("RegisterMtuNotification/GetOverlappedResult failure (IPV%d), error %u\n", ctxt->IpVersion, dwStatus);
+ ctxt->CallBack(ctxt->ServiceHandle, QCDEV_INVALID_MTU, ctxt->Context);
+ }
+ else
+ {
+ ctxt->CallBack(ctxt->ServiceHandle, event, ctxt->Context);
+ }
+ }
+ }
+
+ if (ov.hEvent != NULL)
+ {
+ CloseHandle(ov.hEvent);
+ }
+
+ free(ctxt);
+
+ QCMTU_Print("<--RegisterMtuNotification: IPV%d\n", ipver);
+
+ return 0;
+
+} // RegisterMtuNotification
diff --git a/src/windows/tools/wwansvc/qmicl.h b/src/windows/tools/wwansvc/qmicl.h
new file mode 100644
index 0000000..f56b2a8
--- /dev/null
+++ b/src/windows/tools/wwansvc/qmicl.h
@@ -0,0 +1,94 @@
+/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*
+
+ Q M I C L . H
+
+GENERAL DESCRIPTION
+ This file defines data structures and function prototypes for the
+ QMI client MTU notification handling logic.
+
+ Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ SPDX-License-Identifier: BSD-3-Clause
+
+*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/
+
+#ifndef QMICL_H
+#define QMICL_H
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include "api.h"
+
+#define MAX_DEC_DIGITS 10
+#define QMUX_CTL_FLAG_SINGLE_MSG 0x00
+#define QMUX_CTL_FLAG_COMPOUND_MSG 0x01
+#define QMUX_CTL_FLAG_TYPE_CMD 0x00
+#define QMUX_CTL_FLAG_TYPE_RSP 0x02
+#define QMUX_CTL_FLAG_TYPE_IND 0x04
+#define QMUX_CTL_FLAG_MASK_COMPOUND 0x01
+#define QMUX_CTL_FLAG_MASK_TYPE 0x06 // 00-cmd, 01-rsp, 10-ind
+
+#define QMUX_MSG_OVERHEAD_BYTES 4 // Type(USHORT) Length(USHORT)
+
+typedef enum _QMI_RESULT_CODE_TYPE
+{
+ QMI_RESULT_SUCCESS = 0x0000,
+ QMI_RESULT_FAILURE = 0x0001
+} QMI_RESULT_CODE_TYPE;
+
+#ifndef QC_SERVICE
+#define QCMTU_Print printf
+#endif // QC_SERVICE
+
+typedef struct _MTU_NOTIFICATION_CONTEXT
+{
+ HANDLE ServiceHandle;
+ UCHAR IpVersion;
+ NOTIFICATION_CALLBACK CallBack;
+ PVOID Context;
+} MTU_NOTIFICATION_CONTEXT, *PMTU_NOTIFICATION_CONTEXT;
+
+// Function Prototypes
+
+BOOL QMICL_StartMTUService(PVOID NicInfo);
+
+HANDLE WINAPI StartMtuThread(PVOID MtuThreadContext);
+
+DWORD WINAPI QMICL_MainThread(LPVOID Context);
+
+int PackWdsResetReq(VOID);
+
+VOID UnpackWdsResetRsp(PCHAR Message, ULONG Length, PCHAR Text, ULONG TextSize);
+
+BOOL CreateEventsForMtuThread(PDEVMN_NIC_RECORD Context);
+
+VOID CloseEventsForMtuThread(PDEVMN_NIC_RECORD Context);
+
+VOID MyIPV4MtuNotificationCallback
+(
+ HANDLE ServiceHandle,
+ ULONG MtuSize,
+ PDEVMN_NIC_RECORD Context
+);
+
+VOID MyIPV6MtuNotificationCallback
+(
+ HANDLE ServiceHandle,
+ ULONG MtuSize,
+ PDEVMN_NIC_RECORD Context
+);
+
+HANDLE QMICL_RegisterMtuNotification
+(
+ HANDLE ServiceHandle,
+ UCHAR IpVersion,
+ NOTIFICATION_CALLBACK CallBack,
+ PVOID Context
+);
+
+DWORD WINAPI RegisterMtuNotification(PVOID Context);
+
+#endif // QMICL_H
diff --git a/src/windows/tools/wwansvc/reg.c b/src/windows/tools/wwansvc/reg.c
new file mode 100644
index 0000000..73eed76
--- /dev/null
+++ b/src/windows/tools/wwansvc/reg.c
@@ -0,0 +1,388 @@
+/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*
+
+ R E G . C
+
+GENERAL DESCRIPTION
+ This file implements Windows registry query and device enumeration
+ helper functions for the Qualcomm MTU configuration service.
+
+ Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ SPDX-License-Identifier: BSD-3-Clause
+
+*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/
+
+#include "reg.h"
+
+BOOL QueryKey
+(
+ HKEY hKey,
+ PCHAR DeviceFriendlyName,
+ PCHAR ControlFileName,
+ PCHAR FullKeyName
+)
+{
+ TCHAR subKey[MAX_KEY_LENGTH];
+ DWORD nameLen;
+ TCHAR className[MAX_PATH] = TEXT("");
+ DWORD classNameLen = MAX_PATH;
+ DWORD numSubKeys = 0;
+ DWORD subKeyMaxLen;
+ DWORD classMaxLen;
+ DWORD numKeyValues;
+ DWORD valueMaxLen;
+ DWORD valueDataMaxLen;
+ DWORD securityDescLen;
+ FILETIME lastWriteTime;
+ char fullKeyName[MAX_KEY_LENGTH];
+
+ DWORD i, retCode;
+
+ // retrieve class name
+ retCode = RegQueryInfoKey
+ (
+ hKey,
+ className,
+ &classNameLen,
+ NULL,
+ &numSubKeys,
+ &subKeyMaxLen,
+ &classMaxLen,
+ &numKeyValues,
+ &valueMaxLen,
+ &valueDataMaxLen,
+ &securityDescLen,
+ &lastWriteTime
+ );
+
+ // go through subkeys
+ if (numSubKeys)
+ {
+ for (i = 0; i < numSubKeys; i++)
+ {
+ nameLen = MAX_KEY_LENGTH;
+ retCode = RegEnumKeyEx
+ (
+ hKey, i,
+ subKey,
+ &nameLen,
+ NULL,
+ NULL,
+ NULL,
+ &lastWriteTime
+ );
+ if (retCode == ERROR_SUCCESS)
+ {
+ BOOL result;
+
+ // _tprintf(TEXT("C-(%d) %s\n"), i+1, subKey);
+ sprintf(fullKeyName, "%s\\%s", FullKeyName, TEXT(subKey));
+ result = FindDeviceInstance(fullKeyName, DeviceFriendlyName, ControlFileName);
+ if (result == TRUE)
+ {
+ // printf("QueryKey: TRUE\n");
+ return TRUE;
+ }
+ }
+ }
+ }
+
+ // printf("QueryKey: FALSE\n");
+
+ return FALSE;
+} // QueryKey
+
+BOOL FindDeviceInstance
+(
+ PTCHAR InstanceKey,
+ PCHAR DeviceFriendlyName,
+ PCHAR ControlFileName
+)
+{
+ HKEY hTestKey;
+ BOOL ret = FALSE;
+
+ if (RegOpenKeyEx
+ (
+ HKEY_LOCAL_MACHINE,
+ InstanceKey,
+ 0,
+ KEY_READ,
+ &hTestKey
+ ) == ERROR_SUCCESS
+ )
+ {
+ ret = QCWWAN_GetEntryValue
+ (
+ hTestKey,
+ DeviceFriendlyName,
+ "QCDeviceControlFile",
+ ControlFileName
+ );
+
+ RegCloseKey(hTestKey);
+ }
+
+ return ret;
+}
+
+BOOL QCWWAN_GetEntryValue
+(
+ HKEY hKey,
+ PCHAR DeviceFriendlyName,
+ PCHAR EntryName, // "QCDeviceControlFile"
+ PCHAR ControlFileName
+)
+{
+ DWORD retCode = ERROR_SUCCESS;
+ TCHAR valueName[MAX_VALUE_NAME];
+ TCHAR swKey[MAX_VALUE_NAME];
+ DWORD valueNameLen = MAX_VALUE_NAME;
+ BOOL instanceFound = FALSE;
+ HKEY hSwKey;
+
+ valueName[0] = 0;
+
+ // first get device friendly name
+ retCode = RegQueryValueEx
+ (
+ hKey,
+ "FriendlyName", // "DriverDesc", // value name
+ NULL, // reserved
+ NULL, // returned type
+ valueName,
+ &valueNameLen
+ );
+
+ if (retCode == ERROR_SUCCESS)
+ {
+ valueName[valueNameLen] = 0;
+
+ if (strcmp(valueName, DeviceFriendlyName) == 0)
+ {
+ instanceFound = TRUE;
+ }
+ // printf("FriendlyName <%s>\n", valueName);
+ }
+ else
+ {
+ // no FriendlyName, get DeviceDesc
+ valueName[0] = 0;
+ valueNameLen = MAX_VALUE_NAME;
+ retCode = RegQueryValueEx
+ (
+ hKey,
+ "DeviceDesc", // value name
+ NULL, // reserved
+ NULL, // returned type
+ valueName,
+ &valueNameLen
+ );
+ if (retCode == ERROR_SUCCESS)
+ {
+ valueName[valueNameLen] = 0;
+
+ if (strcmp(valueName, DeviceFriendlyName) == 0)
+ {
+ instanceFound = TRUE;
+ }
+ // printf("DeviceDesc:reg-%d <%s>\n", instanceFound, valueName);
+ // printf("DeviceDesc:frn-%d <%s>\n", instanceFound, DeviceFriendlyName);
+ }
+ }
+
+ if (instanceFound == TRUE)
+ {
+ // Get "Driver" instance path
+ valueName[0] = 0;
+ valueNameLen = MAX_VALUE_NAME;
+ retCode = RegQueryValueEx
+ (
+ hKey,
+ "Driver", // value name
+ NULL, // reserved
+ NULL, // returned type
+ valueName,
+ &valueNameLen
+ );
+ if (retCode == ERROR_SUCCESS)
+ {
+ // Construct device software key
+ valueName[valueNameLen] = 0;
+ sprintf(swKey, "%s\\%s", QCNET_REG_SW_KEY, TEXT(valueName));
+
+ // printf("Got SW Key <%s>\n", swKey);
+
+ // Open device software key
+ retCode = RegOpenKeyEx
+ (
+ HKEY_LOCAL_MACHINE,
+ swKey,
+ 0,
+ KEY_READ,
+ &hSwKey
+ );
+
+ if (retCode == ERROR_SUCCESS)
+ {
+ // Retrieve the control file name
+ valueName[0] = 0;
+ valueNameLen = MAX_VALUE_NAME;
+ retCode = RegQueryValueEx
+ (
+ hSwKey,
+ EntryName, // value name
+ NULL, // reserved
+ NULL, // returned type
+ valueName,
+ &valueNameLen
+ );
+
+ if (retCode == ERROR_SUCCESS)
+ {
+ PCHAR p = (PCHAR)valueName + valueNameLen;
+
+ valueName[valueNameLen] = 0;
+
+ while ((p > valueName) && (*p != '\\'))
+ {
+ p--;
+ }
+ if (*p == '\\') p++;
+ strcpy(ControlFileName, p);
+ // _tprintf(TEXT("QCDeviceControlFile: %s\n"), valueName);
+ return TRUE;
+ }
+
+ RegCloseKey(hSwKey);
+ } // if (retCode == ERROR_SUCCESS)
+ } // if (retCode == ERROR_SUCCESS)
+ else
+ {
+ printf("Error: cannot get device software key, retCode %u\n", retCode);
+ }
+ } // if (instanceFound == TRUE)
+
+ return FALSE;
+}
+
+BOOL QCWWAN_GetControlFileName
+(
+ PCHAR DeviceFriendlyName,
+ PCHAR ControlFileName
+)
+{
+ HKEY hTestKey;
+
+ if (RegOpenKeyEx
+ (
+ HKEY_LOCAL_MACHINE,
+ TEXT(QCNET_REG_HW_KEY),
+ 0,
+ KEY_READ,
+ &hTestKey
+ ) == ERROR_SUCCESS
+ )
+ {
+ TCHAR subKey[MAX_KEY_LENGTH];
+ DWORD nameLen;
+ TCHAR className[MAX_PATH] = TEXT("");
+ DWORD classNameLen = MAX_PATH;
+ DWORD numSubKeys = 0;
+ DWORD subKeyMaxLen;
+ DWORD classMaxLen;
+ DWORD numKeyValues;
+ DWORD valueMaxLen;
+ DWORD valueDataMaxLen;
+ DWORD securityDescLen;
+ FILETIME lastWriteTime;
+ DWORD i, retCode;
+
+ // retrieve class name
+ retCode = RegQueryInfoKey
+ (
+ hTestKey,
+ className,
+ &classNameLen,
+ NULL,
+ &numSubKeys,
+ &subKeyMaxLen,
+ &classMaxLen,
+ &numKeyValues,
+ &valueMaxLen,
+ &valueDataMaxLen,
+ &securityDescLen,
+ &lastWriteTime
+ );
+
+ // go through subkeys
+ if (numSubKeys)
+ {
+ for (i = 0; i < numSubKeys; i++)
+ {
+ nameLen = MAX_KEY_LENGTH;
+ retCode = RegEnumKeyEx
+ (
+ hTestKey, i,
+ subKey,
+ &nameLen,
+ NULL,
+ NULL,
+ NULL,
+ &lastWriteTime
+ );
+ if (retCode == ERROR_SUCCESS)
+ {
+ BOOL result;
+
+ // _tprintf(TEXT("A-(%d) %s\n"), i+1, subKey);
+ result = QueryUSBDeviceKeys(subKey, DeviceFriendlyName, ControlFileName);
+ if (result == TRUE)
+ {
+ // printf("QueryKey: TRUE\n");
+ return TRUE;
+ }
+ }
+ }
+ }
+
+ RegCloseKey(hTestKey);
+
+ return retCode;
+ }
+
+ return FALSE;
+}
+
+BOOL QueryUSBDeviceKeys
+(
+ PTCHAR InstanceKey,
+ PCHAR DeviceFriendlyName,
+ PCHAR ControlFileName
+)
+{
+ HKEY hTestKey;
+ char fullKeyName[MAX_KEY_LENGTH];
+ BOOL ret = FALSE;
+
+ sprintf(fullKeyName, "%s\\%s", QCNET_REG_HW_KEY, TEXT(InstanceKey));
+
+ // printf("B-full key name: [%s]\n", fullKeyName);
+
+ if (RegOpenKeyEx
+ (
+ HKEY_LOCAL_MACHINE,
+ fullKeyName,
+ 0,
+ KEY_READ,
+ &hTestKey
+ ) == ERROR_SUCCESS
+ )
+ {
+ ret = QueryKey(hTestKey, DeviceFriendlyName, ControlFileName, fullKeyName);
+ RegCloseKey(hTestKey);
+
+ return ret;
+ }
+
+ return FALSE;
+} // QueryUSBDeviceKeys
diff --git a/src/windows/tools/wwansvc/reg.h b/src/windows/tools/wwansvc/reg.h
new file mode 100644
index 0000000..8f819dc
--- /dev/null
+++ b/src/windows/tools/wwansvc/reg.h
@@ -0,0 +1,63 @@
+/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*
+
+ R E G . H
+
+GENERAL DESCRIPTION
+ This file defines registry key constants and function prototypes for
+ device enumeration helpers in the Qualcomm MTU configuration service.
+
+ Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ SPDX-License-Identifier: BSD-3-Clause
+
+*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/
+
+#ifndef REG_H
+#define REG_H
+
+#include
+#include
+#include
+
+#define QCNET_REG_HW_KEY "SYSTEM\\CurrentControlSet\\Enum\\USB"
+#define QCNET_REG_SW_KEY "SYSTEM\\CurrentControlSet\\Control\\Class"
+
+#define MAX_KEY_LENGTH 255
+#define MAX_VALUE_NAME 16383
+
+BOOL QueryKey
+(
+ HKEY hKey,
+ PCHAR DeviceFriendlyName,
+ PCHAR ControlFileName,
+ PCHAR FullKeyName
+);
+
+BOOL FindDeviceInstance
+(
+ PTCHAR InstanceKey,
+ PCHAR DeviceFriendlyName,
+ PCHAR ControlFileName
+);
+
+BOOL QCWWAN_GetEntryValue
+(
+ HKEY hKey,
+ PCHAR DeviceFriendlyName,
+ PCHAR EntryName,
+ PCHAR ControlFileName
+);
+
+BOOL QCWWAN_GetControlFileName
+(
+ PCHAR DeviceFriendlyName,
+ PCHAR ControlFileName
+);
+
+BOOL QueryUSBDeviceKeys
+(
+ PTCHAR InstanceKey,
+ PCHAR DeviceFriendlyName,
+ PCHAR ControlFileName
+);
+
+#endif // REG_H
diff --git a/src/windows/tools/wwansvc/resource.h b/src/windows/tools/wwansvc/resource.h
new file mode 100644
index 0000000..439b0cf
--- /dev/null
+++ b/src/windows/tools/wwansvc/resource.h
@@ -0,0 +1,19 @@
+// Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+// SPDX-License-Identifier: BSD-3-Clause
+
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by qcmtusvc.rc
+//
+#define IDS_APP_TITLE 103
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 101
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1000
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/src/windows/tools/wwansvc/resource.rc b/src/windows/tools/wwansvc/resource.rc
new file mode 100644
index 0000000..546169a
--- /dev/null
+++ b/src/windows/tools/wwansvc/resource.rc
@@ -0,0 +1,111 @@
+// Microsoft Visual C++ generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "winres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (United States) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+ "#include ""afxres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 1,0,0,0
+ PRODUCTVERSION 1,0,0,0
+ FILEFLAGSMASK 0x17L
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x4L
+ FILETYPE 0x1L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "CompanyName", "Qualcomm Technologies, Inc."
+ VALUE "FileDescription", "Qualcomm Mtusvc Service"
+ VALUE "FileVersion", "1,0,0,0"
+ VALUE "InternalName", "qcmtusvc"
+ VALUE "LegalCopyright", "Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries."
+ VALUE "OriginalFilename", "qcmtusvc.exe"
+ VALUE "ProductName", "Qualcomm Mtusvc Service"
+ VALUE "ProductVersion", "1,0,0,0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+
+STRINGTABLE
+BEGIN
+ IDS_APP_TITLE "qcmtusvc"
+END
+
+#endif // English (United States) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/src/windows/tools/wwansvc/utils.c b/src/windows/tools/wwansvc/utils.c
new file mode 100644
index 0000000..879fcf1
--- /dev/null
+++ b/src/windows/tools/wwansvc/utils.c
@@ -0,0 +1,365 @@
+/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*
+
+ U T I L S . C
+
+GENERAL DESCRIPTION
+ This file implements utility functions including logging, device
+ service file retrieval, and notification registration helpers for
+ the Qualcomm MTU configuration service.
+
+ Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ SPDX-License-Identifier: BSD-3-Clause
+
+*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include "utils.h"
+
+BOOL WINAPI GetServiceFile
+(
+ HANDLE hd,
+ PCHAR ServiceFileName,
+ UCHAR ServiceType
+)
+{
+ DWORD bytesReturned = 0;
+
+ if (DeviceIoControl(
+ hd,
+ IOCTL_QCDEV_GET_SERVICE_FILE,
+ (LPVOID)&ServiceType,
+ (DWORD)sizeof(UCHAR),
+ (LPVOID)ServiceFileName,
+ (DWORD)SERVICE_FILE_BUF_LEN,
+ &bytesReturned,
+ NULL
+ )
+ )
+ {
+ printf("Got service file <%s>\n", ServiceFileName);
+
+ return TRUE;
+ }
+
+ printf("GetServiceFile failure (error code 0x%x)\n", GetLastError());
+
+ return FALSE;
+} // GetServiceFile
+
+DWORD WINAPI RegisterNotification(PVOID Context)
+{
+ ULONG event, rtnBytes = 0, ulNotification = 0;
+ OVERLAPPED ov;
+ BOOL bResult;
+ DWORD dwStatus = NO_ERROR, dwReturnBytes = 0;
+ PNOTIFICATION_CONTEXT ctxt = (PNOTIFICATION_CONTEXT)Context;
+
+ ZeroMemory(&ov, sizeof(ov));
+ ov.Offset = 0;
+ ov.OffsetHigh = 0;
+ ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (ov.hEvent == NULL)
+ {
+ printf("RegisterDeviceNotification failure, evt error %u\n", GetLastError());
+ ctxt->CallBack(ctxt->ServiceHandle, 0, ctxt->Context);
+ free(ctxt);
+ return 0;
+ }
+
+ QCMTU_Print("RegisterNotification: calling DeviceIoControl (handle 0x%x)\n", ctxt->ServiceHandle);
+ bResult = DeviceIoControl
+ (
+ ctxt->ServiceHandle,
+ IOCTL_QCDEV_WAIT_NOTIFY,
+ NULL,
+ 0,
+ &event,
+ sizeof(event),
+ &rtnBytes,
+ &ov
+ );
+ QCMTU_Print("RegisterNotification: post-DeviceIoControl (handle 0x%x)\n", ctxt->ServiceHandle);
+ if (bResult == TRUE)
+ {
+ ctxt->CallBack(ctxt->ServiceHandle, event, ctxt->Context);
+ }
+ else
+ {
+ dwStatus = GetLastError();
+
+ if (ERROR_IO_PENDING != dwStatus)
+ {
+ printf("RegisterNotification failure, error %u\n", dwStatus);
+ ctxt->CallBack(ctxt->ServiceHandle, 0, ctxt->Context);
+ }
+ else
+ {
+ bResult = GetOverlappedResult
+ (
+ ctxt->ServiceHandle,
+ &ov,
+ &dwReturnBytes,
+ TRUE // no return until operaqtion completes
+ );
+
+ if (bResult == FALSE)
+ {
+ dwStatus = GetLastError();
+ printf("RegisterNotification/GetOverlappedResult failure, error %u\n", dwStatus);
+ ctxt->CallBack(ctxt->ServiceHandle, 0, ctxt->Context);
+ }
+ else
+ {
+ ctxt->CallBack(ctxt->ServiceHandle, event, ctxt->Context);
+ }
+ }
+ }
+
+ if (ov.hEvent != NULL)
+ {
+ CloseHandle(ov.hEvent);
+ }
+
+ free(ctxt);
+
+ return 0;
+
+} // RegisterDeviceNotification
+
+// ================= Byte Ordering Functions ===============
+USHORT rv16(PUSHORT s)
+{
+ USHORT v = *s;
+
+ return (*s = ((v & 0xff00) >> 8) | ((v & 0x00ff) << 8));
+}
+
+ULONG rv32(PULONG l)
+{
+ ULONG v = *l;
+
+ return (*l = ((v & 0xff000000) >> 24) |
+ ((v & 0x00ff0000) >> 8) |
+ ((v & 0x0000ff00) << 8) |
+ ((v & 0x000000ff) << 24));
+}
+
+USHORT rv16v(USHORT v)
+{
+ return (((v & 0xff00) >> 8) | ((v & 0x00ff) << 8));
+}
+
+ULONG rv32v(ULONG v)
+{
+ return (((v & 0xff000000) >> 24) |
+ ((v & 0x00ff0000) >> 8) |
+ ((v & 0x0000ff00) << 8) |
+ ((v & 0x000000ff) << 24));
+}
+
+// Formatted binary output
+VOID USBUTL_PrintBytes(PVOID Buf, ULONG len, ULONG PktLen, char *info)
+{
+ char buf[1024], *p, cBuf[128], *cp;
+ char *buffer;
+ ULONG count = 0, lastCnt = 0, spaceNeeded;
+ ULONG i, s;
+
+#define SPLIT_CHAR '|'
+
+ buffer = (char *)Buf;
+ RtlZeroMemory(buf, 1024);
+ p = buf;
+ cp = cBuf;
+
+ if (PktLen < len)
+ {
+ len = PktLen;
+ }
+
+ sprintf(p, "\r\n\t --- <%s> DATA %u/%u BYTES ---\r\n", info, len, PktLen);
+ p += strlen(p);
+
+ for (i = 1; i <= len; i++)
+ {
+ if (i % 16 == 1)
+ {
+ sprintf(p, " %04u: ", i - 1);
+ p += 9;
+ }
+
+ sprintf(p, "%02X ", (UCHAR)buffer[i - 1]);
+ if (isprint(buffer[i - 1]) && (!isspace(buffer[i - 1])))
+ {
+ sprintf(cp, "%c", buffer[i - 1]);
+ }
+ else
+ {
+ sprintf(cp, ".");
+ }
+
+ p += 3;
+ cp += 1;
+
+ if ((i % 16) == 8)
+ {
+ sprintf(p, " ");
+ p += 2;
+ }
+ if (i % 16 == 0)
+ {
+ if (i % 64 == 0)
+ {
+ sprintf(p, " %c %s\r\n\r\n", SPLIT_CHAR, cBuf);
+ }
+ else
+ {
+ sprintf(p, " %c %s\r\n", SPLIT_CHAR, cBuf);
+ }
+
+ QCSER_DbgPrint
+ (
+ QCSER_DBG_MASK_CONTROL,
+ QCSER_DBG_LEVEL_FORCE,
+ (buf)
+ );
+ RtlZeroMemory(buf, 1024);
+ p = buf;
+ cp = cBuf;
+ }
+ }
+
+ lastCnt = i % 16;
+
+ if (lastCnt == 0)
+ {
+ lastCnt = 16;
+ }
+
+ if (lastCnt != 1)
+ {
+ // 10 + 3*8 + 2 + 3*8 = 60 (full line bytes)
+ spaceNeeded = (16 - lastCnt + 1) * 3;
+
+ if (lastCnt <= 8)
+ {
+ spaceNeeded += 2;
+ }
+ for (s = 0; s < spaceNeeded; s++)
+ {
+ sprintf(p++, " ");
+ }
+ sprintf(p, " %c %s\r\n\t --- <%s> END OF DATA BYTES(%u/%uB) ---\n",
+ SPLIT_CHAR, cBuf, info, len, PktLen);
+ QCSER_DbgPrint
+ (
+ QCSER_DBG_MASK_CONTROL,
+ QCSER_DBG_LEVEL_FORCE,
+ (buf)
+ );
+ }
+ else
+ {
+ sprintf(buf, "\r\n\t --- <%s> END OF DATA BYTES(%u/%uB) ---\n", info, len, PktLen);
+ QCSER_DbgPrint
+ (
+ QCSER_DBG_MASK_CONTROL,
+ QCSER_DBG_LEVEL_FORCE,
+ (buf)
+ );
+ }
+} //USBUTL_PrintBytes
+
+PCHAR GetCharArray
+(
+ PCHAR InBuffer,
+ PCHAR CharArray,
+ UCHAR MaxElements,
+ PUCHAR ActualElements
+)
+{
+ PCHAR p = InBuffer;
+ CHAR valBuf[1024];
+ PCHAR pv;
+ int v, n, idx = 0;
+
+ ZeroMemory(valBuf, 1024);
+ pv = valBuf;
+
+ // search for '['
+ while ((*p != '[') && (*p != 0))
+ {
+ p++;
+ }
+
+ if (*p == 0)
+ {
+ // syntax error
+ *ActualElements = 0;
+ return NULL;
+ }
+
+ p++;
+ // copy the values in to buffer
+ while ((*p != ']') && (*p != 0))
+ {
+ *pv++ = *p++;
+ }
+
+ // if no ']' found, syntax error
+ if (*p == 0)
+ {
+ return NULL;
+ }
+
+ // ++p is the return value
+
+ // process valBuf
+ pv = valBuf;
+
+ while (TRUE)
+ {
+ char valStr[512];
+
+ n = sscanf(pv, "%s", valStr);
+ if ((n == 0) || (n == EOF) || (idx >= MaxElements))
+ {
+ break;
+ }
+
+ sscanf(valStr, "%d", &v);
+ CharArray[idx++] = (char)v;
+ pv += (strlen(valStr) + 1);
+ }
+
+ *ActualElements = (UCHAR)idx;
+
+ return ++p;
+
+} // GetCharArray
+
+#ifdef QC_SERVICE
+
+#include
+#include
+#include
+
+VOID _cdecl QCMTU_Print(PCHAR Format, ...)
+{
+ va_list arguments;
+ UCHAR msgBuffer[DBG_MSG_MAX_SZ];
+
+ va_start(arguments, Format);
+ _vsnprintf_s(msgBuffer, DBG_MSG_MAX_SZ, _TRUNCATE, Format, arguments);
+ va_end(arguments);
+
+ OutputDebugString(msgBuffer);
+}
+
+#endif // QC_SERVICE
+
diff --git a/src/windows/tools/wwansvc/utils.h b/src/windows/tools/wwansvc/utils.h
new file mode 100644
index 0000000..a36a768
--- /dev/null
+++ b/src/windows/tools/wwansvc/utils.h
@@ -0,0 +1,120 @@
+/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*
+
+ U T I L S . H
+
+GENERAL DESCRIPTION
+ This file defines utility macros, debug logging, and function
+ prototypes for the Qualcomm MTU configuration service helpers.
+
+ Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ SPDX-License-Identifier: BSD-3-Clause
+
+*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/
+
+#ifndef UTILS_H
+#define UTILS_H
+
+#include "api.h"
+
+#define DBG_MSG_MAX_SZ 1024
+#define KEY_PRESSED_ESC 0x1B
+
+#define QCSER_DbgPrint(x,y,z) printf("%s", z);
+
+// Borrow some definitions from ntddk.h
+VOID FORCEINLINE
+InitializeListHead(IN PLIST_ENTRY ListHead)
+{
+ ListHead->Flink = ListHead->Blink = ListHead;
+}
+
+#define IsListEmpty(ListHead) \
+ ((ListHead)->Flink == (ListHead))
+
+VOID FORCEINLINE
+RemoveEntryList(IN PLIST_ENTRY Entry)
+{
+ PLIST_ENTRY Blink;
+ PLIST_ENTRY Flink;
+
+ Flink = Entry->Flink;
+ Blink = Entry->Blink;
+ Blink->Flink = Flink;
+ Flink->Blink = Blink;
+}
+
+PLIST_ENTRY FORCEINLINE
+RemoveHeadList(IN PLIST_ENTRY ListHead)
+{
+ PLIST_ENTRY Flink;
+ PLIST_ENTRY Entry;
+
+ Entry = ListHead->Flink;
+ Flink = Entry->Flink;
+ ListHead->Flink = Flink;
+ Flink->Blink = ListHead;
+ return Entry;
+}
+
+PLIST_ENTRY FORCEINLINE
+RemoveTailList(IN PLIST_ENTRY ListHead)
+{
+ PLIST_ENTRY Blink;
+ PLIST_ENTRY Entry;
+
+ Entry = ListHead->Blink;
+ Blink = Entry->Blink;
+ ListHead->Blink = Blink;
+ Blink->Flink = ListHead;
+ return Entry;
+}
+
+VOID FORCEINLINE
+InsertTailList(IN PLIST_ENTRY ListHead, IN PLIST_ENTRY Entry)
+{
+ PLIST_ENTRY Blink;
+
+ Blink = ListHead->Blink;
+ Entry->Flink = ListHead;
+ Entry->Blink = Blink;
+ Blink->Flink = Entry;
+ ListHead->Blink = Entry;
+}
+
+VOID FORCEINLINE
+InsertHeadList(IN PLIST_ENTRY ListHead, IN PLIST_ENTRY Entry)
+{
+ PLIST_ENTRY Flink;
+
+ Flink = ListHead->Flink;
+ Entry->Flink = Flink;
+ Entry->Blink = ListHead;
+ Flink->Blink = Entry;
+ ListHead->Flink = Entry;
+}
+
+// Function prototypes
+BOOL WINAPI GetServiceFile
+(
+ HANDLE hd,
+ PCHAR ServiceFileName,
+ UCHAR ServiceType
+);
+
+DWORD WINAPI RegisterNotification(PVOID Context);
+
+VOID USBUTL_PrintBytes(PVOID Buf, ULONG len, ULONG PktLen, char *info);
+
+PCHAR GetCharArray
+(
+ PCHAR InBuffer,
+ PCHAR CharArray,
+ UCHAR MaxElements,
+ PUCHAR ActualElements
+);
+
+#ifdef QC_SERVICE
+VOID __cdecl QCMTU_Print(PCHAR Format, ...);
+#endif
+
+#endif // UTILS_H