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>
2049aac820SOleksandr Andrushchenko #include <linux/input/mt.h>
2149851ca0SDmitry Torokhov #include <linux/slab.h>
2249851ca0SDmitry Torokhov 
2349851ca0SDmitry Torokhov #include <asm/xen/hypervisor.h>
2449851ca0SDmitry Torokhov 
2549851ca0SDmitry Torokhov #include <xen/xen.h>
2649851ca0SDmitry Torokhov #include <xen/events.h>
2749851ca0SDmitry Torokhov #include <xen/page.h>
2849851ca0SDmitry Torokhov #include <xen/grant_table.h>
2949851ca0SDmitry Torokhov #include <xen/interface/grant_table.h>
3049851ca0SDmitry Torokhov #include <xen/interface/io/fbif.h>
3149851ca0SDmitry Torokhov #include <xen/interface/io/kbdif.h>
3249851ca0SDmitry Torokhov #include <xen/xenbus.h>
3351c71a3bSKonrad Rzeszutek Wilk #include <xen/platform_pci.h>
3449851ca0SDmitry Torokhov 
3549851ca0SDmitry Torokhov struct xenkbd_info {
3649851ca0SDmitry Torokhov 	struct input_dev *kbd;
3749851ca0SDmitry Torokhov 	struct input_dev *ptr;
3849aac820SOleksandr Andrushchenko 	struct input_dev *mtouch;
3949851ca0SDmitry Torokhov 	struct xenkbd_page *page;
4049851ca0SDmitry Torokhov 	int gref;
4149851ca0SDmitry Torokhov 	int irq;
4249851ca0SDmitry Torokhov 	struct xenbus_device *xbdev;
4349851ca0SDmitry Torokhov 	char phys[32];
4449aac820SOleksandr Andrushchenko 	/* current MT slot/contact ID we are injecting events in */
4549aac820SOleksandr Andrushchenko 	int mtouch_cur_contact_id;
4649851ca0SDmitry Torokhov };
4749851ca0SDmitry Torokhov 
488b3afdfaSJuergen Gross enum { KPARAM_X, KPARAM_Y, KPARAM_CNT };
498b3afdfaSJuergen Gross static int ptr_size[KPARAM_CNT] = { XENFB_WIDTH, XENFB_HEIGHT };
508b3afdfaSJuergen Gross module_param_array(ptr_size, int, NULL, 0444);
518b3afdfaSJuergen Gross MODULE_PARM_DESC(ptr_size,
528b3afdfaSJuergen Gross 	"Pointing device width, height in pixels (default 800,600)");
538b3afdfaSJuergen Gross 
54*7cffcadeSDawei Li static void xenkbd_remove(struct xenbus_device *);
5549851ca0SDmitry Torokhov static int xenkbd_connect_backend(struct xenbus_device *, struct xenkbd_info *);
5649851ca0SDmitry Torokhov static void xenkbd_disconnect_backend(struct xenkbd_info *);
5749851ca0SDmitry Torokhov 
5849851ca0SDmitry Torokhov /*
5949851ca0SDmitry Torokhov  * Note: if you need to send out events, see xenfb_do_update() for how
6049851ca0SDmitry Torokhov  * to do that.
6149851ca0SDmitry Torokhov  */
6249851ca0SDmitry Torokhov 
xenkbd_handle_motion_event(struct xenkbd_info * info,struct xenkbd_motion * motion)6349aac820SOleksandr Andrushchenko static void xenkbd_handle_motion_event(struct xenkbd_info *info,
6449aac820SOleksandr Andrushchenko 				       struct xenkbd_motion *motion)
6549aac820SOleksandr Andrushchenko {
663ef8a2eeSOleksandr Andrushchenko 	if (unlikely(!info->ptr))
673ef8a2eeSOleksandr Andrushchenko 		return;
683ef8a2eeSOleksandr Andrushchenko 
6949aac820SOleksandr Andrushchenko 	input_report_rel(info->ptr, REL_X, motion->rel_x);
7049aac820SOleksandr Andrushchenko 	input_report_rel(info->ptr, REL_Y, motion->rel_y);
7149aac820SOleksandr Andrushchenko 	if (motion->rel_z)
7249aac820SOleksandr Andrushchenko 		input_report_rel(info->ptr, REL_WHEEL, -motion->rel_z);
7349aac820SOleksandr Andrushchenko 	input_sync(info->ptr);
7449aac820SOleksandr Andrushchenko }
7549aac820SOleksandr Andrushchenko 
xenkbd_handle_position_event(struct xenkbd_info * info,struct xenkbd_position * pos)7649aac820SOleksandr Andrushchenko static void xenkbd_handle_position_event(struct xenkbd_info *info,
7749aac820SOleksandr Andrushchenko 					 struct xenkbd_position *pos)
7849aac820SOleksandr Andrushchenko {
793ef8a2eeSOleksandr Andrushchenko 	if (unlikely(!info->ptr))
803ef8a2eeSOleksandr Andrushchenko 		return;
813ef8a2eeSOleksandr Andrushchenko 
8249aac820SOleksandr Andrushchenko 	input_report_abs(info->ptr, ABS_X, pos->abs_x);
8349aac820SOleksandr Andrushchenko 	input_report_abs(info->ptr, ABS_Y, pos->abs_y);
8449aac820SOleksandr Andrushchenko 	if (pos->rel_z)
8549aac820SOleksandr Andrushchenko 		input_report_rel(info->ptr, REL_WHEEL, -pos->rel_z);
8649aac820SOleksandr Andrushchenko 	input_sync(info->ptr);
8749aac820SOleksandr Andrushchenko }
8849aac820SOleksandr Andrushchenko 
xenkbd_handle_key_event(struct xenkbd_info * info,struct xenkbd_key * key)8949aac820SOleksandr Andrushchenko static void xenkbd_handle_key_event(struct xenkbd_info *info,
9049aac820SOleksandr Andrushchenko 				    struct xenkbd_key *key)
9149aac820SOleksandr Andrushchenko {
9249aac820SOleksandr Andrushchenko 	struct input_dev *dev;
930ca06810SLiang Yan 	int value = key->pressed;
9449aac820SOleksandr Andrushchenko 
9549aac820SOleksandr Andrushchenko 	if (test_bit(key->keycode, info->ptr->keybit)) {
9649aac820SOleksandr Andrushchenko 		dev = info->ptr;
9749aac820SOleksandr Andrushchenko 	} else if (test_bit(key->keycode, info->kbd->keybit)) {
9849aac820SOleksandr Andrushchenko 		dev = info->kbd;
990ca06810SLiang Yan 		if (key->pressed && test_bit(key->keycode, info->kbd->key))
1000ca06810SLiang Yan 			value = 2; /* Mark as autorepeat */
10149aac820SOleksandr Andrushchenko 	} else {
10249aac820SOleksandr Andrushchenko 		pr_warn("unhandled keycode 0x%x\n", key->keycode);
10349aac820SOleksandr Andrushchenko 		return;
10449aac820SOleksandr Andrushchenko 	}
10549aac820SOleksandr Andrushchenko 
1063ef8a2eeSOleksandr Andrushchenko 	if (unlikely(!dev))
1073ef8a2eeSOleksandr Andrushchenko 		return;
1083ef8a2eeSOleksandr Andrushchenko 
1090ca06810SLiang Yan 	input_event(dev, EV_KEY, key->keycode, value);
11049aac820SOleksandr Andrushchenko 	input_sync(dev);
11149aac820SOleksandr Andrushchenko }
11249aac820SOleksandr Andrushchenko 
xenkbd_handle_mt_event(struct xenkbd_info * info,struct xenkbd_mtouch * mtouch)11349aac820SOleksandr Andrushchenko static void xenkbd_handle_mt_event(struct xenkbd_info *info,
11449aac820SOleksandr Andrushchenko 				   struct xenkbd_mtouch *mtouch)
11549aac820SOleksandr Andrushchenko {
11649aac820SOleksandr Andrushchenko 	if (unlikely(!info->mtouch))
11749aac820SOleksandr Andrushchenko 		return;
11849aac820SOleksandr Andrushchenko 
11949aac820SOleksandr Andrushchenko 	if (mtouch->contact_id != info->mtouch_cur_contact_id) {
12049aac820SOleksandr Andrushchenko 		info->mtouch_cur_contact_id = mtouch->contact_id;
12149aac820SOleksandr Andrushchenko 		input_mt_slot(info->mtouch, mtouch->contact_id);
12249aac820SOleksandr Andrushchenko 	}
12349aac820SOleksandr Andrushchenko 
12449aac820SOleksandr Andrushchenko 	switch (mtouch->event_type) {
12549aac820SOleksandr Andrushchenko 	case XENKBD_MT_EV_DOWN:
12649aac820SOleksandr Andrushchenko 		input_mt_report_slot_state(info->mtouch, MT_TOOL_FINGER, true);
1276f49c4f5SGustavo A. R. Silva 		fallthrough;
12849aac820SOleksandr Andrushchenko 
12949aac820SOleksandr Andrushchenko 	case XENKBD_MT_EV_MOTION:
13049aac820SOleksandr Andrushchenko 		input_report_abs(info->mtouch, ABS_MT_POSITION_X,
13149aac820SOleksandr Andrushchenko 				 mtouch->u.pos.abs_x);
13249aac820SOleksandr Andrushchenko 		input_report_abs(info->mtouch, ABS_MT_POSITION_Y,
13349aac820SOleksandr Andrushchenko 				 mtouch->u.pos.abs_y);
13449aac820SOleksandr Andrushchenko 		break;
13549aac820SOleksandr Andrushchenko 
13649aac820SOleksandr Andrushchenko 	case XENKBD_MT_EV_SHAPE:
13749aac820SOleksandr Andrushchenko 		input_report_abs(info->mtouch, ABS_MT_TOUCH_MAJOR,
13849aac820SOleksandr Andrushchenko 				 mtouch->u.shape.major);
13949aac820SOleksandr Andrushchenko 		input_report_abs(info->mtouch, ABS_MT_TOUCH_MINOR,
14049aac820SOleksandr Andrushchenko 				 mtouch->u.shape.minor);
14149aac820SOleksandr Andrushchenko 		break;
14249aac820SOleksandr Andrushchenko 
14349aac820SOleksandr Andrushchenko 	case XENKBD_MT_EV_ORIENT:
14449aac820SOleksandr Andrushchenko 		input_report_abs(info->mtouch, ABS_MT_ORIENTATION,
14549aac820SOleksandr Andrushchenko 				 mtouch->u.orientation);
14649aac820SOleksandr Andrushchenko 		break;
14749aac820SOleksandr Andrushchenko 
14849aac820SOleksandr Andrushchenko 	case XENKBD_MT_EV_UP:
1495fc70e35SJiada Wang 		input_mt_report_slot_inactive(info->mtouch);
15049aac820SOleksandr Andrushchenko 		break;
15149aac820SOleksandr Andrushchenko 
15249aac820SOleksandr Andrushchenko 	case XENKBD_MT_EV_SYN:
15349aac820SOleksandr Andrushchenko 		input_mt_sync_frame(info->mtouch);
15449aac820SOleksandr Andrushchenko 		input_sync(info->mtouch);
15549aac820SOleksandr Andrushchenko 		break;
15649aac820SOleksandr Andrushchenko 	}
15749aac820SOleksandr Andrushchenko }
15849aac820SOleksandr Andrushchenko 
xenkbd_handle_event(struct xenkbd_info * info,union xenkbd_in_event * event)15949aac820SOleksandr Andrushchenko static void xenkbd_handle_event(struct xenkbd_info *info,
16049aac820SOleksandr Andrushchenko 				union xenkbd_in_event *event)
16149aac820SOleksandr Andrushchenko {
16249aac820SOleksandr Andrushchenko 	switch (event->type) {
16349aac820SOleksandr Andrushchenko 	case XENKBD_TYPE_MOTION:
16449aac820SOleksandr Andrushchenko 		xenkbd_handle_motion_event(info, &event->motion);
16549aac820SOleksandr Andrushchenko 		break;
16649aac820SOleksandr Andrushchenko 
16749aac820SOleksandr Andrushchenko 	case XENKBD_TYPE_KEY:
16849aac820SOleksandr Andrushchenko 		xenkbd_handle_key_event(info, &event->key);
16949aac820SOleksandr Andrushchenko 		break;
17049aac820SOleksandr Andrushchenko 
17149aac820SOleksandr Andrushchenko 	case XENKBD_TYPE_POS:
17249aac820SOleksandr Andrushchenko 		xenkbd_handle_position_event(info, &event->pos);
17349aac820SOleksandr Andrushchenko 		break;
17449aac820SOleksandr Andrushchenko 
17549aac820SOleksandr Andrushchenko 	case XENKBD_TYPE_MTOUCH:
17649aac820SOleksandr Andrushchenko 		xenkbd_handle_mt_event(info, &event->mtouch);
17749aac820SOleksandr Andrushchenko 		break;
17849aac820SOleksandr Andrushchenko 	}
17949aac820SOleksandr Andrushchenko }
18049aac820SOleksandr Andrushchenko 
input_handler(int rq,void * dev_id)18149851ca0SDmitry Torokhov static irqreturn_t input_handler(int rq, void *dev_id)
18249851ca0SDmitry Torokhov {
18349851ca0SDmitry Torokhov 	struct xenkbd_info *info = dev_id;
18449851ca0SDmitry Torokhov 	struct xenkbd_page *page = info->page;
18549851ca0SDmitry Torokhov 	__u32 cons, prod;
18649851ca0SDmitry Torokhov 
18749851ca0SDmitry Torokhov 	prod = page->in_prod;
18849851ca0SDmitry Torokhov 	if (prod == page->in_cons)
18949851ca0SDmitry Torokhov 		return IRQ_HANDLED;
19049851ca0SDmitry Torokhov 	rmb();			/* ensure we see ring contents up to prod */
19149aac820SOleksandr Andrushchenko 	for (cons = page->in_cons; cons != prod; cons++)
19249aac820SOleksandr Andrushchenko 		xenkbd_handle_event(info, &XENKBD_IN_RING_REF(page, cons));
19349851ca0SDmitry Torokhov 	mb();			/* ensure we got ring contents */
19449851ca0SDmitry Torokhov 	page->in_cons = cons;
19549851ca0SDmitry Torokhov 	notify_remote_via_irq(info->irq);
19649851ca0SDmitry Torokhov 
19749851ca0SDmitry Torokhov 	return IRQ_HANDLED;
19849851ca0SDmitry Torokhov }
19949851ca0SDmitry Torokhov 
xenkbd_probe(struct xenbus_device * dev,const struct xenbus_device_id * id)2005298cc4cSBill Pemberton static int xenkbd_probe(struct xenbus_device *dev,
20149851ca0SDmitry Torokhov 				  const struct xenbus_device_id *id)
20249851ca0SDmitry Torokhov {
20381362c6fSJuergen Gross 	int ret, i;
2043ef8a2eeSOleksandr Andrushchenko 	bool with_mtouch, with_kbd, with_ptr;
20549851ca0SDmitry Torokhov 	struct xenkbd_info *info;
20649aac820SOleksandr Andrushchenko 	struct input_dev *kbd, *ptr, *mtouch;
20749851ca0SDmitry Torokhov 
20849851ca0SDmitry Torokhov 	info = kzalloc(sizeof(*info), GFP_KERNEL);
20949851ca0SDmitry Torokhov 	if (!info) {
21049851ca0SDmitry Torokhov 		xenbus_dev_fatal(dev, -ENOMEM, "allocating info structure");
21149851ca0SDmitry Torokhov 		return -ENOMEM;
21249851ca0SDmitry Torokhov 	}
21349851ca0SDmitry Torokhov 	dev_set_drvdata(&dev->dev, info);
21449851ca0SDmitry Torokhov 	info->xbdev = dev;
21549851ca0SDmitry Torokhov 	info->irq = -1;
21649851ca0SDmitry Torokhov 	info->gref = -1;
21749851ca0SDmitry Torokhov 	snprintf(info->phys, sizeof(info->phys), "xenbus/%s", dev->nodename);
21849851ca0SDmitry Torokhov 
21949851ca0SDmitry Torokhov 	info->page = (void *)__get_free_page(GFP_KERNEL | __GFP_ZERO);
22049851ca0SDmitry Torokhov 	if (!info->page)
22149851ca0SDmitry Torokhov 		goto error_nomem;
22249851ca0SDmitry Torokhov 
2233ef8a2eeSOleksandr Andrushchenko 	/*
2243ef8a2eeSOleksandr Andrushchenko 	 * The below are reverse logic, e.g. if the feature is set, then
2253ef8a2eeSOleksandr Andrushchenko 	 * do not expose the corresponding virtual device.
2263ef8a2eeSOleksandr Andrushchenko 	 */
2273ef8a2eeSOleksandr Andrushchenko 	with_kbd = !xenbus_read_unsigned(dev->otherend,
2283ef8a2eeSOleksandr Andrushchenko 					 XENKBD_FIELD_FEAT_DSBL_KEYBRD, 0);
2293ef8a2eeSOleksandr Andrushchenko 
2303ef8a2eeSOleksandr Andrushchenko 	with_ptr = !xenbus_read_unsigned(dev->otherend,
2313ef8a2eeSOleksandr Andrushchenko 					 XENKBD_FIELD_FEAT_DSBL_POINTER, 0);
2323ef8a2eeSOleksandr Andrushchenko 
2333ef8a2eeSOleksandr Andrushchenko 	/* Direct logic: if set, then create multi-touch device. */
2343ef8a2eeSOleksandr Andrushchenko 	with_mtouch = xenbus_read_unsigned(dev->otherend,
2353ef8a2eeSOleksandr Andrushchenko 					   XENKBD_FIELD_FEAT_MTOUCH, 0);
2363ef8a2eeSOleksandr Andrushchenko 	if (with_mtouch) {
2373ef8a2eeSOleksandr Andrushchenko 		ret = xenbus_write(XBT_NIL, dev->nodename,
2383ef8a2eeSOleksandr Andrushchenko 				   XENKBD_FIELD_REQ_MTOUCH, "1");
2393ef8a2eeSOleksandr Andrushchenko 		if (ret) {
2403ef8a2eeSOleksandr Andrushchenko 			pr_warn("xenkbd: can't request multi-touch");
2413ef8a2eeSOleksandr Andrushchenko 			with_mtouch = 0;
2423ef8a2eeSOleksandr Andrushchenko 		}
2433ef8a2eeSOleksandr Andrushchenko 	}
2443ef8a2eeSOleksandr Andrushchenko 
2453ef8a2eeSOleksandr Andrushchenko 	/* keyboard */
2463ef8a2eeSOleksandr Andrushchenko 	if (with_kbd) {
2473ef8a2eeSOleksandr Andrushchenko 		kbd = input_allocate_device();
2483ef8a2eeSOleksandr Andrushchenko 		if (!kbd)
2493ef8a2eeSOleksandr Andrushchenko 			goto error_nomem;
2503ef8a2eeSOleksandr Andrushchenko 		kbd->name = "Xen Virtual Keyboard";
2513ef8a2eeSOleksandr Andrushchenko 		kbd->phys = info->phys;
2523ef8a2eeSOleksandr Andrushchenko 		kbd->id.bustype = BUS_PCI;
2533ef8a2eeSOleksandr Andrushchenko 		kbd->id.vendor = 0x5853;
2543ef8a2eeSOleksandr Andrushchenko 		kbd->id.product = 0xffff;
2553ef8a2eeSOleksandr Andrushchenko 
2563ef8a2eeSOleksandr Andrushchenko 		__set_bit(EV_KEY, kbd->evbit);
2573ef8a2eeSOleksandr Andrushchenko 		for (i = KEY_ESC; i < KEY_UNKNOWN; i++)
2583ef8a2eeSOleksandr Andrushchenko 			__set_bit(i, kbd->keybit);
2593ef8a2eeSOleksandr Andrushchenko 		for (i = KEY_OK; i < KEY_MAX; i++)
2603ef8a2eeSOleksandr Andrushchenko 			__set_bit(i, kbd->keybit);
2613ef8a2eeSOleksandr Andrushchenko 
2623ef8a2eeSOleksandr Andrushchenko 		ret = input_register_device(kbd);
2633ef8a2eeSOleksandr Andrushchenko 		if (ret) {
2643ef8a2eeSOleksandr Andrushchenko 			input_free_device(kbd);
2653ef8a2eeSOleksandr Andrushchenko 			xenbus_dev_fatal(dev, ret,
2663ef8a2eeSOleksandr Andrushchenko 					 "input_register_device(kbd)");
2673ef8a2eeSOleksandr Andrushchenko 			goto error;
2683ef8a2eeSOleksandr Andrushchenko 		}
2693ef8a2eeSOleksandr Andrushchenko 		info->kbd = kbd;
2703ef8a2eeSOleksandr Andrushchenko 	}
2713ef8a2eeSOleksandr Andrushchenko 
2723ef8a2eeSOleksandr Andrushchenko 	/* pointing device */
2733ef8a2eeSOleksandr Andrushchenko 	if (with_ptr) {
2743ef8a2eeSOleksandr Andrushchenko 		unsigned int abs;
2753ef8a2eeSOleksandr Andrushchenko 
2768b3afdfaSJuergen Gross 		/* Set input abs params to match backend screen res */
277ad2ee015SOleksandr Andrushchenko 		abs = xenbus_read_unsigned(dev->otherend,
278ad2ee015SOleksandr Andrushchenko 					   XENKBD_FIELD_FEAT_ABS_POINTER, 0);
279ad2ee015SOleksandr Andrushchenko 		ptr_size[KPARAM_X] = xenbus_read_unsigned(dev->otherend,
280ad2ee015SOleksandr Andrushchenko 							  XENKBD_FIELD_WIDTH,
2818b3afdfaSJuergen Gross 							  ptr_size[KPARAM_X]);
282ad2ee015SOleksandr Andrushchenko 		ptr_size[KPARAM_Y] = xenbus_read_unsigned(dev->otherend,
283ad2ee015SOleksandr Andrushchenko 							  XENKBD_FIELD_HEIGHT,
2848b3afdfaSJuergen Gross 							  ptr_size[KPARAM_Y]);
285ec7aa963SInsu Yun 		if (abs) {
286cd6763beSJan Beulich 			ret = xenbus_write(XBT_NIL, dev->nodename,
287ad2ee015SOleksandr Andrushchenko 					   XENKBD_FIELD_REQ_ABS_POINTER, "1");
288ec7aa963SInsu Yun 			if (ret) {
289fef5f569SJoe Perches 				pr_warn("xenkbd: can't request abs-pointer\n");
290ec7aa963SInsu Yun 				abs = 0;
291ec7aa963SInsu Yun 			}
292ec7aa963SInsu Yun 		}
29349851ca0SDmitry Torokhov 
29449851ca0SDmitry Torokhov 		ptr = input_allocate_device();
29549851ca0SDmitry Torokhov 		if (!ptr)
29649851ca0SDmitry Torokhov 			goto error_nomem;
29749851ca0SDmitry Torokhov 		ptr->name = "Xen Virtual Pointer";
29849851ca0SDmitry Torokhov 		ptr->phys = info->phys;
29949851ca0SDmitry Torokhov 		ptr->id.bustype = BUS_PCI;
30049851ca0SDmitry Torokhov 		ptr->id.vendor = 0x5853;
30149851ca0SDmitry Torokhov 		ptr->id.product = 0xfffe;
30249851ca0SDmitry Torokhov 
30349851ca0SDmitry Torokhov 		if (abs) {
30449851ca0SDmitry Torokhov 			__set_bit(EV_ABS, ptr->evbit);
3053ef8a2eeSOleksandr Andrushchenko 			input_set_abs_params(ptr, ABS_X, 0,
3063ef8a2eeSOleksandr Andrushchenko 					     ptr_size[KPARAM_X], 0, 0);
3073ef8a2eeSOleksandr Andrushchenko 			input_set_abs_params(ptr, ABS_Y, 0,
3083ef8a2eeSOleksandr Andrushchenko 					     ptr_size[KPARAM_Y], 0, 0);
30949851ca0SDmitry Torokhov 		} else {
31049851ca0SDmitry Torokhov 			input_set_capability(ptr, EV_REL, REL_X);
31149851ca0SDmitry Torokhov 			input_set_capability(ptr, EV_REL, REL_Y);
31249851ca0SDmitry Torokhov 		}
31349851ca0SDmitry Torokhov 		input_set_capability(ptr, EV_REL, REL_WHEEL);
31449851ca0SDmitry Torokhov 
31549851ca0SDmitry Torokhov 		__set_bit(EV_KEY, ptr->evbit);
31649851ca0SDmitry Torokhov 		for (i = BTN_LEFT; i <= BTN_TASK; i++)
31749851ca0SDmitry Torokhov 			__set_bit(i, ptr->keybit);
31849851ca0SDmitry Torokhov 
31949851ca0SDmitry Torokhov 		ret = input_register_device(ptr);
32049851ca0SDmitry Torokhov 		if (ret) {
32149851ca0SDmitry Torokhov 			input_free_device(ptr);
3223ef8a2eeSOleksandr Andrushchenko 			xenbus_dev_fatal(dev, ret,
3233ef8a2eeSOleksandr Andrushchenko 					 "input_register_device(ptr)");
32449851ca0SDmitry Torokhov 			goto error;
32549851ca0SDmitry Torokhov 		}
32649851ca0SDmitry Torokhov 		info->ptr = ptr;
3273ef8a2eeSOleksandr Andrushchenko 	}
32849851ca0SDmitry Torokhov 
32949aac820SOleksandr Andrushchenko 	/* multi-touch device */
3303ef8a2eeSOleksandr Andrushchenko 	if (with_mtouch) {
33149aac820SOleksandr Andrushchenko 		int num_cont, width, height;
33249aac820SOleksandr Andrushchenko 
33349aac820SOleksandr Andrushchenko 		mtouch = input_allocate_device();
33449aac820SOleksandr Andrushchenko 		if (!mtouch)
33549aac820SOleksandr Andrushchenko 			goto error_nomem;
33649aac820SOleksandr Andrushchenko 
337ce6f7d08SOleksandr Andrushchenko 		num_cont = xenbus_read_unsigned(info->xbdev->otherend,
33849aac820SOleksandr Andrushchenko 						XENKBD_FIELD_MT_NUM_CONTACTS,
33949aac820SOleksandr Andrushchenko 						1);
340ce6f7d08SOleksandr Andrushchenko 		width = xenbus_read_unsigned(info->xbdev->otherend,
34149aac820SOleksandr Andrushchenko 					     XENKBD_FIELD_MT_WIDTH,
34249aac820SOleksandr Andrushchenko 					     XENFB_WIDTH);
343ce6f7d08SOleksandr Andrushchenko 		height = xenbus_read_unsigned(info->xbdev->otherend,
34449aac820SOleksandr Andrushchenko 					      XENKBD_FIELD_MT_HEIGHT,
34549aac820SOleksandr Andrushchenko 					      XENFB_HEIGHT);
34649aac820SOleksandr Andrushchenko 
34749aac820SOleksandr Andrushchenko 		mtouch->name = "Xen Virtual Multi-touch";
34849aac820SOleksandr Andrushchenko 		mtouch->phys = info->phys;
34949aac820SOleksandr Andrushchenko 		mtouch->id.bustype = BUS_PCI;
35049aac820SOleksandr Andrushchenko 		mtouch->id.vendor = 0x5853;
35149aac820SOleksandr Andrushchenko 		mtouch->id.product = 0xfffd;
35249aac820SOleksandr Andrushchenko 
35349aac820SOleksandr Andrushchenko 		input_set_abs_params(mtouch, ABS_MT_TOUCH_MAJOR,
35449aac820SOleksandr Andrushchenko 				     0, 255, 0, 0);
35549aac820SOleksandr Andrushchenko 		input_set_abs_params(mtouch, ABS_MT_POSITION_X,
35649aac820SOleksandr Andrushchenko 				     0, width, 0, 0);
35749aac820SOleksandr Andrushchenko 		input_set_abs_params(mtouch, ABS_MT_POSITION_Y,
35849aac820SOleksandr Andrushchenko 				     0, height, 0, 0);
35949aac820SOleksandr Andrushchenko 
36049aac820SOleksandr Andrushchenko 		ret = input_mt_init_slots(mtouch, num_cont, INPUT_MT_DIRECT);
36149aac820SOleksandr Andrushchenko 		if (ret) {
36249aac820SOleksandr Andrushchenko 			input_free_device(mtouch);
36349aac820SOleksandr Andrushchenko 			xenbus_dev_fatal(info->xbdev, ret,
36449aac820SOleksandr Andrushchenko 					 "input_mt_init_slots");
36549aac820SOleksandr Andrushchenko 			goto error;
36649aac820SOleksandr Andrushchenko 		}
36749aac820SOleksandr Andrushchenko 
36849aac820SOleksandr Andrushchenko 		ret = input_register_device(mtouch);
36949aac820SOleksandr Andrushchenko 		if (ret) {
37049aac820SOleksandr Andrushchenko 			input_free_device(mtouch);
37149aac820SOleksandr Andrushchenko 			xenbus_dev_fatal(info->xbdev, ret,
37249aac820SOleksandr Andrushchenko 					 "input_register_device(mtouch)");
37349aac820SOleksandr Andrushchenko 			goto error;
37449aac820SOleksandr Andrushchenko 		}
37549aac820SOleksandr Andrushchenko 		info->mtouch_cur_contact_id = -1;
37649aac820SOleksandr Andrushchenko 		info->mtouch = mtouch;
37749aac820SOleksandr Andrushchenko 	}
37849aac820SOleksandr Andrushchenko 
3793ef8a2eeSOleksandr Andrushchenko 	if (!(with_kbd || with_ptr || with_mtouch)) {
3803ef8a2eeSOleksandr Andrushchenko 		ret = -ENXIO;
3813ef8a2eeSOleksandr Andrushchenko 		goto error;
3823ef8a2eeSOleksandr Andrushchenko 	}
3833ef8a2eeSOleksandr Andrushchenko 
38449851ca0SDmitry Torokhov 	ret = xenkbd_connect_backend(dev, info);
38549851ca0SDmitry Torokhov 	if (ret < 0)
38649851ca0SDmitry Torokhov 		goto error;
38749851ca0SDmitry Torokhov 
38849851ca0SDmitry Torokhov 	return 0;
38949851ca0SDmitry Torokhov 
39049851ca0SDmitry Torokhov  error_nomem:
39149851ca0SDmitry Torokhov 	ret = -ENOMEM;
39249851ca0SDmitry Torokhov 	xenbus_dev_fatal(dev, ret, "allocating device memory");
39349851ca0SDmitry Torokhov  error:
39449851ca0SDmitry Torokhov 	xenkbd_remove(dev);
39549851ca0SDmitry Torokhov 	return ret;
39649851ca0SDmitry Torokhov }
39749851ca0SDmitry Torokhov 
xenkbd_resume(struct xenbus_device * dev)39849851ca0SDmitry Torokhov static int xenkbd_resume(struct xenbus_device *dev)
39949851ca0SDmitry Torokhov {
40049851ca0SDmitry Torokhov 	struct xenkbd_info *info = dev_get_drvdata(&dev->dev);
40149851ca0SDmitry Torokhov 
40249851ca0SDmitry Torokhov 	xenkbd_disconnect_backend(info);
40349851ca0SDmitry Torokhov 	memset(info->page, 0, PAGE_SIZE);
40449851ca0SDmitry Torokhov 	return xenkbd_connect_backend(dev, info);
40549851ca0SDmitry Torokhov }
40649851ca0SDmitry Torokhov 
xenkbd_remove(struct xenbus_device * dev)407*7cffcadeSDawei Li static void xenkbd_remove(struct xenbus_device *dev)
40849851ca0SDmitry Torokhov {
40949851ca0SDmitry Torokhov 	struct xenkbd_info *info = dev_get_drvdata(&dev->dev);
41049851ca0SDmitry Torokhov 
41149851ca0SDmitry Torokhov 	xenkbd_disconnect_backend(info);
41249851ca0SDmitry Torokhov 	if (info->kbd)
41349851ca0SDmitry Torokhov 		input_unregister_device(info->kbd);
41449851ca0SDmitry Torokhov 	if (info->ptr)
41549851ca0SDmitry Torokhov 		input_unregister_device(info->ptr);
41649aac820SOleksandr Andrushchenko 	if (info->mtouch)
41749aac820SOleksandr Andrushchenko 		input_unregister_device(info->mtouch);
41849851ca0SDmitry Torokhov 	free_page((unsigned long)info->page);
41949851ca0SDmitry Torokhov 	kfree(info);
42049851ca0SDmitry Torokhov }
42149851ca0SDmitry Torokhov 
xenkbd_connect_backend(struct xenbus_device * dev,struct xenkbd_info * info)42249851ca0SDmitry Torokhov static int xenkbd_connect_backend(struct xenbus_device *dev,
42349851ca0SDmitry Torokhov 				  struct xenkbd_info *info)
42449851ca0SDmitry Torokhov {
42549851ca0SDmitry Torokhov 	int ret, evtchn;
42649851ca0SDmitry Torokhov 	struct xenbus_transaction xbt;
42749851ca0SDmitry Torokhov 
42849851ca0SDmitry Torokhov 	ret = gnttab_grant_foreign_access(dev->otherend_id,
4290df4f266SJulien Grall 	                                  virt_to_gfn(info->page), 0);
43049851ca0SDmitry Torokhov 	if (ret < 0)
43149851ca0SDmitry Torokhov 		return ret;
43249851ca0SDmitry Torokhov 	info->gref = ret;
43349851ca0SDmitry Torokhov 
43449851ca0SDmitry Torokhov 	ret = xenbus_alloc_evtchn(dev, &evtchn);
43549851ca0SDmitry Torokhov 	if (ret)
43649851ca0SDmitry Torokhov 		goto error_grant;
43749851ca0SDmitry Torokhov 	ret = bind_evtchn_to_irqhandler(evtchn, input_handler,
43849851ca0SDmitry Torokhov 					0, dev->devicetype, info);
43949851ca0SDmitry Torokhov 	if (ret < 0) {
44049851ca0SDmitry Torokhov 		xenbus_dev_fatal(dev, ret, "bind_evtchn_to_irqhandler");
44149851ca0SDmitry Torokhov 		goto error_evtchan;
44249851ca0SDmitry Torokhov 	}
44349851ca0SDmitry Torokhov 	info->irq = ret;
44449851ca0SDmitry Torokhov 
44549851ca0SDmitry Torokhov  again:
44649851ca0SDmitry Torokhov 	ret = xenbus_transaction_start(&xbt);
44749851ca0SDmitry Torokhov 	if (ret) {
44849851ca0SDmitry Torokhov 		xenbus_dev_fatal(dev, ret, "starting transaction");
44949851ca0SDmitry Torokhov 		goto error_irqh;
45049851ca0SDmitry Torokhov 	}
451ad2ee015SOleksandr Andrushchenko 	ret = xenbus_printf(xbt, dev->nodename, XENKBD_FIELD_RING_REF, "%lu",
4520df4f266SJulien Grall 			    virt_to_gfn(info->page));
45349851ca0SDmitry Torokhov 	if (ret)
45449851ca0SDmitry Torokhov 		goto error_xenbus;
455ad2ee015SOleksandr Andrushchenko 	ret = xenbus_printf(xbt, dev->nodename, XENKBD_FIELD_RING_GREF,
456ad2ee015SOleksandr Andrushchenko 			    "%u", info->gref);
45749851ca0SDmitry Torokhov 	if (ret)
45849851ca0SDmitry Torokhov 		goto error_xenbus;
459ad2ee015SOleksandr Andrushchenko 	ret = xenbus_printf(xbt, dev->nodename, XENKBD_FIELD_EVT_CHANNEL, "%u",
46049851ca0SDmitry Torokhov 			    evtchn);
46149851ca0SDmitry Torokhov 	if (ret)
46249851ca0SDmitry Torokhov 		goto error_xenbus;
46349851ca0SDmitry Torokhov 	ret = xenbus_transaction_end(xbt, 0);
46449851ca0SDmitry Torokhov 	if (ret) {
46549851ca0SDmitry Torokhov 		if (ret == -EAGAIN)
46649851ca0SDmitry Torokhov 			goto again;
46749851ca0SDmitry Torokhov 		xenbus_dev_fatal(dev, ret, "completing transaction");
46849851ca0SDmitry Torokhov 		goto error_irqh;
46949851ca0SDmitry Torokhov 	}
47049851ca0SDmitry Torokhov 
47149851ca0SDmitry Torokhov 	xenbus_switch_state(dev, XenbusStateInitialised);
47249851ca0SDmitry Torokhov 	return 0;
47349851ca0SDmitry Torokhov 
47449851ca0SDmitry Torokhov  error_xenbus:
47549851ca0SDmitry Torokhov 	xenbus_transaction_end(xbt, 1);
47649851ca0SDmitry Torokhov 	xenbus_dev_fatal(dev, ret, "writing xenstore");
47749851ca0SDmitry Torokhov  error_irqh:
47849851ca0SDmitry Torokhov 	unbind_from_irqhandler(info->irq, info);
47949851ca0SDmitry Torokhov 	info->irq = -1;
48049851ca0SDmitry Torokhov  error_evtchan:
48149851ca0SDmitry Torokhov 	xenbus_free_evtchn(dev, evtchn);
48249851ca0SDmitry Torokhov  error_grant:
48349f8b459SJuergen Gross 	gnttab_end_foreign_access(info->gref, NULL);
48449851ca0SDmitry Torokhov 	info->gref = -1;
48549851ca0SDmitry Torokhov 	return ret;
48649851ca0SDmitry Torokhov }
48749851ca0SDmitry Torokhov 
xenkbd_disconnect_backend(struct xenkbd_info * info)48849851ca0SDmitry Torokhov static void xenkbd_disconnect_backend(struct xenkbd_info *info)
48949851ca0SDmitry Torokhov {
49049851ca0SDmitry Torokhov 	if (info->irq >= 0)
49149851ca0SDmitry Torokhov 		unbind_from_irqhandler(info->irq, info);
49249851ca0SDmitry Torokhov 	info->irq = -1;
49349851ca0SDmitry Torokhov 	if (info->gref >= 0)
49449f8b459SJuergen Gross 		gnttab_end_foreign_access(info->gref, NULL);
49549851ca0SDmitry Torokhov 	info->gref = -1;
49649851ca0SDmitry Torokhov }
49749851ca0SDmitry Torokhov 
xenkbd_backend_changed(struct xenbus_device * dev,enum xenbus_state backend_state)49849851ca0SDmitry Torokhov static void xenkbd_backend_changed(struct xenbus_device *dev,
49949851ca0SDmitry Torokhov 				   enum xenbus_state backend_state)
50049851ca0SDmitry Torokhov {
50149851ca0SDmitry Torokhov 	switch (backend_state) {
50249851ca0SDmitry Torokhov 	case XenbusStateInitialising:
50349851ca0SDmitry Torokhov 	case XenbusStateInitialised:
50449851ca0SDmitry Torokhov 	case XenbusStateReconfiguring:
50549851ca0SDmitry Torokhov 	case XenbusStateReconfigured:
50649851ca0SDmitry Torokhov 	case XenbusStateUnknown:
50749851ca0SDmitry Torokhov 		break;
50849851ca0SDmitry Torokhov 
50949851ca0SDmitry Torokhov 	case XenbusStateInitWait:
51049851ca0SDmitry Torokhov 		xenbus_switch_state(dev, XenbusStateConnected);
51149851ca0SDmitry Torokhov 		break;
51249851ca0SDmitry Torokhov 
51349851ca0SDmitry Torokhov 	case XenbusStateConnected:
51449851ca0SDmitry Torokhov 		/*
51549851ca0SDmitry Torokhov 		 * Work around xenbus race condition: If backend goes
51649851ca0SDmitry Torokhov 		 * through InitWait to Connected fast enough, we can
51749851ca0SDmitry Torokhov 		 * get Connected twice here.
51849851ca0SDmitry Torokhov 		 */
51949851ca0SDmitry Torokhov 		if (dev->state != XenbusStateConnected)
5208b3afdfaSJuergen Gross 			xenbus_switch_state(dev, XenbusStateConnected);
52149851ca0SDmitry Torokhov 		break;
52249851ca0SDmitry Torokhov 
5232ebb939aSDavid Vrabel 	case XenbusStateClosed:
5242ebb939aSDavid Vrabel 		if (dev->state == XenbusStateClosed)
5252ebb939aSDavid Vrabel 			break;
5266f49c4f5SGustavo A. R. Silva 		fallthrough;	/* Missed the backend's CLOSING state */
52749851ca0SDmitry Torokhov 	case XenbusStateClosing:
52849851ca0SDmitry Torokhov 		xenbus_frontend_closed(dev);
52949851ca0SDmitry Torokhov 		break;
53049851ca0SDmitry Torokhov 	}
53149851ca0SDmitry Torokhov }
53249851ca0SDmitry Torokhov 
53349851ca0SDmitry Torokhov static const struct xenbus_device_id xenkbd_ids[] = {
534ad2ee015SOleksandr Andrushchenko 	{ XENKBD_DRIVER_NAME },
53549851ca0SDmitry Torokhov 	{ "" }
53649851ca0SDmitry Torokhov };
53749851ca0SDmitry Torokhov 
53895afae48SDavid Vrabel static struct xenbus_driver xenkbd_driver = {
53995afae48SDavid Vrabel 	.ids = xenkbd_ids,
54049851ca0SDmitry Torokhov 	.probe = xenkbd_probe,
54149851ca0SDmitry Torokhov 	.remove = xenkbd_remove,
54249851ca0SDmitry Torokhov 	.resume = xenkbd_resume,
54349851ca0SDmitry Torokhov 	.otherend_changed = xenkbd_backend_changed,
54437a72b08SJuergen Gross 	.not_essential = true,
54595afae48SDavid Vrabel };
54649851ca0SDmitry Torokhov 
xenkbd_init(void)54749851ca0SDmitry Torokhov static int __init xenkbd_init(void)
54849851ca0SDmitry Torokhov {
5495f098ecdSStefano Stabellini 	if (!xen_domain())
55049851ca0SDmitry Torokhov 		return -ENODEV;
55149851ca0SDmitry Torokhov 
55249851ca0SDmitry Torokhov 	/* Nothing to do if running in dom0. */
55349851ca0SDmitry Torokhov 	if (xen_initial_domain())
55449851ca0SDmitry Torokhov 		return -ENODEV;
55549851ca0SDmitry Torokhov 
55651c71a3bSKonrad Rzeszutek Wilk 	if (!xen_has_pv_devices())
55751c71a3bSKonrad Rzeszutek Wilk 		return -ENODEV;
55851c71a3bSKonrad Rzeszutek Wilk 
55949851ca0SDmitry Torokhov 	return xenbus_register_frontend(&xenkbd_driver);
56049851ca0SDmitry Torokhov }
56149851ca0SDmitry Torokhov 
xenkbd_cleanup(void)56249851ca0SDmitry Torokhov static void __exit xenkbd_cleanup(void)
56349851ca0SDmitry Torokhov {
56449851ca0SDmitry Torokhov 	xenbus_unregister_driver(&xenkbd_driver);
56549851ca0SDmitry Torokhov }
56649851ca0SDmitry Torokhov 
56749851ca0SDmitry Torokhov module_init(xenkbd_init);
56849851ca0SDmitry Torokhov module_exit(xenkbd_cleanup);
56949851ca0SDmitry Torokhov 
57049851ca0SDmitry Torokhov MODULE_DESCRIPTION("Xen virtual keyboard/pointer device frontend");
57149851ca0SDmitry Torokhov MODULE_LICENSE("GPL");
572ad2ee015SOleksandr Andrushchenko MODULE_ALIAS("xen:" XENKBD_DRIVER_NAME);
573