Blame view

drivers/hid/hid-sjoy.c 4.99 KB
fac733f02   Jussi Kivilinna   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
  /*
   *  Force feedback support for SmartJoy PLUS PS2->USB adapter
   *
   *  Copyright (c) 2009 Jussi Kivilinna <jussi.kivilinna@mbnet.fi>
   *
   *  Based of hid-pl.c and hid-gaff.c
   *   Copyright (c) 2007, 2009 Anssi Hannula <anssi.hannula@gmail.com>
   *   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
   */
  
  /* #define DEBUG */
  
  #include <linux/input.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
30
  #include <linux/slab.h>
fac733f02   Jussi Kivilinna   HID: force feedba...
31
32
  #include <linux/usb.h>
  #include <linux/hid.h>
8f86a2c3c   Paul Gortmaker   hid: Add module.h...
33
  #include <linux/module.h>
fac733f02   Jussi Kivilinna   HID: force feedba...
34
35
36
37
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
  #include "hid-ids.h"
  
  #ifdef CONFIG_SMARTJOYPLUS_FF
  #include "usbhid/usbhid.h"
  
  struct sjoyff_device {
  	struct hid_report *report;
  };
  
  static int hid_sjoyff_play(struct input_dev *dev, void *data,
  			 struct ff_effect *effect)
  {
  	struct hid_device *hid = input_get_drvdata(dev);
  	struct sjoyff_device *sjoyff = data;
  	u32 left, right;
  
  	left = effect->u.rumble.strong_magnitude;
  	right = effect->u.rumble.weak_magnitude;
  	dev_dbg(&dev->dev, "called with 0x%08x 0x%08x
  ", left, right);
  
  	left = left * 0xff / 0xffff;
  	right = (right != 0); /* on/off only */
  
  	sjoyff->report->field[0]->value[1] = right;
  	sjoyff->report->field[0]->value[2] = left;
  	dev_dbg(&dev->dev, "running with 0x%02x 0x%02x
  ", left, right);
  	usbhid_submit_report(hid, sjoyff->report, USB_DIR_OUT);
  
  	return 0;
  }
  
  static int sjoyff_init(struct hid_device *hid)
  {
  	struct sjoyff_device *sjoyff;
  	struct hid_report *report;
ad395ccad   Sean Young   IHD: Support forc...
71
  	struct hid_input *hidinput;
fac733f02   Jussi Kivilinna   HID: force feedba...
72
73
74
75
76
77
78
  	struct list_head *report_list =
  			&hid->report_enum[HID_OUTPUT_REPORT].report_list;
  	struct list_head *report_ptr = report_list;
  	struct input_dev *dev;
  	int error;
  
  	if (list_empty(report_list)) {
4291ee305   Joe Perches   HID: Add and use ...
79
80
  		hid_err(hid, "no output reports found
  ");
fac733f02   Jussi Kivilinna   HID: force feedba...
81
82
  		return -ENODEV;
  	}
ad395ccad   Sean Young   IHD: Support forc...
83
84
  	list_for_each_entry(hidinput, &hid->inputs, list) {
  		report_ptr = report_ptr->next;
fac733f02   Jussi Kivilinna   HID: force feedba...
85

ad395ccad   Sean Young   IHD: Support forc...
86
87
88
89
90
  		if (report_ptr == report_list) {
  			hid_err(hid, "required output report is missing
  ");
  			return -ENODEV;
  		}
fac733f02   Jussi Kivilinna   HID: force feedba...
91

ad395ccad   Sean Young   IHD: Support forc...
92
93
94
95
96
97
  		report = list_entry(report_ptr, struct hid_report, list);
  		if (report->maxfield < 1) {
  			hid_err(hid, "no fields in the report
  ");
  			return -ENODEV;
  		}
fac733f02   Jussi Kivilinna   HID: force feedba...
98

ad395ccad   Sean Young   IHD: Support forc...
99
100
101
102
103
  		if (report->field[0]->report_count < 3) {
  			hid_err(hid, "not enough values in the field
  ");
  			return -ENODEV;
  		}
fac733f02   Jussi Kivilinna   HID: force feedba...
104

ad395ccad   Sean Young   IHD: Support forc...
105
106
107
  		sjoyff = kzalloc(sizeof(struct sjoyff_device), GFP_KERNEL);
  		if (!sjoyff)
  			return -ENOMEM;
fac733f02   Jussi Kivilinna   HID: force feedba...
108

ad395ccad   Sean Young   IHD: Support forc...
109
  		dev = hidinput->input;
fac733f02   Jussi Kivilinna   HID: force feedba...
110

ad395ccad   Sean Young   IHD: Support forc...
111
  		set_bit(FF_RUMBLE, dev->ffbit);
fac733f02   Jussi Kivilinna   HID: force feedba...
112

ad395ccad   Sean Young   IHD: Support forc...
113
114
115
116
117
  		error = input_ff_create_memless(dev, sjoyff, hid_sjoyff_play);
  		if (error) {
  			kfree(sjoyff);
  			return error;
  		}
fac733f02   Jussi Kivilinna   HID: force feedba...
118

ad395ccad   Sean Young   IHD: Support forc...
119
120
121
122
123
124
  		sjoyff->report = report;
  		sjoyff->report->field[0]->value[0] = 0x01;
  		sjoyff->report->field[0]->value[1] = 0x00;
  		sjoyff->report->field[0]->value[2] = 0x00;
  		usbhid_submit_report(hid, sjoyff->report, USB_DIR_OUT);
  	}
fac733f02   Jussi Kivilinna   HID: force feedba...
125

4291ee305   Joe Perches   HID: Add and use ...
126
127
  	hid_info(hid, "Force feedback for SmartJoy PLUS PS2/USB adapter
  ");
fac733f02   Jussi Kivilinna   HID: force feedba...
128
129
130
131
132
133
134
135
136
137
138
139
140
  
  	return 0;
  }
  #else
  static inline int sjoyff_init(struct hid_device *hid)
  {
  	return 0;
  }
  #endif
  
  static int sjoy_probe(struct hid_device *hdev, const struct hid_device_id *id)
  {
  	int ret;
ad395ccad   Sean Young   IHD: Support forc...
141
  	hdev->quirks |= id->driver_data;
fac733f02   Jussi Kivilinna   HID: force feedba...
142
143
  	ret = hid_parse(hdev);
  	if (ret) {
4291ee305   Joe Perches   HID: Add and use ...
144
145
  		hid_err(hdev, "parse failed
  ");
fac733f02   Jussi Kivilinna   HID: force feedba...
146
147
148
149
150
  		goto err;
  	}
  
  	ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF);
  	if (ret) {
4291ee305   Joe Perches   HID: Add and use ...
151
152
  		hid_err(hdev, "hw start failed
  ");
fac733f02   Jussi Kivilinna   HID: force feedba...
153
154
155
156
157
158
159
160
161
162
163
  		goto err;
  	}
  
  	sjoyff_init(hdev);
  
  	return 0;
  err:
  	return ret;
  }
  
  static const struct hid_device_id sjoy_devices[] = {
1bcc20675   Sean Young   HID: Add device I...
164
165
166
167
168
169
170
  	{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SUPER_JOY_BOX_3_PRO) },
  	{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SUPER_DUAL_BOX_PRO),
  		.driver_data = HID_QUIRK_MULTI_INPUT | HID_QUIRK_NOGET |
  			       HID_QUIRK_SKIP_OUTPUT_REPORTS },
  	{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SUPER_JOY_BOX_5_PRO),
  		.driver_data = HID_QUIRK_MULTI_INPUT | HID_QUIRK_NOGET |
  			       HID_QUIRK_SKIP_OUTPUT_REPORTS },
fac733f02   Jussi Kivilinna   HID: force feedba...
171
  	{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_SMARTJOY_PLUS) },
ad395ccad   Sean Young   IHD: Support forc...
172
173
174
  	{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_DUAL_USB_JOYPAD),
  		.driver_data = HID_QUIRK_MULTI_INPUT | HID_QUIRK_NOGET |
  			       HID_QUIRK_SKIP_OUTPUT_REPORTS },
fac733f02   Jussi Kivilinna   HID: force feedba...
175
176
177
178
179
180
181
182
183
  	{ }
  };
  MODULE_DEVICE_TABLE(hid, sjoy_devices);
  
  static struct hid_driver sjoy_driver = {
  	.name = "smartjoyplus",
  	.id_table = sjoy_devices,
  	.probe = sjoy_probe,
  };
a24f423bd   Peter Huewe   HID: adding __ini...
184
  static int __init sjoy_init(void)
fac733f02   Jussi Kivilinna   HID: force feedba...
185
186
187
  {
  	return hid_register_driver(&sjoy_driver);
  }
a24f423bd   Peter Huewe   HID: adding __ini...
188
  static void __exit sjoy_exit(void)
fac733f02   Jussi Kivilinna   HID: force feedba...
189
190
191
192
193
194
195
196
  {
  	hid_unregister_driver(&sjoy_driver);
  }
  
  module_init(sjoy_init);
  module_exit(sjoy_exit);
  MODULE_LICENSE("GPL");
  MODULE_AUTHOR("Jussi Kivilinna");