1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Force feedback support for Logitech RumblePad and Rumblepad 2
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Copyright (c) 2008 Anssi Hannula <anssi.hannula@gmail.com>
6*4882a593Smuzhiyun */
7*4882a593Smuzhiyun
8*4882a593Smuzhiyun /*
9*4882a593Smuzhiyun */
10*4882a593Smuzhiyun
11*4882a593Smuzhiyun
12*4882a593Smuzhiyun #include <linux/input.h>
13*4882a593Smuzhiyun #include <linux/slab.h>
14*4882a593Smuzhiyun #include <linux/hid.h>
15*4882a593Smuzhiyun
16*4882a593Smuzhiyun #include "hid-lg.h"
17*4882a593Smuzhiyun
18*4882a593Smuzhiyun struct lg2ff_device {
19*4882a593Smuzhiyun struct hid_report *report;
20*4882a593Smuzhiyun };
21*4882a593Smuzhiyun
play_effect(struct input_dev * dev,void * data,struct ff_effect * effect)22*4882a593Smuzhiyun static int play_effect(struct input_dev *dev, void *data,
23*4882a593Smuzhiyun struct ff_effect *effect)
24*4882a593Smuzhiyun {
25*4882a593Smuzhiyun struct hid_device *hid = input_get_drvdata(dev);
26*4882a593Smuzhiyun struct lg2ff_device *lg2ff = data;
27*4882a593Smuzhiyun int weak, strong;
28*4882a593Smuzhiyun
29*4882a593Smuzhiyun strong = effect->u.rumble.strong_magnitude;
30*4882a593Smuzhiyun weak = effect->u.rumble.weak_magnitude;
31*4882a593Smuzhiyun
32*4882a593Smuzhiyun if (weak || strong) {
33*4882a593Smuzhiyun weak = weak * 0xff / 0xffff;
34*4882a593Smuzhiyun strong = strong * 0xff / 0xffff;
35*4882a593Smuzhiyun
36*4882a593Smuzhiyun lg2ff->report->field[0]->value[0] = 0x51;
37*4882a593Smuzhiyun lg2ff->report->field[0]->value[2] = weak;
38*4882a593Smuzhiyun lg2ff->report->field[0]->value[4] = strong;
39*4882a593Smuzhiyun } else {
40*4882a593Smuzhiyun lg2ff->report->field[0]->value[0] = 0xf3;
41*4882a593Smuzhiyun lg2ff->report->field[0]->value[2] = 0x00;
42*4882a593Smuzhiyun lg2ff->report->field[0]->value[4] = 0x00;
43*4882a593Smuzhiyun }
44*4882a593Smuzhiyun
45*4882a593Smuzhiyun hid_hw_request(hid, lg2ff->report, HID_REQ_SET_REPORT);
46*4882a593Smuzhiyun return 0;
47*4882a593Smuzhiyun }
48*4882a593Smuzhiyun
lg2ff_init(struct hid_device * hid)49*4882a593Smuzhiyun int lg2ff_init(struct hid_device *hid)
50*4882a593Smuzhiyun {
51*4882a593Smuzhiyun struct lg2ff_device *lg2ff;
52*4882a593Smuzhiyun struct hid_report *report;
53*4882a593Smuzhiyun struct hid_input *hidinput;
54*4882a593Smuzhiyun struct input_dev *dev;
55*4882a593Smuzhiyun int error;
56*4882a593Smuzhiyun
57*4882a593Smuzhiyun if (list_empty(&hid->inputs)) {
58*4882a593Smuzhiyun hid_err(hid, "no inputs found\n");
59*4882a593Smuzhiyun return -ENODEV;
60*4882a593Smuzhiyun }
61*4882a593Smuzhiyun hidinput = list_entry(hid->inputs.next, struct hid_input, list);
62*4882a593Smuzhiyun dev = hidinput->input;
63*4882a593Smuzhiyun
64*4882a593Smuzhiyun /* Check that the report looks ok */
65*4882a593Smuzhiyun report = hid_validate_values(hid, HID_OUTPUT_REPORT, 0, 0, 7);
66*4882a593Smuzhiyun if (!report)
67*4882a593Smuzhiyun return -ENODEV;
68*4882a593Smuzhiyun
69*4882a593Smuzhiyun lg2ff = kmalloc(sizeof(struct lg2ff_device), GFP_KERNEL);
70*4882a593Smuzhiyun if (!lg2ff)
71*4882a593Smuzhiyun return -ENOMEM;
72*4882a593Smuzhiyun
73*4882a593Smuzhiyun set_bit(FF_RUMBLE, dev->ffbit);
74*4882a593Smuzhiyun
75*4882a593Smuzhiyun error = input_ff_create_memless(dev, lg2ff, play_effect);
76*4882a593Smuzhiyun if (error) {
77*4882a593Smuzhiyun kfree(lg2ff);
78*4882a593Smuzhiyun return error;
79*4882a593Smuzhiyun }
80*4882a593Smuzhiyun
81*4882a593Smuzhiyun lg2ff->report = report;
82*4882a593Smuzhiyun report->field[0]->value[0] = 0xf3;
83*4882a593Smuzhiyun report->field[0]->value[1] = 0x00;
84*4882a593Smuzhiyun report->field[0]->value[2] = 0x00;
85*4882a593Smuzhiyun report->field[0]->value[3] = 0x00;
86*4882a593Smuzhiyun report->field[0]->value[4] = 0x00;
87*4882a593Smuzhiyun report->field[0]->value[5] = 0x00;
88*4882a593Smuzhiyun report->field[0]->value[6] = 0x00;
89*4882a593Smuzhiyun
90*4882a593Smuzhiyun hid_hw_request(hid, report, HID_REQ_SET_REPORT);
91*4882a593Smuzhiyun
92*4882a593Smuzhiyun hid_info(hid, "Force feedback for Logitech variant 2 rumble devices by Anssi Hannula <anssi.hannula@gmail.com>\n");
93*4882a593Smuzhiyun
94*4882a593Smuzhiyun return 0;
95*4882a593Smuzhiyun }
96