Blame view

drivers/hid/hid-gaff.c 4.75 KB
42859e0bd   Lukasz Lubojanski   HID: force feedba...
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
30
  /*
   *  Force feedback support for GreenAsia (Product ID 0x12) based devices
   *
   *  The devices are distributed under various names and the same USB device ID
   *  can be used in many game controllers.
   *
   *
   *  0e8f:0012 "GreenAsia Inc.    USB Joystick     "
   *   - tested with MANTA Warior MM816 and SpeedLink Strike2 SL-6635.
   *
   *  Copyright (c) 2008 Lukasz Lubojanski <lukasz@lubojanski.info>
   */
  
  /*
   * 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
   */
  
  #include <linux/input.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
31
  #include <linux/slab.h>
42859e0bd   Lukasz Lubojanski   HID: force feedba...
32
33
  #include <linux/usb.h>
  #include <linux/hid.h>
8f86a2c3c   Paul Gortmaker   hid: Add module.h...
34
  #include <linux/module.h>
42859e0bd   Lukasz Lubojanski   HID: force feedba...
35
  #include "hid-ids.h"
0f6f4319a   Jiri Kosina   HID: fix hid-ff d...
36
37
  
  #ifdef CONFIG_GREENASIA_FF
42859e0bd   Lukasz Lubojanski   HID: force feedba...
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
  #include "usbhid/usbhid.h"
  
  struct gaff_device {
  	struct hid_report *report;
  };
  
  static int hid_gaff_play(struct input_dev *dev, void *data,
  			 struct ff_effect *effect)
  {
  	struct hid_device *hid = input_get_drvdata(dev);
  	struct gaff_device *gaff = data;
  	int left, right;
  
  	left = effect->u.rumble.strong_magnitude;
  	right = effect->u.rumble.weak_magnitude;
  
  	dbg_hid("called with 0x%04x 0x%04x", left, right);
  
  	left = left * 0xfe / 0xffff;
  	right = right * 0xfe / 0xffff;
  
  	gaff->report->field[0]->value[0] = 0x51;
  	gaff->report->field[0]->value[1] = 0x0;
  	gaff->report->field[0]->value[2] = right;
  	gaff->report->field[0]->value[3] = 0;
  	gaff->report->field[0]->value[4] = left;
  	gaff->report->field[0]->value[5] = 0;
  	dbg_hid("running with 0x%02x 0x%02x", left, right);
  	usbhid_submit_report(hid, gaff->report, USB_DIR_OUT);
  
  	gaff->report->field[0]->value[0] = 0xfa;
  	gaff->report->field[0]->value[1] = 0xfe;
  	gaff->report->field[0]->value[2] = 0x0;
  	gaff->report->field[0]->value[4] = 0x0;
  
  	usbhid_submit_report(hid, gaff->report, USB_DIR_OUT);
  
  	return 0;
  }
  
  static int gaff_init(struct hid_device *hid)
  {
  	struct gaff_device *gaff;
  	struct hid_report *report;
  	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 list_head *report_ptr = report_list;
  	struct input_dev *dev = hidinput->input;
  	int error;
  
  	if (list_empty(report_list)) {
4291ee305   Joe Perches   HID: Add and use ...
91
92
  		hid_err(hid, "no output reports found
  ");
42859e0bd   Lukasz Lubojanski   HID: force feedba...
93
94
95
96
97
98
99
  		return -ENODEV;
  	}
  
  	report_ptr = report_ptr->next;
  
  	report = list_entry(report_ptr, struct hid_report, list);
  	if (report->maxfield < 1) {
4291ee305   Joe Perches   HID: Add and use ...
100
101
  		hid_err(hid, "no fields in the report
  ");
42859e0bd   Lukasz Lubojanski   HID: force feedba...
102
103
104
105
  		return -ENODEV;
  	}
  
  	if (report->field[0]->report_count < 6) {
4291ee305   Joe Perches   HID: Add and use ...
106
107
  		hid_err(hid, "not enough values in the field
  ");
42859e0bd   Lukasz Lubojanski   HID: force feedba...
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
  		return -ENODEV;
  	}
  
  	gaff = kzalloc(sizeof(struct gaff_device), GFP_KERNEL);
  	if (!gaff)
  		return -ENOMEM;
  
  	set_bit(FF_RUMBLE, dev->ffbit);
  
  	error = input_ff_create_memless(dev, gaff, hid_gaff_play);
  	if (error) {
  		kfree(gaff);
  		return error;
  	}
  
  	gaff->report = report;
  	gaff->report->field[0]->value[0] = 0x51;
  	gaff->report->field[0]->value[1] = 0x00;
  	gaff->report->field[0]->value[2] = 0x00;
  	gaff->report->field[0]->value[3] = 0x00;
  	usbhid_submit_report(hid, gaff->report, USB_DIR_OUT);
  
  	gaff->report->field[0]->value[0] = 0xfa;
  	gaff->report->field[0]->value[1] = 0xfe;
  
  	usbhid_submit_report(hid, gaff->report, USB_DIR_OUT);
4291ee305   Joe Perches   HID: Add and use ...
134
135
  	hid_info(hid, "Force Feedback for GreenAsia 0x12 devices by Lukasz Lubojanski <lukasz@lubojanski.info>
  ");
42859e0bd   Lukasz Lubojanski   HID: force feedba...
136
137
138
  
  	return 0;
  }
0f6f4319a   Jiri Kosina   HID: fix hid-ff d...
139
140
141
142
143
144
  #else
  static inline int gaff_init(struct hid_device *hdev)
  {
  	return 0;
  }
  #endif
42859e0bd   Lukasz Lubojanski   HID: force feedba...
145
146
147
148
149
150
151
152
153
  
  static int ga_probe(struct hid_device *hdev, const struct hid_device_id *id)
  {
  	int ret;
  
  	dev_dbg(&hdev->dev, "Greenasia HID hardware probe...");
  
  	ret = hid_parse(hdev);
  	if (ret) {
4291ee305   Joe Perches   HID: Add and use ...
154
155
  		hid_err(hdev, "parse failed
  ");
42859e0bd   Lukasz Lubojanski   HID: force feedba...
156
157
158
159
160
  		goto err;
  	}
  
  	ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF);
  	if (ret) {
4291ee305   Joe Perches   HID: Add and use ...
161
162
  		hid_err(hdev, "hw start failed
  ");
42859e0bd   Lukasz Lubojanski   HID: force feedba...
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
  		goto err;
  	}
  
  	gaff_init(hdev);
  
  	return 0;
  err:
  	return ret;
  }
  
  static const struct hid_device_id ga_devices[] = {
  	{ HID_USB_DEVICE(USB_VENDOR_ID_GREENASIA, 0x0012),  },
  	{ }
  };
  MODULE_DEVICE_TABLE(hid, ga_devices);
  
  static struct hid_driver ga_driver = {
  	.name = "greenasia",
  	.id_table = ga_devices,
  	.probe = ga_probe,
  };
  
  static int __init ga_init(void)
  {
  	return hid_register_driver(&ga_driver);
  }
  
  static void __exit ga_exit(void)
  {
  	hid_unregister_driver(&ga_driver);
  }
  
  module_init(ga_init);
  module_exit(ga_exit);
  MODULE_LICENSE("GPL");