hid-tmff.c 6.7 KB
Newer Older
L
Linus Torvalds 已提交
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 29
/*
 * 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
 */

J
Jiri Slaby 已提交
30
#include <linux/hid.h>
L
Linus Torvalds 已提交
31 32 33
#include <linux/input.h>
#include <linux/usb.h>

J
Jiri Slaby 已提交
34 35
#include "hid-ids.h"

36 37 38 39 40 41 42 43 44 45
static const signed short ff_rumble[] = {
	FF_RUMBLE,
	-1
};

static const signed short ff_joystick[] = {
	FF_CONSTANT,
	-1
};

46 47 48 49 50 51
#ifdef CONFIG_THRUSTMASTER_FF
#include "usbhid/usbhid.h"

/* Usages for thrustmaster devices I know about */
#define THRUSTMASTER_USAGE_FF	(HID_UP_GENDESK | 0xbb)

L
Linus Torvalds 已提交
52 53
struct tmff_device {
	struct hid_report *report;
54
	struct hid_field *ff_field;
55
};
L
Linus Torvalds 已提交
56

57
/* Changes values from 0 to 0xffff into values from minimum to maximum */
J
Jiri Slaby 已提交
58
static inline int tmff_scale_u16(unsigned int in, int minimum, int maximum)
59 60
{
	int ret;
L
Linus Torvalds 已提交
61

62 63 64 65 66 67 68
	ret = (in * (maximum - minimum) / 0xffff) + minimum;
	if (ret < minimum)
		return minimum;
	if (ret > maximum)
		return maximum;
	return ret;
}
L
Linus Torvalds 已提交
69

70
/* Changes values from -0x80 to 0x7f into values from minimum to maximum */
J
Jiri Slaby 已提交
71
static inline int tmff_scale_s8(int in, int minimum, int maximum)
72 73 74 75 76 77 78 79 80 81 82
{
	int ret;

	ret = (((in + 0x80) * (maximum - minimum)) / 0xff) + minimum;
	if (ret < minimum)
		return minimum;
	if (ret > maximum)
		return maximum;
	return ret;
}

J
Jiri Slaby 已提交
83 84
static int tmff_play(struct input_dev *dev, void *data,
		struct ff_effect *effect)
85
{
86
	struct hid_device *hid = input_get_drvdata(dev);
87
	struct tmff_device *tmff = data;
88 89
	struct hid_field *ff_field = tmff->ff_field;
	int x, y;
90
	int left, right;	/* Rumbling */
L
Linus Torvalds 已提交
91

92 93
	switch (effect->type) {
	case FF_CONSTANT:
J
Jiri Slaby 已提交
94
		x = tmff_scale_s8(effect->u.ramp.start_level,
95 96
					ff_field->logical_minimum,
					ff_field->logical_maximum);
J
Jiri Slaby 已提交
97
		y = tmff_scale_s8(effect->u.ramp.end_level,
98 99 100 101 102 103 104 105 106 107
					ff_field->logical_minimum,
					ff_field->logical_maximum);

		dbg_hid("(x, y)=(%04x, %04x)\n", 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:
J
Jiri Slaby 已提交
108
		left = tmff_scale_u16(effect->u.rumble.weak_magnitude,
109 110
					ff_field->logical_minimum,
					ff_field->logical_maximum);
J
Jiri Slaby 已提交
111
		right = tmff_scale_u16(effect->u.rumble.strong_magnitude,
112 113 114 115 116 117 118 119 120
					ff_field->logical_minimum,
					ff_field->logical_maximum);

		dbg_hid("(left,right)=(%08x, %08x)\n", left, right);
		ff_field->value[0] = left;
		ff_field->value[1] = right;
		usbhid_submit_report(hid, tmff->report, USB_DIR_OUT);
		break;
	}
121 122
	return 0;
}
L
Linus Torvalds 已提交
123

J
Jiri Slaby 已提交
124
static int tmff_init(struct hid_device *hid, const signed short *ff_bits)
L
Linus Torvalds 已提交
125
{
126
	struct tmff_device *tmff;
127 128
	struct hid_report *report;
	struct list_head *report_list;
J
Jiri Slaby 已提交
129 130
	struct hid_input *hidinput = list_entry(hid->inputs.next,
							struct hid_input, list);
131
	struct input_dev *input_dev = hidinput->input;
132
	int error;
133
	int i;
L
Linus Torvalds 已提交
134

135 136
	tmff = kzalloc(sizeof(struct tmff_device), GFP_KERNEL);
	if (!tmff)
L
Linus Torvalds 已提交
137 138 139
		return -ENOMEM;

	/* Find the report to use */
140 141
	report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
	list_for_each_entry(report, report_list, list) {
L
Linus Torvalds 已提交
142 143 144 145 146 147 148 149 150
		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) {
151 152
			case THRUSTMASTER_USAGE_FF:
				if (field->report_count < 2) {
J
Jiri Slaby 已提交
153 154
					dev_warn(&hid->dev, "ignoring FF field "
						"with report_count < 2\n");
155 156
					continue;
				}
L
Linus Torvalds 已提交
157

J
Jiri Slaby 已提交
158 159
				if (field->logical_maximum ==
						field->logical_minimum) {
J
Jiri Slaby 已提交
160 161 162
					dev_warn(&hid->dev, "ignoring FF field "
							"with logical_maximum "
							"== logical_minimum\n");
163 164
					continue;
				}
L
Linus Torvalds 已提交
165

166
				if (tmff->report && tmff->report != report) {
J
Jiri Slaby 已提交
167 168
					dev_warn(&hid->dev, "ignoring FF field "
							"in other report\n");
169 170
					continue;
				}
L
Linus Torvalds 已提交
171

172
				if (tmff->ff_field && tmff->ff_field != field) {
J
Jiri Slaby 已提交
173 174
					dev_warn(&hid->dev, "ignoring "
							"duplicate FF field\n");
175 176 177 178 179 180 181 182
					continue;
				}

				tmff->report = report;
				tmff->ff_field = field;

				for (i = 0; ff_bits[i] >= 0; i++)
					set_bit(ff_bits[i], input_dev->ffbit);
L
Linus Torvalds 已提交
183

184
				break;
L
Linus Torvalds 已提交
185

186
			default:
J
Jiri Slaby 已提交
187 188
				dev_warn(&hid->dev, "ignoring unknown output "
						"usage %08x\n",
J
Jiri Slaby 已提交
189
						field->usage[0].hid);
190
				continue;
L
Linus Torvalds 已提交
191 192 193 194
			}
		}
	}

195
	if (!tmff->report) {
J
Jiri Slaby 已提交
196
		dev_err(&hid->dev, "can't find FF field in output reports\n");
197 198
		error = -ENODEV;
		goto fail;
L
Linus Torvalds 已提交
199 200
	}

J
Jiri Slaby 已提交
201
	error = input_ff_create_memless(input_dev, tmff, tmff_play);
202 203
	if (error)
		goto fail;
L
Linus Torvalds 已提交
204

J
Jiri Slaby 已提交
205 206
	dev_info(&hid->dev, "force feedback for ThrustMaster devices by Zinx "
			"Verituse <zinx@epicsol.org>");
L
Linus Torvalds 已提交
207
	return 0;
208

J
Jiri Slaby 已提交
209
fail:
210 211
	kfree(tmff);
	return error;
L
Linus Torvalds 已提交
212
}
213 214 215 216 217 218
#else
static inline int tmff_init(struct hid_device *hid, const signed short *ff_bits)
{
	return 0;
}
#endif
L
Linus Torvalds 已提交
219

J
Jiri Slaby 已提交
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274
static int tm_probe(struct hid_device *hdev, const struct hid_device_id *id)
{
	int ret;

	ret = hid_parse(hdev);
	if (ret) {
		dev_err(&hdev->dev, "parse failed\n");
		goto err;
	}

	ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF);
	if (ret) {
		dev_err(&hdev->dev, "hw start failed\n");
		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 },
	{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb304),
		.driver_data = (unsigned long)ff_rumble },
	{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb651),	/* FGT Rumble Force Wheel */
		.driver_data = (unsigned long)ff_rumble },
	{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb654),	/* FGT Force Feedback Wheel */
		.driver_data = (unsigned long)ff_joystick },
	{ }
};
MODULE_DEVICE_TABLE(hid, tm_devices);

static struct hid_driver tm_driver = {
	.name = "thrustmaster",
	.id_table = tm_devices,
	.probe = tm_probe,
};

static int tm_init(void)
{
	return hid_register_driver(&tm_driver);
}

static void tm_exit(void)
{
	hid_unregister_driver(&tm_driver);
}

module_init(tm_init);
module_exit(tm_exit);
MODULE_LICENSE("GPL");