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 448b3afdfaSJuergen Gross enum { KPARAM_X, KPARAM_Y, KPARAM_CNT }; 458b3afdfaSJuergen Gross static int ptr_size[KPARAM_CNT] = { XENFB_WIDTH, XENFB_HEIGHT }; 468b3afdfaSJuergen Gross module_param_array(ptr_size, int, NULL, 0444); 478b3afdfaSJuergen Gross MODULE_PARM_DESC(ptr_size, 488b3afdfaSJuergen Gross "Pointing device width, height in pixels (default 800,600)"); 498b3afdfaSJuergen Gross 5049851ca0SDmitry Torokhov static int xenkbd_remove(struct xenbus_device *); 5149851ca0SDmitry Torokhov static int xenkbd_connect_backend(struct xenbus_device *, struct xenkbd_info *); 5249851ca0SDmitry Torokhov static void xenkbd_disconnect_backend(struct xenkbd_info *); 5349851ca0SDmitry Torokhov 5449851ca0SDmitry Torokhov /* 5549851ca0SDmitry Torokhov * Note: if you need to send out events, see xenfb_do_update() for how 5649851ca0SDmitry Torokhov * to do that. 5749851ca0SDmitry Torokhov */ 5849851ca0SDmitry Torokhov 5949851ca0SDmitry Torokhov static irqreturn_t input_handler(int rq, void *dev_id) 6049851ca0SDmitry Torokhov { 6149851ca0SDmitry Torokhov struct xenkbd_info *info = dev_id; 6249851ca0SDmitry Torokhov struct xenkbd_page *page = info->page; 6349851ca0SDmitry Torokhov __u32 cons, prod; 6449851ca0SDmitry Torokhov 6549851ca0SDmitry Torokhov prod = page->in_prod; 6649851ca0SDmitry Torokhov if (prod == page->in_cons) 6749851ca0SDmitry Torokhov return IRQ_HANDLED; 6849851ca0SDmitry Torokhov rmb(); /* ensure we see ring contents up to prod */ 6949851ca0SDmitry Torokhov for (cons = page->in_cons; cons != prod; cons++) { 7049851ca0SDmitry Torokhov union xenkbd_in_event *event; 7149851ca0SDmitry Torokhov struct input_dev *dev; 7249851ca0SDmitry Torokhov event = &XENKBD_IN_RING_REF(page, cons); 7349851ca0SDmitry Torokhov 7449851ca0SDmitry Torokhov dev = info->ptr; 7549851ca0SDmitry Torokhov switch (event->type) { 7649851ca0SDmitry Torokhov case XENKBD_TYPE_MOTION: 7749851ca0SDmitry Torokhov input_report_rel(dev, REL_X, event->motion.rel_x); 7849851ca0SDmitry Torokhov input_report_rel(dev, REL_Y, event->motion.rel_y); 7949851ca0SDmitry Torokhov if (event->motion.rel_z) 8049851ca0SDmitry Torokhov input_report_rel(dev, REL_WHEEL, 8149851ca0SDmitry Torokhov -event->motion.rel_z); 8249851ca0SDmitry Torokhov break; 8349851ca0SDmitry Torokhov case XENKBD_TYPE_KEY: 8449851ca0SDmitry Torokhov dev = NULL; 8549851ca0SDmitry Torokhov if (test_bit(event->key.keycode, info->kbd->keybit)) 8649851ca0SDmitry Torokhov dev = info->kbd; 8749851ca0SDmitry Torokhov if (test_bit(event->key.keycode, info->ptr->keybit)) 8849851ca0SDmitry Torokhov dev = info->ptr; 8949851ca0SDmitry Torokhov if (dev) 9049851ca0SDmitry Torokhov input_report_key(dev, event->key.keycode, 9149851ca0SDmitry Torokhov event->key.pressed); 9249851ca0SDmitry Torokhov else 93fef5f569SJoe Perches pr_warn("unhandled keycode 0x%x\n", 9449851ca0SDmitry Torokhov event->key.keycode); 9549851ca0SDmitry Torokhov break; 9649851ca0SDmitry Torokhov case XENKBD_TYPE_POS: 9749851ca0SDmitry Torokhov input_report_abs(dev, ABS_X, event->pos.abs_x); 9849851ca0SDmitry Torokhov input_report_abs(dev, ABS_Y, event->pos.abs_y); 9949851ca0SDmitry Torokhov if (event->pos.rel_z) 10049851ca0SDmitry Torokhov input_report_rel(dev, REL_WHEEL, 10149851ca0SDmitry Torokhov -event->pos.rel_z); 10249851ca0SDmitry Torokhov break; 10349851ca0SDmitry Torokhov } 10449851ca0SDmitry Torokhov if (dev) 10549851ca0SDmitry Torokhov input_sync(dev); 10649851ca0SDmitry Torokhov } 10749851ca0SDmitry Torokhov mb(); /* ensure we got ring contents */ 10849851ca0SDmitry Torokhov page->in_cons = cons; 10949851ca0SDmitry Torokhov notify_remote_via_irq(info->irq); 11049851ca0SDmitry Torokhov 11149851ca0SDmitry Torokhov return IRQ_HANDLED; 11249851ca0SDmitry Torokhov } 11349851ca0SDmitry Torokhov 1145298cc4cSBill Pemberton static int xenkbd_probe(struct xenbus_device *dev, 11549851ca0SDmitry Torokhov const struct xenbus_device_id *id) 11649851ca0SDmitry Torokhov { 11781362c6fSJuergen Gross int ret, i; 11881362c6fSJuergen Gross unsigned int abs; 11949851ca0SDmitry Torokhov struct xenkbd_info *info; 12049851ca0SDmitry Torokhov struct input_dev *kbd, *ptr; 12149851ca0SDmitry Torokhov 12249851ca0SDmitry Torokhov info = kzalloc(sizeof(*info), GFP_KERNEL); 12349851ca0SDmitry Torokhov if (!info) { 12449851ca0SDmitry Torokhov xenbus_dev_fatal(dev, -ENOMEM, "allocating info structure"); 12549851ca0SDmitry Torokhov return -ENOMEM; 12649851ca0SDmitry Torokhov } 12749851ca0SDmitry Torokhov dev_set_drvdata(&dev->dev, info); 12849851ca0SDmitry Torokhov info->xbdev = dev; 12949851ca0SDmitry Torokhov info->irq = -1; 13049851ca0SDmitry Torokhov info->gref = -1; 13149851ca0SDmitry Torokhov snprintf(info->phys, sizeof(info->phys), "xenbus/%s", dev->nodename); 13249851ca0SDmitry Torokhov 13349851ca0SDmitry Torokhov info->page = (void *)__get_free_page(GFP_KERNEL | __GFP_ZERO); 13449851ca0SDmitry Torokhov if (!info->page) 13549851ca0SDmitry Torokhov goto error_nomem; 13649851ca0SDmitry Torokhov 1378b3afdfaSJuergen Gross /* Set input abs params to match backend screen res */ 13881362c6fSJuergen Gross abs = xenbus_read_unsigned(dev->otherend, "feature-abs-pointer", 0); 1398b3afdfaSJuergen Gross ptr_size[KPARAM_X] = xenbus_read_unsigned(dev->otherend, "width", 1408b3afdfaSJuergen Gross ptr_size[KPARAM_X]); 1418b3afdfaSJuergen Gross ptr_size[KPARAM_Y] = xenbus_read_unsigned(dev->otherend, "height", 1428b3afdfaSJuergen Gross ptr_size[KPARAM_Y]); 143ec7aa963SInsu Yun if (abs) { 144cd6763beSJan Beulich ret = xenbus_write(XBT_NIL, dev->nodename, 145ec7aa963SInsu Yun "request-abs-pointer", "1"); 146ec7aa963SInsu Yun if (ret) { 147fef5f569SJoe Perches pr_warn("xenkbd: can't request abs-pointer\n"); 148ec7aa963SInsu Yun abs = 0; 149ec7aa963SInsu Yun } 150ec7aa963SInsu Yun } 15149851ca0SDmitry Torokhov 15249851ca0SDmitry Torokhov /* keyboard */ 15349851ca0SDmitry Torokhov kbd = input_allocate_device(); 15449851ca0SDmitry Torokhov if (!kbd) 15549851ca0SDmitry Torokhov goto error_nomem; 15649851ca0SDmitry Torokhov kbd->name = "Xen Virtual Keyboard"; 15749851ca0SDmitry Torokhov kbd->phys = info->phys; 15849851ca0SDmitry Torokhov kbd->id.bustype = BUS_PCI; 15949851ca0SDmitry Torokhov kbd->id.vendor = 0x5853; 16049851ca0SDmitry Torokhov kbd->id.product = 0xffff; 16149851ca0SDmitry Torokhov 16249851ca0SDmitry Torokhov __set_bit(EV_KEY, kbd->evbit); 16349851ca0SDmitry Torokhov for (i = KEY_ESC; i < KEY_UNKNOWN; i++) 16449851ca0SDmitry Torokhov __set_bit(i, kbd->keybit); 16549851ca0SDmitry Torokhov for (i = KEY_OK; i < KEY_MAX; i++) 16649851ca0SDmitry Torokhov __set_bit(i, kbd->keybit); 16749851ca0SDmitry Torokhov 16849851ca0SDmitry Torokhov ret = input_register_device(kbd); 16949851ca0SDmitry Torokhov if (ret) { 17049851ca0SDmitry Torokhov input_free_device(kbd); 17149851ca0SDmitry Torokhov xenbus_dev_fatal(dev, ret, "input_register_device(kbd)"); 17249851ca0SDmitry Torokhov goto error; 17349851ca0SDmitry Torokhov } 17449851ca0SDmitry Torokhov info->kbd = kbd; 17549851ca0SDmitry Torokhov 17649851ca0SDmitry Torokhov /* pointing device */ 17749851ca0SDmitry Torokhov ptr = input_allocate_device(); 17849851ca0SDmitry Torokhov if (!ptr) 17949851ca0SDmitry Torokhov goto error_nomem; 18049851ca0SDmitry Torokhov ptr->name = "Xen Virtual Pointer"; 18149851ca0SDmitry Torokhov ptr->phys = info->phys; 18249851ca0SDmitry Torokhov ptr->id.bustype = BUS_PCI; 18349851ca0SDmitry Torokhov ptr->id.vendor = 0x5853; 18449851ca0SDmitry Torokhov ptr->id.product = 0xfffe; 18549851ca0SDmitry Torokhov 18649851ca0SDmitry Torokhov if (abs) { 18749851ca0SDmitry Torokhov __set_bit(EV_ABS, ptr->evbit); 1888b3afdfaSJuergen Gross input_set_abs_params(ptr, ABS_X, 0, ptr_size[KPARAM_X], 0, 0); 1898b3afdfaSJuergen Gross input_set_abs_params(ptr, ABS_Y, 0, ptr_size[KPARAM_Y], 0, 0); 19049851ca0SDmitry Torokhov } else { 19149851ca0SDmitry Torokhov input_set_capability(ptr, EV_REL, REL_X); 19249851ca0SDmitry Torokhov input_set_capability(ptr, EV_REL, REL_Y); 19349851ca0SDmitry Torokhov } 19449851ca0SDmitry Torokhov input_set_capability(ptr, EV_REL, REL_WHEEL); 19549851ca0SDmitry Torokhov 19649851ca0SDmitry Torokhov __set_bit(EV_KEY, ptr->evbit); 19749851ca0SDmitry Torokhov for (i = BTN_LEFT; i <= BTN_TASK; i++) 19849851ca0SDmitry Torokhov __set_bit(i, ptr->keybit); 19949851ca0SDmitry Torokhov 20049851ca0SDmitry Torokhov ret = input_register_device(ptr); 20149851ca0SDmitry Torokhov if (ret) { 20249851ca0SDmitry Torokhov input_free_device(ptr); 20349851ca0SDmitry Torokhov xenbus_dev_fatal(dev, ret, "input_register_device(ptr)"); 20449851ca0SDmitry Torokhov goto error; 20549851ca0SDmitry Torokhov } 20649851ca0SDmitry Torokhov info->ptr = ptr; 20749851ca0SDmitry Torokhov 20849851ca0SDmitry Torokhov ret = xenkbd_connect_backend(dev, info); 20949851ca0SDmitry Torokhov if (ret < 0) 21049851ca0SDmitry Torokhov goto error; 21149851ca0SDmitry Torokhov 21249851ca0SDmitry Torokhov return 0; 21349851ca0SDmitry Torokhov 21449851ca0SDmitry Torokhov error_nomem: 21549851ca0SDmitry Torokhov ret = -ENOMEM; 21649851ca0SDmitry Torokhov xenbus_dev_fatal(dev, ret, "allocating device memory"); 21749851ca0SDmitry Torokhov error: 21849851ca0SDmitry Torokhov xenkbd_remove(dev); 21949851ca0SDmitry Torokhov return ret; 22049851ca0SDmitry Torokhov } 22149851ca0SDmitry Torokhov 22249851ca0SDmitry Torokhov static int xenkbd_resume(struct xenbus_device *dev) 22349851ca0SDmitry Torokhov { 22449851ca0SDmitry Torokhov struct xenkbd_info *info = dev_get_drvdata(&dev->dev); 22549851ca0SDmitry Torokhov 22649851ca0SDmitry Torokhov xenkbd_disconnect_backend(info); 22749851ca0SDmitry Torokhov memset(info->page, 0, PAGE_SIZE); 22849851ca0SDmitry Torokhov return xenkbd_connect_backend(dev, info); 22949851ca0SDmitry Torokhov } 23049851ca0SDmitry Torokhov 23149851ca0SDmitry Torokhov static int xenkbd_remove(struct xenbus_device *dev) 23249851ca0SDmitry Torokhov { 23349851ca0SDmitry Torokhov struct xenkbd_info *info = dev_get_drvdata(&dev->dev); 23449851ca0SDmitry Torokhov 23549851ca0SDmitry Torokhov xenkbd_disconnect_backend(info); 23649851ca0SDmitry Torokhov if (info->kbd) 23749851ca0SDmitry Torokhov input_unregister_device(info->kbd); 23849851ca0SDmitry Torokhov if (info->ptr) 23949851ca0SDmitry Torokhov input_unregister_device(info->ptr); 24049851ca0SDmitry Torokhov free_page((unsigned long)info->page); 24149851ca0SDmitry Torokhov kfree(info); 24249851ca0SDmitry Torokhov return 0; 24349851ca0SDmitry Torokhov } 24449851ca0SDmitry Torokhov 24549851ca0SDmitry Torokhov static int xenkbd_connect_backend(struct xenbus_device *dev, 24649851ca0SDmitry Torokhov struct xenkbd_info *info) 24749851ca0SDmitry Torokhov { 24849851ca0SDmitry Torokhov int ret, evtchn; 24949851ca0SDmitry Torokhov struct xenbus_transaction xbt; 25049851ca0SDmitry Torokhov 25149851ca0SDmitry Torokhov ret = gnttab_grant_foreign_access(dev->otherend_id, 2520df4f266SJulien Grall virt_to_gfn(info->page), 0); 25349851ca0SDmitry Torokhov if (ret < 0) 25449851ca0SDmitry Torokhov return ret; 25549851ca0SDmitry Torokhov info->gref = ret; 25649851ca0SDmitry Torokhov 25749851ca0SDmitry Torokhov ret = xenbus_alloc_evtchn(dev, &evtchn); 25849851ca0SDmitry Torokhov if (ret) 25949851ca0SDmitry Torokhov goto error_grant; 26049851ca0SDmitry Torokhov ret = bind_evtchn_to_irqhandler(evtchn, input_handler, 26149851ca0SDmitry Torokhov 0, dev->devicetype, info); 26249851ca0SDmitry Torokhov if (ret < 0) { 26349851ca0SDmitry Torokhov xenbus_dev_fatal(dev, ret, "bind_evtchn_to_irqhandler"); 26449851ca0SDmitry Torokhov goto error_evtchan; 26549851ca0SDmitry Torokhov } 26649851ca0SDmitry Torokhov info->irq = ret; 26749851ca0SDmitry Torokhov 26849851ca0SDmitry Torokhov again: 26949851ca0SDmitry Torokhov ret = xenbus_transaction_start(&xbt); 27049851ca0SDmitry Torokhov if (ret) { 27149851ca0SDmitry Torokhov xenbus_dev_fatal(dev, ret, "starting transaction"); 27249851ca0SDmitry Torokhov goto error_irqh; 27349851ca0SDmitry Torokhov } 27449851ca0SDmitry Torokhov ret = xenbus_printf(xbt, dev->nodename, "page-ref", "%lu", 2750df4f266SJulien Grall virt_to_gfn(info->page)); 27649851ca0SDmitry Torokhov if (ret) 27749851ca0SDmitry Torokhov goto error_xenbus; 27849851ca0SDmitry Torokhov ret = xenbus_printf(xbt, dev->nodename, "page-gref", "%u", info->gref); 27949851ca0SDmitry Torokhov if (ret) 28049851ca0SDmitry Torokhov goto error_xenbus; 28149851ca0SDmitry Torokhov ret = xenbus_printf(xbt, dev->nodename, "event-channel", "%u", 28249851ca0SDmitry Torokhov evtchn); 28349851ca0SDmitry Torokhov if (ret) 28449851ca0SDmitry Torokhov goto error_xenbus; 28549851ca0SDmitry Torokhov ret = xenbus_transaction_end(xbt, 0); 28649851ca0SDmitry Torokhov if (ret) { 28749851ca0SDmitry Torokhov if (ret == -EAGAIN) 28849851ca0SDmitry Torokhov goto again; 28949851ca0SDmitry Torokhov xenbus_dev_fatal(dev, ret, "completing transaction"); 29049851ca0SDmitry Torokhov goto error_irqh; 29149851ca0SDmitry Torokhov } 29249851ca0SDmitry Torokhov 29349851ca0SDmitry Torokhov xenbus_switch_state(dev, XenbusStateInitialised); 29449851ca0SDmitry Torokhov return 0; 29549851ca0SDmitry Torokhov 29649851ca0SDmitry Torokhov error_xenbus: 29749851ca0SDmitry Torokhov xenbus_transaction_end(xbt, 1); 29849851ca0SDmitry Torokhov xenbus_dev_fatal(dev, ret, "writing xenstore"); 29949851ca0SDmitry Torokhov error_irqh: 30049851ca0SDmitry Torokhov unbind_from_irqhandler(info->irq, info); 30149851ca0SDmitry Torokhov info->irq = -1; 30249851ca0SDmitry Torokhov error_evtchan: 30349851ca0SDmitry Torokhov xenbus_free_evtchn(dev, evtchn); 30449851ca0SDmitry Torokhov error_grant: 3054d544e3bSChang Huaixin gnttab_end_foreign_access(info->gref, 0, 0UL); 30649851ca0SDmitry Torokhov info->gref = -1; 30749851ca0SDmitry Torokhov return ret; 30849851ca0SDmitry Torokhov } 30949851ca0SDmitry Torokhov 31049851ca0SDmitry Torokhov static void xenkbd_disconnect_backend(struct xenkbd_info *info) 31149851ca0SDmitry Torokhov { 31249851ca0SDmitry Torokhov if (info->irq >= 0) 31349851ca0SDmitry Torokhov unbind_from_irqhandler(info->irq, info); 31449851ca0SDmitry Torokhov info->irq = -1; 31549851ca0SDmitry Torokhov if (info->gref >= 0) 3164d544e3bSChang Huaixin gnttab_end_foreign_access(info->gref, 0, 0UL); 31749851ca0SDmitry Torokhov info->gref = -1; 31849851ca0SDmitry Torokhov } 31949851ca0SDmitry Torokhov 32049851ca0SDmitry Torokhov static void xenkbd_backend_changed(struct xenbus_device *dev, 32149851ca0SDmitry Torokhov enum xenbus_state backend_state) 32249851ca0SDmitry Torokhov { 32349851ca0SDmitry Torokhov switch (backend_state) { 32449851ca0SDmitry Torokhov case XenbusStateInitialising: 32549851ca0SDmitry Torokhov case XenbusStateInitialised: 32649851ca0SDmitry Torokhov case XenbusStateReconfiguring: 32749851ca0SDmitry Torokhov case XenbusStateReconfigured: 32849851ca0SDmitry Torokhov case XenbusStateUnknown: 32949851ca0SDmitry Torokhov break; 33049851ca0SDmitry Torokhov 33149851ca0SDmitry Torokhov case XenbusStateInitWait: 33249851ca0SDmitry Torokhov xenbus_switch_state(dev, XenbusStateConnected); 33349851ca0SDmitry Torokhov break; 33449851ca0SDmitry Torokhov 33549851ca0SDmitry Torokhov case XenbusStateConnected: 33649851ca0SDmitry Torokhov /* 33749851ca0SDmitry Torokhov * Work around xenbus race condition: If backend goes 33849851ca0SDmitry Torokhov * through InitWait to Connected fast enough, we can 33949851ca0SDmitry Torokhov * get Connected twice here. 34049851ca0SDmitry Torokhov */ 34149851ca0SDmitry Torokhov if (dev->state != XenbusStateConnected) 3428b3afdfaSJuergen Gross xenbus_switch_state(dev, XenbusStateConnected); 34349851ca0SDmitry Torokhov break; 34449851ca0SDmitry Torokhov 3452ebb939aSDavid Vrabel case XenbusStateClosed: 3462ebb939aSDavid Vrabel if (dev->state == XenbusStateClosed) 3472ebb939aSDavid Vrabel break; 3482ebb939aSDavid Vrabel /* Missed the backend's CLOSING state -- fallthrough */ 34949851ca0SDmitry Torokhov case XenbusStateClosing: 35049851ca0SDmitry Torokhov xenbus_frontend_closed(dev); 35149851ca0SDmitry Torokhov break; 35249851ca0SDmitry Torokhov } 35349851ca0SDmitry Torokhov } 35449851ca0SDmitry Torokhov 35549851ca0SDmitry Torokhov static const struct xenbus_device_id xenkbd_ids[] = { 35649851ca0SDmitry Torokhov { "vkbd" }, 35749851ca0SDmitry Torokhov { "" } 35849851ca0SDmitry Torokhov }; 35949851ca0SDmitry Torokhov 36095afae48SDavid Vrabel static struct xenbus_driver xenkbd_driver = { 36195afae48SDavid Vrabel .ids = xenkbd_ids, 36249851ca0SDmitry Torokhov .probe = xenkbd_probe, 36349851ca0SDmitry Torokhov .remove = xenkbd_remove, 36449851ca0SDmitry Torokhov .resume = xenkbd_resume, 36549851ca0SDmitry Torokhov .otherend_changed = xenkbd_backend_changed, 36695afae48SDavid Vrabel }; 36749851ca0SDmitry Torokhov 36849851ca0SDmitry Torokhov static int __init xenkbd_init(void) 36949851ca0SDmitry Torokhov { 3705f098ecdSStefano Stabellini if (!xen_domain()) 37149851ca0SDmitry Torokhov return -ENODEV; 37249851ca0SDmitry Torokhov 37349851ca0SDmitry Torokhov /* Nothing to do if running in dom0. */ 37449851ca0SDmitry Torokhov if (xen_initial_domain()) 37549851ca0SDmitry Torokhov return -ENODEV; 37649851ca0SDmitry Torokhov 37751c71a3bSKonrad Rzeszutek Wilk if (!xen_has_pv_devices()) 37851c71a3bSKonrad Rzeszutek Wilk return -ENODEV; 37951c71a3bSKonrad Rzeszutek Wilk 38049851ca0SDmitry Torokhov return xenbus_register_frontend(&xenkbd_driver); 38149851ca0SDmitry Torokhov } 38249851ca0SDmitry Torokhov 38349851ca0SDmitry Torokhov static void __exit xenkbd_cleanup(void) 38449851ca0SDmitry Torokhov { 38549851ca0SDmitry Torokhov xenbus_unregister_driver(&xenkbd_driver); 38649851ca0SDmitry Torokhov } 38749851ca0SDmitry Torokhov 38849851ca0SDmitry Torokhov module_init(xenkbd_init); 38949851ca0SDmitry Torokhov module_exit(xenkbd_cleanup); 39049851ca0SDmitry Torokhov 39149851ca0SDmitry Torokhov MODULE_DESCRIPTION("Xen virtual keyboard/pointer device frontend"); 39249851ca0SDmitry Torokhov MODULE_LICENSE("GPL"); 39349851ca0SDmitry Torokhov MODULE_ALIAS("xen:vkbd"); 394