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.

2417 lines
60 KiB

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