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