blob: 18575a4e0d630f030a86082f3c1cf533b1ebb442 [file] [log] [blame]
Stephane Chattyb6353f42009-12-22 23:04:17 +01001/*
2 * HID driver for 3M PCT multitouch panels
3 *
Stephane Chatty6dec1432010-04-11 14:51:24 +02004 * Copyright (c) 2009-2010 Stephane Chatty <chatty@enac.fr>
Henrik Rydberg1f01a1f2010-09-21 22:12:12 +02005 * Copyright (c) 2010 Henrik Rydberg <rydberg@euromail.se>
6 * Copyright (c) 2010 Canonical, Ltd.
Stephane Chattyb6353f42009-12-22 23:04:17 +01007 *
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>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090020#include <linux/slab.h>
Stephane Chattyb6353f42009-12-22 23:04:17 +010021#include <linux/usb.h>
Henrik Rydberg47c78e82010-11-27 09:16:48 +010022#include <linux/input/mt.h>
Stephane Chattyb6353f42009-12-22 23:04:17 +010023
Stephane Chattyb6353f42009-12-22 23:04:17 +010024MODULE_AUTHOR("Stephane Chatty <chatty@enac.fr>");
25MODULE_DESCRIPTION("3M PCT multitouch panels");
26MODULE_LICENSE("GPL");
27
28#include "hid-ids.h"
29
Henrik Rydberg41035902010-09-21 16:11:44 +020030#define MAX_SLOTS 60
Henrik Rydberg1f01a1f2010-09-21 22:12:12 +020031#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
Henrik Rydberg41035902010-09-21 16:11:44 +020037
Stephane Chattyb6353f42009-12-22 23:04:17 +010038struct mmm_finger {
Stephane Chatty6dec1432010-04-11 14:51:24 +020039 __s32 x, y, w, h;
Henrik Rydberg1f01a1f2010-09-21 22:12:12 +020040 __u16 id;
Henrik Rydberg1f01a1f2010-09-21 22:12:12 +020041 bool prev_touch;
Stephane Chattyb6353f42009-12-22 23:04:17 +010042 bool touch, valid;
43};
44
45struct mmm_data {
Henrik Rydberg41035902010-09-21 16:11:44 +020046 struct mmm_finger f[MAX_SLOTS];
Henrik Rydberg1f01a1f2010-09-21 22:12:12 +020047 __u16 id;
Henrik Rydberg1d958c82010-09-21 23:22:39 +020048 __u8 curid;
Henrik Rydberg41035902010-09-21 16:11:44 +020049 __u8 nexp, nreal;
Stephane Chattyb6353f42009-12-22 23:04:17 +010050 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{
Henrik Rydberg1f01a1f2010-09-21 22:12:12 +020057 int f1 = field->logical_minimum;
58 int f2 = field->logical_maximum;
59 int df = f2 - f1;
60
Stephane Chattyb6353f42009-12-22 23:04:17 +010061 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);
Henrik Rydberg1f01a1f2010-09-21 22:12:12 +020071 input_set_abs_params(hi->input, ABS_MT_POSITION_X,
72 f1, f2, df / SN_MOVE, 0);
Stephane Chattyb6353f42009-12-22 23:04:17 +010073 /* touchscreen emulation */
74 input_set_abs_params(hi->input, ABS_X,
Henrik Rydberg1f01a1f2010-09-21 22:12:12 +020075 f1, f2, df / SN_MOVE, 0);
Stephane Chattyb6353f42009-12-22 23:04:17 +010076 return 1;
77 case HID_GD_Y:
78 hid_map_usage(hi, usage, bit, max,
79 EV_ABS, ABS_MT_POSITION_Y);
Henrik Rydberg1f01a1f2010-09-21 22:12:12 +020080 input_set_abs_params(hi->input, ABS_MT_POSITION_Y,
81 f1, f2, df / SN_MOVE, 0);
Stephane Chattyb6353f42009-12-22 23:04:17 +010082 /* touchscreen emulation */
83 input_set_abs_params(hi->input, ABS_Y,
Henrik Rydberg1f01a1f2010-09-21 22:12:12 +020084 f1, f2, df / SN_MOVE, 0);
Stephane Chattyb6353f42009-12-22 23:04:17 +010085 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);
Henrik Rydberg1f01a1f2010-09-21 22:12:12 +0200104 input_set_capability(hi->input, EV_KEY, BTN_TOUCH);
Stephane Chattyb6353f42009-12-22 23:04:17 +0100105 return 1;
Stephane Chatty6dec1432010-04-11 14:51:24 +0200106 case HID_DG_WIDTH:
107 hid_map_usage(hi, usage, bit, max,
108 EV_ABS, ABS_MT_TOUCH_MAJOR);
Henrik Rydberg1f01a1f2010-09-21 22:12:12 +0200109 input_set_abs_params(hi->input, ABS_MT_TOUCH_MAJOR,
110 f1, f2, df / SN_WIDTH, 0);
Stephane Chatty6dec1432010-04-11 14:51:24 +0200111 return 1;
112 case HID_DG_HEIGHT:
113 hid_map_usage(hi, usage, bit, max,
114 EV_ABS, ABS_MT_TOUCH_MINOR);
Henrik Rydberg1f01a1f2010-09-21 22:12:12 +0200115 input_set_abs_params(hi->input, ABS_MT_TOUCH_MINOR,
116 f1, f2, df / SN_WIDTH, 0);
Stephane Chatty6dec1432010-04-11 14:51:24 +0200117 input_set_abs_params(hi->input, ABS_MT_ORIENTATION,
Henrik Rydberg46c4ba02010-09-21 16:16:09 +0200118 0, 1, 0, 0);
Stephane Chatty6dec1432010-04-11 14:51:24 +0200119 return 1;
Stephane Chattyb6353f42009-12-22 23:04:17 +0100120 case HID_DG_CONTACTID:
Henrik Rydberg41035902010-09-21 16:11:44 +0200121 field->logical_maximum = MAX_TRKID;
Stephane Chattyb6353f42009-12-22 23:04:17 +0100122 hid_map_usage(hi, usage, bit, max,
123 EV_ABS, ABS_MT_TRACKING_ID);
Henrik Rydberg1f01a1f2010-09-21 22:12:12 +0200124 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);
Stephane Chattyb6353f42009-12-22 23:04:17 +0100129 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{
Henrik Rydberg1f01a1f2010-09-21 22:12:12 +0200146 /* tell hid-input to skip setup of these event types */
Stephane Chattyb6353f42009-12-22 23:04:17 +0100147 if (usage->type == EV_KEY || usage->type == EV_ABS)
Henrik Rydberg1f01a1f2010-09-21 22:12:12 +0200148 set_bit(usage->type, hi->input->evbit);
149 return -1;
Stephane Chattyb6353f42009-12-22 23:04:17 +0100150}
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;
Stephane Chattyb6353f42009-12-22 23:04:17 +0100159 int i;
Henrik Rydberg41035902010-09-21 16:11:44 +0200160 for (i = 0; i < MAX_SLOTS; ++i) {
Stephane Chattyb6353f42009-12-22 23:04:17 +0100161 struct mmm_finger *f = &md->f[i];
162 if (!f->valid) {
163 /* this finger is just placeholder data, ignore */
Henrik Rydberg1f01a1f2010-09-21 22:12:12 +0200164 continue;
165 }
166 input_mt_slot(input, i);
167 if (f->touch) {
Stephane Chattyb6353f42009-12-22 23:04:17 +0100168 /* this finger is on the screen */
Stephane Chatty6dec1432010-04-11 14:51:24 +0200169 int wide = (f->w > f->h);
Henrik Rydberg48216fb2010-09-22 11:29:07 +0200170 /* 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
Henrik Rydberg1f01a1f2010-09-21 22:12:12 +0200174 if (!f->prev_touch)
175 f->id = md->id++;
176 input_event(input, EV_ABS, ABS_MT_TRACKING_ID, f->id);
Stephane Chattyb6353f42009-12-22 23:04:17 +0100177 input_event(input, EV_ABS, ABS_MT_POSITION_X, f->x);
178 input_event(input, EV_ABS, ABS_MT_POSITION_Y, f->y);
Stephane Chatty6dec1432010-04-11 14:51:24 +0200179 input_event(input, EV_ABS, ABS_MT_ORIENTATION, wide);
Henrik Rydberg48216fb2010-09-22 11:29:07 +0200180 input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, major);
181 input_event(input, EV_ABS, ABS_MT_TOUCH_MINOR, minor);
Henrik Rydberg1d958c82010-09-21 23:22:39 +0200182 /* touchscreen emulation: pick the oldest contact */
183 if (!oldest || ((f->id - oldest->id) & (SHRT_MAX + 1)))
Stephane Chattyb6353f42009-12-22 23:04:17 +0100184 oldest = f;
185 } else {
186 /* this finger took off the screen */
Henrik Rydberg1f01a1f2010-09-21 22:12:12 +0200187 input_event(input, EV_ABS, ABS_MT_TRACKING_ID, -1);
Stephane Chattyb6353f42009-12-22 23:04:17 +0100188 }
Henrik Rydberg1f01a1f2010-09-21 22:12:12 +0200189 f->prev_touch = f->touch;
Stephane Chattyb6353f42009-12-22 23:04:17 +0100190 f->valid = 0;
191 }
192
193 /* touchscreen emulation */
194 if (oldest) {
Henrik Rydberg1d958c82010-09-21 23:22:39 +0200195 input_event(input, EV_KEY, BTN_TOUCH, 1);
Stephane Chattyb6353f42009-12-22 23:04:17 +0100196 input_event(input, EV_ABS, ABS_X, oldest->x);
197 input_event(input, EV_ABS, ABS_Y, oldest->y);
Henrik Rydberg1d958c82010-09-21 23:22:39 +0200198 } else {
Stephane Chattyb6353f42009-12-22 23:04:17 +0100199 input_event(input, EV_KEY, BTN_TOUCH, 0);
200 }
Henrik Rydberg41035902010-09-21 16:11:44 +0200201 input_sync(input);
Stephane Chattyb6353f42009-12-22 23:04:17 +0100202}
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;
Stephane Chatty6dec1432010-04-11 14:51:24 +0200226 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;
Stephane Chattyb6353f42009-12-22 23:04:17 +0100234 case HID_DG_CONTACTID:
Henrik Rydberg41035902010-09-21 16:11:44 +0200235 value = clamp_val(value, 0, MAX_SLOTS - 1);
Stephane Chattyb6353f42009-12-22 23:04:17 +0100236 if (md->valid) {
237 md->curid = value;
238 md->f[value].touch = md->touch;
239 md->f[value].valid = 1;
Henrik Rydberg41035902010-09-21 16:11:44 +0200240 md->nreal++;
Stephane Chattyb6353f42009-12-22 23:04:17 +0100241 }
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:
Henrik Rydberg41035902010-09-21 16:11:44 +0200252 if (value)
253 md->nexp = value;
254 if (md->nreal >= md->nexp) {
255 mmm_filter_event(md, input);
256 md->nreal = 0;
257 }
Stephane Chattyb6353f42009-12-22 23:04:17 +0100258 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
Henrik Rydberg41035902010-09-21 16:11:44 +0200274 hdev->quirks |= HID_QUIRK_NO_INPUT_SYNC;
275
Stephane Chattyb6353f42009-12-22 23:04:17 +0100276 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) },
Stephane Chatty6dec1432010-04-11 14:51:24 +0200301 { HID_USB_DEVICE(USB_VENDOR_ID_3M, USB_DEVICE_ID_3M2256) },
Stephane Chattyb6353f42009-12-22 23:04:17 +0100302 { }
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);
Stephane Chattyb6353f42009-12-22 23:04:17 +0100334