diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/hid/usbhid/Kconfig | 5 | ||||
-rw-r--r-- | drivers/hid/usbhid/hid-ff.c | 2 | ||||
-rw-r--r-- | drivers/hid/usbhid/hid-tmff.c | 161 |
3 files changed, 124 insertions, 44 deletions
diff --git a/drivers/hid/usbhid/Kconfig b/drivers/hid/usbhid/Kconfig index 1b4b572f899b..b27023f23c87 100644 --- a/drivers/hid/usbhid/Kconfig +++ b/drivers/hid/usbhid/Kconfig | |||
@@ -79,11 +79,12 @@ config PANTHERLORD_FF | |||
79 | to enable force feedback support for it. | 79 | to enable force feedback support for it. |
80 | 80 | ||
81 | config THRUSTMASTER_FF | 81 | config THRUSTMASTER_FF |
82 | bool "ThrustMaster FireStorm Dual Power 2 support (EXPERIMENTAL)" | 82 | bool "ThrustMaster devices support (EXPERIMENTAL)" |
83 | depends on HID_FF && EXPERIMENTAL | 83 | depends on HID_FF && EXPERIMENTAL |
84 | select INPUT_FF_MEMLESS if USB_HID | 84 | select INPUT_FF_MEMLESS if USB_HID |
85 | help | 85 | help |
86 | Say Y here if you have a THRUSTMASTER FireStore Dual Power 2, | 86 | Say Y here if you have a THRUSTMASTER FireStore Dual Power 2 or |
87 | a THRUSTMASTER Ferrari GT Rumble Force or Force Feedback Wheel, | ||
87 | and want to enable force feedback support for it. | 88 | and want to enable force feedback support for it. |
88 | Note: if you say N here, this device will still be supported, but without | 89 | Note: if you say N here, this device will still be supported, but without |
89 | force feedback. | 90 | force feedback. |
diff --git a/drivers/hid/usbhid/hid-ff.c b/drivers/hid/usbhid/hid-ff.c index 23431fbbc3d7..5dacd8ec8072 100644 --- a/drivers/hid/usbhid/hid-ff.c +++ b/drivers/hid/usbhid/hid-ff.c | |||
@@ -67,6 +67,8 @@ static struct hid_ff_initializer inits[] = { | |||
67 | #ifdef CONFIG_THRUSTMASTER_FF | 67 | #ifdef CONFIG_THRUSTMASTER_FF |
68 | { 0x44f, 0xb300, hid_tmff_init }, | 68 | { 0x44f, 0xb300, hid_tmff_init }, |
69 | { 0x44f, 0xb304, hid_tmff_init }, | 69 | { 0x44f, 0xb304, hid_tmff_init }, |
70 | { 0x44f, 0xb651, hid_tmff_init }, /* FGT Rumble Force Wheel */ | ||
71 | { 0x44f, 0xb654, hid_tmff_init }, /* FGT Force Feedback Wheel */ | ||
70 | #endif | 72 | #endif |
71 | #ifdef CONFIG_ZEROPLUS_FF | 73 | #ifdef CONFIG_ZEROPLUS_FF |
72 | { 0xc12, 0x0005, hid_zpff_init }, | 74 | { 0xc12, 0x0005, hid_zpff_init }, |
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 | ||