PageRenderTime 49ms CodeModel.GetById 17ms app.highlight 26ms RepoModel.GetById 2ms app.codeStats 0ms

/drivers/hid/hid-mosart.c

https://gitlab.com/TeamTators/hp-kernel-tenderloin
C | 274 lines | 200 code | 41 blank | 33 comment | 23 complexity | d7a7eeb0d5d1d9f9b40856fa330442f6 MD5 | raw file
  1/*
  2 *  HID driver for the multitouch panel on the ASUS EeePC T91MT
  3 *
  4 *  Copyright (c) 2009-2010 Stephane Chatty <chatty@enac.fr>
  5 *  Copyright (c) 2010 Teemu Tuominen <teemu.tuominen@cybercom.com>
  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#include <linux/slab.h>
 20#include <linux/usb.h>
 21#include "usbhid/usbhid.h"
 22
 23MODULE_AUTHOR("Stephane Chatty <chatty@enac.fr>");
 24MODULE_DESCRIPTION("MosArt dual-touch panel");
 25MODULE_LICENSE("GPL");
 26
 27#include "hid-ids.h"
 28
 29struct mosart_data {
 30	__u16 x, y;
 31	__u8 id;
 32	bool valid;		/* valid finger data, or just placeholder? */
 33	bool first;		/* is this the first finger in this frame? */
 34	bool activity_now;	/* at least one active finger in this frame? */
 35	bool activity;		/* at least one active finger previously? */
 36};
 37
 38static int mosart_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	switch (usage->hid & HID_USAGE_PAGE) {
 43
 44	case HID_UP_GENDESK:
 45		switch (usage->hid) {
 46		case HID_GD_X:
 47			hid_map_usage(hi, usage, bit, max,
 48					EV_ABS, ABS_MT_POSITION_X);
 49			/* touchscreen emulation */
 50			input_set_abs_params(hi->input, ABS_X,
 51						field->logical_minimum,
 52						field->logical_maximum, 0, 0);
 53			return 1;
 54		case HID_GD_Y:
 55			hid_map_usage(hi, usage, bit, max,
 56					EV_ABS, ABS_MT_POSITION_Y);
 57			/* touchscreen emulation */
 58			input_set_abs_params(hi->input, ABS_Y,
 59						field->logical_minimum,
 60						field->logical_maximum, 0, 0);
 61			return 1;
 62		}
 63		return 0;
 64
 65	case HID_UP_DIGITIZER:
 66		switch (usage->hid) {
 67		case HID_DG_CONFIDENCE:
 68		case HID_DG_TIPSWITCH:
 69		case HID_DG_INPUTMODE:
 70		case HID_DG_DEVICEINDEX:
 71		case HID_DG_CONTACTCOUNT:
 72		case HID_DG_CONTACTMAX:
 73		case HID_DG_TIPPRESSURE:
 74		case HID_DG_WIDTH:
 75		case HID_DG_HEIGHT:
 76			return -1;
 77		case HID_DG_INRANGE:
 78			/* touchscreen emulation */
 79			hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_TOUCH);
 80			return 1;
 81
 82		case HID_DG_CONTACTID:
 83			hid_map_usage(hi, usage, bit, max,
 84					EV_ABS, ABS_MT_TRACKING_ID);
 85			return 1;
 86
 87		}
 88		return 0;
 89
 90	case 0xff000000:
 91		/* ignore HID features */
 92		return -1;
 93	}
 94
 95	return 0;
 96}
 97
 98static int mosart_input_mapped(struct hid_device *hdev, struct hid_input *hi,
 99		struct hid_field *field, struct hid_usage *usage,
100		unsigned long **bit, int *max)
101{
102	if (usage->type == EV_KEY || usage->type == EV_ABS)
103		clear_bit(usage->code, *bit);
104
105	return 0;
106}
107
108/*
109 * this function is called when a whole finger has been parsed,
110 * so that it can decide what to send to the input layer.
111 */
112static void mosart_filter_event(struct mosart_data *td, struct input_dev *input)
113{
114	td->first = !td->first; /* touchscreen emulation */
115
116	if (!td->valid) {
117		/*
118		 * touchscreen emulation: if no finger in this frame is valid
119		 * and there previously was finger activity, this is a release
120		 */ 
121		if (!td->first && !td->activity_now && td->activity) {
122			input_event(input, EV_KEY, BTN_TOUCH, 0);
123			td->activity = false;
124		}
125		return;
126	}
127
128	input_event(input, EV_ABS, ABS_MT_TRACKING_ID, td->id);
129	input_event(input, EV_ABS, ABS_MT_POSITION_X, td->x);
130	input_event(input, EV_ABS, ABS_MT_POSITION_Y, td->y);
131
132	input_mt_sync(input);
133	td->valid = false;
134
135	/* touchscreen emulation: if first active finger in this frame... */
136	if (!td->activity_now) {
137		/* if there was no previous activity, emit touch event */
138		if (!td->activity) {
139			input_event(input, EV_KEY, BTN_TOUCH, 1);
140			td->activity = true;
141		}
142		td->activity_now = true;
143		/* and in any case this is our preferred finger */
144		input_event(input, EV_ABS, ABS_X, td->x);
145		input_event(input, EV_ABS, ABS_Y, td->y);
146	}
147}
148
149
150static int mosart_event(struct hid_device *hid, struct hid_field *field,
151				struct hid_usage *usage, __s32 value)
152{
153	struct mosart_data *td = hid_get_drvdata(hid);
154
155	if (hid->claimed & HID_CLAIMED_INPUT) {
156		struct input_dev *input = field->hidinput->input;
157		switch (usage->hid) {
158		case HID_DG_INRANGE:
159			td->valid = !!value;
160			break;
161		case HID_GD_X:
162			td->x = value;
163			break;
164		case HID_GD_Y:
165			td->y = value;
166			mosart_filter_event(td, input);
167			break;
168		case HID_DG_CONTACTID:
169			td->id = value;
170			break;
171		case HID_DG_CONTACTCOUNT:
172			/* touch emulation: this is the last field in a frame */
173			td->first = false;
174			td->activity_now = false;
175			break;
176		case HID_DG_CONFIDENCE:
177		case HID_DG_TIPSWITCH:
178			/* avoid interference from generic hidinput handling */
179			break;
180
181		default:
182			/* fallback to the generic hidinput handling */
183			return 0;
184		}
185	}
186
187	/* we have handled the hidinput part, now remains hiddev */
188	if (hid->claimed & HID_CLAIMED_HIDDEV && hid->hiddev_hid_event)
189		hid->hiddev_hid_event(hid, field, usage, value);
190
191	return 1;
192}
193
194static int mosart_probe(struct hid_device *hdev, const struct hid_device_id *id)
195{
196	int ret;
197	struct mosart_data *td;
198
199
200	td = kmalloc(sizeof(struct mosart_data), GFP_KERNEL);
201	if (!td) {
202		dev_err(&hdev->dev, "cannot allocate MosArt data\n");
203		return -ENOMEM;
204	}
205	td->valid = false;
206	td->activity = false;
207	td->activity_now = false;
208	td->first = false;
209	hid_set_drvdata(hdev, td);
210
211	/* currently, it's better to have one evdev device only */
212#if 0
213	hdev->quirks |= HID_QUIRK_MULTI_INPUT;
214#endif
215
216	ret = hid_parse(hdev);
217	if (ret == 0)
218		ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
219
220	if (ret == 0) {
221		struct hid_report_enum *re = hdev->report_enum
222						+ HID_FEATURE_REPORT;
223		struct hid_report *r = re->report_id_hash[7];
224
225		r->field[0]->value[0] = 0x02;
226		usbhid_submit_report(hdev, r, USB_DIR_OUT);
227	} else 
228		kfree(td);
229
230	return ret;
231}
232
233static void mosart_remove(struct hid_device *hdev)
234{
235	hid_hw_stop(hdev);
236	kfree(hid_get_drvdata(hdev));
237	hid_set_drvdata(hdev, NULL);
238}
239
240static const struct hid_device_id mosart_devices[] = {
241	{ HID_USB_DEVICE(USB_VENDOR_ID_ASUS, USB_DEVICE_ID_ASUS_T91MT) },
242	{ }
243};
244MODULE_DEVICE_TABLE(hid, mosart_devices);
245
246static const struct hid_usage_id mosart_grabbed_usages[] = {
247	{ HID_ANY_ID, HID_ANY_ID, HID_ANY_ID },
248	{ HID_ANY_ID - 1, HID_ANY_ID - 1, HID_ANY_ID - 1}
249};
250
251static struct hid_driver mosart_driver = {
252	.name = "mosart",
253	.id_table = mosart_devices,
254	.probe = mosart_probe,
255	.remove = mosart_remove,
256	.input_mapping = mosart_input_mapping,
257	.input_mapped = mosart_input_mapped,
258	.usage_table = mosart_grabbed_usages,
259	.event = mosart_event,
260};
261
262static int __init mosart_init(void)
263{
264	return hid_register_driver(&mosart_driver);
265}
266
267static void __exit mosart_exit(void)
268{
269	hid_unregister_driver(&mosart_driver);
270}
271
272module_init(mosart_init);
273module_exit(mosart_exit);
274