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.

702 lines
24 KiB

  1. /*++
  2. Copyright (c) 1997 Microsoft Corporation
  3. Module Name:
  4. read.c
  5. Abstract:
  6. This module contains the code for translating HID input reports to mouse
  7. reports, and read initiation and completion code for requests sent to the
  8. HID class driver. This module is part of the HID Mouse Filter Driver.
  9. Environment:
  10. Kernel mode only.
  11. Revision History:
  12. Jan-1997 : Initial writing, Dan Markarian
  13. --*/
  14. #include "mouhid.h"
  15. //
  16. // Private definitions.
  17. //
  18. #define MAX_MOUSE_BUTTONS 5
  19. USHORT HidP_TranslateUsageToUpFlag[MAX_MOUSE_BUTTONS+1] = { 0,
  20. MOUSE_BUTTON_1_UP,
  21. MOUSE_BUTTON_2_UP,
  22. MOUSE_BUTTON_3_UP,
  23. MOUSE_BUTTON_4_UP,
  24. MOUSE_BUTTON_5_UP };
  25. USHORT HidP_TranslateUsageToDownFlag[MAX_MOUSE_BUTTONS+1] = { 0,
  26. MOUSE_BUTTON_1_DOWN,
  27. MOUSE_BUTTON_2_DOWN,
  28. MOUSE_BUTTON_3_DOWN,
  29. MOUSE_BUTTON_4_DOWN,
  30. MOUSE_BUTTON_5_DOWN };
  31. NTSTATUS
  32. MouHid_ReadComplete (
  33. IN PDEVICE_OBJECT DeviceObject,
  34. IN PIRP Irp,
  35. IN PDEVICE_EXTENSION Data // (PVOID Context)
  36. )
  37. /*++
  38. Routine Description:
  39. This routine is the read IRP completion routine. It is called when the
  40. HIDCLASS driver satisfies (or rejects) the IRP request we sent it. The
  41. read report is analysed, and a MOUSE_INPUT_DATA structure is built
  42. and sent to the mouse class driver via a callback routine.
  43. Arguments:
  44. DeviceObject - Pointer to the device object.
  45. Irp - Pointer to the request packet.
  46. Context - Pointer to the device context structure describing the HID device.
  47. Return Value:
  48. NTSTATUS result code.
  49. --*/
  50. {
  51. LONG axisMotion;
  52. ULONG i;
  53. ULONG inputDataConsumed;
  54. PHID_EXTENSION hid;
  55. ULONG numUsages;
  56. KIRQL oldIrql;
  57. BOOLEAN returnToIdleState = FALSE;
  58. NTSTATUS status;
  59. PUSAGE usageList;
  60. BOOLEAN updateProblemFlags = FALSE;
  61. ULONGLONG scratch;
  62. BOOLEAN startRead;
  63. Print (DBG_READ_TRACE, ("ReadComplete: Enter."));
  64. //
  65. // Obtain the current status of the IRP.
  66. //
  67. status = Irp->IoStatus.Status;
  68. //
  69. // Get a pointer to the device extension.
  70. //
  71. hid = Data->HidExtension;
  72. //
  73. // If ReadInterlock is == START_READ, this func has been completed
  74. // synchronously. Place IMMEDIATE_READ into the interlock to signify this
  75. // situation; this will notify StartRead to loop when IoCallDriver returns.
  76. // Otherwise, we have been completed async and it is safe to call StartRead()
  77. //
  78. startRead =
  79. (MOUHID_START_READ !=
  80. InterlockedCompareExchange(&Data->ReadInterlock,
  81. MOUHID_IMMEDIATE_READ,
  82. MOUHID_START_READ));
  83. if (Data->EnableCount == 0) {
  84. goto SetEventAndBreak;
  85. }
  86. //
  87. // Determine if the IRP request was successful.
  88. //
  89. switch (status) {
  90. case STATUS_SUCCESS:
  91. //
  92. // The buffer of the context now contains a single HID packet read
  93. // from the device. Verify this.
  94. //
  95. ASSERT (Irp->IoStatus.Information == hid->Caps.InputReportByteLength);
  96. //
  97. // Clear previous button state (data / flags).
  98. //
  99. Data->InputData.ButtonData = 0;
  100. Data->InputData.ButtonFlags = 0;
  101. //
  102. // Clear last X,Y motion, in case a call to Hidp_GetUsageValue or
  103. // Hidp_GetScaledUsageValue fails.
  104. //
  105. Data->InputData.LastX = 0;
  106. Data->InputData.LastY = 0;
  107. //
  108. // Obtain the current button usages.
  109. //
  110. numUsages = hid->MaxUsages;
  111. if (NT_SUCCESS(HidP_GetUsages (
  112. HidP_Input,
  113. HID_USAGE_PAGE_BUTTON,
  114. 0, // link collection irrelevant
  115. hid->CurrentUsageList,
  116. &numUsages, // max usages in, num usages out
  117. hid->Ppd,
  118. hid->InputBuffer,
  119. hid->Caps.InputReportByteLength))) {
  120. //
  121. // Determine the differences between the current and the previous
  122. // usages. The very first previous usage list buffer is properly
  123. // initialized at creation (all zeros).
  124. //
  125. if (NT_SUCCESS(HidP_UsageListDifference (hid->PreviousUsageList,
  126. hid->CurrentUsageList,
  127. hid->BreakUsageList,
  128. hid->MakeUsageList,
  129. hid->MaxUsages))) {
  130. //
  131. // Determine which buttons went down and set the appropriate
  132. // flags in the mouse report.
  133. //
  134. usageList = hid->MakeUsageList;
  135. for ( i = 0;
  136. i < hid->MaxUsages && *usageList;
  137. i++, usageList++ ) {
  138. if (*usageList <= MAX_MOUSE_BUTTONS) {
  139. Data->InputData.ButtonFlags |=
  140. HidP_TranslateUsageToDownFlag[*usageList];
  141. }
  142. //
  143. // else there are more buttons on this mouse then we have
  144. // translation flags for the Raw input user thread
  145. //
  146. }
  147. //
  148. // Determine which buttons went up and set the appropriate
  149. // flags in the mouse report.
  150. //
  151. usageList = hid->BreakUsageList;
  152. for ( i = 0;
  153. i < hid->MaxUsages && *usageList;
  154. i++, usageList++ ) {
  155. if (*usageList <= MAX_MOUSE_BUTTONS) {
  156. Data->InputData.ButtonFlags |=
  157. HidP_TranslateUsageToUpFlag[*usageList];
  158. }
  159. }
  160. //
  161. // Swap the previous usage list pointer with the current.
  162. //
  163. usageList = hid->PreviousUsageList;
  164. hid->PreviousUsageList = hid->CurrentUsageList;
  165. hid->CurrentUsageList = usageList;
  166. }
  167. }
  168. //
  169. // Type of processing for X,Y,Z values depends on whether these values
  170. // have a bad physical minimum or maximum. If they do, we use routines
  171. // that do not depend on physical min/max.
  172. //
  173. //
  174. // Determine the current X position and save it in the mouse report.
  175. //
  176. if (!(Data->ProblemFlags & PROBLEM_BAD_PHYSICAL_MIN_MAX_X)) {
  177. status = HidP_GetScaledUsageValue(
  178. HidP_Input,
  179. HID_USAGE_PAGE_GENERIC,
  180. 0,
  181. HID_USAGE_GENERIC_X,
  182. &Data->InputData.LastX,
  183. hid->Ppd,
  184. hid->InputBuffer,
  185. hid->Caps.InputReportByteLength);
  186. //
  187. // Bad physical minimum/maximum detected, set flag so that we
  188. // process usage value differently in the future.
  189. //
  190. if (status == HIDP_STATUS_BAD_LOG_PHY_VALUES) {
  191. Data->ProblemFlags |= PROBLEM_BAD_PHYSICAL_MIN_MAX_X;
  192. updateProblemFlags = TRUE;
  193. //
  194. // Correct the MaxX value;
  195. //
  196. hid->MaxX = (1 << (hid->BitSize.X - 1)) - 1;
  197. }
  198. }
  199. if (Data->ProblemFlags & PROBLEM_BAD_PHYSICAL_MIN_MAX_X) {
  200. axisMotion = 0;
  201. HidP_GetUsageValue(HidP_Input,
  202. HID_USAGE_PAGE_GENERIC,
  203. 0,
  204. HID_USAGE_GENERIC_X,
  205. (PULONG) &axisMotion,
  206. hid->Ppd,
  207. hid->InputBuffer,
  208. hid->Caps.InputReportByteLength);
  209. // Sign extend the value manually.
  210. Data->InputData.LastX
  211. = axisMotion | ((axisMotion & (hid->MaxX + 1)) ? (~hid->MaxX)
  212. : 0);
  213. }
  214. if (hid->IsAbsolute) {
  215. //
  216. // We need to scale this value from the physical max
  217. //
  218. scratch = ((LONGLONG)(Data->InputData.LastX) *
  219. MOUHID_RIUT_ABSOLUTE_POINTER_MAX) /
  220. hid->MaxX;
  221. Data->InputData.LastX = (LONG) scratch;
  222. }
  223. //
  224. // Determine the current Y position and save it in the mouse report.
  225. //
  226. if (!(Data->ProblemFlags & PROBLEM_BAD_PHYSICAL_MIN_MAX_Y)) {
  227. status = HidP_GetScaledUsageValue(
  228. HidP_Input,
  229. HID_USAGE_PAGE_GENERIC,
  230. 0,
  231. HID_USAGE_GENERIC_Y,
  232. &Data->InputData.LastY,
  233. hid->Ppd,
  234. hid->InputBuffer,
  235. hid->Caps.InputReportByteLength);
  236. //
  237. // Bad physical minimum/maximum detected, set flag so that we
  238. // process usage value differently in the future.
  239. //
  240. if (status == HIDP_STATUS_BAD_LOG_PHY_VALUES) {
  241. Data->ProblemFlags |= PROBLEM_BAD_PHYSICAL_MIN_MAX_Y;
  242. updateProblemFlags = TRUE;
  243. //
  244. // Correct the MaxY value;
  245. //
  246. hid->MaxY = (1 << (hid->BitSize.Y - 1)) - 1;
  247. }
  248. }
  249. if (Data->ProblemFlags & PROBLEM_BAD_PHYSICAL_MIN_MAX_Y) {
  250. axisMotion = 0;
  251. HidP_GetUsageValue(HidP_Input,
  252. HID_USAGE_PAGE_GENERIC,
  253. 0,
  254. HID_USAGE_GENERIC_Y,
  255. &axisMotion,
  256. hid->Ppd,
  257. hid->InputBuffer,
  258. hid->Caps.InputReportByteLength);
  259. // Sign extend the value manually.
  260. Data->InputData.LastY
  261. = axisMotion | ((axisMotion & (hid->MaxY + 1)) ? (~hid->MaxY)
  262. : 0);
  263. }
  264. if (hid->IsAbsolute) {
  265. //
  266. // We need to scale this value from the physical max
  267. //
  268. scratch = ((LONGLONG)(Data->InputData.LastY) *
  269. MOUHID_RIUT_ABSOLUTE_POINTER_MAX) /
  270. hid->MaxY;
  271. Data->InputData.LastY = (LONG) scratch;
  272. }
  273. //
  274. // Determine the current Z position (wheel).
  275. //
  276. if (FALSE == hid->HasNoWheelUsage) {
  277. axisMotion = 0;
  278. if (!(Data->ProblemFlags & PROBLEM_BAD_PHYSICAL_MIN_MAX_Z)) {
  279. status = HidP_GetScaledUsageValue(
  280. HidP_Input,
  281. HID_USAGE_PAGE_GENERIC,
  282. 0,
  283. HID_USAGE_GENERIC_WHEEL,
  284. &axisMotion,
  285. hid->Ppd,
  286. hid->InputBuffer,
  287. hid->Caps.InputReportByteLength);
  288. //
  289. // If wheel usage not detected, set flag so that we do not
  290. // process wheel usages in the future.
  291. //
  292. if (HIDP_STATUS_USAGE_NOT_FOUND == status) {
  293. hid->HasNoWheelUsage = TRUE;
  294. }
  295. //
  296. // If bad physical minimum/maximum detected, set flag so that
  297. // we process usage value differently in the future.
  298. //
  299. if (status == HIDP_STATUS_BAD_LOG_PHY_VALUES) {
  300. Data->ProblemFlags |= PROBLEM_BAD_PHYSICAL_MIN_MAX_Z;
  301. updateProblemFlags = TRUE;
  302. }
  303. }
  304. if (Data->ProblemFlags & PROBLEM_BAD_PHYSICAL_MIN_MAX_Z) {
  305. HidP_GetUsageValue(HidP_Input,
  306. HID_USAGE_PAGE_GENERIC,
  307. 0,
  308. HID_USAGE_GENERIC_WHEEL,
  309. &axisMotion,
  310. hid->Ppd,
  311. hid->InputBuffer,
  312. hid->Caps.InputReportByteLength);
  313. // Sign extend the value manually.
  314. axisMotion
  315. = axisMotion
  316. | (axisMotion & (1 << (hid->BitSize.Z - 1))
  317. ? (0L - (1 << (hid->BitSize.Z - 1)))
  318. : 0);
  319. }
  320. //
  321. // Encode the Z position information into the MOUSE_INPUT_DATA
  322. // structure same way that the Magellan wheel mouse does.
  323. //
  324. if (0 == axisMotion) {
  325. Data->InputData.ButtonData = 0;
  326. } else {
  327. //
  328. // Unlike PS/2 wheel mice, we don't need to sign flip the wheel
  329. // data (unless it is an early prototype non spec compliant
  330. // device)
  331. //
  332. axisMotion *= Data->WheelScalingFactor;
  333. Data->InputData.ButtonData = Data->FlipFlop ?
  334. (USHORT) -axisMotion : (USHORT) axisMotion;
  335. Data->InputData.ButtonFlags |= MOUSE_WHEEL;
  336. }
  337. } else if (FALSE == hid->HasNoZUsage) {
  338. //
  339. // If there is no Wheel usage then there might be a "z" usage on
  340. // this mouse. Check that.
  341. //
  342. axisMotion = 0;
  343. if (!(Data->ProblemFlags & PROBLEM_BAD_PHYSICAL_MIN_MAX_Z)) {
  344. status = HidP_GetScaledUsageValue(
  345. HidP_Input,
  346. HID_USAGE_PAGE_GENERIC,
  347. 0,
  348. HID_USAGE_GENERIC_Z,
  349. &axisMotion,
  350. hid->Ppd,
  351. hid->InputBuffer,
  352. hid->Caps.InputReportByteLength);
  353. //
  354. // If wheel usage not detected, set flag so that we do not
  355. // process wheel usages in the future.
  356. //
  357. if (HIDP_STATUS_USAGE_NOT_FOUND == status) {
  358. hid->HasNoZUsage = TRUE;
  359. }
  360. //
  361. // If bad physical minimum/maximum detected, set flag so that
  362. // we process usage value differently in the future.
  363. //
  364. if (status == HIDP_STATUS_BAD_LOG_PHY_VALUES) {
  365. Data->ProblemFlags |= PROBLEM_BAD_PHYSICAL_MIN_MAX_Z;
  366. updateProblemFlags = TRUE;
  367. }
  368. }
  369. if (Data->ProblemFlags & PROBLEM_BAD_PHYSICAL_MIN_MAX_Z) {
  370. HidP_GetUsageValue(HidP_Input,
  371. HID_USAGE_PAGE_GENERIC,
  372. 0,
  373. HID_USAGE_GENERIC_Z,
  374. &axisMotion,
  375. hid->Ppd,
  376. hid->InputBuffer,
  377. hid->Caps.InputReportByteLength);
  378. // Sign extend the value manually.
  379. axisMotion
  380. = axisMotion
  381. | (axisMotion & (1 << (hid->BitSize.Z - 1))
  382. ? (0L - (1 << (hid->BitSize.Z - 1)))
  383. : 0);
  384. }
  385. //
  386. // Encode the Z position information into the MOUSE_INPUT_DATA
  387. // structure the same way that the Magellan wheel mouse does.
  388. //
  389. if (0 == axisMotion) {
  390. Data->InputData.ButtonData = 0;
  391. } else {
  392. //
  393. // Unlike PS/2 wheel mice, we don't need to sign flip the wheel
  394. // data (unless it is an early prototype non spec compliant
  395. // device)
  396. //
  397. axisMotion *= Data->WheelScalingFactor;
  398. Data->InputData.ButtonData = Data->FlipFlop ?
  399. (USHORT) -axisMotion : (USHORT) axisMotion;
  400. Data->InputData.ButtonFlags |= MOUSE_WHEEL;
  401. }
  402. }
  403. //
  404. // Leave the remaining mouse input data fields as they were
  405. // initialized (on the device's creation). This includes:
  406. // o UnitID o RawButtons
  407. // o Flags o ExtraInformation
  408. //
  409. // Now send the data up to the mouse class driver via our callback.
  410. //
  411. if (Data->EnableCount)
  412. {
  413. //
  414. // Synchronization issue - it's not a big deal if .Enabled is set
  415. // FALSE after the condition above, but before the callback below,
  416. // so long as the .MouClassCallback field is not nulled. This is
  417. // guaranteed since the disconnect IOCTL is not implemented yet.
  418. //
  419. // Mouse class callback assumes we are running at DISPATCH level,
  420. // however this IoCompletion routine can be running <= DISPATCH.
  421. // Raise the IRQL before calling the callback. [13.1]
  422. //
  423. KeRaiseIrql(DISPATCH_LEVEL, &oldIrql);
  424. //
  425. // Call the callback.
  426. //
  427. (*(PSERVICE_CALLBACK_ROUTINE)
  428. Data->ConnectData.ClassService) (
  429. Data->ConnectData.ClassDeviceObject,
  430. &Data->InputData,
  431. &Data->InputData + 1, // (one data element)
  432. &inputDataConsumed);
  433. //
  434. // Restore the previous IRQL right away.
  435. //
  436. KeLowerIrql(oldIrql);
  437. ASSERT (1 == inputDataConsumed);
  438. }
  439. //
  440. // Update ProblemFlags value in registry and log error on bad physical
  441. // minimum/maximum.
  442. //
  443. if (updateProblemFlags) {
  444. MouHid_UpdateRegistryProblemFlags (Data);
  445. MouHid_LogError(Data->Self->DriverObject,
  446. MOUHID_INVALID_PHYSICAL_MIN_MAX,
  447. NULL);
  448. }
  449. //
  450. // If MouHid_StartRead() fails, it will be handled appropriately
  451. // in the completion routine. Exit this routine without touching
  452. // HidDeviceContext.
  453. //
  454. break;
  455. case STATUS_PRIVILEGE_NOT_HELD:
  456. //
  457. // The create didn't succeed
  458. //
  459. case STATUS_CANCELLED:
  460. //
  461. // The read IRP was cancelled. Do not send any more read IRPs.
  462. //
  463. case STATUS_DELETE_PENDING:
  464. case STATUS_DEVICE_NOT_CONNECTED:
  465. //
  466. // The HID class device object is being deleted. We will soon
  467. // receive Plug 'n Play notification of this device's removal,
  468. // if we have not received it already.
  469. //
  470. SetEventAndBreak:
  471. KeSetEvent (&Data->ReadCompleteEvent, 0, FALSE);
  472. IoReleaseRemoveLock (&Data->RemoveLock, Data->ReadIrp);
  473. startRead = FALSE;
  474. break;
  475. default:
  476. //
  477. // We don't expect any other error codes.
  478. //
  479. TRAP();
  480. }
  481. //
  482. // Initiate the next read request to the HID class driver.
  483. //
  484. if (startRead) {
  485. Print(DBG_READ_TRACE, ("calling StartRead directly\n"));
  486. MouHid_StartRead (Data);
  487. } else {
  488. Print(DBG_READ_TRACE, ("StartRead will loop\n"));
  489. }
  490. return STATUS_MORE_PROCESSING_REQUIRED;
  491. }
  492. NTSTATUS
  493. MouHid_StartRead (
  494. IN PDEVICE_EXTENSION Data
  495. )
  496. /*++
  497. Routine Description:
  498. Initiates a read to the HID class driver.
  499. Note that the routine does not verify that the device context is in the
  500. OperationPending state, but simply assumes it.
  501. Note the IoCount must be incremented before entering into this read loop.
  502. Arguments:
  503. HidDeviceContext - Device context structure describing the HID device.
  504. Return Value:
  505. NTSTATUS result code from IoCallDriver().
  506. --*/
  507. {
  508. PIRP irp;
  509. NTSTATUS status = STATUS_SUCCESS;
  510. PIO_STACK_LOCATION stack;
  511. PHID_EXTENSION hid;
  512. LONG oldInterlock;
  513. Print (DBG_READ_TRACE, ("Start Read: Ente\n"));
  514. hid = Data->HidExtension;
  515. //
  516. // start this read.
  517. //
  518. irp = Data->ReadIrp;
  519. while (1) {
  520. oldInterlock = InterlockedExchange(&Data->ReadInterlock,
  521. MOUHID_START_READ);
  522. //
  523. // END_READ should be the only value here!!! If not, the state machine
  524. // of the interlock has been broken
  525. //
  526. ASSERT(oldInterlock == MOUHID_END_READ);
  527. //
  528. // Set the stack location for the Hid stack.
  529. // Remember to get the file pointer correct.
  530. // NOTE: we do not have any of the cool thread stuff set.
  531. // therefore we need to make sure that we cut this IRP off
  532. // at the knees when it returns. (STATUS_MORE_PROCESSING_REQUIRED)
  533. //
  534. // Note also that Hid class does direct IO.
  535. //
  536. IoReuseIrp (irp, STATUS_SUCCESS);
  537. irp->MdlAddress = hid->InputMdl;
  538. ASSERT (NULL != Data->ReadFile);
  539. stack = IoGetNextIrpStackLocation (irp);
  540. stack->Parameters.Read.Length = hid->Caps.InputReportByteLength;
  541. stack->Parameters.Read.Key = 0;
  542. stack->Parameters.Read.ByteOffset.QuadPart = 0;
  543. stack->MajorFunction = IRP_MJ_READ;
  544. stack->FileObject = Data->ReadFile;
  545. //
  546. // Hook a completion routine for when the device completes.
  547. //
  548. IoSetCompletionRoutine (irp,
  549. MouHid_ReadComplete,
  550. Data,
  551. TRUE,
  552. TRUE,
  553. TRUE);
  554. //
  555. // Unset the fact that the read has been sent. Synchoronizing
  556. // with remove and close code. Remove portion (data->Shuttingdown)
  557. // only really relevant on 9X.
  558. //
  559. KeResetEvent(&Data->ReadSentEvent);
  560. if (!Data->EnableCount || Data->ShuttingDown) {
  561. IoReleaseRemoveLock (&Data->RemoveLock, Data->ReadIrp);
  562. status = Data->ShuttingDown ? STATUS_DELETE_PENDING : STATUS_UNSUCCESSFUL;
  563. KeSetEvent (&Data->ReadSentEvent, 0, FALSE);
  564. break;
  565. } else {
  566. status = IoCallDriver (Data->TopOfStack, irp);
  567. }
  568. KeSetEvent (&Data->ReadSentEvent, 0, FALSE);
  569. if (MOUHID_IMMEDIATE_READ != InterlockedExchange(&Data->ReadInterlock,
  570. MOUHID_END_READ)) {
  571. //
  572. // The read is asynch, will call SerialMouseStartRead from the
  573. // completion routine
  574. //
  575. Print(DBG_READ_TRACE, ("read is pending\n"));
  576. break;
  577. } else {
  578. //
  579. // The read was synchronous (probably bytes in the buffer). The
  580. // completion routine will not call SerialMouseStartRead, so we
  581. // just loop here. This is to prevent us from running out of stack
  582. // space if always call StartRead from the completion routine
  583. //
  584. Print(DBG_READ_TRACE, ("read is looping\n"));
  585. }
  586. }
  587. return status;
  588. }