diff options
author | Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com> | 2017-06-29 03:20:19 -0400 |
---|---|---|
committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2017-07-09 17:43:26 -0400 |
commit | 49aac8204da5f344f52ed9b3eb8736ca7a60c4a8 (patch) | |
tree | e2e921ae3a25618d6ea1b6e8d45396a923e155b4 | |
parent | 65938133784a3092c61b00aa63d1830fb465c1ac (diff) |
Input: xen-kbdfront - add multi-touch support
Extend xen_kbdfront to provide multi-touch support to unprivileged domains.
Signed-off-by: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com>
[dtor: factor out various sub-protocols - multitouch, single touch, keys]
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
-rw-r--r-- | drivers/input/misc/xen-kbdfront.c | 219 |
1 files changed, 179 insertions, 40 deletions
diff --git a/drivers/input/misc/xen-kbdfront.c b/drivers/input/misc/xen-kbdfront.c index eb770613a9bd..fa130e7b734c 100644 --- a/drivers/input/misc/xen-kbdfront.c +++ b/drivers/input/misc/xen-kbdfront.c | |||
@@ -17,6 +17,7 @@ | |||
17 | #include <linux/errno.h> | 17 | #include <linux/errno.h> |
18 | #include <linux/module.h> | 18 | #include <linux/module.h> |
19 | #include <linux/input.h> | 19 | #include <linux/input.h> |
20 | #include <linux/input/mt.h> | ||
20 | #include <linux/slab.h> | 21 | #include <linux/slab.h> |
21 | 22 | ||
22 | #include <asm/xen/hypervisor.h> | 23 | #include <asm/xen/hypervisor.h> |
@@ -34,11 +35,14 @@ | |||
34 | struct xenkbd_info { | 35 | struct xenkbd_info { |
35 | struct input_dev *kbd; | 36 | struct input_dev *kbd; |
36 | struct input_dev *ptr; | 37 | struct input_dev *ptr; |
38 | struct input_dev *mtouch; | ||
37 | struct xenkbd_page *page; | 39 | struct xenkbd_page *page; |
38 | int gref; | 40 | int gref; |
39 | int irq; | 41 | int irq; |
40 | struct xenbus_device *xbdev; | 42 | struct xenbus_device *xbdev; |
41 | char phys[32]; | 43 | char phys[32]; |
44 | /* current MT slot/contact ID we are injecting events in */ | ||
45 | int mtouch_cur_contact_id; | ||
42 | }; | 46 | }; |
43 | 47 | ||
44 | enum { KPARAM_X, KPARAM_Y, KPARAM_CNT }; | 48 | enum { KPARAM_X, KPARAM_Y, KPARAM_CNT }; |
@@ -56,6 +60,112 @@ static void xenkbd_disconnect_backend(struct xenkbd_info *); | |||
56 | * to do that. | 60 | * to do that. |
57 | */ | 61 | */ |
58 | 62 | ||
63 | static void xenkbd_handle_motion_event(struct xenkbd_info *info, | ||
64 | struct xenkbd_motion *motion) | ||
65 | { | ||
66 | input_report_rel(info->ptr, REL_X, motion->rel_x); | ||
67 | input_report_rel(info->ptr, REL_Y, motion->rel_y); | ||
68 | if (motion->rel_z) | ||
69 | input_report_rel(info->ptr, REL_WHEEL, -motion->rel_z); | ||
70 | input_sync(info->ptr); | ||
71 | } | ||
72 | |||
73 | static void xenkbd_handle_position_event(struct xenkbd_info *info, | ||
74 | struct xenkbd_position *pos) | ||
75 | { | ||
76 | input_report_abs(info->ptr, ABS_X, pos->abs_x); | ||
77 | input_report_abs(info->ptr, ABS_Y, pos->abs_y); | ||
78 | if (pos->rel_z) | ||
79 | input_report_rel(info->ptr, REL_WHEEL, -pos->rel_z); | ||
80 | input_sync(info->ptr); | ||
81 | } | ||
82 | |||
83 | static void xenkbd_handle_key_event(struct xenkbd_info *info, | ||
84 | struct xenkbd_key *key) | ||
85 | { | ||
86 | struct input_dev *dev; | ||
87 | |||
88 | if (test_bit(key->keycode, info->ptr->keybit)) { | ||
89 | dev = info->ptr; | ||
90 | } else if (test_bit(key->keycode, info->kbd->keybit)) { | ||
91 | dev = info->kbd; | ||
92 | } else { | ||
93 | pr_warn("unhandled keycode 0x%x\n", key->keycode); | ||
94 | return; | ||
95 | } | ||
96 | |||
97 | input_report_key(dev, key->keycode, key->pressed); | ||
98 | input_sync(dev); | ||
99 | } | ||
100 | |||
101 | static void xenkbd_handle_mt_event(struct xenkbd_info *info, | ||
102 | struct xenkbd_mtouch *mtouch) | ||
103 | { | ||
104 | if (unlikely(!info->mtouch)) | ||
105 | return; | ||
106 | |||
107 | if (mtouch->contact_id != info->mtouch_cur_contact_id) { | ||
108 | info->mtouch_cur_contact_id = mtouch->contact_id; | ||
109 | input_mt_slot(info->mtouch, mtouch->contact_id); | ||
110 | } | ||
111 | |||
112 | switch (mtouch->event_type) { | ||
113 | case XENKBD_MT_EV_DOWN: | ||
114 | input_mt_report_slot_state(info->mtouch, MT_TOOL_FINGER, true); | ||
115 | /* fall through */ | ||
116 | |||
117 | case XENKBD_MT_EV_MOTION: | ||
118 | input_report_abs(info->mtouch, ABS_MT_POSITION_X, | ||
119 | mtouch->u.pos.abs_x); | ||
120 | input_report_abs(info->mtouch, ABS_MT_POSITION_Y, | ||
121 | mtouch->u.pos.abs_y); | ||
122 | break; | ||
123 | |||
124 | case XENKBD_MT_EV_SHAPE: | ||
125 | input_report_abs(info->mtouch, ABS_MT_TOUCH_MAJOR, | ||
126 | mtouch->u.shape.major); | ||
127 | input_report_abs(info->mtouch, ABS_MT_TOUCH_MINOR, | ||
128 | mtouch->u.shape.minor); | ||
129 | break; | ||
130 | |||
131 | case XENKBD_MT_EV_ORIENT: | ||
132 | input_report_abs(info->mtouch, ABS_MT_ORIENTATION, | ||
133 | mtouch->u.orientation); | ||
134 | break; | ||
135 | |||
136 | case XENKBD_MT_EV_UP: | ||
137 | input_mt_report_slot_state(info->mtouch, MT_TOOL_FINGER, false); | ||
138 | break; | ||
139 | |||
140 | case XENKBD_MT_EV_SYN: | ||
141 | input_mt_sync_frame(info->mtouch); | ||
142 | input_sync(info->mtouch); | ||
143 | break; | ||
144 | } | ||
145 | } | ||
146 | |||
147 | static void xenkbd_handle_event(struct xenkbd_info *info, | ||
148 | union xenkbd_in_event *event) | ||
149 | { | ||
150 | switch (event->type) { | ||
151 | case XENKBD_TYPE_MOTION: | ||
152 | xenkbd_handle_motion_event(info, &event->motion); | ||
153 | break; | ||
154 | |||
155 | case XENKBD_TYPE_KEY: | ||
156 | xenkbd_handle_key_event(info, &event->key); | ||
157 | break; | ||
158 | |||
159 | case XENKBD_TYPE_POS: | ||
160 | xenkbd_handle_position_event(info, &event->pos); | ||
161 | break; | ||
162 | |||
163 | case XENKBD_TYPE_MTOUCH: | ||
164 | xenkbd_handle_mt_event(info, &event->mtouch); | ||
165 | break; | ||
166 | } | ||
167 | } | ||
168 | |||
59 | static irqreturn_t input_handler(int rq, void *dev_id) | 169 | static irqreturn_t input_handler(int rq, void *dev_id) |
60 | { | 170 | { |
61 | struct xenkbd_info *info = dev_id; | 171 | struct xenkbd_info *info = dev_id; |
@@ -66,44 +176,8 @@ static irqreturn_t input_handler(int rq, void *dev_id) | |||
66 | if (prod == page->in_cons) | 176 | if (prod == page->in_cons) |
67 | return IRQ_HANDLED; | 177 | return IRQ_HANDLED; |
68 | rmb(); /* ensure we see ring contents up to prod */ | 178 | rmb(); /* ensure we see ring contents up to prod */ |
69 | for (cons = page->in_cons; cons != prod; cons++) { | 179 | for (cons = page->in_cons; cons != prod; cons++) |
70 | union xenkbd_in_event *event; | 180 | xenkbd_handle_event(info, &XENKBD_IN_RING_REF(page, cons)); |
71 | struct input_dev *dev; | ||
72 | event = &XENKBD_IN_RING_REF(page, cons); | ||
73 | |||
74 | dev = info->ptr; | ||
75 | switch (event->type) { | ||
76 | case XENKBD_TYPE_MOTION: | ||
77 | input_report_rel(dev, REL_X, event->motion.rel_x); | ||
78 | input_report_rel(dev, REL_Y, event->motion.rel_y); | ||
79 | if (event->motion.rel_z) | ||
80 | input_report_rel(dev, REL_WHEEL, | ||
81 | -event->motion.rel_z); | ||
82 | break; | ||
83 | case XENKBD_TYPE_KEY: | ||
84 | dev = NULL; | ||
85 | if (test_bit(event->key.keycode, info->kbd->keybit)) | ||
86 | dev = info->kbd; | ||
87 | if (test_bit(event->key.keycode, info->ptr->keybit)) | ||
88 | dev = info->ptr; | ||
89 | if (dev) | ||
90 | input_report_key(dev, event->key.keycode, | ||
91 | event->key.pressed); | ||
92 | else | ||
93 | pr_warn("unhandled keycode 0x%x\n", | ||
94 | event->key.keycode); | ||
95 | break; | ||
96 | case XENKBD_TYPE_POS: | ||
97 | input_report_abs(dev, ABS_X, event->pos.abs_x); | ||
98 | input_report_abs(dev, ABS_Y, event->pos.abs_y); | ||
99 | if (event->pos.rel_z) | ||
100 | input_report_rel(dev, REL_WHEEL, | ||
101 | -event->pos.rel_z); | ||
102 | break; | ||
103 | } | ||
104 | if (dev) | ||
105 | input_sync(dev); | ||
106 | } | ||
107 | mb(); /* ensure we got ring contents */ | 181 | mb(); /* ensure we got ring contents */ |
108 | page->in_cons = cons; | 182 | page->in_cons = cons; |
109 | notify_remote_via_irq(info->irq); | 183 | notify_remote_via_irq(info->irq); |
@@ -115,9 +189,9 @@ static int xenkbd_probe(struct xenbus_device *dev, | |||
115 | const struct xenbus_device_id *id) | 189 | const struct xenbus_device_id *id) |
116 | { | 190 | { |
117 | int ret, i; | 191 | int ret, i; |
118 | unsigned int abs; | 192 | unsigned int abs, touch; |
119 | struct xenkbd_info *info; | 193 | struct xenkbd_info *info; |
120 | struct input_dev *kbd, *ptr; | 194 | struct input_dev *kbd, *ptr, *mtouch; |
121 | 195 | ||
122 | info = kzalloc(sizeof(*info), GFP_KERNEL); | 196 | info = kzalloc(sizeof(*info), GFP_KERNEL); |
123 | if (!info) { | 197 | if (!info) { |
@@ -152,6 +226,17 @@ static int xenkbd_probe(struct xenbus_device *dev, | |||
152 | } | 226 | } |
153 | } | 227 | } |
154 | 228 | ||
229 | touch = xenbus_read_unsigned(dev->nodename, | ||
230 | XENKBD_FIELD_FEAT_MTOUCH, 0); | ||
231 | if (touch) { | ||
232 | ret = xenbus_write(XBT_NIL, dev->nodename, | ||
233 | XENKBD_FIELD_REQ_MTOUCH, "1"); | ||
234 | if (ret) { | ||
235 | pr_warn("xenkbd: can't request multi-touch"); | ||
236 | touch = 0; | ||
237 | } | ||
238 | } | ||
239 | |||
155 | /* keyboard */ | 240 | /* keyboard */ |
156 | kbd = input_allocate_device(); | 241 | kbd = input_allocate_device(); |
157 | if (!kbd) | 242 | if (!kbd) |
@@ -208,6 +293,58 @@ static int xenkbd_probe(struct xenbus_device *dev, | |||
208 | } | 293 | } |
209 | info->ptr = ptr; | 294 | info->ptr = ptr; |
210 | 295 | ||
296 | /* multi-touch device */ | ||
297 | if (touch) { | ||
298 | int num_cont, width, height; | ||
299 | |||
300 | mtouch = input_allocate_device(); | ||
301 | if (!mtouch) | ||
302 | goto error_nomem; | ||
303 | |||
304 | num_cont = xenbus_read_unsigned(info->xbdev->nodename, | ||
305 | XENKBD_FIELD_MT_NUM_CONTACTS, | ||
306 | 1); | ||
307 | width = xenbus_read_unsigned(info->xbdev->nodename, | ||
308 | XENKBD_FIELD_MT_WIDTH, | ||
309 | XENFB_WIDTH); | ||
310 | height = xenbus_read_unsigned(info->xbdev->nodename, | ||
311 | XENKBD_FIELD_MT_HEIGHT, | ||
312 | XENFB_HEIGHT); | ||
313 | |||
314 | mtouch->name = "Xen Virtual Multi-touch"; | ||
315 | mtouch->phys = info->phys; | ||
316 | mtouch->id.bustype = BUS_PCI; | ||
317 | mtouch->id.vendor = 0x5853; | ||
318 | mtouch->id.product = 0xfffd; | ||
319 | |||
320 | input_set_abs_params(mtouch, ABS_MT_TOUCH_MAJOR, | ||
321 | 0, 255, 0, 0); | ||
322 | input_set_abs_params(mtouch, ABS_MT_POSITION_X, | ||
323 | 0, width, 0, 0); | ||
324 | input_set_abs_params(mtouch, ABS_MT_POSITION_Y, | ||
325 | 0, height, 0, 0); | ||
326 | input_set_abs_params(mtouch, ABS_MT_PRESSURE, | ||
327 | 0, 255, 0, 0); | ||
328 | |||
329 | ret = input_mt_init_slots(mtouch, num_cont, INPUT_MT_DIRECT); | ||
330 | if (ret) { | ||
331 | input_free_device(mtouch); | ||
332 | xenbus_dev_fatal(info->xbdev, ret, | ||
333 | "input_mt_init_slots"); | ||
334 | goto error; | ||
335 | } | ||
336 | |||
337 | ret = input_register_device(mtouch); | ||
338 | if (ret) { | ||
339 | input_free_device(mtouch); | ||
340 | xenbus_dev_fatal(info->xbdev, ret, | ||
341 | "input_register_device(mtouch)"); | ||
342 | goto error; | ||
343 | } | ||
344 | info->mtouch_cur_contact_id = -1; | ||
345 | info->mtouch = mtouch; | ||
346 | } | ||
347 | |||
211 | ret = xenkbd_connect_backend(dev, info); | 348 | ret = xenkbd_connect_backend(dev, info); |
212 | if (ret < 0) | 349 | if (ret < 0) |
213 | goto error; | 350 | goto error; |
@@ -240,6 +377,8 @@ static int xenkbd_remove(struct xenbus_device *dev) | |||
240 | input_unregister_device(info->kbd); | 377 | input_unregister_device(info->kbd); |
241 | if (info->ptr) | 378 | if (info->ptr) |
242 | input_unregister_device(info->ptr); | 379 | input_unregister_device(info->ptr); |
380 | if (info->mtouch) | ||
381 | input_unregister_device(info->mtouch); | ||
243 | free_page((unsigned long)info->page); | 382 | free_page((unsigned long)info->page); |
244 | kfree(info); | 383 | kfree(info); |
245 | return 0; | 384 | return 0; |