Blame view

drivers/hid/hid-lgff.c 4.9 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2
3
4
5
6
7
   * Force feedback support for hid-compliant for some of the devices from
   * Logitech, namely:
   * - WingMan Cordless RumblePad
   * - WingMan Force 3D
   *
   *  Copyright (c) 2002-2004 Johann Deneux
dc76c9121   Anssi Hannula   Input: use new FF...
8
   *  Copyright (c) 2006 Anssi Hannula <anssi.hannula@gmail.com>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
   */
  
  /*
   * 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
   *
   * Should you need to contact me, the author, you can do so by
   * e-mail - mail your message to <johann.deneux@it.uu.se>
   */
4291ee305   Joe Perches   HID: Add and use ...
29
  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
30
  #include <linux/input.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
31
  #include <linux/usb.h>
dde5845a5   Jiri Kosina   [PATCH] Generic H...
32
  #include <linux/hid.h>
606bd0a86   Jiri Slaby   HID: move logitec...
33
34
35
  
  #include "usbhid/usbhid.h"
  #include "hid-lg.h"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
36

f9f852df2   Kay Sievers   Driver core: add ...
37
  struct dev_type {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
38
39
  	u16 idVendor;
  	u16 idProduct;
dc76c9121   Anssi Hannula   Input: use new FF...
40
  	const signed short *ff;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
41
  };
dc76c9121   Anssi Hannula   Input: use new FF...
42
  static const signed short ff_rumble[] = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
43
44
45
  	FF_RUMBLE,
  	-1
  };
dc76c9121   Anssi Hannula   Input: use new FF...
46
  static const signed short ff_joystick[] = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
47
48
49
  	FF_CONSTANT,
  	-1
  };
f0bca4598   Sergey Belyashov   HID: autocenterin...
50
51
52
53
54
  static const signed short ff_joystick_ac[] = {
  	FF_CONSTANT,
  	FF_AUTOCENTER,
  	-1
  };
f9f852df2   Kay Sievers   Driver core: add ...
55
  static const struct dev_type devices[] = {
dc76c9121   Anssi Hannula   Input: use new FF...
56
57
58
  	{ 0x046d, 0xc211, ff_rumble },
  	{ 0x046d, 0xc219, ff_rumble },
  	{ 0x046d, 0xc283, ff_joystick },
f0bca4598   Sergey Belyashov   HID: autocenterin...
59
  	{ 0x046d, 0xc286, ff_joystick_ac },
74f292ca8   Gary Stein   HID: add driver f...
60
  	{ 0x046d, 0xc287, ff_joystick_ac },
fd30ea8c8   Jiri Kosina   HID: add force fe...
61
  	{ 0x046d, 0xc293, ff_joystick },
130b1ab3f   Valentin Zagura   Input: HID - add ...
62
  	{ 0x046d, 0xc295, ff_joystick },
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
63
  };
dc76c9121   Anssi Hannula   Input: use new FF...
64
65
  static int hid_lgff_play(struct input_dev *dev, void *data, struct ff_effect *effect)
  {
e07129858   Dmitry Torokhov   HID: switch to us...
66
  	struct hid_device *hid = input_get_drvdata(dev);
dc76c9121   Anssi Hannula   Input: use new FF...
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
  	struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
  	struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
  	int x, y;
  	unsigned int left, right;
  
  #define CLAMP(x) if (x < 0) x = 0; if (x > 0xff) x = 0xff
  
  	switch (effect->type) {
  	case FF_CONSTANT:
  		x = effect->u.ramp.start_level + 0x7f;	/* 0x7f is center */
  		y = effect->u.ramp.end_level + 0x7f;
  		CLAMP(x);
  		CLAMP(y);
  		report->field[0]->value[0] = 0x51;
  		report->field[0]->value[1] = 0x08;
  		report->field[0]->value[2] = x;
  		report->field[0]->value[3] = y;
58037eb96   Jiri Kosina   HID: make debuggi...
84
85
  		dbg_hid("(x, y)=(%04x, %04x)
  ", x, y);
4916b3a57   Jiri Kosina   [PATCH] Generic H...
86
  		usbhid_submit_report(hid, report, USB_DIR_OUT);
dc76c9121   Anssi Hannula   Input: use new FF...
87
88
89
90
91
92
93
94
95
96
97
98
99
  		break;
  
  	case FF_RUMBLE:
  		right = effect->u.rumble.strong_magnitude;
  		left = effect->u.rumble.weak_magnitude;
  		right = right * 0xff / 0xffff;
  		left = left * 0xff / 0xffff;
  		CLAMP(left);
  		CLAMP(right);
  		report->field[0]->value[0] = 0x42;
  		report->field[0]->value[1] = 0x00;
  		report->field[0]->value[2] = left;
  		report->field[0]->value[3] = right;
58037eb96   Jiri Kosina   HID: make debuggi...
100
101
  		dbg_hid("(left, right)=(%04x, %04x)
  ", left, right);
4916b3a57   Jiri Kosina   [PATCH] Generic H...
102
  		usbhid_submit_report(hid, report, USB_DIR_OUT);
dc76c9121   Anssi Hannula   Input: use new FF...
103
104
105
106
  		break;
  	}
  	return 0;
  }
2bea94db8   Sergey Belyashov   HID: Autocenterin...
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
  static void hid_lgff_set_autocenter(struct input_dev *dev, u16 magnitude)
  {
  	struct hid_device *hid = input_get_drvdata(dev);
  	struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
  	struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
  	__s32 *value = report->field[0]->value;
  	magnitude = (magnitude >> 12) & 0xf;
  	*value++ = 0xfe;
  	*value++ = 0x0d;
  	*value++ = magnitude;   /* clockwise strength */
  	*value++ = magnitude;   /* counter-clockwise strength */
  	*value++ = 0x80;
  	*value++ = 0x00;
  	*value = 0x00;
  	usbhid_submit_report(hid, report, USB_DIR_OUT);
  }
606bd0a86   Jiri Slaby   HID: move logitec...
123
  int lgff_init(struct hid_device* hid)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
124
  {
dc76c9121   Anssi Hannula   Input: use new FF...
125
126
127
128
129
  	struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
  	struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
  	struct input_dev *dev = hidinput->input;
  	struct hid_report *report;
  	struct hid_field *field;
00a8691ca   Dmitry Torokhov   Input: hid-lgff -...
130
  	const signed short *ff_bits = ff_joystick;
dc76c9121   Anssi Hannula   Input: use new FF...
131
  	int error;
00a8691ca   Dmitry Torokhov   Input: hid-lgff -...
132
  	int i;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
133
134
  
  	/* Find the report to use */
dc76c9121   Anssi Hannula   Input: use new FF...
135
  	if (list_empty(report_list)) {
4291ee305   Joe Perches   HID: Add and use ...
136
137
  		hid_err(hid, "No output report found
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
138
139
  		return -1;
  	}
dc76c9121   Anssi Hannula   Input: use new FF...
140

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
141
  	/* Check that the report looks ok */
dc76c9121   Anssi Hannula   Input: use new FF...
142
  	report = list_entry(report_list->next, struct hid_report, list);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
143
144
  	field = report->field[0];
  	if (!field) {
4291ee305   Joe Perches   HID: Add and use ...
145
146
  		hid_err(hid, "NULL field
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
147
148
  		return -1;
  	}
dc76c9121   Anssi Hannula   Input: use new FF...
149
150
151
  	for (i = 0; i < ARRAY_SIZE(devices); i++) {
  		if (dev->id.vendor == devices[i].idVendor &&
  		    dev->id.product == devices[i].idProduct) {
00a8691ca   Dmitry Torokhov   Input: hid-lgff -...
152
  			ff_bits = devices[i].ff;
dc76c9121   Anssi Hannula   Input: use new FF...
153
  			break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
154
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
155
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
156

00a8691ca   Dmitry Torokhov   Input: hid-lgff -...
157
158
  	for (i = 0; ff_bits[i] >= 0; i++)
  		set_bit(ff_bits[i], dev->ffbit);
dc76c9121   Anssi Hannula   Input: use new FF...
159
160
161
  	error = input_ff_create_memless(dev, NULL, hid_lgff_play);
  	if (error)
  		return error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
162

2bea94db8   Sergey Belyashov   HID: Autocenterin...
163
164
  	if ( test_bit(FF_AUTOCENTER, dev->ffbit) )
  		dev->ff->set_autocenter = hid_lgff_set_autocenter;
4291ee305   Joe Perches   HID: Add and use ...
165
166
  	pr_info("Force feedback for Logitech force feedback devices by Johann Deneux <johann.deneux@it.uu.se>
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
167

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
168
169
  	return 0;
  }