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