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