hid-lg3ff.c 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. * Force feedback support for Logitech Flight System G940
  4. *
  5. * Copyright (c) 2009 Gary Stein <[email protected]>
  6. */
  7. /*
  8. */
  9. #include <linux/input.h>
  10. #include <linux/hid.h>
  11. #include "hid-lg.h"
  12. /*
  13. * G940 Theory of Operation (from experimentation)
  14. *
  15. * There are 63 fields (only 3 of them currently used)
  16. * 0 - seems to be command field
  17. * 1 - 30 deal with the x axis
  18. * 31 -60 deal with the y axis
  19. *
  20. * Field 1 is x axis constant force
  21. * Field 31 is y axis constant force
  22. *
  23. * other interesting fields 1,2,3,4 on x axis
  24. * (same for 31,32,33,34 on y axis)
  25. *
  26. * 0 0 127 127 makes the joystick autocenter hard
  27. *
  28. * 127 0 127 127 makes the joystick loose on the right,
  29. * but stops all movemnt left
  30. *
  31. * -127 0 -127 -127 makes the joystick loose on the left,
  32. * but stops all movement right
  33. *
  34. * 0 0 -127 -127 makes the joystick rattle very hard
  35. *
  36. * I'm sure these are effects that I don't know enough about them
  37. */
  38. struct lg3ff_device {
  39. struct hid_report *report;
  40. };
  41. static int hid_lg3ff_play(struct input_dev *dev, void *data,
  42. struct ff_effect *effect)
  43. {
  44. struct hid_device *hid = input_get_drvdata(dev);
  45. struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
  46. struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
  47. int x, y;
  48. /*
  49. * Available values in the field should always be 63, but we only use up to
  50. * 35. Instead, clear the entire area, however big it is.
  51. */
  52. memset(report->field[0]->value, 0,
  53. sizeof(__s32) * report->field[0]->report_count);
  54. switch (effect->type) {
  55. case FF_CONSTANT:
  56. /*
  57. * Already clamped in ff_memless
  58. * 0 is center (different then other logitech)
  59. */
  60. x = effect->u.ramp.start_level;
  61. y = effect->u.ramp.end_level;
  62. /* send command byte */
  63. report->field[0]->value[0] = 0x51;
  64. /*
  65. * Sign backwards from other Force3d pro
  66. * which get recast here in two's complement 8 bits
  67. */
  68. report->field[0]->value[1] = (unsigned char)(-x);
  69. report->field[0]->value[31] = (unsigned char)(-y);
  70. hid_hw_request(hid, report, HID_REQ_SET_REPORT);
  71. break;
  72. }
  73. return 0;
  74. }
  75. static void hid_lg3ff_set_autocenter(struct input_dev *dev, u16 magnitude)
  76. {
  77. struct hid_device *hid = input_get_drvdata(dev);
  78. struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
  79. struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
  80. /*
  81. * Auto Centering probed from device
  82. * NOTE: deadman's switch on G940 must be covered
  83. * for effects to work
  84. */
  85. report->field[0]->value[0] = 0x51;
  86. report->field[0]->value[1] = 0x00;
  87. report->field[0]->value[2] = 0x00;
  88. report->field[0]->value[3] = 0x7F;
  89. report->field[0]->value[4] = 0x7F;
  90. report->field[0]->value[31] = 0x00;
  91. report->field[0]->value[32] = 0x00;
  92. report->field[0]->value[33] = 0x7F;
  93. report->field[0]->value[34] = 0x7F;
  94. hid_hw_request(hid, report, HID_REQ_SET_REPORT);
  95. }
  96. static const signed short ff3_joystick_ac[] = {
  97. FF_CONSTANT,
  98. FF_AUTOCENTER,
  99. -1
  100. };
  101. int lg3ff_init(struct hid_device *hid)
  102. {
  103. struct hid_input *hidinput;
  104. struct input_dev *dev;
  105. const signed short *ff_bits = ff3_joystick_ac;
  106. int error;
  107. int i;
  108. if (list_empty(&hid->inputs)) {
  109. hid_err(hid, "no inputs found\n");
  110. return -ENODEV;
  111. }
  112. hidinput = list_entry(hid->inputs.next, struct hid_input, list);
  113. dev = hidinput->input;
  114. /* Check that the report looks ok */
  115. if (!hid_validate_values(hid, HID_OUTPUT_REPORT, 0, 0, 35))
  116. return -ENODEV;
  117. /* Assume single fixed device G940 */
  118. for (i = 0; ff_bits[i] >= 0; i++)
  119. set_bit(ff_bits[i], dev->ffbit);
  120. error = input_ff_create_memless(dev, NULL, hid_lg3ff_play);
  121. if (error)
  122. return error;
  123. if (test_bit(FF_AUTOCENTER, dev->ffbit))
  124. dev->ff->set_autocenter = hid_lg3ff_set_autocenter;
  125. hid_info(hid, "Force feedback for Logitech Flight System G940 by Gary Stein <[email protected]>\n");
  126. return 0;
  127. }