/* * linux/drivers/input/keyboard/xenkbd.c -- Xen para-virtual input device * * Copyright (C) 2005 * * Anthony Liguori * * 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. */ #include #include #include #include #include #include #include #include struct xenkbd_device { struct input_dev *dev; struct xenkbd_info *info; unsigned evtchn; int irq; }; static irqreturn_t input_handler(int rq, void *dev_id, struct pt_regs *regs) { struct xenkbd_device *dev = dev_id; struct xenkbd_info *info = dev ? dev->info : 0; static int button_map[3] = { BTN_RIGHT, BTN_MIDDLE, BTN_LEFT }; __u32 cons, prod; prod = info->in_prod; rmb(); /* ensure we see ring contents up to prod */ for (cons = info->in_cons; cons != prod; cons++) { union xenkbd_in_event *event; event = &XENKBD_IN_RING_REF(info, cons); switch (event->type) { case XENKBD_TYPE_MOTION: input_report_rel(dev->dev, REL_X, event->motion.rel_x); input_report_rel(dev->dev, REL_Y, event->motion.rel_y); break; case XENKBD_TYPE_BUTTON: if (event->button.button < 3) input_report_key(dev->dev, button_map[event->button.button], event->button.pressed); break; case XENKBD_TYPE_KEY: input_report_key(dev->dev, event->key.keycode, event->key.pressed); break; } } input_sync(dev->dev); mb(); /* ensure we got ring contents */ info->in_cons = cons; notify_remote_via_evtchn(dev->evtchn); return IRQ_HANDLED; } static struct xenkbd_device *xenkbd_dev; int __init xenkbd_init(void) { int ret = 0; int i; struct xenkbd_device *dev; struct input_dev *input_dev; struct evtchn_alloc_unbound alloc_unbound; struct xenbus_transaction xbt; /* Nothing to do if running in dom0. */ if (is_initial_xendomain()) return -ENODEV; #if 1 /* if we're not set up to use graphics mode, then don't initialize */ if (xenbus_scanf(XBT_NIL, "console", "use_graphics", "%d", &ret) < 0) return -ENODEV; if (ret == 0) return -ENODEV; #endif dev = kmalloc(sizeof(*dev), GFP_KERNEL); input_dev = input_allocate_device(); if (!dev || !input_dev) return -ENOMEM; dev->dev = input_dev; dev->info = (void *)__get_free_page(GFP_KERNEL); if (!dev->info) { ret = -ENOMEM; goto error; } alloc_unbound.dom = DOMID_SELF; alloc_unbound.remote_dom = 0; ret = HYPERVISOR_event_channel_op(EVTCHNOP_alloc_unbound, &alloc_unbound); if (ret) goto error_freep; dev->evtchn = alloc_unbound.port; ret = bind_evtchn_to_irqhandler(dev->evtchn, input_handler, 0, "xenkbd", dev); if (ret < 0) goto error_freep; dev->irq = ret; input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REL); input_dev->keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT); input_dev->relbit[0] = BIT(REL_X) | BIT(REL_Y); /* FIXME not sure this is quite right */ for (i = 0; i < 256; i++) set_bit(i, input_dev->keybit); input_dev->name = "Xen Virtual Keyboard/Mouse"; input_register_device(input_dev); again: ret = xenbus_transaction_start(&xbt); if (ret) goto error_unreg; ret = xenbus_printf(xbt, "vkbd", "page-ref", "%lu", virt_to_mfn(dev->info)); if (ret) goto error_xenbus; ret = xenbus_printf(xbt, "vkbd", "event-channel", "%u", dev->evtchn); if (ret) goto error_xenbus; ret = xenbus_transaction_end(xbt, 0); if (ret) { if (ret == -EAGAIN) goto again; goto error_unreg; } dev->info->in_cons = dev->info->in_prod = 0; dev->info->out_cons = dev->info->out_prod = 0; xenkbd_dev = dev; return ret; error_xenbus: xenbus_transaction_end(xbt, 1); error_unreg: input_unregister_device(input_dev); unbind_from_irqhandler(dev->irq, dev); error_freep: free_page((unsigned long)dev->info); error: kfree(dev); return ret; } static void __exit xenkbd_cleanup(void) { input_unregister_device(xenkbd_dev->dev); unbind_from_irqhandler(xenkbd_dev->irq, xenkbd_dev); free_page((unsigned long)xenkbd_dev->info); kfree(xenkbd_dev); xenkbd_dev = NULL; } module_init(xenkbd_init); module_exit(xenkbd_cleanup); MODULE_LICENSE("GPL");