Blame view

drivers/hid/hid-tmff.c 7.24 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
  /*
   * Force feedback support for various HID compliant devices by ThrustMaster:
   *    ThrustMaster FireStorm Dual Power 2
   * and possibly others whose device ids haven't been added.
   *
   *  Modified to support ThrustMaster devices by Zinx Verituse
   *  on 2003-01-25 from the Logitech force feedback driver,
   *  which is by Johann Deneux.
   *
   *  Copyright (c) 2003 Zinx Verituse <zinx@epicsol.org>
   *  Copyright (c) 2002 Johann Deneux
   */
  
  /*
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
   * the Free Software Foundation; either version 2 of the License, or
   * (at your option) any later version.
   *
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
   *
   * You should have received a copy of the GNU General Public License
   * along with this program; if not, write to the Free Software
   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
   */
10e41a711   Jiri Slaby   HID: move thrustm...
29
  #include <linux/hid.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
30
  #include <linux/input.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
31
  #include <linux/slab.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
32
  #include <linux/usb.h>
8f86a2c3c   Paul Gortmaker   hid: Add module.h...
33
  #include <linux/module.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
34

10e41a711   Jiri Slaby   HID: move thrustm...
35
  #include "hid-ids.h"
b27c9590c   Dmitry Torokhov   HID: add support ...
36
37
38
39
40
41
42
43
44
  static const signed short ff_rumble[] = {
  	FF_RUMBLE,
  	-1
  };
  
  static const signed short ff_joystick[] = {
  	FF_CONSTANT,
  	-1
  };
0f6f4319a   Jiri Kosina   HID: fix hid-ff d...
45
46
47
48
49
  #ifdef CONFIG_THRUSTMASTER_FF
  #include "usbhid/usbhid.h"
  
  /* Usages for thrustmaster devices I know about */
  #define THRUSTMASTER_USAGE_FF	(HID_UP_GENDESK | 0xbb)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
50
  struct tmff_device {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
51
  	struct hid_report *report;
b27c9590c   Dmitry Torokhov   HID: add support ...
52
  	struct hid_field *ff_field;
dc76c9121   Anssi Hannula   Input: use new FF...
53
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
54

dc76c9121   Anssi Hannula   Input: use new FF...
55
  /* Changes values from 0 to 0xffff into values from minimum to maximum */
10e41a711   Jiri Slaby   HID: move thrustm...
56
  static inline int tmff_scale_u16(unsigned int in, int minimum, int maximum)
dc76c9121   Anssi Hannula   Input: use new FF...
57
58
  {
  	int ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
59

dc76c9121   Anssi Hannula   Input: use new FF...
60
61
62
63
64
65
66
  	ret = (in * (maximum - minimum) / 0xffff) + minimum;
  	if (ret < minimum)
  		return minimum;
  	if (ret > maximum)
  		return maximum;
  	return ret;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
67

b27c9590c   Dmitry Torokhov   HID: add support ...
68
  /* Changes values from -0x80 to 0x7f into values from minimum to maximum */
10e41a711   Jiri Slaby   HID: move thrustm...
69
  static inline int tmff_scale_s8(int in, int minimum, int maximum)
b27c9590c   Dmitry Torokhov   HID: add support ...
70
71
72
73
74
75
76
77
78
79
  {
  	int ret;
  
  	ret = (((in + 0x80) * (maximum - minimum)) / 0xff) + minimum;
  	if (ret < minimum)
  		return minimum;
  	if (ret > maximum)
  		return maximum;
  	return ret;
  }
10e41a711   Jiri Slaby   HID: move thrustm...
80
81
  static int tmff_play(struct input_dev *dev, void *data,
  		struct ff_effect *effect)
dc76c9121   Anssi Hannula   Input: use new FF...
82
  {
e07129858   Dmitry Torokhov   HID: switch to us...
83
  	struct hid_device *hid = input_get_drvdata(dev);
dc76c9121   Anssi Hannula   Input: use new FF...
84
  	struct tmff_device *tmff = data;
b27c9590c   Dmitry Torokhov   HID: add support ...
85
86
  	struct hid_field *ff_field = tmff->ff_field;
  	int x, y;
dc76c9121   Anssi Hannula   Input: use new FF...
87
  	int left, right;	/* Rumbling */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
88

b27c9590c   Dmitry Torokhov   HID: add support ...
89
90
  	switch (effect->type) {
  	case FF_CONSTANT:
10e41a711   Jiri Slaby   HID: move thrustm...
91
  		x = tmff_scale_s8(effect->u.ramp.start_level,
b27c9590c   Dmitry Torokhov   HID: add support ...
92
93
  					ff_field->logical_minimum,
  					ff_field->logical_maximum);
10e41a711   Jiri Slaby   HID: move thrustm...
94
  		y = tmff_scale_s8(effect->u.ramp.end_level,
b27c9590c   Dmitry Torokhov   HID: add support ...
95
96
97
98
99
100
101
102
103
104
105
  					ff_field->logical_minimum,
  					ff_field->logical_maximum);
  
  		dbg_hid("(x, y)=(%04x, %04x)
  ", x, y);
  		ff_field->value[0] = x;
  		ff_field->value[1] = y;
  		usbhid_submit_report(hid, tmff->report, USB_DIR_OUT);
  		break;
  
  	case FF_RUMBLE:
10e41a711   Jiri Slaby   HID: move thrustm...
106
  		left = tmff_scale_u16(effect->u.rumble.weak_magnitude,
b27c9590c   Dmitry Torokhov   HID: add support ...
107
108
  					ff_field->logical_minimum,
  					ff_field->logical_maximum);
10e41a711   Jiri Slaby   HID: move thrustm...
109
  		right = tmff_scale_u16(effect->u.rumble.strong_magnitude,
b27c9590c   Dmitry Torokhov   HID: add support ...
110
111
112
113
114
115
116
117
118
119
  					ff_field->logical_minimum,
  					ff_field->logical_maximum);
  
  		dbg_hid("(left,right)=(%08x, %08x)
  ", left, right);
  		ff_field->value[0] = left;
  		ff_field->value[1] = right;
  		usbhid_submit_report(hid, tmff->report, USB_DIR_OUT);
  		break;
  	}
dc76c9121   Anssi Hannula   Input: use new FF...
120
121
  	return 0;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
122

10e41a711   Jiri Slaby   HID: move thrustm...
123
  static int tmff_init(struct hid_device *hid, const signed short *ff_bits)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
124
  {
dc76c9121   Anssi Hannula   Input: use new FF...
125
  	struct tmff_device *tmff;
3ba5619f0   Li Zefan   HID: fix a potent...
126
127
  	struct hid_report *report;
  	struct list_head *report_list;
10e41a711   Jiri Slaby   HID: move thrustm...
128
129
  	struct hid_input *hidinput = list_entry(hid->inputs.next,
  							struct hid_input, list);
c5b7c7c39   Dmitry Torokhov   [PATCH] drivers/u...
130
  	struct input_dev *input_dev = hidinput->input;
dc76c9121   Anssi Hannula   Input: use new FF...
131
  	int error;
b27c9590c   Dmitry Torokhov   HID: add support ...
132
  	int i;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
133

dc76c9121   Anssi Hannula   Input: use new FF...
134
135
  	tmff = kzalloc(sizeof(struct tmff_device), GFP_KERNEL);
  	if (!tmff)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
136
  		return -ENOMEM;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
137
  	/* Find the report to use */
3ba5619f0   Li Zefan   HID: fix a potent...
138
139
  	report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
  	list_for_each_entry(report, report_list, list) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
140
141
142
143
144
145
146
147
148
  		int fieldnum;
  
  		for (fieldnum = 0; fieldnum < report->maxfield; ++fieldnum) {
  			struct hid_field *field = report->field[fieldnum];
  
  			if (field->maxusage <= 0)
  				continue;
  
  			switch (field->usage[0].hid) {
b27c9590c   Dmitry Torokhov   HID: add support ...
149
150
  			case THRUSTMASTER_USAGE_FF:
  				if (field->report_count < 2) {
4291ee305   Joe Perches   HID: Add and use ...
151
152
  					hid_warn(hid, "ignoring FF field with report_count < 2
  ");
b27c9590c   Dmitry Torokhov   HID: add support ...
153
154
  					continue;
  				}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
155

10e41a711   Jiri Slaby   HID: move thrustm...
156
157
  				if (field->logical_maximum ==
  						field->logical_minimum) {
4291ee305   Joe Perches   HID: Add and use ...
158
159
  					hid_warn(hid, "ignoring FF field with logical_maximum == logical_minimum
  ");
b27c9590c   Dmitry Torokhov   HID: add support ...
160
161
  					continue;
  				}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
162

b27c9590c   Dmitry Torokhov   HID: add support ...
163
  				if (tmff->report && tmff->report != report) {
4291ee305   Joe Perches   HID: Add and use ...
164
165
  					hid_warn(hid, "ignoring FF field in other report
  ");
b27c9590c   Dmitry Torokhov   HID: add support ...
166
167
  					continue;
  				}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
168

b27c9590c   Dmitry Torokhov   HID: add support ...
169
  				if (tmff->ff_field && tmff->ff_field != field) {
4291ee305   Joe Perches   HID: Add and use ...
170
171
  					hid_warn(hid, "ignoring duplicate FF field
  ");
b27c9590c   Dmitry Torokhov   HID: add support ...
172
173
174
175
176
  					continue;
  				}
  
  				tmff->report = report;
  				tmff->ff_field = field;
b27c9590c   Dmitry Torokhov   HID: add support ...
177
178
  				for (i = 0; ff_bits[i] >= 0; i++)
  					set_bit(ff_bits[i], input_dev->ffbit);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
179

b27c9590c   Dmitry Torokhov   HID: add support ...
180
  				break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
181

b27c9590c   Dmitry Torokhov   HID: add support ...
182
  			default:
4291ee305   Joe Perches   HID: Add and use ...
183
184
185
  				hid_warn(hid, "ignoring unknown output usage %08x
  ",
  					 field->usage[0].hid);
b27c9590c   Dmitry Torokhov   HID: add support ...
186
  				continue;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
187
  			}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
188
189
  		}
  	}
b27c9590c   Dmitry Torokhov   HID: add support ...
190
  	if (!tmff->report) {
4291ee305   Joe Perches   HID: Add and use ...
191
192
  		hid_err(hid, "can't find FF field in output reports
  ");
b27c9590c   Dmitry Torokhov   HID: add support ...
193
194
  		error = -ENODEV;
  		goto fail;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
195
  	}
10e41a711   Jiri Slaby   HID: move thrustm...
196
  	error = input_ff_create_memless(input_dev, tmff, tmff_play);
b27c9590c   Dmitry Torokhov   HID: add support ...
197
198
  	if (error)
  		goto fail;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
199

4291ee305   Joe Perches   HID: Add and use ...
200
201
  	hid_info(hid, "force feedback for ThrustMaster devices by Zinx Verituse <zinx@epicsol.org>
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
202
  	return 0;
b27c9590c   Dmitry Torokhov   HID: add support ...
203

10e41a711   Jiri Slaby   HID: move thrustm...
204
  fail:
b27c9590c   Dmitry Torokhov   HID: add support ...
205
206
  	kfree(tmff);
  	return error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
207
  }
0f6f4319a   Jiri Kosina   HID: fix hid-ff d...
208
209
210
211
212
213
  #else
  static inline int tmff_init(struct hid_device *hid, const signed short *ff_bits)
  {
  	return 0;
  }
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
214

10e41a711   Jiri Slaby   HID: move thrustm...
215
216
217
218
219
220
  static int tm_probe(struct hid_device *hdev, const struct hid_device_id *id)
  {
  	int ret;
  
  	ret = hid_parse(hdev);
  	if (ret) {
4291ee305   Joe Perches   HID: Add and use ...
221
222
  		hid_err(hdev, "parse failed
  ");
10e41a711   Jiri Slaby   HID: move thrustm...
223
224
225
226
227
  		goto err;
  	}
  
  	ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF);
  	if (ret) {
4291ee305   Joe Perches   HID: Add and use ...
228
229
  		hid_err(hdev, "hw start failed
  ");
10e41a711   Jiri Slaby   HID: move thrustm...
230
231
232
233
234
235
236
237
238
239
240
241
242
  		goto err;
  	}
  
  	tmff_init(hdev, (void *)id->driver_data);
  
  	return 0;
  err:
  	return ret;
  }
  
  static const struct hid_device_id tm_devices[] = {
  	{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb300),
  		.driver_data = (unsigned long)ff_rumble },
7a84b1336   Ruben Aos Garralda   HID: add rumble s...
243
244
245
246
247
  	{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb304),   /* FireStorm Dual Power 2 (and 3) */
  		.driver_data = (unsigned long)ff_rumble },
  	{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb323),   /* Dual Trigger 3-in-1 (PC Mode) */
  		.driver_data = (unsigned long)ff_rumble },
  	{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb324),   /* Dual Trigger 3-in-1 (PS3 Mode) */
10e41a711   Jiri Slaby   HID: move thrustm...
248
249
250
  		.driver_data = (unsigned long)ff_rumble },
  	{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb651),	/* FGT Rumble Force Wheel */
  		.driver_data = (unsigned long)ff_rumble },
3ee8f0a2b   Markus Rathgeb   HID: Add RGT Clut...
251
252
  	{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb653),	/* RGT Force Feedback CLUTCH Raging Wheel */
  		.driver_data = (unsigned long)ff_joystick },
10e41a711   Jiri Slaby   HID: move thrustm...
253
254
  	{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb654),	/* FGT Force Feedback Wheel */
  		.driver_data = (unsigned long)ff_joystick },
d65c3768a   Simon Wood   HID: add support ...
255
256
  	{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb65a),	/* F430 Force Feedback Wheel */
  		.driver_data = (unsigned long)ff_joystick },
10e41a711   Jiri Slaby   HID: move thrustm...
257
258
259
260
261
262
263
264
265
  	{ }
  };
  MODULE_DEVICE_TABLE(hid, tm_devices);
  
  static struct hid_driver tm_driver = {
  	.name = "thrustmaster",
  	.id_table = tm_devices,
  	.probe = tm_probe,
  };
a24f423bd   Peter Huewe   HID: adding __ini...
266
  static int __init tm_init(void)
10e41a711   Jiri Slaby   HID: move thrustm...
267
268
269
  {
  	return hid_register_driver(&tm_driver);
  }
a24f423bd   Peter Huewe   HID: adding __ini...
270
  static void __exit tm_exit(void)
10e41a711   Jiri Slaby   HID: move thrustm...
271
272
273
274
275
276
277
  {
  	hid_unregister_driver(&tm_driver);
  }
  
  module_init(tm_init);
  module_exit(tm_exit);
  MODULE_LICENSE("GPL");