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.

2387 lines
61 KiB

  1. -------------------------------------------------------------------------------
  2. THIS FILE IS NO LONGER USED!
  3. It is being retained for historical purposes, in case we should need to refer
  4. to previously-existing (and largely broken) analog joystick code. It should
  5. not be distributed to 3rd parties.
  6. -------------------------------------------------------------------------------
  7. //TODO check return and irp->status returns for all routines. Trace 'em as far as necessary.
  8. /*++ BUILD Version: 0001 // Increment this if a change has global effects
  9. Copyright (c) 1995, 1996 Microsoft Corporation
  10. Module Name:
  11. swndr3p.c
  12. Abstract:
  13. Kernel mode device driver for Microsoft SideWinder 3p joystick device
  14. Author:
  15. edbriggs 30-Nov-95
  16. Revision History:
  17. stevez May 96
  18. removed unused code, including analog and 1-bit digital modes.
  19. See analog3p.c, .h for original version
  20. May need 1-bit digital mode for Aztec game cards, may want analog
  21. for future release.
  22. NB there is still a lot of unnecessary code left in this driver
  23. RtlLargeIntegerX calls are historical and can be replaced by __int64
  24. compiler supported arithmetic.
  25. 6/10/96 registry variables now being used for port address
  26. 6/10/96 resets enhanced digital mode if joystick goes to analog mode during
  27. use (for example if user toggles "emulation" switch)
  28. 6/13/96 limits polling to 100/s by setting min time between polls to 10ms
  29. 6/13/96 code structure revised in SidewndrPoll and subroutines
  30. --*/
  31. /*
  32. * $Header: /Joystick/Sidewinder/swndr3p.c 19 1/09/96 10:26p Edbriggs $
  33. */
  34. #include <ntddk.h>
  35. #include <windef.h>
  36. #include <mmsystem.h>
  37. #include <mmddk.h>
  38. #include <ntddjoy.h>
  39. //#include "joylog.h"
  40. //
  41. // Device extension data
  42. //
  43. typedef struct {
  44. //
  45. // JOYSTICKID0 or JOYDSTICKID1
  46. //
  47. DWORD DeviceNumber;
  48. //
  49. // Number of axes supported and configured for this device. The
  50. // Sidewinder 3P supports a maximum of 4 axes
  51. //
  52. DWORD NumberOfAxes;
  53. //
  54. // Current operating mode of the device:
  55. // { Invalid | Analog | Digital | Enhanced | Maximum }
  56. //
  57. DWORD CurrentDeviceMode;
  58. //
  59. // The I/O address of the device. Note, this may be a memory mapped
  60. // address
  61. //
  62. PUCHAR DeviceAddress;
  63. //
  64. // Boolean denoting whether this address is mapped (TRUE) or not)
  65. //
  66. BOOL DeviceIsMapped;
  67. //
  68. // A Spinlock is used to synchronize access to this device. This is
  69. // a pointer to the actual spinlock data area
  70. //
  71. PKSPIN_LOCK SpinLock;
  72. //
  73. // Actual SpinLock data area
  74. //
  75. KSPIN_LOCK SpinLockData;
  76. } JOY_EXTENSION, *PJOY_EXTENSION;
  77. //
  78. // Debugging macros
  79. //
  80. #ifdef DEBUG
  81. #define ENABLE_DEBUG_TRACE
  82. #endif
  83. #ifdef ENABLE_DEBUG_TRACE
  84. #define DebugTrace(_x_) \
  85. DbgPrint("Joystick : "); \
  86. KdPrint(_x_); \
  87. DbgPrint("\n");
  88. #else
  89. #define DebugTrace(_x_)
  90. #endif
  91. //
  92. // Condition Compilation Directives
  93. //
  94. //
  95. // Global values used to speed up calculations in sampling loops
  96. // Also calibration constants set in DriverEntry
  97. // -------------------------------------------------------------
  98. //
  99. JOY_STATISTICS JoyStatistics; // These are used for debugging and performance testing
  100. //
  101. // The high resolution system clock (from KeQueryPerformanceCounter)
  102. // is updated at this frequency
  103. //
  104. DWORD Frequency;
  105. //
  106. // The latency in a call to KeQueryPerformanceCounter in microseconds
  107. //
  108. DWORD dwQPCLatency;
  109. //
  110. // After a write to the joystick port, we spin in a read-port loop, waiting
  111. // for a bit to go high.
  112. // This is the number of iterations to spin before timing out. Set
  113. // to timeout after about 2 milliseconds
  114. LONG nReadLoopMax;
  115. //
  116. // Values for KeDelayExecutionThread
  117. //
  118. LARGE_INTEGER LI1ms;
  119. LARGE_INTEGER LI2ms;
  120. LARGE_INTEGER LI8ms;
  121. LARGE_INTEGER LI10ms;
  122. //
  123. // number of KeQueryPerformanceCounter ticks in 1 millisecond
  124. // (used to prevent too-frequent polling of joystick)
  125. //
  126. DWORD nMinTicksBetweenPolls;
  127. //
  128. // Assembly area for digital packets
  129. //
  130. BYTE NormalPacket[8];
  131. BYTE EnhancedPacket[21];
  132. //
  133. // Last good packet
  134. //
  135. BOOL bLastGoodPacket;
  136. JOY_DD_INPUT_DATA jjLastGoodPacket;
  137. //
  138. // time at which the joystick was last polled
  139. //
  140. LARGE_INTEGER liLastPoll; // set whenever the joystick's polled
  141. //
  142. // End of Global Values
  143. // ---------------------
  144. //
  145. //
  146. // Routine Prototypes
  147. //
  148. NTSTATUS
  149. DriverEntry(
  150. IN PDRIVER_OBJECT pDriverObject,
  151. IN PUNICODE_STRING RegistryPathName
  152. );
  153. NTSTATUS
  154. SidewndrCreateDevice(
  155. PDRIVER_OBJECT pDriverObject,
  156. PWSTR DeviceNameBase,
  157. DWORD DeviceNumber,
  158. DWORD ExtensionSize,
  159. BOOLEAN Exclusive,
  160. DWORD DeviceType,
  161. PDEVICE_OBJECT *DeviceObject
  162. );
  163. NTSTATUS
  164. SidewndrDispatch(
  165. IN PDEVICE_OBJECT pDO,
  166. IN PIRP pIrp
  167. );
  168. NTSTATUS
  169. SidewndrReportNullResourceUsage(
  170. PDEVICE_OBJECT DeviceObject
  171. );
  172. NTSTATUS
  173. SidewndrReadRegistryParameterDWORD(
  174. PUNICODE_STRING RegistryPathName,
  175. PWSTR ParameterName,
  176. PDWORD ParameterValue
  177. );
  178. NTSTATUS
  179. SidewndrMapDevice(
  180. DWORD PortBase,
  181. DWORD NumberOfPorts,
  182. PJOY_EXTENSION pJoyExtension
  183. );
  184. VOID
  185. SidewndrUnload(
  186. PDRIVER_OBJECT pDriverObject
  187. );
  188. NTSTATUS
  189. SidewndrPoll(
  190. IN PDEVICE_OBJECT pDO,
  191. IN PIRP pIrp
  192. );
  193. NTSTATUS
  194. SidewndrEnhancedDigitalPoll(
  195. IN PDEVICE_OBJECT pDO,
  196. IN PIRP pIrp
  197. );
  198. BOOL
  199. SidewndrQuiesce(
  200. PUCHAR JoyPort,
  201. UCHAR Mask
  202. );
  203. DWORD
  204. TimeInMicroSeconds(
  205. DWORD dwTime
  206. );
  207. DWORD
  208. TimeInTicks(
  209. DWORD dwTimeInMicroSeconds
  210. );
  211. NTSTATUS
  212. SidewndrWaitForClockEdge(
  213. DWORD edge,
  214. BYTE *pByte,
  215. PUCHAR JoyPort
  216. );
  217. NTSTATUS
  218. SidewndrReset(
  219. PUCHAR JoyPort
  220. );
  221. NTSTATUS
  222. SidewndrStartAnalogMode(
  223. PUCHAR JoyPort
  224. );
  225. NTSTATUS
  226. SidewndrStartDigitalMode(
  227. PUCHAR JoyPort
  228. );
  229. NTSTATUS
  230. SidewndrStartEnhancedMode(
  231. PUCHAR JoyPort
  232. );
  233. NTSTATUS
  234. SidewndrGetEnhancedPacket(
  235. PUCHAR joyPort
  236. );
  237. NTSTATUS
  238. SidewndrInterpretEnhancedPacket(
  239. PJOY_DD_INPUT_DATA pInput
  240. );
  241. int
  242. lstrnicmpW(
  243. LPWSTR pszA,
  244. LPWSTR pszB,
  245. size_t cch
  246. );
  247. VOID
  248. SidewndrWait (
  249. DWORD TotalWait // in uS
  250. );
  251. BOOL
  252. SidewndrReadWait (
  253. PUCHAR JoyPort,
  254. UCHAR Mask
  255. );
  256. void
  257. SidewndrGetConfig(
  258. LPJOYREGHWCONFIG pConfig,
  259. PJOY_EXTENSION pJoyExtension
  260. );
  261. NTSTATUS
  262. DriverEntry(
  263. IN PDRIVER_OBJECT pDriverObject,
  264. IN PUNICODE_STRING RegistryPathName
  265. )
  266. /*++
  267. Routine Description:
  268. This routine is called at system initialization time to initialize
  269. this driver.
  270. Arguments:
  271. DriverObject - Supplies the driver object.
  272. RegistryPath - Supplies the registry path for this driver.
  273. Return Value:
  274. STATUS_SUCCESS
  275. STATUS_DEVICE_CONFIGURATION_ERROR - Wrong number of axi in the registry
  276. or error status from NT itself
  277. --*/
  278. {
  279. NTSTATUS Status;
  280. PDEVICE_OBJECT JoyDevice0;
  281. PDEVICE_OBJECT JoyDevice1;
  282. DWORD NumberOfAxes;
  283. DWORD DeviceAddress;
  284. DWORD DeviceType;
  285. //
  286. // See how many axes we have from the registry parameters. These parameters
  287. // are set up by the driver installation program, and can be modified by
  288. // control panel
  289. //
  290. //DbgBreakPoint();
  291. JoyStatistics.nVersion = 16; // global, initialize it first thing so we for sure what we're running
  292. DebugTrace(("Sidewndr %d", JoyStatistics.nVersion));
  293. Status = SidewndrReadRegistryParameterDWORD(
  294. RegistryPathName,
  295. JOY_DD_NAXES_U,
  296. &NumberOfAxes
  297. );
  298. DebugTrace(("Number of axes returned from registry: %d", NumberOfAxes));
  299. if (!NT_SUCCESS(Status))
  300. {
  301. SidewndrUnload(pDriverObject);
  302. return Status;
  303. }
  304. if (( NumberOfAxes < 2) || (NumberOfAxes > 4))
  305. {
  306. SidewndrUnload(pDriverObject);
  307. Status = STATUS_DEVICE_CONFIGURATION_ERROR;
  308. return Status;
  309. }
  310. //
  311. // See if the registry contains a device address other than the
  312. // default of 0x201
  313. //
  314. Status = SidewndrReadRegistryParameterDWORD(
  315. RegistryPathName,
  316. JOY_DD_DEVICE_ADDRESS_U,
  317. &DeviceAddress
  318. );
  319. if (NT_SUCCESS(Status))
  320. {
  321. DebugTrace(("Registry specified device address of 0x%x", DeviceAddress));
  322. }
  323. else
  324. {
  325. DebugTrace(("Using default device address of 0x%x", JOY_IO_PORT_ADDRESS));
  326. DeviceAddress = JOY_IO_PORT_ADDRESS;
  327. }
  328. //
  329. // See if there is a device type specified in the registry
  330. //
  331. Status = SidewndrReadRegistryParameterDWORD(
  332. RegistryPathName,
  333. JOY_DD_DEVICE_TYPE_U,
  334. &DeviceType
  335. );
  336. if (!NT_SUCCESS(Status))
  337. {
  338. DebugTrace(("No device type entry for joystick"));
  339. SidewndrUnload(pDriverObject);
  340. Status = STATUS_DEVICE_CONFIGURATION_ERROR;
  341. return Status;
  342. }
  343. DebugTrace(("Joystick device type %d", DeviceType));
  344. // set global large_integers for KeDelayExecutionThread (negative numbers for relative time)
  345. // NB KeDelayExecutionThread calls typically take at least 10 milliseconds on the pentium75 I used for testing,
  346. // no matter how little time is requested
  347. LI1ms = RtlConvertLongToLargeInteger(- 10000);
  348. LI2ms = RtlConvertLongToLargeInteger(- 20000);
  349. LI8ms = RtlConvertLongToLargeInteger(- 80000);
  350. LI10ms = RtlConvertLongToLargeInteger(-100000);
  351. //
  352. // Calculate time thresholds for analog device
  353. //
  354. {
  355. DWORD Remainder;
  356. LARGE_INTEGER LargeFrequency;
  357. DWORD ulStart, ulTemp, ulEnd;
  358. DWORD dwTicks, dwTimems;
  359. int i;
  360. BYTE byteJoy, byteTmp;
  361. //
  362. // Get the system timer resolution expressed in Hertz.
  363. //
  364. KeQueryPerformanceCounter(&LargeFrequency);
  365. Frequency = LargeFrequency.LowPart;
  366. DebugTrace(("Frequency: %u", Frequency));
  367. // need latency for KeQueryPerformanceCounter. While we're at it, let's
  368. // get min time for delay and stall execution
  369. ulStart = KeQueryPerformanceCounter(NULL).LowPart;
  370. for (i = 0; i < 1000; i++) {
  371. ulTemp = KeQueryPerformanceCounter(NULL).LowPart;
  372. }
  373. dwTicks = ulTemp - ulStart;
  374. dwTimems = TimeInMicroSeconds (dwTicks);
  375. dwQPCLatency = (dwTimems / 1000) + 1; // round up
  376. /* following code used only for testing timing of kernel timing routines
  377. ulStart = KeQueryPerformanceCounter(NULL).LowPart;
  378. KeDelayExecutionThread( KernelMode, FALSE, &LI2ms);
  379. ulEnd = KeQueryPerformanceCounter(NULL).LowPart;
  380. DebugTrace(("QPC latency in uS: %u, DET(2ms) in ticks: %u ticks",
  381. dwQPCLatency,
  382. ulEnd - ulStart));
  383. ulStart = KeQueryPerformanceCounter(NULL).LowPart;
  384. for (i = 0; i < 1000; i++) {
  385. KeStallExecutionProcessor(1); // 1 microsecond (Hah!)
  386. }
  387. ulEnd = KeQueryPerformanceCounter(NULL).LowPart;
  388. DebugTrace(("KeStallExecutionProcessor(1) called 1000 times, in ticks: %u",
  389. ulEnd - ulStart));
  390. */
  391. }
  392. //
  393. // Attempt to create the device
  394. //
  395. Status = SidewndrCreateDevice(
  396. pDriverObject,
  397. JOY_DD_DEVICE_NAME_U, // device driver
  398. 0,
  399. sizeof(JOY_EXTENSION),
  400. FALSE, // exclusive access
  401. FILE_DEVICE_UNKNOWN,
  402. &JoyDevice0);
  403. if (!NT_SUCCESS(Status))
  404. {
  405. DebugTrace(("SwndrCreateDevice returned %x", Status));
  406. SidewndrUnload(pDriverObject);
  407. return Status;
  408. }
  409. ((PJOY_EXTENSION)JoyDevice0->DeviceExtension)->DeviceNumber = JOYSTICKID1;
  410. ((PJOY_EXTENSION)JoyDevice0->DeviceExtension)->NumberOfAxes = NumberOfAxes;
  411. ((PJOY_EXTENSION)JoyDevice0->DeviceExtension)->CurrentDeviceMode =
  412. SIDEWINDER3P_ANALOG_MODE;
  413. ((PJOY_EXTENSION)JoyDevice0->DeviceExtension)->DeviceIsMapped = FALSE;
  414. ((PJOY_EXTENSION)JoyDevice0->DeviceExtension)->DeviceAddress = (PUCHAR) 0;
  415. //
  416. // Initialize the spinlock used to synchronize access to this device
  417. //
  418. KeInitializeSpinLock(&((PJOY_EXTENSION)JoyDevice0->DeviceExtension)->SpinLockData);
  419. ((PJOY_EXTENSION)JoyDevice0->DeviceExtension)->SpinLock =
  420. &((PJOY_EXTENSION)JoyDevice0->DeviceExtension)->SpinLockData;
  421. //
  422. // Get the device address into the device extension
  423. //
  424. Status = SidewndrMapDevice(
  425. DeviceAddress,
  426. 1,
  427. (PJOY_EXTENSION)JoyDevice0->DeviceExtension);
  428. // Calibrate nReadLoopMax for spinning in read_port loops to timeout after 2ms
  429. {
  430. int i;
  431. PBYTE JoyPort;
  432. DWORD ulStart, ulEnd;
  433. BYTE byteJoy;
  434. int LoopTimeInMicroSeconds;
  435. i = 1000;
  436. JoyPort = ((PJOY_EXTENSION)JoyDevice0->DeviceExtension)->DeviceAddress;
  437. ulStart = KeQueryPerformanceCounter(NULL).LowPart;
  438. while (i--){
  439. byteJoy = READ_PORT_UCHAR(JoyPort);
  440. if ((byteJoy & X_AXIS_BITMASK)) {
  441. ;
  442. }
  443. }
  444. ulEnd = KeQueryPerformanceCounter(NULL).LowPart;
  445. LoopTimeInMicroSeconds = TimeInMicroSeconds (ulEnd - ulStart);
  446. nReadLoopMax = (1000 * 2000) / LoopTimeInMicroSeconds; // want 2 mS for nReadLoopMax iterations
  447. DebugTrace(("READ_PORT_UCHAR loop, 1000 interations: %u ticks", ulEnd - ulStart));
  448. DebugTrace(("nReadLoopMax: %u", nReadLoopMax));
  449. }
  450. //
  451. // if only 2 axes were requested, we can support a second device
  452. //
  453. // Number of axed should be 4 here, since we're only supporting sidewinder
  454. // in enhanced digital mode. Leave this code in just for safety.
  455. if (2 == NumberOfAxes)
  456. {
  457. Status = SidewndrCreateDevice(
  458. pDriverObject,
  459. JOY_DD_DEVICE_NAME_U,
  460. 1, // device number
  461. sizeof (JOY_EXTENSION),
  462. FALSE, // exclusive access
  463. FILE_DEVICE_UNKNOWN,
  464. &JoyDevice1);
  465. if (!NT_SUCCESS(Status))
  466. {
  467. DebugTrace(("Create device for second device returned %x", Status));
  468. SidewndrUnload(pDriverObject);
  469. return Status;
  470. }
  471. //
  472. // In the analog world (which we are in if there are 2 devices, both
  473. // devices share the same I/O address so just copy it from JoyDevice0
  474. //
  475. ((PJOY_EXTENSION)JoyDevice1->DeviceExtension)->DeviceIsMapped =
  476. ((PJOY_EXTENSION)JoyDevice0->DeviceExtension)->DeviceIsMapped;
  477. ((PJOY_EXTENSION)JoyDevice1->DeviceExtension)->DeviceAddress =
  478. ((PJOY_EXTENSION)JoyDevice0->DeviceExtension)->DeviceAddress;
  479. }
  480. //
  481. // Place the enty points in our driver object
  482. //
  483. pDriverObject->DriverUnload = SidewndrUnload;
  484. pDriverObject->MajorFunction[IRP_MJ_CREATE] = SidewndrDispatch;
  485. pDriverObject->MajorFunction[IRP_MJ_CLOSE] = SidewndrDispatch;
  486. pDriverObject->MajorFunction[IRP_MJ_READ] = SidewndrDispatch;
  487. pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = SidewndrDispatch;
  488. //
  489. // Zero statistics, set misc globals
  490. //
  491. JoyStatistics.EnhancedPolls = 0;
  492. JoyStatistics.EnhancedPollTimeouts = 0;
  493. JoyStatistics.EnhancedPollErrors = 0;
  494. JoyStatistics.nPolledTooSoon = 0;
  495. JoyStatistics.nReset = 0;
  496. {
  497. int i;
  498. for (i = 0; i < MAX_ENHANCEDMODE_ATTEMPTS; i++) {
  499. JoyStatistics.Retries[i] = 0;
  500. }
  501. }
  502. bLastGoodPacket = FALSE;
  503. liLastPoll = KeQueryPerformanceCounter (NULL);
  504. // allow max of 100 polls/s (min time between polls 10ms), which reduces time spinning in the NT kernel
  505. nMinTicksBetweenPolls = TimeInTicks (10000);
  506. return STATUS_SUCCESS;
  507. }
  508. NTSTATUS
  509. SidewndrCreateDevice(
  510. PDRIVER_OBJECT pDriverObject,
  511. PWSTR DeviceNameBase,
  512. DWORD DeviceNumber,
  513. DWORD ExtensionSize,
  514. BOOLEAN Exclusive,
  515. DWORD DeviceType,
  516. PDEVICE_OBJECT *DeviceObject
  517. )
  518. /*++
  519. Routine Description:
  520. This routine is called at driver initialization time to create
  521. the device. The device is created to use Buffered IO.
  522. Arguments:
  523. pDriverObject - Supplies the driver object.
  524. DeviceNameBase - The base name of the device to which a number is appended
  525. DeviceNumber - A number which will be appended to the device name
  526. ExtensionSize - Size of the device extension area
  527. Exclusive - True if exclusive access should be enforced
  528. DeviceType - NT Device type this device is modeled after
  529. DeviceObject - pointer to the device object
  530. Return Value:
  531. STATUS_SUCCESS
  532. or error status from NT itself
  533. --*/
  534. {
  535. WCHAR DeviceName[100];
  536. WCHAR UnicodeDosDeviceName[200];
  537. UNICODE_STRING UnicodeDeviceName;
  538. NTSTATUS Status;
  539. int Length;
  540. (void) wcscpy(DeviceName, DeviceNameBase);
  541. Length = wcslen(DeviceName);
  542. DeviceName[Length + 1] = L'\0';
  543. DeviceName[Length] = (USHORT) (L'0' + DeviceNumber);
  544. (void) RtlInitUnicodeString(&UnicodeDeviceName, DeviceName);
  545. Status = IoCreateDevice(
  546. pDriverObject,
  547. ExtensionSize,
  548. &UnicodeDeviceName,
  549. DeviceType,
  550. 0,
  551. (BOOLEAN) Exclusive,
  552. DeviceObject
  553. );
  554. if (!NT_SUCCESS(Status))
  555. {
  556. return Status;
  557. }
  558. RtlInitUnicodeString((PUNICODE_STRING) &UnicodeDosDeviceName, L"\\DosDevices\\Joy1");
  559. Status = IoCreateSymbolicLink(
  560. (PUNICODE_STRING) &UnicodeDosDeviceName,
  561. (PUNICODE_STRING) &UnicodeDeviceName
  562. );
  563. if (!NT_SUCCESS(Status))
  564. {
  565. return Status;
  566. }
  567. // Set the flag signifying that we will do buffered I/O. This causes NT
  568. // to allocate a buffer on a ReadFile operation which will then be copied
  569. // back to the calling application by the I/O subsystem
  570. (*DeviceObject)->Flags |= DO_BUFFERED_IO;
  571. return Status;
  572. }
  573. NTSTATUS
  574. SidewndrReadRegistryParameterDWORD(
  575. PUNICODE_STRING RegistryPathName,
  576. PWSTR ParameterName,
  577. PDWORD ParameterValue
  578. )
  579. /*++
  580. Routine Description:
  581. This routine reads registry values for the driver configuration
  582. Arguments:
  583. RegistryPathName - Registry path containing the desired parameters
  584. ParameterName - The name of the parameter
  585. ParameterValue - Variable to receive the parameter value
  586. Return Value:
  587. STATUS_SUCCESS --
  588. STATUS_NO_MORE_ENTRIES -- Couldn't find any entries
  589. STATUS_INSUFFICIENT_RESOURCES -- Couldn't allocate paged pool
  590. STATUS_DEVICE_CONFIGURATION_ERROR -- Returned value wasn't a DWORD
  591. or error status from NT itself
  592. --*/
  593. {
  594. OBJECT_ATTRIBUTES ObjectAttributes;
  595. NTSTATUS Status;
  596. HANDLE ServiceKey;
  597. HANDLE DeviceKey; // Key handle of service node
  598. UNICODE_STRING DeviceName; // Key to parameter node
  599. DWORD KeyIndex;
  600. DWORD KeyValueLength;
  601. PBYTE KeyData;
  602. BOOL ValueWasFound;
  603. PKEY_VALUE_FULL_INFORMATION KeyInfo;
  604. InitializeObjectAttributes( &ObjectAttributes,
  605. RegistryPathName,
  606. OBJ_CASE_INSENSITIVE,
  607. NULL,
  608. (PSECURITY_DESCRIPTOR) NULL);
  609. //
  610. // Open a key for our services node entry
  611. //
  612. Status = ZwOpenKey( &ServiceKey,
  613. KEY_READ | KEY_WRITE,
  614. &ObjectAttributes);
  615. if (!NT_SUCCESS(Status))
  616. {
  617. return Status;
  618. }
  619. //
  620. // Open the key to our device subkey
  621. //
  622. RtlInitUnicodeString(&DeviceName, L"Parameters");
  623. InitializeObjectAttributes( &ObjectAttributes,
  624. &DeviceName,
  625. OBJ_CASE_INSENSITIVE,
  626. ServiceKey,
  627. (PSECURITY_DESCRIPTOR) NULL);
  628. Status = ZwOpenKey (&DeviceKey,
  629. KEY_READ | KEY_WRITE,
  630. &ObjectAttributes);
  631. ZwClose(ServiceKey);
  632. if (!NT_SUCCESS(Status))
  633. {
  634. return Status;
  635. }
  636. //
  637. // Loop reading our key values
  638. //
  639. // TODO exit loop when value is found?
  640. ValueWasFound = FALSE;
  641. for (KeyIndex = 0; ; KeyIndex++)
  642. {
  643. KeyValueLength = 0;
  644. //
  645. // find out how much data we will get
  646. //
  647. Status = ZwEnumerateValueKey(
  648. DeviceKey,
  649. KeyIndex,
  650. KeyValueFullInformation,
  651. NULL,
  652. 0,
  653. &KeyValueLength);
  654. if (STATUS_NO_MORE_ENTRIES == Status)
  655. {
  656. break;
  657. }
  658. if (0 == KeyValueLength)
  659. {
  660. return Status;
  661. }
  662. //
  663. // Read the data
  664. //
  665. KeyData = ExAllocatePool (PagedPool, KeyValueLength);
  666. if (NULL == KeyData)
  667. {
  668. return STATUS_INSUFFICIENT_RESOURCES;
  669. }
  670. Status = ZwEnumerateValueKey(
  671. DeviceKey,
  672. KeyIndex,
  673. KeyValueFullInformation,
  674. KeyData,
  675. KeyValueLength,
  676. &KeyValueLength);
  677. if (!NT_SUCCESS(Status))
  678. {
  679. ExFreePool(KeyData);
  680. return Status;
  681. }
  682. KeyInfo = (PKEY_VALUE_FULL_INFORMATION) KeyData;
  683. if (0 == lstrnicmpW(KeyInfo->Name,
  684. ParameterName,
  685. KeyInfo->NameLength / sizeof(WCHAR)))
  686. {
  687. // check its a DWORD
  688. if (REG_DWORD != KeyInfo->Type)
  689. {
  690. ExFreePool(KeyData);
  691. return STATUS_DEVICE_CONFIGURATION_ERROR;
  692. }
  693. ValueWasFound = TRUE;
  694. *ParameterValue = *(PDWORD) (KeyData + KeyInfo->DataOffset);
  695. }
  696. ExFreePool(KeyData);
  697. }
  698. return (ValueWasFound) ? STATUS_SUCCESS : STATUS_DEVICE_CONFIGURATION_ERROR;
  699. }
  700. NTSTATUS
  701. SidewndrDispatch(
  702. IN PDEVICE_OBJECT pDO,
  703. IN PIRP pIrp
  704. )
  705. /*++
  706. Routine Description:
  707. Driver dispatch routine. Processes IRPs based on IRP MajorFunction
  708. Arguments:
  709. pDO -- pointer to the device object
  710. pIrp -- pointer to the IRP to process
  711. Return Value:
  712. Returns the value of the IRP IoStatus.Status
  713. --*/
  714. {
  715. PIO_STACK_LOCATION pIrpStack;
  716. KIRQL OldIrql;
  717. NTSTATUS Status;
  718. DWORD dwRetries = 0;
  719. //DbgBreakPoint();
  720. pIrpStack = IoGetCurrentIrpStackLocation(pIrp);
  721. Status = STATUS_SUCCESS;
  722. pIrp->IoStatus.Status = Status;
  723. pIrp->IoStatus.Information = 0;
  724. switch (pIrpStack->MajorFunction)
  725. {
  726. case IRP_MJ_CREATE:
  727. //
  728. // perform synchronous I/O
  729. //
  730. //pIrpStack->FileObject->Flags |= FO_SYNCHRONOUS_IO;
  731. //NB This is bad code -- we are simply one thread wandering off through the computer -- we should be queuing up a DPC,
  732. //returning status_pending to the calling program, then finishing the job when the dpc goes. This is possible given
  733. //the analog game port technology.
  734. Status = SidewndrReset (((PJOY_EXTENSION)pDO->DeviceExtension)->DeviceAddress);
  735. ((PJOY_EXTENSION)pDO->DeviceExtension)->CurrentDeviceMode =
  736. SIDEWINDER3P_ENHANCED_DIGITAL_MODE;
  737. //KeDelayExecutionThread( KernelMode, FALSE, &LI10ms); //unnecessary since SidewndrReset has a delay in it?
  738. pIrp->IoStatus.Status = Status;
  739. break;
  740. case IRP_MJ_CLOSE:
  741. break;
  742. case IRP_MJ_READ:
  743. //
  744. // Find out which device we are and read, but first make sure
  745. // there is enough room
  746. //
  747. DebugTrace(("IRP_MJ_READ"));
  748. //DbgBreakPoint();
  749. if (pIrpStack->Parameters.Read.Length < sizeof(JOY_DD_INPUT_DATA))
  750. {
  751. Status = STATUS_BUFFER_TOO_SMALL;
  752. pIrp->IoStatus.Status = Status;
  753. break;
  754. }
  755. //
  756. // Serialize and get the current device values
  757. //
  758. KeAcquireSpinLock(((PJOY_EXTENSION) pDO->DeviceExtension)->SpinLock,
  759. & OldIrql);
  760. Status = SidewndrPoll(pDO, pIrp);
  761. //
  762. // release the spinlock
  763. //
  764. KeReleaseSpinLock(((PJOY_EXTENSION)pDO->DeviceExtension)->SpinLock,
  765. OldIrql);
  766. pIrp->IoStatus.Status = Status;
  767. pIrp->IoStatus.Information = sizeof (JOY_DD_INPUT_DATA);
  768. break;
  769. case IRP_MJ_DEVICE_CONTROL:
  770. switch (pIrpStack->Parameters.DeviceIoControl.IoControlCode)
  771. {
  772. case IOCTL_JOY_GET_STATISTICS:
  773. // report statistics
  774. ((PJOY_STATISTICS)pIrp->AssociatedIrp.SystemBuffer)->nVersion = JoyStatistics.nVersion;
  775. ((PJOY_STATISTICS)pIrp->AssociatedIrp.SystemBuffer)->EnhancedPolls = JoyStatistics.EnhancedPolls;
  776. ((PJOY_STATISTICS)pIrp->AssociatedIrp.SystemBuffer)->EnhancedPollTimeouts = JoyStatistics.EnhancedPollTimeouts;
  777. ((PJOY_STATISTICS)pIrp->AssociatedIrp.SystemBuffer)->EnhancedPollErrors = JoyStatistics.EnhancedPollErrors;
  778. ((PJOY_STATISTICS)pIrp->AssociatedIrp.SystemBuffer)->nPolledTooSoon = JoyStatistics.nPolledTooSoon;
  779. ((PJOY_STATISTICS)pIrp->AssociatedIrp.SystemBuffer)->nReset = JoyStatistics.nReset;
  780. {
  781. int i;
  782. for (i = 0; i < MAX_ENHANCEDMODE_ATTEMPTS; i++) {
  783. ((PJOY_STATISTICS)pIrp->AssociatedIrp.SystemBuffer)->Retries[i] = JoyStatistics.Retries[i];
  784. }
  785. }
  786. ((PJOY_STATISTICS)pIrp->AssociatedIrp.SystemBuffer)->dwQPCLatency = dwQPCLatency;
  787. ((PJOY_STATISTICS)pIrp->AssociatedIrp.SystemBuffer)->nReadLoopMax = nReadLoopMax;
  788. ((PJOY_STATISTICS)pIrp->AssociatedIrp.SystemBuffer)->Frequency = Frequency;
  789. Status = STATUS_SUCCESS;
  790. pIrp->IoStatus.Status = Status;
  791. pIrp->IoStatus.Information = sizeof(JOY_STATISTICS);
  792. // reset statistics
  793. JoyStatistics.EnhancedPolls = 0;
  794. JoyStatistics.EnhancedPollTimeouts = 0;
  795. JoyStatistics.EnhancedPollErrors = 0;
  796. JoyStatistics.nPolledTooSoon = 0;
  797. JoyStatistics.nReset = 0;
  798. {
  799. int i;
  800. for (i = 0; i < MAX_ENHANCEDMODE_ATTEMPTS; i++) {
  801. JoyStatistics.Retries[i] = 0;
  802. }
  803. }
  804. break;
  805. case IOCTL_JOY_GET_JOYREGHWCONFIG:
  806. SidewndrGetConfig (
  807. (LPJOYREGHWCONFIG)(pIrp->AssociatedIrp.SystemBuffer),
  808. ((PJOY_EXTENSION)pDO->DeviceExtension)
  809. );
  810. pIrp->IoStatus.Information = sizeof(JOYREGHWCONFIG);
  811. break;
  812. default:
  813. DebugTrace(("Unknown IoControlCode"));
  814. break;
  815. } // end switch on IOCTL code
  816. break;
  817. default:
  818. DebugTrace(("Unknown IRP Major Function %d", pIrpStack->MajorFunction));
  819. } // end switch on IRP_MAJOR_XXXX
  820. // pIrp->IoStatus.Status must be set to Status by this point.
  821. // pIrp->IoStatus.Information must be set to the correct size by this point.
  822. IoCompleteRequest(pIrp, IO_NO_INCREMENT);
  823. return Status;
  824. }
  825. VOID
  826. SidewndrUnload(
  827. PDRIVER_OBJECT pDriverObject
  828. )
  829. /*++
  830. Routine Description:
  831. Driver unload routine. Deletes the device objects
  832. Arguments:
  833. pDriverObject -- pointer to the driver object whose devices we
  834. are about to delete.
  835. Return Value:
  836. Returns Nothing
  837. --*/
  838. {
  839. DWORD DeviceNumber;
  840. WCHAR UnicodeDosDeviceName[200];
  841. //
  842. // Delete all of our devices
  843. //
  844. while (pDriverObject->DeviceObject)
  845. {
  846. DeviceNumber =
  847. ((PJOY_EXTENSION)pDriverObject->DeviceObject->DeviceExtension)->
  848. DeviceNumber;
  849. //
  850. // withdraw claims on hardware by reporting no resource utilization
  851. //
  852. if (pDriverObject->DeviceObject)
  853. {
  854. if (DeviceNumber == 0)
  855. {
  856. SidewndrReportNullResourceUsage(pDriverObject->DeviceObject);
  857. }
  858. }
  859. RtlInitUnicodeString(
  860. (PUNICODE_STRING) &UnicodeDosDeviceName,
  861. L"\\DosDevices\\Joy1");
  862. IoDeleteSymbolicLink(
  863. (PUNICODE_STRING) &UnicodeDosDeviceName);
  864. DebugTrace(("Freeing device %d", DeviceNumber));
  865. IoDeleteDevice(pDriverObject->DeviceObject);
  866. }
  867. }
  868. NTSTATUS
  869. SidewndrPoll(
  870. IN PDEVICE_OBJECT pDO,
  871. IN PIRP pIrp
  872. )
  873. /*++
  874. Routine Description:
  875. Polls the device for position and button information. The polling method
  876. (analog, digital, enhanced) is selected by the CurrentDeviceMode variable
  877. in the device extension.
  878. Only enhanced digital allowed. If other modes are necessary, cut and paste
  879. (and test!) the code from file analog3p.c
  880. Arguments:
  881. pDO -- pointer to the device object
  882. pIrp -- pointer to the IRP to process
  883. if successful, data is put into the pIrp
  884. Return Value:
  885. STATUS_SUCCESS -- if the poll succeeded,
  886. STATUS_TIMEOUT -- if the poll failed
  887. --*/
  888. {
  889. NTSTATUS Status;
  890. PJOY_DD_INPUT_DATA pInput;
  891. pInput = (PJOY_DD_INPUT_DATA)pIrp->AssociatedIrp.SystemBuffer;
  892. Status = STATUS_TIMEOUT;
  893. pIrp->IoStatus.Status = Status;
  894. if (pInput != NULL)
  895. {
  896. pInput->Unplugged = TRUE; // until proven otherwise
  897. }
  898. switch (((PJOY_EXTENSION)pDO->DeviceExtension)->CurrentDeviceMode)
  899. {
  900. case SIDEWINDER3P_INVALID_MODE:
  901. break;
  902. case SIDEWINDER3P_ANALOG_MODE:
  903. break;
  904. case SIDEWINDER3P_DIGITAL_MODE:
  905. break;
  906. case SIDEWINDER3P_ENHANCED_DIGITAL_MODE:
  907. // Don't poll too frequently, instead return last good packet
  908. if (KeQueryPerformanceCounter(NULL).QuadPart < liLastPoll.QuadPart + nMinTicksBetweenPolls) {
  909. JoyStatistics.nPolledTooSoon++;
  910. if (bLastGoodPacket) {
  911. RtlCopyMemory (pInput, &jjLastGoodPacket, sizeof (JOY_DD_INPUT_DATA));
  912. Status = STATUS_SUCCESS;
  913. }
  914. else {
  915. // no last packet, too soon to poll, nothing we can do
  916. Status = STATUS_TIMEOUT;
  917. }
  918. break;
  919. }
  920. // Poll the joystick
  921. Status = SidewndrEnhancedDigitalPoll(pDO, pIrp);
  922. if (Status == STATUS_SUCCESS) {
  923. // Everything's fine
  924. break;
  925. }
  926. else {
  927. // timed out, maybe user switched to analog mode?
  928. Status = SidewndrReset ( (PUCHAR) ((PJOY_EXTENSION)pDO->DeviceExtension)->DeviceAddress);
  929. JoyStatistics.nReset++;
  930. if (Status != STATUS_SUCCESS) {
  931. // won't go digital, maybe unplugged, nothing we can do
  932. break;
  933. }
  934. }
  935. // Now in enhanced digital mode, try polling it again (if user switches joystick between prev lines and
  936. // this line, we'll time out, next query to the joystick will find and solve the problem)
  937. Status = SidewndrEnhancedDigitalPoll(pDO, pIrp);
  938. break;
  939. case SIDEWINDER3P_MAXIMUM_MODE:
  940. break;
  941. default:
  942. break;
  943. }
  944. pIrp->IoStatus.Status = Status;
  945. return Status;
  946. }
  947. NTSTATUS
  948. SidewndrEnhancedDigitalPoll(
  949. IN PDEVICE_OBJECT pDO,
  950. IN PIRP pIrp
  951. )
  952. {
  953. PUCHAR joyPort;
  954. NTSTATUS PollStatus;
  955. NTSTATUS DecodeStatus;
  956. DWORD MaxRetries;
  957. joyPort = ((PJOY_EXTENSION)pDO->DeviceExtension)->DeviceAddress;
  958. // Try to get a good enhanced mode packet up to MAX_ENHANCEDMODE_ATTEMPTS
  959. // If there is a timeout, or if the data are invalid (bad checksum or sync
  960. // bits) wait 1ms for the joystick to reset itself, and try again.
  961. //
  962. // Note that although this should eventually get a good packet, packets
  963. // discarded in the interim (because of errors) will cause button presses
  964. // to be lost.
  965. //
  966. // Although this loses data, it keeps bad data from reaching the caller,
  967. // which seem to be about the best we can do at this stage.
  968. //
  969. // We keep a count of all the errors so that we keep track of just
  970. // how bad the situation really is.
  971. //
  972. for( MaxRetries = 0; MaxRetries < MAX_ENHANCEDMODE_ATTEMPTS; MaxRetries++)
  973. {
  974. // try to read (poll) the device
  975. liLastPoll = KeQueryPerformanceCounter (NULL);
  976. PollStatus = SidewndrGetEnhancedPacket(joyPort);
  977. ++JoyStatistics.EnhancedPolls;
  978. if (PollStatus != STATUS_SUCCESS)
  979. {
  980. // There was a timeout of some sort on the device read.
  981. ++JoyStatistics.EnhancedPollTimeouts;
  982. }
  983. else
  984. {
  985. // The device read completed. Process the data and verify the checksum
  986. // and sync bits. The processed data will be in AssociatedIrp.SystemBuffer
  987. DecodeStatus = SidewndrInterpretEnhancedPacket(
  988. (PJOY_DD_INPUT_DATA)pIrp->AssociatedIrp.SystemBuffer);
  989. if (DecodeStatus != STATUS_SUCCESS)
  990. {
  991. // The data was bad, most likely because we missed some of the nibbles.
  992. ++JoyStatistics.EnhancedPollErrors;
  993. }
  994. else
  995. {
  996. // Everything worked as we had hoped. The data has already been
  997. // deposited in the AssociatedIrp.SystemBuffer.
  998. JoyStatistics.Retries[MaxRetries]++;
  999. return STATUS_SUCCESS;
  1000. }
  1001. }
  1002. // We did not succeed in reading the packet. Wait 1 ms for the device to
  1003. // stabilize before re-trying the read
  1004. //KeDelayExecutionThread( KernelMode, FALSE, &LI1ms); // cannot use KeDelayExecutionThread here
  1005. // because we're at dispatch level, thanks
  1006. // to the spin lock we hold
  1007. // Mail from manolito says (64-48)*10us = 160us should be enough. But I seem to recall reading 21 packets out of 66 sent.
  1008. // Pending answer from manolito, set to 450us.
  1009. SidewndrWait (450); // this is bad because it monopolizes the cpu, but since we're spinlocked anyway, what the heck, do it.
  1010. }
  1011. // We exceeded MAX_ENHANCEDMODE_ATTEMPTS. Something is pretty badly wrong;
  1012. // in any case, a higher level caller will have to decide what to do
  1013. return STATUS_TIMEOUT;
  1014. }
  1015. NTSTATUS
  1016. SidewndrReportNullResourceUsage(
  1017. PDEVICE_OBJECT DeviceObject
  1018. )
  1019. {
  1020. BOOLEAN ResourceConflict;
  1021. CM_RESOURCE_LIST ResourceList;
  1022. NTSTATUS Status;
  1023. ResourceList.Count = 0;
  1024. //
  1025. // Report our usage and detect conflicts
  1026. //
  1027. Status = IoReportResourceUsage( NULL,
  1028. DeviceObject->DriverObject,
  1029. &ResourceList,
  1030. sizeof(DWORD),
  1031. DeviceObject,
  1032. NULL,
  1033. 0,
  1034. FALSE,
  1035. &ResourceConflict);
  1036. if (NT_SUCCESS(Status))
  1037. {
  1038. if (ResourceConflict)
  1039. {
  1040. return STATUS_DEVICE_CONFIGURATION_ERROR;
  1041. }
  1042. else
  1043. {
  1044. return STATUS_SUCCESS;
  1045. }
  1046. }
  1047. else
  1048. {
  1049. return Status;
  1050. }
  1051. }
  1052. BOOL
  1053. SidewndrQuiesce(
  1054. PUCHAR JoyPort,
  1055. UCHAR Mask
  1056. )
  1057. /*++
  1058. Routine Description:
  1059. This routine attempts to insure that the joystick is not still active as a
  1060. result of an earlier operation. This is accomplished by repeatedly reading
  1061. the device and checking that no bits are set in the supplied mask. The idea
  1062. is to check that none of the analog bits (resistive bits) are in use.
  1063. Arguments:
  1064. JoyPort - the address of the port (as returned from hal)
  1065. Mask - the mask specifying which analog bits should be checked.
  1066. Return Value:
  1067. TRUE Quiesce operation succeeded
  1068. FALSE No quiesce within a reasonable period. This generally means
  1069. that the device is unplugged.
  1070. NB This is not a reliable test for "joystick unplugged"
  1071. This routine can return TRUE under some circumstances
  1072. even when there is no joystick
  1073. --*/
  1074. {
  1075. int i;
  1076. UCHAR PortVal;
  1077. //
  1078. // Wait for the stuff to quiesce
  1079. //
  1080. for (i = 0; i < ANALOG_POLL_TIMEOUT; i++) {
  1081. PortVal = READ_PORT_UCHAR(JoyPort);
  1082. if ((PortVal & Mask) == 0){
  1083. return TRUE;
  1084. } else {
  1085. KeStallExecutionProcessor(1);
  1086. }
  1087. }
  1088. //
  1089. // If poll timed out we have an uplugged joystick
  1090. //
  1091. DebugTrace(("SidewndrQuiesce failed!"));
  1092. return FALSE;
  1093. }
  1094. NTSTATUS
  1095. SidewndrMapDevice(
  1096. DWORD PortBase,
  1097. DWORD NumberOfPorts,
  1098. PJOY_EXTENSION pJoyExtension
  1099. )
  1100. {
  1101. DWORD MemType;
  1102. PHYSICAL_ADDRESS PortAddress;
  1103. PHYSICAL_ADDRESS MappedAddress;
  1104. MemType = 1; // IO space
  1105. PortAddress.LowPart = PortBase;
  1106. PortAddress.HighPart = 0;
  1107. HalTranslateBusAddress(
  1108. Isa,
  1109. 0,
  1110. PortAddress,
  1111. &MemType,
  1112. &MappedAddress);
  1113. if (MemType == 0) {
  1114. //
  1115. // Map memory type IO space into our address space
  1116. //
  1117. pJoyExtension->DeviceAddress = (PUCHAR) MmMapIoSpace(MappedAddress,
  1118. NumberOfPorts,
  1119. FALSE);
  1120. pJoyExtension->DeviceIsMapped = TRUE;
  1121. }
  1122. else
  1123. {
  1124. pJoyExtension->DeviceAddress = (PUCHAR) MappedAddress.LowPart;
  1125. pJoyExtension->DeviceIsMapped = FALSE;
  1126. }
  1127. return STATUS_SUCCESS;
  1128. }
  1129. DWORD
  1130. TimeInMicroSeconds(
  1131. DWORD dwTime
  1132. )
  1133. {
  1134. DWORD Remainder;
  1135. return RtlExtendedLargeIntegerDivide(
  1136. RtlEnlargedUnsignedMultiply( dwTime, 1000000L),
  1137. Frequency,
  1138. &Remainder
  1139. ).LowPart;
  1140. }
  1141. DWORD
  1142. TimeInTicks(
  1143. DWORD dwTimeInMicroSeconds
  1144. )
  1145. {
  1146. return (DWORD) (((__int64)dwTimeInMicroSeconds * (__int64)Frequency) / (__int64) 1000000L);
  1147. }
  1148. NTSTATUS
  1149. SidewndrWaitForClockEdge(
  1150. DWORD edge,
  1151. BYTE *pByte,
  1152. PUCHAR JoyPort
  1153. )
  1154. /*++
  1155. Routine Description:
  1156. Waits for the clock line to go high, or low depending on a the supplied
  1157. parameter (edge). If edge is CLOCK_RISING_EDGE, waits for rising edge,
  1158. else if edge is CLOCK_FALLING_EDGE
  1159. An upper bound for the wait duration is set at 1000 iterations.
  1160. Arguments:
  1161. edge -- CLOCK_RISING_EDGE or CLOCK_FALLING Edge to specify what to await
  1162. pByte -- The contents of the device register are returned for other use
  1163. Return Value:
  1164. STATUS_SUCCESS -- the specified edge was detected before timeout
  1165. STATUS_TIMEOUT -- timeout before detecting specified edge.
  1166. --*/
  1167. {
  1168. DWORD maxTimeout;
  1169. BYTE joyByte;
  1170. maxTimeout = nReadLoopMax;
  1171. if (CLOCK_RISING_EDGE == edge)
  1172. {
  1173. while (maxTimeout--)
  1174. {
  1175. joyByte = READ_PORT_UCHAR(JoyPort);
  1176. if (joyByte & CLOCK_BITMASK)
  1177. {
  1178. *pByte = joyByte;
  1179. return STATUS_SUCCESS;
  1180. }
  1181. }
  1182. *pByte = joyByte;
  1183. return STATUS_TIMEOUT;
  1184. }
  1185. else
  1186. {
  1187. while (maxTimeout--)
  1188. {
  1189. joyByte = READ_PORT_UCHAR(JoyPort);
  1190. if (!(joyByte & CLOCK_BITMASK))
  1191. {
  1192. *pByte = joyByte;
  1193. return STATUS_SUCCESS;
  1194. }
  1195. }
  1196. *pByte = joyByte;
  1197. return STATUS_TIMEOUT;
  1198. }
  1199. }
  1200. NTSTATUS
  1201. SidewndrReset(
  1202. PUCHAR JoyPort
  1203. )
  1204. // This resets the joystick to enhanced digital mode.
  1205. {
  1206. DWORD dwRetries;
  1207. NTSTATUS Status;
  1208. dwRetries = 0;
  1209. do {
  1210. ++dwRetries;
  1211. Status = SidewndrStartAnalogMode(JoyPort);
  1212. if (Status == STATUS_TIMEOUT) continue;
  1213. //KeDelayExecutionThread( KernelMode, FALSE, &LI10ms); //MarkSV thinks this is unnecessary
  1214. Status = SidewndrStartDigitalMode(JoyPort);
  1215. if (Status == STATUS_TIMEOUT) continue;
  1216. //KeDelayExecutionThread( KernelMode, FALSE, &LI10ms); //MarkSV thinks this is unnecessary
  1217. Status = SidewndrStartEnhancedMode(JoyPort);
  1218. } while ((Status == STATUS_TIMEOUT) && (dwRetries < 10) );
  1219. // give the joystick time to stabilize MarkSV thinks this is unnecessary
  1220. //KeDelayExecutionThread( KernelMode, FALSE, &LI10ms);
  1221. return Status;
  1222. }
  1223. NTSTATUS
  1224. SidewndrStartAnalogMode(
  1225. PUCHAR JoyPort
  1226. )
  1227. {
  1228. KIRQL OldIrql;
  1229. if(! SidewndrQuiesce(JoyPort, 0x01))
  1230. {
  1231. return STATUS_TIMEOUT;
  1232. }
  1233. KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
  1234. WRITE_PORT_UCHAR(JoyPort, JOY_START_TIMERS);
  1235. if (!SidewndrReadWait(JoyPort, X_AXIS_BITMASK)) goto timeout;
  1236. WRITE_PORT_UCHAR(JoyPort, JOY_START_TIMERS);
  1237. if (!SidewndrReadWait(JoyPort, X_AXIS_BITMASK)) goto timeout;
  1238. WRITE_PORT_UCHAR(JoyPort, JOY_START_TIMERS);
  1239. KeLowerIrql(OldIrql);
  1240. //
  1241. // Wait 1ms to let port settle out
  1242. //
  1243. KeDelayExecutionThread( KernelMode, FALSE, &LI1ms); // MarkSV says 1 ms is enough, original code had 8 ms
  1244. return STATUS_SUCCESS;
  1245. timeout:
  1246. KeLowerIrql(OldIrql);
  1247. return STATUS_TIMEOUT;
  1248. }
  1249. NTSTATUS
  1250. SidewndrStartDigitalMode(
  1251. PUCHAR JoyPort
  1252. )
  1253. {
  1254. KIRQL OldIrql;
  1255. DWORD dwStart, dwX0, dwX1, dwX2, dwX3;
  1256. DebugTrace(("Sidewndr: Digital Mode Requested"));
  1257. SidewndrQuiesce(JoyPort, 0x01);
  1258. KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
  1259. WRITE_PORT_UCHAR(JoyPort, JOY_START_TIMERS);
  1260. if (!SidewndrReadWait(JoyPort, X_AXIS_BITMASK)) goto timeout;
  1261. SidewndrWait (75);
  1262. WRITE_PORT_UCHAR(JoyPort, JOY_START_TIMERS);
  1263. if (!SidewndrReadWait(JoyPort, X_AXIS_BITMASK)) goto timeout;
  1264. SidewndrWait (75 + 726);
  1265. WRITE_PORT_UCHAR(JoyPort, JOY_START_TIMERS);
  1266. if (!SidewndrReadWait(JoyPort, X_AXIS_BITMASK)) goto timeout;
  1267. SidewndrWait (75 + 300);
  1268. WRITE_PORT_UCHAR(JoyPort, JOY_START_TIMERS);
  1269. if (!SidewndrReadWait(JoyPort, X_AXIS_BITMASK)) goto timeout;
  1270. KeLowerIrql(OldIrql);
  1271. SidewndrQuiesce(JoyPort, 0x01);
  1272. return STATUS_SUCCESS;
  1273. timeout:
  1274. KeLowerIrql(OldIrql);
  1275. return STATUS_TIMEOUT;
  1276. }
  1277. NTSTATUS
  1278. SidewndrStartEnhancedMode(
  1279. PUCHAR JoyPort
  1280. )
  1281. {
  1282. DWORD byteIndex;
  1283. DWORD bitIndex;
  1284. BYTE JoyByte;
  1285. NTSTATUS Status;
  1286. KIRQL OldIrql;
  1287. KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
  1288. WRITE_PORT_UCHAR(JoyPort, JOY_START_TIMERS);
  1289. // Wait for serial clock to go high, probably already there.
  1290. Status = SidewndrWaitForClockEdge(CLOCK_RISING_EDGE, &JoyByte, JoyPort);
  1291. if (Status != STATUS_SUCCESS)
  1292. {
  1293. KeLowerIrql(OldIrql);
  1294. DebugTrace(("SidewndrStartEnhancedMode: timeout in first spin"));
  1295. return(STATUS_TIMEOUT);
  1296. }
  1297. for (byteIndex = 0; byteIndex < 6; byteIndex++)
  1298. {
  1299. for (bitIndex = 0; bitIndex < 8; bitIndex++)
  1300. {
  1301. // look for falling edge of serial clock.
  1302. Status = SidewndrWaitForClockEdge(CLOCK_FALLING_EDGE, &JoyByte, JoyPort);
  1303. if (Status != STATUS_SUCCESS)
  1304. {
  1305. KeLowerIrql(OldIrql);
  1306. DebugTrace(("SidewndrStartEnhancedMode: timeout in second spin byteIndex %d bitIndex %d", byteIndex, bitIndex));
  1307. return(STATUS_TIMEOUT);
  1308. }
  1309. // Wait for serial clock to go high.
  1310. Status = SidewndrWaitForClockEdge(CLOCK_RISING_EDGE, &JoyByte, JoyPort);
  1311. if (Status != STATUS_SUCCESS)
  1312. {
  1313. KeLowerIrql(OldIrql);
  1314. DebugTrace(("SidewndrStartEnhancedMode: timeout in third spin"));
  1315. return(STATUS_TIMEOUT);
  1316. }
  1317. }
  1318. }
  1319. // Interrupt the processor again, telling it to send an ID packet.
  1320. // After getting the ID packet it knows to go into enhanced mode.
  1321. // This does not affect the packet currently going.
  1322. WRITE_PORT_UCHAR(JoyPort, JOY_START_TIMERS);
  1323. // Wait out the rest of the packet so we can figure out how long this takes.
  1324. for (byteIndex = 6; byteIndex < 8; byteIndex++)
  1325. {
  1326. for (bitIndex = 0; bitIndex < 8; bitIndex++)
  1327. {
  1328. // look for falling edge of serial clock.
  1329. Status = SidewndrWaitForClockEdge(CLOCK_FALLING_EDGE, &JoyByte, JoyPort);
  1330. if (Status != STATUS_SUCCESS)
  1331. {
  1332. KeLowerIrql(OldIrql);
  1333. DebugTrace(("SidewndrStartEnhancedMode Timeout in 4th spin"));
  1334. return(STATUS_TIMEOUT);
  1335. }
  1336. // Wait for serial clock to go high.
  1337. Status = SidewndrWaitForClockEdge(CLOCK_RISING_EDGE, &JoyByte, JoyPort);
  1338. if (Status != STATUS_SUCCESS)
  1339. {
  1340. KeLowerIrql(OldIrql);
  1341. DebugTrace(("SidewndrStartEnhancedMode Timeout in 5th spin"));
  1342. return(STATUS_TIMEOUT);
  1343. }
  1344. }
  1345. }
  1346. KeLowerIrql(OldIrql);
  1347. //m_tmPacketTime = SystemTime() - tmStartTime;
  1348. // The joystick ID comes across on 20 bytes and we just did 8 bytes,
  1349. // so wait (with interrupts enabled) long enough for the ID packet to
  1350. // complete. After that we should be in enhanced mode. Each nibble takes
  1351. // about 10us, so 1ms should be plenty of time for everything.
  1352. KeDelayExecutionThread( KernelMode, FALSE, &LI1ms);
  1353. return(STATUS_SUCCESS);
  1354. }
  1355. /*++
  1356. *******************************************************************************
  1357. Routine:
  1358. CSidewinder::GetEnhancedPacket
  1359. Description:
  1360. If the joystick is in digital enhanced mode, you can call this to
  1361. get a digital packet and store the data into the class' m_enhancedPacket
  1362. member variable. Call InterpretEnhancedPacket to turn the raw data into
  1363. joystick info.
  1364. Note that while you can get an enhanced packet in 1/3 the time of a normal
  1365. packet (and can thus turn back on interrputs much sooner), you can not get
  1366. enhanced packets any faster than you can get normal packets. This function
  1367. will check to make sure sufficient time has passed since the last time it
  1368. was called and if it hasn't it will wait (with interrupts ENABLED) until
  1369. that is true before asking for another packet.
  1370. This assumes the joystick is in digital enhanced mode and there is no way
  1371. to tell if this is not the case. If the joystick is just in digital
  1372. (non-enhanced) mode then this will return successfully. However, the
  1373. checksum and/or sync bits will not be correct.
  1374. Arguments:
  1375. None.
  1376. Return Value:
  1377. successful if it worked.
  1378. not_digital_mode if the joystick is not in digital mode.
  1379. *******************************************************************************
  1380. --*/
  1381. NTSTATUS
  1382. SidewndrGetEnhancedPacket(
  1383. PUCHAR JoyPort
  1384. )
  1385. {
  1386. KIRQL OldIrql;
  1387. DWORD byteIndex;
  1388. BYTE joyByte;
  1389. BYTE masks[8] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 };
  1390. NTSTATUS Status;
  1391. // While enhanced packets come across faster than normal packets,
  1392. // they can not be called any more frequently. This makes sure
  1393. // we've let enough time since the last packet go by before calling
  1394. // for another.
  1395. KeRaiseIrql(DISPATCH_LEVEL, &OldIrql); // This great and sensitive irql stuff is useless since the spinlock stuff WAY up high puts us a dispatch
  1396. // Start the retrieval operation
  1397. WRITE_PORT_UCHAR(JoyPort, 0);
  1398. // Wait for serial clock to go high, probably already there.
  1399. Status = SidewndrWaitForClockEdge(CLOCK_RISING_EDGE, &joyByte, JoyPort);
  1400. if (Status != STATUS_SUCCESS)
  1401. {
  1402. KeLowerIrql(OldIrql);
  1403. return(STATUS_TIMEOUT);
  1404. }
  1405. for (byteIndex = 0; byteIndex < 21; byteIndex++)
  1406. {
  1407. // look for falling edge of serial clock.
  1408. Status = SidewndrWaitForClockEdge(CLOCK_FALLING_EDGE, &joyByte, JoyPort);
  1409. if (Status != STATUS_SUCCESS)
  1410. {
  1411. KeLowerIrql(OldIrql);
  1412. return(STATUS_TIMEOUT);
  1413. }
  1414. // Wait for serial clock to go high.
  1415. Status = SidewndrWaitForClockEdge(CLOCK_RISING_EDGE, &joyByte, JoyPort);
  1416. if (Status != STATUS_SUCCESS)
  1417. {
  1418. KeLowerIrql(OldIrql);
  1419. return(STATUS_TIMEOUT);
  1420. }
  1421. EnhancedPacket[byteIndex] = (joyByte & ALLDATA_BITMASK) >> 5;
  1422. }
  1423. KeLowerIrql(OldIrql);
  1424. // NB, the joystick will still send 66 packets even though we only needed the first
  1425. // 21 of them. Don't attempt to poll the joystick until it's finished. This is another
  1426. // reason to require a minimum time between polls. (About 500us will be enough.)
  1427. return(STATUS_SUCCESS);
  1428. }
  1429. /*++
  1430. *******************************************************************************
  1431. Routine:
  1432. CSidewinder::InterpretEnhancedPacket
  1433. Description:
  1434. Call this after getting an enhanced packet. It converts the raw data into
  1435. normal joystick data, filling out the class' m_data structure.
  1436. The encoding of the raw Data bits (D1-D3) is given below.
  1437. Data packet format for Enhanced Mode transmission (4 line)
  1438. Byte D3 D2 D1 D0
  1439. 0 Y9 Y8 Y7 SCLK
  1440. 1 X9 X8 X7 SCLK
  1441. 2 B0 1 H3 SCLK
  1442. 3 B3 B2 B1 SCLK
  1443. 4 B6 B5 B4 SCLK
  1444. 5 X1 X0 0 SCLK
  1445. 6 X4 X3 X2 SCLK
  1446. 7 0 X6 X5 SCLK
  1447. 8 Y2 Y1 Y0 SCLK
  1448. 9 Y5 Y4 Y3 SCLK
  1449. 10 T7 0 Y6 SCLK
  1450. 11 R7 T9 T8 SCLK
  1451. 12 B7 CH/TM R8 SCLK
  1452. 13 R1 R0 0 SCLK
  1453. 14 R4 R3 R2 SCLK
  1454. 15 0 R6 R5 SCLK
  1455. 16 T2 T1 T0 SCLK
  1456. 17 T5 T4 T3 SCLK
  1457. 18 CHKSUM0 0 T6 SCLK
  1458. 19 CHKSUM3 CHKSUM2 CHKSUM1 SCLK
  1459. 20 H2 H1 H0 SCLK
  1460. 21 0 0 0 SCLK
  1461. Arguments:
  1462. None.
  1463. Return Value:
  1464. successful if the data was valid.
  1465. bad_packet if either the checksum or sync bits were incorrect.
  1466. *******************************************************************************
  1467. --*/
  1468. NTSTATUS
  1469. SidewndrInterpretEnhancedPacket(
  1470. PJOY_DD_INPUT_DATA pInput
  1471. )
  1472. {
  1473. WORD temp16;
  1474. BYTE temp8;
  1475. BYTE checksum;
  1476. pInput->Unplugged = FALSE;
  1477. pInput->Mode = SIDEWINDER3P_ENHANCED_DIGITAL_MODE;
  1478. //Get xOffset.
  1479. temp16 = 0x0000;
  1480. temp16 |= (EnhancedPacket[1] & 0x07) << 7;
  1481. temp16 |= (EnhancedPacket[7] & 0x03) << 5;
  1482. temp16 |= (EnhancedPacket[6] & 0x07) << 2;
  1483. temp16 |= (EnhancedPacket[5] & 0x06) >> 1;
  1484. pInput->u.DigitalData.XOffset = temp16;
  1485. //Get yOffset.
  1486. temp16 = 0x0000;
  1487. temp16 |= (EnhancedPacket[0] & 0x07) << 7;
  1488. temp16 |= (EnhancedPacket[10] & 0x01) << 6;
  1489. temp16 |= (EnhancedPacket[9] & 0x07) << 3;
  1490. temp16 |= (EnhancedPacket[5] & 0x07);
  1491. pInput->u.DigitalData.YOffset = temp16;
  1492. //Get rzOffset: Only 9 bits (others are 10)
  1493. temp16 = 0x0000;
  1494. temp16 |= (EnhancedPacket[12] & 0x01) << 8;
  1495. temp16 |= (EnhancedPacket[11] & 0x04) << 5;
  1496. temp16 |= (EnhancedPacket[15] & 0x03) << 5;
  1497. temp16 |= (EnhancedPacket[14] & 0x07) << 2;
  1498. temp16 |= (EnhancedPacket[13] & 0x06) >> 1;
  1499. pInput->u.DigitalData.RzOffset = temp16;
  1500. //Get tOffset.
  1501. temp16 = 0x0000;
  1502. temp16 |= (EnhancedPacket[11] & 0x03) << 8;
  1503. temp16 |= (EnhancedPacket[10] & 0x04) << 5;
  1504. temp16 |= (EnhancedPacket[18] & 0x01) << 6;
  1505. temp16 |= (EnhancedPacket[17] & 0x07) << 3;
  1506. temp16 |= (EnhancedPacket[16] & 0x07);
  1507. pInput->u.DigitalData.TOffset = temp16;
  1508. //Get Hat
  1509. temp8 = 0x00;
  1510. temp8 |= (EnhancedPacket[2] & 0x01) << 3;
  1511. temp8 |= (EnhancedPacket[20] & 0x07);
  1512. pInput->u.DigitalData.Hat = temp8;
  1513. //Get Buttons
  1514. temp8 = 0x00;
  1515. temp8 |= (EnhancedPacket[2] & 0x04) >> 2;
  1516. temp8 |= (EnhancedPacket[3] & 0x07) << 1;
  1517. temp8 |= (EnhancedPacket[4] & 0x07) << 4;
  1518. temp8 |= (EnhancedPacket[12] & 0x04) << 5;
  1519. temp8 = ~temp8; // Buttons are 1 = off, 0 = on. Want the opposite.
  1520. pInput->u.DigitalData.Buttons = temp8;
  1521. // Get CH/TM switch.
  1522. pInput->u.DigitalData.Switch_CH_TM =
  1523. ((EnhancedPacket[12] & 0x02) == 0) ? 1 : 2;
  1524. // Get Checksum
  1525. temp8 = 0x00;
  1526. temp8 |= (EnhancedPacket[18] & 0x04) >> 2;
  1527. temp8 |= (EnhancedPacket[19] & 0x07) << 1;
  1528. pInput->u.DigitalData.Checksum = temp8;
  1529. //
  1530. // Check the checksum. Because the enhance mode retrieves the data packet
  1531. // 3 bits at a time, the data is not in the same order that it arrives in
  1532. // in the normal mode. Thus, calculating the checksum requires additional
  1533. // manipulation.
  1534. //
  1535. checksum = pInput->u.DigitalData.Checksum;
  1536. checksum += 0x08 | ((EnhancedPacket[2] & 0x01) << 2) |
  1537. ((EnhancedPacket[1] & 0x06) >> 1);
  1538. checksum += ((EnhancedPacket[1] & 0x01) << 3) |
  1539. (EnhancedPacket[0] & 0x07);
  1540. checksum += (EnhancedPacket[4] & 0x07);
  1541. checksum += ((EnhancedPacket[3] & 0x07) << 1) |
  1542. ((EnhancedPacket[2] & 0x04) >> 2);
  1543. checksum += ((EnhancedPacket[7] & 0x03) << 1) |
  1544. ((EnhancedPacket[6] & 0x04) >> 2);
  1545. checksum += ((EnhancedPacket[6] & 0x03) << 2) |
  1546. ((EnhancedPacket[5] & 0x06) >> 1);
  1547. checksum += ((EnhancedPacket[10] & 0x01) << 2) |
  1548. ((EnhancedPacket[9] & 0x06) >> 1);
  1549. checksum += ((EnhancedPacket[9] & 0x01) << 3) |
  1550. (EnhancedPacket[8] & 0x07);
  1551. checksum += (EnhancedPacket[12] & 0x07);
  1552. checksum += ((EnhancedPacket[11] & 0x07) << 1) |
  1553. ((EnhancedPacket[10] & 0x04) >> 2);
  1554. checksum += ((EnhancedPacket[15] & 0x03) << 1) |
  1555. ((EnhancedPacket[14] & 0x04) >> 2);
  1556. checksum += ((EnhancedPacket[14] & 0x03) << 2) |
  1557. ((EnhancedPacket[13] & 0x06) >> 1);
  1558. checksum += ((EnhancedPacket[18] & 0x01) << 2) |
  1559. ((EnhancedPacket[17] & 0x06) >> 1);
  1560. checksum += ((EnhancedPacket[17] & 0x01) << 3) |
  1561. (EnhancedPacket[16] & 0x07);
  1562. checksum += (EnhancedPacket[20] & 0x07);
  1563. checksum &= 0x0F;
  1564. if (checksum == 0)
  1565. {
  1566. pInput->u.DigitalData.fChecksumCorrect = TRUE;
  1567. }
  1568. else
  1569. {
  1570. pInput->u.DigitalData.fChecksumCorrect = FALSE;
  1571. DebugTrace(("Enhanced packet checksum failed.\n"));
  1572. }
  1573. //
  1574. // Check SyncBits
  1575. //
  1576. if ((EnhancedPacket[2] & 0x02) != 0)
  1577. {
  1578. checksum =
  1579. (EnhancedPacket[5] & 0x01) + (EnhancedPacket[7] & 0x04) +
  1580. (EnhancedPacket[10] & 0x02) + (EnhancedPacket[13] & 0x01) +
  1581. (EnhancedPacket[15] & 0x04) + (EnhancedPacket[18] & 0x02);
  1582. if (checksum == 0)
  1583. {
  1584. pInput->u.DigitalData.fSyncBitsCorrect = TRUE;
  1585. }
  1586. else
  1587. {
  1588. pInput->u.DigitalData.fSyncBitsCorrect = FALSE;
  1589. DebugTrace(("Enhanced packet sync bits incorrect.\n"));
  1590. }
  1591. }
  1592. else
  1593. {
  1594. pInput->u.DigitalData.fSyncBitsCorrect = FALSE;
  1595. }
  1596. if (pInput->u.DigitalData.fChecksumCorrect == TRUE &&
  1597. pInput->u.DigitalData.fSyncBitsCorrect == TRUE)
  1598. {
  1599. // everything worked, save this info as last good packet
  1600. RtlCopyMemory (&jjLastGoodPacket, pInput, sizeof (JOY_DD_INPUT_DATA));
  1601. bLastGoodPacket = TRUE;
  1602. return(STATUS_SUCCESS);
  1603. }
  1604. else
  1605. {
  1606. return(STATUS_TIMEOUT);
  1607. }
  1608. }
  1609. int lstrnicmpW (LPWSTR pszA, LPWSTR pszB, size_t cch)
  1610. {
  1611. if (!pszA || !pszB)
  1612. {
  1613. return (!pszB) - (!pszA); // A,!B:1, !A,B:-1, !A,!B:0
  1614. }
  1615. // while (cch--)
  1616. for ( ; cch > 0; cch--, pszA++, pszB++) // previous version did not increment string pointers [SteveZ]
  1617. {
  1618. if (!*pszA || !*pszB)
  1619. {
  1620. return (!*pszB) - (!*pszA); // A,!B:1, !A,B:-1, !A,!B:0
  1621. }
  1622. if (*pszA != *pszB)
  1623. {
  1624. return (int)(*pszA) - (int)(*pszB); // -1:A<B, 0:A==B, 1:A>B
  1625. }
  1626. }
  1627. return 0; // no differences before told to stop comparing, so A==B
  1628. }
  1629. VOID
  1630. SidewndrWait (
  1631. DWORD TotalWait // in uS
  1632. )
  1633. /*++
  1634. Routine Description:
  1635. This routine waits for the specified number of microseconds. Tolerances for
  1636. the joystick are smaller than NT typically provide, so all timing is isolated
  1637. into this routine, where we can do crude things and play nasty hacks as
  1638. necessary. This routine locks up the cpu, so only use it for putting the joystick
  1639. into digital mode.
  1640. Arguments:
  1641. TotalWait - time to wait in microseconds
  1642. --*/
  1643. {
  1644. DWORD ulStartTime, ulEndTime;
  1645. int nTicks;
  1646. // dwQPCLatency is the calibrated-for-this-machine latency for a call to KeQueryPerfomanceCounter (in uS).
  1647. nTicks = TimeInTicks (TotalWait - dwQPCLatency);
  1648. if (nTicks <= 0) return;
  1649. ulStartTime = KeQueryPerformanceCounter(NULL).LowPart;
  1650. ulEndTime = ulStartTime + nTicks;
  1651. while (KeQueryPerformanceCounter(NULL).LowPart < ulEndTime) {
  1652. ;
  1653. }
  1654. }
  1655. BOOL
  1656. SidewndrReadWait (
  1657. PUCHAR JoyPort,
  1658. UCHAR Mask
  1659. )
  1660. {
  1661. /*++
  1662. read a port and wait until it gives correct answer based on mask.
  1663. timeout after nReadLoopMax iterations (about 2 mS).
  1664. --*/
  1665. int i;
  1666. for (i = 0; i < nReadLoopMax; i++) {
  1667. if ( ! (READ_PORT_UCHAR(JoyPort) & Mask) )
  1668. return TRUE; // port went high
  1669. }
  1670. return FALSE; // timed out
  1671. }
  1672. void
  1673. SidewndrGetConfig (
  1674. LPJOYREGHWCONFIG pConfig,
  1675. PJOY_EXTENSION pJoyExtension
  1676. )
  1677. /*++
  1678. Routine Description:
  1679. This routine is called in response to the IOCTL_JOY_GET_JOYREGHWCONFIG
  1680. query. It fills out a JOYREGHWCONFIG structure with relevant information
  1681. about the given joystick.
  1682. Arguments:
  1683. pConfig - Specifies a JOYREGHWCONFIG structure, to be filled in
  1684. pJoyExtension - Specifies the joystick to query
  1685. Return Value:
  1686. void
  1687. --*/
  1688. {
  1689. pConfig->hws.dwNumButtons = 4;
  1690. switch (pJoyExtension->CurrentDeviceMode)
  1691. {
  1692. case SIDEWINDER3P_ANALOG_MODE:
  1693. {
  1694. pConfig->hws.dwFlags = JOY_HWS_HASPOV |
  1695. JOY_HWS_POVISBUTTONCOMBOS |
  1696. JOY_HWS_HASU |
  1697. JOY_HWS_HASR;
  1698. pConfig->dwUsageSettings = JOY_US_HASRUDDER |
  1699. JOY_US_PRESENT |
  1700. JOY_US_ISOEM;
  1701. pConfig->hwv.jrvHardware.jpMin.dwX = 20;
  1702. pConfig->hwv.jrvHardware.jpMin.dwY = 20;
  1703. pConfig->hwv.jrvHardware.jpMin.dwZ = 0;
  1704. pConfig->hwv.jrvHardware.jpMin.dwR = 20;
  1705. pConfig->hwv.jrvHardware.jpMin.dwU = 20;
  1706. pConfig->hwv.jrvHardware.jpMin.dwV = 0;
  1707. pConfig->hwv.jrvHardware.jpMax.dwX = 1600;
  1708. pConfig->hwv.jrvHardware.jpMax.dwY = 1600;
  1709. pConfig->hwv.jrvHardware.jpMax.dwZ = 0;
  1710. pConfig->hwv.jrvHardware.jpMax.dwR = 1600;
  1711. pConfig->hwv.jrvHardware.jpMax.dwU = 1600;
  1712. pConfig->hwv.jrvHardware.jpMax.dwV = 0;
  1713. pConfig->hwv.jrvHardware.jpCenter.dwX = 790;
  1714. pConfig->hwv.jrvHardware.jpCenter.dwY = 790;
  1715. pConfig->hwv.jrvHardware.jpCenter.dwZ = 0;
  1716. pConfig->hwv.jrvHardware.jpCenter.dwR = 790;
  1717. pConfig->hwv.jrvHardware.jpCenter.dwU = 790;
  1718. pConfig->hwv.jrvHardware.jpCenter.dwV = 0;
  1719. break;
  1720. }
  1721. default:
  1722. case SIDEWINDER3P_DIGITAL_MODE:
  1723. case SIDEWINDER3P_ENHANCED_DIGITAL_MODE:
  1724. {
  1725. pConfig->hws.dwFlags = JOY_HWS_HASPOV |
  1726. JOY_HWS_POVISBUTTONCOMBOS |
  1727. JOY_HWS_HASU |
  1728. JOY_HWS_HASR;
  1729. pConfig->dwUsageSettings = JOY_US_HASRUDDER |
  1730. JOY_US_PRESENT |
  1731. JOY_US_ISOEM;
  1732. pConfig->hwv.jrvHardware.jpMin.dwX = 0;
  1733. pConfig->hwv.jrvHardware.jpMin.dwY = 0;
  1734. pConfig->hwv.jrvHardware.jpMin.dwZ = 0;
  1735. pConfig->hwv.jrvHardware.jpMin.dwR = 0;
  1736. pConfig->hwv.jrvHardware.jpMin.dwU = 0;
  1737. pConfig->hwv.jrvHardware.jpMin.dwV = 0;
  1738. pConfig->hwv.jrvHardware.jpMax.dwX = 1024;
  1739. pConfig->hwv.jrvHardware.jpMax.dwY = 1024;
  1740. pConfig->hwv.jrvHardware.jpMax.dwZ = 0;
  1741. pConfig->hwv.jrvHardware.jpMax.dwR = 512;
  1742. pConfig->hwv.jrvHardware.jpMax.dwU = 1024;
  1743. pConfig->hwv.jrvHardware.jpMax.dwV = 0;
  1744. pConfig->hwv.jrvHardware.jpCenter.dwX = 512;
  1745. pConfig->hwv.jrvHardware.jpCenter.dwY = 512;
  1746. pConfig->hwv.jrvHardware.jpCenter.dwZ = 0;
  1747. pConfig->hwv.jrvHardware.jpCenter.dwR = 256;
  1748. pConfig->hwv.jrvHardware.jpCenter.dwU = 512;
  1749. pConfig->hwv.jrvHardware.jpCenter.dwV = 0;
  1750. break;
  1751. }
  1752. }
  1753. pConfig->hwv.dwCalFlags = JOY_ISCAL_POV;
  1754. pConfig->dwType = JOY_HW_3A_4B_GENERIC;
  1755. pConfig->dwReserved = 0;
  1756. }