Blame view
drivers/input/misc/xen-kbdfront.c
9.49 KB
4ee36dc08 xen pvfb: Para-vi... |
1 2 3 4 5 6 7 8 9 10 11 12 |
/* * Xen para-virtual input device * * Copyright (C) 2005 Anthony Liguori <aliguori@us.ibm.com> * Copyright (C) 2006-2008 Red Hat, Inc., Markus Armbruster <armbru@redhat.com> * * Based on linux/drivers/input/mouse/sermouse.c * * This file is subject to the terms and conditions of the GNU General Public * License. See the file COPYING in the main directory of this archive for * more details. */ |
da0c49011 Input: use pr_fmt... |
13 |
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
4ee36dc08 xen pvfb: Para-vi... |
14 15 16 17 |
#include <linux/kernel.h> #include <linux/errno.h> #include <linux/module.h> #include <linux/input.h> |
5a0e3ad6a include cleanup: ... |
18 |
#include <linux/slab.h> |
1ccbf5344 xen: move Xen-tes... |
19 |
|
4ee36dc08 xen pvfb: Para-vi... |
20 |
#include <asm/xen/hypervisor.h> |
1ccbf5344 xen: move Xen-tes... |
21 22 |
#include <xen/xen.h> |
4ee36dc08 xen pvfb: Para-vi... |
23 24 |
#include <xen/events.h> #include <xen/page.h> |
0a4dfa5dd Input: xen-kbdfro... |
25 26 |
#include <xen/grant_table.h> #include <xen/interface/grant_table.h> |
4ee36dc08 xen pvfb: Para-vi... |
27 28 29 30 31 32 33 34 |
#include <xen/interface/io/fbif.h> #include <xen/interface/io/kbdif.h> #include <xen/xenbus.h> struct xenkbd_info { struct input_dev *kbd; struct input_dev *ptr; struct xenkbd_page *page; |
0a4dfa5dd Input: xen-kbdfro... |
35 |
int gref; |
4ee36dc08 xen pvfb: Para-vi... |
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
int irq; struct xenbus_device *xbdev; char phys[32]; }; static int xenkbd_remove(struct xenbus_device *); static int xenkbd_connect_backend(struct xenbus_device *, struct xenkbd_info *); static void xenkbd_disconnect_backend(struct xenkbd_info *); /* * Note: if you need to send out events, see xenfb_do_update() for how * to do that. */ static irqreturn_t input_handler(int rq, void *dev_id) { struct xenkbd_info *info = dev_id; struct xenkbd_page *page = info->page; __u32 cons, prod; prod = page->in_prod; if (prod == page->in_cons) return IRQ_HANDLED; rmb(); /* ensure we see ring contents up to prod */ for (cons = page->in_cons; cons != prod; cons++) { union xenkbd_in_event *event; struct input_dev *dev; event = &XENKBD_IN_RING_REF(page, cons); dev = info->ptr; switch (event->type) { case XENKBD_TYPE_MOTION: input_report_rel(dev, REL_X, event->motion.rel_x); input_report_rel(dev, REL_Y, event->motion.rel_y); |
6ba0e7b36 xen pvfb: Pointer... |
70 71 72 |
if (event->motion.rel_z) input_report_rel(dev, REL_WHEEL, -event->motion.rel_z); |
4ee36dc08 xen pvfb: Para-vi... |
73 74 75 76 77 78 79 80 81 82 83 |
break; case XENKBD_TYPE_KEY: dev = NULL; if (test_bit(event->key.keycode, info->kbd->keybit)) dev = info->kbd; if (test_bit(event->key.keycode, info->ptr->keybit)) dev = info->ptr; if (dev) input_report_key(dev, event->key.keycode, event->key.pressed); else |
da0c49011 Input: use pr_fmt... |
84 85 86 |
pr_warning("unhandled keycode 0x%x ", event->key.keycode); |
4ee36dc08 xen pvfb: Para-vi... |
87 88 89 90 |
break; case XENKBD_TYPE_POS: input_report_abs(dev, ABS_X, event->pos.abs_x); input_report_abs(dev, ABS_Y, event->pos.abs_y); |
6ba0e7b36 xen pvfb: Pointer... |
91 92 93 |
if (event->pos.rel_z) input_report_rel(dev, REL_WHEEL, -event->pos.rel_z); |
4ee36dc08 xen pvfb: Para-vi... |
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
break; } if (dev) input_sync(dev); } mb(); /* ensure we got ring contents */ page->in_cons = cons; notify_remote_via_irq(info->irq); return IRQ_HANDLED; } static int __devinit xenkbd_probe(struct xenbus_device *dev, const struct xenbus_device_id *id) { |
8c3c283e6 Input: xen-kbdfro... |
109 |
int ret, i, abs; |
4ee36dc08 xen pvfb: Para-vi... |
110 111 112 113 114 115 116 117 |
struct xenkbd_info *info; struct input_dev *kbd, *ptr; info = kzalloc(sizeof(*info), GFP_KERNEL); if (!info) { xenbus_dev_fatal(dev, -ENOMEM, "allocating info structure"); return -ENOMEM; } |
1b713e005 xen: remove drive... |
118 |
dev_set_drvdata(&dev->dev, info); |
4ee36dc08 xen pvfb: Para-vi... |
119 120 |
info->xbdev = dev; info->irq = -1; |
0a4dfa5dd Input: xen-kbdfro... |
121 |
info->gref = -1; |
4ee36dc08 xen pvfb: Para-vi... |
122 123 124 125 126 |
snprintf(info->phys, sizeof(info->phys), "xenbus/%s", dev->nodename); info->page = (void *)__get_free_page(GFP_KERNEL | __GFP_ZERO); if (!info->page) goto error_nomem; |
8c3c283e6 Input: xen-kbdfro... |
127 128 129 130 |
if (xenbus_scanf(XBT_NIL, dev->otherend, "feature-abs-pointer", "%d", &abs) < 0) abs = 0; if (abs) xenbus_printf(XBT_NIL, dev->nodename, "request-abs-pointer", "1"); |
4ee36dc08 xen pvfb: Para-vi... |
131 132 133 134 135 136 137 138 139 |
/* keyboard */ kbd = input_allocate_device(); if (!kbd) goto error_nomem; kbd->name = "Xen Virtual Keyboard"; kbd->phys = info->phys; kbd->id.bustype = BUS_PCI; kbd->id.vendor = 0x5853; kbd->id.product = 0xffff; |
8c3c283e6 Input: xen-kbdfro... |
140 141 |
__set_bit(EV_KEY, kbd->evbit); |
4ee36dc08 xen pvfb: Para-vi... |
142 |
for (i = KEY_ESC; i < KEY_UNKNOWN; i++) |
8c3c283e6 Input: xen-kbdfro... |
143 |
__set_bit(i, kbd->keybit); |
4ee36dc08 xen pvfb: Para-vi... |
144 |
for (i = KEY_OK; i < KEY_MAX; i++) |
8c3c283e6 Input: xen-kbdfro... |
145 |
__set_bit(i, kbd->keybit); |
4ee36dc08 xen pvfb: Para-vi... |
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 |
ret = input_register_device(kbd); if (ret) { input_free_device(kbd); xenbus_dev_fatal(dev, ret, "input_register_device(kbd)"); goto error; } info->kbd = kbd; /* pointing device */ ptr = input_allocate_device(); if (!ptr) goto error_nomem; ptr->name = "Xen Virtual Pointer"; ptr->phys = info->phys; ptr->id.bustype = BUS_PCI; ptr->id.vendor = 0x5853; ptr->id.product = 0xfffe; |
8c3c283e6 Input: xen-kbdfro... |
164 165 166 167 168 169 170 171 172 173 174 175 |
if (abs) { __set_bit(EV_ABS, ptr->evbit); input_set_abs_params(ptr, ABS_X, 0, XENFB_WIDTH, 0, 0); input_set_abs_params(ptr, ABS_Y, 0, XENFB_HEIGHT, 0, 0); } else { input_set_capability(ptr, EV_REL, REL_X); input_set_capability(ptr, EV_REL, REL_Y); } input_set_capability(ptr, EV_REL, REL_WHEEL); __set_bit(EV_KEY, ptr->evbit); |
4ee36dc08 xen pvfb: Para-vi... |
176 |
for (i = BTN_LEFT; i <= BTN_TASK; i++) |
8c3c283e6 Input: xen-kbdfro... |
177 |
__set_bit(i, ptr->keybit); |
4ee36dc08 xen pvfb: Para-vi... |
178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 |
ret = input_register_device(ptr); if (ret) { input_free_device(ptr); xenbus_dev_fatal(dev, ret, "input_register_device(ptr)"); goto error; } info->ptr = ptr; ret = xenkbd_connect_backend(dev, info); if (ret < 0) goto error; return 0; error_nomem: ret = -ENOMEM; xenbus_dev_fatal(dev, ret, "allocating device memory"); error: xenkbd_remove(dev); return ret; } static int xenkbd_resume(struct xenbus_device *dev) { |
1b713e005 xen: remove drive... |
203 |
struct xenkbd_info *info = dev_get_drvdata(&dev->dev); |
4ee36dc08 xen pvfb: Para-vi... |
204 205 206 207 208 209 210 211 |
xenkbd_disconnect_backend(info); memset(info->page, 0, PAGE_SIZE); return xenkbd_connect_backend(dev, info); } static int xenkbd_remove(struct xenbus_device *dev) { |
1b713e005 xen: remove drive... |
212 |
struct xenkbd_info *info = dev_get_drvdata(&dev->dev); |
4ee36dc08 xen pvfb: Para-vi... |
213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 |
xenkbd_disconnect_backend(info); if (info->kbd) input_unregister_device(info->kbd); if (info->ptr) input_unregister_device(info->ptr); free_page((unsigned long)info->page); kfree(info); return 0; } static int xenkbd_connect_backend(struct xenbus_device *dev, struct xenkbd_info *info) { int ret, evtchn; struct xenbus_transaction xbt; |
0a4dfa5dd Input: xen-kbdfro... |
229 230 231 232 233 |
ret = gnttab_grant_foreign_access(dev->otherend_id, virt_to_mfn(info->page), 0); if (ret < 0) return ret; info->gref = ret; |
4ee36dc08 xen pvfb: Para-vi... |
234 235 |
ret = xenbus_alloc_evtchn(dev, &evtchn); if (ret) |
0a4dfa5dd Input: xen-kbdfro... |
236 |
goto error_grant; |
4ee36dc08 xen pvfb: Para-vi... |
237 238 239 |
ret = bind_evtchn_to_irqhandler(evtchn, input_handler, 0, dev->devicetype, info); if (ret < 0) { |
4ee36dc08 xen pvfb: Para-vi... |
240 |
xenbus_dev_fatal(dev, ret, "bind_evtchn_to_irqhandler"); |
0a4dfa5dd Input: xen-kbdfro... |
241 |
goto error_evtchan; |
4ee36dc08 xen pvfb: Para-vi... |
242 243 244 245 246 247 248 |
} info->irq = ret; again: ret = xenbus_transaction_start(&xbt); if (ret) { xenbus_dev_fatal(dev, ret, "starting transaction"); |
0a4dfa5dd Input: xen-kbdfro... |
249 |
goto error_irqh; |
4ee36dc08 xen pvfb: Para-vi... |
250 251 252 253 254 |
} ret = xenbus_printf(xbt, dev->nodename, "page-ref", "%lu", virt_to_mfn(info->page)); if (ret) goto error_xenbus; |
0a4dfa5dd Input: xen-kbdfro... |
255 256 257 |
ret = xenbus_printf(xbt, dev->nodename, "page-gref", "%u", info->gref); if (ret) goto error_xenbus; |
4ee36dc08 xen pvfb: Para-vi... |
258 259 260 261 262 263 264 265 266 |
ret = xenbus_printf(xbt, dev->nodename, "event-channel", "%u", evtchn); if (ret) goto error_xenbus; ret = xenbus_transaction_end(xbt, 0); if (ret) { if (ret == -EAGAIN) goto again; xenbus_dev_fatal(dev, ret, "completing transaction"); |
0a4dfa5dd Input: xen-kbdfro... |
267 |
goto error_irqh; |
4ee36dc08 xen pvfb: Para-vi... |
268 269 270 271 272 273 274 275 |
} xenbus_switch_state(dev, XenbusStateInitialised); return 0; error_xenbus: xenbus_transaction_end(xbt, 1); xenbus_dev_fatal(dev, ret, "writing xenstore"); |
0a4dfa5dd Input: xen-kbdfro... |
276 277 278 279 280 281 282 283 |
error_irqh: unbind_from_irqhandler(info->irq, info); info->irq = -1; error_evtchan: xenbus_free_evtchn(dev, evtchn); error_grant: gnttab_end_foreign_access_ref(info->gref, 0); info->gref = -1; |
4ee36dc08 xen pvfb: Para-vi... |
284 285 286 287 288 289 290 291 |
return ret; } static void xenkbd_disconnect_backend(struct xenkbd_info *info) { if (info->irq >= 0) unbind_from_irqhandler(info->irq, info); info->irq = -1; |
0a4dfa5dd Input: xen-kbdfro... |
292 293 294 |
if (info->gref >= 0) gnttab_end_foreign_access_ref(info->gref, 0); info->gref = -1; |
4ee36dc08 xen pvfb: Para-vi... |
295 296 297 298 299 |
} static void xenkbd_backend_changed(struct xenbus_device *dev, enum xenbus_state backend_state) { |
1b713e005 xen: remove drive... |
300 |
struct xenkbd_info *info = dev_get_drvdata(&dev->dev); |
c36b58e8a Input: xen-kbdfro... |
301 |
int ret, val; |
4ee36dc08 xen pvfb: Para-vi... |
302 303 304 305 |
switch (backend_state) { case XenbusStateInitialising: case XenbusStateInitialised: |
b78c95125 xenbus: prevent w... |
306 307 |
case XenbusStateReconfiguring: case XenbusStateReconfigured: |
4ee36dc08 xen pvfb: Para-vi... |
308 309 310 311 312 313 |
case XenbusStateUnknown: case XenbusStateClosed: break; case XenbusStateInitWait: InitWait: |
c36b58e8a Input: xen-kbdfro... |
314 315 316 317 318 319 320 321 322 323 |
ret = xenbus_scanf(XBT_NIL, info->xbdev->otherend, "feature-abs-pointer", "%d", &val); if (ret < 0) val = 0; if (val) { ret = xenbus_printf(XBT_NIL, info->xbdev->nodename, "request-abs-pointer", "1"); if (ret) pr_warning("xenkbd: can't request abs-pointer"); } |
4ee36dc08 xen pvfb: Para-vi... |
324 325 326 327 328 329 330 331 332 333 334 |
xenbus_switch_state(dev, XenbusStateConnected); break; case XenbusStateConnected: /* * Work around xenbus race condition: If backend goes * through InitWait to Connected fast enough, we can * get Connected twice here. */ if (dev->state != XenbusStateConnected) goto InitWait; /* no InitWait seen yet, fudge it */ |
e4dcff1f6 xen pvfb: Dynamic... |
335 336 337 338 339 340 341 342 343 |
/* Set input abs params to match backend screen res */ if (xenbus_scanf(XBT_NIL, info->xbdev->otherend, "width", "%d", &val) > 0) input_set_abs_params(info->ptr, ABS_X, 0, val, 0, 0); if (xenbus_scanf(XBT_NIL, info->xbdev->otherend, "height", "%d", &val) > 0) input_set_abs_params(info->ptr, ABS_Y, 0, val, 0, 0); |
4ee36dc08 xen pvfb: Para-vi... |
344 345 346 347 348 349 350 |
break; case XenbusStateClosing: xenbus_frontend_closed(dev); break; } } |
c6d570938 Input: xen-kbdfro... |
351 |
static const struct xenbus_device_id xenkbd_ids[] = { |
4ee36dc08 xen pvfb: Para-vi... |
352 353 354 |
{ "vkbd" }, { "" } }; |
73db144b5 Xen: consolidate ... |
355 |
static DEFINE_XENBUS_DRIVER(xenkbd, , |
4ee36dc08 xen pvfb: Para-vi... |
356 357 358 359 |
.probe = xenkbd_probe, .remove = xenkbd_remove, .resume = xenkbd_resume, .otherend_changed = xenkbd_backend_changed, |
73db144b5 Xen: consolidate ... |
360 |
); |
4ee36dc08 xen pvfb: Para-vi... |
361 362 363 |
static int __init xenkbd_init(void) { |
5f098ecd4 Input: xen-kbdfro... |
364 |
if (!xen_domain()) |
4ee36dc08 xen pvfb: Para-vi... |
365 366 367 |
return -ENODEV; /* Nothing to do if running in dom0. */ |
6e833587e xen: clean up dom... |
368 |
if (xen_initial_domain()) |
4ee36dc08 xen pvfb: Para-vi... |
369 |
return -ENODEV; |
ffb78a261 get xenbus_driver... |
370 |
return xenbus_register_frontend(&xenkbd_driver); |
4ee36dc08 xen pvfb: Para-vi... |
371 372 373 374 |
} static void __exit xenkbd_cleanup(void) { |
ffb78a261 get xenbus_driver... |
375 |
xenbus_unregister_driver(&xenkbd_driver); |
4ee36dc08 xen pvfb: Para-vi... |
376 377 378 379 |
} module_init(xenkbd_init); module_exit(xenkbd_cleanup); |
1e892c959 xen pvfb: Module ... |
380 |
MODULE_DESCRIPTION("Xen virtual keyboard/pointer device frontend"); |
4ee36dc08 xen pvfb: Para-vi... |
381 |
MODULE_LICENSE("GPL"); |
1e892c959 xen pvfb: Module ... |
382 |
MODULE_ALIAS("xen:vkbd"); |