18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Xen para-virtual input device
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (C) 2005 Anthony Liguori <aliguori@us.ibm.com>
58c2ecf20Sopenharmony_ci * Copyright (C) 2006-2008 Red Hat, Inc., Markus Armbruster <armbru@redhat.com>
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci *  Based on linux/drivers/input/mouse/sermouse.c
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci *  This file is subject to the terms and conditions of the GNU General Public
108c2ecf20Sopenharmony_ci *  License. See the file COPYING in the main directory of this archive for
118c2ecf20Sopenharmony_ci *  more details.
128c2ecf20Sopenharmony_ci */
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include <linux/kernel.h>
178c2ecf20Sopenharmony_ci#include <linux/errno.h>
188c2ecf20Sopenharmony_ci#include <linux/module.h>
198c2ecf20Sopenharmony_ci#include <linux/input.h>
208c2ecf20Sopenharmony_ci#include <linux/input/mt.h>
218c2ecf20Sopenharmony_ci#include <linux/slab.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#include <asm/xen/hypervisor.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#include <xen/xen.h>
268c2ecf20Sopenharmony_ci#include <xen/events.h>
278c2ecf20Sopenharmony_ci#include <xen/page.h>
288c2ecf20Sopenharmony_ci#include <xen/grant_table.h>
298c2ecf20Sopenharmony_ci#include <xen/interface/grant_table.h>
308c2ecf20Sopenharmony_ci#include <xen/interface/io/fbif.h>
318c2ecf20Sopenharmony_ci#include <xen/interface/io/kbdif.h>
328c2ecf20Sopenharmony_ci#include <xen/xenbus.h>
338c2ecf20Sopenharmony_ci#include <xen/platform_pci.h>
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistruct xenkbd_info {
368c2ecf20Sopenharmony_ci	struct input_dev *kbd;
378c2ecf20Sopenharmony_ci	struct input_dev *ptr;
388c2ecf20Sopenharmony_ci	struct input_dev *mtouch;
398c2ecf20Sopenharmony_ci	struct xenkbd_page *page;
408c2ecf20Sopenharmony_ci	int gref;
418c2ecf20Sopenharmony_ci	int irq;
428c2ecf20Sopenharmony_ci	struct xenbus_device *xbdev;
438c2ecf20Sopenharmony_ci	char phys[32];
448c2ecf20Sopenharmony_ci	/* current MT slot/contact ID we are injecting events in */
458c2ecf20Sopenharmony_ci	int mtouch_cur_contact_id;
468c2ecf20Sopenharmony_ci};
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cienum { KPARAM_X, KPARAM_Y, KPARAM_CNT };
498c2ecf20Sopenharmony_cistatic int ptr_size[KPARAM_CNT] = { XENFB_WIDTH, XENFB_HEIGHT };
508c2ecf20Sopenharmony_cimodule_param_array(ptr_size, int, NULL, 0444);
518c2ecf20Sopenharmony_ciMODULE_PARM_DESC(ptr_size,
528c2ecf20Sopenharmony_ci	"Pointing device width, height in pixels (default 800,600)");
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_cistatic int xenkbd_remove(struct xenbus_device *);
558c2ecf20Sopenharmony_cistatic int xenkbd_connect_backend(struct xenbus_device *, struct xenkbd_info *);
568c2ecf20Sopenharmony_cistatic void xenkbd_disconnect_backend(struct xenkbd_info *);
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci/*
598c2ecf20Sopenharmony_ci * Note: if you need to send out events, see xenfb_do_update() for how
608c2ecf20Sopenharmony_ci * to do that.
618c2ecf20Sopenharmony_ci */
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_cistatic void xenkbd_handle_motion_event(struct xenkbd_info *info,
648c2ecf20Sopenharmony_ci				       struct xenkbd_motion *motion)
658c2ecf20Sopenharmony_ci{
668c2ecf20Sopenharmony_ci	if (unlikely(!info->ptr))
678c2ecf20Sopenharmony_ci		return;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	input_report_rel(info->ptr, REL_X, motion->rel_x);
708c2ecf20Sopenharmony_ci	input_report_rel(info->ptr, REL_Y, motion->rel_y);
718c2ecf20Sopenharmony_ci	if (motion->rel_z)
728c2ecf20Sopenharmony_ci		input_report_rel(info->ptr, REL_WHEEL, -motion->rel_z);
738c2ecf20Sopenharmony_ci	input_sync(info->ptr);
748c2ecf20Sopenharmony_ci}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_cistatic void xenkbd_handle_position_event(struct xenkbd_info *info,
778c2ecf20Sopenharmony_ci					 struct xenkbd_position *pos)
788c2ecf20Sopenharmony_ci{
798c2ecf20Sopenharmony_ci	if (unlikely(!info->ptr))
808c2ecf20Sopenharmony_ci		return;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	input_report_abs(info->ptr, ABS_X, pos->abs_x);
838c2ecf20Sopenharmony_ci	input_report_abs(info->ptr, ABS_Y, pos->abs_y);
848c2ecf20Sopenharmony_ci	if (pos->rel_z)
858c2ecf20Sopenharmony_ci		input_report_rel(info->ptr, REL_WHEEL, -pos->rel_z);
868c2ecf20Sopenharmony_ci	input_sync(info->ptr);
878c2ecf20Sopenharmony_ci}
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_cistatic void xenkbd_handle_key_event(struct xenkbd_info *info,
908c2ecf20Sopenharmony_ci				    struct xenkbd_key *key)
918c2ecf20Sopenharmony_ci{
928c2ecf20Sopenharmony_ci	struct input_dev *dev;
938c2ecf20Sopenharmony_ci	int value = key->pressed;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	if (test_bit(key->keycode, info->ptr->keybit)) {
968c2ecf20Sopenharmony_ci		dev = info->ptr;
978c2ecf20Sopenharmony_ci	} else if (test_bit(key->keycode, info->kbd->keybit)) {
988c2ecf20Sopenharmony_ci		dev = info->kbd;
998c2ecf20Sopenharmony_ci		if (key->pressed && test_bit(key->keycode, info->kbd->key))
1008c2ecf20Sopenharmony_ci			value = 2; /* Mark as autorepeat */
1018c2ecf20Sopenharmony_ci	} else {
1028c2ecf20Sopenharmony_ci		pr_warn("unhandled keycode 0x%x\n", key->keycode);
1038c2ecf20Sopenharmony_ci		return;
1048c2ecf20Sopenharmony_ci	}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	if (unlikely(!dev))
1078c2ecf20Sopenharmony_ci		return;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	input_event(dev, EV_KEY, key->keycode, value);
1108c2ecf20Sopenharmony_ci	input_sync(dev);
1118c2ecf20Sopenharmony_ci}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_cistatic void xenkbd_handle_mt_event(struct xenkbd_info *info,
1148c2ecf20Sopenharmony_ci				   struct xenkbd_mtouch *mtouch)
1158c2ecf20Sopenharmony_ci{
1168c2ecf20Sopenharmony_ci	if (unlikely(!info->mtouch))
1178c2ecf20Sopenharmony_ci		return;
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	if (mtouch->contact_id != info->mtouch_cur_contact_id) {
1208c2ecf20Sopenharmony_ci		info->mtouch_cur_contact_id = mtouch->contact_id;
1218c2ecf20Sopenharmony_ci		input_mt_slot(info->mtouch, mtouch->contact_id);
1228c2ecf20Sopenharmony_ci	}
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	switch (mtouch->event_type) {
1258c2ecf20Sopenharmony_ci	case XENKBD_MT_EV_DOWN:
1268c2ecf20Sopenharmony_ci		input_mt_report_slot_state(info->mtouch, MT_TOOL_FINGER, true);
1278c2ecf20Sopenharmony_ci		fallthrough;
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	case XENKBD_MT_EV_MOTION:
1308c2ecf20Sopenharmony_ci		input_report_abs(info->mtouch, ABS_MT_POSITION_X,
1318c2ecf20Sopenharmony_ci				 mtouch->u.pos.abs_x);
1328c2ecf20Sopenharmony_ci		input_report_abs(info->mtouch, ABS_MT_POSITION_Y,
1338c2ecf20Sopenharmony_ci				 mtouch->u.pos.abs_y);
1348c2ecf20Sopenharmony_ci		break;
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	case XENKBD_MT_EV_SHAPE:
1378c2ecf20Sopenharmony_ci		input_report_abs(info->mtouch, ABS_MT_TOUCH_MAJOR,
1388c2ecf20Sopenharmony_ci				 mtouch->u.shape.major);
1398c2ecf20Sopenharmony_ci		input_report_abs(info->mtouch, ABS_MT_TOUCH_MINOR,
1408c2ecf20Sopenharmony_ci				 mtouch->u.shape.minor);
1418c2ecf20Sopenharmony_ci		break;
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	case XENKBD_MT_EV_ORIENT:
1448c2ecf20Sopenharmony_ci		input_report_abs(info->mtouch, ABS_MT_ORIENTATION,
1458c2ecf20Sopenharmony_ci				 mtouch->u.orientation);
1468c2ecf20Sopenharmony_ci		break;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	case XENKBD_MT_EV_UP:
1498c2ecf20Sopenharmony_ci		input_mt_report_slot_inactive(info->mtouch);
1508c2ecf20Sopenharmony_ci		break;
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	case XENKBD_MT_EV_SYN:
1538c2ecf20Sopenharmony_ci		input_mt_sync_frame(info->mtouch);
1548c2ecf20Sopenharmony_ci		input_sync(info->mtouch);
1558c2ecf20Sopenharmony_ci		break;
1568c2ecf20Sopenharmony_ci	}
1578c2ecf20Sopenharmony_ci}
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_cistatic void xenkbd_handle_event(struct xenkbd_info *info,
1608c2ecf20Sopenharmony_ci				union xenkbd_in_event *event)
1618c2ecf20Sopenharmony_ci{
1628c2ecf20Sopenharmony_ci	switch (event->type) {
1638c2ecf20Sopenharmony_ci	case XENKBD_TYPE_MOTION:
1648c2ecf20Sopenharmony_ci		xenkbd_handle_motion_event(info, &event->motion);
1658c2ecf20Sopenharmony_ci		break;
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	case XENKBD_TYPE_KEY:
1688c2ecf20Sopenharmony_ci		xenkbd_handle_key_event(info, &event->key);
1698c2ecf20Sopenharmony_ci		break;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	case XENKBD_TYPE_POS:
1728c2ecf20Sopenharmony_ci		xenkbd_handle_position_event(info, &event->pos);
1738c2ecf20Sopenharmony_ci		break;
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	case XENKBD_TYPE_MTOUCH:
1768c2ecf20Sopenharmony_ci		xenkbd_handle_mt_event(info, &event->mtouch);
1778c2ecf20Sopenharmony_ci		break;
1788c2ecf20Sopenharmony_ci	}
1798c2ecf20Sopenharmony_ci}
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_cistatic irqreturn_t input_handler(int rq, void *dev_id)
1828c2ecf20Sopenharmony_ci{
1838c2ecf20Sopenharmony_ci	struct xenkbd_info *info = dev_id;
1848c2ecf20Sopenharmony_ci	struct xenkbd_page *page = info->page;
1858c2ecf20Sopenharmony_ci	__u32 cons, prod;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	prod = page->in_prod;
1888c2ecf20Sopenharmony_ci	if (prod == page->in_cons)
1898c2ecf20Sopenharmony_ci		return IRQ_HANDLED;
1908c2ecf20Sopenharmony_ci	rmb();			/* ensure we see ring contents up to prod */
1918c2ecf20Sopenharmony_ci	for (cons = page->in_cons; cons != prod; cons++)
1928c2ecf20Sopenharmony_ci		xenkbd_handle_event(info, &XENKBD_IN_RING_REF(page, cons));
1938c2ecf20Sopenharmony_ci	mb();			/* ensure we got ring contents */
1948c2ecf20Sopenharmony_ci	page->in_cons = cons;
1958c2ecf20Sopenharmony_ci	notify_remote_via_irq(info->irq);
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
1988c2ecf20Sopenharmony_ci}
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_cistatic int xenkbd_probe(struct xenbus_device *dev,
2018c2ecf20Sopenharmony_ci				  const struct xenbus_device_id *id)
2028c2ecf20Sopenharmony_ci{
2038c2ecf20Sopenharmony_ci	int ret, i;
2048c2ecf20Sopenharmony_ci	bool with_mtouch, with_kbd, with_ptr;
2058c2ecf20Sopenharmony_ci	struct xenkbd_info *info;
2068c2ecf20Sopenharmony_ci	struct input_dev *kbd, *ptr, *mtouch;
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	info = kzalloc(sizeof(*info), GFP_KERNEL);
2098c2ecf20Sopenharmony_ci	if (!info) {
2108c2ecf20Sopenharmony_ci		xenbus_dev_fatal(dev, -ENOMEM, "allocating info structure");
2118c2ecf20Sopenharmony_ci		return -ENOMEM;
2128c2ecf20Sopenharmony_ci	}
2138c2ecf20Sopenharmony_ci	dev_set_drvdata(&dev->dev, info);
2148c2ecf20Sopenharmony_ci	info->xbdev = dev;
2158c2ecf20Sopenharmony_ci	info->irq = -1;
2168c2ecf20Sopenharmony_ci	info->gref = -1;
2178c2ecf20Sopenharmony_ci	snprintf(info->phys, sizeof(info->phys), "xenbus/%s", dev->nodename);
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	info->page = (void *)__get_free_page(GFP_KERNEL | __GFP_ZERO);
2208c2ecf20Sopenharmony_ci	if (!info->page)
2218c2ecf20Sopenharmony_ci		goto error_nomem;
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	/*
2248c2ecf20Sopenharmony_ci	 * The below are reverse logic, e.g. if the feature is set, then
2258c2ecf20Sopenharmony_ci	 * do not expose the corresponding virtual device.
2268c2ecf20Sopenharmony_ci	 */
2278c2ecf20Sopenharmony_ci	with_kbd = !xenbus_read_unsigned(dev->otherend,
2288c2ecf20Sopenharmony_ci					 XENKBD_FIELD_FEAT_DSBL_KEYBRD, 0);
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	with_ptr = !xenbus_read_unsigned(dev->otherend,
2318c2ecf20Sopenharmony_ci					 XENKBD_FIELD_FEAT_DSBL_POINTER, 0);
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	/* Direct logic: if set, then create multi-touch device. */
2348c2ecf20Sopenharmony_ci	with_mtouch = xenbus_read_unsigned(dev->otherend,
2358c2ecf20Sopenharmony_ci					   XENKBD_FIELD_FEAT_MTOUCH, 0);
2368c2ecf20Sopenharmony_ci	if (with_mtouch) {
2378c2ecf20Sopenharmony_ci		ret = xenbus_write(XBT_NIL, dev->nodename,
2388c2ecf20Sopenharmony_ci				   XENKBD_FIELD_REQ_MTOUCH, "1");
2398c2ecf20Sopenharmony_ci		if (ret) {
2408c2ecf20Sopenharmony_ci			pr_warn("xenkbd: can't request multi-touch");
2418c2ecf20Sopenharmony_ci			with_mtouch = 0;
2428c2ecf20Sopenharmony_ci		}
2438c2ecf20Sopenharmony_ci	}
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	/* keyboard */
2468c2ecf20Sopenharmony_ci	if (with_kbd) {
2478c2ecf20Sopenharmony_ci		kbd = input_allocate_device();
2488c2ecf20Sopenharmony_ci		if (!kbd)
2498c2ecf20Sopenharmony_ci			goto error_nomem;
2508c2ecf20Sopenharmony_ci		kbd->name = "Xen Virtual Keyboard";
2518c2ecf20Sopenharmony_ci		kbd->phys = info->phys;
2528c2ecf20Sopenharmony_ci		kbd->id.bustype = BUS_PCI;
2538c2ecf20Sopenharmony_ci		kbd->id.vendor = 0x5853;
2548c2ecf20Sopenharmony_ci		kbd->id.product = 0xffff;
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci		__set_bit(EV_KEY, kbd->evbit);
2578c2ecf20Sopenharmony_ci		for (i = KEY_ESC; i < KEY_UNKNOWN; i++)
2588c2ecf20Sopenharmony_ci			__set_bit(i, kbd->keybit);
2598c2ecf20Sopenharmony_ci		for (i = KEY_OK; i < KEY_MAX; i++)
2608c2ecf20Sopenharmony_ci			__set_bit(i, kbd->keybit);
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci		ret = input_register_device(kbd);
2638c2ecf20Sopenharmony_ci		if (ret) {
2648c2ecf20Sopenharmony_ci			input_free_device(kbd);
2658c2ecf20Sopenharmony_ci			xenbus_dev_fatal(dev, ret,
2668c2ecf20Sopenharmony_ci					 "input_register_device(kbd)");
2678c2ecf20Sopenharmony_ci			goto error;
2688c2ecf20Sopenharmony_ci		}
2698c2ecf20Sopenharmony_ci		info->kbd = kbd;
2708c2ecf20Sopenharmony_ci	}
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	/* pointing device */
2738c2ecf20Sopenharmony_ci	if (with_ptr) {
2748c2ecf20Sopenharmony_ci		unsigned int abs;
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci		/* Set input abs params to match backend screen res */
2778c2ecf20Sopenharmony_ci		abs = xenbus_read_unsigned(dev->otherend,
2788c2ecf20Sopenharmony_ci					   XENKBD_FIELD_FEAT_ABS_POINTER, 0);
2798c2ecf20Sopenharmony_ci		ptr_size[KPARAM_X] = xenbus_read_unsigned(dev->otherend,
2808c2ecf20Sopenharmony_ci							  XENKBD_FIELD_WIDTH,
2818c2ecf20Sopenharmony_ci							  ptr_size[KPARAM_X]);
2828c2ecf20Sopenharmony_ci		ptr_size[KPARAM_Y] = xenbus_read_unsigned(dev->otherend,
2838c2ecf20Sopenharmony_ci							  XENKBD_FIELD_HEIGHT,
2848c2ecf20Sopenharmony_ci							  ptr_size[KPARAM_Y]);
2858c2ecf20Sopenharmony_ci		if (abs) {
2868c2ecf20Sopenharmony_ci			ret = xenbus_write(XBT_NIL, dev->nodename,
2878c2ecf20Sopenharmony_ci					   XENKBD_FIELD_REQ_ABS_POINTER, "1");
2888c2ecf20Sopenharmony_ci			if (ret) {
2898c2ecf20Sopenharmony_ci				pr_warn("xenkbd: can't request abs-pointer\n");
2908c2ecf20Sopenharmony_ci				abs = 0;
2918c2ecf20Sopenharmony_ci			}
2928c2ecf20Sopenharmony_ci		}
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci		ptr = input_allocate_device();
2958c2ecf20Sopenharmony_ci		if (!ptr)
2968c2ecf20Sopenharmony_ci			goto error_nomem;
2978c2ecf20Sopenharmony_ci		ptr->name = "Xen Virtual Pointer";
2988c2ecf20Sopenharmony_ci		ptr->phys = info->phys;
2998c2ecf20Sopenharmony_ci		ptr->id.bustype = BUS_PCI;
3008c2ecf20Sopenharmony_ci		ptr->id.vendor = 0x5853;
3018c2ecf20Sopenharmony_ci		ptr->id.product = 0xfffe;
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci		if (abs) {
3048c2ecf20Sopenharmony_ci			__set_bit(EV_ABS, ptr->evbit);
3058c2ecf20Sopenharmony_ci			input_set_abs_params(ptr, ABS_X, 0,
3068c2ecf20Sopenharmony_ci					     ptr_size[KPARAM_X], 0, 0);
3078c2ecf20Sopenharmony_ci			input_set_abs_params(ptr, ABS_Y, 0,
3088c2ecf20Sopenharmony_ci					     ptr_size[KPARAM_Y], 0, 0);
3098c2ecf20Sopenharmony_ci		} else {
3108c2ecf20Sopenharmony_ci			input_set_capability(ptr, EV_REL, REL_X);
3118c2ecf20Sopenharmony_ci			input_set_capability(ptr, EV_REL, REL_Y);
3128c2ecf20Sopenharmony_ci		}
3138c2ecf20Sopenharmony_ci		input_set_capability(ptr, EV_REL, REL_WHEEL);
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci		__set_bit(EV_KEY, ptr->evbit);
3168c2ecf20Sopenharmony_ci		for (i = BTN_LEFT; i <= BTN_TASK; i++)
3178c2ecf20Sopenharmony_ci			__set_bit(i, ptr->keybit);
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci		ret = input_register_device(ptr);
3208c2ecf20Sopenharmony_ci		if (ret) {
3218c2ecf20Sopenharmony_ci			input_free_device(ptr);
3228c2ecf20Sopenharmony_ci			xenbus_dev_fatal(dev, ret,
3238c2ecf20Sopenharmony_ci					 "input_register_device(ptr)");
3248c2ecf20Sopenharmony_ci			goto error;
3258c2ecf20Sopenharmony_ci		}
3268c2ecf20Sopenharmony_ci		info->ptr = ptr;
3278c2ecf20Sopenharmony_ci	}
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	/* multi-touch device */
3308c2ecf20Sopenharmony_ci	if (with_mtouch) {
3318c2ecf20Sopenharmony_ci		int num_cont, width, height;
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci		mtouch = input_allocate_device();
3348c2ecf20Sopenharmony_ci		if (!mtouch)
3358c2ecf20Sopenharmony_ci			goto error_nomem;
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci		num_cont = xenbus_read_unsigned(info->xbdev->otherend,
3388c2ecf20Sopenharmony_ci						XENKBD_FIELD_MT_NUM_CONTACTS,
3398c2ecf20Sopenharmony_ci						1);
3408c2ecf20Sopenharmony_ci		width = xenbus_read_unsigned(info->xbdev->otherend,
3418c2ecf20Sopenharmony_ci					     XENKBD_FIELD_MT_WIDTH,
3428c2ecf20Sopenharmony_ci					     XENFB_WIDTH);
3438c2ecf20Sopenharmony_ci		height = xenbus_read_unsigned(info->xbdev->otherend,
3448c2ecf20Sopenharmony_ci					      XENKBD_FIELD_MT_HEIGHT,
3458c2ecf20Sopenharmony_ci					      XENFB_HEIGHT);
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci		mtouch->name = "Xen Virtual Multi-touch";
3488c2ecf20Sopenharmony_ci		mtouch->phys = info->phys;
3498c2ecf20Sopenharmony_ci		mtouch->id.bustype = BUS_PCI;
3508c2ecf20Sopenharmony_ci		mtouch->id.vendor = 0x5853;
3518c2ecf20Sopenharmony_ci		mtouch->id.product = 0xfffd;
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci		input_set_abs_params(mtouch, ABS_MT_TOUCH_MAJOR,
3548c2ecf20Sopenharmony_ci				     0, 255, 0, 0);
3558c2ecf20Sopenharmony_ci		input_set_abs_params(mtouch, ABS_MT_POSITION_X,
3568c2ecf20Sopenharmony_ci				     0, width, 0, 0);
3578c2ecf20Sopenharmony_ci		input_set_abs_params(mtouch, ABS_MT_POSITION_Y,
3588c2ecf20Sopenharmony_ci				     0, height, 0, 0);
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci		ret = input_mt_init_slots(mtouch, num_cont, INPUT_MT_DIRECT);
3618c2ecf20Sopenharmony_ci		if (ret) {
3628c2ecf20Sopenharmony_ci			input_free_device(mtouch);
3638c2ecf20Sopenharmony_ci			xenbus_dev_fatal(info->xbdev, ret,
3648c2ecf20Sopenharmony_ci					 "input_mt_init_slots");
3658c2ecf20Sopenharmony_ci			goto error;
3668c2ecf20Sopenharmony_ci		}
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci		ret = input_register_device(mtouch);
3698c2ecf20Sopenharmony_ci		if (ret) {
3708c2ecf20Sopenharmony_ci			input_free_device(mtouch);
3718c2ecf20Sopenharmony_ci			xenbus_dev_fatal(info->xbdev, ret,
3728c2ecf20Sopenharmony_ci					 "input_register_device(mtouch)");
3738c2ecf20Sopenharmony_ci			goto error;
3748c2ecf20Sopenharmony_ci		}
3758c2ecf20Sopenharmony_ci		info->mtouch_cur_contact_id = -1;
3768c2ecf20Sopenharmony_ci		info->mtouch = mtouch;
3778c2ecf20Sopenharmony_ci	}
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	if (!(with_kbd || with_ptr || with_mtouch)) {
3808c2ecf20Sopenharmony_ci		ret = -ENXIO;
3818c2ecf20Sopenharmony_ci		goto error;
3828c2ecf20Sopenharmony_ci	}
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	ret = xenkbd_connect_backend(dev, info);
3858c2ecf20Sopenharmony_ci	if (ret < 0)
3868c2ecf20Sopenharmony_ci		goto error;
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	return 0;
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci error_nomem:
3918c2ecf20Sopenharmony_ci	ret = -ENOMEM;
3928c2ecf20Sopenharmony_ci	xenbus_dev_fatal(dev, ret, "allocating device memory");
3938c2ecf20Sopenharmony_ci error:
3948c2ecf20Sopenharmony_ci	xenkbd_remove(dev);
3958c2ecf20Sopenharmony_ci	return ret;
3968c2ecf20Sopenharmony_ci}
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_cistatic int xenkbd_resume(struct xenbus_device *dev)
3998c2ecf20Sopenharmony_ci{
4008c2ecf20Sopenharmony_ci	struct xenkbd_info *info = dev_get_drvdata(&dev->dev);
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	xenkbd_disconnect_backend(info);
4038c2ecf20Sopenharmony_ci	memset(info->page, 0, PAGE_SIZE);
4048c2ecf20Sopenharmony_ci	return xenkbd_connect_backend(dev, info);
4058c2ecf20Sopenharmony_ci}
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_cistatic int xenkbd_remove(struct xenbus_device *dev)
4088c2ecf20Sopenharmony_ci{
4098c2ecf20Sopenharmony_ci	struct xenkbd_info *info = dev_get_drvdata(&dev->dev);
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	xenkbd_disconnect_backend(info);
4128c2ecf20Sopenharmony_ci	if (info->kbd)
4138c2ecf20Sopenharmony_ci		input_unregister_device(info->kbd);
4148c2ecf20Sopenharmony_ci	if (info->ptr)
4158c2ecf20Sopenharmony_ci		input_unregister_device(info->ptr);
4168c2ecf20Sopenharmony_ci	if (info->mtouch)
4178c2ecf20Sopenharmony_ci		input_unregister_device(info->mtouch);
4188c2ecf20Sopenharmony_ci	free_page((unsigned long)info->page);
4198c2ecf20Sopenharmony_ci	kfree(info);
4208c2ecf20Sopenharmony_ci	return 0;
4218c2ecf20Sopenharmony_ci}
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_cistatic int xenkbd_connect_backend(struct xenbus_device *dev,
4248c2ecf20Sopenharmony_ci				  struct xenkbd_info *info)
4258c2ecf20Sopenharmony_ci{
4268c2ecf20Sopenharmony_ci	int ret, evtchn;
4278c2ecf20Sopenharmony_ci	struct xenbus_transaction xbt;
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	ret = gnttab_grant_foreign_access(dev->otherend_id,
4308c2ecf20Sopenharmony_ci	                                  virt_to_gfn(info->page), 0);
4318c2ecf20Sopenharmony_ci	if (ret < 0)
4328c2ecf20Sopenharmony_ci		return ret;
4338c2ecf20Sopenharmony_ci	info->gref = ret;
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	ret = xenbus_alloc_evtchn(dev, &evtchn);
4368c2ecf20Sopenharmony_ci	if (ret)
4378c2ecf20Sopenharmony_ci		goto error_grant;
4388c2ecf20Sopenharmony_ci	ret = bind_evtchn_to_irqhandler(evtchn, input_handler,
4398c2ecf20Sopenharmony_ci					0, dev->devicetype, info);
4408c2ecf20Sopenharmony_ci	if (ret < 0) {
4418c2ecf20Sopenharmony_ci		xenbus_dev_fatal(dev, ret, "bind_evtchn_to_irqhandler");
4428c2ecf20Sopenharmony_ci		goto error_evtchan;
4438c2ecf20Sopenharmony_ci	}
4448c2ecf20Sopenharmony_ci	info->irq = ret;
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci again:
4478c2ecf20Sopenharmony_ci	ret = xenbus_transaction_start(&xbt);
4488c2ecf20Sopenharmony_ci	if (ret) {
4498c2ecf20Sopenharmony_ci		xenbus_dev_fatal(dev, ret, "starting transaction");
4508c2ecf20Sopenharmony_ci		goto error_irqh;
4518c2ecf20Sopenharmony_ci	}
4528c2ecf20Sopenharmony_ci	ret = xenbus_printf(xbt, dev->nodename, XENKBD_FIELD_RING_REF, "%lu",
4538c2ecf20Sopenharmony_ci			    virt_to_gfn(info->page));
4548c2ecf20Sopenharmony_ci	if (ret)
4558c2ecf20Sopenharmony_ci		goto error_xenbus;
4568c2ecf20Sopenharmony_ci	ret = xenbus_printf(xbt, dev->nodename, XENKBD_FIELD_RING_GREF,
4578c2ecf20Sopenharmony_ci			    "%u", info->gref);
4588c2ecf20Sopenharmony_ci	if (ret)
4598c2ecf20Sopenharmony_ci		goto error_xenbus;
4608c2ecf20Sopenharmony_ci	ret = xenbus_printf(xbt, dev->nodename, XENKBD_FIELD_EVT_CHANNEL, "%u",
4618c2ecf20Sopenharmony_ci			    evtchn);
4628c2ecf20Sopenharmony_ci	if (ret)
4638c2ecf20Sopenharmony_ci		goto error_xenbus;
4648c2ecf20Sopenharmony_ci	ret = xenbus_transaction_end(xbt, 0);
4658c2ecf20Sopenharmony_ci	if (ret) {
4668c2ecf20Sopenharmony_ci		if (ret == -EAGAIN)
4678c2ecf20Sopenharmony_ci			goto again;
4688c2ecf20Sopenharmony_ci		xenbus_dev_fatal(dev, ret, "completing transaction");
4698c2ecf20Sopenharmony_ci		goto error_irqh;
4708c2ecf20Sopenharmony_ci	}
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	xenbus_switch_state(dev, XenbusStateInitialised);
4738c2ecf20Sopenharmony_ci	return 0;
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci error_xenbus:
4768c2ecf20Sopenharmony_ci	xenbus_transaction_end(xbt, 1);
4778c2ecf20Sopenharmony_ci	xenbus_dev_fatal(dev, ret, "writing xenstore");
4788c2ecf20Sopenharmony_ci error_irqh:
4798c2ecf20Sopenharmony_ci	unbind_from_irqhandler(info->irq, info);
4808c2ecf20Sopenharmony_ci	info->irq = -1;
4818c2ecf20Sopenharmony_ci error_evtchan:
4828c2ecf20Sopenharmony_ci	xenbus_free_evtchn(dev, evtchn);
4838c2ecf20Sopenharmony_ci error_grant:
4848c2ecf20Sopenharmony_ci	gnttab_end_foreign_access(info->gref, 0, 0UL);
4858c2ecf20Sopenharmony_ci	info->gref = -1;
4868c2ecf20Sopenharmony_ci	return ret;
4878c2ecf20Sopenharmony_ci}
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_cistatic void xenkbd_disconnect_backend(struct xenkbd_info *info)
4908c2ecf20Sopenharmony_ci{
4918c2ecf20Sopenharmony_ci	if (info->irq >= 0)
4928c2ecf20Sopenharmony_ci		unbind_from_irqhandler(info->irq, info);
4938c2ecf20Sopenharmony_ci	info->irq = -1;
4948c2ecf20Sopenharmony_ci	if (info->gref >= 0)
4958c2ecf20Sopenharmony_ci		gnttab_end_foreign_access(info->gref, 0, 0UL);
4968c2ecf20Sopenharmony_ci	info->gref = -1;
4978c2ecf20Sopenharmony_ci}
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_cistatic void xenkbd_backend_changed(struct xenbus_device *dev,
5008c2ecf20Sopenharmony_ci				   enum xenbus_state backend_state)
5018c2ecf20Sopenharmony_ci{
5028c2ecf20Sopenharmony_ci	switch (backend_state) {
5038c2ecf20Sopenharmony_ci	case XenbusStateInitialising:
5048c2ecf20Sopenharmony_ci	case XenbusStateInitialised:
5058c2ecf20Sopenharmony_ci	case XenbusStateReconfiguring:
5068c2ecf20Sopenharmony_ci	case XenbusStateReconfigured:
5078c2ecf20Sopenharmony_ci	case XenbusStateUnknown:
5088c2ecf20Sopenharmony_ci		break;
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci	case XenbusStateInitWait:
5118c2ecf20Sopenharmony_ci		xenbus_switch_state(dev, XenbusStateConnected);
5128c2ecf20Sopenharmony_ci		break;
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci	case XenbusStateConnected:
5158c2ecf20Sopenharmony_ci		/*
5168c2ecf20Sopenharmony_ci		 * Work around xenbus race condition: If backend goes
5178c2ecf20Sopenharmony_ci		 * through InitWait to Connected fast enough, we can
5188c2ecf20Sopenharmony_ci		 * get Connected twice here.
5198c2ecf20Sopenharmony_ci		 */
5208c2ecf20Sopenharmony_ci		if (dev->state != XenbusStateConnected)
5218c2ecf20Sopenharmony_ci			xenbus_switch_state(dev, XenbusStateConnected);
5228c2ecf20Sopenharmony_ci		break;
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci	case XenbusStateClosed:
5258c2ecf20Sopenharmony_ci		if (dev->state == XenbusStateClosed)
5268c2ecf20Sopenharmony_ci			break;
5278c2ecf20Sopenharmony_ci		fallthrough;	/* Missed the backend's CLOSING state */
5288c2ecf20Sopenharmony_ci	case XenbusStateClosing:
5298c2ecf20Sopenharmony_ci		xenbus_frontend_closed(dev);
5308c2ecf20Sopenharmony_ci		break;
5318c2ecf20Sopenharmony_ci	}
5328c2ecf20Sopenharmony_ci}
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_cistatic const struct xenbus_device_id xenkbd_ids[] = {
5358c2ecf20Sopenharmony_ci	{ XENKBD_DRIVER_NAME },
5368c2ecf20Sopenharmony_ci	{ "" }
5378c2ecf20Sopenharmony_ci};
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_cistatic struct xenbus_driver xenkbd_driver = {
5408c2ecf20Sopenharmony_ci	.ids = xenkbd_ids,
5418c2ecf20Sopenharmony_ci	.probe = xenkbd_probe,
5428c2ecf20Sopenharmony_ci	.remove = xenkbd_remove,
5438c2ecf20Sopenharmony_ci	.resume = xenkbd_resume,
5448c2ecf20Sopenharmony_ci	.otherend_changed = xenkbd_backend_changed,
5458c2ecf20Sopenharmony_ci};
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_cistatic int __init xenkbd_init(void)
5488c2ecf20Sopenharmony_ci{
5498c2ecf20Sopenharmony_ci	if (!xen_domain())
5508c2ecf20Sopenharmony_ci		return -ENODEV;
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci	/* Nothing to do if running in dom0. */
5538c2ecf20Sopenharmony_ci	if (xen_initial_domain())
5548c2ecf20Sopenharmony_ci		return -ENODEV;
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci	if (!xen_has_pv_devices())
5578c2ecf20Sopenharmony_ci		return -ENODEV;
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci	return xenbus_register_frontend(&xenkbd_driver);
5608c2ecf20Sopenharmony_ci}
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_cistatic void __exit xenkbd_cleanup(void)
5638c2ecf20Sopenharmony_ci{
5648c2ecf20Sopenharmony_ci	xenbus_unregister_driver(&xenkbd_driver);
5658c2ecf20Sopenharmony_ci}
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_cimodule_init(xenkbd_init);
5688c2ecf20Sopenharmony_cimodule_exit(xenkbd_cleanup);
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Xen virtual keyboard/pointer device frontend");
5718c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
5728c2ecf20Sopenharmony_ciMODULE_ALIAS("xen:" XENKBD_DRIVER_NAME);
573