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 { 11181362c6fSJuergen Gross int ret, i; 11281362c6fSJuergen Gross unsigned int abs; 11349851ca0SDmitry Torokhov struct xenkbd_info *info; 11449851ca0SDmitry Torokhov struct input_dev *kbd, *ptr; 11549851ca0SDmitry Torokhov 11649851ca0SDmitry Torokhov info = kzalloc(sizeof(*info), GFP_KERNEL); 11749851ca0SDmitry Torokhov if (!info) { 11849851ca0SDmitry Torokhov xenbus_dev_fatal(dev, -ENOMEM, "allocating info structure"); 11949851ca0SDmitry Torokhov return -ENOMEM; 12049851ca0SDmitry Torokhov } 12149851ca0SDmitry Torokhov dev_set_drvdata(&dev->dev, info); 12249851ca0SDmitry Torokhov info->xbdev = dev; 12349851ca0SDmitry Torokhov info->irq = -1; 12449851ca0SDmitry Torokhov info->gref = -1; 12549851ca0SDmitry Torokhov snprintf(info->phys, sizeof(info->phys), "xenbus/%s", dev->nodename); 12649851ca0SDmitry Torokhov 12749851ca0SDmitry Torokhov info->page = (void *)__get_free_page(GFP_KERNEL | __GFP_ZERO); 12849851ca0SDmitry Torokhov if (!info->page) 12949851ca0SDmitry Torokhov goto error_nomem; 13049851ca0SDmitry Torokhov 13181362c6fSJuergen Gross abs = xenbus_read_unsigned(dev->otherend, "feature-abs-pointer", 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: 32581362c6fSJuergen Gross if (xenbus_read_unsigned(info->xbdev->otherend, 32681362c6fSJuergen Gross "feature-abs-pointer", 0)) { 327cd6763beSJan Beulich ret = xenbus_write(XBT_NIL, info->xbdev->nodename, 328c36b58e8SIgor Mammedov "request-abs-pointer", "1"); 329c36b58e8SIgor Mammedov if (ret) 330c36b58e8SIgor Mammedov pr_warning("xenkbd: can't request abs-pointer"); 331c36b58e8SIgor Mammedov } 332c36b58e8SIgor Mammedov 33349851ca0SDmitry Torokhov xenbus_switch_state(dev, XenbusStateConnected); 33449851ca0SDmitry Torokhov break; 33549851ca0SDmitry Torokhov 33649851ca0SDmitry Torokhov case XenbusStateConnected: 33749851ca0SDmitry Torokhov /* 33849851ca0SDmitry Torokhov * Work around xenbus race condition: If backend goes 33949851ca0SDmitry Torokhov * through InitWait to Connected fast enough, we can 34049851ca0SDmitry Torokhov * get Connected twice here. 34149851ca0SDmitry Torokhov */ 34249851ca0SDmitry Torokhov if (dev->state != XenbusStateConnected) 34349851ca0SDmitry Torokhov goto InitWait; /* no InitWait seen yet, fudge it */ 34449851ca0SDmitry Torokhov 34549851ca0SDmitry Torokhov /* Set input abs params to match backend screen res */ 34649851ca0SDmitry Torokhov if (xenbus_scanf(XBT_NIL, info->xbdev->otherend, 34749851ca0SDmitry Torokhov "width", "%d", &val) > 0) 34849851ca0SDmitry Torokhov input_set_abs_params(info->ptr, ABS_X, 0, val, 0, 0); 34949851ca0SDmitry Torokhov 35049851ca0SDmitry Torokhov if (xenbus_scanf(XBT_NIL, info->xbdev->otherend, 35149851ca0SDmitry Torokhov "height", "%d", &val) > 0) 35249851ca0SDmitry Torokhov input_set_abs_params(info->ptr, ABS_Y, 0, val, 0, 0); 35349851ca0SDmitry Torokhov 35449851ca0SDmitry Torokhov break; 35549851ca0SDmitry Torokhov 3562ebb939aSDavid Vrabel case XenbusStateClosed: 3572ebb939aSDavid Vrabel if (dev->state == XenbusStateClosed) 3582ebb939aSDavid Vrabel break; 3592ebb939aSDavid Vrabel /* Missed the backend's CLOSING state -- fallthrough */ 36049851ca0SDmitry Torokhov case XenbusStateClosing: 36149851ca0SDmitry Torokhov xenbus_frontend_closed(dev); 36249851ca0SDmitry Torokhov break; 36349851ca0SDmitry Torokhov } 36449851ca0SDmitry Torokhov } 36549851ca0SDmitry Torokhov 36649851ca0SDmitry Torokhov static const struct xenbus_device_id xenkbd_ids[] = { 36749851ca0SDmitry Torokhov { "vkbd" }, 36849851ca0SDmitry Torokhov { "" } 36949851ca0SDmitry Torokhov }; 37049851ca0SDmitry Torokhov 37195afae48SDavid Vrabel static struct xenbus_driver xenkbd_driver = { 37295afae48SDavid Vrabel .ids = xenkbd_ids, 37349851ca0SDmitry Torokhov .probe = xenkbd_probe, 37449851ca0SDmitry Torokhov .remove = xenkbd_remove, 37549851ca0SDmitry Torokhov .resume = xenkbd_resume, 37649851ca0SDmitry Torokhov .otherend_changed = xenkbd_backend_changed, 37795afae48SDavid Vrabel }; 37849851ca0SDmitry Torokhov 37949851ca0SDmitry Torokhov static int __init xenkbd_init(void) 38049851ca0SDmitry Torokhov { 3815f098ecdSStefano Stabellini if (!xen_domain()) 38249851ca0SDmitry Torokhov return -ENODEV; 38349851ca0SDmitry Torokhov 38449851ca0SDmitry Torokhov /* Nothing to do if running in dom0. */ 38549851ca0SDmitry Torokhov if (xen_initial_domain()) 38649851ca0SDmitry Torokhov return -ENODEV; 38749851ca0SDmitry Torokhov 38851c71a3bSKonrad Rzeszutek Wilk if (!xen_has_pv_devices()) 38951c71a3bSKonrad Rzeszutek Wilk return -ENODEV; 39051c71a3bSKonrad Rzeszutek Wilk 39149851ca0SDmitry Torokhov return xenbus_register_frontend(&xenkbd_driver); 39249851ca0SDmitry Torokhov } 39349851ca0SDmitry Torokhov 39449851ca0SDmitry Torokhov static void __exit xenkbd_cleanup(void) 39549851ca0SDmitry Torokhov { 39649851ca0SDmitry Torokhov xenbus_unregister_driver(&xenkbd_driver); 39749851ca0SDmitry Torokhov } 39849851ca0SDmitry Torokhov 39949851ca0SDmitry Torokhov module_init(xenkbd_init); 40049851ca0SDmitry Torokhov module_exit(xenkbd_cleanup); 40149851ca0SDmitry Torokhov 40249851ca0SDmitry Torokhov MODULE_DESCRIPTION("Xen virtual keyboard/pointer device frontend"); 40349851ca0SDmitry Torokhov MODULE_LICENSE("GPL"); 40449851ca0SDmitry Torokhov MODULE_ALIAS("xen:vkbd"); 405