Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1136 lines
37 KiB

  1. // @doc
  2. /**********************************************************************
  3. *
  4. * @module IDirectInputEffectDriver.cpp |
  5. *
  6. * Contains Class Implementation of CIDirectInputEffectDriverClassFactory:
  7. * Factory for Creating Proper Effect Driver
  8. *
  9. * History
  10. * ----------------------------------------------------------
  11. * Matthew L. Coill (mlc) Original Jul-7-1999
  12. *
  13. * (c) 1999 Microsoft Corporation. All right reserved.
  14. *
  15. * @topic This IDirectInputEffectDriver |
  16. * This Driver sits on top of the standard PID driver (which is also
  17. * an IDirectInputEffectDriver) and passes most requests to the PID driver.
  18. * Some requests such as, DownloadEffect and SendForceFeedback command are
  19. * modified for our use. Modification purposes are described at each function
  20. * definition.
  21. *
  22. **********************************************************************/
  23. #include "IDirectInputEffectDriverClassFactory.h"
  24. #include "IDirectInputEffectDriver.h"
  25. #include <WinIOCTL.h> // For CTL_CODE definition
  26. #include "..\\GCKernel.sys\\GckExtrn.h"
  27. #include <crtdbg.h>
  28. #include <objbase.h> // For CoUninitialize
  29. #include <stdio.h>
  30. #include <stdarg.h>
  31. #include <tchar.h>
  32. const GUID IID_IDirectInputEffectDriver = {
  33. 0x02538130,
  34. 0x898F,
  35. 0x11D0,
  36. { 0x9A,0xD0,0x00,0xA0,0xC9,0xA0,0x6E,0x35 }
  37. };
  38. extern TCHAR CLSID_SWPIDDriver_String[];
  39. LONG DllAddRef();
  40. LONG DllRelease();
  41. DWORD __stdcall DoWaitForForceSchemeChange(void* pParameter);
  42. const DWORD c_dwShutdownWait = 500; // (0.5 Seconds)
  43. struct DIHIDFFINITINFO_STRUCT {
  44. DWORD dwSize;
  45. LPWSTR pwszDeviceInterface;
  46. GUID GuidInstance;
  47. };
  48. // PID Defines for Effect Tyoes
  49. #define PID_CONSTANT_FORCE 0x26
  50. #define PID_RAMP 0x27
  51. #define PID_SQUARE 0x30
  52. #define PID_SINE 0x31
  53. #define PID_TRIANGLE 0x32
  54. #define PID_SAWTOOTHUP 0x33
  55. #define PID_SAWTOOTHDOWN 0x34
  56. #define PID_SPRING 0x40
  57. #define PID_DAMPER 0x41
  58. #define PID_INTERTIA 0x42
  59. #define PID_FRICTION 0x43
  60. struct PercentageEntry
  61. {
  62. DWORD dwAngle;
  63. DWORD dwPercentageX;
  64. // DWORD dwPercentageY; Y == 10000 - X
  65. };
  66. // Array of Fixed value data
  67. const PercentageEntry g_PercentagesArray[] =
  68. {
  69. // Angle, Sin^2(Angle)
  70. { 0, 0}, // 0 Degrees
  71. { 1125, 381}, // 11.25 Degrees
  72. { 2250, 1465}, // 22.5 Degrees
  73. { 3375, 3087}, // 33.75 Degrees
  74. { 4500, 5000}, // 45 Degrees
  75. { 5625, 6913}, // 56.25 Degrees
  76. { 6750, 8536}, // 67.50 Degrees
  77. { 7875, 9619}, // 78.75 Degrees
  78. { 9000, 10000}, // 90 Degrees
  79. };
  80. const DWORD c_dwTableQuantization = g_PercentagesArray[1].dwAngle;
  81. const LONG c_lContributionY = 2; // (1/2 = 50%)
  82. const BYTE c_bSideWinderPIDReportID_SetEffect = 1;
  83. // Usage Pages (just PID)
  84. const USAGE c_HidUsagePage_PID = 0x0F;
  85. // Usages
  86. const USAGE c_HidUsage_EffectType = 0x25;
  87. const USAGE c_HidUsage_EffectType_Spring = 0x40;
  88. const USAGE c_HidUsage_EffectBlock_Gain = 0x52;
  89. const USAGE c_HidUsage_EffectBlock_Index = 0x22; // This is the ID of the effect
  90. // Preloaded Effects
  91. const BYTE c_EffectID_RTCSpring = 1;
  92. // Local Debugging Streaming Function that works in release
  93. #undef UseMyDebugOut
  94. void __cdecl myDebugOut (LPCSTR lpszFormat, ...)
  95. {
  96. #ifdef UseMyDebugOut
  97. //Stolen from inline void _cdecl AtlTrace(LPCSTR lpszFormat, ...) in AtlBase.h
  98. va_list args;
  99. va_start(args, lpszFormat);
  100. int nBuf;
  101. char szBuffer[1024];
  102. nBuf = _vsnprintf(szBuffer, sizeof(szBuffer), lpszFormat, args);
  103. _ASSERTE(nBuf < sizeof(szBuffer)); //Output truncated as it was > sizeof(szBuffer)
  104. #ifdef _NDEBUG
  105. OutputDebugStringA(szBuffer);
  106. #else
  107. _RPTF0 (_CRT_WARN, szBuffer);
  108. #endif
  109. va_end(args);
  110. #else
  111. UNREFERENCED_PARAMETER (lpszFormat);
  112. return;
  113. #endif
  114. }
  115. /******************** Class CIDirectInputEffectDriver ***********************/
  116. /*****************************************************************************
  117. **
  118. ** CIDirectInputEffectDriverClassFactory::CIDirectInputEffectDriverClassFactory()
  119. **
  120. ** @mfunc Constructor
  121. **
  122. *****************************************************************************/
  123. CIDirectInputEffectDriver::CIDirectInputEffectDriver
  124. (
  125. IDirectInputEffectDriver* pIPIDEffectDriver, //@parm [IN] Pointer to PID Effect Driver
  126. IClassFactory* pIPIDClassFactory //@parm [IN] Pointer to PID Class Factory
  127. ) :
  128. m_ulReferenceCount(1),
  129. m_dwDIVersion(0xFFFFFFFF),
  130. m_dwExternalDeviceID(0xFFFFFFFF),
  131. m_dwInternalDeviceID(0xFFFFFFFF),
  132. m_pIPIDEffectDriver(pIPIDEffectDriver),
  133. m_pIPIDClassFactory(pIPIDClassFactory),
  134. m_hKernelDeviceDriver(NULL),
  135. m_hKernelDeviceDriverDuplicate(NULL),
  136. m_hHidDeviceDriver(NULL),
  137. m_dwGcKernelDevice(0),
  138. m_hForceSchemeChangeWaitThread(NULL),
  139. m_dwForceSchemeChangeThreadID(0),
  140. m_pPreparsedData(NULL)
  141. {
  142. myDebugOut ("CIDirectInputEffectDriver::Constructor (pIPIDEffectDriver:0x%0p)\n", pIPIDEffectDriver);
  143. // Add to gobal object count
  144. DllAddRef();
  145. // Add references for objects we are holding
  146. m_pIPIDClassFactory->AddRef();
  147. m_pIPIDEffectDriver->AddRef();
  148. ::memset((void*)&m_HidAttributes, 0, sizeof(m_HidAttributes));
  149. m_ForceMapping.AssignmentBlock.CommandHeader.eID = eForceMap;
  150. m_ForceMapping.AssignmentBlock.CommandHeader.ulByteSize = sizeof(m_ForceMapping);
  151. m_ForceMapping.AssignmentBlock.ulVidPid = 0; // Irrelevant
  152. m_ForceMapping.bMapYToX = FALSE;
  153. m_ForceMapping.usRTC = 10000;
  154. m_ForceMapping.usGain = 10000;
  155. }
  156. /*****************************************************************************
  157. **
  158. ** CIDirectInputEffectDriver::~CIDirectInputEffectDriver()
  159. **
  160. ** @mfunc Destructor
  161. **
  162. *****************************************************************************/
  163. CIDirectInputEffectDriver::~CIDirectInputEffectDriver()
  164. {
  165. _ASSERTE(m_pIPIDEffectDriver == NULL);
  166. _ASSERTE(m_ulReferenceCount == 0);
  167. DllRelease(); // Remove our object from the global object count
  168. myDebugOut ("CIDirectInputEffectDriver::Destructor\n");
  169. }
  170. //IUnknown members
  171. /***********************************************************************************
  172. **
  173. ** ULONG CIDirectInputEffectDriver::QueryInterface(REFIID refiid, void** ppvObject)
  174. **
  175. ** @func Query an IUnknown for a particular type. This causes reference count increase locally only.
  176. ** If it is a type we don't know, should we give the PID driver a crack (the PID driver
  177. ** might have a customized private interface, we don't want to ruin that). Currently not
  178. ** going to pass on the Query because this could screwup Symmetry.
  179. **
  180. ** @rdesc S_OK : all is well
  181. ** E_INVALIDARG : if (ppvObject == NULL)
  182. ** E_NOINTERFACE : If requested interface is unsupported
  183. **
  184. *************************************************************************************/
  185. HRESULT __stdcall CIDirectInputEffectDriver::QueryInterface
  186. (
  187. REFIID refiid, //@parm [IN] Identifier of the requested interface
  188. void** ppvObject //@parm [OUT] Address to place requested interface pointer
  189. )
  190. {
  191. myDebugOut ("CIDirectInputEffectDriver::QueryInterface (refiid:0x%0p, ppvObject:0x%0p)\n", refiid, ppvObject);
  192. HRESULT hrPidQuery = m_pIPIDEffectDriver->QueryInterface(refiid, ppvObject);
  193. if (SUCCEEDED(hrPidQuery))
  194. {
  195. // Don't perform a real addref (PID.dll::QueryInterface will do its own)
  196. ::InterlockedIncrement((LONG*)&m_ulReferenceCount);
  197. *ppvObject = this;
  198. }
  199. return hrPidQuery;
  200. }
  201. /***********************************************************************************
  202. **
  203. ** ULONG CIDirectInputEffectDriver::AddRef()
  204. **
  205. ** @func Bumps up the reference count
  206. ** The PID driver reference count is left alone. We only decrement it when
  207. ** this object is ready to go away.
  208. **
  209. ** @rdesc New reference count
  210. **
  211. *************************************************************************************/
  212. ULONG __stdcall CIDirectInputEffectDriver::AddRef()
  213. {
  214. myDebugOut ("CIDirectInputEffectDriver::AddRef (Early) 0x%0p\n", m_ulReferenceCount);
  215. m_pIPIDEffectDriver->AddRef();
  216. return (ULONG)(::InterlockedIncrement((LONG*)&m_ulReferenceCount));
  217. }
  218. /***********************************************************************************
  219. **
  220. ** ULONG CIDirectInputEffectDriver::Release()
  221. **
  222. ** @func Decrements the reference count.
  223. ** if the reference count becomes zero this object is destroyed.
  224. ** The PID Factory reference is only effected if it is time to release all.
  225. **
  226. ** @rdesc New reference count
  227. **
  228. *************************************************************************************/
  229. ULONG __stdcall CIDirectInputEffectDriver::Release()
  230. {
  231. myDebugOut ("CIDirectInputEffectDriver::Release (Early) 0x%0p\n", m_ulReferenceCount);
  232. if (m_ulReferenceCount == 0)
  233. {
  234. return m_ulReferenceCount;
  235. }
  236. if ((::InterlockedDecrement((LONG*)&m_ulReferenceCount)) != 0)
  237. {
  238. m_pIPIDEffectDriver->Release();
  239. return m_ulReferenceCount;
  240. }
  241. // Tell the driver to complete outstanding IOCTLs to this device
  242. if (m_hKernelDeviceDriver == NULL)
  243. { // Don't have a handle to PID driver, so open one
  244. m_hKernelDeviceDriver = ::CreateFile(TEXT(GCK_CONTROL_W32Name), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_DELETE_ON_CLOSE, NULL);
  245. if (m_hKernelDeviceDriver == INVALID_HANDLE_VALUE)
  246. {
  247. m_hKernelDeviceDriver = NULL;
  248. }
  249. }
  250. if (m_hKernelDeviceDriver != NULL) // Handle should be open, but check just incase
  251. {
  252. DWORD dwReturnDataSize;
  253. BOOL fSuccess = DeviceIoControl(m_hKernelDeviceDriver,
  254. IOCTL_GCK_END_FF_NOTIFICATION,
  255. (void*)(&m_dwGcKernelDevice), sizeof(DWORD), // In
  256. NULL, 0, &dwReturnDataSize, // Out
  257. NULL);
  258. if (!fSuccess)
  259. myDebugOut ("CIDirectInputEffectDriver::Release : GCK IOCTL_GCK_END_FF_NOTIFICATION failed!\n");
  260. Sleep(c_dwShutdownWait);
  261. ::CloseHandle(m_hKernelDeviceDriver);
  262. }
  263. else
  264. {
  265. myDebugOut ("CIDirectInputEffectDriver::Release : Could not Open GCK for IOCTL_GCK_END_FF_NOTIFICATION\n");
  266. }
  267. // Free up the preparsed data
  268. if (m_pPreparsedData != NULL)
  269. {
  270. ::HidD_FreePreparsedData(m_pPreparsedData);
  271. m_pPreparsedData = NULL;
  272. }
  273. // Close the handle to the HID path of the driver
  274. ::CloseHandle(m_hHidDeviceDriver);
  275. m_hHidDeviceDriver = NULL;
  276. // Close the thread handle (which should be done by now)
  277. if (m_hForceSchemeChangeWaitThread != NULL)
  278. {
  279. ::CloseHandle(m_hForceSchemeChangeWaitThread);
  280. m_hForceSchemeChangeWaitThread = NULL;
  281. m_dwForceSchemeChangeThreadID = 0;
  282. }
  283. else
  284. {
  285. myDebugOut ("CIDirectInputEffectDriver::Release() m_hForceSchemeCHangeWaitThread did not finish!\n");
  286. }
  287. // Release the low level pid driver and delete ourselves
  288. m_pIPIDEffectDriver->Release();
  289. m_pIPIDEffectDriver = NULL;
  290. // Release the low level factory (include extra release to fix bug in PID.dll)
  291. if (m_pIPIDClassFactory->Release() > 0)
  292. {
  293. m_pIPIDClassFactory->Release();
  294. }
  295. m_pIPIDClassFactory = NULL;
  296. delete this;
  297. return 0;
  298. }
  299. //IDirectInputEffectDriver members
  300. HRESULT __stdcall CIDirectInputEffectDriver::DeviceID
  301. (
  302. DWORD dwDIVersion,
  303. DWORD dwExternalID,
  304. DWORD dwIsBegining,
  305. DWORD dwInternalID,
  306. void* pReserved
  307. )
  308. {
  309. myDebugOut ("CIDirectInputEffectDriver::DeviceID (dwDIVersion:0x%08p dwExternalID:0x%08p dwIsBeginning:0x%08p dwInternalID:0x%08p pReserved:0x%08p)\n",
  310. dwDIVersion, dwExternalID, dwIsBegining, dwInternalID, pReserved);
  311. // Store off some data
  312. m_dwExternalDeviceID = dwExternalID;
  313. m_dwInternalDeviceID = dwInternalID;
  314. bool bPossiblyFirstTime = false;
  315. // Get a handle to the Kernel Device and activate the thread
  316. if (m_hKernelDeviceDriver == NULL)
  317. {
  318. bPossiblyFirstTime = true;
  319. m_hKernelDeviceDriver = ::CreateFile(TEXT(GCK_CONTROL_W32Name), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_DELETE_ON_CLOSE, NULL);
  320. if (m_hKernelDeviceDriver == INVALID_HANDLE_VALUE)
  321. {
  322. m_hKernelDeviceDriver = NULL;
  323. myDebugOut ("CIDirectInputEffectDriver::DeviceID Create GCK File Failed!\n");
  324. }
  325. else
  326. {
  327. InitHidInformation((LPDIHIDFFINITINFO)pReserved); // Set up the HID stuff (preparsed data et al)
  328. if (NULL == pReserved ||
  329. IsBadReadPtr ((const void*)pReserved, (UINT) sizeof (DIHIDFFINITINFO_STRUCT)) )
  330. {
  331. myDebugOut ("CIDirectInputEffectDriver::DeviceID E_INVALIDARG (pReserved is NULL!)\n");
  332. return E_INVALIDARG;
  333. // Call the default guy
  334. //return m_pIPIDEffectDriver->DeviceID(dwDIVersion, dwExternalID, dwIsBegining, dwInternalID, pReserved);
  335. }
  336. //
  337. // get the handle for this device
  338. //
  339. WCHAR* pwcInstanceName = ((DIHIDFFINITINFO_STRUCT*)(pReserved))->pwszDeviceInterface;
  340. DWORD dwBytesReturned;
  341. BOOL fSuccess = ::DeviceIoControl(m_hKernelDeviceDriver, IOCTL_GCK_GET_HANDLE,
  342. pwcInstanceName, ::wcslen(pwcInstanceName)*sizeof(WCHAR),
  343. &m_dwGcKernelDevice, sizeof(m_dwGcKernelDevice), &dwBytesReturned,
  344. NULL);
  345. if (fSuccess != FALSE)
  346. {
  347. // Update the force block
  348. fSuccess =::DeviceIoControl(m_hKernelDeviceDriver, IOCTL_GCK_GET_FF_SCHEME_DATA,
  349. (void*)(&m_dwGcKernelDevice), sizeof(DWORD),
  350. (void*)(&m_ForceMapping), sizeof(m_ForceMapping), &dwBytesReturned,
  351. NULL);
  352. // Get the duplicate handle for the thread
  353. BOOL bDuplicated = ::DuplicateHandle(::GetCurrentProcess(), m_hKernelDeviceDriver, ::GetCurrentProcess(), &m_hKernelDeviceDriverDuplicate, 0, FALSE, DUPLICATE_SAME_ACCESS);
  354. if ((m_hKernelDeviceDriverDuplicate == INVALID_HANDLE_VALUE) || (bDuplicated == FALSE))
  355. {
  356. m_hKernelDeviceDriverDuplicate = NULL;
  357. }
  358. else
  359. {
  360. m_hForceSchemeChangeWaitThread = ::CreateThread(NULL, 0, DoWaitForForceSchemeChange, (void*)this, 0, &m_dwForceSchemeChangeThreadID);
  361. }
  362. }
  363. else
  364. {
  365. myDebugOut ("CIDirectInputEffectDriver::DeviceID IOCTL_GCK_GET_HANDLE Failed!\n");
  366. }
  367. // Close since I need to reopen at the end (why is this happening?)
  368. ::CloseHandle(m_hKernelDeviceDriver);
  369. m_hKernelDeviceDriver = NULL;
  370. }
  371. }
  372. // Hack to get PID.DLL to place keys in registry.
  373. // -- It won't place them if OEM-FF Key is already there
  374. /*
  375. if (bPossiblyFirstTime == true)
  376. {
  377. HKEY hkeyOEM = NULL;
  378. ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("System\\CurrentControlSet\\Control\\MediaProperties\\PrivateProperties\\Joystick\\OEM"), 0, KEY_ALL_ACCESS, &hkeyOEM);
  379. if (hkeyOEM != NULL)
  380. {
  381. // Open key specific to the current device (VIDPID is in m_HidAttributes)
  382. HKEY hkeyOEMForceFeedback = NULL;
  383. TCHAR rgtcDeviceName[64];
  384. ::wsprintf(rgtcDeviceName, TEXT("VID_%04X&PID_%04X\\OEMForceFeedback"), m_HidAttributes.VendorID, m_HidAttributes.ProductID);
  385. ::RegOpenKeyEx(hkeyOEM, rgtcDeviceName, 0, KEY_ALL_ACCESS, &hkeyOEMForceFeedback);
  386. if (hkeyOEMForceFeedback != NULL)
  387. {
  388. // Check to see if the effects key is already there
  389. HKEY hkeyEffects = NULL;
  390. ::RegOpenKeyEx(hkeyOEMForceFeedback, TEXT("Effects"), 0, KEY_READ, &hkeyEffects);
  391. ::RegCloseKey(hkeyOEMForceFeedback);
  392. if (hkeyEffects != NULL)
  393. {
  394. // Effects key is there, this is not the first time we have run
  395. ::RegCloseKey(hkeyEffects);
  396. bPossiblyFirstTime = false;
  397. }
  398. else // Delete the whole OEM ForceFeedback key
  399. {
  400. ::RegDeleteKey(hkeyOEM, rgtcDeviceName);
  401. }
  402. }
  403. }
  404. ::RegCloseKey(hkeyOEM);
  405. }
  406. */
  407. // Call the drivers DeviceID (if we have removed the OEMFF Key it will repopulate)
  408. HRESULT hrPID = m_pIPIDEffectDriver->DeviceID(dwDIVersion, dwExternalID, dwIsBegining, dwInternalID, pReserved);
  409. // Do we need to put ourselves back as the DIEffectDriver?
  410. /* if (bPossiblyFirstTime == true)
  411. {
  412. HKEY hkeyOEM = NULL;
  413. ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("System\\CurrentControlSet\\Control\\MediaProperties\\PrivateProperties\\Joystick\\OEM"), 0, KEY_ALL_ACCESS, &hkeyOEM);
  414. if (hkeyOEM != NULL)
  415. {
  416. HKEY hkeyOEMForceFeedback = NULL;
  417. TCHAR rgtcDeviceName[64];
  418. ::wsprintf(rgtcDeviceName, TEXT("VID_%04X&PID_%04X\\OEMForceFeedback"), m_HidAttributes.VendorID, m_HidAttributes.ProductID);
  419. ::RegOpenKeyEx(hkeyOEM, rgtcDeviceName, 0, KEY_ALL_ACCESS, &hkeyOEMForceFeedback);
  420. // Set the registry CLSID value to us
  421. if (hkeyOEMForceFeedback != NULL)
  422. {
  423. ::RegSetValueEx(hkeyOEMForceFeedback, TEXT("CLSID"), 0, REG_SZ, (BYTE*)CLSID_SWPIDDriver_String, _tcslen(CLSID_SWPIDDriver_String) * sizeof(TCHAR));
  424. ::RegCloseKey(hkeyOEMForceFeedback);
  425. }
  426. ::RegCloseKey(hkeyOEM);
  427. }
  428. }
  429. */
  430. return hrPID; // Value from the System PID driver
  431. }
  432. HRESULT __stdcall CIDirectInputEffectDriver::GetVersions
  433. (
  434. DIDRIVERVERSIONS* pDriverVersions
  435. )
  436. {
  437. myDebugOut ("CIDirectInputEffectDriver::GetVersions (pDriverVersions:0x%08p)\n", pDriverVersions);
  438. return m_pIPIDEffectDriver->GetVersions(pDriverVersions);
  439. }
  440. HRESULT __stdcall CIDirectInputEffectDriver::Escape
  441. (
  442. DWORD dwDeviceID,
  443. DWORD dwEffectID,
  444. DIEFFESCAPE* pEscape
  445. )
  446. {
  447. myDebugOut ("CIDirectInputEffectDriver::Escape (dwDeviceID:0x%08p, dwEffectID:0x%08p, pEscape:0x%08p)\n", dwDeviceID, dwEffectID, pEscape);
  448. return m_pIPIDEffectDriver->Escape(dwDeviceID, dwEffectID, pEscape);
  449. }
  450. /***********************************************************************************
  451. **
  452. ** void CIDirectInputEffectDriver::SetGain(DWORD dwDeviceID, DWORD dwGain)
  453. **
  454. ** @func Modifies the user gain based on settings and sends it down to the lower PID driver
  455. **
  456. ** @rdesc Nothing
  457. **
  458. *************************************************************************************/
  459. HRESULT __stdcall CIDirectInputEffectDriver::SetGain
  460. (
  461. DWORD dwDeviceID, //@parm [IN] ID for device of interest
  462. DWORD dwGain //@parm [IN] User selected gain
  463. )
  464. {
  465. dwGain *= m_ForceMapping.usGain/1000; // 0 - 100K
  466. dwGain /= 10; // 0 - 10K
  467. myDebugOut ("CIDirectInputEffectDriver::SetGain (dwDeviceID:%d, dwGain:%05d:)\n", dwDeviceID, dwGain);
  468. return m_pIPIDEffectDriver->SetGain(dwDeviceID, dwGain);
  469. }
  470. /***********************************************************************************
  471. **
  472. ** HRESULT CopyW2T(LPWSTR pswDest, UINT *puDestSize, LPTSTR ptszSrc)
  473. **
  474. ** @mfunc Copies a WCHAR into a TCHAR while checking buffer length
  475. **
  476. ** @rdesc S_OK on success, MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, ERROR_INSUFFICIENT_BUFFER)
  477. ** if destination buffer is too small
  478. **
  479. *************************************************************************************/
  480. HRESULT CopyW2T
  481. (
  482. LPTSTR ptszDest, // @parm pointer to WCHAR destination buffer
  483. UINT& ruDestSize, // @parm size of dest in WCHAR's
  484. LPCWSTR pwcszSrc // @parm pointer to NULL terminated source string
  485. )
  486. {
  487. UINT uSizeRequired;
  488. HRESULT hr = S_OK;
  489. uSizeRequired = wcslen(pwcszSrc)+1; //the one is for a NULL character
  490. if(ruDestSize < uSizeRequired)
  491. {
  492. hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, ERROR_INSUFFICIENT_BUFFER);
  493. }
  494. else
  495. {
  496. //
  497. // we always return wide, but TCHAR can be WCHAR or char
  498. // this compile time so use preprocessor
  499. //
  500. #ifdef UNICODE
  501. wcscpy(ptszDest, pwcszSrc);
  502. #else
  503. int iRetVal=WideCharToMultiByte
  504. (
  505. CP_ACP,
  506. 0,
  507. pwcszSrc,
  508. -1,
  509. ptszDest,
  510. ruDestSize,
  511. NULL,
  512. NULL
  513. );
  514. if(0==iRetVal)
  515. hr=GetLastError();
  516. #endif //UNICODE
  517. }
  518. //Copy size required, or chars copied (same thing)
  519. ruDestSize = uSizeRequired;
  520. return hr;
  521. }
  522. /***********************************************************************************
  523. **
  524. ** void CIDirectInputEffectDriver::InitHidInformation(void* HidInformation)
  525. **
  526. ** @func Open a hid path to the driver, and get preparsed data and hid caps.
  527. **
  528. ** @rdesc Nothing
  529. **
  530. *************************************************************************************/
  531. void CIDirectInputEffectDriver::InitHidInformation
  532. (
  533. LPDIHIDFFINITINFO pHIDInitInfo //@parm [IN] Pointer to structure containing the HID device name
  534. )
  535. {
  536. myDebugOut ("CIDirectInputEffectDriver::InitHidInformation (pHIDInitInfo: 0x%08p)\n", pHIDInitInfo);
  537. if (pHIDInitInfo != NULL)
  538. {
  539. TCHAR ptchHidDeviceName[MAX_PATH];
  540. unsigned int dwSize = MAX_PATH;
  541. ::CopyW2T(ptchHidDeviceName, dwSize, pHIDInitInfo->pwszDeviceInterface);
  542. m_hHidDeviceDriver = ::CreateFile(ptchHidDeviceName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_DELETE_ON_CLOSE, NULL);
  543. if (m_hHidDeviceDriver == INVALID_HANDLE_VALUE)
  544. {
  545. m_hHidDeviceDriver = NULL;
  546. return;
  547. }
  548. if (m_pPreparsedData == NULL)
  549. {
  550. ::HidD_GetPreparsedData(m_hHidDeviceDriver, &m_pPreparsedData);
  551. if (m_pPreparsedData == NULL)
  552. {
  553. return;
  554. }
  555. }
  556. ::HidP_GetCaps(m_pPreparsedData, &m_HidCaps);
  557. // Find VID/PID the USB way!
  558. ::HidD_GetAttributes(m_hHidDeviceDriver, &m_HidAttributes);
  559. }
  560. }
  561. /***********************************************************************************
  562. **
  563. ** void CIDirectInputEffectDriver::SendSpringChange()
  564. **
  565. ** @func Sends a new Spring Modify report to the driver
  566. **
  567. ** @rdesc Nothing
  568. **
  569. *************************************************************************************/
  570. void CIDirectInputEffectDriver::SendSpringChange()
  571. {
  572. myDebugOut ("CIDirectInputEffectDriver::SendSpringChange ()\n");
  573. if ((m_hHidDeviceDriver != NULL) && (m_pPreparsedData != NULL))
  574. {
  575. // Setup the spring report
  576. // 1. Allocate an array of max output size
  577. BYTE* pbOutReport = new BYTE[m_HidCaps.OutputReportByteLength];
  578. if (pbOutReport == NULL)
  579. {
  580. return;
  581. }
  582. // 2. Zero out array
  583. ::memset(pbOutReport, 0, m_HidCaps.OutputReportByteLength);
  584. // 3. Set the proper report ID
  585. pbOutReport[0] = c_bSideWinderPIDReportID_SetEffect;
  586. // 4. Cheat since we know what the firmware is expecting (Use usage Gunk where easy)
  587. pbOutReport[1] = c_EffectID_RTCSpring; // Effect Block Index (ID)
  588. unsigned short usRTC = m_ForceMapping.usRTC; // 0 - 10K
  589. usRTC /= 100; // 0 - 100
  590. usRTC *= 255; // 0 - 25500
  591. usRTC /= 100; // 0 - 255
  592. if (usRTC > 255)
  593. {
  594. usRTC = 255;
  595. }
  596. pbOutReport[9] = BYTE(usRTC); // Effect Gain - Only item the RTC Spring will look at
  597. // Now that the firmware has change for Godzilla it looks at bunches o stuff
  598. pbOutReport[7] = 1; //sample period
  599. pbOutReport[11] =132; //direction-axis + FW only force polar flag
  600. pbOutReport[13] = 255; //Y - direction
  601. myDebugOut ("CIDirectInputEffectDriver::SendSpringChange -> usRTC:%03d\n", usRTC);
  602. // 5. Send the report down
  603. DWORD dwBytesWritten;
  604. ::WriteFile(m_hHidDeviceDriver, pbOutReport, m_HidCaps.OutputReportByteLength, &dwBytesWritten, NULL);
  605. // 6. Deallocate report array
  606. delete[] pbOutReport;
  607. }
  608. }
  609. /***********************************************************************************
  610. **
  611. ** void CIDirectInputEffectDriver::SendForceFeedbackCommand()
  612. **
  613. ** @func Intercepting this call gives us the chance to set the force level of the
  614. ** RTC Spring after a reset
  615. **
  616. ** @rdesc Result of SendForceFeedbackCommand (from lower driver)
  617. **
  618. *************************************************************************************/
  619. HRESULT __stdcall CIDirectInputEffectDriver::SendForceFeedbackCommand
  620. (
  621. DWORD dwDeviceID, //@parm [IN] ID of device this is for
  622. DWORD dwState //@parm [IN] The command (we are interested in reset)
  623. )
  624. {
  625. myDebugOut ("CIDirectInputEffectDriver::SendForceFeedbackCommand Enter (dwDeviceID:%x, dwState:0x%08p)\n", dwDeviceID, dwState);
  626. HRESULT hr = m_pIPIDEffectDriver->SendForceFeedbackCommand(dwDeviceID, dwState);
  627. myDebugOut ("CIDirectInputEffectDriver::SendForceFeedbackCommand Calling Base (hr:0x%08p)\n", hr);
  628. if (dwState == DISFFC_RESET) // This is how they turn on the RTC Spring
  629. {
  630. myDebugOut ("CIDirectInputEffectDriver::SendForceFeedbackCommand RESET sent!\n");
  631. SendSpringChange();
  632. }
  633. return hr;
  634. }
  635. HRESULT __stdcall CIDirectInputEffectDriver::GetForceFeedbackState
  636. (
  637. DWORD dwDeviceID,
  638. DIDEVICESTATE* pDeviceState
  639. )
  640. {
  641. myDebugOut ("CIDirectInputEffectDriver::GetForceFeedbackState Begin (dwDeviceID:%d, pDeviceState:0x%08p)\n", dwDeviceID, pDeviceState);
  642. HRESULT hrPidDriver = S_OK;
  643. __try
  644. {
  645. hrPidDriver = m_pIPIDEffectDriver->GetForceFeedbackState(dwDeviceID, pDeviceState);
  646. //
  647. // Invert safety switch and user FF switch bits to compensate for PID/DI mismatch
  648. // which caused Fighter Ace II to ignore X/Y axes.
  649. // Note IF clause which will cause fix to be ignored if DI fixes it
  650. //
  651. if (pDeviceState->dwState & DIGFFS_USERFFSWITCHOFF)
  652. pDeviceState->dwState=pDeviceState->dwState^(DIGFFS_SAFETYSWITCHON|DIGFFS_SAFETYSWITCHOFF|DIGFFS_USERFFSWITCHON|DIGFFS_USERFFSWITCHOFF);
  653. }
  654. __except ((GetExceptionCode() == EXCEPTION_INT_DIVIDE_BY_ZERO) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
  655. {
  656. hrPidDriver = DIERR_INPUTLOST;
  657. _RPT0(_CRT_WARN, "!!! Caught EXCEPTION_INT_DIVIDE_BY_ZERO !!!\n");
  658. }
  659. myDebugOut ("CIDirectInputEffectDriver::GetForceFeedbackState End (dwDeviceID:%d, pDeviceState:0x%08p; hr: 0x%08x)\n",
  660. dwDeviceID, pDeviceState, hrPidDriver);
  661. return hrPidDriver;
  662. }
  663. /***********************************************************************************
  664. **
  665. ** void PercentagesFromAngle()
  666. **
  667. ** @func Extrapolate the percentages from the table. Makes use of the fact that
  668. ** sin^2(angle) + cos^2(angle) = 1 and xPercentage + yPercentage = 1
  669. **
  670. ** @rdesc Result of download (from lower driver)
  671. **
  672. *************************************************************************************/
  673. void PercentagesFromAngle
  674. (
  675. DWORD dwAngle, //@parm [IN] Angle to convert to percentages
  676. LONG& lXPercent, //@parm [OUT] Resultant X Percentage
  677. LONG& lYPercent //@parm [OUT] Resultant Y Percentage
  678. )
  679. {
  680. // Get the angle mapping into the first quadrant
  681. DWORD dwMappingAngle = dwAngle; // 0 - 9000
  682. bool bFlipSignX = false; // X is negative in 3rd and 4th quadrants
  683. bool bFlipSignY = true; // Y is negative in 1st and 4th quadrants
  684. if (dwAngle > 9000)
  685. {
  686. bFlipSignY = false;
  687. if (dwAngle > 18000)
  688. {
  689. bFlipSignX = true;
  690. if (dwAngle > 27000) // 27000 - 36000
  691. {
  692. bFlipSignY = true;
  693. dwMappingAngle = 36000 - dwAngle;
  694. }
  695. else // 18000 - 27000
  696. {
  697. dwMappingAngle = dwAngle - 18000;
  698. }
  699. }
  700. else // 9000 - 18000
  701. {
  702. dwMappingAngle = 18000 - dwAngle;
  703. }
  704. }
  705. _ASSERTE(dwMappingAngle <= 9000);
  706. DWORD quantizedEntry = dwMappingAngle / c_dwTableQuantization;
  707. DWORD quantizedAngle = quantizedEntry * c_dwTableQuantization;
  708. if (dwMappingAngle == quantizedAngle)
  709. {
  710. lXPercent = g_PercentagesArray[quantizedEntry].dwPercentageX;
  711. }
  712. else
  713. {
  714. _ASSERTE(quantizedAngle < dwMappingAngle);
  715. _ASSERTE(dwMappingAngle < 9000);
  716. DWORD lValue = g_PercentagesArray[quantizedEntry].dwPercentageX;
  717. DWORD rValue = g_PercentagesArray[quantizedEntry + 1].dwPercentageX;
  718. long int lSlope = ((rValue - lValue) * 1000)/c_dwTableQuantization;
  719. lXPercent = lValue + lSlope * (dwMappingAngle - quantizedAngle);
  720. }
  721. lYPercent = 10000 - lXPercent;
  722. if (bFlipSignX == true)
  723. {
  724. lXPercent *= -1;
  725. }
  726. if (bFlipSignY == true)
  727. {
  728. lYPercent *= -1;
  729. }
  730. }
  731. /***********************************************************************************
  732. **
  733. ** void CIDirectInputEffectDriver::DownloadEffect()
  734. **
  735. ** @func Intercepting this call gives us the chance to map the Y forces to the X
  736. ** axis. Switches off the type to determine if the mapping is done.
  737. **
  738. ** @rdesc Result of download (from lower driver)
  739. **
  740. *************************************************************************************/
  741. HRESULT __stdcall CIDirectInputEffectDriver::DownloadEffect
  742. (
  743. DWORD dwDeviceID, //@parm [IN] ID of device this is for
  744. DWORD dwInternalEffectType, //@parm [IN] Type of effect (major, minor)
  745. DWORD* pdwDnloadID, //@parm [IN, OUT] >0 - ID of effect to modify. 0 new effect ID returned
  746. DIEFFECT* pEffect, //@parm [IN, OUT] Structure containing effect information
  747. DWORD dwFlags //@parm [IN] Download flags
  748. )
  749. {
  750. myDebugOut ("CIDirectInputEffectDriver::DownloadEffect (<NOT DEBUGGED>)\n");
  751. if (pEffect == NULL)
  752. {
  753. return E_INVALIDARG;
  754. }
  755. WORD wType = WORD(dwInternalEffectType & 0x0000FFFF);
  756. bool bGainTruncation = false;
  757. // case EF_BEHAVIOR: // We don't axis-map behaviour
  758. // case EF_USER_DEFINED: // We don't axis-map user defined
  759. // case EF_RTC_SPRING: // We don't axis-map RTC spring
  760. // case EF_VFX_EFFECT: // Visual force VFX Effect!!! Danger Will Robinson!
  761. if ((m_ForceMapping.bMapYToX) && ((wType >= PID_CONSTANT_FORCE) && (wType <= PID_SAWTOOTHDOWN)))
  762. {
  763. // We don't support more than 2 axes (currently), and 0 is probably an error
  764. if ((pEffect->cAxes > 2) || (pEffect->cAxes == 0))
  765. {
  766. return E_NOTIMPL;
  767. }
  768. // We don't support sperical (3 axis force)
  769. if (pEffect->dwFlags & DIEFF_SPHERICAL)
  770. {
  771. return E_NOTIMPL; // .. since got by axis check, programmer goofed up anyway
  772. }
  773. // Are the axes reversed?
  774. bool bAxesReversed = (DIDFT_GETINSTANCE(pEffect->rgdwAxes[0]) == 1);
  775. LONG lPercentX = 0;
  776. LONG lPercentY = 0;
  777. // Polar, figure out percentage that is X and percentage that is Y
  778. if (pEffect->dwFlags & DIEFF_POLAR)
  779. {
  780. if (pEffect->cAxes == 1) // Polar coordinate must have two axes of data (because DX says so)
  781. {
  782. _RPT0(_CRT_WARN, "POLAR effect that has only one AXIS\n");
  783. // return E_INVALIDARG;
  784. }
  785. long int lEffectAngle = pEffect->rglDirection[0]; // in [0] even if reversed
  786. if (bAxesReversed == true) { // Indicates (-1, 0) as origin instead of (0, -1)
  787. lEffectAngle += 27000;
  788. }
  789. while (lEffectAngle < 0) // Make it positive
  790. {
  791. lEffectAngle += 36000;
  792. }
  793. lEffectAngle %= 36000; // Make it from 0 to 35900
  794. PercentagesFromAngle(DWORD(lEffectAngle), lPercentX, lPercentY);
  795. // Not going to bother reseting the angle, since PID.dll just sends it down and wheel ignores Y component
  796. }
  797. else if (pEffect->dwFlags & DIEFF_CARTESIAN)
  798. {
  799. // Here I remove the Y component in case PID.dll maps this to an angle.
  800. if (bAxesReversed == true)
  801. {
  802. lPercentX = pEffect->rglDirection[1];
  803. lPercentY = pEffect->rglDirection[0];
  804. pEffect->rglDirection[0] = 0;
  805. }
  806. else
  807. {
  808. lPercentX = pEffect->rglDirection[0];
  809. lPercentY = pEffect->rglDirection[1];
  810. pEffect->rglDirection[1] = 0;
  811. }
  812. LONG lTotal = abs(lPercentX) + abs(lPercentY);
  813. // DIV ZERO Bug
  814. // If both of the percentages are zero then do nothing
  815. // Jen-Hung Ho
  816. if (lTotal)
  817. {
  818. lPercentX = (lPercentX * 10000)/lTotal;
  819. if ( lPercentY > 0 )
  820. lPercentY = 10000 - abs(lPercentX);
  821. else
  822. lPercentY = abs(lPercentX) - 10000;
  823. }
  824. }
  825. else
  826. {
  827. _ASSERTE(FALSE);
  828. return E_NOTIMPL; // Some new fangled coordinate system
  829. }
  830. #if 0 // tempory remove by Jen-Hung Ho
  831. long int lContributionY = lPercentY/c_lContributionY;
  832. long int lTotal = lPercentX + lContributionY;
  833. #else
  834. long int lTotal;
  835. long int lContributionY = lPercentY/c_lContributionY;
  836. #endif
  837. // If POLAR set proper angle
  838. if (pEffect->dwFlags & DIEFF_POLAR)
  839. {
  840. // Keep as orginal code, add by Jen-Hung Ho
  841. lTotal = lPercentX + lContributionY;
  842. if (lTotal < 0)
  843. {
  844. pEffect->rglDirection[0] = (bAxesReversed == true) ? 0 : 27000;
  845. }
  846. else
  847. {
  848. pEffect->rglDirection[0] = (bAxesReversed == true) ? 18000 : 9000;
  849. }
  850. }
  851. else // Cartesian
  852. {
  853. // use X axis force to determain direction, add by Jen-Hung Ho
  854. // Y axis force follow X axis direction
  855. if ( lPercentX > 0 )
  856. lTotal = lPercentX + abs(lContributionY);
  857. else if ( lPercentX < 0 )
  858. lTotal = lPercentX - abs(lContributionY);
  859. else
  860. lTotal = lContributionY;
  861. // Already removed Y above
  862. if (bAxesReversed == true)
  863. {
  864. pEffect->rglDirection[1] = lTotal;
  865. }
  866. else
  867. {
  868. pEffect->rglDirection[0] = lTotal;
  869. }
  870. }
  871. // Allmost all the time we are changing the angle (and pid always sends it anyways)
  872. dwFlags |= DIEP_DIRECTION;
  873. // We avoid causing truncation - what if there was truncation? Need to check and return
  874. if (pEffect->dwGain > 10000)
  875. {
  876. bGainTruncation = true;
  877. }
  878. if (pEffect->dwFlags & DIEFF_POLAR)
  879. {
  880. // Modify the gain based on lPercentX and lPercentY
  881. pEffect->dwGain = pEffect->dwGain * abs(lTotal);
  882. pEffect->dwGain /= 10000; // Put back in range 0 - 10000
  883. }
  884. // Make sure we don't go out of range and cause DI_TRUNCATED to be returned from below
  885. if (pEffect->dwGain > 10000)
  886. {
  887. pEffect->dwGain = 10000;
  888. }
  889. }
  890. else // We are not mapping fix cartesian pid bug
  891. {
  892. // Cartesian
  893. if (pEffect->dwFlags & DIEFF_CARTESIAN)
  894. {
  895. short int xAxisIndex = 0;
  896. short int yAxisIndex = 1;
  897. // Are the axes reversed?
  898. if (DIDFT_GETINSTANCE(pEffect->rgdwAxes[0]) == 1)
  899. {
  900. xAxisIndex = 1;
  901. yAxisIndex = 0;
  902. }
  903. LONG lTotal = abs(pEffect->rglDirection[0]) + abs(pEffect->rglDirection[1]);
  904. // Fixup the X component so the total maginitude is base on 10K
  905. if (lTotal)
  906. {
  907. pEffect->rglDirection[xAxisIndex] = (10000 * pEffect->rglDirection[xAxisIndex])/lTotal;
  908. }
  909. // Remove the Y component to keep PID.dll from playing with it.
  910. pEffect->rglDirection[yAxisIndex] = 0;
  911. }
  912. }
  913. HRESULT hr = m_pIPIDEffectDriver->DownloadEffect(dwDeviceID, dwInternalEffectType, pdwDnloadID, pEffect, dwFlags);
  914. if ((hr == S_OK) && (bGainTruncation == true))
  915. {
  916. hr = DI_TRUNCATED;
  917. }
  918. return hr;
  919. }
  920. HRESULT __stdcall CIDirectInputEffectDriver::DestroyEffect
  921. (
  922. DWORD dwDeviceID,
  923. DWORD dwDnloadID
  924. )
  925. {
  926. myDebugOut ("CIDirectInputEffectDriver::DestroyEffect Enter(dwDeviceID:%d, dwDnloadID:%d)\n",
  927. dwDeviceID, dwDnloadID);
  928. HRESULT hr = m_pIPIDEffectDriver->DestroyEffect(dwDeviceID, dwDnloadID);
  929. myDebugOut ("CIDirectInputEffectDriver::DestroyEffect Exit (hr:0x%08p)\n", hr);
  930. return hr;
  931. }
  932. HRESULT __stdcall CIDirectInputEffectDriver::StartEffect
  933. (
  934. DWORD dwDeviceID,
  935. DWORD dwDnloadID,
  936. DWORD dwMode,
  937. DWORD dwIterations
  938. )
  939. {
  940. myDebugOut ("CIDirectInputEffectDriver::StartEffect (<NOT DEBUGGED>)\n");
  941. return m_pIPIDEffectDriver->StartEffect(dwDeviceID, dwDnloadID, dwMode, dwIterations);
  942. }
  943. HRESULT __stdcall CIDirectInputEffectDriver::StopEffect
  944. (
  945. DWORD dwDeviceID,
  946. DWORD dwDnloadID
  947. )
  948. {
  949. myDebugOut ("CIDirectInputEffectDriver::StopEffect (<NOT DEBUGGED>)\n");
  950. return m_pIPIDEffectDriver->StopEffect(dwDeviceID, dwDnloadID);
  951. }
  952. HRESULT __stdcall CIDirectInputEffectDriver::GetEffectStatus
  953. (
  954. DWORD dwDeviceID,
  955. DWORD dwDnloadID,
  956. DWORD* pdwStatusCode
  957. )
  958. {
  959. myDebugOut ("CIDirectInputEffectDriver::GetEffectStatus (<NOT DEBUGGED>)\n");
  960. return m_pIPIDEffectDriver->GetEffectStatus(dwDeviceID, dwDnloadID, pdwStatusCode);
  961. }
  962. DWORD __stdcall DoWaitForForceSchemeChange(void* pParameter)
  963. {
  964. myDebugOut ("CIDirectInputEffectDriver DoWaitForForceSchemeChange (pParameter: 0x%08p)\n", pParameter);
  965. CIDirectInputEffectDriver* pIDirectInputEffectDriver = (CIDirectInputEffectDriver*)pParameter;
  966. //TODO remove this it could be really slow!
  967. if (IsBadReadPtr ((const void*)pParameter, sizeof CIDirectInputEffectDriver))
  968. {
  969. myDebugOut ("CIDirectInputEffectDriver DoWaitForForceSchemeChange pParameter is not a valid read ptr!\n");
  970. }
  971. if (pIDirectInputEffectDriver != NULL)
  972. {
  973. pIDirectInputEffectDriver->WaitForForceSchemeChange();
  974. }
  975. return 0;
  976. }
  977. /***********************************************************************************
  978. **
  979. ** void CIDirectInputEffectDriver::WaitForForceSchemeChange()
  980. **
  981. ** @func Thread waits on the Event signal for force scheme change until the object goes away.
  982. ** If event is signaled, WaitForForceSchemeChange() is called
  983. **
  984. ** @rdesc Nothing
  985. **
  986. *************************************************************************************/
  987. void CIDirectInputEffectDriver::WaitForForceSchemeChange()
  988. {
  989. _ASSERTE(m_hKernelDeviceDriverDuplicate != NULL);
  990. if (IsBadReadPtr ((const void*)this, sizeof CIDirectInputEffectDriver))
  991. {
  992. myDebugOut ("CIDirectInputEffectDriver WaitForForceSchemeChange is not a valid read ptr!\n");
  993. }
  994. FORCE_BLOCK forceMap;
  995. DWORD dwReturnDataSize = 0;
  996. for (;m_ulReferenceCount != 0;)
  997. {
  998. // Set up the IOCTL
  999. BOOL bRet = ::DeviceIoControl(m_hKernelDeviceDriverDuplicate, IOCTL_GCK_NOTIFY_FF_SCHEME_CHANGE,
  1000. (void*)(&m_dwGcKernelDevice), sizeof(DWORD), // In
  1001. (void*)(&forceMap), sizeof(forceMap), &dwReturnDataSize, // Out
  1002. NULL);
  1003. _RPT0(_CRT_WARN, "Returned from Scheme Change!\n");
  1004. if ((m_ulReferenceCount != 0) && (bRet != FALSE) && (dwReturnDataSize == sizeof(forceMap)))
  1005. {
  1006. // Need a mutext here
  1007. m_ForceMapping = forceMap;
  1008. SendSpringChange();
  1009. SetGain(m_dwInternalDeviceID, 10000);
  1010. }
  1011. else
  1012. { // We are done
  1013. ::CloseHandle(m_hKernelDeviceDriverDuplicate);
  1014. m_hKernelDeviceDriverDuplicate = NULL;
  1015. ExitThread(2);
  1016. }
  1017. }
  1018. }