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.

1683 lines
55 KiB

  1. /*++
  2. Copyright (c) 1998 - 1999 Microsoft Corporation
  3. Module Name:
  4. pnp.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()
  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. POEMDATA OemData;
  545. int InitialAxisMappings[MAX_AXES];
  546. typedef struct _USAGES
  547. {
  548. UCHAR UsagePage;
  549. UCHAR Usage;
  550. } USAGES, *PUSAGE;
  551. typedef struct _JOYCLASSPARAMS
  552. {
  553. UCHAR TopLevelUsage;
  554. USAGES Usages[MAX_AXES];
  555. } JOYCLASSPARAMS, *PJOYCLASSPARAMS;
  556. PJOYCLASSPARAMS pJoyParams;
  557. /*
  558. * Canned parameters for devices
  559. * The top-level usage must be either HID_USAGE_GENERIC_JOYSTICK or
  560. * HID_USAGE_GENERIC_GAMEPAD in order for the device to be treated as a
  561. * game controller.
  562. * The poll limits are specified in uSecs so the value stored here is 1000000/x
  563. */
  564. JOYCLASSPARAMS JoystickParams =
  565. {
  566. HID_USAGE_GENERIC_JOYSTICK,
  567. { { HID_USAGE_PAGE_GENERIC, HID_USAGE_GENERIC_X} ,
  568. { HID_USAGE_PAGE_GENERIC, HID_USAGE_GENERIC_Y} ,
  569. { HID_USAGE_PAGE_SIMULATION, HID_USAGE_SIMULATION_THROTTLE} ,
  570. { HID_USAGE_PAGE_SIMULATION, HID_USAGE_SIMULATION_RUDDER} }
  571. };
  572. JOYCLASSPARAMS GamepadParams =
  573. {
  574. HID_USAGE_GENERIC_GAMEPAD,
  575. { { HID_USAGE_PAGE_GENERIC, HID_USAGE_GENERIC_X} ,
  576. { HID_USAGE_PAGE_GENERIC, HID_USAGE_GENERIC_Y} ,
  577. { HID_USAGE_PAGE_SIMULATION, HID_USAGE_SIMULATION_THROTTLE} ,
  578. { HID_USAGE_PAGE_SIMULATION, HID_USAGE_SIMULATION_RUDDER} }
  579. };
  580. JOYCLASSPARAMS CarCtrlParams =
  581. {
  582. HID_USAGE_GENERIC_JOYSTICK,
  583. { { HID_USAGE_PAGE_GENERIC, HID_USAGE_GENERIC_X} ,
  584. { HID_USAGE_PAGE_GENERIC, HID_USAGE_GENERIC_Y} ,
  585. { HID_USAGE_PAGE_SIMULATION, HID_USAGE_SIMULATION_THROTTLE} ,
  586. { HID_USAGE_PAGE_SIMULATION, HID_USAGE_SIMULATION_RUDDER} }
  587. };
  588. JOYCLASSPARAMS FlightYokeParams =
  589. {
  590. HID_USAGE_GENERIC_JOYSTICK,
  591. { { HID_USAGE_PAGE_GENERIC, HID_USAGE_GENERIC_X} ,
  592. { HID_USAGE_PAGE_GENERIC, HID_USAGE_GENERIC_Y} ,
  593. { HID_USAGE_PAGE_SIMULATION, HID_USAGE_SIMULATION_THROTTLE} ,
  594. { HID_USAGE_PAGE_SIMULATION, HID_USAGE_SIMULATION_RUDDER} }
  595. };
  596. PAGED_CODE();
  597. HGM_DBGPRINT( FILE_HIDJOY | HGM_FENTRY,\
  598. ("HGM_GenerateReport(ucIn=0x%x,DeviceObject=0x%x)",\
  599. rgGameReport, DeviceObject) );
  600. /*
  601. * Get a pointer to the device extension
  602. */
  603. DeviceExtension = GET_MINIDRIVER_DEVICE_EXTENSION(DeviceObject);
  604. /*
  605. * Although the axes have already been validated and mapped in
  606. * HGM_JoystickConfig this function destroys the mapping when it compacts
  607. * the axes towards the start of the descriptor report. Since this
  608. * function will be called once to find the descriptor length and then
  609. * again to read the report, the mappings are regenerated again each
  610. * time through. Although this results in the parameters being
  611. * interpreted three times (for validation, descriptor size and
  612. * descriptor content) it avoids the possibility of a discrepancy in
  613. * implementation of separate functions.
  614. */
  615. ntStatus = HGM_MapAxesFromDevExt( DeviceExtension );
  616. ASSERTMSG( "HGM_GenerateReport:", ntStatus == STATUS_SUCCESS );
  617. pucReport = rgGameReport;
  618. dwFlags = DeviceExtension->HidGameOemData.OemData[(DeviceExtension->fSiblingFound!=0)].joy_hws_dwFlags;
  619. /*
  620. * What manner of beast have we ?
  621. */
  622. if( dwFlags & JOY_HWS_ISGAMEPAD )
  623. {
  624. pJoyParams = &GamepadParams;
  625. }
  626. else if( dwFlags & JOY_HWS_ISYOKE )
  627. {
  628. pJoyParams = &FlightYokeParams;
  629. }
  630. else if( dwFlags & JOY_HWS_ISCARCTRL )
  631. {
  632. pJoyParams = &CarCtrlParams;
  633. }
  634. else
  635. {
  636. pJoyParams = &JoystickParams;
  637. }
  638. #define NEXT_BYTE( pReport, Data ) \
  639. ASSERTMSG( "HGM_GenerateReport:", pReport+sizeof(UCHAR)-rgGameReport < MAXBYTES_GAME_REPORT ); \
  640. *pReport++ = Data;
  641. #define NEXT_LONG( pReport, Data ) \
  642. ASSERTMSG( "HGM_GenerateReport:", pReport+sizeof(ULONG)-rgGameReport < MAXBYTES_GAME_REPORT); \
  643. *(((LONG UNALIGNED*)(pReport))++) = Data;
  644. #define ITEM_DEFAULT 0x00 /* Data, Array, Absolute, No Wrap, Linear, Preferred State, Has no NULL */
  645. #define ITEM_VARIABLE 0x02 /* as ITEM_DEFAULT but value is a variable, not an array */
  646. #define ITEM_HASNULL 0x40 /* as ITEM_DEFAULT but values out of range are considered NULL */
  647. #define ITEM_ANALOG_AXIS ITEM_VARIABLE
  648. #define ITEM_DIGITAL_POV (ITEM_VARIABLE|ITEM_HASNULL)
  649. #define ITEM_BUTTON ITEM_VARIABLE
  650. #define ITEM_PADDING 0x01 /* Constant (nothing else applies) */
  651. /* USAGE_PAGE (Generic Desktop) */
  652. NEXT_BYTE(pucReport, HIDP_GLOBAL_USAGE_PAGE_1);
  653. NEXT_BYTE(pucReport, HID_USAGE_PAGE_GENERIC);
  654. /* USAGE (Joystick | GamePad ) */
  655. NEXT_BYTE(pucReport, HIDP_LOCAL_USAGE_1);
  656. NEXT_BYTE(pucReport, pJoyParams->TopLevelUsage);
  657. /* Logical Min is the smallest value that could be produced by a poll */
  658. NEXT_BYTE(pucReport, HIDP_GLOBAL_LOG_MIN_4);
  659. NEXT_LONG(pucReport, 0 );
  660. /* Logical Max is the largest value that could be produced by a poll */
  661. NEXT_BYTE(pucReport, HIDP_GLOBAL_LOG_MAX_4);
  662. NEXT_LONG(pucReport, AXIS_FULL_SCALE );
  663. /* Start a Linked collection */
  664. /*
  665. * Since this is a generic driver we know knothing about the physical
  666. * distribution of controls on the device so we put everything in a
  667. * single collection. If, for instance, we knew that some buttons were
  668. * on the base and some on the stick we could better describe them by
  669. * reporting them in separate collections.
  670. */
  671. NEXT_BYTE(pucReport, HIDP_MAIN_COLLECTION);
  672. NEXT_BYTE(pucReport, 0x0 );
  673. /* Define one axis at a time */
  674. NEXT_BYTE(pucReport, HIDP_GLOBAL_REPORT_COUNT_1);
  675. NEXT_BYTE(pucReport, 0x1);
  676. /* Each axis is a 32 bits value */
  677. NEXT_BYTE(pucReport, HIDP_GLOBAL_REPORT_SIZE);
  678. NEXT_BYTE(pucReport, 8 * sizeof(ULONG) );
  679. /*
  680. * Do the axis
  681. * Although HID could cope with the "active" axes being mixed with the
  682. * dummy ones, it makes life simpler to move them to the start.
  683. * Pass through all the axis maps generated by HGM_JoystickConfig
  684. * and map all the active ones into the descriptor, copying the usages
  685. * appropriate for the type of device.
  686. * Since a polled POV is nothing more than a different interpretation
  687. * of axis data, this is added after any axes.
  688. */
  689. C_ASSERT( sizeof( InitialAxisMappings ) == sizeof( DeviceExtension->AxisMap ) );
  690. RtlCopyMemory( InitialAxisMappings, DeviceExtension->AxisMap, sizeof( InitialAxisMappings ) );
  691. Idx = 0;
  692. for( UsageIdx = 0; UsageIdx < MAX_AXES; UsageIdx++ )
  693. {
  694. if( InitialAxisMappings[UsageIdx] >= INVALID_INDEX )
  695. {
  696. continue;
  697. }
  698. DeviceExtension->AxisMap[Idx] = InitialAxisMappings[UsageIdx];
  699. NEXT_BYTE(pucReport, HIDP_LOCAL_USAGE_4);
  700. NEXT_BYTE(pucReport, pJoyParams->Usages[UsageIdx].Usage);
  701. NEXT_BYTE(pucReport, 0x0);
  702. NEXT_BYTE(pucReport, pJoyParams->Usages[UsageIdx].UsagePage);
  703. NEXT_BYTE(pucReport, 0x0);
  704. /* Data Field */
  705. NEXT_BYTE(pucReport, HIDP_MAIN_INPUT_1);
  706. NEXT_BYTE(pucReport, ITEM_ANALOG_AXIS);
  707. HGM_DBGPRINT( FILE_HIDJOY | HGM_GEN_REPORT, \
  708. ("HGM_GenerateReport:Idx=%d, UsageIdx=%d, Mapping=%d, Usage=%02x, Page=%02x",\
  709. Idx, UsageIdx, DeviceExtension->AxisMap[UsageIdx], \
  710. pJoyParams->Usages[UsageIdx].Usage, pJoyParams->Usages[UsageIdx].UsagePage ) ) ;
  711. Idx++;
  712. }
  713. if( dwFlags & JOY_HWS_POVISPOLL )
  714. {
  715. /*
  716. * A polled POV is just the same as an axis.
  717. * Note, we have already checked that there is an axis for use as the POV.
  718. * Also, this type of POV can be distinguished from a digital POV by it's
  719. * lack of a NULL value.
  720. */
  721. NEXT_BYTE(pucReport, HIDP_LOCAL_USAGE_4);
  722. NEXT_BYTE(pucReport, HID_USAGE_GENERIC_HATSWITCH);
  723. NEXT_BYTE(pucReport, 0x0);
  724. NEXT_BYTE(pucReport, HID_USAGE_PAGE_GENERIC);
  725. NEXT_BYTE(pucReport, 0x0);
  726. /* Data Field */
  727. NEXT_BYTE(pucReport, HIDP_MAIN_INPUT_1);
  728. NEXT_BYTE(pucReport, ITEM_ANALOG_AXIS);
  729. HGM_DBGPRINT( FILE_HIDJOY | HGM_GEN_REPORT, \
  730. ("HGM_GenerateReport:Idx=%d, Set to polled POV", Idx ) ) ;
  731. Idx++;
  732. }
  733. /*
  734. * Now fill in any remaining axis values as dummys
  735. */
  736. while( Idx < MAX_AXES )
  737. {
  738. /* Constant Field */
  739. NEXT_BYTE(pucReport, HIDP_MAIN_INPUT_1);
  740. NEXT_BYTE(pucReport, ITEM_PADDING);
  741. HGM_DBGPRINT( FILE_HIDJOY | HGM_GEN_REPORT, \
  742. ("HGM_GenerateReport:Idx=%d, Set to constant field", Idx ) ) ;
  743. Idx++;
  744. }
  745. /*
  746. * Now move on to the byte sized fields
  747. */
  748. if( dwFlags & JOY_HWS_POVISBUTTONCOMBOS )
  749. {
  750. /*
  751. * Redefine the logical and physical ranges from now on
  752. * A digital POV has a NULL value (a value outside the logical range)
  753. * when the POV is centered. To make life easier call the NULL value
  754. * zero, so the logical range is from 1 to 4.
  755. /* Logical Min */
  756. NEXT_BYTE(pucReport, HIDP_GLOBAL_LOG_MIN_1);
  757. NEXT_BYTE(pucReport, 1 );
  758. /* Logical Max */
  759. NEXT_BYTE(pucReport, HIDP_GLOBAL_LOG_MAX_1);
  760. NEXT_BYTE(pucReport, 4 );
  761. /*
  762. * report for digital POV is 3 bits data plus 5 constant bits to fill
  763. * the byte.
  764. */
  765. NEXT_BYTE(pucReport, HIDP_LOCAL_USAGE_4);
  766. NEXT_BYTE(pucReport, HID_USAGE_GENERIC_HATSWITCH);
  767. NEXT_BYTE(pucReport, 0x0);
  768. NEXT_BYTE(pucReport, HID_USAGE_PAGE_GENERIC);
  769. NEXT_BYTE(pucReport, 0x0);
  770. /* Data Field */
  771. NEXT_BYTE(pucReport, HIDP_GLOBAL_REPORT_SIZE);
  772. NEXT_BYTE(pucReport, 0x3);
  773. NEXT_BYTE(pucReport, HIDP_MAIN_INPUT_1);
  774. NEXT_BYTE(pucReport, ITEM_DIGITAL_POV);
  775. /* top 5 bits constant */
  776. NEXT_BYTE(pucReport, HIDP_GLOBAL_REPORT_SIZE);
  777. NEXT_BYTE(pucReport, 0x5);
  778. NEXT_BYTE(pucReport, HIDP_MAIN_INPUT_1);
  779. NEXT_BYTE(pucReport, ITEM_PADDING);
  780. HGM_DBGPRINT( FILE_HIDJOY | HGM_GEN_REPORT, \
  781. ("HGM_GenerateReport:First button combo POV is on" ) ) ;
  782. if( dwFlags & JOY_HWS_HASPOV2 )
  783. {
  784. NEXT_BYTE(pucReport, HIDP_LOCAL_USAGE_4);
  785. NEXT_BYTE(pucReport, HID_USAGE_GENERIC_HATSWITCH);
  786. NEXT_BYTE(pucReport, 0x0);
  787. NEXT_BYTE(pucReport, HID_USAGE_PAGE_GENERIC);
  788. NEXT_BYTE(pucReport, 0x0);
  789. /* Data Field */
  790. NEXT_BYTE(pucReport, HIDP_GLOBAL_REPORT_SIZE);
  791. NEXT_BYTE(pucReport, 0x3);
  792. NEXT_BYTE(pucReport, HIDP_MAIN_INPUT_1);
  793. NEXT_BYTE(pucReport, ITEM_DIGITAL_POV);
  794. /* top 5 bits constant */
  795. NEXT_BYTE(pucReport, HIDP_GLOBAL_REPORT_SIZE);
  796. NEXT_BYTE(pucReport, 0x5);
  797. NEXT_BYTE(pucReport, HIDP_MAIN_INPUT_1);
  798. NEXT_BYTE(pucReport, ITEM_PADDING);
  799. HGM_DBGPRINT( FILE_HIDJOY | HGM_GEN_REPORT, \
  800. ("HGM_GenerateReport:Second button combo POV is on" ) ) ;
  801. }
  802. else
  803. {
  804. /* 8 bits of constant data instead of second POV */
  805. NEXT_BYTE(pucReport, HIDP_GLOBAL_REPORT_SIZE);
  806. NEXT_BYTE(pucReport, 0x8);
  807. /* Constant Field */
  808. NEXT_BYTE(pucReport, HIDP_MAIN_INPUT_1);
  809. NEXT_BYTE(pucReport, ITEM_PADDING);
  810. HGM_DBGPRINT( FILE_HIDJOY | HGM_GEN_REPORT, \
  811. ("HGM_GenerateReport:No second button combo POV" ) ) ;
  812. }
  813. }
  814. else
  815. {
  816. /* 16 bits of constant data instead of button combo POVs */
  817. NEXT_BYTE(pucReport, HIDP_GLOBAL_REPORT_SIZE);
  818. NEXT_BYTE(pucReport, 0x10);
  819. /* Constant Field */
  820. NEXT_BYTE(pucReport, HIDP_MAIN_INPUT_1);
  821. NEXT_BYTE(pucReport, ITEM_PADDING);
  822. HGM_DBGPRINT( FILE_HIDJOY | HGM_GEN_REPORT, \
  823. ("HGM_GenerateReport:Button combo POV are off" ) ) ;
  824. }
  825. /*
  826. * Now the buttons
  827. */
  828. for( Idx = 0x0; Idx < DeviceExtension->nButtons; Idx++ )
  829. {
  830. /* Report size is 1 bit for button */
  831. NEXT_BYTE(pucReport, HIDP_GLOBAL_REPORT_SIZE);
  832. NEXT_BYTE(pucReport, 0x1);
  833. NEXT_BYTE(pucReport, HIDP_LOCAL_USAGE_4);
  834. NEXT_BYTE(pucReport, (UCHAR)(Idx + 1) );
  835. NEXT_BYTE(pucReport, 0x0);
  836. NEXT_BYTE(pucReport, HID_USAGE_PAGE_BUTTON);
  837. NEXT_BYTE(pucReport, 0x0);
  838. /* Data field */
  839. NEXT_BYTE(pucReport, HIDP_MAIN_INPUT_1);
  840. NEXT_BYTE(pucReport, ITEM_BUTTON);
  841. /* 7 bits of constant data */
  842. NEXT_BYTE(pucReport, HIDP_GLOBAL_REPORT_SIZE);
  843. NEXT_BYTE(pucReport, 0x7);
  844. NEXT_BYTE(pucReport, HIDP_MAIN_INPUT_1);
  845. NEXT_BYTE(pucReport, ITEM_PADDING);
  846. HGM_DBGPRINT( FILE_HIDJOY | HGM_GEN_REPORT, \
  847. ("HGM_GenerateReport:Button %u on",Idx ) ) ;
  848. }
  849. if( Idx < MAX_BUTTONS )
  850. {
  851. /* Constant report for 8 * unused buttons bits */
  852. NEXT_BYTE(pucReport, HIDP_GLOBAL_REPORT_SIZE);
  853. NEXT_BYTE(pucReport, (UCHAR)((MAX_BUTTONS-Idx)*8) );
  854. /* Constant Field */
  855. NEXT_BYTE(pucReport, HIDP_MAIN_INPUT_1);
  856. NEXT_BYTE(pucReport, ITEM_PADDING);
  857. HGM_DBGPRINT( FILE_HIDJOY | HGM_GEN_REPORT, \
  858. ("HGM_GenerateReport:Last %u buttons off",MAX_BUTTONS-Idx ) ) ;
  859. }
  860. /* End of collection, We're done ! */
  861. NEXT_BYTE(pucReport, HIDP_MAIN_ENDCOLLECTION);
  862. #undef NEXT_BYTE
  863. #undef NEXT_LONG
  864. if( pucReport - rgGameReport > MAXBYTES_GAME_REPORT)
  865. {
  866. ntStatus = STATUS_BUFFER_TOO_SMALL;
  867. *pCbReport = 0x0;
  868. RtlZeroMemory(rgGameReport, sizeof(rgGameReport));
  869. } else
  870. {
  871. *pCbReport = (USHORT) (pucReport - rgGameReport);
  872. ntStatus = STATUS_SUCCESS;
  873. }
  874. HGM_DBGPRINT( FILE_HIDJOY | HGM_GEN_REPORT,\
  875. ("HGM_GenerateReport: ReportSize=0x%x",\
  876. *pCbReport) );
  877. HGM_EXITPROC(FILE_HIDJOY | HGM_FEXIT_STATUSOK, "HGM_GenerateReport", ntStatus);
  878. return ( ntStatus );
  879. } /* HGM_GenerateReport */
  880. /*****************************************************************************
  881. *
  882. * @doc EXTERNAL
  883. *
  884. * @func NTSTATUS | HGM_JoystickConfig |
  885. *
  886. * Check that the configuration is valid whilst there is still time
  887. * to refuse it.
  888. * <nl>HGM_GenerateReport uses the results generated here if the
  889. * settings are OK.
  890. *
  891. * @parm IN PDEVICE_OBJECT | DeviceObject |
  892. *
  893. * Pointer to the device object
  894. *
  895. * @rvalue STATUS_SUCCESS | success
  896. * @rvalue STATUS_DEVICE_CONFIGURATION_ERROR | Invalid configuration specified
  897. *
  898. *****************************************************************************/
  899. NTSTATUS INTERNAL
  900. HGM_JoystickConfig
  901. (
  902. IN PDEVICE_OBJECT DeviceObject
  903. )
  904. {
  905. PDEVICE_EXTENSION DeviceExtension;
  906. POEMDATA OemData;
  907. NTSTATUS ntStatus;
  908. int Idx;
  909. PAGED_CODE();
  910. HGM_DBGPRINT( FILE_HIDJOY | HGM_FENTRY,\
  911. ("HGM_JoystickConfig(DeviceObject=0x%x)",\
  912. DeviceObject) );
  913. /*
  914. * Get a pointer to the device extension
  915. */
  916. DeviceExtension = GET_MINIDRIVER_DEVICE_EXTENSION(DeviceObject);
  917. ntStatus = HGM_MapAxesFromDevExt( DeviceExtension );
  918. if( DeviceExtension->ReadAccessorDigital )
  919. {
  920. DeviceExtension->ScaledTimeout = AXIS_TIMEOUT;
  921. }
  922. else
  923. {
  924. /*
  925. * Calculate time thresholds for analog device
  926. */
  927. if( ( DeviceExtension->HidGameOemData.OemData[0].Timeout < ANALOG_POLL_TIMEOUT_MIN )
  928. ||( DeviceExtension->HidGameOemData.OemData[0].Timeout > ANALOG_POLL_TIMEOUT_MAX ) )
  929. {
  930. HGM_DBGPRINT(FILE_HIDJOY | HGM_BABBLE,\
  931. ("Ignoring out of range timeout: %u uSecs",\
  932. DeviceExtension->HidGameOemData.OemData[0].Timeout));
  933. DeviceExtension->ScaledTimeout = (ULONG)( ( (ULONGLONG)ANALOG_POLL_TIMEOUT_DFT
  934. * (ULONGLONG)(AXIS_FULL_SCALE<<SCALE_SHIFT) )
  935. / (ULONGLONG)ANALOG_POLL_TIMEOUT_MAX );
  936. }
  937. else
  938. {
  939. DeviceExtension->ScaledTimeout = (ULONG)( ( (ULONGLONG)DeviceExtension->HidGameOemData.OemData[0].Timeout
  940. * (ULONGLONG)(AXIS_FULL_SCALE<<SCALE_SHIFT) )
  941. / (ULONGLONG)ANALOG_POLL_TIMEOUT_MAX );
  942. }
  943. HGM_DBGPRINT(FILE_HIDJOY | HGM_BABBLE,\
  944. ("ScaledTimeout: %u",\
  945. DeviceExtension->ScaledTimeout));
  946. /*
  947. * Use one quarter of the minimum poll timeout as a starting value
  948. * for the time between two polls which will be considered to have
  949. * been interrupted.
  950. */
  951. DeviceExtension->ScaledThreshold = (ULONG)( ( (ULONGLONG)ANALOG_POLL_TIMEOUT_MIN
  952. * (ULONGLONG)AXIS_FULL_SCALE )
  953. / (ULONGLONG)ANALOG_POLL_TIMEOUT_MAX )>>2;
  954. }
  955. /*
  956. * Set initial values of LastGoodAxis so that the device will not show
  957. * up as present until we get at least one valid poll.
  958. */
  959. for( Idx = MAX_AXES; Idx >= 0; Idx-- )
  960. {
  961. DeviceExtension->LastGoodAxis[Idx] = AXIS_TIMEOUT;
  962. }
  963. HGM_EXITPROC(FILE_HIDJOY | HGM_FEXIT_STATUSOK, "HGM_JoystickConfig", ntStatus);
  964. return ntStatus;
  965. } /* HGM_JoystickConfig */
  966. /*****************************************************************************
  967. *
  968. * @doc EXTERNAL
  969. *
  970. * @func NTSTATUS | HGM_InitAnalog |
  971. *
  972. * Check that the configuration is valid whilst there is still time
  973. * to refuse it.
  974. * <nl>Detect and validate sibling relationships and call
  975. * HGM_JoystickConfig for the rest of the work.
  976. *
  977. * @parm IN PDEVICE_OBJECT | DeviceObject |
  978. *
  979. * Pointer to the device object
  980. *
  981. * @rvalue STATUS_SUCCESS | success
  982. * @rvalue STATUS_DEVICE_CONFIGURATION_ERROR | Invalid configuration specified
  983. *
  984. *****************************************************************************/
  985. /*
  986. * Disable warning for variable used before set as it is hard for a compiler
  987. * to see that the use of DeviceExtension_Sibling is gated by a flag which
  988. * can only be set after DeviceExtension_Sibling is initialized.
  989. */
  990. #pragma warning( disable:4701 )
  991. NTSTATUS EXTERNAL
  992. HGM_InitAnalog
  993. (
  994. IN PDEVICE_OBJECT DeviceObject
  995. )
  996. {
  997. NTSTATUS ntStatus;
  998. PDEVICE_EXTENSION DeviceExtension;
  999. PDEVICE_EXTENSION DeviceExtension_Sibling;
  1000. PLIST_ENTRY pEntry;
  1001. #define ARE_WE_RELATED(_x_, _y_) \
  1002. ( \
  1003. (_x_)->GameContext == (_y_)->GameContext && \
  1004. (_x_)->WriteAccessor == (_y_)->WriteAccessor && \
  1005. (_x_)->ReadAccessor == (_y_)->ReadAccessor \
  1006. )
  1007. PAGED_CODE ();
  1008. /*
  1009. * Get a pointer to the device extension
  1010. */
  1011. DeviceExtension = GET_MINIDRIVER_DEVICE_EXTENSION(DeviceObject);
  1012. /*
  1013. * No modifications to the Global List while we are looking at it
  1014. */
  1015. ExAcquireFastMutex (&Global.Mutex);
  1016. /*
  1017. * For two joysticks interface two fdos are created to service them
  1018. * but physically they both share the same port.
  1019. * For the second sibling certain extra rules must be applied so we
  1020. * search our list of devices for another device using the same port
  1021. * and if we find one mark this one as a sibling.
  1022. */
  1023. for(pEntry = Global.DeviceListHead.Flink;
  1024. pEntry != &Global.DeviceListHead;
  1025. pEntry = pEntry->Flink)
  1026. {
  1027. /*
  1028. * Obtain the device Extension of the Sibling
  1029. */
  1030. DeviceExtension_Sibling = CONTAINING_RECORD(pEntry, DEVICE_EXTENSION, Link);
  1031. if( DeviceExtension_Sibling != DeviceExtension
  1032. && TRUE == ARE_WE_RELATED(DeviceExtension, DeviceExtension_Sibling)
  1033. && TRUE == DeviceExtension_Sibling->fStarted )
  1034. {
  1035. #ifdef CHANGE_DEVICE
  1036. if( DeviceExtension_Sibling->fReplaced )
  1037. {
  1038. HGM_DBGPRINT(FILE_HIDJOY | HGM_BABBLE, ("Outgoing Sibling found (0x%x)", DeviceExtension_Sibling));
  1039. }
  1040. else
  1041. {
  1042. #endif /* CHANGE_DEVICE */
  1043. HGM_DBGPRINT(FILE_HIDJOY | HGM_BABBLE, ("Sibling found (0x%x)", DeviceExtension_Sibling));
  1044. DeviceExtension->fSiblingFound = TRUE;
  1045. #ifdef CHANGE_DEVICE
  1046. }
  1047. #endif /* CHANGE_DEVICE */
  1048. break;
  1049. }
  1050. }
  1051. /*
  1052. * We are done, release the Mutex
  1053. */
  1054. ExReleaseFastMutex (&Global.Mutex);
  1055. /*
  1056. * check the axis and button configuration for the joystick
  1057. */
  1058. ntStatus = HGM_JoystickConfig(DeviceObject);
  1059. if( NT_SUCCESS( ntStatus ) )
  1060. {
  1061. /*
  1062. * Make sure that sibling axes are not overlapped
  1063. */
  1064. if( DeviceExtension->fSiblingFound &&
  1065. (DeviceExtension_Sibling->resistiveInputMask & DeviceExtension->resistiveInputMask) != 0x0 )
  1066. {
  1067. HGM_DBGPRINT(FILE_HIDJOY |HGM_ERROR,\
  1068. ("HGM_InitDevice: OverLapping Resources ResisitiveInputMask(0x%x) Sibling(0x%x)",\
  1069. DeviceExtension->resistiveInputMask,DeviceExtension_Sibling->resistiveInputMask ));
  1070. ntStatus = STATUS_DEVICE_CONFIGURATION_ERROR;
  1071. }
  1072. }
  1073. else
  1074. {
  1075. HGM_DBGPRINT(FILE_HIDJOY | HGM_ERROR,\
  1076. ("HGM_InitDevice: JoystickConfig Failed"));
  1077. }
  1078. return( ntStatus );
  1079. } /* HGM_InitAnalog */
  1080. /*
  1081. * Change device sample only code
  1082. */
  1083. #ifdef CHANGE_DEVICE
  1084. /*****************************************************************************
  1085. *
  1086. * @doc EXTERNAL
  1087. *
  1088. * @func VOID | HGM_ChangeHandler |
  1089. *
  1090. * Use IOCTL_GAMEENUM_EXPOSE_SIBLING and IOCTL_GAMEENUM_REMOVE_SELF
  1091. * to change the attributes of the device.
  1092. *
  1093. * @parm IN OUT PDEVICE_EXTENSION | DeviceExtension |
  1094. *
  1095. * Pointer to the mini-driver device extension.
  1096. *
  1097. *****************************************************************************/
  1098. VOID
  1099. HGM_ChangeHandler
  1100. (
  1101. IN OUT PDEVICE_EXTENSION DeviceExtension
  1102. )
  1103. {
  1104. NTSTATUS ntStatus = STATUS_SUCCESS;
  1105. KEVENT IoctlCompleteEvent;
  1106. IO_STATUS_BLOCK IoStatus;
  1107. PIO_STACK_LOCATION irpStack, nextStack;
  1108. PIRP pIrp;
  1109. PVOID SiblingHandle;
  1110. GAMEENUM_EXPOSE_SIBLING ExposeSibling;
  1111. PAGED_CODE ();
  1112. HGM_DBGPRINT(FILE_HIDJOY | HGM_FENTRY,\
  1113. ("HGM_ChangeHandler(DeviceExtension=0x%x)", DeviceExtension));
  1114. KeInitializeEvent(&IoctlCompleteEvent, NotificationEvent, FALSE);
  1115. pIrp = IoBuildDeviceIoControlRequest (
  1116. IOCTL_GAMEENUM_EXPOSE_SIBLING,
  1117. DeviceExtension->NextDeviceObject,
  1118. &ExposeSibling,
  1119. sizeof( ExposeSibling ),
  1120. &ExposeSibling,
  1121. sizeof( ExposeSibling ),
  1122. TRUE,
  1123. &IoctlCompleteEvent,
  1124. &IoStatus);
  1125. if( pIrp )
  1126. {
  1127. /*
  1128. * For demonstration purposes only, we don't actually change the
  1129. * device, we just re-expose the same one. If the device really
  1130. * needs to be changed, this would be signalled either by a
  1131. * change in the OemData on the newly exposed device or by using
  1132. * a specific HardwareID string.
  1133. * Note the nAxis and nButton fields will always be zero for an
  1134. * exposed sibling.
  1135. */
  1136. RtlZeroMemory( &ExposeSibling, sizeof( ExposeSibling ) );
  1137. ExposeSibling.Size = sizeof( ExposeSibling );
  1138. ExposeSibling.HardwareHandle = &SiblingHandle;
  1139. C_ASSERT( sizeof( ExposeSibling.OemData ) == sizeof( DeviceExtension->HidGameOemData.Game_Oem_Data ) );
  1140. RtlCopyMemory(ExposeSibling.OemData, DeviceExtension->HidGameOemData.Game_Oem_Data, sizeof(ExposeSibling.OemData));
  1141. ASSERT( ExposeSibling.UnitID == 0 );
  1142. /*
  1143. * Setting a NULL pointer causes the HardwareID of this sibling to be used
  1144. */
  1145. ExposeSibling.HardwareIDs = NULL;
  1146. /*
  1147. * issue a synchronous request to GameEnum to expose this new sibling
  1148. */
  1149. ntStatus = IoCallDriver( DeviceExtension->NextDeviceObject, pIrp );
  1150. if( ntStatus == STATUS_PENDING )
  1151. {
  1152. ntStatus = KeWaitForSingleObject (&IoctlCompleteEvent, Executive, KernelMode, FALSE, NULL);
  1153. }
  1154. if( NT_SUCCESS(ntStatus) )
  1155. {
  1156. /*
  1157. * All went well so remove self
  1158. */
  1159. HGM_DBGPRINT(FILE_HIDJOY | HGM_BABBLE, ("Sibling exposed!"));
  1160. pIrp = IoBuildDeviceIoControlRequest (
  1161. IOCTL_GAMEENUM_REMOVE_SELF,
  1162. DeviceExtension->NextDeviceObject,
  1163. NULL,
  1164. 0,
  1165. NULL,
  1166. 0,
  1167. TRUE,
  1168. &IoctlCompleteEvent,
  1169. &IoStatus);
  1170. if( pIrp )
  1171. {
  1172. /*
  1173. * issue a synchronous request to GameEnum to remove self
  1174. */
  1175. ntStatus = IoCallDriver( DeviceExtension->NextDeviceObject, pIrp );
  1176. if( ntStatus == STATUS_PENDING )
  1177. {
  1178. ntStatus = KeWaitForSingleObject( &IoctlCompleteEvent, Executive, KernelMode, FALSE, NULL );
  1179. }
  1180. if( NT_SUCCESS(ntStatus) )
  1181. {
  1182. /*
  1183. * All done
  1184. */
  1185. HGM_DBGPRINT(FILE_HIDJOY | HGM_BABBLE, ("Removed self!"));
  1186. }
  1187. else
  1188. {
  1189. /*
  1190. * Something bad happened but there's little we can do
  1191. */
  1192. HGM_DBGPRINT(FILE_HIDJOY | HGM_ERROR,\
  1193. ("Failed to remove self with GameEnum error: 0x%08x", \
  1194. ntStatus));
  1195. }
  1196. }
  1197. else
  1198. {
  1199. ntStatus = STATUS_NO_MEMORY;
  1200. HGM_DBGPRINT(FILE_HIDJOY | HGM_ERROR, \
  1201. ("Failed to create IRP for remove self") );
  1202. }
  1203. }
  1204. else
  1205. {
  1206. /*
  1207. * Something bad happened so reset the flag and carry on
  1208. */
  1209. DeviceExtension->fReplaced = FALSE;
  1210. HGM_DBGPRINT(FILE_HIDJOY | HGM_WARN,\
  1211. ("Failed to expose sibling with GameEnum error: 0x%08x", ntStatus));
  1212. }
  1213. }
  1214. else
  1215. {
  1216. ntStatus = STATUS_NO_MEMORY;
  1217. DeviceExtension->fReplaced = FALSE;
  1218. HGM_DBGPRINT(FILE_HIDJOY | HGM_ERROR, \
  1219. ("Failed to create IRP for expose sibling") );
  1220. }
  1221. /*
  1222. * We've finished touching the DeviceExtension now.
  1223. */
  1224. HGM_DecRequestCount( DeviceExtension );
  1225. HGM_EXITPROC(FILE_HIDJOY|HGM_FEXIT_STATUSOK, "HGM_ChangeHandler", ntStatus);
  1226. return;
  1227. } /* HGM_ChangeHandler */
  1228. /*****************************************************************************
  1229. *
  1230. * @doc EXTERNAL
  1231. *
  1232. * @func VOID | HGM_DeviceChanged |
  1233. *
  1234. * Start the process of changing the device attributes by stashing
  1235. * away all the data needed and then initializing and queuing a work
  1236. * item to call the IOCTL at the required PASSIVE_LEVEL.
  1237. *
  1238. * @parm IN OUT PDEVICE_EXTENSION | DeviceExtension |
  1239. *
  1240. * Pointer to the mini-driver device extension.
  1241. *
  1242. *****************************************************************************/
  1243. VOID
  1244. HGM_DeviceChanged
  1245. (
  1246. IN OUT PDEVICE_EXTENSION DeviceExtension
  1247. )
  1248. {
  1249. NTSTATUS ntStatus;
  1250. /*
  1251. * Since the work item will use the device extension, bump the usage
  1252. * count up one in case anyone tries to remove the device between
  1253. * now and when the work item gets to run. If that fails, forget it.
  1254. */
  1255. ntStatus = HGM_IncRequestCount( DeviceExtension );
  1256. if( NT_SUCCESS(ntStatus) )
  1257. {
  1258. DeviceExtension->fReplaced = TRUE;
  1259. ExInitializeWorkItem( &DeviceExtension->WorkItem,
  1260. (PWORKER_THREAD_ROUTINE)HGM_ChangeHandler, DeviceExtension );
  1261. ExQueueWorkItem( &DeviceExtension->WorkItem, DelayedWorkQueue );
  1262. }
  1263. else
  1264. {
  1265. HGM_DBGPRINT(FILE_HIDJOY | HGM_WARN, ("Failed to change device") );
  1266. }
  1267. } /* HGM_DeviceChanged */
  1268. #endif /* CHANGE_DEVICE */
  1269. /*****************************************************************************
  1270. *
  1271. * @doc EXTERNAL
  1272. *
  1273. * @func VOID | HGM_Game2HID |
  1274. *
  1275. * Process the data returned from polling the gameport into values
  1276. * and buttons for returning to HID.
  1277. * <nl>The meaning of the data is interpreted according to the
  1278. * characteristics of the device described in the hardware settings
  1279. * flags.
  1280. *
  1281. * @parm IN PDEVICE_EXTENSION | DeviceExtension |
  1282. *
  1283. * Pointer to the mini-driver device extension.
  1284. *
  1285. * @parm IN OUT PUHIDGAME_INPUT_DATA | pHIDData |
  1286. *
  1287. * Pointer to the buffer into which the HID report should be written.
  1288. * This buffer must be assumed to be unaligned.
  1289. *
  1290. *****************************************************************************/
  1291. VOID
  1292. HGM_Game2HID
  1293. (
  1294. IN PDEVICE_EXTENSION DeviceExtension,
  1295. IN OUT PUHIDGAME_INPUT_DATA pHIDData
  1296. )
  1297. {
  1298. LONG Idx;
  1299. /*
  1300. * Use a local buffer to assemble the report as the real buffer may not
  1301. * be aligned.
  1302. */
  1303. HIDGAME_INPUT_DATA LocalBuffer;
  1304. RtlZeroMemory( &LocalBuffer, sizeof( LocalBuffer ) );
  1305. /*
  1306. * Remap axis
  1307. */
  1308. for(Idx = 0x0; Idx < DeviceExtension->nAxes; Idx++ )
  1309. {
  1310. LocalBuffer.Axis[Idx] = DeviceExtension->LastGoodAxis[DeviceExtension->AxisMap[Idx]];
  1311. }
  1312. /*
  1313. * Copy buttons and remap any POVs
  1314. */
  1315. if( DeviceExtension->fSiblingFound )
  1316. {
  1317. /*
  1318. * Simplest case, 2nd half poll must be 2A 2B
  1319. */
  1320. LocalBuffer.Button[0] = DeviceExtension->LastGoodButton[2];
  1321. LocalBuffer.Button[1] = DeviceExtension->LastGoodButton[3];
  1322. }
  1323. else if( DeviceExtension->HidGameOemData.OemData[0].joy_hws_dwFlags & JOY_HWS_POVISBUTTONCOMBOS )
  1324. {
  1325. UCHAR Buttons = 0;
  1326. for( Idx = 3; Idx >=0; Idx-- )
  1327. {
  1328. Buttons <<= 1;
  1329. Buttons = (UCHAR)(Buttons + (UCHAR)(DeviceExtension->LastGoodButton[Idx] >> BUTTON_BIT));
  1330. }
  1331. if( DeviceExtension->HidGameOemData.OemData[0].joy_hws_dwFlags & JOY_HWS_HASPOV2 )
  1332. {
  1333. Idx = c2PComboLU[Buttons];
  1334. }
  1335. else
  1336. {
  1337. Idx = c1PComboLU[Buttons];
  1338. }
  1339. if( Idx >= P1_NULL )
  1340. {
  1341. if( Idx < P2_NULL )
  1342. {
  1343. LocalBuffer.hatswitch[0] = (UCHAR)(Idx & POV_MASK);
  1344. }
  1345. else
  1346. {
  1347. LocalBuffer.hatswitch[1] = (UCHAR)(Idx & POV_MASK);
  1348. }
  1349. }
  1350. else
  1351. {
  1352. #ifdef CHANGE_DEVICE
  1353. if( ( Idx >= DeviceExtension->nButtons ) && ( !DeviceExtension->fReplaced ) )
  1354. {
  1355. /*
  1356. * If a higher button was pressed than expected, use
  1357. * remove_self/expose_sibling to change expectations.
  1358. */
  1359. HGM_DeviceChanged( DeviceExtension );
  1360. }
  1361. #endif /* CHANGE_DEVICE */
  1362. LocalBuffer.Button[Idx] = BUTTON_ON;
  1363. }
  1364. }
  1365. else
  1366. {
  1367. if( DeviceExtension->HidGameOemData.OemData[0].joy_hws_dwFlags & JOY_HWS_POVISPOLL )
  1368. {
  1369. /*
  1370. * Following the axis mapping loop, Idx is one larger than
  1371. * DeviceExtension->nAxes which is the correct index for a
  1372. * polled POV.
  1373. */
  1374. LocalBuffer.Axis[Idx] = DeviceExtension->LastGoodAxis[DeviceExtension->povMap];
  1375. }
  1376. /*
  1377. * Check buttons on R and Z axes
  1378. */
  1379. if( DeviceExtension->nButtons > 5 )
  1380. {
  1381. if( DeviceExtension->LastGoodAxis[3] > DeviceExtension->button6limit )
  1382. {
  1383. /*
  1384. * New max found so button is off
  1385. */
  1386. HGM_DBGPRINT( FILE_HIDJOY | HGM_BABBLE2, \
  1387. ("HGM_Game2HID: Changing button 6 limit from %u to %u", \
  1388. DeviceExtension->button6limit, DeviceExtension->LastGoodAxis[3] ) ) ;
  1389. DeviceExtension->button6limit = DeviceExtension->LastGoodAxis[3];
  1390. }
  1391. else if( DeviceExtension->LastGoodAxis[3] < (DeviceExtension->button6limit>>1) )
  1392. {
  1393. LocalBuffer.Button[5] = BUTTON_ON;
  1394. }
  1395. }
  1396. if( DeviceExtension->nButtons > 4 )
  1397. {
  1398. Idx = 4;
  1399. if( DeviceExtension->LastGoodAxis[2] > DeviceExtension->button5limit )
  1400. {
  1401. /*
  1402. * New max found so button is off
  1403. */
  1404. HGM_DBGPRINT( FILE_HIDJOY | HGM_BABBLE2, \
  1405. ("HGM_Game2HID: Changing button 5 limit from %u to %u", \
  1406. DeviceExtension->button5limit, DeviceExtension->LastGoodAxis[2] ) ) ;
  1407. DeviceExtension->button5limit = DeviceExtension->LastGoodAxis[2];
  1408. }
  1409. else if( DeviceExtension->LastGoodAxis[2] < (DeviceExtension->button5limit>>1) )
  1410. {
  1411. LocalBuffer.Button[4] = BUTTON_ON;
  1412. }
  1413. }
  1414. else
  1415. {
  1416. Idx = DeviceExtension->nButtons;
  1417. }
  1418. /*
  1419. * Copy all standard buttons
  1420. */
  1421. while( Idx-- )
  1422. {
  1423. LocalBuffer.Button[Idx] = DeviceExtension->LastGoodButton[Idx];
  1424. }
  1425. }
  1426. C_ASSERT( sizeof( *pHIDData ) == sizeof( LocalBuffer ) );
  1427. RtlCopyMemory( pHIDData, &LocalBuffer, sizeof( LocalBuffer ) );
  1428. HGM_DBGPRINT( FILE_HIDJOY | HGM_BABBLE2, \
  1429. ("HGM_Game2HID: Axes: X: %08x Y: %08x Z: %08x R: %08x", \
  1430. LocalBuffer.Axis[0], LocalBuffer.Axis[1], LocalBuffer.Axis[2], LocalBuffer.Axis[3] ) ) ;
  1431. HGM_DBGPRINT( FILE_HIDJOY | HGM_BABBLE2, \
  1432. ("HGM_Game2HID: P1: %d P2: %d Buttons %d, %d, %d, %d, %d, %d, %d, %d, %d, %d", \
  1433. LocalBuffer.hatswitch[0], LocalBuffer.hatswitch[1], \
  1434. LocalBuffer.Button[0], LocalBuffer.Button[1], LocalBuffer.Button[2], LocalBuffer.Button[3], LocalBuffer.Button[4], \
  1435. LocalBuffer.Button[5], LocalBuffer.Button[6], LocalBuffer.Button[7], LocalBuffer.Button[8], LocalBuffer.Button[9] ) ) ;
  1436. } /* HGM_Game2HID */