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>
3249851ca0SDmitry Torokhov 
3349851ca0SDmitry Torokhov struct xenkbd_info {
3449851ca0SDmitry Torokhov 	struct input_dev *kbd;
3549851ca0SDmitry Torokhov 	struct input_dev *ptr;
3649851ca0SDmitry Torokhov 	struct xenkbd_page *page;
3749851ca0SDmitry Torokhov 	int gref;
3849851ca0SDmitry Torokhov 	int irq;
3949851ca0SDmitry Torokhov 	struct xenbus_device *xbdev;
4049851ca0SDmitry Torokhov 	char phys[32];
4149851ca0SDmitry Torokhov };
4249851ca0SDmitry Torokhov 
4349851ca0SDmitry Torokhov static int xenkbd_remove(struct xenbus_device *);
4449851ca0SDmitry Torokhov static int xenkbd_connect_backend(struct xenbus_device *, struct xenkbd_info *);
4549851ca0SDmitry Torokhov static void xenkbd_disconnect_backend(struct xenkbd_info *);
4649851ca0SDmitry Torokhov 
4749851ca0SDmitry Torokhov /*
4849851ca0SDmitry Torokhov  * Note: if you need to send out events, see xenfb_do_update() for how
4949851ca0SDmitry Torokhov  * to do that.
5049851ca0SDmitry Torokhov  */
5149851ca0SDmitry Torokhov 
5249851ca0SDmitry Torokhov static irqreturn_t input_handler(int rq, void *dev_id)
5349851ca0SDmitry Torokhov {
5449851ca0SDmitry Torokhov 	struct xenkbd_info *info = dev_id;
5549851ca0SDmitry Torokhov 	struct xenkbd_page *page = info->page;
5649851ca0SDmitry Torokhov 	__u32 cons, prod;
5749851ca0SDmitry Torokhov 
5849851ca0SDmitry Torokhov 	prod = page->in_prod;
5949851ca0SDmitry Torokhov 	if (prod == page->in_cons)
6049851ca0SDmitry Torokhov 		return IRQ_HANDLED;
6149851ca0SDmitry Torokhov 	rmb();			/* ensure we see ring contents up to prod */
6249851ca0SDmitry Torokhov 	for (cons = page->in_cons; cons != prod; cons++) {
6349851ca0SDmitry Torokhov 		union xenkbd_in_event *event;
6449851ca0SDmitry Torokhov 		struct input_dev *dev;
6549851ca0SDmitry Torokhov 		event = &XENKBD_IN_RING_REF(page, cons);
6649851ca0SDmitry Torokhov 
6749851ca0SDmitry Torokhov 		dev = info->ptr;
6849851ca0SDmitry Torokhov 		switch (event->type) {
6949851ca0SDmitry Torokhov 		case XENKBD_TYPE_MOTION:
7049851ca0SDmitry Torokhov 			input_report_rel(dev, REL_X, event->motion.rel_x);
7149851ca0SDmitry Torokhov 			input_report_rel(dev, REL_Y, event->motion.rel_y);
7249851ca0SDmitry Torokhov 			if (event->motion.rel_z)
7349851ca0SDmitry Torokhov 				input_report_rel(dev, REL_WHEEL,
7449851ca0SDmitry Torokhov 						 -event->motion.rel_z);
7549851ca0SDmitry Torokhov 			break;
7649851ca0SDmitry Torokhov 		case XENKBD_TYPE_KEY:
7749851ca0SDmitry Torokhov 			dev = NULL;
7849851ca0SDmitry Torokhov 			if (test_bit(event->key.keycode, info->kbd->keybit))
7949851ca0SDmitry Torokhov 				dev = info->kbd;
8049851ca0SDmitry Torokhov 			if (test_bit(event->key.keycode, info->ptr->keybit))
8149851ca0SDmitry Torokhov 				dev = info->ptr;
8249851ca0SDmitry Torokhov 			if (dev)
8349851ca0SDmitry Torokhov 				input_report_key(dev, event->key.keycode,
8449851ca0SDmitry Torokhov 						 event->key.pressed);
8549851ca0SDmitry Torokhov 			else
8649851ca0SDmitry Torokhov 				pr_warning("unhandled keycode 0x%x\n",
8749851ca0SDmitry Torokhov 					   event->key.keycode);
8849851ca0SDmitry Torokhov 			break;
8949851ca0SDmitry Torokhov 		case XENKBD_TYPE_POS:
9049851ca0SDmitry Torokhov 			input_report_abs(dev, ABS_X, event->pos.abs_x);
9149851ca0SDmitry Torokhov 			input_report_abs(dev, ABS_Y, event->pos.abs_y);
9249851ca0SDmitry Torokhov 			if (event->pos.rel_z)
9349851ca0SDmitry Torokhov 				input_report_rel(dev, REL_WHEEL,
9449851ca0SDmitry Torokhov 						 -event->pos.rel_z);
9549851ca0SDmitry Torokhov 			break;
9649851ca0SDmitry Torokhov 		}
9749851ca0SDmitry Torokhov 		if (dev)
9849851ca0SDmitry Torokhov 			input_sync(dev);
9949851ca0SDmitry Torokhov 	}
10049851ca0SDmitry Torokhov 	mb();			/* ensure we got ring contents */
10149851ca0SDmitry Torokhov 	page->in_cons = cons;
10249851ca0SDmitry Torokhov 	notify_remote_via_irq(info->irq);
10349851ca0SDmitry Torokhov 
10449851ca0SDmitry Torokhov 	return IRQ_HANDLED;
10549851ca0SDmitry Torokhov }
10649851ca0SDmitry Torokhov 
10749851ca0SDmitry Torokhov static int __devinit xenkbd_probe(struct xenbus_device *dev,
10849851ca0SDmitry Torokhov 				  const struct xenbus_device_id *id)
10949851ca0SDmitry Torokhov {
11049851ca0SDmitry Torokhov 	int ret, i, abs;
11149851ca0SDmitry Torokhov 	struct xenkbd_info *info;
11249851ca0SDmitry Torokhov 	struct input_dev *kbd, *ptr;
11349851ca0SDmitry Torokhov 
11449851ca0SDmitry Torokhov 	info = kzalloc(sizeof(*info), GFP_KERNEL);
11549851ca0SDmitry Torokhov 	if (!info) {
11649851ca0SDmitry Torokhov 		xenbus_dev_fatal(dev, -ENOMEM, "allocating info structure");
11749851ca0SDmitry Torokhov 		return -ENOMEM;
11849851ca0SDmitry Torokhov 	}
11949851ca0SDmitry Torokhov 	dev_set_drvdata(&dev->dev, info);
12049851ca0SDmitry Torokhov 	info->xbdev = dev;
12149851ca0SDmitry Torokhov 	info->irq = -1;
12249851ca0SDmitry Torokhov 	info->gref = -1;
12349851ca0SDmitry Torokhov 	snprintf(info->phys, sizeof(info->phys), "xenbus/%s", dev->nodename);
12449851ca0SDmitry Torokhov 
12549851ca0SDmitry Torokhov 	info->page = (void *)__get_free_page(GFP_KERNEL | __GFP_ZERO);
12649851ca0SDmitry Torokhov 	if (!info->page)
12749851ca0SDmitry Torokhov 		goto error_nomem;
12849851ca0SDmitry Torokhov 
12949851ca0SDmitry Torokhov 	if (xenbus_scanf(XBT_NIL, dev->otherend, "feature-abs-pointer", "%d", &abs) < 0)
13049851ca0SDmitry Torokhov 		abs = 0;
13149851ca0SDmitry Torokhov 	if (abs)
13249851ca0SDmitry Torokhov 		xenbus_printf(XBT_NIL, dev->nodename, "request-abs-pointer", "1");
13349851ca0SDmitry Torokhov 
13449851ca0SDmitry Torokhov 	/* keyboard */
13549851ca0SDmitry Torokhov 	kbd = input_allocate_device();
13649851ca0SDmitry Torokhov 	if (!kbd)
13749851ca0SDmitry Torokhov 		goto error_nomem;
13849851ca0SDmitry Torokhov 	kbd->name = "Xen Virtual Keyboard";
13949851ca0SDmitry Torokhov 	kbd->phys = info->phys;
14049851ca0SDmitry Torokhov 	kbd->id.bustype = BUS_PCI;
14149851ca0SDmitry Torokhov 	kbd->id.vendor = 0x5853;
14249851ca0SDmitry Torokhov 	kbd->id.product = 0xffff;
14349851ca0SDmitry Torokhov 
14449851ca0SDmitry Torokhov 	__set_bit(EV_KEY, kbd->evbit);
14549851ca0SDmitry Torokhov 	for (i = KEY_ESC; i < KEY_UNKNOWN; i++)
14649851ca0SDmitry Torokhov 		__set_bit(i, kbd->keybit);
14749851ca0SDmitry Torokhov 	for (i = KEY_OK; i < KEY_MAX; i++)
14849851ca0SDmitry Torokhov 		__set_bit(i, kbd->keybit);
14949851ca0SDmitry Torokhov 
15049851ca0SDmitry Torokhov 	ret = input_register_device(kbd);
15149851ca0SDmitry Torokhov 	if (ret) {
15249851ca0SDmitry Torokhov 		input_free_device(kbd);
15349851ca0SDmitry Torokhov 		xenbus_dev_fatal(dev, ret, "input_register_device(kbd)");
15449851ca0SDmitry Torokhov 		goto error;
15549851ca0SDmitry Torokhov 	}
15649851ca0SDmitry Torokhov 	info->kbd = kbd;
15749851ca0SDmitry Torokhov 
15849851ca0SDmitry Torokhov 	/* pointing device */
15949851ca0SDmitry Torokhov 	ptr = input_allocate_device();
16049851ca0SDmitry Torokhov 	if (!ptr)
16149851ca0SDmitry Torokhov 		goto error_nomem;
16249851ca0SDmitry Torokhov 	ptr->name = "Xen Virtual Pointer";
16349851ca0SDmitry Torokhov 	ptr->phys = info->phys;
16449851ca0SDmitry Torokhov 	ptr->id.bustype = BUS_PCI;
16549851ca0SDmitry Torokhov 	ptr->id.vendor = 0x5853;
16649851ca0SDmitry Torokhov 	ptr->id.product = 0xfffe;
16749851ca0SDmitry Torokhov 
16849851ca0SDmitry Torokhov 	if (abs) {
16949851ca0SDmitry Torokhov 		__set_bit(EV_ABS, ptr->evbit);
17049851ca0SDmitry Torokhov 		input_set_abs_params(ptr, ABS_X, 0, XENFB_WIDTH, 0, 0);
17149851ca0SDmitry Torokhov 		input_set_abs_params(ptr, ABS_Y, 0, XENFB_HEIGHT, 0, 0);
17249851ca0SDmitry Torokhov 	} else {
17349851ca0SDmitry Torokhov 		input_set_capability(ptr, EV_REL, REL_X);
17449851ca0SDmitry Torokhov 		input_set_capability(ptr, EV_REL, REL_Y);
17549851ca0SDmitry Torokhov 	}
17649851ca0SDmitry Torokhov 	input_set_capability(ptr, EV_REL, REL_WHEEL);
17749851ca0SDmitry Torokhov 
17849851ca0SDmitry Torokhov 	__set_bit(EV_KEY, ptr->evbit);
17949851ca0SDmitry Torokhov 	for (i = BTN_LEFT; i <= BTN_TASK; i++)
18049851ca0SDmitry Torokhov 		__set_bit(i, ptr->keybit);
18149851ca0SDmitry Torokhov 
18249851ca0SDmitry Torokhov 	ret = input_register_device(ptr);
18349851ca0SDmitry Torokhov 	if (ret) {
18449851ca0SDmitry Torokhov 		input_free_device(ptr);
18549851ca0SDmitry Torokhov 		xenbus_dev_fatal(dev, ret, "input_register_device(ptr)");
18649851ca0SDmitry Torokhov 		goto error;
18749851ca0SDmitry Torokhov 	}
18849851ca0SDmitry Torokhov 	info->ptr = ptr;
18949851ca0SDmitry Torokhov 
19049851ca0SDmitry Torokhov 	ret = xenkbd_connect_backend(dev, info);
19149851ca0SDmitry Torokhov 	if (ret < 0)
19249851ca0SDmitry Torokhov 		goto error;
19349851ca0SDmitry Torokhov 
19449851ca0SDmitry Torokhov 	return 0;
19549851ca0SDmitry Torokhov 
19649851ca0SDmitry Torokhov  error_nomem:
19749851ca0SDmitry Torokhov 	ret = -ENOMEM;
19849851ca0SDmitry Torokhov 	xenbus_dev_fatal(dev, ret, "allocating device memory");
19949851ca0SDmitry Torokhov  error:
20049851ca0SDmitry Torokhov 	xenkbd_remove(dev);
20149851ca0SDmitry Torokhov 	return ret;
20249851ca0SDmitry Torokhov }
20349851ca0SDmitry Torokhov 
20449851ca0SDmitry Torokhov static int xenkbd_resume(struct xenbus_device *dev)
20549851ca0SDmitry Torokhov {
20649851ca0SDmitry Torokhov 	struct xenkbd_info *info = dev_get_drvdata(&dev->dev);
20749851ca0SDmitry Torokhov 
20849851ca0SDmitry Torokhov 	xenkbd_disconnect_backend(info);
20949851ca0SDmitry Torokhov 	memset(info->page, 0, PAGE_SIZE);
21049851ca0SDmitry Torokhov 	return xenkbd_connect_backend(dev, info);
21149851ca0SDmitry Torokhov }
21249851ca0SDmitry Torokhov 
21349851ca0SDmitry Torokhov static int xenkbd_remove(struct xenbus_device *dev)
21449851ca0SDmitry Torokhov {
21549851ca0SDmitry Torokhov 	struct xenkbd_info *info = dev_get_drvdata(&dev->dev);
21649851ca0SDmitry Torokhov 
21749851ca0SDmitry Torokhov 	xenkbd_disconnect_backend(info);
21849851ca0SDmitry Torokhov 	if (info->kbd)
21949851ca0SDmitry Torokhov 		input_unregister_device(info->kbd);
22049851ca0SDmitry Torokhov 	if (info->ptr)
22149851ca0SDmitry Torokhov 		input_unregister_device(info->ptr);
22249851ca0SDmitry Torokhov 	free_page((unsigned long)info->page);
22349851ca0SDmitry Torokhov 	kfree(info);
22449851ca0SDmitry Torokhov 	return 0;
22549851ca0SDmitry Torokhov }
22649851ca0SDmitry Torokhov 
22749851ca0SDmitry Torokhov static int xenkbd_connect_backend(struct xenbus_device *dev,
22849851ca0SDmitry Torokhov 				  struct xenkbd_info *info)
22949851ca0SDmitry Torokhov {
23049851ca0SDmitry Torokhov 	int ret, evtchn;
23149851ca0SDmitry Torokhov 	struct xenbus_transaction xbt;
23249851ca0SDmitry Torokhov 
23349851ca0SDmitry Torokhov 	ret = gnttab_grant_foreign_access(dev->otherend_id,
23449851ca0SDmitry Torokhov 	                                  virt_to_mfn(info->page), 0);
23549851ca0SDmitry Torokhov 	if (ret < 0)
23649851ca0SDmitry Torokhov 		return ret;
23749851ca0SDmitry Torokhov 	info->gref = ret;
23849851ca0SDmitry Torokhov 
23949851ca0SDmitry Torokhov 	ret = xenbus_alloc_evtchn(dev, &evtchn);
24049851ca0SDmitry Torokhov 	if (ret)
24149851ca0SDmitry Torokhov 		goto error_grant;
24249851ca0SDmitry Torokhov 	ret = bind_evtchn_to_irqhandler(evtchn, input_handler,
24349851ca0SDmitry Torokhov 					0, dev->devicetype, info);
24449851ca0SDmitry Torokhov 	if (ret < 0) {
24549851ca0SDmitry Torokhov 		xenbus_dev_fatal(dev, ret, "bind_evtchn_to_irqhandler");
24649851ca0SDmitry Torokhov 		goto error_evtchan;
24749851ca0SDmitry Torokhov 	}
24849851ca0SDmitry Torokhov 	info->irq = ret;
24949851ca0SDmitry Torokhov 
25049851ca0SDmitry Torokhov  again:
25149851ca0SDmitry Torokhov 	ret = xenbus_transaction_start(&xbt);
25249851ca0SDmitry Torokhov 	if (ret) {
25349851ca0SDmitry Torokhov 		xenbus_dev_fatal(dev, ret, "starting transaction");
25449851ca0SDmitry Torokhov 		goto error_irqh;
25549851ca0SDmitry Torokhov 	}
25649851ca0SDmitry Torokhov 	ret = xenbus_printf(xbt, dev->nodename, "page-ref", "%lu",
25749851ca0SDmitry Torokhov 			    virt_to_mfn(info->page));
25849851ca0SDmitry Torokhov 	if (ret)
25949851ca0SDmitry Torokhov 		goto error_xenbus;
26049851ca0SDmitry Torokhov 	ret = xenbus_printf(xbt, dev->nodename, "page-gref", "%u", info->gref);
26149851ca0SDmitry Torokhov 	if (ret)
26249851ca0SDmitry Torokhov 		goto error_xenbus;
26349851ca0SDmitry Torokhov 	ret = xenbus_printf(xbt, dev->nodename, "event-channel", "%u",
26449851ca0SDmitry Torokhov 			    evtchn);
26549851ca0SDmitry Torokhov 	if (ret)
26649851ca0SDmitry Torokhov 		goto error_xenbus;
26749851ca0SDmitry Torokhov 	ret = xenbus_transaction_end(xbt, 0);
26849851ca0SDmitry Torokhov 	if (ret) {
26949851ca0SDmitry Torokhov 		if (ret == -EAGAIN)
27049851ca0SDmitry Torokhov 			goto again;
27149851ca0SDmitry Torokhov 		xenbus_dev_fatal(dev, ret, "completing transaction");
27249851ca0SDmitry Torokhov 		goto error_irqh;
27349851ca0SDmitry Torokhov 	}
27449851ca0SDmitry Torokhov 
27549851ca0SDmitry Torokhov 	xenbus_switch_state(dev, XenbusStateInitialised);
27649851ca0SDmitry Torokhov 	return 0;
27749851ca0SDmitry Torokhov 
27849851ca0SDmitry Torokhov  error_xenbus:
27949851ca0SDmitry Torokhov 	xenbus_transaction_end(xbt, 1);
28049851ca0SDmitry Torokhov 	xenbus_dev_fatal(dev, ret, "writing xenstore");
28149851ca0SDmitry Torokhov  error_irqh:
28249851ca0SDmitry Torokhov 	unbind_from_irqhandler(info->irq, info);
28349851ca0SDmitry Torokhov 	info->irq = -1;
28449851ca0SDmitry Torokhov  error_evtchan:
28549851ca0SDmitry Torokhov 	xenbus_free_evtchn(dev, evtchn);
28649851ca0SDmitry Torokhov  error_grant:
28749851ca0SDmitry Torokhov 	gnttab_end_foreign_access_ref(info->gref, 0);
28849851ca0SDmitry Torokhov 	info->gref = -1;
28949851ca0SDmitry Torokhov 	return ret;
29049851ca0SDmitry Torokhov }
29149851ca0SDmitry Torokhov 
29249851ca0SDmitry Torokhov static void xenkbd_disconnect_backend(struct xenkbd_info *info)
29349851ca0SDmitry Torokhov {
29449851ca0SDmitry Torokhov 	if (info->irq >= 0)
29549851ca0SDmitry Torokhov 		unbind_from_irqhandler(info->irq, info);
29649851ca0SDmitry Torokhov 	info->irq = -1;
29749851ca0SDmitry Torokhov 	if (info->gref >= 0)
29849851ca0SDmitry Torokhov 		gnttab_end_foreign_access_ref(info->gref, 0);
29949851ca0SDmitry Torokhov 	info->gref = -1;
30049851ca0SDmitry Torokhov }
30149851ca0SDmitry Torokhov 
30249851ca0SDmitry Torokhov static void xenkbd_backend_changed(struct xenbus_device *dev,
30349851ca0SDmitry Torokhov 				   enum xenbus_state backend_state)
30449851ca0SDmitry Torokhov {
30549851ca0SDmitry Torokhov 	struct xenkbd_info *info = dev_get_drvdata(&dev->dev);
30649851ca0SDmitry Torokhov 	int val;
30749851ca0SDmitry Torokhov 
30849851ca0SDmitry Torokhov 	switch (backend_state) {
30949851ca0SDmitry Torokhov 	case XenbusStateInitialising:
31049851ca0SDmitry Torokhov 	case XenbusStateInitialised:
31149851ca0SDmitry Torokhov 	case XenbusStateReconfiguring:
31249851ca0SDmitry Torokhov 	case XenbusStateReconfigured:
31349851ca0SDmitry Torokhov 	case XenbusStateUnknown:
31449851ca0SDmitry Torokhov 	case XenbusStateClosed:
31549851ca0SDmitry Torokhov 		break;
31649851ca0SDmitry Torokhov 
31749851ca0SDmitry Torokhov 	case XenbusStateInitWait:
31849851ca0SDmitry Torokhov InitWait:
31949851ca0SDmitry Torokhov 		xenbus_switch_state(dev, XenbusStateConnected);
32049851ca0SDmitry Torokhov 		break;
32149851ca0SDmitry Torokhov 
32249851ca0SDmitry Torokhov 	case XenbusStateConnected:
32349851ca0SDmitry Torokhov 		/*
32449851ca0SDmitry Torokhov 		 * Work around xenbus race condition: If backend goes
32549851ca0SDmitry Torokhov 		 * through InitWait to Connected fast enough, we can
32649851ca0SDmitry Torokhov 		 * get Connected twice here.
32749851ca0SDmitry Torokhov 		 */
32849851ca0SDmitry Torokhov 		if (dev->state != XenbusStateConnected)
32949851ca0SDmitry Torokhov 			goto InitWait; /* no InitWait seen yet, fudge it */
33049851ca0SDmitry Torokhov 
33149851ca0SDmitry Torokhov 		/* Set input abs params to match backend screen res */
33249851ca0SDmitry Torokhov 		if (xenbus_scanf(XBT_NIL, info->xbdev->otherend,
33349851ca0SDmitry Torokhov 				 "width", "%d", &val) > 0)
33449851ca0SDmitry Torokhov 			input_set_abs_params(info->ptr, ABS_X, 0, val, 0, 0);
33549851ca0SDmitry Torokhov 
33649851ca0SDmitry Torokhov 		if (xenbus_scanf(XBT_NIL, info->xbdev->otherend,
33749851ca0SDmitry Torokhov 				 "height", "%d", &val) > 0)
33849851ca0SDmitry Torokhov 			input_set_abs_params(info->ptr, ABS_Y, 0, val, 0, 0);
33949851ca0SDmitry Torokhov 
34049851ca0SDmitry Torokhov 		break;
34149851ca0SDmitry Torokhov 
34249851ca0SDmitry Torokhov 	case XenbusStateClosing:
34349851ca0SDmitry Torokhov 		xenbus_frontend_closed(dev);
34449851ca0SDmitry Torokhov 		break;
34549851ca0SDmitry Torokhov 	}
34649851ca0SDmitry Torokhov }
34749851ca0SDmitry Torokhov 
34849851ca0SDmitry Torokhov static const struct xenbus_device_id xenkbd_ids[] = {
34949851ca0SDmitry Torokhov 	{ "vkbd" },
35049851ca0SDmitry Torokhov 	{ "" }
35149851ca0SDmitry Torokhov };
35249851ca0SDmitry Torokhov 
35349851ca0SDmitry Torokhov static struct xenbus_driver xenkbd_driver = {
35449851ca0SDmitry Torokhov 	.name = "vkbd",
35549851ca0SDmitry Torokhov 	.owner = THIS_MODULE,
35649851ca0SDmitry Torokhov 	.ids = xenkbd_ids,
35749851ca0SDmitry Torokhov 	.probe = xenkbd_probe,
35849851ca0SDmitry Torokhov 	.remove = xenkbd_remove,
35949851ca0SDmitry Torokhov 	.resume = xenkbd_resume,
36049851ca0SDmitry Torokhov 	.otherend_changed = xenkbd_backend_changed,
36149851ca0SDmitry Torokhov };
36249851ca0SDmitry Torokhov 
36349851ca0SDmitry Torokhov static int __init xenkbd_init(void)
36449851ca0SDmitry Torokhov {
36549851ca0SDmitry Torokhov 	if (!xen_pv_domain())
36649851ca0SDmitry Torokhov 		return -ENODEV;
36749851ca0SDmitry Torokhov 
36849851ca0SDmitry Torokhov 	/* Nothing to do if running in dom0. */
36949851ca0SDmitry Torokhov 	if (xen_initial_domain())
37049851ca0SDmitry Torokhov 		return -ENODEV;
37149851ca0SDmitry Torokhov 
37249851ca0SDmitry Torokhov 	return xenbus_register_frontend(&xenkbd_driver);
37349851ca0SDmitry Torokhov }
37449851ca0SDmitry Torokhov 
37549851ca0SDmitry Torokhov static void __exit xenkbd_cleanup(void)
37649851ca0SDmitry Torokhov {
37749851ca0SDmitry Torokhov 	xenbus_unregister_driver(&xenkbd_driver);
37849851ca0SDmitry Torokhov }
37949851ca0SDmitry Torokhov 
38049851ca0SDmitry Torokhov module_init(xenkbd_init);
38149851ca0SDmitry Torokhov module_exit(xenkbd_cleanup);
38249851ca0SDmitry Torokhov 
38349851ca0SDmitry Torokhov MODULE_DESCRIPTION("Xen virtual keyboard/pointer device frontend");
38449851ca0SDmitry Torokhov MODULE_LICENSE("GPL");
38549851ca0SDmitry Torokhov MODULE_ALIAS("xen:vkbd");
386