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