aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/hid/usbhid/hid-tmff.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/hid/usbhid/hid-tmff.c')
-rw-r--r--drivers/hid/usbhid/hid-tmff.c161
1 files changed, 119 insertions, 42 deletions
diff --git a/drivers/hid/usbhid/hid-tmff.c b/drivers/hid/usbhid/hid-tmff.c
index 555bb48b4295..69882a726e99 100644
--- a/drivers/hid/usbhid/hid-tmff.c
+++ b/drivers/hid/usbhid/hid-tmff.c
@@ -36,16 +36,39 @@
36#include "usbhid.h" 36#include "usbhid.h"
37 37
38/* Usages for thrustmaster devices I know about */ 38/* Usages for thrustmaster devices I know about */
39#define THRUSTMASTER_USAGE_RUMBLE_LR (HID_UP_GENDESK | 0xbb) 39#define THRUSTMASTER_USAGE_FF (HID_UP_GENDESK | 0xbb)
40 40
41struct dev_type {
42 u16 idVendor;
43 u16 idProduct;
44 const signed short *ff;
45};
46
47static const signed short ff_rumble[] = {
48 FF_RUMBLE,
49 -1
50};
51
52static const signed short ff_joystick[] = {
53 FF_CONSTANT,
54 -1
55};
56
57static const struct dev_type devices[] = {
58 { 0x44f, 0xb300, ff_rumble },
59 { 0x44f, 0xb304, ff_rumble },
60 { 0x44f, 0xb651, ff_rumble }, /* FGT Rumble Force Wheel */
61 { 0x44f, 0xb654, ff_joystick }, /* FGT Force Feedback Wheel */
62};
41 63
42struct tmff_device { 64struct tmff_device {
43 struct hid_report *report; 65 struct hid_report *report;
44 struct hid_field *rumble; 66 struct hid_field *ff_field;
45}; 67};
46 68
47/* Changes values from 0 to 0xffff into values from minimum to maximum */ 69/* Changes values from 0 to 0xffff into values from minimum to maximum */
48static inline int hid_tmff_scale(unsigned int in, int minimum, int maximum) 70static inline int hid_tmff_scale_u16(unsigned int in,
71 int minimum, int maximum)
49{ 72{
50 int ret; 73 int ret;
51 74
@@ -57,22 +80,57 @@ static inline int hid_tmff_scale(unsigned int in, int minimum, int maximum)
57 return ret; 80 return ret;
58} 81}
59 82
83/* Changes values from -0x80 to 0x7f into values from minimum to maximum */
84static inline int hid_tmff_scale_s8(int in,
85 int minimum, int maximum)
86{
87 int ret;
88
89 ret = (((in + 0x80) * (maximum - minimum)) / 0xff) + minimum;
90 if (ret < minimum)
91 return minimum;
92 if (ret > maximum)
93 return maximum;
94 return ret;
95}
96
60static int hid_tmff_play(struct input_dev *dev, void *data, struct ff_effect *effect) 97static int hid_tmff_play(struct input_dev *dev, void *data, struct ff_effect *effect)
61{ 98{
62 struct hid_device *hid = input_get_drvdata(dev); 99 struct hid_device *hid = input_get_drvdata(dev);
63 struct tmff_device *tmff = data; 100 struct tmff_device *tmff = data;
101 struct hid_field *ff_field = tmff->ff_field;
102 int x, y;
64 int left, right; /* Rumbling */ 103 int left, right; /* Rumbling */
65 104
66 left = hid_tmff_scale(effect->u.rumble.weak_magnitude, 105 switch (effect->type) {
67 tmff->rumble->logical_minimum, tmff->rumble->logical_maximum); 106 case FF_CONSTANT:
68 right = hid_tmff_scale(effect->u.rumble.strong_magnitude, 107 x = hid_tmff_scale_s8(effect->u.ramp.start_level,
69 tmff->rumble->logical_minimum, tmff->rumble->logical_maximum); 108 ff_field->logical_minimum,
70 109 ff_field->logical_maximum);
71 tmff->rumble->value[0] = left; 110 y = hid_tmff_scale_s8(effect->u.ramp.end_level,
72 tmff->rumble->value[1] = right; 111 ff_field->logical_minimum,
73 dbg_hid("(left,right)=(%08x, %08x)\n", left, right); 112 ff_field->logical_maximum);
74 usbhid_submit_report(hid, tmff->report, USB_DIR_OUT); 113
75 114 dbg_hid("(x, y)=(%04x, %04x)\n", x, y);
115 ff_field->value[0] = x;
116 ff_field->value[1] = y;
117 usbhid_submit_report(hid, tmff->report, USB_DIR_OUT);
118 break;
119
120 case FF_RUMBLE:
121 left = hid_tmff_scale_u16(effect->u.rumble.weak_magnitude,
122 ff_field->logical_minimum,
123 ff_field->logical_maximum);
124 right = hid_tmff_scale_u16(effect->u.rumble.strong_magnitude,
125 ff_field->logical_minimum,
126 ff_field->logical_maximum);
127
128 dbg_hid("(left,right)=(%08x, %08x)\n", left, right);
129 ff_field->value[0] = left;
130 ff_field->value[1] = right;
131 usbhid_submit_report(hid, tmff->report, USB_DIR_OUT);
132 break;
133 }
76 return 0; 134 return 0;
77} 135}
78 136
@@ -82,14 +140,16 @@ int hid_tmff_init(struct hid_device *hid)
82 struct list_head *pos; 140 struct list_head *pos;
83 struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list); 141 struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
84 struct input_dev *input_dev = hidinput->input; 142 struct input_dev *input_dev = hidinput->input;
143 const signed short *ff_bits = ff_joystick;
85 int error; 144 int error;
145 int i;
86 146
87 tmff = kzalloc(sizeof(struct tmff_device), GFP_KERNEL); 147 tmff = kzalloc(sizeof(struct tmff_device), GFP_KERNEL);
88 if (!tmff) 148 if (!tmff)
89 return -ENOMEM; 149 return -ENOMEM;
90 150
91 /* Find the report to use */ 151 /* Find the report to use */
92 __list_for_each(pos, &hid->report_enum[HID_OUTPUT_REPORT].report_list) { 152 list_for_each(pos, &hid->report_enum[HID_OUTPUT_REPORT].report_list) {
93 struct hid_report *report = (struct hid_report *)pos; 153 struct hid_report *report = (struct hid_report *)pos;
94 int fieldnum; 154 int fieldnum;
95 155
@@ -100,48 +160,65 @@ int hid_tmff_init(struct hid_device *hid)
100 continue; 160 continue;
101 161
102 switch (field->usage[0].hid) { 162 switch (field->usage[0].hid) {
103 case THRUSTMASTER_USAGE_RUMBLE_LR: 163 case THRUSTMASTER_USAGE_FF:
104 if (field->report_count < 2) { 164 if (field->report_count < 2) {
105 warn("ignoring THRUSTMASTER_USAGE_RUMBLE_LR with report_count < 2"); 165 warn("ignoring FF field with report_count < 2");
106 continue; 166 continue;
107 } 167 }
108 168
109 if (field->logical_maximum == field->logical_minimum) { 169 if (field->logical_maximum == field->logical_minimum) {
110 warn("ignoring THRUSTMASTER_USAGE_RUMBLE_LR with logical_maximum == logical_minimum"); 170 warn("ignoring FF field with logical_maximum == logical_minimum");
111 continue; 171 continue;
112 } 172 }
113 173
114 if (tmff->report && tmff->report != report) { 174 if (tmff->report && tmff->report != report) {
115 warn("ignoring THRUSTMASTER_USAGE_RUMBLE_LR in other report"); 175 warn("ignoring FF field in other report");
116 continue; 176 continue;
117 } 177 }
118 178
119 if (tmff->rumble && tmff->rumble != field) { 179 if (tmff->ff_field && tmff->ff_field != field) {
120 warn("ignoring duplicate THRUSTMASTER_USAGE_RUMBLE_LR"); 180 warn("ignoring duplicate FF field");
121 continue; 181 continue;
182 }
183
184 tmff->report = report;
185 tmff->ff_field = field;
186
187 for (i = 0; i < ARRAY_SIZE(devices); i++) {
188 if (input_dev->id.vendor == devices[i].idVendor &&
189 input_dev->id.product == devices[i].idProduct) {
190 ff_bits = devices[i].ff;
191 break;
122 } 192 }
193 }
123 194
124 tmff->report = report; 195 for (i = 0; ff_bits[i] >= 0; i++)
125 tmff->rumble = field; 196 set_bit(ff_bits[i], input_dev->ffbit);
126 197
127 set_bit(FF_RUMBLE, input_dev->ffbit); 198 break;
128 break;
129 199
130 default: 200 default:
131 warn("ignoring unknown output usage %08x", field->usage[0].hid); 201 warn("ignoring unknown output usage %08x", field->usage[0].hid);
132 continue; 202 continue;
133 } 203 }
134 } 204 }
135 } 205 }
136 206
137 error = input_ff_create_memless(input_dev, tmff, hid_tmff_play); 207 if (!tmff->report) {
138 if (error) { 208 err("cant find FF field in output reports\n");
139 kfree(tmff); 209 error = -ENODEV;
140 return error; 210 goto fail;
141 } 211 }
142 212
143 info("Force feedback for ThrustMaster rumble pad devices by Zinx Verituse <zinx@epicsol.org>"); 213 error = input_ff_create_memless(input_dev, tmff, hid_tmff_play);
214 if (error)
215 goto fail;
144 216
217 info("Force feedback for ThrustMaster devices by Zinx Verituse <zinx@epicsol.org>");
145 return 0; 218 return 0;
219
220 fail:
221 kfree(tmff);
222 return error;
146} 223}
147 224