diff options
Diffstat (limited to 'drivers/hid/hid-3m-pct.c')
-rw-r--r-- | drivers/hid/hid-3m-pct.c | 305 |
1 files changed, 0 insertions, 305 deletions
diff --git a/drivers/hid/hid-3m-pct.c b/drivers/hid/hid-3m-pct.c deleted file mode 100644 index 5243ae2d3730..000000000000 --- a/drivers/hid/hid-3m-pct.c +++ /dev/null | |||
@@ -1,305 +0,0 @@ | |||
1 | /* | ||
2 | * HID driver for 3M PCT multitouch panels | ||
3 | * | ||
4 | * Copyright (c) 2009-2010 Stephane Chatty <chatty@enac.fr> | ||
5 | * Copyright (c) 2010 Henrik Rydberg <rydberg@euromail.se> | ||
6 | * Copyright (c) 2010 Canonical, Ltd. | ||
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/slab.h> | ||
21 | #include <linux/usb.h> | ||
22 | #include <linux/input/mt.h> | ||
23 | |||
24 | MODULE_AUTHOR("Stephane Chatty <chatty@enac.fr>"); | ||
25 | MODULE_DESCRIPTION("3M PCT multitouch panels"); | ||
26 | MODULE_LICENSE("GPL"); | ||
27 | |||
28 | #include "hid-ids.h" | ||
29 | |||
30 | #define MAX_SLOTS 60 | ||
31 | |||
32 | /* estimated signal-to-noise ratios */ | ||
33 | #define SN_MOVE 2048 | ||
34 | #define SN_WIDTH 128 | ||
35 | |||
36 | struct mmm_finger { | ||
37 | __s32 x, y, w, h; | ||
38 | bool touch, valid; | ||
39 | }; | ||
40 | |||
41 | struct mmm_data { | ||
42 | struct mmm_finger f[MAX_SLOTS]; | ||
43 | __u8 curid; | ||
44 | __u8 nexp, nreal; | ||
45 | bool touch, valid; | ||
46 | }; | ||
47 | |||
48 | static int mmm_input_mapping(struct hid_device *hdev, struct hid_input *hi, | ||
49 | struct hid_field *field, struct hid_usage *usage, | ||
50 | unsigned long **bit, int *max) | ||
51 | { | ||
52 | int f1 = field->logical_minimum; | ||
53 | int f2 = field->logical_maximum; | ||
54 | int df = f2 - f1; | ||
55 | |||
56 | switch (usage->hid & HID_USAGE_PAGE) { | ||
57 | |||
58 | case HID_UP_BUTTON: | ||
59 | return -1; | ||
60 | |||
61 | case HID_UP_GENDESK: | ||
62 | switch (usage->hid) { | ||
63 | case HID_GD_X: | ||
64 | hid_map_usage(hi, usage, bit, max, | ||
65 | EV_ABS, ABS_MT_POSITION_X); | ||
66 | input_set_abs_params(hi->input, ABS_MT_POSITION_X, | ||
67 | f1, f2, df / SN_MOVE, 0); | ||
68 | /* touchscreen emulation */ | ||
69 | input_set_abs_params(hi->input, ABS_X, | ||
70 | f1, f2, df / SN_MOVE, 0); | ||
71 | return 1; | ||
72 | case HID_GD_Y: | ||
73 | hid_map_usage(hi, usage, bit, max, | ||
74 | EV_ABS, ABS_MT_POSITION_Y); | ||
75 | input_set_abs_params(hi->input, ABS_MT_POSITION_Y, | ||
76 | f1, f2, df / SN_MOVE, 0); | ||
77 | /* touchscreen emulation */ | ||
78 | input_set_abs_params(hi->input, ABS_Y, | ||
79 | f1, f2, df / SN_MOVE, 0); | ||
80 | return 1; | ||
81 | } | ||
82 | return 0; | ||
83 | |||
84 | case HID_UP_DIGITIZER: | ||
85 | switch (usage->hid) { | ||
86 | /* we do not want to map these: no input-oriented meaning */ | ||
87 | case 0x14: | ||
88 | case 0x23: | ||
89 | case HID_DG_INPUTMODE: | ||
90 | case HID_DG_DEVICEINDEX: | ||
91 | case HID_DG_CONTACTCOUNT: | ||
92 | case HID_DG_CONTACTMAX: | ||
93 | case HID_DG_INRANGE: | ||
94 | case HID_DG_CONFIDENCE: | ||
95 | return -1; | ||
96 | case HID_DG_TIPSWITCH: | ||
97 | /* touchscreen emulation */ | ||
98 | hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_TOUCH); | ||
99 | input_set_capability(hi->input, EV_KEY, BTN_TOUCH); | ||
100 | return 1; | ||
101 | case HID_DG_WIDTH: | ||
102 | hid_map_usage(hi, usage, bit, max, | ||
103 | EV_ABS, ABS_MT_TOUCH_MAJOR); | ||
104 | input_set_abs_params(hi->input, ABS_MT_TOUCH_MAJOR, | ||
105 | f1, f2, df / SN_WIDTH, 0); | ||
106 | return 1; | ||
107 | case HID_DG_HEIGHT: | ||
108 | hid_map_usage(hi, usage, bit, max, | ||
109 | EV_ABS, ABS_MT_TOUCH_MINOR); | ||
110 | input_set_abs_params(hi->input, ABS_MT_TOUCH_MINOR, | ||
111 | f1, f2, df / SN_WIDTH, 0); | ||
112 | input_set_abs_params(hi->input, ABS_MT_ORIENTATION, | ||
113 | 0, 1, 0, 0); | ||
114 | return 1; | ||
115 | case HID_DG_CONTACTID: | ||
116 | input_mt_init_slots(hi->input, MAX_SLOTS); | ||
117 | return 1; | ||
118 | } | ||
119 | /* let hid-input decide for the others */ | ||
120 | return 0; | ||
121 | |||
122 | case 0xff000000: | ||
123 | /* we do not want to map these: no input-oriented meaning */ | ||
124 | return -1; | ||
125 | } | ||
126 | |||
127 | return 0; | ||
128 | } | ||
129 | |||
130 | static int mmm_input_mapped(struct hid_device *hdev, struct hid_input *hi, | ||
131 | struct hid_field *field, struct hid_usage *usage, | ||
132 | unsigned long **bit, int *max) | ||
133 | { | ||
134 | /* tell hid-input to skip setup of these event types */ | ||
135 | if (usage->type == EV_KEY || usage->type == EV_ABS) | ||
136 | set_bit(usage->type, hi->input->evbit); | ||
137 | return -1; | ||
138 | } | ||
139 | |||
140 | /* | ||
141 | * this function is called when a whole packet has been received and processed, | ||
142 | * so that it can decide what to send to the input layer. | ||
143 | */ | ||
144 | static void mmm_filter_event(struct mmm_data *md, struct input_dev *input) | ||
145 | { | ||
146 | int i; | ||
147 | for (i = 0; i < MAX_SLOTS; ++i) { | ||
148 | struct mmm_finger *f = &md->f[i]; | ||
149 | if (!f->valid) { | ||
150 | /* this finger is just placeholder data, ignore */ | ||
151 | continue; | ||
152 | } | ||
153 | input_mt_slot(input, i); | ||
154 | input_mt_report_slot_state(input, MT_TOOL_FINGER, f->touch); | ||
155 | if (f->touch) { | ||
156 | /* this finger is on the screen */ | ||
157 | int wide = (f->w > f->h); | ||
158 | /* divided by two to match visual scale of touch */ | ||
159 | int major = max(f->w, f->h) >> 1; | ||
160 | int minor = min(f->w, f->h) >> 1; | ||
161 | |||
162 | input_event(input, EV_ABS, ABS_MT_POSITION_X, f->x); | ||
163 | input_event(input, EV_ABS, ABS_MT_POSITION_Y, f->y); | ||
164 | input_event(input, EV_ABS, ABS_MT_ORIENTATION, wide); | ||
165 | input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, major); | ||
166 | input_event(input, EV_ABS, ABS_MT_TOUCH_MINOR, minor); | ||
167 | } | ||
168 | f->valid = 0; | ||
169 | } | ||
170 | |||
171 | input_mt_report_pointer_emulation(input, true); | ||
172 | input_sync(input); | ||
173 | } | ||
174 | |||
175 | /* | ||
176 | * this function is called upon all reports | ||
177 | * so that we can accumulate contact point information, | ||
178 | * and call input_mt_sync after each point. | ||
179 | */ | ||
180 | static int mmm_event(struct hid_device *hid, struct hid_field *field, | ||
181 | struct hid_usage *usage, __s32 value) | ||
182 | { | ||
183 | struct mmm_data *md = hid_get_drvdata(hid); | ||
184 | /* | ||
185 | * strangely, this function can be called before | ||
186 | * field->hidinput is initialized! | ||
187 | */ | ||
188 | if (hid->claimed & HID_CLAIMED_INPUT) { | ||
189 | struct input_dev *input = field->hidinput->input; | ||
190 | switch (usage->hid) { | ||
191 | case HID_DG_TIPSWITCH: | ||
192 | md->touch = value; | ||
193 | break; | ||
194 | case HID_DG_CONFIDENCE: | ||
195 | md->valid = value; | ||
196 | break; | ||
197 | case HID_DG_WIDTH: | ||
198 | if (md->valid) | ||
199 | md->f[md->curid].w = value; | ||
200 | break; | ||
201 | case HID_DG_HEIGHT: | ||
202 | if (md->valid) | ||
203 | md->f[md->curid].h = value; | ||
204 | break; | ||
205 | case HID_DG_CONTACTID: | ||
206 | value = clamp_val(value, 0, MAX_SLOTS - 1); | ||
207 | if (md->valid) { | ||
208 | md->curid = value; | ||
209 | md->f[value].touch = md->touch; | ||
210 | md->f[value].valid = 1; | ||
211 | md->nreal++; | ||
212 | } | ||
213 | break; | ||
214 | case HID_GD_X: | ||
215 | if (md->valid) | ||
216 | md->f[md->curid].x = value; | ||
217 | break; | ||
218 | case HID_GD_Y: | ||
219 | if (md->valid) | ||
220 | md->f[md->curid].y = value; | ||
221 | break; | ||
222 | case HID_DG_CONTACTCOUNT: | ||
223 | if (value) | ||
224 | md->nexp = value; | ||
225 | if (md->nreal >= md->nexp) { | ||
226 | mmm_filter_event(md, input); | ||
227 | md->nreal = 0; | ||
228 | } | ||
229 | break; | ||
230 | } | ||
231 | } | ||
232 | |||
233 | /* we have handled the hidinput part, now remains hiddev */ | ||
234 | if (hid->claimed & HID_CLAIMED_HIDDEV && hid->hiddev_hid_event) | ||
235 | hid->hiddev_hid_event(hid, field, usage, value); | ||
236 | |||
237 | return 1; | ||
238 | } | ||
239 | |||
240 | static int mmm_probe(struct hid_device *hdev, const struct hid_device_id *id) | ||
241 | { | ||
242 | int ret; | ||
243 | struct mmm_data *md; | ||
244 | |||
245 | hdev->quirks |= HID_QUIRK_NO_INPUT_SYNC; | ||
246 | |||
247 | md = kzalloc(sizeof(struct mmm_data), GFP_KERNEL); | ||
248 | if (!md) { | ||
249 | hid_err(hdev, "cannot allocate 3M data\n"); | ||
250 | return -ENOMEM; | ||
251 | } | ||
252 | hid_set_drvdata(hdev, md); | ||
253 | |||
254 | ret = hid_parse(hdev); | ||
255 | if (!ret) | ||
256 | ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); | ||
257 | |||
258 | if (ret) | ||
259 | kfree(md); | ||
260 | return ret; | ||
261 | } | ||
262 | |||
263 | static void mmm_remove(struct hid_device *hdev) | ||
264 | { | ||
265 | hid_hw_stop(hdev); | ||
266 | kfree(hid_get_drvdata(hdev)); | ||
267 | hid_set_drvdata(hdev, NULL); | ||
268 | } | ||
269 | |||
270 | static const struct hid_device_id mmm_devices[] = { | ||
271 | { HID_USB_DEVICE(USB_VENDOR_ID_3M, USB_DEVICE_ID_3M1968) }, | ||
272 | { HID_USB_DEVICE(USB_VENDOR_ID_3M, USB_DEVICE_ID_3M2256) }, | ||
273 | { } | ||
274 | }; | ||
275 | MODULE_DEVICE_TABLE(hid, mmm_devices); | ||
276 | |||
277 | static const struct hid_usage_id mmm_grabbed_usages[] = { | ||
278 | { HID_ANY_ID, HID_ANY_ID, HID_ANY_ID }, | ||
279 | { HID_ANY_ID - 1, HID_ANY_ID - 1, HID_ANY_ID - 1} | ||
280 | }; | ||
281 | |||
282 | static struct hid_driver mmm_driver = { | ||
283 | .name = "3m-pct", | ||
284 | .id_table = mmm_devices, | ||
285 | .probe = mmm_probe, | ||
286 | .remove = mmm_remove, | ||
287 | .input_mapping = mmm_input_mapping, | ||
288 | .input_mapped = mmm_input_mapped, | ||
289 | .usage_table = mmm_grabbed_usages, | ||
290 | .event = mmm_event, | ||
291 | }; | ||
292 | |||
293 | static int __init mmm_init(void) | ||
294 | { | ||
295 | return hid_register_driver(&mmm_driver); | ||
296 | } | ||
297 | |||
298 | static void __exit mmm_exit(void) | ||
299 | { | ||
300 | hid_unregister_driver(&mmm_driver); | ||
301 | } | ||
302 | |||
303 | module_init(mmm_init); | ||
304 | module_exit(mmm_exit); | ||
305 | |||