summaryrefslogtreecommitdiff
path: root/usr/src/dkms_source_tree/hid-3m-pct.c
diff options
context:
space:
mode:
authorHenrik Rydberg <rydberg@euromail.se>2010-08-13 17:52:16 +0100
committerHenrik Rydberg <rydberg@euromail.se>2010-08-13 17:52:16 +0100
commitde63db0bd27b24c49ef15be379f55b777b8f219f (patch)
treeb622a34966f4b62c64d108af2e67d17ec433e3e6 /usr/src/dkms_source_tree/hid-3m-pct.c
initial version
Diffstat (limited to 'usr/src/dkms_source_tree/hid-3m-pct.c')
-rw-r--r--usr/src/dkms_source_tree/hid-3m-pct.c289
1 files changed, 289 insertions, 0 deletions
diff --git a/usr/src/dkms_source_tree/hid-3m-pct.c b/usr/src/dkms_source_tree/hid-3m-pct.c
new file mode 100644
index 0000000..fc57c0c
--- /dev/null
+++ b/usr/src/dkms_source_tree/hid-3m-pct.c
@@ -0,0 +1,289 @@
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 int i, index = -1;
129
130 /*
131 * we need to iterate on all fingers to decide if we have a press
132 * or a release event in our touchscreen emulation.
133 */
134 for (i = 0; i < 10; ++i) {
135 struct mmm_finger *f = &md->f[i];
136 if (!f->valid) {
137 /* this finger is just placeholder data, ignore */
138 } else if (f->touch) {
139 /* this finger is on the screen */
140 int wide = (f->w > f->h);
141 input_event(input, EV_ABS, ABS_MT_TRACKING_ID, i);
142 input_event(input, EV_ABS, ABS_MT_POSITION_X, f->x);
143 input_event(input, EV_ABS, ABS_MT_POSITION_Y, f->y);
144 input_event(input, EV_ABS, ABS_MT_ORIENTATION, wide);
145 input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR,
146 wide ? f->w : f->h);
147 input_event(input, EV_ABS, ABS_MT_TOUCH_MINOR,
148 wide ? f->h : f->w);
149 input_mt_sync(input);
150
151 if (index < 0)
152 index = i;
153 }
154 f->valid = 0;
155 }
156
157 /* touchscreen emulation */
158 if (index >= 0) {
159 struct mmm_finger *f = &md->f[index];
160 input_event(input, EV_KEY, BTN_TOUCH, 1);
161 input_event(input, EV_ABS, ABS_X, f->x);
162 input_event(input, EV_ABS, ABS_Y, f->y);
163 } else {
164 input_event(input, EV_KEY, BTN_TOUCH, 0);
165 }
166}
167
168/*
169 * this function is called upon all reports
170 * so that we can accumulate contact point information,
171 * and call input_mt_sync after each point.
172 */
173static int mmm_event(struct hid_device *hid, struct hid_field *field,
174 struct hid_usage *usage, __s32 value)
175{
176 struct mmm_data *md = hid_get_drvdata(hid);
177 /*
178 * strangely, this function can be called before
179 * field->hidinput is initialized!
180 */
181 if (hid->claimed & HID_CLAIMED_INPUT) {
182 struct input_dev *input = field->hidinput->input;
183 switch (usage->hid) {
184 case HID_DG_TIPSWITCH:
185 md->touch = value;
186 break;
187 case HID_DG_CONFIDENCE:
188 md->valid = value;
189 break;
190 case HID_DG_WIDTH:
191 if (md->valid)
192 md->f[md->curid].w = value;
193 break;
194 case HID_DG_HEIGHT:
195 if (md->valid)
196 md->f[md->curid].h = value;
197 break;
198 case HID_DG_CONTACTID:
199 if (md->valid) {
200 md->curid = value;
201 md->f[value].touch = md->touch;
202 md->f[value].valid = 1;
203 }
204 break;
205 case HID_GD_X:
206 if (md->valid)
207 md->f[md->curid].x = value;
208 break;
209 case HID_GD_Y:
210 if (md->valid)
211 md->f[md->curid].y = value;
212 break;
213 case HID_DG_CONTACTCOUNT:
214 mmm_filter_event(md, input);
215 break;
216 }
217 }
218
219 /* we have handled the hidinput part, now remains hiddev */
220 if (hid->claimed & HID_CLAIMED_HIDDEV && hid->hiddev_hid_event)
221 hid->hiddev_hid_event(hid, field, usage, value);
222
223 return 1;
224}
225
226static int mmm_probe(struct hid_device *hdev, const struct hid_device_id *id)
227{
228 int ret;
229 struct mmm_data *md;
230
231 md = kzalloc(sizeof(struct mmm_data), GFP_KERNEL);
232 if (!md) {
233 dev_err(&hdev->dev, "cannot allocate 3M data\n");
234 return -ENOMEM;
235 }
236 hid_set_drvdata(hdev, md);
237
238 ret = hid_parse(hdev);
239 if (!ret)
240 ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
241
242 if (ret)
243 kfree(md);
244 return ret;
245}
246
247static void mmm_remove(struct hid_device *hdev)
248{
249 hid_hw_stop(hdev);
250 kfree(hid_get_drvdata(hdev));
251 hid_set_drvdata(hdev, NULL);
252}
253
254static const struct hid_device_id mmm_devices[] = {
255 { HID_USB_DEVICE(USB_VENDOR_ID_3M, USB_DEVICE_ID_3M1968) },
256 { HID_USB_DEVICE(USB_VENDOR_ID_3M, USB_DEVICE_ID_3M2256) },
257 { }
258};
259MODULE_DEVICE_TABLE(hid, mmm_devices);
260
261static const struct hid_usage_id mmm_grabbed_usages[] = {
262 { HID_ANY_ID, HID_ANY_ID, HID_ANY_ID },
263 { HID_ANY_ID - 1, HID_ANY_ID - 1, HID_ANY_ID - 1}
264};
265
266static struct hid_driver mmm_driver = {
267 .name = "3m-pct",
268 .id_table = mmm_devices,
269 .probe = mmm_probe,
270 .remove = mmm_remove,
271 .input_mapping = mmm_input_mapping,
272 .input_mapped = mmm_input_mapped,
273 .usage_table = mmm_grabbed_usages,
274 .event = mmm_event,
275};
276
277static int __init mmm_init(void)
278{
279 return hid_register_driver(&mmm_driver);
280}
281
282static void __exit mmm_exit(void)
283{
284 hid_unregister_driver(&mmm_driver);
285}
286
287module_init(mmm_init);
288module_exit(mmm_exit);
289