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.

1715 lines
58 KiB

  1. /*++
  2. Copyright (c) 1998 - 1999 Microsoft Corporation
  3. Module Name:
  4. hidjoy.c
  5. Abstract: This module contains routines Generate the HID report and
  6. configure the joystick.
  7. Environment:
  8. Kernel mode
  9. @@BEGIN_DDKSPLIT
  10. Author:
  11. Eliyas Yakub (Mar, 11, 1997)
  12. Revision History:
  13. Updated by Eliyas on Feb 5 1998
  14. @@END_DDKSPLIT
  15. --*/
  16. #include "hidgame.h"
  17. #ifdef ALLOC_PRAGMA
  18. #pragma alloc_text (INIT, HGM_DriverInit)
  19. #pragma alloc_text (PAGE, HGM_SetupButtons)
  20. #pragma alloc_text (PAGE, HGM_MapAxesFromDevExt)
  21. #pragma alloc_text (PAGE, HGM_GenerateReport)
  22. #pragma alloc_text (PAGE, HGM_JoystickConfig)
  23. #pragma alloc_text (PAGE, HGM_InitAnalog)
  24. /* Sample only functions */
  25. #ifdef CHANGE_DEVICE
  26. #pragma alloc_text (PAGE, HGM_ChangeHandler)
  27. #pragma alloc_text (PAGE, HGM_DeviceChanged)
  28. #endif /* CHANGE_DEVICE */
  29. #endif
  30. /*
  31. * A few look up tables to translate the JOY_HWS_* flags into axis masks.
  32. * These flags allow any axis to be polled on any of the four axis bits in
  33. * the gameport. For example, the X axis on a standard joystick is found on
  34. * bit 0 (LSB) and the Y axis is on bit 1; however many steering wheel/pedal
  35. * controllers have X on bit 0 but Y on bit 2. Although very few of these
  36. * combinations are known to be used, supporting all the flags only causes a
  37. * little extra work on setup. For each axis, there are three flags, one for
  38. * each of the possible non-standard bit masks. Since it is possible that
  39. * more than one of these may be set the invalid combinations are marked so
  40. * that they can be refused.
  41. */
  42. #define NA ( 0x80 )
  43. /*
  44. * Short versions of bit masks for axes
  45. */
  46. #define X1 AXIS_X
  47. #define Y1 AXIS_Y
  48. #define X2 AXIS_R
  49. #define Y2 AXIS_Z
  50. /*
  51. * Per axis flag masks and look up tables.
  52. * In each case, combinations with more than one bit set are invalid
  53. */
  54. #define XMAPBITS (JOY_HWS_XISJ2Y | JOY_HWS_XISJ2X | JOY_HWS_XISJ1Y)
  55. /*
  56. * 0 0 0 0001
  57. * 0 0 1 0010
  58. * 0 1 0 0100
  59. * 1 0 0 1000
  60. */
  61. static const unsigned char XLU[8] = { X1,Y1,X2,NA,Y2,NA,NA,NA };
  62. #define XMAPSHFT 7
  63. #define YMAPBITS (JOY_HWS_YISJ2Y | JOY_HWS_YISJ2X | JOY_HWS_YISJ1X)
  64. /* 0 0 0 0010
  65. * 0 0 1 0001
  66. * 0 1 0 0100
  67. * 1 0 0 1000
  68. */
  69. static const unsigned char YLU[8] = { Y1,X1,X2,NA,Y2,NA,NA,NA };
  70. #define YMAPSHFT 10
  71. #define RMAPBITS (JOY_HWS_RISJ2Y | JOY_HWS_RISJ1X | JOY_HWS_RISJ1Y)
  72. /* 0 0 0 0100
  73. * 0 0 1 0010
  74. * 0 1 0 0001
  75. * 1 0 0 1000
  76. */
  77. static const unsigned char RLU[8] = { X2,Y1,X1,NA,Y2,NA,NA,NA };
  78. #define RMAPSHFT 20
  79. #define ZMAPBITS (JOY_HWS_ZISJ2X | JOY_HWS_ZISJ1X | JOY_HWS_ZISJ1Y)
  80. /* 0 0 0 1000
  81. * 0 0 1 0010
  82. * 0 1 0 0001
  83. * 1 0 0 0100
  84. */
  85. static const unsigned char ZLU[8] = { Y2,Y1,X1,NA,X2,NA,NA,NA };
  86. #define ZMAPSHFT 13
  87. #define POVMAPBITS (JOY_HWS_POVISJ2X | JOY_HWS_POVISJ1X | JOY_HWS_POVISJ1Y)
  88. /*
  89. * POV is the same as Z but with a larger shift
  90. */
  91. #define POVMAPSHFT 16
  92. #undef X1
  93. #undef Y1
  94. #undef X2
  95. #undef Y2
  96. /*
  97. * This translates from an axis bitmask to an axis value index. The elements
  98. * used should be as follows (X marks unsed) { X, 0, 1, X, 2, X, X, X, 3 }.
  99. */
  100. static const unsigned char cAxisIndexTable[9] = { 0, 0, 1, 0, 2, 0, 0, 0, 3 };
  101. typedef enum _POV1
  102. {
  103. P1_NULL = 0x80,
  104. P1_0,
  105. P1_90,
  106. P1_180,
  107. P1_270
  108. } POV1;
  109. typedef enum _POV2
  110. {
  111. P2_NULL = 0xc0,
  112. P2_0,
  113. P2_90,
  114. P2_180,
  115. P2_270
  116. } POV2;
  117. #define POV_MASK ((unsigned char)(~(P1_NULL | P2_NULL)))
  118. /*
  119. * Look up tables for button combos
  120. * Buttons are zero based so use P1_NULL for a zero input so we don't have to
  121. * special case it as a do nothing button.
  122. * The 7th Button can be mapped either from it's unique combination or as
  123. * foreward on a second POV being read as buttons 7 - 10.
  124. */
  125. static const unsigned char c1PComboLU[] = { P1_NULL,0, 1, P1_270,
  126. 2, 4, 8, P1_180,
  127. 3, 5, 7, P1_90,
  128. 9, 6, 6, P1_0 };
  129. static const unsigned char c2PComboLU[] = { P1_NULL,0, 1, P1_270,
  130. 2, 4, P2_180, P1_180,
  131. 3, 5, P2_90, P1_90,
  132. P2_270, 6, P2_0, P1_0 };
  133. /*****************************************************************************
  134. *
  135. * @doc EXTERNAL
  136. *
  137. * @func NTSTATUS | HGM_DriverInit |
  138. *
  139. * Perform global initialization.
  140. * <nl>This is called from DriverEntry. Try to initialize a CPU
  141. * specific timer but if it fails set up default
  142. *
  143. * @rvalue STATUS_SUCCESS | success
  144. * @rvalue STATUS_UNSUCCESSFUL | not success
  145. *
  146. *****************************************************************************/
  147. NTSTATUS EXTERNAL
  148. HGM_DriverInit( void )
  149. {
  150. NTSTATUS ntStatus = STATUS_SUCCESS;
  151. if( !HGM_CPUCounterInit() )
  152. {
  153. LARGE_INTEGER QPCFrequency;
  154. KeQueryPerformanceCounter( &QPCFrequency );
  155. if( ( QPCFrequency.HighPart == 0 )
  156. && ( QPCFrequency.LowPart <= 10000 ) )
  157. {
  158. ntStatus = STATUS_UNSUCCESSFUL;
  159. HGM_DBGPRINT(FILE_HIDJOY | HGM_ERROR,\
  160. ("QPC at %I64u Hz is unusable",
  161. QPCFrequency.QuadPart ));
  162. }
  163. else
  164. {
  165. Global.CounterScale = CALCULATE_SCALE( QPCFrequency.QuadPart );
  166. Global.ReadCounter = (COUNTER_FUNCTION)&KeQueryPerformanceCounter;
  167. HGM_DBGPRINT(FILE_HIDJOY | HGM_BABBLE,\
  168. ("QPC at %I64u Hz used with scale %d",
  169. QPCFrequency.QuadPart, Global.CounterScale ));
  170. }
  171. }
  172. return ntStatus;
  173. }
  174. /*****************************************************************************
  175. *
  176. * @doc EXTERNAL
  177. *
  178. * @func NTSTATUS | HGM_SetupButtons |
  179. *
  180. * Use the flags in the DeviceExtension to check and set up buttons.
  181. * <nl>This is called both from HGM_JoystickConfig to validate the
  182. * configuration and HGM_GenerateReport to prepare for polling.
  183. *
  184. * @parm IN OUT PDEVICE_EXTENSION | DeviceExtension |
  185. *
  186. * Pointer to the minidriver device extension
  187. *
  188. * @rvalue STATUS_SUCCESS | success
  189. * @rvalue STATUS_DEVICE_CONFIGURATION_ERROR | The configuration is invalid
  190. *
  191. *****************************************************************************/
  192. NTSTATUS INTERNAL
  193. HGM_SetupButtons
  194. (
  195. IN OUT PDEVICE_EXTENSION DeviceExtension
  196. )
  197. {
  198. NTSTATUS ntStatus = STATUS_SUCCESS;
  199. if( DeviceExtension->fSiblingFound )
  200. {
  201. if( DeviceExtension->nButtons > 2 )
  202. {
  203. ntStatus = STATUS_DEVICE_CONFIGURATION_ERROR;
  204. HGM_DBGPRINT( FILE_HIDJOY | HGM_ERROR,\
  205. ("HGM_SetupButtons: failing config of sibling device with %u buttons",\
  206. DeviceExtension->nButtons));
  207. }
  208. if( DeviceExtension->HidGameOemData.OemData[1].joy_hws_dwFlags & JOY_HWS_POVISBUTTONCOMBOS )
  209. {
  210. ntStatus = STATUS_DEVICE_CONFIGURATION_ERROR;
  211. HGM_DBGPRINT( FILE_HIDJOY | HGM_ERROR,\
  212. ("HGM_SetupButtons: failing config of sibling device with combo buttons" ));
  213. }
  214. }
  215. else
  216. {
  217. if( DeviceExtension->HidGameOemData.OemData[0].joy_hws_dwFlags & JOY_HWS_POVISBUTTONCOMBOS )
  218. {
  219. if( DeviceExtension->nButtons > MAX_BUTTONS )
  220. {
  221. ntStatus = STATUS_DEVICE_CONFIGURATION_ERROR;
  222. HGM_DBGPRINT( FILE_HIDJOY | HGM_ERROR,\
  223. ("HGM_SetupButtons: failing config of button combo device with %u buttons",\
  224. DeviceExtension->nButtons));
  225. }
  226. }
  227. else
  228. {
  229. if( DeviceExtension->nButtons > 4 )
  230. {
  231. if( DeviceExtension->resistiveInputMask & AXIS_R )
  232. {
  233. ntStatus = STATUS_DEVICE_CONFIGURATION_ERROR;
  234. HGM_DBGPRINT( FILE_HIDJOY | HGM_ERROR,\
  235. ("HGM_SetupButtons: failing config of device with R axis and %u buttons",\
  236. DeviceExtension->nButtons));
  237. }
  238. else
  239. {
  240. /*
  241. * 5th button always read from R axis.
  242. * Set the inital on/off boundary low
  243. */
  244. DeviceExtension->resistiveInputMask |= AXIS_R;
  245. DeviceExtension->button5limit = 2;
  246. }
  247. if( DeviceExtension->nButtons > 5 )
  248. {
  249. if( DeviceExtension->resistiveInputMask & AXIS_Z )
  250. {
  251. ntStatus = STATUS_DEVICE_CONFIGURATION_ERROR;
  252. HGM_DBGPRINT( FILE_HIDJOY | HGM_ERROR,\
  253. ("HGM_SetupButtons: failing config of device with Z axis and %u buttons",\
  254. DeviceExtension->nButtons));
  255. }
  256. else
  257. {
  258. /*
  259. * 6th button always read from Z axis.
  260. * Set the inital on/off boundary low
  261. */
  262. DeviceExtension->resistiveInputMask |= AXIS_Z;
  263. DeviceExtension->button6limit = 2;
  264. }
  265. if( DeviceExtension->nButtons > 6 )
  266. {
  267. ntStatus = STATUS_DEVICE_CONFIGURATION_ERROR;
  268. HGM_DBGPRINT( FILE_HIDJOY | HGM_ERROR,\
  269. ("HGM_SetupButtons: failing config of device with %u buttons",\
  270. DeviceExtension->nButtons));
  271. }
  272. }
  273. }
  274. }
  275. }
  276. return( ntStatus );
  277. } /* HGM_SetupButtons */
  278. /*****************************************************************************
  279. *
  280. * @doc EXTERNAL
  281. *
  282. * @func NTSTATUS | HGM_MapAxesFromDevExt |
  283. *
  284. * Use the flags in the DeviceExtension to generate mappings for each
  285. * axis.
  286. * <nl>This is called both from HGM_JoystickConfig to validate the
  287. * configuration and HGM_GenerateReport to use the axis maps.
  288. *
  289. *
  290. * @parm IN OUT PDEVICE_EXTENSION | DeviceExtension |
  291. *
  292. * Pointer to the minidriver device extension
  293. *
  294. * @rvalue STATUS_SUCCESS | success
  295. * @rvalue STATUS_DEVICE_CONFIGURATION_ERROR | The configuration is invalid
  296. *
  297. *****************************************************************************/
  298. NTSTATUS EXTERNAL
  299. HGM_MapAxesFromDevExt
  300. (
  301. IN OUT PDEVICE_EXTENSION DeviceExtension
  302. )
  303. {
  304. NTSTATUS ntStatus;
  305. ULONG dwFlags;
  306. int nAxis;
  307. UCHAR AxisMask;
  308. ntStatus = STATUS_SUCCESS;
  309. dwFlags = DeviceExtension->HidGameOemData.OemData[(DeviceExtension->fSiblingFound!=0)].joy_hws_dwFlags;
  310. HGM_DBGPRINT( FILE_HIDJOY | HGM_BABBLE2,\
  311. ("HGM_MapAxesFromDevExt: - - - dwFlags=0x%x - - -", dwFlags));
  312. #define XIS (0)
  313. #define YIS (1)
  314. #define ZIS (2)
  315. #define RIS (3)
  316. /*
  317. * Check X and Y last as Z, R and POV must not overlap
  318. * The are no flags to indicate the presence of X or Y so if they
  319. * overlap, this indicates that they are not used,
  320. */
  321. DeviceExtension->resistiveInputMask = 0;
  322. for( nAxis=MAX_AXES; nAxis>=0; nAxis-- )
  323. {
  324. DeviceExtension->AxisMap[nAxis] = INVALID_INDEX;
  325. }
  326. nAxis = 0;
  327. DeviceExtension->povMap = INVALID_INDEX;
  328. if( dwFlags & JOY_HWS_HASZ )
  329. {
  330. AxisMask = ZLU[(dwFlags & ZMAPBITS) >> ZMAPSHFT];
  331. if( AxisMask >= NA )
  332. {
  333. HGM_DBGPRINT( FILE_HIDJOY | HGM_ERROR,\
  334. ("HGM_MapAxesFromDevExt: Z axis mapping error dwFlags=0x%x",\
  335. dwFlags));
  336. ntStatus = STATUS_DEVICE_CONFIGURATION_ERROR;
  337. }
  338. else
  339. {
  340. nAxis = 1;
  341. DeviceExtension->resistiveInputMask = AxisMask;
  342. DeviceExtension->AxisMap[ZIS] = cAxisIndexTable[AxisMask];
  343. }
  344. }
  345. if( dwFlags & JOY_HWS_HASR )
  346. {
  347. AxisMask = RLU[(dwFlags & RMAPBITS) >> RMAPSHFT];
  348. if( AxisMask >= NA )
  349. {
  350. HGM_DBGPRINT( FILE_HIDJOY | HGM_ERROR,\
  351. ("HGM_MapAxesFromDevExt: R axis mapping error dwFlags=0x%x",\
  352. dwFlags));
  353. ntStatus = STATUS_DEVICE_CONFIGURATION_ERROR;
  354. }
  355. else
  356. {
  357. if( DeviceExtension->resistiveInputMask & AxisMask )
  358. {
  359. HGM_DBGPRINT( FILE_HIDJOY | HGM_ERROR, \
  360. ("HGM_MapAxesFromDevExt: R axis mapped to same as Z axis"));
  361. ntStatus = STATUS_DEVICE_CONFIGURATION_ERROR ;
  362. }
  363. else
  364. {
  365. nAxis++;
  366. DeviceExtension->resistiveInputMask |= AxisMask;
  367. DeviceExtension->AxisMap[RIS] = cAxisIndexTable[AxisMask];
  368. }
  369. }
  370. }
  371. if( dwFlags & JOY_HWS_HASPOV )
  372. {
  373. switch( dwFlags & ( JOY_HWS_POVISPOLL | JOY_HWS_POVISBUTTONCOMBOS ) )
  374. {
  375. case 0:
  376. HGM_DBGPRINT(FILE_HIDJOY | HGM_ERROR,\
  377. ("HGM_MapAxesFromDevExt: POV is not polled or button combo"));
  378. ntStatus = STATUS_DEVICE_CONFIGURATION_ERROR;
  379. break;
  380. case JOY_HWS_POVISBUTTONCOMBOS:
  381. break;
  382. case JOY_HWS_POVISPOLL:
  383. AxisMask = ZLU[(dwFlags & POVMAPBITS) >> POVMAPSHFT];
  384. if( AxisMask >= NA )
  385. {
  386. HGM_DBGPRINT( FILE_HIDJOY | HGM_ERROR,\
  387. ("HGM_MapAxesFromDevExt: POV axis mapping error dwFlags=0x%x",\
  388. dwFlags));
  389. ntStatus = STATUS_DEVICE_CONFIGURATION_ERROR ;
  390. }
  391. else
  392. {
  393. if( DeviceExtension->resistiveInputMask & AxisMask )
  394. {
  395. HGM_DBGPRINT( FILE_HIDJOY | HGM_ERROR,\
  396. ("HGM_MapAxesFromDevExt: POV axis mapped to same as Z or R axis") );
  397. ntStatus = STATUS_DEVICE_CONFIGURATION_ERROR ;
  398. }
  399. else
  400. {
  401. DeviceExtension->resistiveInputMask |= AxisMask;
  402. DeviceExtension->povMap = cAxisIndexTable[AxisMask];
  403. }
  404. }
  405. break;
  406. case JOY_HWS_POVISPOLL | JOY_HWS_POVISBUTTONCOMBOS:
  407. HGM_DBGPRINT(FILE_HIDJOY | HGM_ERROR,\
  408. ("HGM_MapAxesFromDevExt: POV reports button combo and polled"));
  409. ntStatus = STATUS_DEVICE_CONFIGURATION_ERROR;
  410. break;
  411. }
  412. }
  413. else if( dwFlags & ( JOY_HWS_POVISPOLL | JOY_HWS_POVISBUTTONCOMBOS ) )
  414. {
  415. HGM_DBGPRINT(FILE_HIDJOY | HGM_ERROR,\
  416. ("HGM_MapAxesFromDevExt: non-existant POV is polled or button combo"));
  417. ntStatus = STATUS_DEVICE_CONFIGURATION_ERROR ;
  418. }
  419. AxisMask = XLU[( dwFlags & XMAPBITS ) >> XMAPSHFT];
  420. if( AxisMask >= NA )
  421. {
  422. HGM_DBGPRINT( FILE_HIDJOY | HGM_ERROR,\
  423. ("HGM_MapAxesFromDevExt: X axis mapping error dwFlags=0x%x",\
  424. dwFlags));
  425. ntStatus = STATUS_DEVICE_CONFIGURATION_ERROR ;
  426. }
  427. else
  428. {
  429. if( DeviceExtension->resistiveInputMask & AxisMask )
  430. {
  431. HGM_DBGPRINT( FILE_HIDJOY | HGM_WARN,\
  432. ("HGM_MapAxesFromDevExt: X axis mapped to same as another axis") );
  433. }
  434. else
  435. {
  436. nAxis++;
  437. DeviceExtension->resistiveInputMask |= AxisMask;
  438. DeviceExtension->AxisMap[XIS] = cAxisIndexTable[AxisMask];
  439. }
  440. }
  441. AxisMask = YLU[( dwFlags & YMAPBITS ) >> YMAPSHFT];
  442. if( AxisMask >= NA )
  443. {
  444. HGM_DBGPRINT( FILE_HIDJOY | HGM_ERROR,\
  445. ("HGM_MapAxesFromDevExt: Y axis mapping error dwFlags=0x%x",\
  446. dwFlags));
  447. ntStatus = STATUS_DEVICE_CONFIGURATION_ERROR ;
  448. }
  449. else
  450. {
  451. if( DeviceExtension->resistiveInputMask & AxisMask )
  452. {
  453. HGM_DBGPRINT( FILE_HIDJOY | HGM_WARN,\
  454. ("HGM_MapAxesFromDevExt: Y axis mapped to same as another axis") );
  455. }
  456. else
  457. {
  458. nAxis++;
  459. DeviceExtension->resistiveInputMask |= AxisMask;
  460. DeviceExtension->AxisMap[YIS] = cAxisIndexTable[AxisMask];
  461. }
  462. }
  463. #undef XIS
  464. #undef YIS
  465. #undef ZIS
  466. #undef RIS
  467. #undef NA
  468. /*
  469. * Don't fail for this if CHANGE_DEVICE is defined because an exposed
  470. * sibling will always have an nAxis of zero.
  471. */
  472. #ifdef CHANGE_DEVICE
  473. if( DeviceExtension->nAxes )
  474. {
  475. #endif /* CHANGE_DEVICE */
  476. if( nAxis != DeviceExtension->nAxes )
  477. {
  478. ntStatus = STATUS_DEVICE_CONFIGURATION_ERROR ;
  479. HGM_DBGPRINT(FILE_HIDJOY | HGM_ERROR,\
  480. ("HGM_MapAxesFromDevExt: nAxis(%d) != DeviceExtension->nAxes(%d)", \
  481. nAxis, (int) DeviceExtension->nAxes));
  482. }
  483. #ifdef CHANGE_DEVICE
  484. }
  485. else
  486. {
  487. /*
  488. * This must be an exposed sibling so store the calculated nAxis and
  489. * a nButton just to look different.
  490. */
  491. DeviceExtension->nAxes = (USHORT)nAxis;
  492. DeviceExtension->nButtons = MAX_BUTTONS;
  493. }
  494. #endif /* CHANGE_DEVICE */
  495. HGM_DBGPRINT( FILE_HIDJOY | HGM_BABBLE,\
  496. ("HGM_MapAxesFromDevExt: uResistiveInputMask=0x%x",\
  497. DeviceExtension->resistiveInputMask) );
  498. if( NT_SUCCESS(ntStatus) )
  499. {
  500. ntStatus = HGM_SetupButtons( DeviceExtension );
  501. }
  502. return( ntStatus );
  503. } /* HGM_MapAxesFromDevExt */
  504. /*****************************************************************************
  505. *
  506. * @doc EXTERNAL
  507. *
  508. * @func NTSTATUS | HGM_GenerateReport |
  509. *
  510. * Generates a hid report descriptor for a n-axis, m-button joystick,
  511. * depending on number of buttons and joy_hws_flags field.
  512. *
  513. * @parm IN PDEVICE_OBJECT | DeviceObject |
  514. *
  515. * Pointer to the device object
  516. *
  517. * @parm IN OUT UCHAR * | rgGameReport[MAXBYTES_GAME_REPORT] |
  518. *
  519. * Array that receives the HID report descriptor
  520. *
  521. * @parm OUT PUSHORT | pCbReport |
  522. *
  523. * Address of a short integer that receives size of
  524. * HID report descriptor.
  525. *
  526. * @rvalue STATUS_SUCCESS | success
  527. * @rvalue STATUS_BUFFER_TOO_SMALL | Need more memory for HID descriptor
  528. *
  529. *****************************************************************************/
  530. NTSTATUS INTERNAL
  531. HGM_GenerateReport
  532. (
  533. IN PDEVICE_OBJECT DeviceObject,
  534. OUT UCHAR rgGameReport[MAXBYTES_GAME_REPORT],
  535. OUT PUSHORT pCbReport
  536. )
  537. {
  538. NTSTATUS ntStatus;
  539. PDEVICE_EXTENSION DeviceExtension;
  540. UCHAR *pucReport;
  541. int Idx;
  542. int UsageIdx;
  543. ULONG dwFlags;
  544. int InitialAxisMappings[MAX_AXES];
  545. typedef struct _USAGES
  546. {
  547. UCHAR UsagePage;
  548. UCHAR Usage;
  549. } USAGES, *PUSAGE;
  550. typedef struct _JOYCLASSPARAMS
  551. {
  552. UCHAR TopLevelUsage;
  553. USAGES Usages[MAX_AXES];
  554. } JOYCLASSPARAMS, *PJOYCLASSPARAMS;
  555. PJOYCLASSPARAMS pJoyParams;
  556. /*
  557. * Canned parameters for devices
  558. * The top-level usage must be either HID_USAGE_GENERIC_JOYSTICK or
  559. * HID_USAGE_GENERIC_GAMEPAD in order for the device to be treated as a
  560. * game controller.
  561. * The poll limits are specified in uSecs so the value stored here is 1000000/x
  562. */
  563. JOYCLASSPARAMS JoystickParams =
  564. {
  565. HID_USAGE_GENERIC_JOYSTICK,
  566. { { HID_USAGE_PAGE_GENERIC, HID_USAGE_GENERIC_X} ,
  567. { HID_USAGE_PAGE_GENERIC, HID_USAGE_GENERIC_Y} ,
  568. { HID_USAGE_PAGE_SIMULATION, HID_USAGE_SIMULATION_THROTTLE} ,
  569. { HID_USAGE_PAGE_SIMULATION, HID_USAGE_SIMULATION_RUDDER} }
  570. };
  571. JOYCLASSPARAMS GamepadParams =
  572. {
  573. HID_USAGE_GENERIC_GAMEPAD,
  574. { { HID_USAGE_PAGE_GENERIC, HID_USAGE_GENERIC_X} ,
  575. { HID_USAGE_PAGE_GENERIC, HID_USAGE_GENERIC_Y} ,
  576. { HID_USAGE_PAGE_SIMULATION, HID_USAGE_SIMULATION_THROTTLE} ,
  577. { HID_USAGE_PAGE_SIMULATION, HID_USAGE_SIMULATION_RUDDER} }
  578. };
  579. JOYCLASSPARAMS CarCtrlParams =
  580. {
  581. HID_USAGE_GENERIC_JOYSTICK,
  582. { { HID_USAGE_PAGE_GENERIC, HID_USAGE_GENERIC_X} ,
  583. { HID_USAGE_PAGE_GENERIC, HID_USAGE_GENERIC_Y} ,
  584. { HID_USAGE_PAGE_SIMULATION, HID_USAGE_SIMULATION_THROTTLE} ,
  585. { HID_USAGE_PAGE_SIMULATION, HID_USAGE_SIMULATION_RUDDER} }
  586. };
  587. JOYCLASSPARAMS FlightYokeParams =
  588. {
  589. HID_USAGE_GENERIC_JOYSTICK,
  590. { { HID_USAGE_PAGE_GENERIC, HID_USAGE_GENERIC_X} ,
  591. { HID_USAGE_PAGE_GENERIC, HID_USAGE_GENERIC_Y} ,
  592. { HID_USAGE_PAGE_SIMULATION, HID_USAGE_SIMULATION_THROTTLE} ,
  593. { HID_USAGE_PAGE_SIMULATION, HID_USAGE_SIMULATION_RUDDER} }
  594. };
  595. PAGED_CODE();
  596. HGM_DBGPRINT( FILE_HIDJOY | HGM_FENTRY,\
  597. ("HGM_GenerateReport(ucIn=0x%x,DeviceObject=0x%x)",\
  598. rgGameReport, DeviceObject) );
  599. /*
  600. * Get a pointer to the device extension
  601. */
  602. DeviceExtension = GET_MINIDRIVER_DEVICE_EXTENSION(DeviceObject);
  603. /*
  604. * Although the axes have already been validated and mapped in
  605. * HGM_JoystickConfig this function destroys the mapping when it compacts
  606. * the axes towards the start of the descriptor report. Since this
  607. * function will be called once to find the descriptor length and then
  608. * again to read the report, the mappings are regenerated again each
  609. * time through. Although this results in the parameters being
  610. * interpreted three times (for validation, descriptor size and
  611. * descriptor content) it avoids the possibility of a discrepancy in
  612. * implementation of separate functions.
  613. */
  614. ntStatus = HGM_MapAxesFromDevExt( DeviceExtension );
  615. ASSERTMSG( "HGM_GenerateReport:", ntStatus == STATUS_SUCCESS );
  616. pucReport = rgGameReport;
  617. dwFlags = DeviceExtension->HidGameOemData.OemData[(DeviceExtension->fSiblingFound!=0)].joy_hws_dwFlags;
  618. /*
  619. * What manner of beast have we ?
  620. */
  621. if( dwFlags & JOY_HWS_ISGAMEPAD )
  622. {
  623. pJoyParams = &GamepadParams;
  624. }
  625. else if( dwFlags & JOY_HWS_ISYOKE )
  626. {
  627. pJoyParams = &FlightYokeParams;
  628. }
  629. else if( dwFlags & JOY_HWS_ISCARCTRL )
  630. {
  631. pJoyParams = &CarCtrlParams;
  632. }
  633. else
  634. {
  635. pJoyParams = &JoystickParams;
  636. }
  637. #define NEXT_BYTE( pReport, Data ) \
  638. ASSERTMSG( "HGM_GenerateReport:", pReport+sizeof(UCHAR)-rgGameReport < MAXBYTES_GAME_REPORT ); \
  639. *pReport++ = Data;
  640. #define NEXT_LONG( pReport, Data ) \
  641. ASSERTMSG( "HGM_GenerateReport:", pReport+sizeof(ULONG)-rgGameReport < MAXBYTES_GAME_REPORT); \
  642. *(((LONG UNALIGNED*)(pReport))++) = Data;
  643. #define ITEM_DEFAULT 0x00 /* Data, Array, Absolute, No Wrap, Linear, Preferred State, Has no NULL */
  644. #define ITEM_VARIABLE 0x02 /* as ITEM_DEFAULT but value is a variable, not an array */
  645. #define ITEM_HASNULL 0x40 /* as ITEM_DEFAULT but values out of range are considered NULL */
  646. #define ITEM_ANALOG_AXIS ITEM_VARIABLE
  647. #define ITEM_DIGITAL_POV (ITEM_VARIABLE|ITEM_HASNULL)
  648. #define ITEM_BUTTON ITEM_VARIABLE
  649. #define ITEM_PADDING 0x01 /* Constant (nothing else applies) */
  650. /* USAGE_PAGE (Generic Desktop) */
  651. NEXT_BYTE(pucReport, HIDP_GLOBAL_USAGE_PAGE_1);
  652. NEXT_BYTE(pucReport, HID_USAGE_PAGE_GENERIC);
  653. /* USAGE (Joystick | GamePad ) */
  654. NEXT_BYTE(pucReport, HIDP_LOCAL_USAGE_1);
  655. NEXT_BYTE(pucReport, pJoyParams->TopLevelUsage);
  656. /* Logical Min is the smallest value that could be produced by a poll */
  657. NEXT_BYTE(pucReport, HIDP_GLOBAL_LOG_MIN_4);
  658. NEXT_LONG(pucReport, 0 );
  659. /* Logical Max is the largest value that could be produced by a poll */
  660. NEXT_BYTE(pucReport, HIDP_GLOBAL_LOG_MAX_4);
  661. NEXT_LONG(pucReport, AXIS_FULL_SCALE );
  662. /* Start a Linked collection */
  663. /*
  664. * Since this is a generic driver we know knothing about the physical
  665. * distribution of controls on the device so we put everything in a
  666. * single collection. If, for instance, we knew that some buttons were
  667. * on the base and some on the stick we could better describe them by
  668. * reporting them in separate collections.
  669. */
  670. NEXT_BYTE(pucReport, HIDP_MAIN_COLLECTION);
  671. NEXT_BYTE(pucReport, 0x0 );
  672. /* Define one axis at a time */
  673. NEXT_BYTE(pucReport, HIDP_GLOBAL_REPORT_COUNT_1);
  674. NEXT_BYTE(pucReport, 0x1);
  675. /* Each axis is a 32 bits value */
  676. NEXT_BYTE(pucReport, HIDP_GLOBAL_REPORT_SIZE);
  677. NEXT_BYTE(pucReport, 8 * sizeof(ULONG) );
  678. /*
  679. * Do the axis
  680. * Although HID could cope with the "active" axes being mixed with the
  681. * dummy ones, it makes life simpler to move them to the start.
  682. * Pass through all the axis maps generated by HGM_JoystickConfig
  683. * and map all the active ones into the descriptor, copying the usages
  684. * appropriate for the type of device.
  685. * Since a polled POV is nothing more than a different interpretation
  686. * of axis data, this is added after any axes.
  687. */
  688. C_ASSERT( sizeof( InitialAxisMappings ) == sizeof( DeviceExtension->AxisMap ) );
  689. RtlCopyMemory( InitialAxisMappings, DeviceExtension->AxisMap, sizeof( InitialAxisMappings ) );
  690. Idx = 0;
  691. for( UsageIdx = 0; UsageIdx < MAX_AXES; UsageIdx++ )
  692. {
  693. if( InitialAxisMappings[UsageIdx] >= INVALID_INDEX )
  694. {
  695. continue;
  696. }
  697. DeviceExtension->AxisMap[Idx] = InitialAxisMappings[UsageIdx];
  698. NEXT_BYTE(pucReport, HIDP_LOCAL_USAGE_4);
  699. NEXT_BYTE(pucReport, pJoyParams->Usages[UsageIdx].Usage);
  700. NEXT_BYTE(pucReport, 0x0);
  701. NEXT_BYTE(pucReport, pJoyParams->Usages[UsageIdx].UsagePage);
  702. NEXT_BYTE(pucReport, 0x0);
  703. /* Data Field */
  704. NEXT_BYTE(pucReport, HIDP_MAIN_INPUT_1);
  705. NEXT_BYTE(pucReport, ITEM_ANALOG_AXIS);
  706. HGM_DBGPRINT( FILE_HIDJOY | HGM_GEN_REPORT, \
  707. ("HGM_GenerateReport:Idx=%d, UsageIdx=%d, Mapping=%d, Usage=%02x, Page=%02x",\
  708. Idx, UsageIdx, DeviceExtension->AxisMap[UsageIdx], \
  709. pJoyParams->Usages[UsageIdx].Usage, pJoyParams->Usages[UsageIdx].UsagePage ) ) ;
  710. Idx++;
  711. }
  712. if( dwFlags & JOY_HWS_POVISPOLL )
  713. {
  714. /*
  715. * A polled POV is just the same as an axis.
  716. * Note, we have already checked that there is an axis for use as the POV.
  717. * Also, this type of POV can be distinguished from a digital POV by it's
  718. * lack of a NULL value.
  719. */
  720. NEXT_BYTE(pucReport, HIDP_LOCAL_USAGE_4);
  721. NEXT_BYTE(pucReport, HID_USAGE_GENERIC_HATSWITCH);
  722. NEXT_BYTE(pucReport, 0x0);
  723. NEXT_BYTE(pucReport, HID_USAGE_PAGE_GENERIC);
  724. NEXT_BYTE(pucReport, 0x0);
  725. /* Data Field */
  726. NEXT_BYTE(pucReport, HIDP_MAIN_INPUT_1);
  727. NEXT_BYTE(pucReport, ITEM_ANALOG_AXIS);
  728. HGM_DBGPRINT( FILE_HIDJOY | HGM_GEN_REPORT, \
  729. ("HGM_GenerateReport:Idx=%d, Set to polled POV", Idx ) ) ;
  730. Idx++;
  731. }
  732. /*
  733. * Now fill in any remaining axis values as dummys
  734. */
  735. while( Idx < MAX_AXES )
  736. {
  737. /* Constant Field */
  738. NEXT_BYTE(pucReport, HIDP_MAIN_INPUT_1);
  739. NEXT_BYTE(pucReport, ITEM_PADDING);
  740. HGM_DBGPRINT( FILE_HIDJOY | HGM_GEN_REPORT, \
  741. ("HGM_GenerateReport:Idx=%d, Set to constant field", Idx ) ) ;
  742. Idx++;
  743. }
  744. /*
  745. * Now move on to the byte sized fields
  746. */
  747. if( dwFlags & JOY_HWS_POVISBUTTONCOMBOS )
  748. {
  749. /*
  750. * Redefine the logical and physical ranges from now on
  751. * A digital POV has a NULL value (a value outside the logical range)
  752. * when the POV is centered. To make life easier call the NULL value
  753. * zero, so the logical range is from 1 to 4.
  754. /* Logical Min */
  755. NEXT_BYTE(pucReport, HIDP_GLOBAL_LOG_MIN_1);
  756. NEXT_BYTE(pucReport, 1 );
  757. /* Logical Max */
  758. NEXT_BYTE(pucReport, HIDP_GLOBAL_LOG_MAX_1);
  759. NEXT_BYTE(pucReport, 4 );
  760. /*
  761. * report for digital POV is 3 bits data plus 5 constant bits to fill
  762. * the byte.
  763. */
  764. NEXT_BYTE(pucReport, HIDP_LOCAL_USAGE_4);
  765. NEXT_BYTE(pucReport, HID_USAGE_GENERIC_HATSWITCH);
  766. NEXT_BYTE(pucReport, 0x0);
  767. NEXT_BYTE(pucReport, HID_USAGE_PAGE_GENERIC);
  768. NEXT_BYTE(pucReport, 0x0);
  769. /* Data Field */
  770. NEXT_BYTE(pucReport, HIDP_GLOBAL_REPORT_SIZE);
  771. NEXT_BYTE(pucReport, 0x3);
  772. NEXT_BYTE(pucReport, HIDP_MAIN_INPUT_1);
  773. NEXT_BYTE(pucReport, ITEM_DIGITAL_POV);
  774. /* top 5 bits constant */
  775. NEXT_BYTE(pucReport, HIDP_GLOBAL_REPORT_SIZE);
  776. NEXT_BYTE(pucReport, 0x5);
  777. NEXT_BYTE(pucReport, HIDP_MAIN_INPUT_1);
  778. NEXT_BYTE(pucReport, ITEM_PADDING);
  779. HGM_DBGPRINT( FILE_HIDJOY | HGM_GEN_REPORT, \
  780. ("HGM_GenerateReport:First button combo POV is on" ) ) ;
  781. if( dwFlags & JOY_HWS_HASPOV2 )
  782. {
  783. NEXT_BYTE(pucReport, HIDP_LOCAL_USAGE_4);
  784. NEXT_BYTE(pucReport, HID_USAGE_GENERIC_HATSWITCH);
  785. NEXT_BYTE(pucReport, 0x0);
  786. NEXT_BYTE(pucReport, HID_USAGE_PAGE_GENERIC);
  787. NEXT_BYTE(pucReport, 0x0);
  788. /* Data Field */
  789. NEXT_BYTE(pucReport, HIDP_GLOBAL_REPORT_SIZE);
  790. NEXT_BYTE(pucReport, 0x3);
  791. NEXT_BYTE(pucReport, HIDP_MAIN_INPUT_1);
  792. NEXT_BYTE(pucReport, ITEM_DIGITAL_POV);
  793. /* top 5 bits constant */
  794. NEXT_BYTE(pucReport, HIDP_GLOBAL_REPORT_SIZE);
  795. NEXT_BYTE(pucReport, 0x5);
  796. NEXT_BYTE(pucReport, HIDP_MAIN_INPUT_1);
  797. NEXT_BYTE(pucReport, ITEM_PADDING);
  798. HGM_DBGPRINT( FILE_HIDJOY | HGM_GEN_REPORT, \
  799. ("HGM_GenerateReport:Second button combo POV is on" ) ) ;
  800. }
  801. else
  802. {
  803. /* 8 bits of constant data instead of second POV */
  804. NEXT_BYTE(pucReport, HIDP_GLOBAL_REPORT_SIZE);
  805. NEXT_BYTE(pucReport, 0x8);
  806. /* Constant Field */
  807. NEXT_BYTE(pucReport, HIDP_MAIN_INPUT_1);
  808. NEXT_BYTE(pucReport, ITEM_PADDING);
  809. HGM_DBGPRINT( FILE_HIDJOY | HGM_GEN_REPORT, \
  810. ("HGM_GenerateReport:No second button combo POV" ) ) ;
  811. }
  812. }
  813. else
  814. {
  815. /* 16 bits of constant data instead of button combo POVs */
  816. NEXT_BYTE(pucReport, HIDP_GLOBAL_REPORT_SIZE);
  817. NEXT_BYTE(pucReport, 0x10);
  818. /* Constant Field */
  819. NEXT_BYTE(pucReport, HIDP_MAIN_INPUT_1);
  820. NEXT_BYTE(pucReport, ITEM_PADDING);
  821. HGM_DBGPRINT( FILE_HIDJOY | HGM_GEN_REPORT, \
  822. ("HGM_GenerateReport:Button combo POV are off" ) ) ;
  823. }
  824. /*
  825. * Now the buttons
  826. */
  827. for( Idx = 0x0; Idx < DeviceExtension->nButtons; Idx++ )
  828. {
  829. /* Report size is 1 bit for button */
  830. NEXT_BYTE(pucReport, HIDP_GLOBAL_REPORT_SIZE);
  831. NEXT_BYTE(pucReport, 0x1);
  832. NEXT_BYTE(pucReport, HIDP_LOCAL_USAGE_4);
  833. NEXT_BYTE(pucReport, (UCHAR)(Idx + 1) );
  834. NEXT_BYTE(pucReport, 0x0);
  835. NEXT_BYTE(pucReport, HID_USAGE_PAGE_BUTTON);
  836. NEXT_BYTE(pucReport, 0x0);
  837. /* Data field */
  838. NEXT_BYTE(pucReport, HIDP_MAIN_INPUT_1);
  839. NEXT_BYTE(pucReport, ITEM_BUTTON);
  840. /* 7 bits of constant data */
  841. NEXT_BYTE(pucReport, HIDP_GLOBAL_REPORT_SIZE);
  842. NEXT_BYTE(pucReport, 0x7);
  843. NEXT_BYTE(pucReport, HIDP_MAIN_INPUT_1);
  844. NEXT_BYTE(pucReport, ITEM_PADDING);
  845. HGM_DBGPRINT( FILE_HIDJOY | HGM_GEN_REPORT, \
  846. ("HGM_GenerateReport:Button %u on",Idx ) ) ;
  847. }
  848. if( Idx < MAX_BUTTONS )
  849. {
  850. /* Constant report for 8 * unused buttons bits */
  851. NEXT_BYTE(pucReport, HIDP_GLOBAL_REPORT_SIZE);
  852. NEXT_BYTE(pucReport, (UCHAR)((MAX_BUTTONS-Idx)*8) );
  853. /* Constant Field */
  854. NEXT_BYTE(pucReport, HIDP_MAIN_INPUT_1);
  855. NEXT_BYTE(pucReport, ITEM_PADDING);
  856. HGM_DBGPRINT( FILE_HIDJOY | HGM_GEN_REPORT, \
  857. ("HGM_GenerateReport:Last %u buttons off",MAX_BUTTONS-Idx ) ) ;
  858. }
  859. /* End of collection, We're done ! */
  860. NEXT_BYTE(pucReport, HIDP_MAIN_ENDCOLLECTION);
  861. #undef NEXT_BYTE
  862. #undef NEXT_LONG
  863. if( pucReport - rgGameReport > MAXBYTES_GAME_REPORT)
  864. {
  865. ntStatus = STATUS_BUFFER_TOO_SMALL;
  866. *pCbReport = 0x0;
  867. RtlZeroMemory(rgGameReport, sizeof(rgGameReport));
  868. } else
  869. {
  870. *pCbReport = (USHORT) (pucReport - rgGameReport);
  871. ntStatus = STATUS_SUCCESS;
  872. }
  873. HGM_DBGPRINT( FILE_HIDJOY | HGM_GEN_REPORT,\
  874. ("HGM_GenerateReport: ReportSize=0x%x",\
  875. *pCbReport) );
  876. HGM_EXITPROC(FILE_HIDJOY | HGM_FEXIT_STATUSOK, "HGM_GenerateReport", ntStatus);
  877. return ( ntStatus );
  878. } /* HGM_GenerateReport */
  879. /*****************************************************************************
  880. *
  881. * @doc EXTERNAL
  882. *
  883. * @func NTSTATUS | HGM_JoystickConfig |
  884. *
  885. * Check that the configuration is valid whilst there is still time
  886. * to refuse it.
  887. * <nl>HGM_GenerateReport uses the results generated here if the
  888. * settings are OK.
  889. *
  890. * @parm IN PDEVICE_OBJECT | DeviceObject |
  891. *
  892. * Pointer to the device object
  893. *
  894. * @rvalue STATUS_SUCCESS | success
  895. * @rvalue STATUS_DEVICE_CONFIGURATION_ERROR | Invalid configuration specified
  896. *
  897. *****************************************************************************/
  898. NTSTATUS INTERNAL
  899. HGM_JoystickConfig
  900. (
  901. IN PDEVICE_OBJECT DeviceObject
  902. )
  903. {
  904. PDEVICE_EXTENSION DeviceExtension;
  905. NTSTATUS ntStatus;
  906. int Idx;
  907. PAGED_CODE();
  908. HGM_DBGPRINT( FILE_HIDJOY | HGM_FENTRY,\
  909. ("HGM_JoystickConfig(DeviceObject=0x%x)",\
  910. DeviceObject) );
  911. /*
  912. * Get a pointer to the device extension
  913. */
  914. DeviceExtension = GET_MINIDRIVER_DEVICE_EXTENSION(DeviceObject);
  915. ntStatus = HGM_MapAxesFromDevExt( DeviceExtension );
  916. if( DeviceExtension->ReadAccessorDigital )
  917. {
  918. DeviceExtension->ScaledTimeout = AXIS_TIMEOUT;
  919. }
  920. else
  921. {
  922. /*
  923. * Calculate time thresholds for analog device
  924. */
  925. if( ( DeviceExtension->HidGameOemData.OemData[0].Timeout < ANALOG_POLL_TIMEOUT_MIN )
  926. ||( DeviceExtension->HidGameOemData.OemData[0].Timeout > ANALOG_POLL_TIMEOUT_MAX ) )
  927. {
  928. HGM_DBGPRINT(FILE_HIDJOY | HGM_BABBLE,\
  929. ("Ignoring out of range timeout: %u uSecs",\
  930. DeviceExtension->HidGameOemData.OemData[0].Timeout));
  931. DeviceExtension->ScaledTimeout = (ULONG)( ( (ULONGLONG)ANALOG_POLL_TIMEOUT_DFT
  932. * (ULONGLONG)(AXIS_FULL_SCALE<<SCALE_SHIFT) )
  933. / (ULONGLONG)ANALOG_POLL_TIMEOUT_MAX );
  934. }
  935. else
  936. {
  937. DeviceExtension->ScaledTimeout = (ULONG)( ( (ULONGLONG)DeviceExtension->HidGameOemData.OemData[0].Timeout
  938. * (ULONGLONG)(AXIS_FULL_SCALE<<SCALE_SHIFT) )
  939. / (ULONGLONG)ANALOG_POLL_TIMEOUT_MAX );
  940. }
  941. HGM_DBGPRINT(FILE_HIDJOY | HGM_BABBLE,\
  942. ("ScaledTimeout: %u",\
  943. DeviceExtension->ScaledTimeout));
  944. /*
  945. * Use one quarter of the minimum poll timeout as a starting value
  946. * for the time between two polls which will be considered to have
  947. * been interrupted.
  948. */
  949. DeviceExtension->ScaledThreshold = (ULONG)( ( (ULONGLONG)ANALOG_POLL_TIMEOUT_MIN
  950. * (ULONGLONG)AXIS_FULL_SCALE )
  951. / (ULONGLONG)ANALOG_POLL_TIMEOUT_MAX )>>2;
  952. }
  953. /*
  954. * Set initial values of LastGoodAxis so that the device will not show
  955. * up as present until we get at least one valid poll.
  956. */
  957. for( Idx = MAX_AXES; Idx >= 0; Idx-- )
  958. {
  959. DeviceExtension->LastGoodAxis[Idx] = AXIS_TIMEOUT;
  960. }
  961. HGM_EXITPROC(FILE_HIDJOY | HGM_FEXIT_STATUSOK, "HGM_JoystickConfig", ntStatus);
  962. return ntStatus;
  963. } /* HGM_JoystickConfig */
  964. /*****************************************************************************
  965. *
  966. * @doc EXTERNAL
  967. *
  968. * @func NTSTATUS | HGM_InitAnalog |
  969. *
  970. * Check that the configuration is valid whilst there is still time
  971. * to refuse it.
  972. * <nl>Detect and validate sibling relationships and call
  973. * HGM_JoystickConfig for the rest of the work.
  974. *
  975. * @parm IN PDEVICE_OBJECT | DeviceObject |
  976. *
  977. * Pointer to the device object
  978. *
  979. * @rvalue STATUS_SUCCESS | success
  980. * @rvalue STATUS_DEVICE_CONFIGURATION_ERROR | Invalid configuration specified
  981. *
  982. *****************************************************************************/
  983. /*
  984. * Disable warning for variable used before set as it is hard for a compiler
  985. * to see that the use of DeviceExtension_Sibling is gated by a flag which
  986. * can only be set after DeviceExtension_Sibling is initialized.
  987. */
  988. #pragma warning( disable:4701 )
  989. NTSTATUS EXTERNAL
  990. HGM_InitAnalog
  991. (
  992. IN PDEVICE_OBJECT DeviceObject
  993. )
  994. {
  995. NTSTATUS ntStatus;
  996. PDEVICE_EXTENSION DeviceExtension;
  997. PDEVICE_EXTENSION DeviceExtension_Sibling;
  998. PLIST_ENTRY pEntry;
  999. #define ARE_WE_RELATED(_x_, _y_) \
  1000. ( \
  1001. (_x_)->GameContext == (_y_)->GameContext && \
  1002. (_x_)->WriteAccessor == (_y_)->WriteAccessor && \
  1003. (_x_)->ReadAccessor == (_y_)->ReadAccessor \
  1004. )
  1005. PAGED_CODE ();
  1006. /*
  1007. * Get a pointer to the device extension
  1008. */
  1009. DeviceExtension = GET_MINIDRIVER_DEVICE_EXTENSION(DeviceObject);
  1010. /*
  1011. * No modifications to the Global List while we are looking at it
  1012. */
  1013. ExAcquireFastMutex (&Global.Mutex);
  1014. /*
  1015. * For two joysticks interface two fdos are created to service them
  1016. * but physically they both share the same port.
  1017. * For the second sibling certain extra rules must be applied so we
  1018. * search our list of devices for another device using the same port
  1019. * and if we find one mark this one as a sibling.
  1020. */
  1021. for(pEntry = Global.DeviceListHead.Flink;
  1022. pEntry != &Global.DeviceListHead;
  1023. pEntry = pEntry->Flink)
  1024. {
  1025. /*
  1026. * Obtain the device Extension of the Sibling
  1027. */
  1028. DeviceExtension_Sibling = CONTAINING_RECORD(pEntry, DEVICE_EXTENSION, Link);
  1029. if( DeviceExtension_Sibling != DeviceExtension
  1030. && TRUE == ARE_WE_RELATED(DeviceExtension, DeviceExtension_Sibling)
  1031. && TRUE == DeviceExtension_Sibling->fStarted )
  1032. {
  1033. #ifdef CHANGE_DEVICE
  1034. if( DeviceExtension_Sibling->fReplaced )
  1035. {
  1036. HGM_DBGPRINT(FILE_HIDJOY | HGM_BABBLE, ("Outgoing Sibling found (0x%x)", DeviceExtension_Sibling));
  1037. }
  1038. else
  1039. {
  1040. #endif /* CHANGE_DEVICE */
  1041. HGM_DBGPRINT(FILE_HIDJOY | HGM_BABBLE, ("Sibling found (0x%x)", DeviceExtension_Sibling));
  1042. DeviceExtension->fSiblingFound = TRUE;
  1043. #ifdef CHANGE_DEVICE
  1044. }
  1045. #endif /* CHANGE_DEVICE */
  1046. break;
  1047. }
  1048. }
  1049. /*
  1050. * We are done, release the Mutex
  1051. */
  1052. ExReleaseFastMutex (&Global.Mutex);
  1053. /*
  1054. * check the axis and button configuration for the joystick
  1055. */
  1056. ntStatus = HGM_JoystickConfig(DeviceObject);
  1057. if( NT_SUCCESS( ntStatus ) )
  1058. {
  1059. /*
  1060. * Make sure that sibling axes are not overlapped
  1061. */
  1062. if( DeviceExtension->fSiblingFound &&
  1063. (DeviceExtension_Sibling->resistiveInputMask & DeviceExtension->resistiveInputMask) != 0x0 )
  1064. {
  1065. HGM_DBGPRINT(FILE_HIDJOY |HGM_ERROR,\
  1066. ("HGM_InitDevice: OverLapping Resources ResisitiveInputMask(0x%x) Sibling(0x%x)",\
  1067. DeviceExtension->resistiveInputMask,DeviceExtension_Sibling->resistiveInputMask ));
  1068. ntStatus = STATUS_DEVICE_CONFIGURATION_ERROR;
  1069. }
  1070. }
  1071. else
  1072. {
  1073. HGM_DBGPRINT(FILE_HIDJOY | HGM_ERROR,\
  1074. ("HGM_InitDevice: JoystickConfig Failed"));
  1075. }
  1076. return( ntStatus );
  1077. } /* HGM_InitAnalog */
  1078. /*
  1079. * Change device sample only code
  1080. */
  1081. #ifdef CHANGE_DEVICE
  1082. /*****************************************************************************
  1083. *
  1084. * @doc EXTERNAL
  1085. *
  1086. * @func VOID | HGM_ChangeHandler |
  1087. *
  1088. * Use IOCTL_GAMEENUM_EXPOSE_SIBLING and IOCTL_GAMEENUM_REMOVE_SELF
  1089. * to change the attributes of the device.
  1090. *
  1091. * @parm IN PDEVICE_OBJECT | DeviceObject |
  1092. *
  1093. * Pointer to the device object
  1094. *
  1095. * @parm PIO_WORKITEM | WorkItem |
  1096. *
  1097. * The work item that this call is being processed under.
  1098. *
  1099. *****************************************************************************/
  1100. VOID
  1101. HGM_ChangeHandler
  1102. (
  1103. IN PDEVICE_OBJECT DeviceObject,
  1104. PIO_WORKITEM WorkItem
  1105. )
  1106. {
  1107. NTSTATUS ntStatus = STATUS_SUCCESS;
  1108. PDEVICE_EXTENSION DeviceExtension;
  1109. KEVENT IoctlCompleteEvent;
  1110. IO_STATUS_BLOCK IoStatus;
  1111. PIO_STACK_LOCATION irpStack, nextStack;
  1112. PIRP pIrp;
  1113. PVOID SiblingHandle;
  1114. GAMEENUM_EXPOSE_SIBLING ExposeSibling;
  1115. PAGED_CODE ();
  1116. /*
  1117. * Get a pointer to the device extension
  1118. */
  1119. DeviceExtension = GET_MINIDRIVER_DEVICE_EXTENSION(DeviceObject);
  1120. HGM_DBGPRINT(FILE_HIDJOY | HGM_FENTRY,\
  1121. ("HGM_ChangeHandler(DeviceExtension=0x%x)", DeviceExtension));
  1122. KeInitializeEvent(&IoctlCompleteEvent, NotificationEvent, FALSE);
  1123. pIrp = IoBuildDeviceIoControlRequest (
  1124. IOCTL_GAMEENUM_EXPOSE_SIBLING,
  1125. DeviceExtension->NextDeviceObject,
  1126. &ExposeSibling,
  1127. sizeof( ExposeSibling ),
  1128. &ExposeSibling,
  1129. sizeof( ExposeSibling ),
  1130. TRUE,
  1131. &IoctlCompleteEvent,
  1132. &IoStatus);
  1133. if( pIrp )
  1134. {
  1135. /*
  1136. * For demonstration purposes only, we don't actually change the
  1137. * device, we just re-expose the same one. If the device really
  1138. * needs to be changed, this would be signalled either by a
  1139. * change in the OemData on the newly exposed device or by using
  1140. * a specific HardwareID string.
  1141. * Note the nAxis and nButton fields will always be zero for an
  1142. * exposed sibling.
  1143. */
  1144. RtlZeroMemory( &ExposeSibling, sizeof( ExposeSibling ) );
  1145. ExposeSibling.Size = sizeof( ExposeSibling );
  1146. ExposeSibling.HardwareHandle = &SiblingHandle;
  1147. C_ASSERT( sizeof( ExposeSibling.OemData ) == sizeof( DeviceExtension->HidGameOemData.Game_Oem_Data ) );
  1148. RtlCopyMemory(ExposeSibling.OemData, DeviceExtension->HidGameOemData.Game_Oem_Data, sizeof(ExposeSibling.OemData));
  1149. ASSERT( ExposeSibling.UnitID == 0 );
  1150. /*
  1151. * Setting a NULL pointer causes the HardwareID of this sibling to be used
  1152. */
  1153. ExposeSibling.HardwareIDs = NULL;
  1154. /*
  1155. * issue a synchronous request to GameEnum to expose this new sibling
  1156. */
  1157. ntStatus = IoCallDriver( DeviceExtension->NextDeviceObject, pIrp );
  1158. if( ntStatus == STATUS_PENDING )
  1159. {
  1160. ntStatus = KeWaitForSingleObject (&IoctlCompleteEvent, Executive, KernelMode, FALSE, NULL);
  1161. }
  1162. if( NT_SUCCESS(ntStatus) )
  1163. {
  1164. /*
  1165. * All went well so remove self
  1166. */
  1167. HGM_DBGPRINT(FILE_HIDJOY | HGM_BABBLE, ("Sibling exposed!"));
  1168. pIrp = IoBuildDeviceIoControlRequest (
  1169. IOCTL_GAMEENUM_REMOVE_SELF,
  1170. DeviceExtension->NextDeviceObject,
  1171. NULL,
  1172. 0,
  1173. NULL,
  1174. 0,
  1175. TRUE,
  1176. &IoctlCompleteEvent,
  1177. &IoStatus);
  1178. if( pIrp )
  1179. {
  1180. /*
  1181. * issue a synchronous request to GameEnum to remove self
  1182. */
  1183. ntStatus = IoCallDriver( DeviceExtension->NextDeviceObject, pIrp );
  1184. if( ntStatus == STATUS_PENDING )
  1185. {
  1186. ntStatus = KeWaitForSingleObject( &IoctlCompleteEvent, Executive, KernelMode, FALSE, NULL );
  1187. }
  1188. if( NT_SUCCESS(ntStatus) )
  1189. {
  1190. /*
  1191. * All done
  1192. */
  1193. HGM_DBGPRINT(FILE_HIDJOY | HGM_BABBLE, ("Removed self!"));
  1194. }
  1195. else
  1196. {
  1197. /*
  1198. * Something bad happened but there's little we can do
  1199. */
  1200. HGM_DBGPRINT(FILE_HIDJOY | HGM_ERROR,\
  1201. ("Failed to remove self with GameEnum error: 0x%08x", \
  1202. ntStatus));
  1203. }
  1204. }
  1205. else
  1206. {
  1207. ntStatus = STATUS_NO_MEMORY;
  1208. HGM_DBGPRINT(FILE_HIDJOY | HGM_ERROR, \
  1209. ("Failed to create IRP for remove self") );
  1210. }
  1211. }
  1212. else
  1213. {
  1214. /*
  1215. * Something bad happened so reset the flag and carry on
  1216. */
  1217. DeviceExtension->fReplaced = FALSE;
  1218. HGM_DBGPRINT(FILE_HIDJOY | HGM_WARN,\
  1219. ("Failed to expose sibling with GameEnum error: 0x%08x", ntStatus));
  1220. }
  1221. }
  1222. else
  1223. {
  1224. ntStatus = STATUS_NO_MEMORY;
  1225. DeviceExtension->fReplaced = FALSE;
  1226. HGM_DBGPRINT(FILE_HIDJOY | HGM_ERROR, \
  1227. ("Failed to create IRP for expose sibling") );
  1228. }
  1229. /*
  1230. * The work is done, so free the resources
  1231. */
  1232. IoFreeWorkItem( WorkItem );
  1233. /*
  1234. * We've finished touching the DeviceExtension now.
  1235. */
  1236. HGM_DecRequestCount( DeviceExtension );
  1237. HGM_EXITPROC(FILE_HIDJOY|HGM_FEXIT_STATUSOK, "HGM_ChangeHandler", ntStatus);
  1238. return;
  1239. } /* HGM_ChangeHandler */
  1240. /*****************************************************************************
  1241. *
  1242. * @doc EXTERNAL
  1243. *
  1244. * @func VOID | HGM_DeviceChanged |
  1245. *
  1246. * Start the process of changing the device attributes by stashing
  1247. * away all the data needed and then initializing and queuing a work
  1248. * item to call the IOCTL at the required PASSIVE_LEVEL.
  1249. *
  1250. * @parm IN PDEVICE_OBJECT | DeviceObject |
  1251. *
  1252. * Pointer to the device object
  1253. *
  1254. * @parm IN OUT PDEVICE_EXTENSION | DeviceExtension |
  1255. *
  1256. * Pointer to the mini-driver device extension.
  1257. *
  1258. *****************************************************************************/
  1259. VOID
  1260. HGM_DeviceChanged
  1261. (
  1262. IN PDEVICE_OBJECT DeviceObject,
  1263. IN OUT PDEVICE_EXTENSION DeviceExtension
  1264. )
  1265. {
  1266. NTSTATUS ntStatus;
  1267. PIO_WORKITEM WorkItem;
  1268. /*
  1269. * Since the work item will use the device extension, bump the usage
  1270. * count up one in case anyone tries to remove the device between
  1271. * now and when the work item gets to run. If that fails, forget it.
  1272. */
  1273. ntStatus = HGM_IncRequestCount( DeviceExtension );
  1274. if( NT_SUCCESS(ntStatus) )
  1275. {
  1276. WorkItem = IoAllocateWorkItem( DeviceObject );
  1277. if( WorkItem )
  1278. {
  1279. DeviceExtension->fReplaced = TRUE;
  1280. IoQueueWorkItem( WorkItem, (PIO_WORKITEM_ROUTINE)HGM_ChangeHandler,
  1281. DelayedWorkQueue, WorkItem );
  1282. }
  1283. else
  1284. {
  1285. HGM_DecRequestCount( DeviceExtension );
  1286. HGM_DBGPRINT(FILE_HIDJOY | HGM_WARN, ("Failed to allocate work item to change device") );
  1287. }
  1288. }
  1289. else
  1290. {
  1291. HGM_DBGPRINT(FILE_HIDJOY | HGM_WARN, ("Failed to change device as device is being removed") );
  1292. }
  1293. } /* HGM_DeviceChanged */
  1294. #endif /* CHANGE_DEVICE */
  1295. /*****************************************************************************
  1296. *
  1297. * @doc EXTERNAL
  1298. *
  1299. * @func VOID | HGM_Game2HID |
  1300. *
  1301. * Process the data returned from polling the gameport into values
  1302. * and buttons for returning to HID.
  1303. * <nl>The meaning of the data is interpreted according to the
  1304. * characteristics of the device described in the hardware settings
  1305. * flags.
  1306. *
  1307. * @parm IN PDEVICE_OBJECT | DeviceObject |
  1308. *
  1309. * Pointer to the device object
  1310. *
  1311. * @parm IN PDEVICE_EXTENSION | DeviceExtension |
  1312. *
  1313. * Pointer to the mini-driver device extension.
  1314. *
  1315. * @parm IN OUT PUHIDGAME_INPUT_DATA | pHIDData |
  1316. *
  1317. * Pointer to the buffer into which the HID report should be written.
  1318. * This buffer must be assumed to be unaligned.
  1319. *
  1320. *****************************************************************************/
  1321. VOID
  1322. HGM_Game2HID
  1323. (
  1324. #ifdef CHANGE_DEVICE
  1325. IN PDEVICE_OBJECT DeviceObject,
  1326. #endif /* CHANGE_DEVICE */
  1327. IN PDEVICE_EXTENSION DeviceExtension,
  1328. IN OUT PUHIDGAME_INPUT_DATA pHIDData
  1329. )
  1330. {
  1331. LONG Idx;
  1332. /*
  1333. * Use a local buffer to assemble the report as the real buffer may not
  1334. * be aligned.
  1335. */
  1336. HIDGAME_INPUT_DATA LocalBuffer;
  1337. RtlZeroMemory( &LocalBuffer, sizeof( LocalBuffer ) );
  1338. /*
  1339. * Remap axis
  1340. */
  1341. for(Idx = 0x0; Idx < DeviceExtension->nAxes; Idx++ )
  1342. {
  1343. LocalBuffer.Axis[Idx] = DeviceExtension->LastGoodAxis[DeviceExtension->AxisMap[Idx]];
  1344. }
  1345. /*
  1346. * Copy buttons and remap any POVs
  1347. */
  1348. if( DeviceExtension->fSiblingFound )
  1349. {
  1350. /*
  1351. * Simplest case, 2nd half poll must be 2A 2B
  1352. */
  1353. LocalBuffer.Button[0] = DeviceExtension->LastGoodButton[2];
  1354. LocalBuffer.Button[1] = DeviceExtension->LastGoodButton[3];
  1355. }
  1356. else if( DeviceExtension->HidGameOemData.OemData[0].joy_hws_dwFlags & JOY_HWS_POVISBUTTONCOMBOS )
  1357. {
  1358. UCHAR Buttons = 0;
  1359. for( Idx = 3; Idx >=0; Idx-- )
  1360. {
  1361. Buttons <<= 1;
  1362. Buttons = (UCHAR)(Buttons + (UCHAR)(DeviceExtension->LastGoodButton[Idx] >> BUTTON_BIT));
  1363. }
  1364. if( DeviceExtension->HidGameOemData.OemData[0].joy_hws_dwFlags & JOY_HWS_HASPOV2 )
  1365. {
  1366. Idx = c2PComboLU[Buttons];
  1367. }
  1368. else
  1369. {
  1370. Idx = c1PComboLU[Buttons];
  1371. }
  1372. if( Idx >= P1_NULL )
  1373. {
  1374. if( Idx < P2_NULL )
  1375. {
  1376. LocalBuffer.hatswitch[0] = (UCHAR)(Idx & POV_MASK);
  1377. }
  1378. else
  1379. {
  1380. LocalBuffer.hatswitch[1] = (UCHAR)(Idx & POV_MASK);
  1381. }
  1382. }
  1383. else
  1384. {
  1385. #ifdef CHANGE_DEVICE
  1386. if( ( Idx >= DeviceExtension->nButtons ) && ( !DeviceExtension->fReplaced ) )
  1387. {
  1388. /*
  1389. * If a higher button was pressed than expected, use
  1390. * remove_self/expose_sibling to change expectations.
  1391. */
  1392. HGM_DeviceChanged( DeviceObject, DeviceExtension );
  1393. }
  1394. #endif /* CHANGE_DEVICE */
  1395. LocalBuffer.Button[Idx] = BUTTON_ON;
  1396. }
  1397. }
  1398. else
  1399. {
  1400. if( DeviceExtension->HidGameOemData.OemData[0].joy_hws_dwFlags & JOY_HWS_POVISPOLL )
  1401. {
  1402. /*
  1403. * Following the axis mapping loop, Idx is one larger than
  1404. * DeviceExtension->nAxes which is the correct index for a
  1405. * polled POV.
  1406. */
  1407. LocalBuffer.Axis[Idx] = DeviceExtension->LastGoodAxis[DeviceExtension->povMap];
  1408. }
  1409. /*
  1410. * Check buttons on R and Z axes
  1411. */
  1412. if( DeviceExtension->nButtons > 5 )
  1413. {
  1414. if( DeviceExtension->LastGoodAxis[3] > DeviceExtension->button6limit )
  1415. {
  1416. /*
  1417. * New max found so button is off
  1418. */
  1419. HGM_DBGPRINT( FILE_HIDJOY | HGM_BABBLE2, \
  1420. ("HGM_Game2HID: Changing button 6 limit from %u to %u", \
  1421. DeviceExtension->button6limit, DeviceExtension->LastGoodAxis[3] ) ) ;
  1422. DeviceExtension->button6limit = DeviceExtension->LastGoodAxis[3];
  1423. }
  1424. else if( DeviceExtension->LastGoodAxis[3] < (DeviceExtension->button6limit>>1) )
  1425. {
  1426. LocalBuffer.Button[5] = BUTTON_ON;
  1427. }
  1428. }
  1429. if( DeviceExtension->nButtons > 4 )
  1430. {
  1431. Idx = 4;
  1432. if( DeviceExtension->LastGoodAxis[2] > DeviceExtension->button5limit )
  1433. {
  1434. /*
  1435. * New max found so button is off
  1436. */
  1437. HGM_DBGPRINT( FILE_HIDJOY | HGM_BABBLE2, \
  1438. ("HGM_Game2HID: Changing button 5 limit from %u to %u", \
  1439. DeviceExtension->button5limit, DeviceExtension->LastGoodAxis[2] ) ) ;
  1440. DeviceExtension->button5limit = DeviceExtension->LastGoodAxis[2];
  1441. }
  1442. else if( DeviceExtension->LastGoodAxis[2] < (DeviceExtension->button5limit>>1) )
  1443. {
  1444. LocalBuffer.Button[4] = BUTTON_ON;
  1445. }
  1446. }
  1447. else
  1448. {
  1449. Idx = DeviceExtension->nButtons;
  1450. }
  1451. /*
  1452. * Copy all standard buttons
  1453. */
  1454. while( Idx-- )
  1455. {
  1456. LocalBuffer.Button[Idx] = DeviceExtension->LastGoodButton[Idx];
  1457. }
  1458. }
  1459. C_ASSERT( sizeof( *pHIDData ) == sizeof( LocalBuffer ) );
  1460. RtlCopyMemory( pHIDData, &LocalBuffer, sizeof( LocalBuffer ) );
  1461. HGM_DBGPRINT( FILE_HIDJOY | HGM_BABBLE2, \
  1462. ("HGM_Game2HID: Axes: X: %08x Y: %08x Z: %08x R: %08x", \
  1463. LocalBuffer.Axis[0], LocalBuffer.Axis[1], LocalBuffer.Axis[2], LocalBuffer.Axis[3] ) ) ;
  1464. HGM_DBGPRINT( FILE_HIDJOY | HGM_BABBLE2, \
  1465. ("HGM_Game2HID: P1: %d P2: %d Buttons %d, %d, %d, %d, %d, %d, %d, %d, %d, %d", \
  1466. LocalBuffer.hatswitch[0], LocalBuffer.hatswitch[1], \
  1467. LocalBuffer.Button[0], LocalBuffer.Button[1], LocalBuffer.Button[2], LocalBuffer.Button[3], LocalBuffer.Button[4], \
  1468. LocalBuffer.Button[5], LocalBuffer.Button[6], LocalBuffer.Button[7], LocalBuffer.Button[8], LocalBuffer.Button[9] ) ) ;
  1469. } /* HGM_Game2HID */