• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/linux/linux-2.6.36/drivers/hid/
1/*
2 *  HID driver for 3M PCT multitouch panels
3 *
4 *  Copyright (c) 2009-2010 Stephane Chatty <chatty@enac.fr>
5 *
6 */
7
8/*
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License as published by the Free
11 * Software Foundation; either version 2 of the License, or (at your option)
12 * any later version.
13 */
14
15#include <linux/device.h>
16#include <linux/hid.h>
17#include <linux/module.h>
18#include <linux/slab.h>
19#include <linux/usb.h>
20
21MODULE_AUTHOR("Stephane Chatty <chatty@enac.fr>");
22MODULE_DESCRIPTION("3M PCT multitouch panels");
23MODULE_LICENSE("GPL");
24
25#include "hid-ids.h"
26
27struct mmm_finger {
28	__s32 x, y, w, h;
29	__u8 rank;
30	bool touch, valid;
31};
32
33struct mmm_data {
34	struct mmm_finger f[10];
35	__u8 curid, num;
36	bool touch, valid;
37};
38
39static int mmm_input_mapping(struct hid_device *hdev, struct hid_input *hi,
40		struct hid_field *field, struct hid_usage *usage,
41		unsigned long **bit, int *max)
42{
43	switch (usage->hid & HID_USAGE_PAGE) {
44
45	case HID_UP_BUTTON:
46		return -1;
47
48	case HID_UP_GENDESK:
49		switch (usage->hid) {
50		case HID_GD_X:
51			hid_map_usage(hi, usage, bit, max,
52					EV_ABS, ABS_MT_POSITION_X);
53			/* touchscreen emulation */
54			input_set_abs_params(hi->input, ABS_X,
55						field->logical_minimum,
56						field->logical_maximum, 0, 0);
57			return 1;
58		case HID_GD_Y:
59			hid_map_usage(hi, usage, bit, max,
60					EV_ABS, ABS_MT_POSITION_Y);
61			/* touchscreen emulation */
62			input_set_abs_params(hi->input, ABS_Y,
63						field->logical_minimum,
64						field->logical_maximum, 0, 0);
65			return 1;
66		}
67		return 0;
68
69	case HID_UP_DIGITIZER:
70		switch (usage->hid) {
71		/* we do not want to map these: no input-oriented meaning */
72		case 0x14:
73		case 0x23:
74		case HID_DG_INPUTMODE:
75		case HID_DG_DEVICEINDEX:
76		case HID_DG_CONTACTCOUNT:
77		case HID_DG_CONTACTMAX:
78		case HID_DG_INRANGE:
79		case HID_DG_CONFIDENCE:
80			return -1;
81		case HID_DG_TIPSWITCH:
82			/* touchscreen emulation */
83			hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_TOUCH);
84			return 1;
85		case HID_DG_WIDTH:
86			hid_map_usage(hi, usage, bit, max,
87					EV_ABS, ABS_MT_TOUCH_MAJOR);
88			return 1;
89		case HID_DG_HEIGHT:
90			hid_map_usage(hi, usage, bit, max,
91					EV_ABS, ABS_MT_TOUCH_MINOR);
92			input_set_abs_params(hi->input, ABS_MT_ORIENTATION,
93					1, 1, 0, 0);
94			return 1;
95		case HID_DG_CONTACTID:
96			field->logical_maximum = 59;
97			hid_map_usage(hi, usage, bit, max,
98					EV_ABS, ABS_MT_TRACKING_ID);
99			return 1;
100		}
101		/* let hid-input decide for the others */
102		return 0;
103
104	case 0xff000000:
105		/* we do not want to map these: no input-oriented meaning */
106		return -1;
107	}
108
109	return 0;
110}
111
112static int mmm_input_mapped(struct hid_device *hdev, struct hid_input *hi,
113		struct hid_field *field, struct hid_usage *usage,
114		unsigned long **bit, int *max)
115{
116	if (usage->type == EV_KEY || usage->type == EV_ABS)
117		clear_bit(usage->code, *bit);
118
119	return 0;
120}
121
122/*
123 * this function is called when a whole packet has been received and processed,
124 * so that it can decide what to send to the input layer.
125 */
126static void mmm_filter_event(struct mmm_data *md, struct input_dev *input)
127{
128	struct mmm_finger *oldest = 0;
129	bool pressed = false, released = false;
130	int i;
131
132	/*
133	 * we need to iterate on all fingers to decide if we have a press
134	 * or a release event in our touchscreen emulation.
135	 */
136	for (i = 0; i < 10; ++i) {
137		struct mmm_finger *f = &md->f[i];
138		if (!f->valid) {
139			/* this finger is just placeholder data, ignore */
140		} else if (f->touch) {
141			/* this finger is on the screen */
142			int wide = (f->w > f->h);
143			input_event(input, EV_ABS, ABS_MT_TRACKING_ID, i);
144			input_event(input, EV_ABS, ABS_MT_POSITION_X, f->x);
145			input_event(input, EV_ABS, ABS_MT_POSITION_Y, f->y);
146			input_event(input, EV_ABS, ABS_MT_ORIENTATION, wide);
147			input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR,
148						wide ? f->w : f->h);
149			input_event(input, EV_ABS, ABS_MT_TOUCH_MINOR,
150						wide ? f->h : f->w);
151			input_mt_sync(input);
152			/*
153			 * touchscreen emulation: maintain the age rank
154			 * of this finger, decide if we have a press
155			 */
156			if (f->rank == 0) {
157				f->rank = ++(md->num);
158				if (f->rank == 1)
159					pressed = true;
160			}
161			if (f->rank == 1)
162				oldest = f;
163		} else {
164			/* this finger took off the screen */
165			/* touchscreen emulation: maintain age rank of others */
166			int j;
167
168			for (j = 0; j < 10; ++j) {
169				struct mmm_finger *g = &md->f[j];
170				if (g->rank > f->rank) {
171					g->rank--;
172					if (g->rank == 1)
173						oldest = g;
174				}
175			}
176			f->rank = 0;
177			--(md->num);
178			if (md->num == 0)
179				released = true;
180		}
181		f->valid = 0;
182	}
183
184	/* touchscreen emulation */
185	if (oldest) {
186		if (pressed)
187			input_event(input, EV_KEY, BTN_TOUCH, 1);
188		input_event(input, EV_ABS, ABS_X, oldest->x);
189		input_event(input, EV_ABS, ABS_Y, oldest->y);
190	} else if (released) {
191		input_event(input, EV_KEY, BTN_TOUCH, 0);
192	}
193}
194
195/*
196 * this function is called upon all reports
197 * so that we can accumulate contact point information,
198 * and call input_mt_sync after each point.
199 */
200static int mmm_event(struct hid_device *hid, struct hid_field *field,
201				struct hid_usage *usage, __s32 value)
202{
203	struct mmm_data *md = hid_get_drvdata(hid);
204	/*
205	 * strangely, this function can be called before
206	 * field->hidinput is initialized!
207	 */
208	if (hid->claimed & HID_CLAIMED_INPUT) {
209		struct input_dev *input = field->hidinput->input;
210		switch (usage->hid) {
211		case HID_DG_TIPSWITCH:
212			md->touch = value;
213			break;
214		case HID_DG_CONFIDENCE:
215			md->valid = value;
216			break;
217		case HID_DG_WIDTH:
218			if (md->valid)
219				md->f[md->curid].w = value;
220			break;
221		case HID_DG_HEIGHT:
222			if (md->valid)
223				md->f[md->curid].h = value;
224			break;
225		case HID_DG_CONTACTID:
226			if (md->valid) {
227				md->curid = value;
228				md->f[value].touch = md->touch;
229				md->f[value].valid = 1;
230			}
231			break;
232		case HID_GD_X:
233			if (md->valid)
234				md->f[md->curid].x = value;
235			break;
236		case HID_GD_Y:
237			if (md->valid)
238				md->f[md->curid].y = value;
239			break;
240		case HID_DG_CONTACTCOUNT:
241			mmm_filter_event(md, input);
242			break;
243		}
244	}
245
246	/* we have handled the hidinput part, now remains hiddev */
247	if (hid->claimed & HID_CLAIMED_HIDDEV && hid->hiddev_hid_event)
248		hid->hiddev_hid_event(hid, field, usage, value);
249
250	return 1;
251}
252
253static int mmm_probe(struct hid_device *hdev, const struct hid_device_id *id)
254{
255	int ret;
256	struct mmm_data *md;
257
258	md = kzalloc(sizeof(struct mmm_data), GFP_KERNEL);
259	if (!md) {
260		dev_err(&hdev->dev, "cannot allocate 3M data\n");
261		return -ENOMEM;
262	}
263	hid_set_drvdata(hdev, md);
264
265	ret = hid_parse(hdev);
266	if (!ret)
267		ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
268
269	if (ret)
270		kfree(md);
271	return ret;
272}
273
274static void mmm_remove(struct hid_device *hdev)
275{
276	hid_hw_stop(hdev);
277	kfree(hid_get_drvdata(hdev));
278	hid_set_drvdata(hdev, NULL);
279}
280
281static const struct hid_device_id mmm_devices[] = {
282	{ HID_USB_DEVICE(USB_VENDOR_ID_3M, USB_DEVICE_ID_3M1968) },
283	{ HID_USB_DEVICE(USB_VENDOR_ID_3M, USB_DEVICE_ID_3M2256) },
284	{ }
285};
286MODULE_DEVICE_TABLE(hid, mmm_devices);
287
288static const struct hid_usage_id mmm_grabbed_usages[] = {
289	{ HID_ANY_ID, HID_ANY_ID, HID_ANY_ID },
290	{ HID_ANY_ID - 1, HID_ANY_ID - 1, HID_ANY_ID - 1}
291};
292
293static struct hid_driver mmm_driver = {
294	.name = "3m-pct",
295	.id_table = mmm_devices,
296	.probe = mmm_probe,
297	.remove = mmm_remove,
298	.input_mapping = mmm_input_mapping,
299	.input_mapped = mmm_input_mapped,
300	.usage_table = mmm_grabbed_usages,
301	.event = mmm_event,
302};
303
304static int __init mmm_init(void)
305{
306	return hid_register_driver(&mmm_driver);
307}
308
309static void __exit mmm_exit(void)
310{
311	hid_unregister_driver(&mmm_driver);
312}
313
314module_init(mmm_init);
315module_exit(mmm_exit);
316