Commit | Line | Data |
---|---|---|
0c3910c2 SC |
1 | /* |
2 | * HID driver for eGalax dual-touch panels | |
3 | * | |
4 | * Copyright (c) 2010 Stephane Chatty <chatty@enac.fr> | |
f7bc8046 HR |
5 | * Copyright (c) 2010 Henrik Rydberg <rydberg@euromail.se> |
6 | * Copyright (c) 2010 Canonical, Ltd. | |
0c3910c2 SC |
7 | * |
8 | */ | |
9 | ||
10 | /* | |
11 | * This program is free software; you can redistribute it and/or modify it | |
12 | * under the terms of the GNU General Public License as published by the Free | |
13 | * Software Foundation; either version 2 of the License, or (at your option) | |
14 | * any later version. | |
15 | */ | |
16 | ||
17 | #include <linux/device.h> | |
18 | #include <linux/hid.h> | |
19 | #include <linux/module.h> | |
20 | #include <linux/usb.h> | |
f7bc8046 | 21 | #include <linux/input/mt.h> |
c872b0fc | 22 | #include <linux/slab.h> |
0c3910c2 SC |
23 | #include "usbhid/usbhid.h" |
24 | ||
25 | MODULE_AUTHOR("Stephane Chatty <chatty@enac.fr>"); | |
26 | MODULE_DESCRIPTION("eGalax dual-touch panel"); | |
27 | MODULE_LICENSE("GPL"); | |
28 | ||
29 | #include "hid-ids.h" | |
30 | ||
f7bc8046 HR |
31 | #define MAX_SLOTS 2 |
32 | ||
17c76068 HR |
33 | /* estimated signal-to-noise ratios */ |
34 | #define SN_MOVE 4096 | |
35 | #define SN_PRESSURE 32 | |
36 | ||
0c3910c2 | 37 | struct egalax_data { |
f7bc8046 HR |
38 | int valid; |
39 | int slot; | |
40 | int touch; | |
41 | int x, y, z; | |
0c3910c2 SC |
42 | }; |
43 | ||
4a864183 HR |
44 | static void set_abs(struct input_dev *input, unsigned int code, |
45 | struct hid_field *field, int snratio) | |
46 | { | |
47 | int fmin = field->logical_minimum; | |
48 | int fmax = field->logical_maximum; | |
49 | int fuzz = snratio ? (fmax - fmin) / snratio : 0; | |
50 | input_set_abs_params(input, code, fmin, fmax, fuzz, 0); | |
51 | } | |
52 | ||
0c3910c2 SC |
53 | static int egalax_input_mapping(struct hid_device *hdev, struct hid_input *hi, |
54 | struct hid_field *field, struct hid_usage *usage, | |
55 | unsigned long **bit, int *max) | |
56 | { | |
4a864183 HR |
57 | struct input_dev *input = hi->input; |
58 | ||
0c3910c2 SC |
59 | switch (usage->hid & HID_USAGE_PAGE) { |
60 | ||
61 | case HID_UP_GENDESK: | |
62 | switch (usage->hid) { | |
63 | case HID_GD_X: | |
b88cbd3a | 64 | field->logical_maximum = 32760; |
0c3910c2 SC |
65 | hid_map_usage(hi, usage, bit, max, |
66 | EV_ABS, ABS_MT_POSITION_X); | |
17c76068 | 67 | set_abs(input, ABS_MT_POSITION_X, field, SN_MOVE); |
0c3910c2 | 68 | /* touchscreen emulation */ |
17c76068 | 69 | set_abs(input, ABS_X, field, SN_MOVE); |
0c3910c2 SC |
70 | return 1; |
71 | case HID_GD_Y: | |
b88cbd3a | 72 | field->logical_maximum = 32760; |
0c3910c2 SC |
73 | hid_map_usage(hi, usage, bit, max, |
74 | EV_ABS, ABS_MT_POSITION_Y); | |
17c76068 | 75 | set_abs(input, ABS_MT_POSITION_Y, field, SN_MOVE); |
0c3910c2 | 76 | /* touchscreen emulation */ |
17c76068 | 77 | set_abs(input, ABS_Y, field, SN_MOVE); |
0c3910c2 SC |
78 | return 1; |
79 | } | |
80 | return 0; | |
81 | ||
82 | case HID_UP_DIGITIZER: | |
83 | switch (usage->hid) { | |
84 | case HID_DG_TIPSWITCH: | |
85 | /* touchscreen emulation */ | |
86 | hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_TOUCH); | |
4a864183 | 87 | input_set_capability(input, EV_KEY, BTN_TOUCH); |
0c3910c2 SC |
88 | return 1; |
89 | case HID_DG_INRANGE: | |
90 | case HID_DG_CONFIDENCE: | |
91 | case HID_DG_CONTACTCOUNT: | |
92 | case HID_DG_CONTACTMAX: | |
93 | return -1; | |
94 | case HID_DG_CONTACTID: | |
f7bc8046 | 95 | input_mt_init_slots(input, MAX_SLOTS); |
0c3910c2 SC |
96 | return 1; |
97 | case HID_DG_TIPPRESSURE: | |
80a469e4 | 98 | field->logical_minimum = 0; |
0c3910c2 SC |
99 | hid_map_usage(hi, usage, bit, max, |
100 | EV_ABS, ABS_MT_PRESSURE); | |
17c76068 | 101 | set_abs(input, ABS_MT_PRESSURE, field, SN_PRESSURE); |
f5166110 | 102 | /* touchscreen emulation */ |
17c76068 | 103 | set_abs(input, ABS_PRESSURE, field, SN_PRESSURE); |
0c3910c2 SC |
104 | return 1; |
105 | } | |
106 | return 0; | |
107 | } | |
108 | ||
109 | /* ignore others (from other reports we won't get anyway) */ | |
110 | return -1; | |
111 | } | |
112 | ||
113 | static int egalax_input_mapped(struct hid_device *hdev, struct hid_input *hi, | |
114 | struct hid_field *field, struct hid_usage *usage, | |
115 | unsigned long **bit, int *max) | |
116 | { | |
4a864183 | 117 | /* tell hid-input to skip setup of these event types */ |
0c3910c2 | 118 | if (usage->type == EV_KEY || usage->type == EV_ABS) |
4a864183 HR |
119 | set_bit(usage->type, hi->input->evbit); |
120 | return -1; | |
0c3910c2 SC |
121 | } |
122 | ||
123 | /* | |
124 | * this function is called when a whole finger has been parsed, | |
125 | * so that it can decide what to send to the input layer. | |
126 | */ | |
127 | static void egalax_filter_event(struct egalax_data *td, struct input_dev *input) | |
128 | { | |
f7bc8046 HR |
129 | input_mt_slot(input, td->slot); |
130 | input_mt_report_slot_state(input, MT_TOOL_FINGER, td->touch); | |
131 | if (td->touch) { | |
132 | input_event(input, EV_ABS, ABS_MT_POSITION_X, td->x); | |
133 | input_event(input, EV_ABS, ABS_MT_POSITION_Y, td->y); | |
0c3910c2 | 134 | input_event(input, EV_ABS, ABS_MT_PRESSURE, td->z); |
0c3910c2 | 135 | } |
f7bc8046 | 136 | input_mt_report_pointer_emulation(input, true); |
0c3910c2 SC |
137 | } |
138 | ||
0c3910c2 SC |
139 | static int egalax_event(struct hid_device *hid, struct hid_field *field, |
140 | struct hid_usage *usage, __s32 value) | |
141 | { | |
142 | struct egalax_data *td = hid_get_drvdata(hid); | |
143 | ||
83e44918 CB |
144 | /* Note, eGalax has two product lines: the first is resistive and |
145 | * uses a standard parallel multitouch protocol (product ID == | |
146 | * 48xx). The second is capacitive and uses an unusual "serial" | |
147 | * protocol with a different message for each multitouch finger | |
f7bc8046 | 148 | * (product ID == 72xx). |
83e44918 | 149 | */ |
0c3910c2 SC |
150 | if (hid->claimed & HID_CLAIMED_INPUT) { |
151 | struct input_dev *input = field->hidinput->input; | |
152 | ||
153 | switch (usage->hid) { | |
154 | case HID_DG_INRANGE: | |
f7bc8046 HR |
155 | td->valid = value; |
156 | break; | |
0c3910c2 SC |
157 | case HID_DG_CONFIDENCE: |
158 | /* avoid interference from generic hidinput handling */ | |
159 | break; | |
160 | case HID_DG_TIPSWITCH: | |
f7bc8046 | 161 | td->touch = value; |
0c3910c2 SC |
162 | break; |
163 | case HID_DG_TIPPRESSURE: | |
164 | td->z = value; | |
165 | break; | |
166 | case HID_DG_CONTACTID: | |
f7bc8046 | 167 | td->slot = clamp_val(value, 0, MAX_SLOTS - 1); |
0c3910c2 SC |
168 | break; |
169 | case HID_GD_X: | |
170 | td->x = value; | |
171 | break; | |
172 | case HID_GD_Y: | |
173 | td->y = value; | |
174 | /* this is the last field in a finger */ | |
f7bc8046 HR |
175 | if (td->valid) |
176 | egalax_filter_event(td, input); | |
0c3910c2 SC |
177 | break; |
178 | case HID_DG_CONTACTCOUNT: | |
179 | /* touch emulation: this is the last field in a frame */ | |
0c3910c2 SC |
180 | break; |
181 | ||
182 | default: | |
183 | /* fallback to the generic hidinput handling */ | |
184 | return 0; | |
185 | } | |
186 | } | |
187 | ||
188 | /* we have handled the hidinput part, now remains hiddev */ | |
189 | if (hid->claimed & HID_CLAIMED_HIDDEV && hid->hiddev_hid_event) | |
190 | hid->hiddev_hid_event(hid, field, usage, value); | |
191 | ||
192 | return 1; | |
193 | } | |
194 | ||
195 | static int egalax_probe(struct hid_device *hdev, const struct hid_device_id *id) | |
196 | { | |
197 | int ret; | |
198 | struct egalax_data *td; | |
199 | struct hid_report *report; | |
200 | ||
5a39ce5b | 201 | td = kzalloc(sizeof(struct egalax_data), GFP_KERNEL); |
0c3910c2 | 202 | if (!td) { |
4291ee30 | 203 | hid_err(hdev, "cannot allocate eGalax data\n"); |
0c3910c2 SC |
204 | return -ENOMEM; |
205 | } | |
206 | hid_set_drvdata(hdev, td); | |
207 | ||
208 | ret = hid_parse(hdev); | |
209 | if (ret) | |
210 | goto end; | |
211 | ||
212 | ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); | |
213 | if (ret) | |
214 | goto end; | |
215 | ||
216 | report = hdev->report_enum[HID_FEATURE_REPORT].report_id_hash[5]; | |
217 | if (report) { | |
218 | report->field[0]->value[0] = 2; | |
219 | usbhid_submit_report(hdev, report, USB_DIR_OUT); | |
220 | } | |
221 | ||
222 | end: | |
223 | if (ret) | |
224 | kfree(td); | |
225 | ||
226 | return ret; | |
227 | } | |
228 | ||
229 | static void egalax_remove(struct hid_device *hdev) | |
230 | { | |
231 | hid_hw_stop(hdev); | |
232 | kfree(hid_get_drvdata(hdev)); | |
233 | hid_set_drvdata(hdev, NULL); | |
234 | } | |
235 | ||
236 | static const struct hid_device_id egalax_devices[] = { | |
237 | { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, | |
238 | USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH) }, | |
83e44918 CB |
239 | { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, |
240 | USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH1) }, | |
4e93db23 HR |
241 | { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, |
242 | USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH2) }, | |
28906ad6 RN |
243 | { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, |
244 | USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH3) }, | |
4dd295a7 AR |
245 | { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, |
246 | USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH4) }, | |
0c3910c2 SC |
247 | { } |
248 | }; | |
249 | MODULE_DEVICE_TABLE(hid, egalax_devices); | |
250 | ||
251 | static const struct hid_usage_id egalax_grabbed_usages[] = { | |
252 | { HID_ANY_ID, HID_ANY_ID, HID_ANY_ID }, | |
253 | { HID_ANY_ID - 1, HID_ANY_ID - 1, HID_ANY_ID - 1} | |
254 | }; | |
255 | ||
256 | static struct hid_driver egalax_driver = { | |
257 | .name = "egalax-touch", | |
258 | .id_table = egalax_devices, | |
259 | .probe = egalax_probe, | |
260 | .remove = egalax_remove, | |
261 | .input_mapping = egalax_input_mapping, | |
262 | .input_mapped = egalax_input_mapped, | |
263 | .usage_table = egalax_grabbed_usages, | |
264 | .event = egalax_event, | |
265 | }; | |
266 | ||
267 | static int __init egalax_init(void) | |
268 | { | |
269 | return hid_register_driver(&egalax_driver); | |
270 | } | |
271 | ||
272 | static void __exit egalax_exit(void) | |
273 | { | |
274 | hid_unregister_driver(&egalax_driver); | |
275 | } | |
276 | ||
277 | module_init(egalax_init); | |
278 | module_exit(egalax_exit); | |
279 |