149851ca0SDmitry Torokhov /* 249851ca0SDmitry Torokhov * Xen para-virtual input device 349851ca0SDmitry Torokhov * 449851ca0SDmitry Torokhov * Copyright (C) 2005 Anthony Liguori <aliguori@us.ibm.com> 549851ca0SDmitry Torokhov * Copyright (C) 2006-2008 Red Hat, Inc., Markus Armbruster <armbru@redhat.com> 649851ca0SDmitry Torokhov * 749851ca0SDmitry Torokhov * Based on linux/drivers/input/mouse/sermouse.c 849851ca0SDmitry Torokhov * 949851ca0SDmitry Torokhov * This file is subject to the terms and conditions of the GNU General Public 1049851ca0SDmitry Torokhov * License. See the file COPYING in the main directory of this archive for 1149851ca0SDmitry Torokhov * more details. 1249851ca0SDmitry Torokhov */ 1349851ca0SDmitry Torokhov 1449851ca0SDmitry Torokhov #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 1549851ca0SDmitry Torokhov 1649851ca0SDmitry Torokhov #include <linux/kernel.h> 1749851ca0SDmitry Torokhov #include <linux/errno.h> 1849851ca0SDmitry Torokhov #include <linux/module.h> 1949851ca0SDmitry Torokhov #include <linux/input.h> 2049851ca0SDmitry Torokhov #include <linux/slab.h> 2149851ca0SDmitry Torokhov 2249851ca0SDmitry Torokhov #include <asm/xen/hypervisor.h> 2349851ca0SDmitry Torokhov 2449851ca0SDmitry Torokhov #include <xen/xen.h> 2549851ca0SDmitry Torokhov #include <xen/events.h> 2649851ca0SDmitry Torokhov #include <xen/page.h> 2749851ca0SDmitry Torokhov #include <xen/grant_table.h> 2849851ca0SDmitry Torokhov #include <xen/interface/grant_table.h> 2949851ca0SDmitry Torokhov #include <xen/interface/io/fbif.h> 3049851ca0SDmitry Torokhov #include <xen/interface/io/kbdif.h> 3149851ca0SDmitry Torokhov #include <xen/xenbus.h> 3251c71a3bSKonrad Rzeszutek Wilk #include <xen/platform_pci.h> 3349851ca0SDmitry Torokhov 3449851ca0SDmitry Torokhov struct xenkbd_info { 3549851ca0SDmitry Torokhov struct input_dev *kbd; 3649851ca0SDmitry Torokhov struct input_dev *ptr; 3749851ca0SDmitry Torokhov struct xenkbd_page *page; 3849851ca0SDmitry Torokhov int gref; 3949851ca0SDmitry Torokhov int irq; 4049851ca0SDmitry Torokhov struct xenbus_device *xbdev; 4149851ca0SDmitry Torokhov char phys[32]; 4249851ca0SDmitry Torokhov }; 4349851ca0SDmitry Torokhov 4449851ca0SDmitry Torokhov static int xenkbd_remove(struct xenbus_device *); 4549851ca0SDmitry Torokhov static int xenkbd_connect_backend(struct xenbus_device *, struct xenkbd_info *); 4649851ca0SDmitry Torokhov static void xenkbd_disconnect_backend(struct xenkbd_info *); 4749851ca0SDmitry Torokhov 4849851ca0SDmitry Torokhov /* 4949851ca0SDmitry Torokhov * Note: if you need to send out events, see xenfb_do_update() for how 5049851ca0SDmitry Torokhov * to do that. 5149851ca0SDmitry Torokhov */ 5249851ca0SDmitry Torokhov 5349851ca0SDmitry Torokhov static irqreturn_t input_handler(int rq, void *dev_id) 5449851ca0SDmitry Torokhov { 5549851ca0SDmitry Torokhov struct xenkbd_info *info = dev_id; 5649851ca0SDmitry Torokhov struct xenkbd_page *page = info->page; 5749851ca0SDmitry Torokhov __u32 cons, prod; 5849851ca0SDmitry Torokhov 5949851ca0SDmitry Torokhov prod = page->in_prod; 6049851ca0SDmitry Torokhov if (prod == page->in_cons) 6149851ca0SDmitry Torokhov return IRQ_HANDLED; 6249851ca0SDmitry Torokhov rmb(); /* ensure we see ring contents up to prod */ 6349851ca0SDmitry Torokhov for (cons = page->in_cons; cons != prod; cons++) { 6449851ca0SDmitry Torokhov union xenkbd_in_event *event; 6549851ca0SDmitry Torokhov struct input_dev *dev; 6649851ca0SDmitry Torokhov event = &XENKBD_IN_RING_REF(page, cons); 6749851ca0SDmitry Torokhov 6849851ca0SDmitry Torokhov dev = info->ptr; 6949851ca0SDmitry Torokhov switch (event->type) { 7049851ca0SDmitry Torokhov case XENKBD_TYPE_MOTION: 7149851ca0SDmitry Torokhov input_report_rel(dev, REL_X, event->motion.rel_x); 7249851ca0SDmitry Torokhov input_report_rel(dev, REL_Y, event->motion.rel_y); 7349851ca0SDmitry Torokhov if (event->motion.rel_z) 7449851ca0SDmitry Torokhov input_report_rel(dev, REL_WHEEL, 7549851ca0SDmitry Torokhov -event->motion.rel_z); 7649851ca0SDmitry Torokhov break; 7749851ca0SDmitry Torokhov case XENKBD_TYPE_KEY: 7849851ca0SDmitry Torokhov dev = NULL; 7949851ca0SDmitry Torokhov if (test_bit(event->key.keycode, info->kbd->keybit)) 8049851ca0SDmitry Torokhov dev = info->kbd; 8149851ca0SDmitry Torokhov if (test_bit(event->key.keycode, info->ptr->keybit)) 8249851ca0SDmitry Torokhov dev = info->ptr; 8349851ca0SDmitry Torokhov if (dev) 8449851ca0SDmitry Torokhov input_report_key(dev, event->key.keycode, 8549851ca0SDmitry Torokhov event->key.pressed); 8649851ca0SDmitry Torokhov else 8749851ca0SDmitry Torokhov pr_warning("unhandled keycode 0x%x\n", 8849851ca0SDmitry Torokhov event->key.keycode); 8949851ca0SDmitry Torokhov break; 9049851ca0SDmitry Torokhov case XENKBD_TYPE_POS: 9149851ca0SDmitry Torokhov input_report_abs(dev, ABS_X, event->pos.abs_x); 9249851ca0SDmitry Torokhov input_report_abs(dev, ABS_Y, event->pos.abs_y); 9349851ca0SDmitry Torokhov if (event->pos.rel_z) 9449851ca0SDmitry Torokhov input_report_rel(dev, REL_WHEEL, 9549851ca0SDmitry Torokhov -event->pos.rel_z); 9649851ca0SDmitry Torokhov break; 9749851ca0SDmitry Torokhov } 9849851ca0SDmitry Torokhov if (dev) 9949851ca0SDmitry Torokhov input_sync(dev); 10049851ca0SDmitry Torokhov } 10149851ca0SDmitry Torokhov mb(); /* ensure we got ring contents */ 10249851ca0SDmitry Torokhov page->in_cons = cons; 10349851ca0SDmitry Torokhov notify_remote_via_irq(info->irq); 10449851ca0SDmitry Torokhov 10549851ca0SDmitry Torokhov return IRQ_HANDLED; 10649851ca0SDmitry Torokhov } 10749851ca0SDmitry Torokhov 1085298cc4cSBill Pemberton static int xenkbd_probe(struct xenbus_device *dev, 10949851ca0SDmitry Torokhov const struct xenbus_device_id *id) 11049851ca0SDmitry Torokhov { 11149851ca0SDmitry Torokhov int ret, i, abs; 11249851ca0SDmitry Torokhov struct xenkbd_info *info; 11349851ca0SDmitry Torokhov struct input_dev *kbd, *ptr; 11449851ca0SDmitry Torokhov 11549851ca0SDmitry Torokhov info = kzalloc(sizeof(*info), GFP_KERNEL); 11649851ca0SDmitry Torokhov if (!info) { 11749851ca0SDmitry Torokhov xenbus_dev_fatal(dev, -ENOMEM, "allocating info structure"); 11849851ca0SDmitry Torokhov return -ENOMEM; 11949851ca0SDmitry Torokhov } 12049851ca0SDmitry Torokhov dev_set_drvdata(&dev->dev, info); 12149851ca0SDmitry Torokhov info->xbdev = dev; 12249851ca0SDmitry Torokhov info->irq = -1; 12349851ca0SDmitry Torokhov info->gref = -1; 12449851ca0SDmitry Torokhov snprintf(info->phys, sizeof(info->phys), "xenbus/%s", dev->nodename); 12549851ca0SDmitry Torokhov 12649851ca0SDmitry Torokhov info->page = (void *)__get_free_page(GFP_KERNEL | __GFP_ZERO); 12749851ca0SDmitry Torokhov if (!info->page) 12849851ca0SDmitry Torokhov goto error_nomem; 12949851ca0SDmitry Torokhov 13049851ca0SDmitry Torokhov if (xenbus_scanf(XBT_NIL, dev->otherend, "feature-abs-pointer", "%d", &abs) < 0) 13149851ca0SDmitry Torokhov abs = 0; 132ec7aa963SInsu Yun if (abs) { 133cd6763beSJan Beulich ret = xenbus_write(XBT_NIL, dev->nodename, 134ec7aa963SInsu Yun "request-abs-pointer", "1"); 135ec7aa963SInsu Yun if (ret) { 136ec7aa963SInsu Yun pr_warning("xenkbd: can't request abs-pointer"); 137ec7aa963SInsu Yun abs = 0; 138ec7aa963SInsu Yun } 139ec7aa963SInsu Yun } 14049851ca0SDmitry Torokhov 14149851ca0SDmitry Torokhov /* keyboard */ 14249851ca0SDmitry Torokhov kbd = input_allocate_device(); 14349851ca0SDmitry Torokhov if (!kbd) 14449851ca0SDmitry Torokhov goto error_nomem; 14549851ca0SDmitry Torokhov kbd->name = "Xen Virtual Keyboard"; 14649851ca0SDmitry Torokhov kbd->phys = info->phys; 14749851ca0SDmitry Torokhov kbd->id.bustype = BUS_PCI; 14849851ca0SDmitry Torokhov kbd->id.vendor = 0x5853; 14949851ca0SDmitry Torokhov kbd->id.product = 0xffff; 15049851ca0SDmitry Torokhov 15149851ca0SDmitry Torokhov __set_bit(EV_KEY, kbd->evbit); 15249851ca0SDmitry Torokhov for (i = KEY_ESC; i < KEY_UNKNOWN; i++) 15349851ca0SDmitry Torokhov __set_bit(i, kbd->keybit); 15449851ca0SDmitry Torokhov for (i = KEY_OK; i < KEY_MAX; i++) 15549851ca0SDmitry Torokhov __set_bit(i, kbd->keybit); 15649851ca0SDmitry Torokhov 15749851ca0SDmitry Torokhov ret = input_register_device(kbd); 15849851ca0SDmitry Torokhov if (ret) { 15949851ca0SDmitry Torokhov input_free_device(kbd); 16049851ca0SDmitry Torokhov xenbus_dev_fatal(dev, ret, "input_register_device(kbd)"); 16149851ca0SDmitry Torokhov goto error; 16249851ca0SDmitry Torokhov } 16349851ca0SDmitry Torokhov info->kbd = kbd; 16449851ca0SDmitry Torokhov 16549851ca0SDmitry Torokhov /* pointing device */ 16649851ca0SDmitry Torokhov ptr = input_allocate_device(); 16749851ca0SDmitry Torokhov if (!ptr) 16849851ca0SDmitry Torokhov goto error_nomem; 16949851ca0SDmitry Torokhov ptr->name = "Xen Virtual Pointer"; 17049851ca0SDmitry Torokhov ptr->phys = info->phys; 17149851ca0SDmitry Torokhov ptr->id.bustype = BUS_PCI; 17249851ca0SDmitry Torokhov ptr->id.vendor = 0x5853; 17349851ca0SDmitry Torokhov ptr->id.product = 0xfffe; 17449851ca0SDmitry Torokhov 17549851ca0SDmitry Torokhov if (abs) { 17649851ca0SDmitry Torokhov __set_bit(EV_ABS, ptr->evbit); 17749851ca0SDmitry Torokhov input_set_abs_params(ptr, ABS_X, 0, XENFB_WIDTH, 0, 0); 17849851ca0SDmitry Torokhov input_set_abs_params(ptr, ABS_Y, 0, XENFB_HEIGHT, 0, 0); 17949851ca0SDmitry Torokhov } else { 18049851ca0SDmitry Torokhov input_set_capability(ptr, EV_REL, REL_X); 18149851ca0SDmitry Torokhov input_set_capability(ptr, EV_REL, REL_Y); 18249851ca0SDmitry Torokhov } 18349851ca0SDmitry Torokhov input_set_capability(ptr, EV_REL, REL_WHEEL); 18449851ca0SDmitry Torokhov 18549851ca0SDmitry Torokhov __set_bit(EV_KEY, ptr->evbit); 18649851ca0SDmitry Torokhov for (i = BTN_LEFT; i <= BTN_TASK; i++) 18749851ca0SDmitry Torokhov __set_bit(i, ptr->keybit); 18849851ca0SDmitry Torokhov 18949851ca0SDmitry Torokhov ret = input_register_device(ptr); 19049851ca0SDmitry Torokhov if (ret) { 19149851ca0SDmitry Torokhov input_free_device(ptr); 19249851ca0SDmitry Torokhov xenbus_dev_fatal(dev, ret, "input_register_device(ptr)"); 19349851ca0SDmitry Torokhov goto error; 19449851ca0SDmitry Torokhov } 19549851ca0SDmitry Torokhov info->ptr = ptr; 19649851ca0SDmitry Torokhov 19749851ca0SDmitry Torokhov ret = xenkbd_connect_backend(dev, info); 19849851ca0SDmitry Torokhov if (ret < 0) 19949851ca0SDmitry Torokhov goto error; 20049851ca0SDmitry Torokhov 20149851ca0SDmitry Torokhov return 0; 20249851ca0SDmitry Torokhov 20349851ca0SDmitry Torokhov error_nomem: 20449851ca0SDmitry Torokhov ret = -ENOMEM; 20549851ca0SDmitry Torokhov xenbus_dev_fatal(dev, ret, "allocating device memory"); 20649851ca0SDmitry Torokhov error: 20749851ca0SDmitry Torokhov xenkbd_remove(dev); 20849851ca0SDmitry Torokhov return ret; 20949851ca0SDmitry Torokhov } 21049851ca0SDmitry Torokhov 21149851ca0SDmitry Torokhov static int xenkbd_resume(struct xenbus_device *dev) 21249851ca0SDmitry Torokhov { 21349851ca0SDmitry Torokhov struct xenkbd_info *info = dev_get_drvdata(&dev->dev); 21449851ca0SDmitry Torokhov 21549851ca0SDmitry Torokhov xenkbd_disconnect_backend(info); 21649851ca0SDmitry Torokhov memset(info->page, 0, PAGE_SIZE); 21749851ca0SDmitry Torokhov return xenkbd_connect_backend(dev, info); 21849851ca0SDmitry Torokhov } 21949851ca0SDmitry Torokhov 22049851ca0SDmitry Torokhov static int xenkbd_remove(struct xenbus_device *dev) 22149851ca0SDmitry Torokhov { 22249851ca0SDmitry Torokhov struct xenkbd_info *info = dev_get_drvdata(&dev->dev); 22349851ca0SDmitry Torokhov 22449851ca0SDmitry Torokhov xenkbd_disconnect_backend(info); 22549851ca0SDmitry Torokhov if (info->kbd) 22649851ca0SDmitry Torokhov input_unregister_device(info->kbd); 22749851ca0SDmitry Torokhov if (info->ptr) 22849851ca0SDmitry Torokhov input_unregister_device(info->ptr); 22949851ca0SDmitry Torokhov free_page((unsigned long)info->page); 23049851ca0SDmitry Torokhov kfree(info); 23149851ca0SDmitry Torokhov return 0; 23249851ca0SDmitry Torokhov } 23349851ca0SDmitry Torokhov 23449851ca0SDmitry Torokhov static int xenkbd_connect_backend(struct xenbus_device *dev, 23549851ca0SDmitry Torokhov struct xenkbd_info *info) 23649851ca0SDmitry Torokhov { 23749851ca0SDmitry Torokhov int ret, evtchn; 23849851ca0SDmitry Torokhov struct xenbus_transaction xbt; 23949851ca0SDmitry Torokhov 24049851ca0SDmitry Torokhov ret = gnttab_grant_foreign_access(dev->otherend_id, 2410df4f266SJulien Grall virt_to_gfn(info->page), 0); 24249851ca0SDmitry Torokhov if (ret < 0) 24349851ca0SDmitry Torokhov return ret; 24449851ca0SDmitry Torokhov info->gref = ret; 24549851ca0SDmitry Torokhov 24649851ca0SDmitry Torokhov ret = xenbus_alloc_evtchn(dev, &evtchn); 24749851ca0SDmitry Torokhov if (ret) 24849851ca0SDmitry Torokhov goto error_grant; 24949851ca0SDmitry Torokhov ret = bind_evtchn_to_irqhandler(evtchn, input_handler, 25049851ca0SDmitry Torokhov 0, dev->devicetype, info); 25149851ca0SDmitry Torokhov if (ret < 0) { 25249851ca0SDmitry Torokhov xenbus_dev_fatal(dev, ret, "bind_evtchn_to_irqhandler"); 25349851ca0SDmitry Torokhov goto error_evtchan; 25449851ca0SDmitry Torokhov } 25549851ca0SDmitry Torokhov info->irq = ret; 25649851ca0SDmitry Torokhov 25749851ca0SDmitry Torokhov again: 25849851ca0SDmitry Torokhov ret = xenbus_transaction_start(&xbt); 25949851ca0SDmitry Torokhov if (ret) { 26049851ca0SDmitry Torokhov xenbus_dev_fatal(dev, ret, "starting transaction"); 26149851ca0SDmitry Torokhov goto error_irqh; 26249851ca0SDmitry Torokhov } 26349851ca0SDmitry Torokhov ret = xenbus_printf(xbt, dev->nodename, "page-ref", "%lu", 2640df4f266SJulien Grall virt_to_gfn(info->page)); 26549851ca0SDmitry Torokhov if (ret) 26649851ca0SDmitry Torokhov goto error_xenbus; 26749851ca0SDmitry Torokhov ret = xenbus_printf(xbt, dev->nodename, "page-gref", "%u", info->gref); 26849851ca0SDmitry Torokhov if (ret) 26949851ca0SDmitry Torokhov goto error_xenbus; 27049851ca0SDmitry Torokhov ret = xenbus_printf(xbt, dev->nodename, "event-channel", "%u", 27149851ca0SDmitry Torokhov evtchn); 27249851ca0SDmitry Torokhov if (ret) 27349851ca0SDmitry Torokhov goto error_xenbus; 27449851ca0SDmitry Torokhov ret = xenbus_transaction_end(xbt, 0); 27549851ca0SDmitry Torokhov if (ret) { 27649851ca0SDmitry Torokhov if (ret == -EAGAIN) 27749851ca0SDmitry Torokhov goto again; 27849851ca0SDmitry Torokhov xenbus_dev_fatal(dev, ret, "completing transaction"); 27949851ca0SDmitry Torokhov goto error_irqh; 28049851ca0SDmitry Torokhov } 28149851ca0SDmitry Torokhov 28249851ca0SDmitry Torokhov xenbus_switch_state(dev, XenbusStateInitialised); 28349851ca0SDmitry Torokhov return 0; 28449851ca0SDmitry Torokhov 28549851ca0SDmitry Torokhov error_xenbus: 28649851ca0SDmitry Torokhov xenbus_transaction_end(xbt, 1); 28749851ca0SDmitry Torokhov xenbus_dev_fatal(dev, ret, "writing xenstore"); 28849851ca0SDmitry Torokhov error_irqh: 28949851ca0SDmitry Torokhov unbind_from_irqhandler(info->irq, info); 29049851ca0SDmitry Torokhov info->irq = -1; 29149851ca0SDmitry Torokhov error_evtchan: 29249851ca0SDmitry Torokhov xenbus_free_evtchn(dev, evtchn); 29349851ca0SDmitry Torokhov error_grant: 2944d544e3bSChang Huaixin gnttab_end_foreign_access(info->gref, 0, 0UL); 29549851ca0SDmitry Torokhov info->gref = -1; 29649851ca0SDmitry Torokhov return ret; 29749851ca0SDmitry Torokhov } 29849851ca0SDmitry Torokhov 29949851ca0SDmitry Torokhov static void xenkbd_disconnect_backend(struct xenkbd_info *info) 30049851ca0SDmitry Torokhov { 30149851ca0SDmitry Torokhov if (info->irq >= 0) 30249851ca0SDmitry Torokhov unbind_from_irqhandler(info->irq, info); 30349851ca0SDmitry Torokhov info->irq = -1; 30449851ca0SDmitry Torokhov if (info->gref >= 0) 3054d544e3bSChang Huaixin gnttab_end_foreign_access(info->gref, 0, 0UL); 30649851ca0SDmitry Torokhov info->gref = -1; 30749851ca0SDmitry Torokhov } 30849851ca0SDmitry Torokhov 30949851ca0SDmitry Torokhov static void xenkbd_backend_changed(struct xenbus_device *dev, 31049851ca0SDmitry Torokhov enum xenbus_state backend_state) 31149851ca0SDmitry Torokhov { 31249851ca0SDmitry Torokhov struct xenkbd_info *info = dev_get_drvdata(&dev->dev); 313c36b58e8SIgor Mammedov int ret, val; 31449851ca0SDmitry Torokhov 31549851ca0SDmitry Torokhov switch (backend_state) { 31649851ca0SDmitry Torokhov case XenbusStateInitialising: 31749851ca0SDmitry Torokhov case XenbusStateInitialised: 31849851ca0SDmitry Torokhov case XenbusStateReconfiguring: 31949851ca0SDmitry Torokhov case XenbusStateReconfigured: 32049851ca0SDmitry Torokhov case XenbusStateUnknown: 32149851ca0SDmitry Torokhov break; 32249851ca0SDmitry Torokhov 32349851ca0SDmitry Torokhov case XenbusStateInitWait: 32449851ca0SDmitry Torokhov InitWait: 325c36b58e8SIgor Mammedov ret = xenbus_scanf(XBT_NIL, info->xbdev->otherend, 326c36b58e8SIgor Mammedov "feature-abs-pointer", "%d", &val); 327c36b58e8SIgor Mammedov if (ret < 0) 328c36b58e8SIgor Mammedov val = 0; 329c36b58e8SIgor Mammedov if (val) { 330cd6763beSJan Beulich ret = xenbus_write(XBT_NIL, info->xbdev->nodename, 331c36b58e8SIgor Mammedov "request-abs-pointer", "1"); 332c36b58e8SIgor Mammedov if (ret) 333c36b58e8SIgor Mammedov pr_warning("xenkbd: can't request abs-pointer"); 334c36b58e8SIgor Mammedov } 335c36b58e8SIgor Mammedov 33649851ca0SDmitry Torokhov xenbus_switch_state(dev, XenbusStateConnected); 33749851ca0SDmitry Torokhov break; 33849851ca0SDmitry Torokhov 33949851ca0SDmitry Torokhov case XenbusStateConnected: 34049851ca0SDmitry Torokhov /* 34149851ca0SDmitry Torokhov * Work around xenbus race condition: If backend goes 34249851ca0SDmitry Torokhov * through InitWait to Connected fast enough, we can 34349851ca0SDmitry Torokhov * get Connected twice here. 34449851ca0SDmitry Torokhov */ 34549851ca0SDmitry Torokhov if (dev->state != XenbusStateConnected) 34649851ca0SDmitry Torokhov goto InitWait; /* no InitWait seen yet, fudge it */ 34749851ca0SDmitry Torokhov 34849851ca0SDmitry Torokhov /* Set input abs params to match backend screen res */ 34949851ca0SDmitry Torokhov if (xenbus_scanf(XBT_NIL, info->xbdev->otherend, 35049851ca0SDmitry Torokhov "width", "%d", &val) > 0) 35149851ca0SDmitry Torokhov input_set_abs_params(info->ptr, ABS_X, 0, val, 0, 0); 35249851ca0SDmitry Torokhov 35349851ca0SDmitry Torokhov if (xenbus_scanf(XBT_NIL, info->xbdev->otherend, 35449851ca0SDmitry Torokhov "height", "%d", &val) > 0) 35549851ca0SDmitry Torokhov input_set_abs_params(info->ptr, ABS_Y, 0, val, 0, 0); 35649851ca0SDmitry Torokhov 35749851ca0SDmitry Torokhov break; 35849851ca0SDmitry Torokhov 3592ebb939aSDavid Vrabel case XenbusStateClosed: 3602ebb939aSDavid Vrabel if (dev->state == XenbusStateClosed) 3612ebb939aSDavid Vrabel break; 3622ebb939aSDavid Vrabel /* Missed the backend's CLOSING state -- fallthrough */ 36349851ca0SDmitry Torokhov case XenbusStateClosing: 36449851ca0SDmitry Torokhov xenbus_frontend_closed(dev); 36549851ca0SDmitry Torokhov break; 36649851ca0SDmitry Torokhov } 36749851ca0SDmitry Torokhov } 36849851ca0SDmitry Torokhov 36949851ca0SDmitry Torokhov static const struct xenbus_device_id xenkbd_ids[] = { 37049851ca0SDmitry Torokhov { "vkbd" }, 37149851ca0SDmitry Torokhov { "" } 37249851ca0SDmitry Torokhov }; 37349851ca0SDmitry Torokhov 37495afae48SDavid Vrabel static struct xenbus_driver xenkbd_driver = { 37595afae48SDavid Vrabel .ids = xenkbd_ids, 37649851ca0SDmitry Torokhov .probe = xenkbd_probe, 37749851ca0SDmitry Torokhov .remove = xenkbd_remove, 37849851ca0SDmitry Torokhov .resume = xenkbd_resume, 37949851ca0SDmitry Torokhov .otherend_changed = xenkbd_backend_changed, 38095afae48SDavid Vrabel }; 38149851ca0SDmitry Torokhov 38249851ca0SDmitry Torokhov static int __init xenkbd_init(void) 38349851ca0SDmitry Torokhov { 3845f098ecdSStefano Stabellini if (!xen_domain()) 38549851ca0SDmitry Torokhov return -ENODEV; 38649851ca0SDmitry Torokhov 38749851ca0SDmitry Torokhov /* Nothing to do if running in dom0. */ 38849851ca0SDmitry Torokhov if (xen_initial_domain()) 38949851ca0SDmitry Torokhov return -ENODEV; 39049851ca0SDmitry Torokhov 39151c71a3bSKonrad Rzeszutek Wilk if (!xen_has_pv_devices()) 39251c71a3bSKonrad Rzeszutek Wilk return -ENODEV; 39351c71a3bSKonrad Rzeszutek Wilk 39449851ca0SDmitry Torokhov return xenbus_register_frontend(&xenkbd_driver); 39549851ca0SDmitry Torokhov } 39649851ca0SDmitry Torokhov 39749851ca0SDmitry Torokhov static void __exit xenkbd_cleanup(void) 39849851ca0SDmitry Torokhov { 39949851ca0SDmitry Torokhov xenbus_unregister_driver(&xenkbd_driver); 40049851ca0SDmitry Torokhov } 40149851ca0SDmitry Torokhov 40249851ca0SDmitry Torokhov module_init(xenkbd_init); 40349851ca0SDmitry Torokhov module_exit(xenkbd_cleanup); 40449851ca0SDmitry Torokhov 40549851ca0SDmitry Torokhov MODULE_DESCRIPTION("Xen virtual keyboard/pointer device frontend"); 40649851ca0SDmitry Torokhov MODULE_LICENSE("GPL"); 40749851ca0SDmitry Torokhov MODULE_ALIAS("xen:vkbd"); 408