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#include <linux/hid.h>
27#include <linux/input.h>
28#include <linux/module.h>
29#include <linux/slab.h>
30
31#include "hid-ids.h"
32
33#ifdef CONFIG_HOLTEK_FF
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
71
72
73
74
75
76
77#define HOLTEKFF_MSG_LENGTH 7
78
79static const u8 start_effect_1[] = { 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 };
80static const u8 stop_all4[] = { 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
81static const u8 stop_all6[] = { 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
82
83struct holtekff_device {
84 struct hid_field *field;
85};
86
87static void holtekff_send(struct holtekff_device *holtekff,
88 struct hid_device *hid,
89 const u8 data[HOLTEKFF_MSG_LENGTH])
90{
91 int i;
92
93 for (i = 0; i < HOLTEKFF_MSG_LENGTH; i++) {
94 holtekff->field->value[i] = data[i];
95 }
96
97 dbg_hid("sending %7ph\n", data);
98
99 hid_hw_request(hid, holtekff->field->report, HID_REQ_SET_REPORT);
100}
101
102static int holtekff_play(struct input_dev *dev, void *data,
103 struct ff_effect *effect)
104{
105 struct hid_device *hid = input_get_drvdata(dev);
106 struct holtekff_device *holtekff = data;
107 int left, right;
108
109 u8 buf[HOLTEKFF_MSG_LENGTH] =
110 { 0x01, 0x01, 0xff, 0xff, 0x10, 0xe0, 0x00 };
111
112 left = effect->u.rumble.strong_magnitude;
113 right = effect->u.rumble.weak_magnitude;
114 dbg_hid("called with 0x%04x 0x%04x\n", left, right);
115
116 if (!left && !right) {
117 holtekff_send(holtekff, hid, stop_all6);
118 return 0;
119 }
120
121 if (left)
122 buf[1] |= 0x80;
123 if (right)
124 buf[1] |= 0x40;
125
126
127 buf[6] = min(0xf, (left >> 12) + (right >> 12));
128
129 holtekff_send(holtekff, hid, buf);
130 holtekff_send(holtekff, hid, start_effect_1);
131
132 return 0;
133}
134
135static int holtekff_init(struct hid_device *hid)
136{
137 struct holtekff_device *holtekff;
138 struct hid_report *report;
139 struct hid_input *hidinput = list_entry(hid->inputs.next,
140 struct hid_input, list);
141 struct list_head *report_list =
142 &hid->report_enum[HID_OUTPUT_REPORT].report_list;
143 struct input_dev *dev = hidinput->input;
144 int error;
145
146 if (list_empty(report_list)) {
147 hid_err(hid, "no output report found\n");
148 return -ENODEV;
149 }
150
151 report = list_entry(report_list->next, struct hid_report, list);
152
153 if (report->maxfield < 1 || report->field[0]->report_count != 7) {
154 hid_err(hid, "unexpected output report layout\n");
155 return -ENODEV;
156 }
157
158 holtekff = kzalloc(sizeof(*holtekff), GFP_KERNEL);
159 if (!holtekff)
160 return -ENOMEM;
161
162 set_bit(FF_RUMBLE, dev->ffbit);
163
164 holtekff->field = report->field[0];
165
166
167 holtekff_send(holtekff, hid, stop_all4);
168 holtekff_send(holtekff, hid, stop_all6);
169
170 error = input_ff_create_memless(dev, holtekff, holtekff_play);
171 if (error) {
172 kfree(holtekff);
173 return error;
174 }
175
176 hid_info(hid, "Force feedback for Holtek On Line Grip based devices by Anssi Hannula <anssi.hannula@iki.fi>\n");
177
178 return 0;
179}
180#else
181static inline int holtekff_init(struct hid_device *hid)
182{
183 return 0;
184}
185#endif
186
187static int holtek_probe(struct hid_device *hdev, const struct hid_device_id *id)
188{
189 int ret;
190
191 ret = hid_parse(hdev);
192 if (ret) {
193 hid_err(hdev, "parse failed\n");
194 goto err;
195 }
196
197 ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF);
198 if (ret) {
199 hid_err(hdev, "hw start failed\n");
200 goto err;
201 }
202
203 holtekff_init(hdev);
204
205 return 0;
206err:
207 return ret;
208}
209
210static const struct hid_device_id holtek_devices[] = {
211 { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK, USB_DEVICE_ID_HOLTEK_ON_LINE_GRIP) },
212 { }
213};
214MODULE_DEVICE_TABLE(hid, holtek_devices);
215
216static struct hid_driver holtek_driver = {
217 .name = "holtek",
218 .id_table = holtek_devices,
219 .probe = holtek_probe,
220};
221module_hid_driver(holtek_driver);
222
223MODULE_LICENSE("GPL");
224MODULE_AUTHOR("Anssi Hannula <anssi.hannula@iki.fi>");
225MODULE_DESCRIPTION("Force feedback support for Holtek On Line Grip based devices");
226