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.

623 lines
21 KiB

  1. /*****************************************************************************
  2. *
  3. * DIHidDat.c
  4. *
  5. * Copyright (c) 1996 Microsoft Corporation. All Rights Reserved.
  6. *
  7. * Abstract:
  8. *
  9. * HID data management.
  10. *
  11. * Contents:
  12. *
  13. * CHid_AddDeviceData
  14. *
  15. *****************************************************************************/
  16. #include "dinputpr.h"
  17. /*****************************************************************************
  18. *
  19. * The sqiffle for this file.
  20. *
  21. *****************************************************************************/
  22. #define sqfl sqflHidDev
  23. /*****************************************************************************
  24. *
  25. * @doc INTERNAL
  26. *
  27. * @method void | CHid | DelDeviceData |
  28. *
  29. * Remove an item of device data from the list.
  30. *
  31. * We grab the last item and slide it into place, updating
  32. * the various pointers as we go.
  33. *
  34. * @parm PHIDREPORTINFO | phri |
  35. *
  36. * The HID report from which the item is being deleted.
  37. *
  38. * @parm int | idataDel |
  39. *
  40. * The data value being deleted.
  41. *
  42. * @parm HIDP_REPORT_TYPE | type |
  43. *
  44. * The report type we are mangling with.
  45. *
  46. *****************************************************************************/
  47. void INTERNAL
  48. CHid_DelDeviceData(PCHID this, PHIDREPORTINFO phri, int idataDel,
  49. HIDP_REPORT_TYPE type)
  50. {
  51. DWORD dwBase = this->rgdwBase[type];
  52. int iobjDel = phri->rgdata[idataDel].DataIndex + dwBase;
  53. PHIDOBJCAPS phocDel = &this->rghoc[iobjDel];
  54. int idataSrc;
  55. SquirtSqflPtszV(sqflHidOutput,
  56. TEXT("DelDeviceData(%d) - cdataUsed = %d, obj=%d"),
  57. idataDel, phri->cdataUsed, iobjDel);
  58. AssertF(idataDel >= 0);
  59. AssertF(idataDel < phri->cdataUsed);
  60. AssertF(phri->cdataUsed > 0);
  61. /*
  62. * Wipe out the item being deleted.
  63. * Remember that the report needs to be rebuilt.
  64. */
  65. AssertF(phocDel->idata == idataDel);
  66. phocDel->idata = -1;
  67. phri->fNeedClear = TRUE;
  68. /*
  69. * Slide the top item into its place.
  70. */
  71. idataSrc = (int)--(phri->cdataUsed);
  72. if (idataSrc > idataDel) {
  73. int iobjSrc;
  74. PHIDOBJCAPS phocSrc;
  75. AssertF(idataSrc > 0 && idataSrc < phri->cdataMax);
  76. iobjSrc = phri->rgdata[idataSrc].DataIndex + dwBase;
  77. phocSrc = &this->rghoc[iobjSrc];
  78. AssertF(phocSrc->idata == idataSrc);
  79. phocSrc->idata = idataDel;
  80. phri->rgdata[idataDel] = phri->rgdata[idataSrc];
  81. }
  82. }
  83. /*****************************************************************************
  84. *
  85. * @doc INTERNAL
  86. *
  87. * @method void | CHid | ResetDeviceData |
  88. *
  89. * Clean out all old device data from the list.
  90. *
  91. * @parm PHIDREPORTINFO | phri |
  92. *
  93. * The HID report which should be reset.
  94. *
  95. * @parm HIDP_REPORT_TYPE | type |
  96. *
  97. * The report type we are mangling with.
  98. *
  99. *****************************************************************************/
  100. void EXTERNAL
  101. CHid_ResetDeviceData(PCHID this, PHIDREPORTINFO phri, HIDP_REPORT_TYPE type)
  102. {
  103. SquirtSqflPtszV(sqflHidOutput,
  104. TEXT("ResetDeviceData(%d) - cdataUsed = %d"),
  105. type, phri->cdataUsed);
  106. if (phri->cdataUsed) {
  107. int idata;
  108. DWORD dwBase = this->rgdwBase[type];
  109. phri->fNeedClear = TRUE;
  110. for (idata = 0; idata < phri->cdataUsed; idata++) {
  111. int iobj = phri->rgdata[idata].DataIndex + dwBase;
  112. PHIDOBJCAPS phoc = &this->rghoc[iobj];
  113. AssertF(phoc->idata == idata);
  114. phoc->idata = -1;
  115. }
  116. phri->cdataUsed = 0;
  117. }
  118. }
  119. /*****************************************************************************
  120. *
  121. * @doc INTERNAL
  122. *
  123. * @method HRESULT | CHid | AddDeviceData |
  124. *
  125. * Add (or replace) a piece of device data to the array.
  126. *
  127. * If we are removing a button, then we delete it, because
  128. * the HID way of talking about a button is "If you don't
  129. * talk about it, then it isn't set."
  130. *
  131. * @parm UINT | uiObj |
  132. *
  133. * The object being added.
  134. *
  135. * @parm DWORD | dwData |
  136. *
  137. * The data value to add.
  138. *
  139. * @returns
  140. *
  141. * Returns a COM error code. The following error codes are
  142. * intended to be illustrative and not necessarily comprehensive.
  143. *
  144. * <c DI_OK> = <c S_OK>: The operation completed successfully.
  145. *
  146. * <c DIERR_REPORTFULL>: Too many items are set in the report.
  147. * ISSUE-2001/03/29-timgill Need more return code clarification
  148. *
  149. *****************************************************************************/
  150. HRESULT EXTERNAL
  151. CHid_AddDeviceData(PCHID this, UINT uiObj, DWORD dwData)
  152. {
  153. HRESULT hres;
  154. LPDIOBJECTDATAFORMAT podf;
  155. AssertF(uiObj < this->df.dwNumObjs);
  156. podf = &this->df.rgodf[uiObj];
  157. if (podf->dwType & DIDFT_OUTPUT) {
  158. PHIDOBJCAPS phoc = &this->rghoc[uiObj];
  159. PHIDGROUPCAPS pcaps = phoc->pcaps;
  160. PHIDREPORTINFO phri;
  161. // 7/19/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers.
  162. SquirtSqflPtszV(sqflHidOutput,
  163. TEXT("CHid_AddDeviceData(%p, %d, %d) - type %d"),
  164. this, uiObj, dwData, pcaps->type);
  165. /*
  166. * Decide if it's HidP_Output or HidP_Feature.
  167. */
  168. AssertF(HidP_IsOutputLike(pcaps->type));
  169. switch (pcaps->type) {
  170. case HidP_Output: phri = &this->hriOut; break;
  171. case HidP_Feature: phri = &this->hriFea; break;
  172. default: AssertF(0); hres = E_FAIL; goto done;
  173. }
  174. AssertF(phri->cdataUsed <= phri->cdataMax);
  175. if (phoc->idata == -1) {
  176. SquirtSqflPtszV(sqflHidOutput,
  177. TEXT("CHid_AddDeviceData - no iData"));
  178. } else {
  179. AssertF(phoc->idata < phri->cdataUsed);
  180. AssertF(uiObj == phri->rgdata[phoc->idata].DataIndex +
  181. this->rgdwBase[pcaps->type]);
  182. SquirtSqflPtszV(sqflHidOutput,
  183. TEXT("CHid_AddDeviceData - iData = %d ")
  184. TEXT("DataIndex = %d"),
  185. phoc->idata,
  186. phri->rgdata[phoc->idata].DataIndex);
  187. }
  188. phri->fChanged = TRUE;
  189. if (pcaps->IsValue) {
  190. /*
  191. * Just swipe the value.
  192. * The fallthrough code will handle this.
  193. */
  194. } else {
  195. /*
  196. * If the button is being deleted, then delete it
  197. * and that's all.
  198. */
  199. if (dwData == 0) {
  200. if (phoc->idata >= 0) {
  201. CHid_DelDeviceData(this, phri, phoc->idata, pcaps->type);
  202. AssertF(phoc->idata == -1);
  203. } else {
  204. SquirtSqflPtszV(sqflHidOutput,
  205. TEXT("CHid_AddDeviceData - nop"));
  206. }
  207. hres = S_OK;
  208. goto done;
  209. } else {
  210. dwData = TRUE; /* HidP_SetData requires this for buttons */
  211. }
  212. }
  213. /*
  214. * If there is not already a slot for this item, then
  215. * find one.
  216. */
  217. if (phoc->idata < 0) {
  218. if (phri->cdataUsed < phri->cdataMax) {
  219. USHORT DataIndex;
  220. phoc->idata = phri->cdataUsed++;
  221. DataIndex = (USHORT)(uiObj - this->rgdwBase[pcaps->type]);
  222. phri->rgdata[phoc->idata].DataIndex = DataIndex;
  223. SquirtSqflPtszV(sqflHidOutput,
  224. TEXT("CHid_AddDeviceData - create iData = %d ")
  225. TEXT("DataIndex = %d"),
  226. phoc->idata,
  227. DataIndex);
  228. } else {
  229. RPF("SendDeviceData: No room for more data");
  230. hres = DIERR_REPORTFULL;
  231. goto done;
  232. }
  233. }
  234. AssertF(phri->cdataUsed <= phri->cdataMax);
  235. AssertF(phoc->idata >= 0 && phoc->idata < phri->cdataUsed);
  236. AssertF(uiObj == phri->rgdata[phoc->idata].DataIndex +
  237. this->rgdwBase[pcaps->type]);
  238. /*
  239. * Here it comes... The entire purpose of this function
  240. * is the following line of code... (Well, not the
  241. * *entire* purpose, but 90% of it...)
  242. */
  243. phri->rgdata[phoc->idata].RawValue = dwData;
  244. SquirtSqflPtszV(sqflHidOutput,
  245. TEXT("CHid_AddDeviceData - iData(%d) dwData = %d"),
  246. phoc->idata, dwData);
  247. hres = S_OK;
  248. done:;
  249. } else {
  250. RPF("SendDeviceData: Object %08x is not DIDFT_OUTPUT",
  251. podf->dwType);
  252. hres = E_INVALIDARG;
  253. }
  254. return hres;
  255. }
  256. /*****************************************************************************
  257. *
  258. * @doc INTERNAL
  259. *
  260. * @method HRESULT | CHid | SendHIDReport |
  261. *
  262. * Build up an output or feature report and send it.
  263. * If the report has not changed, then do nothing.
  264. *
  265. * @parm PHIDREPORTINFO | phri |
  266. *
  267. * Describes the HID report we should build.
  268. *
  269. * @parm OUTPUTHIDREPORT | OutputHIDReport |
  270. *
  271. * Output a HID report to wherever it's supposed to go.
  272. *
  273. * @parm HIDP_REPORT_TYPE | type |
  274. *
  275. * The report type being sent.
  276. * <c HidP_Output> or <c HidP_Feature>.
  277. *
  278. * @returns
  279. *
  280. * Returns a COM error code. The following error codes are
  281. * intended to be illustrative and not necessarily comprehensive.
  282. *
  283. * <c DI_OK> = <c S_OK>: The operation completed successfully
  284. * and the report is ready to be sent to the device.
  285. *
  286. * <c DIERR_REPORTFULL>: Too many items are set in the report.
  287. *
  288. * @cb HRESULT CALLBACK | SendHIDReportProc |
  289. *
  290. * An internal callback which takes a composed HID report
  291. * and sends it in the appropriate manner to the device.
  292. *
  293. * @parm PCHID | this |
  294. *
  295. * The device in question.
  296. *
  297. * @parm PHIDREPORTINFO | phri |
  298. *
  299. * The report being sent.
  300. *
  301. *****************************************************************************/
  302. STDMETHODIMP
  303. CHid_SendHIDReport(PCHID this, PHIDREPORTINFO phri, HIDP_REPORT_TYPE type,
  304. SENDHIDREPORT SendHIDReport)
  305. {
  306. HRESULT hres = S_OK;
  307. DWORD cdata;
  308. NTSTATUS stat;
  309. if (phri->fChanged) {
  310. if (phri->fNeedClear) {
  311. ZeroMemory(phri->pvReport, phri->cbReport);
  312. phri->fNeedClear = FALSE;
  313. }
  314. cdata = phri->cdataUsed;
  315. stat = HidP_SetData(type, phri->rgdata, &cdata, this->ppd,
  316. phri->pvReport, phri->cbReport);
  317. if (SUCCEEDED(stat) && (int)cdata == phri->cdataUsed) {
  318. if ( SUCCEEDED( hres = SendHIDReport(this, phri) ) ) {
  319. phri->fChanged = FALSE;
  320. }
  321. } else if (stat == HIDP_STATUS_BUFFER_TOO_SMALL) {
  322. hres = DIERR_REPORTFULL;
  323. } else {
  324. RPF("SendDeviceData: Unexpected HID failure");
  325. hres = E_FAIL;
  326. }
  327. } else {
  328. /* Vacuous success */
  329. }
  330. return hres;
  331. }
  332. /*****************************************************************************
  333. *
  334. * @doc INTERNAL
  335. *
  336. * @method NTSTATUS | CHid | ParseData |
  337. *
  338. * Parse a single input report and set up the
  339. * <e CHid.pvStage> buffer to contain the new device state.
  340. *
  341. * @parm HIDP_REPORT_TYPE | type |
  342. *
  343. * HID report type being parsed.
  344. *
  345. * @parm PHIDREPORTINFO | phri |
  346. *
  347. * Information that tells us how to parse the report.
  348. *
  349. *****************************************************************************/
  350. NTSTATUS INTERNAL
  351. CHid_ParseData(PCHID this, HIDP_REPORT_TYPE type, PHIDREPORTINFO phri)
  352. {
  353. NTSTATUS stat = E_FAIL;
  354. /*
  355. * Do this only if there are inputs at all. This avoids
  356. * annoying boundary conditions.
  357. */
  358. UCHAR uReportId;
  359. ULONG cdataMax = phri->cdataMax;
  360. if (cdataMax && phri->cbReport) {
  361. uReportId = *(UCHAR*)phri->pvReport;
  362. if( uReportId < this->wMaxReportId[type] &&
  363. *(this->pEnableReportId[type] + uReportId) )
  364. {
  365. stat = HidP_GetData(type, phri->rgdata, &cdataMax,
  366. this->ppd, phri->pvReport, phri->cbReport);
  367. if (SUCCEEDED(stat)) {
  368. ULONG idata;
  369. /*
  370. * If we successfully got stuff, then wipe out the old
  371. * buttons and start with new ones.
  372. *
  373. * HID data parsing rules differ from buttons to axes.
  374. * For buttons, the rule is that if it isn't in the
  375. * report, then the button isn't presed.
  376. *
  377. * Compare axes, where the rule is that if it isn't
  378. * in the report, then the value is unchanged.
  379. *
  380. * To avoid deleting buttons that are reported in reports
  381. * other than the one just received we check for multiple
  382. * reports during initialization and if necessary set up mask
  383. * arrays for the buttons. The mask is an arrays of bytes of
  384. * the same length as the button data, one for each report
  385. * that contains any buttons. If the device only has one
  386. * report there are no mask arrays so we can optimize by just
  387. * zeroing all the buttons. If the device has multiple
  388. * reports there is an array of pointers to the mask arrays,
  389. * if a report has no buttons, the pointer is NULL so no
  390. * further processing is required. For reports that do have
  391. * buttons, each byte in the button data is ANDed with the
  392. * corresponding byte in the mask so that only buttons in
  393. * the received report are zeroed.
  394. */
  395. if( this->rgpbButtonMasks == NULL )
  396. {
  397. /*
  398. * Only one report so just zero all buttons
  399. * This is the normal case so it is important that this
  400. * be done as quickly as possible.
  401. */
  402. ZeroMemory(pvAddPvCb(this->pvStage, this->ibButtonData),
  403. this->cbButtonData);
  404. }
  405. else
  406. {
  407. if( this->rgpbButtonMasks[uReportId-1] != NULL )
  408. {
  409. /*
  410. * ISSUE-2001/05/12-MarcAnd Could mask buttons faster
  411. * If we do this often, we could consider doing masks
  412. * with multiple bytes in each opperation.
  413. */
  414. PBYTE pbMask;
  415. PBYTE pbButtons;
  416. PBYTE pbButtonEnd = pvAddPvCb(this->pvStage, this->ibButtonData + this->cbButtonData);
  417. for( pbMask = this->rgpbButtonMasks[uReportId-1],
  418. pbButtons = pvAddPvCb(this->pvStage, this->ibButtonData);
  419. pbButtons < pbButtonEnd;
  420. pbMask++, pbButtons++ )
  421. {
  422. *pbButtons &= *pbMask;
  423. }
  424. }
  425. else
  426. {
  427. /*
  428. * No buttons in this report
  429. */
  430. }
  431. }
  432. for (idata = 0; idata < cdataMax; idata++) {
  433. UINT uiObj;
  434. PHIDGROUPCAPS pcaps;
  435. /*
  436. * Be careful and make sure that HID didn't
  437. * give us anything with a bogus item index.
  438. *
  439. * ISSUE-2001/03/29-timgill Not Feature-friendly.
  440. */
  441. AssertF(this->rgdwBase[HidP_Input] == 0);
  442. SquirtSqflPtszV(sqfl | sqflTrace,
  443. TEXT("HidP_GetData: %2d -> %d"),
  444. phri->rgdata[idata].DataIndex,
  445. phri->rgdata[idata].RawValue);
  446. uiObj = this->rgdwBase[type] + phri->rgdata[idata].DataIndex;
  447. if (uiObj < this->df.dwNumObjs &&
  448. (pcaps = this->rghoc[uiObj].pcaps) &&
  449. pcaps->type == type) {
  450. LPDIOBJECTDATAFORMAT podf;
  451. LONG lValue = (LONG)phri->rgdata[idata].RawValue;
  452. /*
  453. * Sign-extend the raw value if necessary.
  454. */
  455. if (lValue & pcaps->lMask ) {
  456. if( pcaps->IsSigned)
  457. lValue |= pcaps->lMask;
  458. else
  459. lValue &= pcaps->lMask;
  460. }
  461. if (HidP_IsOutputLike(pcaps->type)) {
  462. HRESULT hres;
  463. hres = CHid_AddDeviceData(this, uiObj, lValue);
  464. AssertF(SUCCEEDED(hres));
  465. }
  466. podf = &this->df.rgodf[uiObj];
  467. if (!pcaps->IsValue) {
  468. LPBYTE pb = pvAddPvCb(this->pvStage, podf->dwOfs);
  469. AssertF(lValue);
  470. *pb = 0x80;
  471. } else {
  472. LONG UNALIGNED *pl = pvAddPvCb(this->pvStage, podf->dwOfs);
  473. // ISSUE-2001/03/29-timgill need to consider how logical/physical mapping can alter scaling
  474. if (podf->dwType & DIDFT_RELAXIS) {
  475. if (pcaps->usGranularity) {
  476. lValue = -lValue * pcaps->usGranularity;
  477. }
  478. *pl += lValue;
  479. } else if ( (podf->dwType & DIDFT_ABSAXIS)
  480. #ifdef WINNT
  481. || ((podf->dwType & DIDFT_POV) && pcaps->IsPolledPOV)
  482. #endif
  483. ) {
  484. PJOYRANGECONVERT pjrc;
  485. *pl = lValue;
  486. /*
  487. * Apply the ramp if any.
  488. */
  489. pjrc = this->rghoc[uiObj].pjrc;
  490. if( pjrc
  491. && !( this->pvi->fl & VIFL_RELATIVE ) )
  492. {
  493. CCal_CookRange(pjrc, pl);
  494. }
  495. } else if (podf->dwType & DIDFT_BUTTON) {
  496. /*
  497. * Current applications do not expect any values
  498. * other than zero and 0x80. Just in case
  499. * someone has implemented an analog button the
  500. * way we had suggested, make sure we any value
  501. * greater than or equal to half pressed reports
  502. * 0x80 and anything else reports as zero.
  503. * Note, out of range values default to zero.
  504. */
  505. if( ( lValue <= pcaps->Logical.Max )
  506. && ( ( lValue - pcaps->Logical.Min ) >=
  507. ( ( ( pcaps->Logical.Max - pcaps->Logical.Min ) + 1 ) / 2 ) ) )
  508. {
  509. *((PBYTE)pl) = 0x80;
  510. }
  511. else
  512. {
  513. *((PBYTE)pl) = 0;
  514. }
  515. } else if (podf->dwType & DIDFT_POV) {
  516. /*
  517. * For (non-polled) POVs, an out of range value
  518. * is a NULL aka centered. Otherwise work out
  519. * the angle from the fraction of the circle.
  520. */
  521. if (lValue < pcaps->Logical.Min ||
  522. lValue > pcaps->Logical.Max) {
  523. *pl = JOY_POVCENTERED;
  524. } else {
  525. lValue -= pcaps->Logical.Min;
  526. *pl = lValue * pcaps->usGranularity;
  527. }
  528. }
  529. }
  530. } else {
  531. SquirtSqflPtszV(sqfl | sqflTrace,
  532. TEXT("HidP_GetData: Unable to use data element"));
  533. }
  534. }
  535. stat = S_OK;
  536. }
  537. stat = S_OK;
  538. }
  539. } else {
  540. stat = E_FAIL;
  541. }
  542. return stat;
  543. }