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