Source code of Windows XP (NT5)
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.

614 lines
16 KiB

  1. /*****************************************************************************
  2. *
  3. * DIEmH.c
  4. *
  5. * Copyright (c) 1996 Microsoft Corporation. All Rights Reserved.
  6. *
  7. * Abstract:
  8. *
  9. * Emulation module for HID. HID is always run at ring 3,
  10. * so "emulation" is a bit of a misnomer.
  11. *
  12. * Contents:
  13. *
  14. * CEm_HID_CreateInstance
  15. *
  16. *****************************************************************************/
  17. #include "dinputpr.h"
  18. /*****************************************************************************
  19. *
  20. * The sqiffle for this file.
  21. *
  22. *****************************************************************************/
  23. #define sqfl sqflEm
  24. /*****************************************************************************
  25. *
  26. * Forward declarations
  27. *
  28. * CEm_HID_ReadComplete and CEm_HID_IssueRead schedule each other
  29. * back and forth.
  30. *
  31. *****************************************************************************/
  32. void CALLBACK
  33. CEm_HID_ReadComplete(DWORD dwError, DWORD cbRead, LPOVERLAPPED po);
  34. /*****************************************************************************
  35. *
  36. * HID "emulation"
  37. *
  38. *****************************************************************************/
  39. STDMETHODIMP CEm_HID_Acquire(PEM this, BOOL fAcquire);
  40. /*****************************************************************************
  41. *
  42. * @doc INTERNAL
  43. *
  44. * @func BOOL | FakeCancelIO |
  45. *
  46. * Stub function which doesn't do anything but
  47. * keeps us from crashing.
  48. *
  49. * @parm HANDLE | h |
  50. *
  51. * The handle whose I/O is supposed to be cancelled.
  52. *
  53. *****************************************************************************/
  54. BOOL WINAPI
  55. FakeCancelIO(HANDLE h)
  56. {
  57. AssertF(0);
  58. return FALSE;
  59. }
  60. /*****************************************************************************
  61. *
  62. * @doc INTERNAL
  63. *
  64. * @func BOOL | FakeTryEnterCriticalSection |
  65. *
  66. * We use TryEnterCriticalSection in DEBUG to detect deadlock
  67. * If the function does not exist, just enter CritSection and report
  68. * true. This compromises some debug functionality.
  69. *
  70. * @parm LPCRITICAL_SECTION | lpCriticalSection |
  71. *
  72. * Address of Critical Section to be entered.
  73. *
  74. *****************************************************************************/
  75. #ifdef XDEBUG
  76. BOOL WINAPI
  77. FakeTryEnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection)
  78. {
  79. EnterCriticalSection(lpCriticalSection);
  80. return TRUE;
  81. }
  82. #endif
  83. /*****************************************************************************
  84. *
  85. * @doc INTERNAL
  86. *
  87. * @func void | CEm_HID_Hold |
  88. *
  89. * Place a hold on both the parent device and the
  90. * emulation structure, so neither will go away while
  91. * we aren't paying attention.
  92. *
  93. * @parm PCHID | this |
  94. *
  95. * The item to be held.
  96. *
  97. *****************************************************************************/
  98. void INTERNAL
  99. CEm_Hid_Hold(PCHID this)
  100. {
  101. CEm_AddRef(pemFromPvi(this->pvi));
  102. Common_Hold(this);
  103. }
  104. /*****************************************************************************
  105. *
  106. * @doc INTERNAL
  107. *
  108. * @func void | CEm_HID_Unhold |
  109. *
  110. * Release the holds we placed via <f CEm_HID_Hold>.
  111. *
  112. * @parm PCHID | this |
  113. *
  114. * The item to be unheld.
  115. *
  116. *****************************************************************************/
  117. void INTERNAL
  118. CEm_Hid_Unhold(PCHID this)
  119. {
  120. CEm_Release(pemFromPvi(this->pvi));
  121. Common_Unhold(this);
  122. }
  123. /*****************************************************************************
  124. *
  125. * @doc EXTERNAL
  126. *
  127. * @func BOOL | CEm_HID_IssueRead |
  128. *
  129. * Issue another read request.
  130. *
  131. * @parm PCHID | this |
  132. *
  133. * The device on which the read is to be issued.
  134. *
  135. * @returns
  136. *
  137. * Returns nonzero if the read was successfully issued.
  138. *
  139. *****************************************************************************/
  140. BOOL EXTERNAL
  141. CEm_HID_IssueRead(PCHID this)
  142. {
  143. BOOL fRc;
  144. fRc = ReadFileEx(this->hdevEm, this->hriIn.pvReport,
  145. this->hriIn.cbReport, &this->o,
  146. CEm_HID_ReadComplete);
  147. if(!fRc)
  148. {
  149. /*
  150. * Couldn't issue read; force an unacquire.
  151. *
  152. * Unhold the device once, since the read loop is gone.
  153. */
  154. // 7/18/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers.
  155. SquirtSqflPtszV(sqfl | sqflError,
  156. TEXT("IssueRead: Access to HID device(%p, handle=0x%x) lost le=0x%x!"),
  157. this, this->hdevEm, GetLastError() );
  158. DllEnterCrit();
  159. ConfirmF(SUCCEEDED(GPA_DeletePtr(&g_plts->gpaHid, pemFromPvi(this->pvi))));
  160. DllLeaveCrit();
  161. CEm_ForceDeviceUnacquire(&this->ed,
  162. (!(this->pvi->fl & VIFL_ACQUIRED)) ? FDUFL_UNPLUGGED : 0);
  163. CEm_Hid_Unhold(this);
  164. // 7/19/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers.
  165. SquirtSqflPtszV(sqfl | sqflVerbose,
  166. TEXT("Removed HID device(%p) from GPA "), this);
  167. }
  168. return fRc;
  169. }
  170. /*****************************************************************************
  171. *
  172. * @doc INTERNAL
  173. *
  174. * @func void | CEm_HID_PrepareState |
  175. *
  176. * Prepare the staging area for a new device state
  177. * by assuming that nothing has changed.
  178. *
  179. * @parm PCHID | this |
  180. *
  181. * The device on which a read has just completed.
  182. *
  183. *****************************************************************************/
  184. void INLINE
  185. CEm_HID_PrepareState(PCHID this)
  186. {
  187. /*
  188. * Copy over everything...
  189. */
  190. CopyMemory(this->pvStage, this->pvPhys, this->cbPhys);
  191. }
  192. /*****************************************************************************
  193. *
  194. * @doc INTERNAL
  195. *
  196. * @func void | CEm_HID_ReadComplete |
  197. *
  198. * APC function which is called when an I/O has completed.
  199. *
  200. * @parm DWORD | dwError |
  201. *
  202. * Error code, or zero on success.
  203. *
  204. * @parm DWORD | cbRead |
  205. *
  206. * Number of bytes actually read.
  207. *
  208. * @parm LPOVERLAPPED | po |
  209. *
  210. * I/O packet that completed.
  211. *
  212. *****************************************************************************/
  213. void CALLBACK
  214. CEm_HID_ReadComplete(DWORD dwError, DWORD cbRead, LPOVERLAPPED po)
  215. {
  216. PCHID this = pchidFromPo(po);
  217. //EnterProc(Cem_HID_ReadComplete, (_"ddp", dwError, cbRead, po ));
  218. /*
  219. * Cannot own any critical sections because CEm_ForceDeviceUnacquire
  220. * assumes that no critical sections are taken.
  221. */
  222. AssertF(!CDIDev_InCrit(this->pvi->pdd));
  223. AssertF(!DllInCrit());
  224. /*
  225. * Process the data.
  226. *
  227. * Note: We can get error STATUS_DEVICE_NOT_CONNECTED
  228. * or ERROR_READ_FAULT if the device is unplugged.
  229. */
  230. if(dwError == 0 &&
  231. this->o.InternalHigh == this->caps.InputReportByteLength)
  232. {
  233. NTSTATUS stat;
  234. CEm_HID_PrepareState(this);
  235. stat = CHid_ParseData(this, HidP_Input, &this->hriIn);
  236. if(SUCCEEDED(stat))
  237. {
  238. CEm_AddState(&this->ed, this->pvStage, GetTickCount());
  239. }
  240. CEm_HID_IssueRead(this);
  241. } else
  242. {
  243. if(!dwError)
  244. {
  245. // 7/19/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers.
  246. SquirtSqflPtszV(sqflError | sqfl,
  247. TEXT("ReadComplete HID(%p) short read! Got %d wanted %d"),
  248. this,
  249. this->o.InternalHigh,
  250. this->caps.InputReportByteLength);
  251. } else
  252. {
  253. // 7/19/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers.
  254. SquirtSqflPtszV(sqflError | sqfl,
  255. TEXT("ReadComplete HID(%p) read failed! error=0x%08x "),
  256. this, dwError);
  257. }
  258. DllEnterCrit();
  259. ConfirmF(SUCCEEDED(GPA_DeletePtr(&g_plts->gpaHid, pemFromPvi(this->pvi))));
  260. DllLeaveCrit();
  261. CEm_ForceDeviceUnacquire(&this->ed,
  262. (!(this->pvi->fl & VIFL_ACQUIRED)) ? FDUFL_UNPLUGGED : 0);
  263. CEm_Hid_Unhold(this);
  264. }
  265. /*
  266. * And wait for more data.
  267. * If the read failed, then CEm_HID_IssueRead() will its Reference
  268. */
  269. // CEm_HID_IssueRead(this);
  270. //ExitProc();
  271. }
  272. /*****************************************************************************
  273. *
  274. * @doc INTERNAL
  275. *
  276. * @func void | CEm_HID_Sync |
  277. *
  278. * Kick off a read or kill the existing one.
  279. *
  280. * @parm PLLTHREADSTATE | plts |
  281. *
  282. * Thread hook state containing hook information to synchronize.
  283. *
  284. * @parm PEM | pem |
  285. *
  286. * Who is the poor victim?
  287. *
  288. *****************************************************************************/
  289. void EXTERNAL
  290. CEm_HID_Sync(PLLTHREADSTATE plts, PEM pem)
  291. {
  292. PCHID this;
  293. EnterProc(CEm_HID_Sync, (_ "pp", plts, pem ));
  294. this = pchidFromPem(pem);
  295. AssertF(GPA_FindPtr(&plts->gpaHid, pem));
  296. AssertF(this->pvi == &pem->vi);
  297. AssertF(pem->ped == &this->ed);
  298. /*
  299. * Cannot own any critical sections because CEm_HID_IssueRead
  300. * may result in a call to CEm_ForceDeviceUnacquire, which
  301. * in turn assumes that no critical sections are taken.
  302. */
  303. AssertF(!CDIDev_InCrit(this->pvi->pdd));
  304. AssertF(!DllInCrit());
  305. if( pem->vi.fl & VIFL_ACQUIRED )
  306. {
  307. AssertF(this->hdevEm == INVALID_HANDLE_VALUE);
  308. /*
  309. * Start reading.
  310. *
  311. * While underneath the device critical section, duplicate
  312. * the handle so we can avoid race conditions with the
  313. * main thread (when the main thread closes the handle,
  314. * we need to keep our private version alive so we can
  315. * clean it up nicely).
  316. */
  317. /*
  318. * Need to look again, in case the device has already
  319. * been unacquired before we get a chance to synchronize
  320. * with the main thread. This can happen, for example,
  321. * if the app quickly does an Acquire/Unacquire without
  322. * an intervening thread switch.
  323. */
  324. AssertF(!CDIDev_InCrit(this->pvi->pdd));
  325. //CDIDev_EnterCrit(this->pvi->pdd);
  326. if(this->hdev != INVALID_HANDLE_VALUE)
  327. {
  328. HANDLE hProcessMe = GetCurrentProcess();
  329. HANDLE hdevEm;
  330. if(DuplicateHandle(hProcessMe, this->hdev,
  331. hProcessMe, &hdevEm, GENERIC_READ,
  332. 0, 0))
  333. {
  334. this->hdevEm = hdevEm;
  335. }
  336. }
  337. //CDIDev_LeaveCrit(this->pvi->pdd);
  338. if(this->hdevEm != INVALID_HANDLE_VALUE)
  339. {
  340. /*
  341. * On Win98, HidD_FlushQueue will fail if the underlying
  342. * device is dead. Whereas on NT, it blindly succeeds.
  343. * Therefore, we cannot trust the return value.
  344. */
  345. HidD_FlushQueue(this->hdevEm);
  346. }
  347. /*
  348. * Even if we have failed to duplicate the handle
  349. * we still want to issue the read. A error in read
  350. * will force the device to be unacquired
  351. */
  352. CEm_HID_IssueRead(this);
  353. // 7/19/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers.
  354. SquirtSqflPtszV(sqfl | sqflVerbose,
  355. TEXT(" StartReading(%p) "),
  356. this);
  357. } else
  358. {
  359. HANDLE hdev;
  360. /*
  361. * Stop reading. There is still another outstanding
  362. * hold by the read loop, which will be cleaned up when
  363. * the the I/O cancel is received.
  364. */
  365. AssertF(this->hdevEm != INVALID_HANDLE_VALUE);
  366. hdev = this->hdevEm;
  367. this->hdevEm = INVALID_HANDLE_VALUE;
  368. if(hdev != INVALID_HANDLE_VALUE)
  369. {
  370. /*
  371. * We don't need to call CancelIo because we're closing
  372. * the handle soon anyway. Which is good, because Memphis
  373. * B#55771 prevents CancelIo from working on read-only
  374. * handles (which we are).
  375. *
  376. */
  377. /* Need CancelIo on NT otherwise HID devices appear only on every
  378. * consecutive plug in
  379. */
  380. _CancelIO(hdev);
  381. CloseHandle(hdev);
  382. }
  383. // 7/19/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers.
  384. SquirtSqflPtszV(sqfl | sqflVerbose,
  385. TEXT(" StopReading(%p) "),
  386. this);
  387. }
  388. ExitProc();
  389. }
  390. /*****************************************************************************
  391. *
  392. * @doc INTERNAL
  393. *
  394. * @func HRESULT | CEm_HID_Acquire |
  395. *
  396. * Acquire/unacquire a HID device.
  397. *
  398. * @parm PEM | pem |
  399. *
  400. * Device being acquired.
  401. *
  402. * @parm BOOL | fAcquire |
  403. *
  404. * Whether the device is being acquired or unacquired.
  405. *
  406. *****************************************************************************/
  407. STDMETHODIMP
  408. CEm_HID_Acquire(PEM pem, BOOL fAcquire)
  409. {
  410. HRESULT hres;
  411. PLLTHREADSTATE plts;
  412. PCHID pchid;
  413. EnterProc(CEm_HID_Acquire, (_ "pu", pem, fAcquire));
  414. AssertF(pem->dwSignature == CEM_SIGNATURE);
  415. pchid = pchidFromPem(pem);
  416. if( fAcquire )
  417. {
  418. pchid->hdev = CHid_OpenDevicePath(pchid, FILE_FLAG_OVERLAPPED);
  419. if(pchid->hdev != INVALID_HANDLE_VALUE )
  420. {
  421. hres = S_OK;
  422. } else {
  423. hres = DIERR_UNPLUGGED;
  424. }
  425. } else
  426. {
  427. AssertF(pchid->hdev != INVALID_HANDLE_VALUE);
  428. _CancelIO(pchid->hdev);
  429. CloseHandle(pchid->hdev);
  430. pchid->hdev = INVALID_HANDLE_VALUE;
  431. hres = S_OK;
  432. }
  433. if( pchid->IsPolledInput )
  434. {
  435. hres = S_OK;
  436. AssertF(pchid->hdevEm == INVALID_HANDLE_VALUE);
  437. } else if( SUCCEEDED(hres) )
  438. {
  439. #ifdef USE_WM_INPUT
  440. ResetEvent( g_hEventHid );
  441. #endif
  442. hres = CEm_GetWorkerThread(pem, &plts);
  443. if(SUCCEEDED(hres) )
  444. {
  445. if(fAcquire )
  446. { /* Begin the I/O */
  447. /*
  448. * Must apply the hold before adding to the list
  449. * to avoid a race condition where the worker thread
  450. * unholds the pchid before we can hold it.
  451. *
  452. * The rule is that there is a hold to track each copy
  453. * of the device on the gpaHid.
  454. */
  455. CEm_Hid_Hold(pchid);
  456. /*
  457. * Add ourselves to the busy list, and wake up
  458. * the worker thread to tell him to start paying attention.
  459. */
  460. DllEnterCrit();
  461. hres = GPA_Append(&plts->gpaHid, pem);
  462. DllLeaveCrit();
  463. // 7/19/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers.
  464. SquirtSqflPtszV(sqfl | sqflVerbose,
  465. TEXT("Added HID device(%p) to GPA "), pchid );
  466. if(FAILED(hres))
  467. {
  468. CEm_Hid_Unhold(pchid);
  469. }
  470. NudgeWorkerThreadPem(plts, pem);
  471. #ifdef USE_WM_INPUT
  472. if( g_fRawInput ) {
  473. DWORD dwRc;
  474. dwRc = WaitForSingleObject( g_hEventHid, INFINITE );
  475. }
  476. #endif
  477. } else
  478. {
  479. HANDLE hdev;
  480. hdev = pchid->hdevEm;
  481. pchid->hdevEm = INVALID_HANDLE_VALUE;
  482. if(hdev != INVALID_HANDLE_VALUE)
  483. {
  484. _CancelIO(hdev);
  485. CloseHandle(hdev);
  486. }
  487. }
  488. }
  489. }
  490. ExitOleProc();
  491. return hres;
  492. }
  493. /*****************************************************************************
  494. *
  495. * @doc INTERNAL
  496. *
  497. * @func HRESULT | CEm_HID_CreateInstance |
  498. *
  499. * Create a HID thing.
  500. *
  501. * @parm PVXDDEVICEFORMAT | pdevf |
  502. *
  503. * What the object should look like.
  504. *
  505. * @parm PVXDINSTANCE * | ppviOut |
  506. *
  507. * The answer goes here.
  508. *
  509. *****************************************************************************/
  510. HRESULT EXTERNAL
  511. CEm_HID_CreateInstance(PVXDDEVICEFORMAT pdevf, PVXDINSTANCE *ppviOut)
  512. {
  513. PCHID pchid = (PCHID)pdevf->dwExtra;
  514. PED ped = &pchid->ed;
  515. AssertF(ped->pState == 0);
  516. AssertF(ped->pDevType == 0);
  517. *(PPV)&ped->pState = pchid->pvPhys; /* De-const */
  518. ped->Acquire = CEm_HID_Acquire;
  519. ped->cAcquire = -1;
  520. ped->cbData = pdevf->cbData;
  521. ped->cRef = 0x0;
  522. return CEm_CreateInstance(pdevf, ppviOut, &pchid->ed);
  523. }