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