diff options
author | Dmitry Torokhov <dtor@mail.ru> | 2007-07-30 08:56:26 -0400 |
---|---|---|
committer | Jiri Kosina <jkosina@suse.cz> | 2007-10-14 07:40:00 -0400 |
commit | b27c9590ca0f44681fe2504a7ec427ff1bb77e82 (patch) | |
tree | 7a9bb6cc6c0a09fe60888de4cb1efe8bc1dfaf85 /drivers/hid/usbhid/hid-tmff.c | |
parent | 933e3187d0042d9381d932757dc1f931d984e56d (diff) |
HID: add support for Thrustmaster FGT Force Feedback wheel
Rework thrustmaster force-feedback module to support devices having
different types of force feedback effects. Add signatures of
Thrustmaster FGT Rumble Force and Thrustmaster FGT Force Feedback
wheels to the list of devices dupported by the module.
Parts of the patch were lifted off a simalar patch by
Anssi Hannula <anssi.hannula@gmail.com>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Diffstat (limited to 'drivers/hid/usbhid/hid-tmff.c')
-rw-r--r-- | drivers/hid/usbhid/hid-tmff.c | 161 |
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 | ||
41 | struct dev_type { | ||
42 | u16 idVendor; | ||
43 | u16 idProduct; | ||
44 | const signed short *ff; | ||
45 | }; | ||
46 | |||
47 | static const signed short ff_rumble[] = { | ||
48 | FF_RUMBLE, | ||
49 | -1 | ||
50 | }; | ||
51 | |||
52 | static const signed short ff_joystick[] = { | ||
53 | FF_CONSTANT, | ||
54 | -1 | ||
55 | }; | ||
56 | |||
57 | static 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 | ||
42 | struct tmff_device { | 64 | struct 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 */ |
48 | static inline int hid_tmff_scale(unsigned int in, int minimum, int maximum) | 70 | static 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 */ | ||
84 | static 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 | |||
60 | static int hid_tmff_play(struct input_dev *dev, void *data, struct ff_effect *effect) | 97 | static 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 | ||