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.

993 lines
28 KiB

  1. /*****************************************************************************
  2. * PidHid.c
  3. *
  4. * Copyright (c) 1999 Microsoft Corporation. All Rights Reserved.
  5. *
  6. * Abstract:
  7. *
  8. * HID utility routines for PID .
  9. *
  10. *****************************************************************************/
  11. #include "PidPr.h"
  12. #define sqfl ( sqflHid )
  13. /*****************************************************************************
  14. *
  15. * PID_GetReportId
  16. *
  17. * Obtain the HID report ID given the usage, usagePage and LinkCollection
  18. *
  19. * IDirectInputEffectDriver | ped |
  20. *
  21. * The effect driver interface
  22. *
  23. * PPIDREPORT | pPidReport |
  24. *
  25. * Address of PIDREPORT structure
  26. *
  27. * USHORT | uLinkCollection |
  28. *
  29. * LinkCollection ID
  30. *
  31. * OUT UCHAR * | pReportId |
  32. *
  33. * Report ID. Undefined if unsuccessful
  34. *
  35. * Returns:
  36. *
  37. * HRESULT
  38. * Error code
  39. *
  40. *****************************************************************************/
  41. STDMETHODIMP
  42. PID_GetReportId
  43. (
  44. IDirectInputEffectDriver *ped,
  45. PPIDREPORT pPidReport,
  46. USHORT uLinkCollection,
  47. UCHAR* pReportId
  48. )
  49. {
  50. CPidDrv *this = (CPidDrv *)ped;
  51. HRESULT hres = S_OK;
  52. EnterProcI(PID_GetReportId, (_"xxxx", ped, pPidReport, pReportId));
  53. if( SUCCEEDED(hres) )
  54. {
  55. HIDP_VALUE_CAPS ValCaps;
  56. USHORT cAValCaps = 0x1;
  57. USAGE Usage = DIGETUSAGE(pPidReport->rgPidUsage->dwUsage);
  58. USAGE UsagePage = DIGETUSAGEPAGE(pPidReport->rgPidUsage->dwUsage);
  59. hres = HidP_GetSpecificValueCaps
  60. (
  61. pPidReport->HidP_Type,
  62. UsagePage,
  63. uLinkCollection,
  64. Usage,
  65. &ValCaps,
  66. &cAValCaps,
  67. this->ppd
  68. );
  69. // If the report has no values, only buttons
  70. if(hres == HIDP_STATUS_USAGE_NOT_FOUND )
  71. {
  72. // Guarded Laziness
  73. CAssertF(cbX(HIDP_VALUE_CAPS) == cbX(HIDP_BUTTON_CAPS) );
  74. CAssertF(FIELD_OFFSET(HIDP_VALUE_CAPS, ReportID) == FIELD_OFFSET(HIDP_BUTTON_CAPS, ReportID) );
  75. hres = HidP_GetSpecificButtonCaps
  76. (
  77. pPidReport->HidP_Type,
  78. UsagePage,
  79. uLinkCollection,
  80. 0x0,
  81. (PHIDP_BUTTON_CAPS)&ValCaps,
  82. &cAValCaps,
  83. this->ppd
  84. );
  85. }
  86. if( SUCCEEDED(hres ) || ( hres == HIDP_STATUS_BUFFER_TOO_SMALL) )
  87. {
  88. (*pReportId) = ValCaps.ReportID;
  89. hres = S_OK;
  90. } else
  91. {
  92. SquirtSqflPtszV(sqfl | sqflError,
  93. TEXT("%s: FAIL HidP_GetValCaps for CollectionId%d (%x %x:%s) "),
  94. s_tszProc, uLinkCollection, UsagePage, Usage, PIDUSAGETXT(UsagePage,Usage) );
  95. }
  96. }
  97. ExitOleProc();
  98. return hres;
  99. }
  100. /*****************************************************************************
  101. *
  102. * PID_GetCollectionIndex
  103. *
  104. * Obtain the collection index for collection usage page & usage.
  105. *
  106. *
  107. * The external joystick number being addressed.
  108. *
  109. * dwEffect
  110. *
  111. * The effect to be queried.
  112. *
  113. * pdwStatus
  114. *
  115. * Receives the effect status in the form of zero
  116. * or more DIEGES_* flags.
  117. *
  118. * Returns:
  119. * Collection Index ( 0 .. NumberLinkCollectionNodes -1 ) on success
  120. * 0x0 on failure
  121. *
  122. *****************************************************************************/
  123. STDMETHODIMP
  124. PID_GetLinkCollectionIndex
  125. (
  126. IDirectInputEffectDriver *ped,
  127. USAGE UsagePage,
  128. USAGE Collection,
  129. USHORT Parent,
  130. PUSHORT puLinkCollection )
  131. {
  132. CPidDrv *this = (CPidDrv *)ped;
  133. USHORT indx;
  134. HRESULT hres;
  135. PHIDP_LINK_COLLECTION_NODE pLinkCollection;
  136. EnterProcI(PID_GetLinkCollectionIndex, (_"xxxxx", this, UsagePage, Collection, Parent, puLinkCollection));
  137. hres = DIERR_PID_USAGENOTFOUND;
  138. *puLinkCollection = 0x0;
  139. for(indx = 0x0, pLinkCollection = this->pLinkCollection;
  140. indx < this->caps.NumberLinkCollectionNodes;
  141. indx++, pLinkCollection++ )
  142. {
  143. if( pLinkCollection->LinkUsagePage == UsagePage &&
  144. pLinkCollection->LinkUsage == Collection )
  145. {
  146. if( Parent && Parent != pLinkCollection->Parent )
  147. {
  148. continue;
  149. }
  150. *puLinkCollection = indx;
  151. hres = S_OK;
  152. break;
  153. }
  154. }
  155. if( FAILED(hres) )
  156. {
  157. SquirtSqflPtszV(sqfl | sqflVerbose,
  158. TEXT("%s: FAIL No LinkCollection for (%x %x:%s) "),
  159. s_tszProc, UsagePage, Collection, PIDUSAGETXT(UsagePage,Collection) );
  160. }else
  161. {
  162. SquirtSqflPtszV(sqfl | sqflVerbose,
  163. TEXT("%s: LinkCollection for (%x %x:%s)=%x "),
  164. s_tszProc, UsagePage, Collection, PIDUSAGETXT(UsagePage,Collection), *puLinkCollection );
  165. }
  166. ExitBenignOleProc();
  167. return (hres) ;
  168. }
  169. //Helper fn to tell us when we're dealing w/ an "absolute" usage, since they require special handling
  170. BOOL PID_IsUsageAbsoluteLike
  171. (
  172. IDirectInputEffectDriver *ped,
  173. USHORT Usage
  174. )
  175. {
  176. //"Absolute-like" usages need special handling,
  177. //since we can't simply translate data into logical units by scaling
  178. //but need to calculate exponent, etc.
  179. //and then use special procedure to set the values
  180. //"Absolute" usages are all time usages as well as trigger button usages
  181. if ((Usage == HID_USAGE_PID_DURATION) || (Usage ==HID_USAGE_PID_SAMPLE_PERIOD ) ||
  182. (Usage == HID_USAGE_PID_TRIGGER_REPEAT_INTERVAL) || (Usage == HID_USAGE_PID_START_DELAY) ||
  183. (Usage == HID_USAGE_PID_ATTACK_TIME ) ||(Usage == HID_USAGE_PID_FADE_TIME) ||
  184. (Usage == HID_USAGE_PID_PERIOD) || (Usage == HID_USAGE_PID_TRIGGER_BUTTON))
  185. {
  186. return TRUE;
  187. }
  188. return FALSE;
  189. }
  190. //Helper fn to tell us when we're dealing w/ a magnitude that can be both positive and negative,
  191. //since we have to scale those differently
  192. BOOL PID_IsUsagePositiveNegative
  193. (
  194. IDirectInputEffectDriver *ped,
  195. USHORT Usage,
  196. USHORT LinkCollection
  197. )
  198. {
  199. BOOL isPosNeg = FALSE;
  200. //All the usages corresponding to the structures given by LONGs can be positive or negative.
  201. //Exception is the direction / angle, which should already be scaled into the range 0 - 360 * DI_DEGREES,
  202. //so should be treated as only positive.
  203. //Another exception is DICONDITION.lDeadband, which is defined as a LONG, but our headers
  204. //say it can only be in the range 0 to DI_FFNOMINALMAX.
  205. if ((Usage == HID_USAGE_PID_CP_OFFSET) ||
  206. (Usage == HID_USAGE_PID_POSITIVE_COEFFICIENT) || (Usage == HID_USAGE_PID_NEGATIVE_COEFFICIENT) ||
  207. (Usage == HID_USAGE_PID_RAMP_START) ||(Usage == HID_USAGE_PID_RAMP_END) ||
  208. (Usage == HID_USAGE_PID_OFFSET))
  209. {
  210. isPosNeg = TRUE;
  211. }
  212. //Magnitude of the constant force and the magnitude of the periodic force are defined to be the same thing,
  213. //but only the constant force magnitude can be both positive and negative.
  214. //To distinguish them, need to look at the collection.
  215. //Get constant force's collection and compare.
  216. if (Usage == HID_USAGE_PID_MAGNITUDE)
  217. {
  218. USHORT ConstCollection = 0x0;
  219. PID_GetLinkCollectionIndex(ped, g_Constant.UsagePage, g_Constant.Collection, 0x0, &ConstCollection);
  220. if (LinkCollection == ConstCollection)
  221. {
  222. isPosNeg = TRUE;
  223. }
  224. }
  225. return isPosNeg;
  226. }
  227. STDMETHODIMP
  228. PID_PackValue
  229. (
  230. IDirectInputEffectDriver *ped,
  231. PPIDREPORT pPidReport,
  232. USHORT LinkCollection,
  233. PVOID pvData,
  234. UINT cbData,
  235. PCHAR pReport,
  236. ULONG cbReport
  237. )
  238. {
  239. CPidDrv *this = (CPidDrv *)ped;
  240. HRESULT hres;
  241. PPIDUSAGE pPidUsage;
  242. UINT indx;
  243. EnterProcI( PID_PackValue, (_"xxxxxxx", ped, pPidReport, LinkCollection, pvData, cbData, pReport, cbReport));
  244. hres = S_OK;
  245. // Loop over all data values in the PID Report
  246. for(indx = 0x0, pPidUsage = pPidReport->rgPidUsage;
  247. indx < pPidReport->cAPidUsage;
  248. indx++, pPidUsage++ )
  249. {
  250. // Make sure the offsets are valid
  251. if( pPidUsage->DataOffset < cbData )
  252. {
  253. LONG lValue;
  254. NTSTATUS ntStat;
  255. USHORT Usage = DIGETUSAGE(pPidUsage->dwUsage);
  256. USHORT UsagePage = DIGETUSAGEPAGE(pPidUsage->dwUsage);
  257. lValue = *((LONG*)((UCHAR*)pvData+pPidUsage->DataOffset));
  258. ntStat = HidP_SetScaledUsageValue
  259. (
  260. pPidReport->HidP_Type,
  261. UsagePage,
  262. LinkCollection,
  263. Usage,
  264. lValue,
  265. this->ppd,
  266. pReport,
  267. cbReport
  268. );
  269. if( FAILED(ntStat) )
  270. {
  271. // HidP_SetScaledUsageValue FAILED
  272. SquirtSqflPtszV(sqfl | sqflBenign,
  273. TEXT("%s: FAIL HidP_SetScaledUsageValue:0x%x for(%x,%x,%x:%s)=0x%x "),
  274. s_tszProc, ntStat,
  275. LinkCollection, UsagePage, Usage,
  276. PIDUSAGETXT(UsagePage,Usage),
  277. lValue );
  278. // Try to set the unscaled value to get something that might make sense
  279. if( ntStat != HIDP_STATUS_USAGE_NOT_FOUND )
  280. {
  281. lValue = -1;
  282. // The range could be messed up.
  283. ntStat = HidP_SetUsageValue
  284. (
  285. pPidReport->HidP_Type,
  286. UsagePage,
  287. LinkCollection,
  288. Usage,
  289. lValue,
  290. this->ppd,
  291. pReport,
  292. cbReport
  293. );
  294. if(FAILED(ntStat) )
  295. {
  296. SquirtSqflPtszV(sqfl | sqflBenign,
  297. TEXT("%s: FAIL HidP_SetUsageValue:0x%x for(%x,%x,%x:%s)=0x%x "),
  298. s_tszProc, ntStat,
  299. LinkCollection, UsagePage, Usage,
  300. PIDUSAGETXT(UsagePage,Usage),
  301. lValue );
  302. }
  303. }
  304. } else
  305. {
  306. SquirtSqflPtszV(sqfl | sqflVerbose,
  307. TEXT("%s: HidP_SetScaledUsageValue:0x%x for(%x,%x,%x:%s)=0x%x "),
  308. s_tszProc, ntStat,
  309. LinkCollection, UsagePage, Usage,
  310. PIDUSAGETXT(UsagePage,Usage),
  311. lValue );
  312. }
  313. } else
  314. {
  315. //SquirtSqflPtszV(sqfl | sqflBenign,
  316. // TEXT("%s: FAIL Invalid Offset(%d), max(%d) "),
  317. // s_tszProc, pPidUsage->DataOffset, cbData );
  318. }
  319. }
  320. ExitOleProc();
  321. return hres;
  322. }
  323. //blocking version -- used for creating a new effect or destroying an effect, and for custom forces
  324. STDMETHODIMP
  325. PID_SendReportBl
  326. (
  327. IDirectInputEffectDriver *ped,
  328. PUCHAR pReport,
  329. UINT cbReport,
  330. HIDP_REPORT_TYPE HidP_Type
  331. )
  332. {
  333. CPidDrv *this = (CPidDrv *)ped;
  334. HRESULT hres;
  335. EnterProcI( PID_SendReportBl, (_"xxxx", ped, pReport, cbReport, HidP_Type));
  336. hres = S_OK;
  337. if( HidP_Type == HidP_Output )
  338. {
  339. BOOL frc;
  340. UINT cbWritten;
  341. frc = WriteFile (this->hdev,
  342. pReport,
  343. cbReport,
  344. &cbWritten,
  345. NULL);
  346. if( frc != TRUE || cbWritten != cbReport )
  347. {
  348. LONG lrc = GetLastError();
  349. hres = hresLe(lrc);
  350. SquirtSqflPtszV(sqfl | sqflError,
  351. TEXT("%s: FAIL WriteFile():%d (cbWritten(0x%x) cbReport(0x%x) Le(0x%x)"),
  352. s_tszProc, frc, cbWritten, cbReport, lrc );
  353. }
  354. } else if( HidP_Type == HidP_Feature )
  355. {
  356. hres = HidD_SetFeature
  357. (this->hdev,
  358. pReport,
  359. cbReport
  360. );
  361. if(FAILED(hres) )
  362. {
  363. SquirtSqflPtszV(sqfl | sqflError,
  364. TEXT("%s: FAIL SendD_Feature() hres:0x%x"),
  365. s_tszProc, hres );
  366. }
  367. } else
  368. {
  369. hres = DIERR_PID_USAGENOTFOUND;
  370. }
  371. ExitOleProc();
  372. return hres;
  373. }
  374. STDMETHODIMP
  375. PID_SendReport
  376. (
  377. IDirectInputEffectDriver *ped,
  378. PUCHAR pReport,
  379. UINT cbReport,
  380. HIDP_REPORT_TYPE HidP_Type,
  381. BOOL bBlocking,
  382. UINT blockNr,
  383. UINT totalBlocks
  384. )
  385. {
  386. CPidDrv *this = (CPidDrv *)ped;
  387. HRESULT hres = S_OK;
  388. EnterProcI( PID_SendReport, (_"xxxx", ped, pReport, cbReport, HidP_Type));
  389. if (bBlocking == TRUE)
  390. {
  391. hres = PID_SendReportBl(ped, pReport, cbReport, HidP_Type);
  392. }
  393. else
  394. {
  395. AssertF(this->hThread != 0x0);
  396. AssertF(this->hWrite != 0x0);
  397. AssertF(this->hWriteComplete != 0x0);
  398. //blockNr is 0-based.
  399. AssertF(totalBlocks > 0);
  400. AssertF(blockNr < totalBlocks);
  401. if( HidP_Type == HidP_Output )
  402. {
  403. //WaitForMultipleObjects() till the completion event becomes set.
  404. //we save each report into the appropriate place in the array.
  405. //when we get all the reports, we set the event to signal to the other thread to write.
  406. // Windows bug 627797 -- do not use INFINITE wait, so that we don't hang the app
  407. //if smth goes wrong w/ the previous write, but instead use the blocking version.
  408. DWORD dwWait = WaitForMultipleObjects(1, &this->hWriteComplete, FALSE, 1000);
  409. if (dwWait == WAIT_OBJECT_0)
  410. {
  411. AssertF(this->dwWriteAttempt == 0);
  412. //save the report data
  413. ZeroMemory(this->pWriteReport[blockNr], this->cbWriteReport[blockNr]);
  414. memcpy(this->pWriteReport[blockNr], pReport, cbReport);
  415. this->cbWriteReport[blockNr] = (USHORT)cbReport;
  416. if (blockNr == totalBlocks-1)
  417. {
  418. this->totalBlocks = totalBlocks;
  419. this->blockNr = 0;
  420. ResetEvent(this->hWriteComplete);
  421. SetEvent(this->hWrite);
  422. }
  423. }
  424. else
  425. {
  426. //The wait interval has expired, or an error has occured
  427. RPF( TEXT("Waiting for the write completion event ended without the event being signaled, dwWait = %u"), dwWait);
  428. //call the blocking version
  429. hres = PID_SendReportBl(ped, pReport, cbReport, HidP_Type);
  430. }
  431. } else if( HidP_Type == HidP_Feature )
  432. {
  433. hres = HidD_SetFeature
  434. (this->hdev,
  435. pReport,
  436. cbReport
  437. );
  438. if(FAILED(hres) )
  439. {
  440. SquirtSqflPtszV(sqfl | sqflError,
  441. TEXT("%s: FAIL SendD_Feature() hres:0x%x"),
  442. s_tszProc, hres );
  443. }
  444. } else
  445. {
  446. hres = DIERR_PID_USAGENOTFOUND;
  447. }
  448. }
  449. ExitOleProc();
  450. return hres;
  451. }
  452. STDMETHODIMP
  453. PID_ParseReport
  454. (
  455. IDirectInputEffectDriver *ped,
  456. PPIDREPORT pPidReport,
  457. USHORT LinkCollection,
  458. PVOID pvData,
  459. UINT cbData,
  460. PCHAR pReport,
  461. ULONG cbReport
  462. )
  463. {
  464. CPidDrv *this = (CPidDrv *)ped;
  465. HRESULT hres;
  466. PPIDUSAGE pPidUsage;
  467. UINT indx;
  468. EnterProcI( PID_ParseReport, (_"xxxxxxx", ped, pPidReport, pvData, cbData, pReport, cbReport));
  469. hres = S_OK;
  470. // Loop over all data values in the PID Report
  471. for(indx = 0x0, pPidUsage = pPidReport->rgPidUsage;
  472. indx < pPidReport->cAPidUsage;
  473. indx++, pPidUsage++ )
  474. {
  475. // Make sure the offsets are valid
  476. if( pPidUsage->DataOffset < cbData )
  477. {
  478. LONG lValue;
  479. NTSTATUS ntStat;
  480. USHORT Usage = DIGETUSAGE(pPidUsage->dwUsage);
  481. USHORT UsagePage = DIGETUSAGEPAGE(pPidUsage->dwUsage);
  482. ntStat = HidP_GetScaledUsageValue
  483. (
  484. pPidReport->HidP_Type,
  485. UsagePage,
  486. LinkCollection,
  487. Usage,
  488. &lValue,
  489. this->ppd,
  490. pReport,
  491. cbReport
  492. );
  493. if(SUCCEEDED(ntStat))
  494. {
  495. *((LONG*)((UCHAR*)pvData+pPidUsage->DataOffset)) = lValue;
  496. } else
  497. {
  498. hres |= E_NOTIMPL;
  499. SquirtSqflPtszV(sqfl | sqflBenign,
  500. TEXT("%s: FAIL HidP_GetScaledUsageValue:0x%x for(%x,%x,%x:%s)"),
  501. s_tszProc, ntStat,
  502. LinkCollection, UsagePage, Usage,
  503. PIDUSAGETXT(UsagePage,Usage) );
  504. }
  505. } else
  506. {
  507. SquirtSqflPtszV(sqfl | sqflBenign,
  508. TEXT("%s: FAIL Invalid Offset(%d), max(%d) "),
  509. s_tszProc, pPidUsage->DataOffset, cbData );
  510. }
  511. }
  512. ExitBenignOleProc();
  513. return hres;
  514. }
  515. STDMETHODIMP
  516. PID_GetReport
  517. (
  518. IDirectInputEffectDriver *ped,
  519. PPIDREPORT pPidReport,
  520. USHORT LinkCollection,
  521. PVOID pReport,
  522. UINT cbReport
  523. )
  524. {
  525. HRESULT hres = S_OK;
  526. CPidDrv *this = (CPidDrv *)ped;
  527. UCHAR ReportId;
  528. EnterProcI( PID_GetReport, (_"xxxxx", ped, pPidReport, LinkCollection, pReport, cbReport));
  529. if( SUCCEEDED(hres) )
  530. {
  531. hres = PID_GetReportId(ped, pPidReport, LinkCollection, &ReportId);
  532. if(SUCCEEDED(hres) )
  533. {
  534. AssertF(pPidReport->HidP_Type == HidP_Feature);
  535. if(SUCCEEDED(hres) )
  536. {
  537. ZeroBuf(pReport, cbReport);
  538. /*
  539. * The Win9x headers do not yet have use HidP_InitializeReportForID
  540. * use MAXULONG_PTR to tell the header sets apart so that we can still build
  541. */
  542. #ifdef WINNT
  543. /*hres*=*/HidP_InitializeReportForID
  544. (
  545. pPidReport->HidP_Type, //ReportType,
  546. ReportId, //ReportID,
  547. this->ppd, //PreparsedData
  548. pReport, //Report
  549. cbReport //ReportLength
  550. );
  551. #else
  552. (*(PUCHAR)pReport) = ReportId;
  553. hres = S_OK;
  554. #endif
  555. if( FAILED(hres) )
  556. {
  557. SquirtSqflPtszV(sqfl | sqflError,
  558. TEXT("%s: FAIL HidP_InitializeReportForId:0x%x for Type(%d) CollectionId%d ReportID%d "),
  559. s_tszProc, hres, pPidReport->HidP_Type, LinkCollection, ReportId );
  560. }
  561. if( SUCCEEDED(hres)
  562. && pPidReport->HidP_Type == HidP_Feature )
  563. {
  564. BOOL frc;
  565. frc = HidD_GetFeature
  566. (
  567. this->hdev, // HidDeviceObject,
  568. pReport, // ReportBuffer,
  569. cbReport //ReportBufferLength
  570. );
  571. if( frc != TRUE )
  572. {
  573. hres = DIERR_PID_USAGENOTFOUND;
  574. }
  575. }
  576. }
  577. }
  578. }
  579. ExitOleProc();
  580. return(hres);
  581. }
  582. /*****************************************************************************
  583. *
  584. * PID_ComputeScalingFactors
  585. *
  586. * Dinput units for various parameters are well defined. The device may choose
  587. * to implement the units that it is most comfortable with. This routine
  588. * computes scaling factors that are to be used when scaling DINPUT parameters
  589. * before they are send to the device.
  590. *
  591. * IDirectInputEffectDriver | ped |
  592. *
  593. * The effect driver interface
  594. *
  595. * PPIDREPORT | pPidReport |
  596. *
  597. * Address of PIDREPORT structure
  598. *
  599. * USHORT | uLinkCollection |
  600. *
  601. * LinkCollection ID
  602. *
  603. * IN OUT PVOID | pvData |
  604. *
  605. * Parameter data. On entry value is the nominal scale used by Dinput.
  606. * For example: Angles: DI_DEGREES, DI_FFNOMINALMAX, DI_SECONDS
  607. *
  608. * IN UINT | cbData |
  609. *
  610. * Number of valid DWORDS in pvData
  611. *
  612. * Returns:
  613. *
  614. * HRESULT
  615. * Error code
  616. * E_NOTIMPL: Did not find any usage / usage Page
  617. * DIERR_PID_INVALIDSCALING: Unsupported device scaling parameters.
  618. * S_OK: Scaling value for at least one parameter was found
  619. *
  620. *****************************************************************************/
  621. STDMETHODIMP
  622. PID_ComputeScalingFactors
  623. (
  624. IDirectInputEffectDriver *ped,
  625. PPIDREPORT pPidReport,
  626. USHORT LinkCollection,
  627. PVOID pvData,
  628. UINT cbData,
  629. PVOID pvOffset,
  630. UINT cbOffset
  631. )
  632. {
  633. HRESULT hres = E_NOTIMPL;
  634. CPidDrv *this = (CPidDrv *)ped;
  635. UINT indx;
  636. PPIDUSAGE pPidUsage;
  637. EnterProcI( PID_ComputeScalingFactors, (_"xxxxxxx", ped, pPidReport, LinkCollection, pvData, cbData, pvOffset, cbOffset));
  638. // Loop over all data values in the PID Report
  639. for(indx = 0x0, pPidUsage = pPidReport->rgPidUsage;
  640. indx < pPidReport->cAPidUsage;
  641. indx++, pPidUsage++ )
  642. {
  643. // Make sure the offsets are valid
  644. if (( pPidUsage->DataOffset < cbData ) && (pPidUsage->DataOffset < cbOffset))
  645. {
  646. NTSTATUS ntStat;
  647. HIDP_VALUE_CAPS ValCaps;
  648. USHORT cAValCaps = 0x1;
  649. USHORT Usage = DIGETUSAGE(pPidUsage->dwUsage);
  650. USHORT UsagePage = DIGETUSAGEPAGE(pPidUsage->dwUsage);
  651. PDWORD pdwValue;
  652. PDWORD pdwOffset;
  653. DWORD dwScale = 0x1;
  654. DWORD dwOffset = 0x0;
  655. pdwValue = ((DWORD*)((UCHAR*)pvData+pPidUsage->DataOffset));
  656. pdwOffset = ((DWORD*)((UCHAR*)pvOffset+pPidUsage->DataOffset));
  657. ntStat = HidP_GetSpecificValueCaps
  658. (
  659. pPidReport->HidP_Type,
  660. UsagePage,
  661. LinkCollection,
  662. Usage,
  663. &ValCaps,
  664. &cAValCaps,
  665. this->ppd
  666. );
  667. if(SUCCEEDED(ntStat))
  668. {
  669. //some units are "absolute" and thus don't need to be scaled to the limits.
  670. //for them, we just find out the correct units
  671. if (PID_IsUsageAbsoluteLike(ped, Usage))
  672. {
  673. if( ! ValCaps.Units )
  674. {
  675. SquirtSqflPtszV(sqfl | sqflVerbose,
  676. TEXT("%s:No Units(%x,%x %x:%s) Max:%d Scale:%d "),
  677. s_tszProc, LinkCollection, UsagePage, Usage, PIDUSAGETXT(UsagePage,Usage),
  678. ValCaps.PhysicalMax, dwScale );
  679. // No units, scaling exponent is default = 1
  680. hres = S_FALSE;
  681. } else
  682. {
  683. LONG UnitExp;
  684. UnitExp = (LONG)ValCaps.UnitsExp ;
  685. if( UnitExp > 0x0 )
  686. {
  687. RPF(TEXT("Driver does not support Units (%x,%x %x:%s) Exp:%d Max:%d"),
  688. LinkCollection, UsagePage, Usage, PIDUSAGETXT(UsagePage,Usage), ValCaps.UnitsExp, ValCaps.PhysicalMax ) ;
  689. hres = DIERR_PID_INVALIDSCALING;
  690. }else
  691. {
  692. hres = S_OK;
  693. }
  694. if(SUCCEEDED(hres) )
  695. {
  696. dwScale = (*pdwValue);
  697. for(; UnitExp; UnitExp++ )
  698. {
  699. dwScale /= 10;
  700. }
  701. if( dwScale == 0 )
  702. {
  703. RPF(TEXT("Driver does not support Units (%x,%x %x:%s) Exp:%d Max:%d"),
  704. LinkCollection, UsagePage, Usage, PIDUSAGETXT(UsagePage,Usage), ValCaps.UnitsExp, ValCaps.PhysicalMax ) ;
  705. dwScale = 0x1;
  706. hres = DIERR_PID_INVALIDSCALING;
  707. }else
  708. {
  709. hres = S_OK;
  710. }
  711. }
  712. SquirtSqflPtszV(sqfl | sqflVerbose,
  713. TEXT("%s: (%x,%x %x:%s) Exp%d Max:%d Scale:%d "),
  714. s_tszProc, LinkCollection, UsagePage, Usage, PIDUSAGETXT(UsagePage,Usage),
  715. ValCaps.UnitsExp, ValCaps.PhysicalMax, (*pdwValue) );
  716. }
  717. }
  718. else
  719. {
  720. //for everything else, get Physical and /or Logical Min/ Max
  721. //From PID spec, doesn't have to have a Physical / Logical Min, but does have to have either Physical or Logical Max
  722. if ((!ValCaps.PhysicalMax) && (!ValCaps.LogicalMax))
  723. {
  724. RPF(TEXT("Driver does not have either Physical Max or Logical Max for (%x,%x %x:%s)"),
  725. LinkCollection, UsagePage, Usage, PIDUSAGETXT(UsagePage,Usage)) ;
  726. hres = DIERR_PID_INVALIDSCALING;
  727. }
  728. else
  729. {
  730. //Compute the scaling value from either Physical or Logical Min/ Max and store it
  731. int Scale = 0;
  732. int Min = 0;
  733. int Max = 0;
  734. if (ValCaps.PhysicalMax)
  735. {
  736. Max = ValCaps.PhysicalMax;
  737. if (ValCaps.PhysicalMin)
  738. {
  739. Min = ValCaps.PhysicalMin;
  740. }
  741. }
  742. else
  743. {
  744. Max = ValCaps.LogicalMax;
  745. if (ValCaps.LogicalMin)
  746. {
  747. Min = ValCaps.LogicalMin;
  748. }
  749. }
  750. #ifdef DEBUG
  751. //if Min/max are not in correct order, print a message so that we know if there are any problems w/ the forces
  752. if (Min >= Max)
  753. {
  754. RPF(TEXT("Maximum of the device's range is %d, not bigger than minimum %d"), Max, Min);
  755. }
  756. #endif
  757. //certain magnitudes can be both positive and negative -- for those, we need to know the device's offset
  758. if (PID_IsUsagePositiveNegative(ped, Usage, LinkCollection))
  759. {
  760. Scale = (Max - Min)/2;
  761. dwOffset = (Max + Min)/2;
  762. }
  763. //other magnitudes can only be positive
  764. else
  765. {
  766. Scale = Max - Min;
  767. dwOffset = Min;
  768. }
  769. //for angular usages, multiply by DI_FFNOMINALMAX and divide by 360 * DI_DEGREES
  770. //we are doing this since later we will have no way of knowing that the values represent angles,
  771. //and will thus divide all the values by DI_FFNOMINALMAX
  772. if (*pdwValue == 360 * DI_DEGREES)
  773. {
  774. dwScale = MulDiv(Scale, DI_FFNOMINALMAX, (360 * DI_DEGREES));
  775. }
  776. else
  777. {
  778. dwScale = Scale;
  779. }
  780. hres = S_OK;
  781. }
  782. }
  783. } else
  784. {
  785. // HidP_SetScaledUsageValue FAILED
  786. SquirtSqflPtszV(sqfl | sqflBenign,
  787. TEXT("%s: FAIL HidP_GetSpecificValueCaps:0x%x for(%x,%x,%x:%s)=0x%x "),
  788. s_tszProc, ntStat,
  789. LinkCollection, UsagePage, Usage,
  790. PIDUSAGETXT(UsagePage,Usage),
  791. dwScale );
  792. }
  793. (*pdwValue) = dwScale;
  794. (*pdwOffset) = dwOffset;
  795. } else
  796. {
  797. //SquirtSqflPtszV(sqfl | sqflVerbose,
  798. // TEXT("%s: FAIL Invalid Offset(%d), max(%d) "),
  799. // s_tszProc, pPidUsage->DataOffset, cbData );
  800. }
  801. }
  802. ExitOleProc();
  803. return hres;
  804. }
  805. /*****************************************************************************
  806. *
  807. * PID_ApplyScalingFactors
  808. *
  809. * Dinput units for various parameters are well defined. The device may choose
  810. * to implement the units that it is most comfortable with. This routine
  811. * apply scaling factors that are to be used when scaling DINPUT parameters
  812. * before they are send to the device.
  813. *
  814. * IDirectInputEffectDriver | ped |
  815. *
  816. * The effect driver interface
  817. *
  818. * PPIDREPORT | pPidReport |
  819. *
  820. * Address of PIDREPORT structure
  821. *
  822. * IN PVOID | pvScale |
  823. *
  824. * Scaling values
  825. *
  826. * IN UINT | cbScale |
  827. *
  828. * Number of scaling values.
  829. *
  830. * IN OUT PVOID | pvData |
  831. *
  832. * Array of data values.
  833. *
  834. * IN UINT | cbData |
  835. *
  836. * Number of data values.
  837. *
  838. * Returns:
  839. *
  840. * HRESULT
  841. * Error code
  842. * E_NOTIMPL: Did not find any usage / usage Page
  843. * DIERR_PID_INVALIDSCALING: Unsupported device scaling parameters.
  844. * S_OK: Scaling value for at least one parameter was found
  845. *
  846. *****************************************************************************/
  847. STDMETHODIMP
  848. PID_ApplyScalingFactors
  849. (
  850. IDirectInputEffectDriver *ped,
  851. PPIDREPORT pPidReport,
  852. PVOID pvScale,
  853. UINT cbScale,
  854. PVOID pvOffset,
  855. UINT cbOffset,
  856. PVOID pvData,
  857. UINT cbData
  858. )
  859. {
  860. HRESULT hres = S_OK;
  861. CPidDrv *this = (CPidDrv *)ped;
  862. UINT indx;
  863. PPIDUSAGE pPidUsage;
  864. EnterProcI( PID_ApplyScalingFactors, (_"xxxxxxxx", ped, pPidReport, pvScale, cbScale, pvOffset, cbOffset, pvData, cbData));
  865. // Loop over all data values in the PID Report
  866. for(indx = 0x0, pPidUsage = pPidReport->rgPidUsage;
  867. indx < pPidReport->cAPidUsage;
  868. indx++, pPidUsage++ )
  869. {
  870. // Make sure we the offsets are valid
  871. if( (pPidUsage->DataOffset < cbData) &&
  872. (pPidUsage->DataOffset < cbScale) && ((pPidUsage->DataOffset < cbOffset) ))
  873. {
  874. PUINT pValue;
  875. PUINT pScale;
  876. PUINT pOffset;
  877. pValue = ((PUINT)((UCHAR*)pvData +pPidUsage->DataOffset));
  878. pScale = ((PUINT)((UCHAR*)pvScale +pPidUsage->DataOffset));
  879. pOffset = ((PUINT)((UCHAR*)pvOffset +pPidUsage->DataOffset));
  880. //"absolute"-like usages need special handling, because they don't need to be scaled to the max device values
  881. if (PID_IsUsageAbsoluteLike(ped, DIGETUSAGE(pPidUsage->dwUsage)))
  882. {
  883. if( (*pScale) > 0x1 )
  884. {
  885. (*pValue) /= (*pScale) ;
  886. }
  887. }
  888. //for everything else, do a calculation based on Logical or Physical Min/ Max
  889. else
  890. {
  891. (int)(*pValue) = MulDiv((*pScale), (*pValue), DI_FFNOMINALMAX) + (*pOffset);
  892. }
  893. } else
  894. {
  895. //SquirtSqflPtszV(sqfl | sqflBenign,
  896. // TEXT("%s: FAIL Invalid Offset(%d), max(%d) "),
  897. // s_tszProc, pPidUsage->DataOffset, cbData );
  898. }
  899. }
  900. ExitOleProc();
  901. return hres;
  902. }