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