hid-ntrig.c revision b0549cf1a35a92edf053d94066e60fb0ed02bb71
1/*
2 *  HID driver for N-Trig touchscreens
3 *
4 *  Copyright (c) 2008 Rafi Rubin
5 *  Copyright (c) 2009 Stephane Chatty
6 *
7 */
8
9/*
10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU General Public License as published by the Free
12 * Software Foundation; either version 2 of the License, or (at your option)
13 * any later version.
14 */
15
16#include <linux/device.h>
17#include <linux/hid.h>
18#include <linux/module.h>
19
20#include "hid-ids.h"
21
22#define NTRIG_DUPLICATE_USAGES	0x001
23
24#define nt_map_key_clear(c)	hid_map_usage_clear(hi, usage, bit, max, \
25					EV_KEY, (c))
26
27struct ntrig_data {
28	__s32 x, y, id, w, h;
29	bool reading_a_point, found_contact_id;
30};
31
32/*
33 * this driver is aimed at two firmware versions in circulation:
34 *  - dual pen/finger single touch
35 *  - finger multitouch, pen not working
36 */
37
38static int ntrig_input_mapping(struct hid_device *hdev, struct hid_input *hi,
39		struct hid_field *field, struct hid_usage *usage,
40		unsigned long **bit, int *max)
41{
42	/* No special mappings needed for the pen */
43	if (field->application == HID_DG_PEN)
44		return 0;
45
46	switch (usage->hid & HID_USAGE_PAGE) {
47	case HID_UP_GENDESK:
48		switch (usage->hid) {
49		case HID_GD_X:
50			hid_map_usage(hi, usage, bit, max,
51					EV_ABS, ABS_MT_POSITION_X);
52			input_set_abs_params(hi->input, ABS_X,
53					field->logical_minimum,
54					field->logical_maximum, 0, 0);
55			return 1;
56		case HID_GD_Y:
57			hid_map_usage(hi, usage, bit, max,
58					EV_ABS, ABS_MT_POSITION_Y);
59			input_set_abs_params(hi->input, ABS_Y,
60					field->logical_minimum,
61					field->logical_maximum, 0, 0);
62			return 1;
63		}
64		return 0;
65
66	case HID_UP_DIGITIZER:
67		switch (usage->hid) {
68		/* we do not want to map these for now */
69		case HID_DG_CONTACTID: /* value is useless */
70		case HID_DG_INPUTMODE:
71		case HID_DG_DEVICEINDEX:
72		case HID_DG_CONTACTCOUNT:
73		case HID_DG_CONTACTMAX:
74			return -1;
75
76		/* original mapping by Rafi Rubin */
77		case HID_DG_CONFIDENCE:
78			nt_map_key_clear(BTN_TOOL_DOUBLETAP);
79			return 1;
80
81		/* width/height mapped on TouchMajor/TouchMinor/Orientation */
82		case HID_DG_WIDTH:
83			hid_map_usage(hi, usage, bit, max,
84					EV_ABS, ABS_MT_TOUCH_MAJOR);
85			return 1;
86		case HID_DG_HEIGHT:
87			hid_map_usage(hi, usage, bit, max,
88					EV_ABS, ABS_MT_TOUCH_MINOR);
89			input_set_abs_params(hi->input, ABS_MT_ORIENTATION,
90					0, 1, 0, 0);
91			return 1;
92		}
93		return 0;
94
95	case 0xff000000:
96		/* we do not want to map these: no input-oriented meaning */
97		return -1;
98	}
99
100	return 0;
101}
102
103static int ntrig_input_mapped(struct hid_device *hdev, struct hid_input *hi,
104		struct hid_field *field, struct hid_usage *usage,
105		unsigned long **bit, int *max)
106{
107	/* No special mappings needed for the pen */
108	if (field->application == HID_DG_PEN)
109		return 0;
110
111	if (usage->type == EV_KEY || usage->type == EV_REL
112			|| usage->type == EV_ABS)
113		clear_bit(usage->code, *bit);
114
115	return 0;
116}
117
118/*
119 * this function is called upon all reports
120 * so that we can filter contact point information,
121 * decide whether we are in multi or single touch mode
122 * and call input_mt_sync after each point if necessary
123 */
124static int ntrig_event (struct hid_device *hid, struct hid_field *field,
125		                        struct hid_usage *usage, __s32 value)
126{
127	struct input_dev *input = field->hidinput->input;
128	struct ntrig_data *nd = hid_get_drvdata(hid);
129
130	/* No special handling needed for the pen */
131	if (field->application == HID_DG_PEN)
132		return 0;
133
134        if (hid->claimed & HID_CLAIMED_INPUT) {
135		switch (usage->hid) {
136		case HID_GD_X:
137			nd->x = value;
138			nd->reading_a_point = 1;
139			break;
140		case HID_GD_Y:
141			nd->y = value;
142			break;
143		case HID_DG_CONTACTID:
144			nd->id = value;
145			/* we receive this only when in multitouch mode */
146			nd->found_contact_id = 1;
147			break;
148		case HID_DG_WIDTH:
149			nd->w = value;
150			break;
151		case HID_DG_HEIGHT:
152			nd->h = value;
153			/*
154			 * when in single touch mode, this is the last
155			 * report received in a finger event. We want
156			 * to emit a normal (X, Y) position
157			 */
158			if (!nd->found_contact_id) {
159				input_event(input, EV_ABS, ABS_X, nd->x);
160				input_event(input, EV_ABS, ABS_Y, nd->y);
161			}
162			break;
163		case 0xff000002:
164			/*
165			 * we receive this when the device is in multitouch
166			 * mode. The first of the three values tagged with
167			 * this usage tells if the contact point is real
168			 * or a placeholder
169			 */
170			if (!nd->reading_a_point || value != 1)
171				break;
172			/* emit a normal (X, Y) for the first point only */
173			if (nd->id == 0) {
174				input_event(input, EV_ABS, ABS_X, nd->x);
175				input_event(input, EV_ABS, ABS_Y, nd->y);
176			}
177			input_event(input, EV_ABS, ABS_MT_POSITION_X, nd->x);
178			input_event(input, EV_ABS, ABS_MT_POSITION_Y, nd->y);
179			if (nd->w > nd->h) {
180				input_event(input, EV_ABS,
181						ABS_MT_ORIENTATION, 1);
182				input_event(input, EV_ABS,
183						ABS_MT_TOUCH_MAJOR, nd->w);
184				input_event(input, EV_ABS,
185						ABS_MT_TOUCH_MINOR, nd->h);
186			} else {
187				input_event(input, EV_ABS,
188						ABS_MT_ORIENTATION, 0);
189				input_event(input, EV_ABS,
190						ABS_MT_TOUCH_MAJOR, nd->h);
191				input_event(input, EV_ABS,
192						ABS_MT_TOUCH_MINOR, nd->w);
193			}
194			input_mt_sync(field->hidinput->input);
195			nd->reading_a_point = 0;
196			nd->found_contact_id = 0;
197			break;
198
199		default:
200			/* fallback to the generic hidinput handling */
201			return 0;
202		}
203	}
204
205	/* we have handled the hidinput part, now remains hiddev */
206	if ((hid->claimed & HID_CLAIMED_HIDDEV) && hid->hiddev_hid_event)
207		hid->hiddev_hid_event(hid, field, usage, value);
208
209	return 1;
210}
211
212static int ntrig_probe(struct hid_device *hdev, const struct hid_device_id *id)
213{
214	int ret;
215	struct ntrig_data *nd;
216	struct hid_input *hidinput;
217	struct input_dev *input;
218
219	if (id->driver_data)
220		hdev->quirks |= HID_QUIRK_MULTI_INPUT;
221
222	nd = kmalloc(sizeof(struct ntrig_data), GFP_KERNEL);
223	if (!nd) {
224		dev_err(&hdev->dev, "cannot allocate N-Trig data\n");
225		return -ENOMEM;
226	}
227	nd->reading_a_point = 0;
228	nd->found_contact_id = 0;
229	hid_set_drvdata(hdev, nd);
230
231	ret = hid_parse(hdev);
232	if (ret) {
233		dev_err(&hdev->dev, "parse failed\n");
234		goto err_free;
235	}
236
237	ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF);
238	if (ret) {
239		dev_err(&hdev->dev, "hw start failed\n");
240		goto err_free;
241	}
242
243
244	list_for_each_entry(hidinput, &hdev->inputs, list) {
245		input = hidinput->input;
246		switch (hidinput->report->field[0]->application) {
247		case HID_DG_PEN:
248			input->name = "N-Trig Pen";
249			break;
250		case HID_DG_TOUCHSCREEN:
251			/*
252			 * The physical touchscreen (single touch)
253			 * input has a value for physical, whereas
254			 * the multitouch only has logical input
255			 * fields.
256			 */
257			input->name =
258				(hidinput->report->field[0]
259				 ->physical) ?
260				"N-Trig Touchscreen" :
261				"N-Trig MultiTouch";
262			break;
263		}
264	}
265
266	return 0;
267err_free:
268	kfree(nd);
269	return ret;
270}
271
272static void ntrig_remove(struct hid_device *hdev)
273{
274	hid_hw_stop(hdev);
275	kfree(hid_get_drvdata(hdev));
276}
277
278static const struct hid_device_id ntrig_devices[] = {
279	{ HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN),
280		.driver_data = NTRIG_DUPLICATE_USAGES },
281	{ }
282};
283MODULE_DEVICE_TABLE(hid, ntrig_devices);
284
285static const struct hid_usage_id ntrig_grabbed_usages[] = {
286	{ HID_ANY_ID, HID_ANY_ID, HID_ANY_ID },
287	{ HID_ANY_ID - 1, HID_ANY_ID - 1, HID_ANY_ID - 1 }
288};
289
290static struct hid_driver ntrig_driver = {
291	.name = "ntrig",
292	.id_table = ntrig_devices,
293	.probe = ntrig_probe,
294	.remove = ntrig_remove,
295	.input_mapping = ntrig_input_mapping,
296	.input_mapped = ntrig_input_mapped,
297	.usage_table = ntrig_grabbed_usages,
298	.event = ntrig_event,
299};
300
301static int __init ntrig_init(void)
302{
303	return hid_register_driver(&ntrig_driver);
304}
305
306static void __exit ntrig_exit(void)
307{
308	hid_unregister_driver(&ntrig_driver);
309}
310
311module_init(ntrig_init);
312module_exit(ntrig_exit);
313MODULE_LICENSE("GPL");
314