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