162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * Xen para-virtual input device
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (C) 2005 Anthony Liguori <aliguori@us.ibm.com>
562306a36Sopenharmony_ci * Copyright (C) 2006-2008 Red Hat, Inc., Markus Armbruster <armbru@redhat.com>
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci *  Based on linux/drivers/input/mouse/sermouse.c
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci *  This file is subject to the terms and conditions of the GNU General Public
1062306a36Sopenharmony_ci *  License. See the file COPYING in the main directory of this archive for
1162306a36Sopenharmony_ci *  more details.
1262306a36Sopenharmony_ci */
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include <linux/kernel.h>
1762306a36Sopenharmony_ci#include <linux/errno.h>
1862306a36Sopenharmony_ci#include <linux/module.h>
1962306a36Sopenharmony_ci#include <linux/input.h>
2062306a36Sopenharmony_ci#include <linux/input/mt.h>
2162306a36Sopenharmony_ci#include <linux/slab.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#include <asm/xen/hypervisor.h>
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#include <xen/xen.h>
2662306a36Sopenharmony_ci#include <xen/events.h>
2762306a36Sopenharmony_ci#include <xen/page.h>
2862306a36Sopenharmony_ci#include <xen/grant_table.h>
2962306a36Sopenharmony_ci#include <xen/interface/grant_table.h>
3062306a36Sopenharmony_ci#include <xen/interface/io/fbif.h>
3162306a36Sopenharmony_ci#include <xen/interface/io/kbdif.h>
3262306a36Sopenharmony_ci#include <xen/xenbus.h>
3362306a36Sopenharmony_ci#include <xen/platform_pci.h>
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistruct xenkbd_info {
3662306a36Sopenharmony_ci	struct input_dev *kbd;
3762306a36Sopenharmony_ci	struct input_dev *ptr;
3862306a36Sopenharmony_ci	struct input_dev *mtouch;
3962306a36Sopenharmony_ci	struct xenkbd_page *page;
4062306a36Sopenharmony_ci	int gref;
4162306a36Sopenharmony_ci	int irq;
4262306a36Sopenharmony_ci	struct xenbus_device *xbdev;
4362306a36Sopenharmony_ci	char phys[32];
4462306a36Sopenharmony_ci	/* current MT slot/contact ID we are injecting events in */
4562306a36Sopenharmony_ci	int mtouch_cur_contact_id;
4662306a36Sopenharmony_ci};
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cienum { KPARAM_X, KPARAM_Y, KPARAM_CNT };
4962306a36Sopenharmony_cistatic int ptr_size[KPARAM_CNT] = { XENFB_WIDTH, XENFB_HEIGHT };
5062306a36Sopenharmony_cimodule_param_array(ptr_size, int, NULL, 0444);
5162306a36Sopenharmony_ciMODULE_PARM_DESC(ptr_size,
5262306a36Sopenharmony_ci	"Pointing device width, height in pixels (default 800,600)");
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_cistatic void xenkbd_remove(struct xenbus_device *);
5562306a36Sopenharmony_cistatic int xenkbd_connect_backend(struct xenbus_device *, struct xenkbd_info *);
5662306a36Sopenharmony_cistatic void xenkbd_disconnect_backend(struct xenkbd_info *);
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci/*
5962306a36Sopenharmony_ci * Note: if you need to send out events, see xenfb_do_update() for how
6062306a36Sopenharmony_ci * to do that.
6162306a36Sopenharmony_ci */
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_cistatic void xenkbd_handle_motion_event(struct xenkbd_info *info,
6462306a36Sopenharmony_ci				       struct xenkbd_motion *motion)
6562306a36Sopenharmony_ci{
6662306a36Sopenharmony_ci	if (unlikely(!info->ptr))
6762306a36Sopenharmony_ci		return;
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	input_report_rel(info->ptr, REL_X, motion->rel_x);
7062306a36Sopenharmony_ci	input_report_rel(info->ptr, REL_Y, motion->rel_y);
7162306a36Sopenharmony_ci	if (motion->rel_z)
7262306a36Sopenharmony_ci		input_report_rel(info->ptr, REL_WHEEL, -motion->rel_z);
7362306a36Sopenharmony_ci	input_sync(info->ptr);
7462306a36Sopenharmony_ci}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistatic void xenkbd_handle_position_event(struct xenkbd_info *info,
7762306a36Sopenharmony_ci					 struct xenkbd_position *pos)
7862306a36Sopenharmony_ci{
7962306a36Sopenharmony_ci	if (unlikely(!info->ptr))
8062306a36Sopenharmony_ci		return;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	input_report_abs(info->ptr, ABS_X, pos->abs_x);
8362306a36Sopenharmony_ci	input_report_abs(info->ptr, ABS_Y, pos->abs_y);
8462306a36Sopenharmony_ci	if (pos->rel_z)
8562306a36Sopenharmony_ci		input_report_rel(info->ptr, REL_WHEEL, -pos->rel_z);
8662306a36Sopenharmony_ci	input_sync(info->ptr);
8762306a36Sopenharmony_ci}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_cistatic void xenkbd_handle_key_event(struct xenkbd_info *info,
9062306a36Sopenharmony_ci				    struct xenkbd_key *key)
9162306a36Sopenharmony_ci{
9262306a36Sopenharmony_ci	struct input_dev *dev;
9362306a36Sopenharmony_ci	int value = key->pressed;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	if (test_bit(key->keycode, info->ptr->keybit)) {
9662306a36Sopenharmony_ci		dev = info->ptr;
9762306a36Sopenharmony_ci	} else if (test_bit(key->keycode, info->kbd->keybit)) {
9862306a36Sopenharmony_ci		dev = info->kbd;
9962306a36Sopenharmony_ci		if (key->pressed && test_bit(key->keycode, info->kbd->key))
10062306a36Sopenharmony_ci			value = 2; /* Mark as autorepeat */
10162306a36Sopenharmony_ci	} else {
10262306a36Sopenharmony_ci		pr_warn("unhandled keycode 0x%x\n", key->keycode);
10362306a36Sopenharmony_ci		return;
10462306a36Sopenharmony_ci	}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	if (unlikely(!dev))
10762306a36Sopenharmony_ci		return;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	input_event(dev, EV_KEY, key->keycode, value);
11062306a36Sopenharmony_ci	input_sync(dev);
11162306a36Sopenharmony_ci}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_cistatic void xenkbd_handle_mt_event(struct xenkbd_info *info,
11462306a36Sopenharmony_ci				   struct xenkbd_mtouch *mtouch)
11562306a36Sopenharmony_ci{
11662306a36Sopenharmony_ci	if (unlikely(!info->mtouch))
11762306a36Sopenharmony_ci		return;
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	if (mtouch->contact_id != info->mtouch_cur_contact_id) {
12062306a36Sopenharmony_ci		info->mtouch_cur_contact_id = mtouch->contact_id;
12162306a36Sopenharmony_ci		input_mt_slot(info->mtouch, mtouch->contact_id);
12262306a36Sopenharmony_ci	}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	switch (mtouch->event_type) {
12562306a36Sopenharmony_ci	case XENKBD_MT_EV_DOWN:
12662306a36Sopenharmony_ci		input_mt_report_slot_state(info->mtouch, MT_TOOL_FINGER, true);
12762306a36Sopenharmony_ci		fallthrough;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	case XENKBD_MT_EV_MOTION:
13062306a36Sopenharmony_ci		input_report_abs(info->mtouch, ABS_MT_POSITION_X,
13162306a36Sopenharmony_ci				 mtouch->u.pos.abs_x);
13262306a36Sopenharmony_ci		input_report_abs(info->mtouch, ABS_MT_POSITION_Y,
13362306a36Sopenharmony_ci				 mtouch->u.pos.abs_y);
13462306a36Sopenharmony_ci		break;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	case XENKBD_MT_EV_SHAPE:
13762306a36Sopenharmony_ci		input_report_abs(info->mtouch, ABS_MT_TOUCH_MAJOR,
13862306a36Sopenharmony_ci				 mtouch->u.shape.major);
13962306a36Sopenharmony_ci		input_report_abs(info->mtouch, ABS_MT_TOUCH_MINOR,
14062306a36Sopenharmony_ci				 mtouch->u.shape.minor);
14162306a36Sopenharmony_ci		break;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	case XENKBD_MT_EV_ORIENT:
14462306a36Sopenharmony_ci		input_report_abs(info->mtouch, ABS_MT_ORIENTATION,
14562306a36Sopenharmony_ci				 mtouch->u.orientation);
14662306a36Sopenharmony_ci		break;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	case XENKBD_MT_EV_UP:
14962306a36Sopenharmony_ci		input_mt_report_slot_inactive(info->mtouch);
15062306a36Sopenharmony_ci		break;
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	case XENKBD_MT_EV_SYN:
15362306a36Sopenharmony_ci		input_mt_sync_frame(info->mtouch);
15462306a36Sopenharmony_ci		input_sync(info->mtouch);
15562306a36Sopenharmony_ci		break;
15662306a36Sopenharmony_ci	}
15762306a36Sopenharmony_ci}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_cistatic void xenkbd_handle_event(struct xenkbd_info *info,
16062306a36Sopenharmony_ci				union xenkbd_in_event *event)
16162306a36Sopenharmony_ci{
16262306a36Sopenharmony_ci	switch (event->type) {
16362306a36Sopenharmony_ci	case XENKBD_TYPE_MOTION:
16462306a36Sopenharmony_ci		xenkbd_handle_motion_event(info, &event->motion);
16562306a36Sopenharmony_ci		break;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	case XENKBD_TYPE_KEY:
16862306a36Sopenharmony_ci		xenkbd_handle_key_event(info, &event->key);
16962306a36Sopenharmony_ci		break;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	case XENKBD_TYPE_POS:
17262306a36Sopenharmony_ci		xenkbd_handle_position_event(info, &event->pos);
17362306a36Sopenharmony_ci		break;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	case XENKBD_TYPE_MTOUCH:
17662306a36Sopenharmony_ci		xenkbd_handle_mt_event(info, &event->mtouch);
17762306a36Sopenharmony_ci		break;
17862306a36Sopenharmony_ci	}
17962306a36Sopenharmony_ci}
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_cistatic irqreturn_t input_handler(int rq, void *dev_id)
18262306a36Sopenharmony_ci{
18362306a36Sopenharmony_ci	struct xenkbd_info *info = dev_id;
18462306a36Sopenharmony_ci	struct xenkbd_page *page = info->page;
18562306a36Sopenharmony_ci	__u32 cons, prod;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	prod = page->in_prod;
18862306a36Sopenharmony_ci	if (prod == page->in_cons)
18962306a36Sopenharmony_ci		return IRQ_HANDLED;
19062306a36Sopenharmony_ci	rmb();			/* ensure we see ring contents up to prod */
19162306a36Sopenharmony_ci	for (cons = page->in_cons; cons != prod; cons++)
19262306a36Sopenharmony_ci		xenkbd_handle_event(info, &XENKBD_IN_RING_REF(page, cons));
19362306a36Sopenharmony_ci	mb();			/* ensure we got ring contents */
19462306a36Sopenharmony_ci	page->in_cons = cons;
19562306a36Sopenharmony_ci	notify_remote_via_irq(info->irq);
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	return IRQ_HANDLED;
19862306a36Sopenharmony_ci}
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_cistatic int xenkbd_probe(struct xenbus_device *dev,
20162306a36Sopenharmony_ci				  const struct xenbus_device_id *id)
20262306a36Sopenharmony_ci{
20362306a36Sopenharmony_ci	int ret, i;
20462306a36Sopenharmony_ci	bool with_mtouch, with_kbd, with_ptr;
20562306a36Sopenharmony_ci	struct xenkbd_info *info;
20662306a36Sopenharmony_ci	struct input_dev *kbd, *ptr, *mtouch;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	info = kzalloc(sizeof(*info), GFP_KERNEL);
20962306a36Sopenharmony_ci	if (!info) {
21062306a36Sopenharmony_ci		xenbus_dev_fatal(dev, -ENOMEM, "allocating info structure");
21162306a36Sopenharmony_ci		return -ENOMEM;
21262306a36Sopenharmony_ci	}
21362306a36Sopenharmony_ci	dev_set_drvdata(&dev->dev, info);
21462306a36Sopenharmony_ci	info->xbdev = dev;
21562306a36Sopenharmony_ci	info->irq = -1;
21662306a36Sopenharmony_ci	info->gref = -1;
21762306a36Sopenharmony_ci	snprintf(info->phys, sizeof(info->phys), "xenbus/%s", dev->nodename);
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	info->page = (void *)__get_free_page(GFP_KERNEL | __GFP_ZERO);
22062306a36Sopenharmony_ci	if (!info->page)
22162306a36Sopenharmony_ci		goto error_nomem;
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	/*
22462306a36Sopenharmony_ci	 * The below are reverse logic, e.g. if the feature is set, then
22562306a36Sopenharmony_ci	 * do not expose the corresponding virtual device.
22662306a36Sopenharmony_ci	 */
22762306a36Sopenharmony_ci	with_kbd = !xenbus_read_unsigned(dev->otherend,
22862306a36Sopenharmony_ci					 XENKBD_FIELD_FEAT_DSBL_KEYBRD, 0);
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	with_ptr = !xenbus_read_unsigned(dev->otherend,
23162306a36Sopenharmony_ci					 XENKBD_FIELD_FEAT_DSBL_POINTER, 0);
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	/* Direct logic: if set, then create multi-touch device. */
23462306a36Sopenharmony_ci	with_mtouch = xenbus_read_unsigned(dev->otherend,
23562306a36Sopenharmony_ci					   XENKBD_FIELD_FEAT_MTOUCH, 0);
23662306a36Sopenharmony_ci	if (with_mtouch) {
23762306a36Sopenharmony_ci		ret = xenbus_write(XBT_NIL, dev->nodename,
23862306a36Sopenharmony_ci				   XENKBD_FIELD_REQ_MTOUCH, "1");
23962306a36Sopenharmony_ci		if (ret) {
24062306a36Sopenharmony_ci			pr_warn("xenkbd: can't request multi-touch");
24162306a36Sopenharmony_ci			with_mtouch = 0;
24262306a36Sopenharmony_ci		}
24362306a36Sopenharmony_ci	}
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	/* keyboard */
24662306a36Sopenharmony_ci	if (with_kbd) {
24762306a36Sopenharmony_ci		kbd = input_allocate_device();
24862306a36Sopenharmony_ci		if (!kbd)
24962306a36Sopenharmony_ci			goto error_nomem;
25062306a36Sopenharmony_ci		kbd->name = "Xen Virtual Keyboard";
25162306a36Sopenharmony_ci		kbd->phys = info->phys;
25262306a36Sopenharmony_ci		kbd->id.bustype = BUS_PCI;
25362306a36Sopenharmony_ci		kbd->id.vendor = 0x5853;
25462306a36Sopenharmony_ci		kbd->id.product = 0xffff;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci		__set_bit(EV_KEY, kbd->evbit);
25762306a36Sopenharmony_ci		for (i = KEY_ESC; i < KEY_UNKNOWN; i++)
25862306a36Sopenharmony_ci			__set_bit(i, kbd->keybit);
25962306a36Sopenharmony_ci		for (i = KEY_OK; i < KEY_MAX; i++)
26062306a36Sopenharmony_ci			__set_bit(i, kbd->keybit);
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci		ret = input_register_device(kbd);
26362306a36Sopenharmony_ci		if (ret) {
26462306a36Sopenharmony_ci			input_free_device(kbd);
26562306a36Sopenharmony_ci			xenbus_dev_fatal(dev, ret,
26662306a36Sopenharmony_ci					 "input_register_device(kbd)");
26762306a36Sopenharmony_ci			goto error;
26862306a36Sopenharmony_ci		}
26962306a36Sopenharmony_ci		info->kbd = kbd;
27062306a36Sopenharmony_ci	}
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	/* pointing device */
27362306a36Sopenharmony_ci	if (with_ptr) {
27462306a36Sopenharmony_ci		unsigned int abs;
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci		/* Set input abs params to match backend screen res */
27762306a36Sopenharmony_ci		abs = xenbus_read_unsigned(dev->otherend,
27862306a36Sopenharmony_ci					   XENKBD_FIELD_FEAT_ABS_POINTER, 0);
27962306a36Sopenharmony_ci		ptr_size[KPARAM_X] = xenbus_read_unsigned(dev->otherend,
28062306a36Sopenharmony_ci							  XENKBD_FIELD_WIDTH,
28162306a36Sopenharmony_ci							  ptr_size[KPARAM_X]);
28262306a36Sopenharmony_ci		ptr_size[KPARAM_Y] = xenbus_read_unsigned(dev->otherend,
28362306a36Sopenharmony_ci							  XENKBD_FIELD_HEIGHT,
28462306a36Sopenharmony_ci							  ptr_size[KPARAM_Y]);
28562306a36Sopenharmony_ci		if (abs) {
28662306a36Sopenharmony_ci			ret = xenbus_write(XBT_NIL, dev->nodename,
28762306a36Sopenharmony_ci					   XENKBD_FIELD_REQ_ABS_POINTER, "1");
28862306a36Sopenharmony_ci			if (ret) {
28962306a36Sopenharmony_ci				pr_warn("xenkbd: can't request abs-pointer\n");
29062306a36Sopenharmony_ci				abs = 0;
29162306a36Sopenharmony_ci			}
29262306a36Sopenharmony_ci		}
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci		ptr = input_allocate_device();
29562306a36Sopenharmony_ci		if (!ptr)
29662306a36Sopenharmony_ci			goto error_nomem;
29762306a36Sopenharmony_ci		ptr->name = "Xen Virtual Pointer";
29862306a36Sopenharmony_ci		ptr->phys = info->phys;
29962306a36Sopenharmony_ci		ptr->id.bustype = BUS_PCI;
30062306a36Sopenharmony_ci		ptr->id.vendor = 0x5853;
30162306a36Sopenharmony_ci		ptr->id.product = 0xfffe;
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci		if (abs) {
30462306a36Sopenharmony_ci			__set_bit(EV_ABS, ptr->evbit);
30562306a36Sopenharmony_ci			input_set_abs_params(ptr, ABS_X, 0,
30662306a36Sopenharmony_ci					     ptr_size[KPARAM_X], 0, 0);
30762306a36Sopenharmony_ci			input_set_abs_params(ptr, ABS_Y, 0,
30862306a36Sopenharmony_ci					     ptr_size[KPARAM_Y], 0, 0);
30962306a36Sopenharmony_ci		} else {
31062306a36Sopenharmony_ci			input_set_capability(ptr, EV_REL, REL_X);
31162306a36Sopenharmony_ci			input_set_capability(ptr, EV_REL, REL_Y);
31262306a36Sopenharmony_ci		}
31362306a36Sopenharmony_ci		input_set_capability(ptr, EV_REL, REL_WHEEL);
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci		__set_bit(EV_KEY, ptr->evbit);
31662306a36Sopenharmony_ci		for (i = BTN_LEFT; i <= BTN_TASK; i++)
31762306a36Sopenharmony_ci			__set_bit(i, ptr->keybit);
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci		ret = input_register_device(ptr);
32062306a36Sopenharmony_ci		if (ret) {
32162306a36Sopenharmony_ci			input_free_device(ptr);
32262306a36Sopenharmony_ci			xenbus_dev_fatal(dev, ret,
32362306a36Sopenharmony_ci					 "input_register_device(ptr)");
32462306a36Sopenharmony_ci			goto error;
32562306a36Sopenharmony_ci		}
32662306a36Sopenharmony_ci		info->ptr = ptr;
32762306a36Sopenharmony_ci	}
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	/* multi-touch device */
33062306a36Sopenharmony_ci	if (with_mtouch) {
33162306a36Sopenharmony_ci		int num_cont, width, height;
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci		mtouch = input_allocate_device();
33462306a36Sopenharmony_ci		if (!mtouch)
33562306a36Sopenharmony_ci			goto error_nomem;
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci		num_cont = xenbus_read_unsigned(info->xbdev->otherend,
33862306a36Sopenharmony_ci						XENKBD_FIELD_MT_NUM_CONTACTS,
33962306a36Sopenharmony_ci						1);
34062306a36Sopenharmony_ci		width = xenbus_read_unsigned(info->xbdev->otherend,
34162306a36Sopenharmony_ci					     XENKBD_FIELD_MT_WIDTH,
34262306a36Sopenharmony_ci					     XENFB_WIDTH);
34362306a36Sopenharmony_ci		height = xenbus_read_unsigned(info->xbdev->otherend,
34462306a36Sopenharmony_ci					      XENKBD_FIELD_MT_HEIGHT,
34562306a36Sopenharmony_ci					      XENFB_HEIGHT);
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci		mtouch->name = "Xen Virtual Multi-touch";
34862306a36Sopenharmony_ci		mtouch->phys = info->phys;
34962306a36Sopenharmony_ci		mtouch->id.bustype = BUS_PCI;
35062306a36Sopenharmony_ci		mtouch->id.vendor = 0x5853;
35162306a36Sopenharmony_ci		mtouch->id.product = 0xfffd;
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci		input_set_abs_params(mtouch, ABS_MT_TOUCH_MAJOR,
35462306a36Sopenharmony_ci				     0, 255, 0, 0);
35562306a36Sopenharmony_ci		input_set_abs_params(mtouch, ABS_MT_POSITION_X,
35662306a36Sopenharmony_ci				     0, width, 0, 0);
35762306a36Sopenharmony_ci		input_set_abs_params(mtouch, ABS_MT_POSITION_Y,
35862306a36Sopenharmony_ci				     0, height, 0, 0);
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci		ret = input_mt_init_slots(mtouch, num_cont, INPUT_MT_DIRECT);
36162306a36Sopenharmony_ci		if (ret) {
36262306a36Sopenharmony_ci			input_free_device(mtouch);
36362306a36Sopenharmony_ci			xenbus_dev_fatal(info->xbdev, ret,
36462306a36Sopenharmony_ci					 "input_mt_init_slots");
36562306a36Sopenharmony_ci			goto error;
36662306a36Sopenharmony_ci		}
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci		ret = input_register_device(mtouch);
36962306a36Sopenharmony_ci		if (ret) {
37062306a36Sopenharmony_ci			input_free_device(mtouch);
37162306a36Sopenharmony_ci			xenbus_dev_fatal(info->xbdev, ret,
37262306a36Sopenharmony_ci					 "input_register_device(mtouch)");
37362306a36Sopenharmony_ci			goto error;
37462306a36Sopenharmony_ci		}
37562306a36Sopenharmony_ci		info->mtouch_cur_contact_id = -1;
37662306a36Sopenharmony_ci		info->mtouch = mtouch;
37762306a36Sopenharmony_ci	}
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	if (!(with_kbd || with_ptr || with_mtouch)) {
38062306a36Sopenharmony_ci		ret = -ENXIO;
38162306a36Sopenharmony_ci		goto error;
38262306a36Sopenharmony_ci	}
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	ret = xenkbd_connect_backend(dev, info);
38562306a36Sopenharmony_ci	if (ret < 0)
38662306a36Sopenharmony_ci		goto error;
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	return 0;
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci error_nomem:
39162306a36Sopenharmony_ci	ret = -ENOMEM;
39262306a36Sopenharmony_ci	xenbus_dev_fatal(dev, ret, "allocating device memory");
39362306a36Sopenharmony_ci error:
39462306a36Sopenharmony_ci	xenkbd_remove(dev);
39562306a36Sopenharmony_ci	return ret;
39662306a36Sopenharmony_ci}
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_cistatic int xenkbd_resume(struct xenbus_device *dev)
39962306a36Sopenharmony_ci{
40062306a36Sopenharmony_ci	struct xenkbd_info *info = dev_get_drvdata(&dev->dev);
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	xenkbd_disconnect_backend(info);
40362306a36Sopenharmony_ci	memset(info->page, 0, PAGE_SIZE);
40462306a36Sopenharmony_ci	return xenkbd_connect_backend(dev, info);
40562306a36Sopenharmony_ci}
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_cistatic void xenkbd_remove(struct xenbus_device *dev)
40862306a36Sopenharmony_ci{
40962306a36Sopenharmony_ci	struct xenkbd_info *info = dev_get_drvdata(&dev->dev);
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	xenkbd_disconnect_backend(info);
41262306a36Sopenharmony_ci	if (info->kbd)
41362306a36Sopenharmony_ci		input_unregister_device(info->kbd);
41462306a36Sopenharmony_ci	if (info->ptr)
41562306a36Sopenharmony_ci		input_unregister_device(info->ptr);
41662306a36Sopenharmony_ci	if (info->mtouch)
41762306a36Sopenharmony_ci		input_unregister_device(info->mtouch);
41862306a36Sopenharmony_ci	free_page((unsigned long)info->page);
41962306a36Sopenharmony_ci	kfree(info);
42062306a36Sopenharmony_ci}
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_cistatic int xenkbd_connect_backend(struct xenbus_device *dev,
42362306a36Sopenharmony_ci				  struct xenkbd_info *info)
42462306a36Sopenharmony_ci{
42562306a36Sopenharmony_ci	int ret, evtchn;
42662306a36Sopenharmony_ci	struct xenbus_transaction xbt;
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	ret = gnttab_grant_foreign_access(dev->otherend_id,
42962306a36Sopenharmony_ci	                                  virt_to_gfn(info->page), 0);
43062306a36Sopenharmony_ci	if (ret < 0)
43162306a36Sopenharmony_ci		return ret;
43262306a36Sopenharmony_ci	info->gref = ret;
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	ret = xenbus_alloc_evtchn(dev, &evtchn);
43562306a36Sopenharmony_ci	if (ret)
43662306a36Sopenharmony_ci		goto error_grant;
43762306a36Sopenharmony_ci	ret = bind_evtchn_to_irqhandler(evtchn, input_handler,
43862306a36Sopenharmony_ci					0, dev->devicetype, info);
43962306a36Sopenharmony_ci	if (ret < 0) {
44062306a36Sopenharmony_ci		xenbus_dev_fatal(dev, ret, "bind_evtchn_to_irqhandler");
44162306a36Sopenharmony_ci		goto error_evtchan;
44262306a36Sopenharmony_ci	}
44362306a36Sopenharmony_ci	info->irq = ret;
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci again:
44662306a36Sopenharmony_ci	ret = xenbus_transaction_start(&xbt);
44762306a36Sopenharmony_ci	if (ret) {
44862306a36Sopenharmony_ci		xenbus_dev_fatal(dev, ret, "starting transaction");
44962306a36Sopenharmony_ci		goto error_irqh;
45062306a36Sopenharmony_ci	}
45162306a36Sopenharmony_ci	ret = xenbus_printf(xbt, dev->nodename, XENKBD_FIELD_RING_REF, "%lu",
45262306a36Sopenharmony_ci			    virt_to_gfn(info->page));
45362306a36Sopenharmony_ci	if (ret)
45462306a36Sopenharmony_ci		goto error_xenbus;
45562306a36Sopenharmony_ci	ret = xenbus_printf(xbt, dev->nodename, XENKBD_FIELD_RING_GREF,
45662306a36Sopenharmony_ci			    "%u", info->gref);
45762306a36Sopenharmony_ci	if (ret)
45862306a36Sopenharmony_ci		goto error_xenbus;
45962306a36Sopenharmony_ci	ret = xenbus_printf(xbt, dev->nodename, XENKBD_FIELD_EVT_CHANNEL, "%u",
46062306a36Sopenharmony_ci			    evtchn);
46162306a36Sopenharmony_ci	if (ret)
46262306a36Sopenharmony_ci		goto error_xenbus;
46362306a36Sopenharmony_ci	ret = xenbus_transaction_end(xbt, 0);
46462306a36Sopenharmony_ci	if (ret) {
46562306a36Sopenharmony_ci		if (ret == -EAGAIN)
46662306a36Sopenharmony_ci			goto again;
46762306a36Sopenharmony_ci		xenbus_dev_fatal(dev, ret, "completing transaction");
46862306a36Sopenharmony_ci		goto error_irqh;
46962306a36Sopenharmony_ci	}
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	xenbus_switch_state(dev, XenbusStateInitialised);
47262306a36Sopenharmony_ci	return 0;
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci error_xenbus:
47562306a36Sopenharmony_ci	xenbus_transaction_end(xbt, 1);
47662306a36Sopenharmony_ci	xenbus_dev_fatal(dev, ret, "writing xenstore");
47762306a36Sopenharmony_ci error_irqh:
47862306a36Sopenharmony_ci	unbind_from_irqhandler(info->irq, info);
47962306a36Sopenharmony_ci	info->irq = -1;
48062306a36Sopenharmony_ci error_evtchan:
48162306a36Sopenharmony_ci	xenbus_free_evtchn(dev, evtchn);
48262306a36Sopenharmony_ci error_grant:
48362306a36Sopenharmony_ci	gnttab_end_foreign_access(info->gref, NULL);
48462306a36Sopenharmony_ci	info->gref = -1;
48562306a36Sopenharmony_ci	return ret;
48662306a36Sopenharmony_ci}
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_cistatic void xenkbd_disconnect_backend(struct xenkbd_info *info)
48962306a36Sopenharmony_ci{
49062306a36Sopenharmony_ci	if (info->irq >= 0)
49162306a36Sopenharmony_ci		unbind_from_irqhandler(info->irq, info);
49262306a36Sopenharmony_ci	info->irq = -1;
49362306a36Sopenharmony_ci	if (info->gref >= 0)
49462306a36Sopenharmony_ci		gnttab_end_foreign_access(info->gref, NULL);
49562306a36Sopenharmony_ci	info->gref = -1;
49662306a36Sopenharmony_ci}
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_cistatic void xenkbd_backend_changed(struct xenbus_device *dev,
49962306a36Sopenharmony_ci				   enum xenbus_state backend_state)
50062306a36Sopenharmony_ci{
50162306a36Sopenharmony_ci	switch (backend_state) {
50262306a36Sopenharmony_ci	case XenbusStateInitialising:
50362306a36Sopenharmony_ci	case XenbusStateInitialised:
50462306a36Sopenharmony_ci	case XenbusStateReconfiguring:
50562306a36Sopenharmony_ci	case XenbusStateReconfigured:
50662306a36Sopenharmony_ci	case XenbusStateUnknown:
50762306a36Sopenharmony_ci		break;
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	case XenbusStateInitWait:
51062306a36Sopenharmony_ci		xenbus_switch_state(dev, XenbusStateConnected);
51162306a36Sopenharmony_ci		break;
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	case XenbusStateConnected:
51462306a36Sopenharmony_ci		/*
51562306a36Sopenharmony_ci		 * Work around xenbus race condition: If backend goes
51662306a36Sopenharmony_ci		 * through InitWait to Connected fast enough, we can
51762306a36Sopenharmony_ci		 * get Connected twice here.
51862306a36Sopenharmony_ci		 */
51962306a36Sopenharmony_ci		if (dev->state != XenbusStateConnected)
52062306a36Sopenharmony_ci			xenbus_switch_state(dev, XenbusStateConnected);
52162306a36Sopenharmony_ci		break;
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	case XenbusStateClosed:
52462306a36Sopenharmony_ci		if (dev->state == XenbusStateClosed)
52562306a36Sopenharmony_ci			break;
52662306a36Sopenharmony_ci		fallthrough;	/* Missed the backend's CLOSING state */
52762306a36Sopenharmony_ci	case XenbusStateClosing:
52862306a36Sopenharmony_ci		xenbus_frontend_closed(dev);
52962306a36Sopenharmony_ci		break;
53062306a36Sopenharmony_ci	}
53162306a36Sopenharmony_ci}
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_cistatic const struct xenbus_device_id xenkbd_ids[] = {
53462306a36Sopenharmony_ci	{ XENKBD_DRIVER_NAME },
53562306a36Sopenharmony_ci	{ "" }
53662306a36Sopenharmony_ci};
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_cistatic struct xenbus_driver xenkbd_driver = {
53962306a36Sopenharmony_ci	.ids = xenkbd_ids,
54062306a36Sopenharmony_ci	.probe = xenkbd_probe,
54162306a36Sopenharmony_ci	.remove = xenkbd_remove,
54262306a36Sopenharmony_ci	.resume = xenkbd_resume,
54362306a36Sopenharmony_ci	.otherend_changed = xenkbd_backend_changed,
54462306a36Sopenharmony_ci	.not_essential = true,
54562306a36Sopenharmony_ci};
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_cistatic int __init xenkbd_init(void)
54862306a36Sopenharmony_ci{
54962306a36Sopenharmony_ci	if (!xen_domain())
55062306a36Sopenharmony_ci		return -ENODEV;
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	/* Nothing to do if running in dom0. */
55362306a36Sopenharmony_ci	if (xen_initial_domain())
55462306a36Sopenharmony_ci		return -ENODEV;
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	if (!xen_has_pv_devices())
55762306a36Sopenharmony_ci		return -ENODEV;
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	return xenbus_register_frontend(&xenkbd_driver);
56062306a36Sopenharmony_ci}
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_cistatic void __exit xenkbd_cleanup(void)
56362306a36Sopenharmony_ci{
56462306a36Sopenharmony_ci	xenbus_unregister_driver(&xenkbd_driver);
56562306a36Sopenharmony_ci}
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_cimodule_init(xenkbd_init);
56862306a36Sopenharmony_cimodule_exit(xenkbd_cleanup);
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ciMODULE_DESCRIPTION("Xen virtual keyboard/pointer device frontend");
57162306a36Sopenharmony_ciMODULE_LICENSE("GPL");
57262306a36Sopenharmony_ciMODULE_ALIAS("xen:" XENKBD_DRIVER_NAME);
573