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