Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1071 lines
28 KiB

  1. //**************************************************************************
  2. //
  3. // DEVICE.C -- Xena Gaming Project
  4. //
  5. // Version 3.XX
  6. //
  7. // Copyright (c) 1997 Microsoft Corporation. All rights reserved.
  8. //
  9. // @doc
  10. // @module DEVICE.C | Routines to support device class calls
  11. //**************************************************************************
  12. #include "msgame.h"
  13. //---------------------------------------------------------------------------
  14. // Alloc_text pragma to specify routines that can be paged out.
  15. //---------------------------------------------------------------------------
  16. #ifdef ALLOC_PRAGMA
  17. #pragma alloc_text (INIT, DEVICE_DriverEntry)
  18. #endif
  19. //---------------------------------------------------------------------------
  20. // Device Delcarations
  21. //---------------------------------------------------------------------------
  22. #ifndef SAITEK
  23. DECLARE_DEVICE(Midas);
  24. DECLARE_DEVICE(Juno);
  25. DECLARE_DEVICE(Jolt);
  26. DECLARE_DEVICE(GamePad);
  27. DECLARE_DEVICE(Tilt);
  28. #endif
  29. DECLARE_DEVICE(LedZep);
  30. static PDEVICEINFO MiniDrivers[] = {
  31. #ifndef SAITEK
  32. INSTANCE_DEVICE(Midas), //default
  33. INSTANCE_DEVICE(Juno),
  34. INSTANCE_DEVICE(Jolt),
  35. INSTANCE_DEVICE(GamePad),
  36. INSTANCE_DEVICE(Tilt),
  37. #endif
  38. INSTANCE_DEVICE(LedZep)
  39. };
  40. //---------------------------------------------------------------------------
  41. // Private Data
  42. //---------------------------------------------------------------------------
  43. static BOOLEAN DeviceDetected = FALSE;
  44. static ULONG DetectAttempts = 0;
  45. static ULONG LastDetectTime = 0;
  46. static KIRQL SpinLockIrql = PASSIVE_LEVEL;
  47. static KSPIN_LOCK DevSpinLock = {0};
  48. static ULONG SuccessPackets[MAX_DEVICE_UNITS] = {0,0,0,0};
  49. static ULONG PollingAttempts[MAX_DEVICE_UNITS] = {0,0,0,0};
  50. //---------------------------------------------------------------------------
  51. // Public Data
  52. //---------------------------------------------------------------------------
  53. public ULONG POV_Values[] = {
  54. JOY_POVCENTERED,
  55. JOY_POVFORWARD,
  56. JOY_POVFORWARD+4500,
  57. JOY_POVRIGHT,
  58. JOY_POVRIGHT+4500,
  59. JOY_POVBACKWARD,
  60. JOY_POVBACKWARD+4500,
  61. JOY_POVLEFT,
  62. JOY_POVLEFT+4500
  63. };
  64. public ULONG PollingInterval = POLLING_INTERVAL;
  65. //---------------------------------------------------------------------------
  66. // Private Procedures
  67. //---------------------------------------------------------------------------
  68. static VOID DEVICE_AcquireDevice (VOID);
  69. static VOID DEVICE_ReleaseDevice (VOID);
  70. static NTSTATUS DEVICE_HotPlugDevice (PGAMEPORT PortInfo);
  71. static NTSTATUS DEVICE_RemoveSiblings (PGAMEPORT PortInfo);
  72. static BOOLEAN DEVICE_DetectClocks (PGAMEPORT PortInfo, ULONG TimeOut);
  73. static BOOLEAN DEVICE_QuickDetect (PGAMEPORT PortInfo);
  74. static NTSTATUS DEVICE_DetectDevices (PGAMEPORT PortInfo);
  75. //---------------------------------------------------------------------------
  76. // @func Acquires exclusive access to gameport (mutex)
  77. // @rdesc Returns nothing
  78. // @comm Public function
  79. //---------------------------------------------------------------------------
  80. VOID DEVICE_AcquireDevice (VOID)
  81. {
  82. KeAcquireSpinLock (&DevSpinLock, &SpinLockIrql);
  83. }
  84. //---------------------------------------------------------------------------
  85. // @func Releases exclusive access to gameport (mutex)
  86. // @rdesc Returns nothing
  87. // @comm Public function
  88. //---------------------------------------------------------------------------
  89. VOID DEVICE_ReleaseDevice (VOID)
  90. {
  91. KeReleaseSpinLock (&DevSpinLock, SpinLockIrql);
  92. }
  93. //---------------------------------------------------------------------------
  94. // @func Detects hot-plugging of gamepad devices
  95. // @parm PGAMEPORT | PortInfo | Gameport parameters
  96. // @rdesc Returns one of the following values:
  97. // @flag STATUS_DEVICE_NOT_CONNECTED | An error occurred
  98. // @flag STATUS_SIBLING_REMOVED | An device has been removed
  99. // @flag STATUS_SIBLING_ADDED | An device has been added
  100. // @flag STATUS_SUCCESS | Everything is fine
  101. // @comm Private function
  102. //---------------------------------------------------------------------------
  103. NTSTATUS DEVICE_HotPlugDevice (PGAMEPORT PortInfo)
  104. {
  105. ULONG UnitId;
  106. PDEVICEINFO DevInfo;
  107. MsGamePrint ((DBG_VERBOSE, "%s: DEVICE_HotPlugDevice Enter\n"));
  108. //
  109. // Get pointer to this device
  110. //
  111. DevInfo = GET_DEVICE_INFO(PortInfo);
  112. //
  113. // Skip if no device detected
  114. //
  115. if (!DevInfo || !DevInfo->NumDevices)
  116. {
  117. MsGamePrint ((DBG_SEVERE, "%s: DEVICE_HotPlugDevice Called With No Device!\n", MSGAME_NAME));
  118. return (STATUS_DEVICE_NOT_CONNECTED);
  119. }
  120. //
  121. // Get UnitId for tracking by device
  122. //
  123. UnitId = GET_DEVICE_UNIT(PortInfo);
  124. //
  125. // Check if number devices has changed
  126. //
  127. if (((DevInfo->DeviceCount+DevInfo->DevicePending) != DevInfo->NumDevices) && (SuccessPackets[UnitId]++ > HOT_PLUG_PACKETS))
  128. {
  129. SuccessPackets[UnitId] = 0;
  130. if ((DevInfo->DeviceCount+DevInfo->DevicePending) > DevInfo->NumDevices)
  131. {
  132. MsGamePrint ((DBG_CONTROL, "%s: DEVICE_HotPlugDevice Removing Sibling\n", MSGAME_NAME));
  133. //
  134. // Decrement pending count to avoid removing twice
  135. //
  136. InterlockedDecrement (&DevInfo->DevicePending);
  137. return (STATUS_SIBLING_REMOVED);
  138. }
  139. else
  140. {
  141. MsGamePrint ((DBG_CONTROL, "%s: DEVICE_HotPlugDevice Adding Sibling\n", MSGAME_NAME));
  142. //
  143. // Increment pending count to avoid adding twice
  144. //
  145. InterlockedIncrement (&DevInfo->DevicePending);
  146. return (STATUS_SIBLING_ADDED);
  147. }
  148. }
  149. //
  150. // Return status
  151. //
  152. MsGamePrint ((DBG_VERBOSE, "%s: DEVICE_HotPlugDevice Exit\n", MSGAME_NAME));
  153. return (STATUS_SUCCESS);
  154. }
  155. //---------------------------------------------------------------------------
  156. // @func Removes sibling lists if possible
  157. // @parm PGAMEPORT | PortInfo | Gameport parameters
  158. // @rdesc Returns one of the following values:
  159. // @flag STATUS_DEVICE_NOT_CONNECTED | An error occurred
  160. // @flag STATUS_SIBLING_REMOVED | An device has been removed
  161. // @comm Private function
  162. //---------------------------------------------------------------------------
  163. NTSTATUS DEVICE_RemoveSiblings (PGAMEPORT PortInfo)
  164. {
  165. PDEVICEINFO DevInfo;
  166. MsGamePrint ((DBG_VERBOSE, "%s: DEVICE_RemoveSiblings Enter\n"));
  167. //
  168. // Get pointer to this device
  169. //
  170. DevInfo = GET_DEVICE_INFO(PortInfo);
  171. //
  172. // Skip if no device detected
  173. //
  174. if (!DevInfo)
  175. {
  176. MsGamePrint ((DBG_SEVERE, "%s: DEVICE_RemoveSiblings Called With No Device!\n", MSGAME_NAME));
  177. return (STATUS_DEVICE_NOT_CONNECTED);
  178. }
  179. //
  180. // Zero number of devices
  181. //
  182. DevInfo->NumDevices = 1;
  183. //
  184. // Check if more than one device
  185. //
  186. if ((DevInfo->DeviceCount+DevInfo->DevicePending) > 1)
  187. {
  188. MsGamePrint ((DBG_CONTROL, "%s: DEVICE_RemoveSiblings Removing Sibling\n", MSGAME_NAME));
  189. //
  190. // Decrement pending count to avoid removing twice
  191. //
  192. InterlockedDecrement (&DevInfo->DevicePending);
  193. return (STATUS_SIBLING_REMOVED);
  194. }
  195. //
  196. // Return status
  197. //
  198. MsGamePrint ((DBG_VERBOSE, "%s: DEVICE_RemoveSiblings Exit\n", MSGAME_NAME));
  199. return (STATUS_DEVICE_NOT_CONNECTED);
  200. }
  201. //---------------------------------------------------------------------------
  202. // @func Detect digital gameport clocks
  203. // @parm PGAMEPORT | PortInfo | Gameport parameters
  204. // @parm ULONG | TimeOut | Loops to try for clocks
  205. // @rdesc Returns true if clocks detected, false otherwise
  206. // @comm Private function
  207. //---------------------------------------------------------------------------
  208. BOOLEAN DEVICE_DetectClocks (PGAMEPORT PortInfo, ULONG TimeOut)
  209. {
  210. BOOLEAN Result = FALSE;
  211. _asm
  212. {
  213. ;StartLoop:
  214. xor ebx, ebx
  215. mov edx, PortInfo
  216. mov ecx, TimeOut
  217. push 0 ; write byte to gameport
  218. push edx
  219. call PORTIO_Write
  220. push edx ; read byte from gameport
  221. call PORTIO_Read
  222. mov ah, al
  223. ClockLoop:
  224. push edx ; read byte from gameport
  225. call PORTIO_Read
  226. xor al, ah
  227. test al, CLOCK_BIT_MASK
  228. je NextLoop
  229. ;FoundClock:
  230. inc ebx
  231. cmp ebx, QUICK_DETECT_CLOCKS
  232. jb NextLoop
  233. mov Result, TRUE
  234. jmp ExitLoop
  235. NextLoop:
  236. loop ClockLoop
  237. ExitLoop:
  238. nop
  239. }
  240. return (Result);
  241. }
  242. //---------------------------------------------------------------------------
  243. // @func Detects whether digital device is connected
  244. // @parm PGAMEPORT | PortInfo | Gameport parameters
  245. // @rdesc Returns true if clocks detected, false otherwise
  246. // @comm Private function
  247. //---------------------------------------------------------------------------
  248. BOOLEAN DEVICE_QuickDetect (PGAMEPORT PortInfo)
  249. {
  250. ULONG i;
  251. ULONG TimeOut;
  252. DETECT_ORDER DetectOrder;
  253. TimeOut = TIMER_CalibratePort (PortInfo, QUICK_DETECT_TIME);
  254. if (DEVICE_DetectClocks (PortInfo, TimeOut))
  255. {
  256. MsGamePrint ((DBG_CONTROL, "%s: DEVICE_QuickDetect Found Digital Clocks!\n", MSGAME_NAME));
  257. return (TRUE);
  258. }
  259. MsGamePrint ((DBG_CONTROL, "%s: DEVICE_QuickDetect Trying Analog Devices\n", MSGAME_NAME));
  260. for (DetectOrder = DETECT_FIRST; DetectOrder <= DETECT_LAST; DetectOrder++)
  261. for (i = 0; i < ARRAY_SIZE(MiniDrivers); i++)
  262. if (MiniDrivers[i]->DetectOrder == DetectOrder)
  263. if (MiniDrivers[i]->IsAnalog)
  264. if (MiniDrivers[i]->Services->ConnectDevice (PortInfo) == STATUS_SUCCESS)
  265. {
  266. MsGamePrint ((DBG_CONTROL, "%s: DEVICE_QuickDetect Found Analog Device!\n", MSGAME_NAME));
  267. return (TRUE);
  268. }
  269. MsGamePrint ((DBG_SEVERE, "%s: DEVICE_QuickDetect Failed to Find Digital Device!\n", MSGAME_NAME));
  270. return (FALSE);
  271. }
  272. //---------------------------------------------------------------------------
  273. // @func Detects which device type is connected
  274. // @parm PGAMEPORT | PortInfo | Gameport parameters
  275. // @rdesc Returns one of the following values:
  276. // @flag STATUS_DEVICE_NOT_CONNECTED | An error occurred
  277. // @flag STATUS_DEVICE_CHANGED | The device has changed
  278. // @flag STATUS_SUCCESS | Everything is fine
  279. // @comm Private function
  280. //---------------------------------------------------------------------------
  281. NTSTATUS DEVICE_DetectDevices (PGAMEPORT PortInfo)
  282. {
  283. ULONG i, j;
  284. PDEVICEINFO DevInfo;
  285. DETECT_ORDER DetectOrder;
  286. //
  287. // This an initial redetect or system startup device
  288. //
  289. MsGamePrint ((DBG_INFORM, "%s: DEVICE_DetectDevices Enter\n", MSGAME_NAME));
  290. //
  291. // Skip if we've already detected a new device removed this one
  292. //
  293. if (GET_DEVICE_DETECTED (PortInfo))
  294. {
  295. MsGamePrint ((DBG_CONTROL, "%s: DEVICE_DetectDevices Device Already Detected!\n", MSGAME_NAME));
  296. return (STATUS_DELETE_PENDING);
  297. }
  298. //
  299. // Get pointer to this device
  300. //
  301. DevInfo = GET_DEVICE_INFO(PortInfo);
  302. //
  303. // Skip if we're trying too hard
  304. //
  305. if (DetectAttempts++ > MAX_DETECT_ATTEMPTS)
  306. {
  307. if (TIMER_GetTickCount () < (LastDetectTime + MAX_DETECT_INTERVAL))
  308. return (STATUS_DEVICE_NOT_CONNECTED);
  309. LastDetectTime = TIMER_GetTickCount();
  310. }
  311. //
  312. // Calibrate timer each try
  313. //
  314. TIMER_Calibrate ();
  315. //
  316. // Calibrate port timeouts
  317. //
  318. PORTIO_CalibrateTimeOut (PortInfo);
  319. //
  320. // Perform quick detection in case none attached
  321. //
  322. if (DEVICE_QuickDetect (PortInfo))
  323. {
  324. for (DetectOrder = DETECT_FIRST; DetectOrder <= DETECT_LAST; DetectOrder++)
  325. for (i = 0; i < ARRAY_SIZE(MiniDrivers); i++)
  326. if (MiniDrivers[i]->DetectOrder == DetectOrder)
  327. if (MiniDrivers[i]->Services->ConnectDevice (PortInfo) == STATUS_SUCCESS)
  328. {
  329. MsGamePrint ((DBG_CONTROL, "%s: %s Connected OK\n", MSGAME_NAME, MiniDrivers[i]->DeviceName));
  330. DeviceDetected = TRUE;
  331. DetectAttempts = 0;
  332. PollingAttempts[0] = 0;
  333. if (!DevInfo)
  334. {
  335. //
  336. // Assign device type
  337. //
  338. SET_DEVICE_INFO (PortInfo, MiniDrivers[i]);
  339. MsGamePrint ((DBG_CONTROL, "%s: DEVICE_DetectDevices Setting Device\n", MSGAME_NAME));
  340. return (STATUS_SUCCESS);
  341. }
  342. else if (DevInfo != MiniDrivers[i])
  343. {
  344. //
  345. // Change device type
  346. //
  347. SET_DEVICE_DETECTED (PortInfo, MiniDrivers[i]);
  348. MsGamePrint ((DBG_CONTROL, "%s: DEVICE_DetectDevices Changing Device\n", MSGAME_NAME));
  349. return (STATUS_DEVICE_CHANGED);
  350. }
  351. else
  352. {
  353. //
  354. // Same device found
  355. //
  356. MsGamePrint ((DBG_CONTROL, "%s: DEVICE_DetectDevices Same Device Found\n", MSGAME_NAME));
  357. return (STATUS_SUCCESS);
  358. }
  359. }
  360. }
  361. //
  362. // Mark device not detected
  363. //
  364. DeviceDetected = FALSE;
  365. //
  366. // Return status
  367. //
  368. MsGamePrint ((DBG_CONTROL, "%s: DEVICE_DetectDevices Failed\n", MSGAME_NAME));
  369. return (STATUS_DEVICE_NOT_CONNECTED);
  370. }
  371. //---------------------------------------------------------------------------
  372. // @func Calcuates and returns is data is odd parity
  373. // @parm PVOID | Data | Pointer to raw data
  374. // @parm ULONG | Size | Size of raw data buffer
  375. // @rdesc True if oded parity, False otherwise
  376. // @comm Public function
  377. //---------------------------------------------------------------------------
  378. BOOLEAN DEVICE_IsOddParity (PVOID Data, ULONG Count)
  379. {
  380. LONG Result = ERROR_SUCCESS;
  381. LONG Parity;
  382. __asm
  383. {
  384. push edi
  385. push esi
  386. mov esi, Data
  387. mov ecx, Count
  388. xor eax, eax
  389. IsOddLoop:
  390. xor al, [esi]
  391. inc esi
  392. loop IsOddLoop
  393. xor al, ah
  394. jpo IsOddComplete
  395. mov Parity, eax
  396. mov Result, ERROR_PARITYBITS
  397. IsOddComplete:
  398. pop esi
  399. pop edi
  400. }
  401. if (Result == ERROR_PARITYBITS)
  402. MsGamePrint ((DBG_SEVERE, "%s: DEVICE_IsOddParity - Parity bits failed %ld\n", MSGAME_NAME, Parity));
  403. return (!Result);
  404. }
  405. //---------------------------------------------------------------------------
  406. // @func Driver entry point for device layer
  407. // @rdesc Returns NT status code
  408. // @comm Public function
  409. //---------------------------------------------------------------------------
  410. NTSTATUS DEVICE_DriverEntry (VOID)
  411. {
  412. ULONG i;
  413. NTSTATUS ntStatus;
  414. KeInitializeSpinLock (&DevSpinLock);
  415. for (i = 0; i < ARRAY_SIZE(MiniDrivers); i++)
  416. {
  417. //
  418. // Call Mini-DriverEntry
  419. //
  420. ntStatus = MiniDrivers[i]->Services->DriverEntry ();
  421. if (NT_ERROR(ntStatus))
  422. {
  423. //
  424. // Abort driver loading
  425. //
  426. MsGamePrint ((DBG_SEVERE, "%s: DEVICE_DriverEntry: %s Failed Driver Entry\n", MSGAME_NAME, MiniDrivers[i]->DeviceName));
  427. break;
  428. }
  429. }
  430. return (ntStatus);
  431. }
  432. //---------------------------------------------------------------------------
  433. // @func Detects gameport IO collisions
  434. // @parm PPACKETINFO | DataPacket | Device packet info struct
  435. // @rdesc Returns True for collision, False otherwise
  436. // @comm Public function
  437. //---------------------------------------------------------------------------
  438. BOOLEAN DEVICE_IsCollision (PPACKETINFO DataPacket)
  439. {
  440. if ((DataPacket->TimeStamp + PollingInterval) > TIMER_GetTickCount ())
  441. return (TRUE);
  442. return (PORTIO_IsClockActive (&DataPacket->PortInfo, DataPacket->ClockDutyCycle));
  443. }
  444. //---------------------------------------------------------------------------
  445. // @func Copies and returns HID device descriptor for a device
  446. // @parm PGAMEPORT | PortInfo | Gameport parameters
  447. // @parm PUCHAR | Descriptor | Output buffer for descriptor
  448. // @parm ULONG | MaxSize | Size of buffer for descriptor
  449. // @parm PULONG | Copied | Bytes copied to buffer for descriptor
  450. // @rdesc Returns NT status code
  451. // @comm Public function
  452. //---------------------------------------------------------------------------
  453. NTSTATUS DEVICE_GetDeviceDescriptor (PGAMEPORT PortInfo, PUCHAR Descriptor, ULONG MaxSize, PULONG Copied)
  454. {
  455. PDEVICEINFO DevInfo;
  456. MsGamePrint ((DBG_INFORM, "%s: DEVICE_GetDeviceDescriptor Enter\n", MSGAME_NAME));
  457. //
  458. // Get pointer to this device
  459. //
  460. DevInfo = GET_DEVICE_INFO(PortInfo);
  461. //
  462. // Zero returned size first
  463. //
  464. *Copied = 0;
  465. //
  466. // Skip if no device detected
  467. //
  468. if (!DevInfo)
  469. {
  470. MsGamePrint ((DBG_SEVERE, "%s: DEVICE_GetDeviceDescriptor Called With No Device!\n", MSGAME_NAME));
  471. return (STATUS_DEVICE_NOT_CONNECTED);
  472. }
  473. //
  474. // Check output buffer size
  475. //
  476. if (MaxSize < sizeof (HID_DESCRIPTOR))
  477. {
  478. MsGamePrint ((DBG_SEVERE, "%s: DEVICE_GetDeviceDescriptor - Buffer too small = %lu\n", MSGAME_NAME, MaxSize));
  479. return (STATUS_BUFFER_TOO_SMALL);
  480. }
  481. //
  482. // Copy descriptor to output buffer
  483. //
  484. memcpy (Descriptor, DevInfo->DevDescriptor, sizeof (HID_DESCRIPTOR));
  485. //
  486. // Return number bytes copied
  487. //
  488. *Copied = sizeof (HID_DESCRIPTOR);
  489. //
  490. // Return status
  491. //
  492. return (STATUS_SUCCESS);
  493. }
  494. //---------------------------------------------------------------------------
  495. // @func Copies and returns HID report descriptor for a device
  496. // @parm PGAMEPORT | PortInfo | Gameport parameters
  497. // @parm PUCHAR | Descriptor | Output buffer for descriptor
  498. // @parm ULONG | MaxSize | Size of buffer for descriptor
  499. // @parm PULONG | Copied | Bytes copied to buffer for descriptor
  500. // @rdesc Returns NT status code
  501. // @comm Public function
  502. //---------------------------------------------------------------------------
  503. NTSTATUS DEVICE_GetReportDescriptor (PGAMEPORT PortInfo, PUCHAR Descriptor, ULONG MaxSize, PULONG Copied)
  504. {
  505. PDEVICEINFO DevInfo;
  506. MsGamePrint ((DBG_INFORM, "%s: DEVICE_GetReportDescriptor Enter\n", MSGAME_NAME));
  507. //
  508. // Get pointer to this device
  509. //
  510. DevInfo = GET_DEVICE_INFO(PortInfo);
  511. //
  512. // Zero returned size first
  513. //
  514. *Copied = 0;
  515. //
  516. // Skip if no device detected
  517. //
  518. if (!DevInfo)
  519. {
  520. MsGamePrint ((DBG_SEVERE, "%s: DEVICE_GetReportDescriptor Called With No Device!\n", MSGAME_NAME));
  521. return (STATUS_DEVICE_NOT_CONNECTED);
  522. }
  523. //
  524. // Check output buffer size
  525. //
  526. if (MaxSize < DevInfo->RptDescSize)
  527. {
  528. MsGamePrint ((DBG_SEVERE, "%s: DEVICE_GetReportDescriptor Buffer too small = %lu\n", MSGAME_NAME, MaxSize));
  529. return (STATUS_BUFFER_TOO_SMALL);
  530. }
  531. //
  532. // Copy descriptor to output buffer
  533. //
  534. memcpy (Descriptor, DevInfo->RptDescriptor, DevInfo->RptDescSize);
  535. //
  536. // Return number bytes copied
  537. //
  538. *Copied = DevInfo->RptDescSize;
  539. //
  540. // Return status
  541. //
  542. return (STATUS_SUCCESS);
  543. }
  544. //---------------------------------------------------------------------------
  545. // @func Device handler for Pnp Start Device IRP
  546. // @parm PGAMEPORT | PortInfo | Gameport parameters
  547. // @rdesc Returns NT status code
  548. // @comm Public function
  549. //---------------------------------------------------------------------------
  550. NTSTATUS DEVICE_StartDevice (PGAMEPORT PortInfo, PWCHAR HardwareId)
  551. {
  552. ULONG i, UnitId, Default = 0;
  553. PGAMEPORT p, *Device;
  554. PDEVICEINFO DevInfo = NULL;
  555. MsGamePrint ((DBG_CONTROL, "%s: DEVICE_StartDevice Called For %ws\n", MSGAME_NAME, HardwareId));
  556. //
  557. // Try requested device based on HardwareId
  558. //
  559. for (i = 0; i < ARRAY_SIZE(MiniDrivers); i++)
  560. if (MSGAME_CompareHardwareIds (HardwareId, MiniDrivers[i]->HardwareId))
  561. {
  562. Default = i;
  563. if (NT_SUCCESS(MiniDrivers[i]->Services->ConnectDevice (PortInfo)))
  564. DevInfo = MiniDrivers[i];
  565. break;
  566. }
  567. //
  568. // If requested device fails, do a detect
  569. //
  570. if (!DevInfo)
  571. {
  572. DEVICE_DetectDevices (PortInfo);
  573. DevInfo = GET_DEVICE_INFO (PortInfo);
  574. }
  575. //
  576. // If detect fails, force the requested device
  577. //
  578. if (!DevInfo)
  579. {
  580. DevInfo = MiniDrivers[Default];
  581. DevInfo->NumDevices++;
  582. }
  583. //
  584. // Make sure these are set at this point
  585. //
  586. ASSERT(DevInfo);
  587. SET_DEVICE_INFO(PortInfo, DevInfo);
  588. //
  589. // Add device and allocate unit id
  590. //
  591. DEVICE_AcquireDevice ();
  592. UnitId = 0;
  593. Device = &DevInfo->Siblings;
  594. while (p = *Device)
  595. {
  596. MsGamePrint ((DBG_CONTROL, "%s: DEVICE_StartDevice Reassigning UnitId From %lu to %lu\n", MSGAME_NAME, GET_DEVICE_UNIT(p), UnitId));
  597. SET_DEVICE_UNIT(p, UnitId++);
  598. Device = &GET_DEVICE_SIBLING(p);
  599. }
  600. *Device = PortInfo;
  601. SET_DEVICE_UNIT(PortInfo, UnitId);
  602. SET_DEVICE_SIBLING(PortInfo, NULL);
  603. SET_DEVICE_ID(PortInfo, DevInfo->DeviceId);
  604. DEVICE_ReleaseDevice ();
  605. MsGamePrint ((DBG_CONTROL, "%s: DEVICE_StartDevice Assigned UnitId = %lu\n", MSGAME_NAME, UnitId));
  606. //
  607. // Increment device count
  608. //
  609. InterlockedIncrement (&DevInfo->DeviceCount);
  610. if (DevInfo->DevicePending)
  611. InterlockedDecrement (&DevInfo->DevicePending);
  612. //
  613. // Call the mini-driver to process
  614. //
  615. DevInfo->Services->StartDevice (PortInfo);
  616. //
  617. // Return success always
  618. //
  619. return (STATUS_SUCCESS);
  620. }
  621. //---------------------------------------------------------------------------
  622. // @func Device handler for HID Read Report IRP
  623. // @parm PGAMEPORT | PortInfo | Gameport parameters
  624. // @parm PUCHAR | Report | Output buffer for report
  625. // @parm ULONG | MaxSize | Size of buffer for report
  626. // @parm PULONG | Copied | Bytes copied to buffer for report
  627. // @rdesc Returns Returns NT status code
  628. // @comm Public function <en->
  629. // Performs hot-plugging on success or does device detection if no
  630. // device selected or error.
  631. //---------------------------------------------------------------------------
  632. NTSTATUS DEVICE_ReadReport (PGAMEPORT PortInfo, PUCHAR Report, ULONG MaxSize, PULONG Copied)
  633. {
  634. NTSTATUS ntStatus;
  635. PDEVICEINFO DevInfo;
  636. DEVICE_PACKET Packet;
  637. MsGamePrint ((DBG_VERBOSE, "%s: DEVICE_ReadReport Enter\n", MSGAME_NAME));
  638. //
  639. // Initialize packet members
  640. //
  641. memset (&Packet, 0, sizeof (Packet));
  642. Packet.id = GET_DEVICE_UNIT (PortInfo);
  643. //
  644. // Check output buffer
  645. //
  646. if (MaxSize < sizeof (DEVICE_PACKET))
  647. {
  648. MsGamePrint ((DBG_SEVERE, "%s: DEVICE_ReadReport Bad Buffer Size = %lu\n", MSGAME_NAME, MaxSize));
  649. return (STATUS_BUFFER_TOO_SMALL);
  650. }
  651. //
  652. // Skip if device changed
  653. //
  654. if (GET_DEVICE_DETECTED (PortInfo))
  655. {
  656. MsGamePrint ((DBG_INFORM, "%s: DEVICE_ReadReport Device In Process of Being Changed!\n", MSGAME_NAME));
  657. ntStatus = STATUS_DEVICE_BUSY;
  658. goto DEVICE_ReadReport_Exit;
  659. }
  660. //
  661. // Get pointer to this device
  662. //
  663. DevInfo = GET_DEVICE_INFO(PortInfo);
  664. //
  665. // Skip if no device detected
  666. //
  667. if (!DevInfo || !DeviceDetected)
  668. {
  669. MsGamePrint ((DBG_SEVERE, "%s: DEVICE_ReadReport Called With No Device!\n", MSGAME_NAME));
  670. SuccessPackets[0] = 0;
  671. ntStatus = DEVICE_DetectDevices (PortInfo);
  672. if (!NT_SUCCESS (ntStatus))
  673. goto DEVICE_ReadReport_Exit;
  674. //
  675. // Get pointer to new device
  676. //
  677. DevInfo = GET_DEVICE_INFO(PortInfo);
  678. goto DEVICE_ReadReport_Exit;
  679. }
  680. //
  681. // Call the mini-driver to process
  682. //
  683. ntStatus = DevInfo->Services->ReadReport (PortInfo, &Packet);
  684. //
  685. // Process returned status
  686. //
  687. if (NT_SUCCESS (ntStatus))
  688. {
  689. //
  690. // Check for hot-plugging
  691. //
  692. ntStatus = DEVICE_HotPlugDevice (PortInfo);
  693. PollingAttempts[Packet.id] = 0;
  694. goto DEVICE_ReadReport_Exit;
  695. }
  696. else if (ntStatus == STATUS_DEVICE_BUSY)
  697. {
  698. //
  699. // Access to port denied
  700. //
  701. MsGamePrint ((DBG_CONTROL, "%s: DEVICE_ReadReport Device Busy\n", MSGAME_NAME));
  702. goto DEVICE_ReadReport_Exit;
  703. }
  704. else
  705. {
  706. //
  707. // Force success if just transitory
  708. //
  709. if ((++PollingAttempts[Packet.id] <= MAX_POLLING_ATTEMPTS) && DeviceDetected)
  710. {
  711. MsGamePrint ((DBG_CRITICAL, "%s: DEVICE_ReadReport Force Success\n", MSGAME_NAME));
  712. ntStatus = STATUS_SUCCESS;
  713. }
  714. else if ((PollingAttempts[Packet.id] % MAX_POLLING_ATTEMPTS) == 0)
  715. {
  716. MsGamePrint ((DBG_CRITICAL, "%s: DEVICE_ReadReport Failed %lu In a Row\n", MSGAME_NAME, PollingAttempts[Packet.id]));
  717. //
  718. // Try and see what's out there
  719. //
  720. ntStatus = DEVICE_DetectDevices (PortInfo);
  721. //
  722. // If nothing found, destroy any siblings
  723. //
  724. if (NT_ERROR(ntStatus))
  725. ntStatus = DEVICE_RemoveSiblings (PortInfo);
  726. }
  727. else
  728. {
  729. //
  730. // Just bounce this request
  731. //
  732. ntStatus = STATUS_DEVICE_NOT_CONNECTED;
  733. }
  734. //
  735. // Clear sucessful packet counts
  736. //
  737. SuccessPackets[Packet.id] = 0;
  738. }
  739. //---------------------
  740. DEVICE_ReadReport_Exit:
  741. //---------------------
  742. if( ntStatus == STATUS_DEVICE_BUSY)
  743. {
  744. ntStatus = STATUS_SUCCESS;
  745. }
  746. //
  747. // Return packet data always
  748. //
  749. memcpy (Report, &Packet, sizeof (Packet));
  750. if (NT_SUCCESS(ntStatus))
  751. {
  752. *Copied += sizeof (Packet);
  753. }
  754. else
  755. *Copied = 0x0;
  756. //
  757. // Return status code
  758. //
  759. return (ntStatus);
  760. }
  761. //---------------------------------------------------------------------------
  762. // @func Device handler for Pnp Stop Device IRP
  763. // @parm PGAMEPORT | PortInfo | Gameport parameters
  764. // @rdesc Returns NT status code
  765. // @comm Public function
  766. //---------------------------------------------------------------------------
  767. NTSTATUS DEVICE_StopDevice (PGAMEPORT PortInfo, BOOLEAN TouchHardware)
  768. {
  769. ULONG UnitId;
  770. PGAMEPORT p, *Device;
  771. PDEVICEINFO DevInfo;
  772. MsGamePrint ((DBG_INFORM, "%s: DEVICE_StopDevice Enter\n", MSGAME_NAME));
  773. //
  774. // Get pointer to this device
  775. //
  776. DevInfo = GET_DEVICE_INFO(PortInfo);
  777. //
  778. // Skip if no device detected
  779. //
  780. if (!DevInfo)
  781. {
  782. MsGamePrint ((DBG_SEVERE, "%s: DEVICE_StopDevice Called With No Device!\n", MSGAME_NAME));
  783. return (STATUS_DEVICE_NOT_CONNECTED);
  784. }
  785. MsGamePrint ((DBG_CONTROL, "%s: DEVICE_StopDevice Received for %s[%lu]\n", MSGAME_NAME, DevInfo->DeviceName, GET_DEVICE_UNIT(PortInfo)));
  786. //
  787. // Remove sibling and reallocate unit ids
  788. //
  789. DEVICE_AcquireDevice ();
  790. UnitId = 0;
  791. Device = &DevInfo->Siblings;
  792. while (p = *Device)
  793. {
  794. if (p == PortInfo)
  795. {
  796. MsGamePrint ((DBG_CONTROL, "%s: DEVICE_StopDevice Unlinking UnitId = %lu\n", MSGAME_NAME, GET_DEVICE_UNIT(p)));
  797. *Device = GET_DEVICE_SIBLING(p);
  798. }
  799. else
  800. {
  801. MsGamePrint ((DBG_CONTROL, "%s: DEVICE_StopDevice Reassigning UnitId From %lu to %lu\n", MSGAME_NAME, GET_DEVICE_UNIT(p), UnitId));
  802. SET_DEVICE_UNIT(p, UnitId++);
  803. }
  804. Device = &GET_DEVICE_SIBLING(p);
  805. }
  806. DEVICE_ReleaseDevice ();
  807. MsGamePrint ((DBG_CONTROL, "%s: DEVICE_StopDevice Released UnitId = %lu\n", MSGAME_NAME, GET_DEVICE_UNIT (PortInfo)));
  808. //
  809. // Decrement device count
  810. //
  811. InterlockedDecrement (&DevInfo->DeviceCount);
  812. if (DevInfo->DevicePending)
  813. InterlockedIncrement (&DevInfo->DevicePending);
  814. //
  815. // Call the mini-driver to process
  816. //
  817. return (DevInfo->Services->StopDevice (PortInfo, TouchHardware));
  818. }
  819. //---------------------------------------------------------------------------
  820. // @func Device handler for HID Get Feature IRP
  821. // @parm PGAMEPORT | PortInfo | Gameport parameters
  822. // @parm HID_REPORT_ID | ReportId | HID feature report id
  823. // @parm PUCHAR | ReportBuffer | Output buffer for report
  824. // @parm ULONG | ReportSize | Size of buffer for report
  825. // @parm PULONG | Returned | Bytes copied to buffer for report
  826. // @rdesc Returns Returns NT status code
  827. // @comm Public function
  828. //---------------------------------------------------------------------------
  829. NTSTATUS DEVICE_GetFeature (PGAMEPORT PortInfo, HID_REPORT_ID ReportId, PVOID ReportBuffer, ULONG ReportSize, PULONG Returned)
  830. {
  831. PDEVICEINFO DevInfo;
  832. MsGamePrint ((DBG_INFORM, "%s: DEVICE_GetFeature Enter\n", MSGAME_NAME));
  833. //
  834. // Get pointer to this device
  835. //
  836. DevInfo = GET_DEVICE_INFO(PortInfo);
  837. //
  838. // Skip if no device detected
  839. //
  840. if (!DevInfo)
  841. {
  842. MsGamePrint ((DBG_SEVERE, "%s: DEVICE_GetFeature Called With No Device!\n", MSGAME_NAME));
  843. return (STATUS_DEVICE_NOT_CONNECTED);
  844. }
  845. //
  846. // Skip if features not supported
  847. //
  848. if (!DevInfo->Services->GetFeature)
  849. {
  850. MsGamePrint ((DBG_SEVERE, "%s: DEVICE_GetFeature Called With No Mini-Driver Support!\n", MSGAME_NAME));
  851. return (STATUS_INVALID_DEVICE_REQUEST);
  852. }
  853. //
  854. // Call the mini-driver to process
  855. //
  856. MsGamePrint ((DBG_INFORM, "%s: DEVICE_GetFeature For ReportId = %lu\n", MSGAME_NAME, (ULONG)ReportId));
  857. return (DevInfo->Services->GetFeature (PortInfo, ReportId, ReportBuffer, ReportSize, Returned));
  858. }