Blame view

drivers/hid/hid-axff.c 4.24 KB
1a59d1b8e   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
c0dbcc33c   Sergei Kolzun   HID: add ACRUX ga...
2
3
4
5
6
7
8
9
  /*
   * Force feedback support for ACRUX game controllers
   *
   * From what I have gathered, these devices are mass produced in China
   * by several vendors. They often share the same design as the original
   * Xbox 360 controller.
   *
   * 1a34:0802 "ACRUX USB GAMEPAD 8116"
b55ebc27b   Sergei Kolzun   HID: ACRUX - hand...
10
   *  - tested with an EXEQ EQ-PCU-02090 game controller.
c0dbcc33c   Sergei Kolzun   HID: add ACRUX ga...
11
12
13
14
15
   *
   * Copyright (c) 2010 Sergei Kolzun <x0r@dv-life.ru>
   */
  
  /*
c0dbcc33c   Sergei Kolzun   HID: add ACRUX ga...
16
17
18
19
   */
  
  #include <linux/input.h>
  #include <linux/slab.h>
c0dbcc33c   Sergei Kolzun   HID: add ACRUX ga...
20
  #include <linux/hid.h>
8f86a2c3c   Paul Gortmaker   hid: Add module.h...
21
  #include <linux/module.h>
c0dbcc33c   Sergei Kolzun   HID: add ACRUX ga...
22
23
  
  #include "hid-ids.h"
0ae438109   Dmitry Torokhov   HID: ACRUX - acti...
24
25
  
  #ifdef CONFIG_HID_ACRUX_FF
c0dbcc33c   Sergei Kolzun   HID: add ACRUX ga...
26
27
28
29
30
31
32
33
34
  
  struct axff_device {
  	struct hid_report *report;
  };
  
  static int axff_play(struct input_dev *dev, void *data, struct ff_effect *effect)
  {
  	struct hid_device *hid = input_get_drvdata(dev);
  	struct axff_device *axff = data;
b55ebc27b   Sergei Kolzun   HID: ACRUX - hand...
35
36
  	struct hid_report *report = axff->report;
  	int field_count = 0;
c0dbcc33c   Sergei Kolzun   HID: add ACRUX ga...
37
  	int left, right;
b55ebc27b   Sergei Kolzun   HID: ACRUX - hand...
38
  	int i, j;
c0dbcc33c   Sergei Kolzun   HID: add ACRUX ga...
39
40
41
42
43
44
45
46
  
  	left = effect->u.rumble.strong_magnitude;
  	right = effect->u.rumble.weak_magnitude;
  
  	dbg_hid("called with 0x%04x 0x%04x", left, right);
  
  	left = left * 0xff / 0xffff;
  	right = right * 0xff / 0xffff;
b55ebc27b   Sergei Kolzun   HID: ACRUX - hand...
47
48
49
50
51
52
53
  	for (i = 0; i < report->maxfield; i++) {
  		for (j = 0; j < report->field[i]->report_count; j++) {
  			report->field[i]->value[j] =
  				field_count % 2 ? right : left;
  			field_count++;
  		}
  	}
c0dbcc33c   Sergei Kolzun   HID: add ACRUX ga...
54
  	dbg_hid("running with 0x%02x 0x%02x", left, right);
d88142725   Benjamin Tissoires   HID: use hid_hw_r...
55
  	hid_hw_request(hid, axff->report, HID_REQ_SET_REPORT);
c0dbcc33c   Sergei Kolzun   HID: add ACRUX ga...
56
57
58
59
60
61
62
63
  
  	return 0;
  }
  
  static int axff_init(struct hid_device *hid)
  {
  	struct axff_device *axff;
  	struct hid_report *report;
d9d4b1e46   Alan Stern   HID: Fix assumpti...
64
  	struct hid_input *hidinput;
c0dbcc33c   Sergei Kolzun   HID: add ACRUX ga...
65
  	struct list_head *report_list =&hid->report_enum[HID_OUTPUT_REPORT].report_list;
d9d4b1e46   Alan Stern   HID: Fix assumpti...
66
  	struct input_dev *dev;
b55ebc27b   Sergei Kolzun   HID: ACRUX - hand...
67
68
  	int field_count = 0;
  	int i, j;
c0dbcc33c   Sergei Kolzun   HID: add ACRUX ga...
69
  	int error;
d9d4b1e46   Alan Stern   HID: Fix assumpti...
70
71
72
73
74
75
76
  	if (list_empty(&hid->inputs)) {
  		hid_err(hid, "no inputs found
  ");
  		return -ENODEV;
  	}
  	hidinput = list_first_entry(&hid->inputs, struct hid_input, list);
  	dev = hidinput->input;
c0dbcc33c   Sergei Kolzun   HID: add ACRUX ga...
77
  	if (list_empty(report_list)) {
4291ee305   Joe Perches   HID: Add and use ...
78
79
  		hid_err(hid, "no output reports found
  ");
c0dbcc33c   Sergei Kolzun   HID: add ACRUX ga...
80
81
82
83
  		return -ENODEV;
  	}
  
  	report = list_first_entry(report_list, struct hid_report, list);
b55ebc27b   Sergei Kolzun   HID: ACRUX - hand...
84
85
86
87
88
89
  	for (i = 0; i < report->maxfield; i++) {
  		for (j = 0; j < report->field[i]->report_count; j++) {
  			report->field[i]->value[j] = 0x00;
  			field_count++;
  		}
  	}
c0dbcc33c   Sergei Kolzun   HID: add ACRUX ga...
90

e17f5d766   Tristan Rice   HID: enable Mayfl...
91
  	if (field_count < 4 && hid->product != 0xf705) {
b55ebc27b   Sergei Kolzun   HID: ACRUX - hand...
92
93
94
  		hid_err(hid, "not enough fields in the report: %d
  ",
  			field_count);
c0dbcc33c   Sergei Kolzun   HID: add ACRUX ga...
95
96
97
98
99
100
101
102
103
104
105
106
107
108
  		return -ENODEV;
  	}
  
  	axff = kzalloc(sizeof(struct axff_device), GFP_KERNEL);
  	if (!axff)
  		return -ENOMEM;
  
  	set_bit(FF_RUMBLE, dev->ffbit);
  
  	error = input_ff_create_memless(dev, axff, axff_play);
  	if (error)
  		goto err_free_mem;
  
  	axff->report = report;
d88142725   Benjamin Tissoires   HID: use hid_hw_r...
109
  	hid_hw_request(hid, axff->report, HID_REQ_SET_REPORT);
c0dbcc33c   Sergei Kolzun   HID: add ACRUX ga...
110

b55ebc27b   Sergei Kolzun   HID: ACRUX - hand...
111
112
  	hid_info(hid, "Force Feedback for ACRUX game controllers by Sergei Kolzun <x0r@dv-life.ru>
  ");
c0dbcc33c   Sergei Kolzun   HID: add ACRUX ga...
113
114
115
116
117
118
119
  
  	return 0;
  
  err_free_mem:
  	kfree(axff);
  	return error;
  }
0ae438109   Dmitry Torokhov   HID: ACRUX - acti...
120
121
122
123
124
125
  #else
  static inline int axff_init(struct hid_device *hid)
  {
  	return 0;
  }
  #endif
c0dbcc33c   Sergei Kolzun   HID: add ACRUX ga...
126
127
128
129
  
  static int ax_probe(struct hid_device *hdev, const struct hid_device_id *id)
  {
  	int error;
4291ee305   Joe Perches   HID: Add and use ...
130
131
  	dev_dbg(&hdev->dev, "ACRUX HID hardware probe...
  ");
c0dbcc33c   Sergei Kolzun   HID: add ACRUX ga...
132
133
134
  
  	error = hid_parse(hdev);
  	if (error) {
4291ee305   Joe Perches   HID: Add and use ...
135
136
  		hid_err(hdev, "parse failed
  ");
c0dbcc33c   Sergei Kolzun   HID: add ACRUX ga...
137
138
139
140
141
  		return error;
  	}
  
  	error = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF);
  	if (error) {
4291ee305   Joe Perches   HID: Add and use ...
142
143
  		hid_err(hdev, "hw start failed
  ");
c0dbcc33c   Sergei Kolzun   HID: add ACRUX ga...
144
145
146
147
148
149
150
151
152
  		return error;
  	}
  
  	error = axff_init(hdev);
  	if (error) {
  		/*
  		 * Do not fail device initialization completely as device
  		 * may still be partially operable, just warn.
  		 */
4291ee305   Joe Perches   HID: Add and use ...
153
  		hid_warn(hdev,
c0dbcc33c   Sergei Kolzun   HID: add ACRUX ga...
154
155
156
157
  			 "Failed to enable force feedback support, error: %d
  ",
  			 error);
  	}
0ae438109   Dmitry Torokhov   HID: ACRUX - acti...
158
159
160
161
162
163
164
165
  	/*
  	 * We need to start polling device right away, otherwise
  	 * it will go into a coma.
  	 */
  	error = hid_hw_open(hdev);
  	if (error) {
  		dev_err(&hdev->dev, "hw open failed
  ");
b30d89d10   Axel Lin   HID: ACRUX - add ...
166
  		hid_hw_stop(hdev);
0ae438109   Dmitry Torokhov   HID: ACRUX - acti...
167
168
  		return error;
  	}
c0dbcc33c   Sergei Kolzun   HID: add ACRUX ga...
169
170
  	return 0;
  }
0ae438109   Dmitry Torokhov   HID: ACRUX - acti...
171
172
173
174
175
  static void ax_remove(struct hid_device *hdev)
  {
  	hid_hw_close(hdev);
  	hid_hw_stop(hdev);
  }
c0dbcc33c   Sergei Kolzun   HID: add ACRUX ga...
176
177
  static const struct hid_device_id ax_devices[] = {
  	{ HID_USB_DEVICE(USB_VENDOR_ID_ACRUX, 0x0802), },
e17f5d766   Tristan Rice   HID: enable Mayfl...
178
  	{ HID_USB_DEVICE(USB_VENDOR_ID_ACRUX, 0xf705), },
c0dbcc33c   Sergei Kolzun   HID: add ACRUX ga...
179
180
181
182
183
  	{ }
  };
  MODULE_DEVICE_TABLE(hid, ax_devices);
  
  static struct hid_driver ax_driver = {
0ae438109   Dmitry Torokhov   HID: ACRUX - acti...
184
185
186
187
  	.name		= "acrux",
  	.id_table	= ax_devices,
  	.probe		= ax_probe,
  	.remove		= ax_remove,
c0dbcc33c   Sergei Kolzun   HID: add ACRUX ga...
188
  };
f425458ea   H Hartley Sweeten   HID: Use module_h...
189
  module_hid_driver(ax_driver);
c0dbcc33c   Sergei Kolzun   HID: add ACRUX ga...
190
191
192
193
  
  MODULE_AUTHOR("Sergei Kolzun");
  MODULE_DESCRIPTION("Force feedback support for ACRUX game controllers");
  MODULE_LICENSE("GPL");