aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/hid/hid-lg4ff.c
diff options
context:
space:
mode:
authorMichal Malý <madcatxster@gmail.com>2012-09-23 16:41:08 -0400
committerJiri Kosina <jkosina@suse.cz>2012-09-25 09:41:02 -0400
commit2b24a960016b8d3221a6dd2764ab97247c48dd97 (patch)
treecf01b767eee96bd9d178a43d8c694cafeb287b3c /drivers/hid/hid-lg4ff.c
parent74479ba861b5cd77d445c2ec8f59521ae509f7db (diff)
HID: hid-lg4ff: Adjust X axis input value accordingly to selected range.
Range limiting command for the Driving Force Pro wheel is only a FF_SPRING effect so that the wheel creates resistance when the user tries to turn it past the limit. It is however possible to overpower the FFB motors quite easily which leads to the X axis value exceeding the expected limit. This confuses games which dynamically adjust calibration using the highest/lowest min and max values reported by the wheel. Joydev device driver also doesn't take in account any changes in an axis range after the joystick device is created. This patch recalculates received ABS_X axis value so it is always in <0; 16383> range where 0 is the left limit and 16383 the right limit. Logitech driver for Windows does the same thing. As for any concerns about possible loss of precision, I compared a large set of raw/adjusted values generated by "mult_frac" to values returned by the Windows driver and I got a 100% match. Other Logitech wheels will probably need a similar fix, but I currently lack the information needed to write one. Signed-off-by: Michal Malý <madcatxster@gmail.com> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Diffstat (limited to 'drivers/hid/hid-lg4ff.c')
-rw-r--r--drivers/hid/hid-lg4ff.c54
1 files changed, 54 insertions, 0 deletions
diff --git a/drivers/hid/hid-lg4ff.c b/drivers/hid/hid-lg4ff.c
index c40e957aab91..fe15b3bdffcf 100644
--- a/drivers/hid/hid-lg4ff.c
+++ b/drivers/hid/hid-lg4ff.c
@@ -53,6 +53,7 @@ static ssize_t lg4ff_range_store(struct device *dev, struct device_attribute *at
53static DEVICE_ATTR(range, S_IRWXU | S_IRWXG | S_IRWXO, lg4ff_range_show, lg4ff_range_store); 53static DEVICE_ATTR(range, S_IRWXU | S_IRWXG | S_IRWXO, lg4ff_range_show, lg4ff_range_store);
54 54
55struct lg4ff_device_entry { 55struct lg4ff_device_entry {
56 __u32 product_id;
56 __u16 range; 57 __u16 range;
57 __u16 min_range; 58 __u16 min_range;
58 __u16 max_range; 59 __u16 max_range;
@@ -129,6 +130,56 @@ static const struct lg4ff_usb_revision lg4ff_revs[] = {
129 {G27_REV_MAJ, G27_REV_MIN, &native_g27}, /* G27 */ 130 {G27_REV_MAJ, G27_REV_MIN, &native_g27}, /* G27 */
130}; 131};
131 132
133/* Recalculates X axis value accordingly to currently selected range */
134static __s32 lg4ff_adjust_dfp_x_axis(__s32 value, __u16 range)
135{
136 __u16 max_range;
137 __s32 new_value;
138
139 if (range == 900)
140 return value;
141 else if (range == 200)
142 return value;
143 else if (range < 200)
144 max_range = 200;
145 else
146 max_range = 900;
147
148 new_value = 8192 + mult_frac(value - 8192, max_range, range);
149 if (new_value < 0)
150 return 0;
151 else if (new_value > 16383)
152 return 16383;
153 else
154 return new_value;
155}
156
157int lg4ff_adjust_input_event(struct hid_device *hid, struct hid_field *field,
158 struct hid_usage *usage, __s32 value, struct lg_drv_data *drv_data)
159{
160 struct lg4ff_device_entry *entry = drv_data->device_props;
161 __s32 new_value = 0;
162
163 if (!entry) {
164 hid_err(hid, "Device properties not found");
165 return 0;
166 }
167
168 switch (entry->product_id) {
169 case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:
170 switch (usage->code) {
171 case ABS_X:
172 new_value = lg4ff_adjust_dfp_x_axis(value, entry->range);
173 input_event(field->hidinput->input, usage->type, usage->code, new_value);
174 return 1;
175 default:
176 return 0;
177 }
178 default:
179 return 0;
180 }
181}
182
132static int hid_lg4ff_play(struct input_dev *dev, void *data, struct ff_effect *effect) 183static int hid_lg4ff_play(struct input_dev *dev, void *data, struct ff_effect *effect)
133{ 184{
134 struct hid_device *hid = input_get_drvdata(dev); 185 struct hid_device *hid = input_get_drvdata(dev);
@@ -531,6 +582,7 @@ int lg4ff_init(struct hid_device *hid)
531 } 582 }
532 drv_data->device_props = entry; 583 drv_data->device_props = entry;
533 584
585 entry->product_id = lg4ff_devices[i].product_id;
534 entry->min_range = lg4ff_devices[i].min_range; 586 entry->min_range = lg4ff_devices[i].min_range;
535 entry->max_range = lg4ff_devices[i].max_range; 587 entry->max_range = lg4ff_devices[i].max_range;
536 entry->set_range = lg4ff_devices[i].set_range; 588 entry->set_range = lg4ff_devices[i].set_range;
@@ -601,6 +653,8 @@ out:
601 return 0; 653 return 0;
602} 654}
603 655
656
657
604int lg4ff_deinit(struct hid_device *hid) 658int lg4ff_deinit(struct hid_device *hid)
605{ 659{
606 struct lg4ff_device_entry *entry; 660 struct lg4ff_device_entry *entry;