From: Erik Arfvidson <erik.arfvidson@xxxxxxxxxx> This driver provides mouse and keyboard input to Unisys s-Par Partition Desktop application. This device is created by the visorbus device. Signed-off-by: Erik Arfvidson <erik.arfvidson@xxxxxxxxxx> Signed-off-by: Benjamin Romer <benjamin.romer@xxxxxxxxxx> --- drivers/staging/unisys/Kconfig | 1 + drivers/staging/unisys/Makefile | 1 + drivers/staging/unisys/visorhid/Kconfig | 10 + drivers/staging/unisys/visorhid/Makefile | 7 + drivers/staging/unisys/visorhid/keyboardchannel.h | 32 + drivers/staging/unisys/visorhid/mousechannel.h | 32 + drivers/staging/unisys/visorhid/ultrainputreport.h | 82 +++ drivers/staging/unisys/visorhid/visorhid.c | 798 +++++++++++++++++++++ 8 files changed, 963 insertions(+) create mode 100644 drivers/staging/unisys/visorhid/Kconfig create mode 100644 drivers/staging/unisys/visorhid/Makefile create mode 100644 drivers/staging/unisys/visorhid/keyboardchannel.h create mode 100644 drivers/staging/unisys/visorhid/mousechannel.h create mode 100644 drivers/staging/unisys/visorhid/ultrainputreport.h create mode 100644 drivers/staging/unisys/visorhid/visorhid.c diff --git a/drivers/staging/unisys/Kconfig b/drivers/staging/unisys/Kconfig index 778f9d0..bdc8ba8 100644 --- a/drivers/staging/unisys/Kconfig +++ b/drivers/staging/unisys/Kconfig @@ -13,5 +13,6 @@ if UNISYSSPAR source "drivers/staging/unisys/visorbus/Kconfig" source "drivers/staging/unisys/visornic/Kconfig" +source "drivers/staging/unisys/visorhid/Kconfig" endif # UNISYSSPAR diff --git a/drivers/staging/unisys/Makefile b/drivers/staging/unisys/Makefile index a515ebc..d071094 100644 --- a/drivers/staging/unisys/Makefile +++ b/drivers/staging/unisys/Makefile @@ -3,3 +3,4 @@ # obj-$(CONFIG_UNISYS_VISORBUS) += visorbus/ obj-$(CONFIG_UNISYS_VISORNIC) += visornic/ +obj-$(CONFIG_UNISYS_VISORHID) += visorhid/ diff --git a/drivers/staging/unisys/visorhid/Kconfig b/drivers/staging/unisys/visorhid/Kconfig new file mode 100644 index 0000000..3b83e2c --- /dev/null +++ b/drivers/staging/unisys/visorhid/Kconfig @@ -0,0 +1,10 @@ +# +# Unisys visorhid configuration +# + +config UNISYS_VISORHID + tristate "Unisys visorhid driver" + depends on UNISYSSPAR && UNISYS_VISORBUS && FB + ---help--- + If you say Y here, you will enable the Unisys visorhid driver. + diff --git a/drivers/staging/unisys/visorhid/Makefile b/drivers/staging/unisys/visorhid/Makefile new file mode 100644 index 0000000..e457bd1 --- /dev/null +++ b/drivers/staging/unisys/visorhid/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for Unisys visorhid +# + +obj-$(CONFIG_UNISYS_VISORHID) += visorhid.o + +ccflags-y += -Idrivers/staging/unisys/include diff --git a/drivers/staging/unisys/visorhid/keyboardchannel.h b/drivers/staging/unisys/visorhid/keyboardchannel.h new file mode 100644 index 0000000..0722c60 --- /dev/null +++ b/drivers/staging/unisys/visorhid/keyboardchannel.h @@ -0,0 +1,32 @@ +/* Copyright (C) 2010 - 2013 UNISYS CORPORATION + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + */ + +#ifndef __KEYBOARDCHANNEL_H__ +#define __KEYBOARDCHANNEL_H__ + +#include <linux/kernel.h> +#include <linux/uuid.h> + +#include "channel.h" +#include "ultrainputreport.h" + +/* {C73416D0-B0B8-44af-B304-9D2AE99F1B3D} */ +#define SPAR_KEYBOARD_CHANNEL_PROTOCOL_UUID \ + UUID_LE(0xc73416d0, 0xb0b8, 0x44af, \ + 0xb3, 0x4, 0x9d, 0x2a, 0xe9, 0x9f, 0x1b, 0x3d) +#define SPAR_KEYBOARD_CHANNEL_PROTOCOL_VERSIONID 1 +#define KEYBOARD_MAXINPUTREPORTS 50 + +#endif diff --git a/drivers/staging/unisys/visorhid/mousechannel.h b/drivers/staging/unisys/visorhid/mousechannel.h new file mode 100644 index 0000000..ac6883b --- /dev/null +++ b/drivers/staging/unisys/visorhid/mousechannel.h @@ -0,0 +1,32 @@ +/* Copyright (C) 2010 - 2013 UNISYS CORPORATION + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + */ + +#ifndef __MOUSECHANNEL_H__ +#define __MOUSECHANNEL_H__ + +#include <linux/kernel.h> +#include <linux/uuid.h> + +#include "channel.h" +#include "ultrainputreport.h" + +/* {ADDF07D4-94A9-46e2-81C3-61ABCDBDBD87} */ +#define SPAR_MOUSE_CHANNEL_PROTOCOL_UUID \ + UUID_LE(0xaddf07d4, 0x94a9, 0x46e2, \ + 0x81, 0xc3, 0x61, 0xab, 0xcd, 0xbd, 0xbd, 0x87) +#define SPAR_MOUSE_CHANNEL_PROTOCOL_VERSIONID 1 +#define MOUSE_MAXINPUTREPORTS 50 + +#endif diff --git a/drivers/staging/unisys/visorhid/ultrainputreport.h b/drivers/staging/unisys/visorhid/ultrainputreport.h new file mode 100644 index 0000000..4cd75cc --- /dev/null +++ b/drivers/staging/unisys/visorhid/ultrainputreport.h @@ -0,0 +1,82 @@ +/* Copyright (C) 2010 - 2013 UNISYS CORPORATION + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + */ + +#ifndef __ULTRAINPUTREPORT_H__ +#define __ULTRAINPUTREPORT_H__ + +#include <linux/types.h> + +#include "ultrainputreport.h" + +/* Identifies mouse and keyboard activity which is specified by the firmware to + * the host using the cmsimpleinput protocol. @ingroup coretypes + */ +enum ultra_inputaction { + inputaction_none = 0, + inputaction_xy_motion = 1, /*< only motion; arg1=x, arg2=y */ + inputaction_mouse_button_down = 2, /*< arg1: 1=left, 2=center, 3=right, + 4, * 5 */ + inputaction_mouse_button_up = 3, /*< arg1: 1=left, 2=center, 3=right, + 4,* 5 */ + inputaction_mouse_button_click = 4, /*< arg1: 1=left, 2=center, 3=right, + * 4, 5 */ + inputaction_mouse_button_dclick = 5, /*< arg1: 1=left, 2=center, + 3=right, * 4, 5 */ + inputaction_wheel_rotate_away = 6, /*< arg1: wheel rotation away from + * user */ + inputaction_wheel_rotate_toward = 7, /*< arg1: wheel rotation toward + * user */ + inputaction_set_max_xy = 8, /*< set screen maxXY; arg1=x, arg2=y */ + inputaction_key_down = 64, /*< arg1: scancode, as follows: + * If arg1 <= 0xff, it's a 1-byte + * scancode and arg1 is that scancode. + * If arg1 > 0xff, it's a 2-byte + * scanecode, with the 1st byte in the + * low 8 bits, and the 2nd byte in the + * high 8 bits. E.g., the right ALT key + * would appear as x'38e0'. + */ + inputaction_key_up = 65, /*< arg1: scancode (in same format as + * inputaction_keyDown) + */ + inputaction_set_locking_key_state = 66, + /*< arg1: scancode (in same format + * as inputaction_keyDown); + * MUST refer to one of the + * locking keys, like capslock, + * numlock, or scrolllock + * arg2: 1 iff locking key should be + * in the LOCKED position + * (e.g., light is ON) + */ + inputaction_key_down_up = 67, /*< arg1: scancode (in same format + * as inputaction_keyDown) + */ + inputaction_last +}; + +struct ultra_inputactivity { + u16 action; + u16 arg1; + u16 arg2; + u16 arg3; +} __packed; + +struct ultra_inputreport { + u64 seq_no; + struct ultra_inputactivity activity; +} __packed; + +#endif diff --git a/drivers/staging/unisys/visorhid/visorhid.c b/drivers/staging/unisys/visorhid/visorhid.c new file mode 100644 index 0000000..7384bef --- /dev/null +++ b/drivers/staging/unisys/visorhid/visorhid.c @@ -0,0 +1,798 @@ +/* visorhid.c + * + * Copyright (C) 2011 - 2014 UNISYS CORPORATION + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + */ + +/* This driver lives in a generic guest Linux partition, and registers to + * receive keyboard and mouse channels from the visorbus driver. It reads + * inputs from such channels, and delivers it to the Linux OS in the + * standard way the Linux expects for input drivers. + */ + +#include <linux/buffer_head.h> +#include <linux/fb.h> +#include <linux/fs.h> +#include <linux/input.h> +#include <linux/serio.h> +#include <linux/uaccess.h> + +#include <asm/segment.h> + +#include "keyboardchannel.h" +#include "mousechannel.h" +#include "version.h" +#include "visorbus.h" + +#define MAXDEVICES 16384 +#define PIXELS_ACROSS_DEFAULT 800 +#define PIXELS_DOWN_DEFAULT 600 +#define DEV_NAME_SIZE 99 +#define SYSFS_VIRTUALSIZE "/sys/class/graphics/fb0/virtual_size" + +static spinlock_t devnopool_lock; +static void *dev_no_pool; /* < pool to grab device numbers from */ +static const uuid_le spar_keyboard_channel_protocol_uuid = + SPAR_KEYBOARD_CHANNEL_PROTOCOL_UUID; +static const uuid_le spar_mouse_channel_protocol_uuid = + SPAR_MOUSE_CHANNEL_PROTOCOL_UUID; +static int visorhid_probe(struct visor_device *dev); +static void visorhid_remove(struct visor_device *dev); +static void visorhid_channel_interrupt(struct visor_device *dev); +static int visorhid_pause(struct visor_device *dev, + visorbus_state_complete_func complete_func); +static int visorhid_resume(struct visor_device *dev, + visorbus_state_complete_func complete_func); +static struct input_dev *register_client_keyboard(void); +static struct input_dev *register_client_mouse(void); +static struct input_dev *register_client_wheel(void); +static void unregister_client_input(struct input_dev *visorinput_dev); + +/* GUIDS for all channel types supported by this driver. */ +static struct visor_channeltype_descriptor visorhid_channel_types[] = { + { + .guid = SPAR_KEYBOARD_CHANNEL_PROTOCOL_UUID, + .name = "keyboard" + }, + { + .guid = SPAR_MOUSE_CHANNEL_PROTOCOL_UUID, + .name = "mouse" + }, + { + .guid = NULL_UUID_LE, + .name = NULL + } +}; + +/** This is used to tell the visor bus driver which types of visor devices + * we support, and what functions to call when a visor device that we support + * is attached or removed. + */ +static struct visor_driver visorhid_driver = { + .name = "visorhid", + .version = VERSION, + .vertag = NULL, + .owner = THIS_MODULE, + .channel_types = visorhid_channel_types, + .probe = visorhid_probe, + .remove = visorhid_remove, + .channel_interrupt = visorhid_channel_interrupt, + .pause = visorhid_pause, + .resume = visorhid_resume, +}; + +/* This is the private data that we store for each device. + * A pointer to this struct is kept in each "struct device" + */ +struct visorhid_devdata { + int devno; + struct visor_device *dev; + /** lock for dev */ + struct rw_semaphore lock_visor_dev; + char name[DEV_NAME_SIZE]; + struct list_head list_all; /* < link within list_all_devices list */ + struct kref kref; + struct input_dev *visorinput_dev; + struct input_dev *visorinput_dev2; + bool supported_client_device; + bool paused; +}; + + /* List of all visorhid_devdata structs, + * linked via the list_all member + */ +static LIST_HEAD(list_all_devices); +static DEFINE_SPINLOCK(lock_all_devices); + +/* Borrowed from drivers/input/keyboard/atakbd.c */ +/* This maps 1-byte scancodes to keycodes. */ +static unsigned char visorkbd_keycode[256] = { /* American layout */ + [0] = KEY_GRAVE, + [1] = KEY_ESC, + [2] = KEY_1, + [3] = KEY_2, + [4] = KEY_3, + [5] = KEY_4, + [6] = KEY_5, + [7] = KEY_6, + [8] = KEY_7, + [9] = KEY_8, + [10] = KEY_9, + [11] = KEY_0, + [12] = KEY_MINUS, + [13] = KEY_EQUAL, + [14] = KEY_BACKSPACE, + [15] = KEY_TAB, + [16] = KEY_Q, + [17] = KEY_W, + [18] = KEY_E, + [19] = KEY_R, + [20] = KEY_T, + [21] = KEY_Y, + [22] = KEY_U, + [23] = KEY_I, + [24] = KEY_O, + [25] = KEY_P, + [26] = KEY_LEFTBRACE, + [27] = KEY_RIGHTBRACE, + [28] = KEY_ENTER, + [29] = KEY_LEFTCTRL, + [30] = KEY_A, + [31] = KEY_S, + [32] = KEY_D, + [33] = KEY_F, + [34] = KEY_G, + [35] = KEY_H, + [36] = KEY_J, + [37] = KEY_K, + [38] = KEY_L, + [39] = KEY_SEMICOLON, + [40] = KEY_APOSTROPHE, + [41] = KEY_GRAVE, /* FIXME, '#' */ + [42] = KEY_LEFTSHIFT, + [43] = KEY_BACKSLASH, /* FIXME, '~' */ + [44] = KEY_Z, + [45] = KEY_X, + [46] = KEY_C, + [47] = KEY_V, + [48] = KEY_B, + [49] = KEY_N, + [50] = KEY_M, + [51] = KEY_COMMA, + [52] = KEY_DOT, + [53] = KEY_SLASH, + [54] = KEY_RIGHTSHIFT, + [55] = KEY_KPASTERISK, + [56] = KEY_LEFTALT, + [57] = KEY_SPACE, + [58] = KEY_CAPSLOCK, + [59] = KEY_F1, + [60] = KEY_F2, + [61] = KEY_F3, + [62] = KEY_F4, + [63] = KEY_F5, + [64] = KEY_F6, + [65] = KEY_F7, + [66] = KEY_F8, + [67] = KEY_F9, + [68] = KEY_F10, + [69] = KEY_NUMLOCK, + [70] = KEY_SCROLLLOCK, + [71] = KEY_KP7, + [72] = KEY_KP8, + [73] = KEY_KP9, + [74] = KEY_KPMINUS, + [75] = KEY_KP4, + [76] = KEY_KP5, + [77] = KEY_KP6, + [78] = KEY_KPPLUS, + [79] = KEY_KP1, + [80] = KEY_KP2, + [81] = KEY_KP3, + [82] = KEY_KP0, + [83] = KEY_KPDOT, + [87] = KEY_F11, + [88] = KEY_F12, + [90] = KEY_KPLEFTPAREN, + [91] = KEY_KPRIGHTPAREN, + [92] = KEY_KPASTERISK, /* FIXME */ + [93] = KEY_KPASTERISK, + [94] = KEY_KPPLUS, + [95] = KEY_HELP, + [96] = KEY_KPENTER, + [97] = KEY_RIGHTCTRL, + [98] = KEY_KPSLASH, + [99] = KEY_KPLEFTPAREN, + [100] = KEY_KPRIGHTPAREN, + [101] = KEY_KPSLASH, + [102] = KEY_HOME, + [103] = KEY_UP, + [104] = KEY_PAGEUP, + [105] = KEY_LEFT, + [106] = KEY_RIGHT, + [107] = KEY_END, + [108] = KEY_DOWN, + [109] = KEY_PAGEDOWN, + [110] = KEY_INSERT, + [111] = KEY_DELETE, + [112] = KEY_MACRO, + [113] = KEY_MUTE +}; + +/* This maps the <xx> in extended scancodes of the form "0xE0 <xx>" into + * keycodes. + */ +static unsigned char ext_keycode[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 */ + 0, 0, 0, 0, KEY_KPENTER, KEY_RIGHTCTRL, 0, 0, /* 0x18 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20 */ + KEY_RIGHTALT, 0, 0, 0, 0, 0, 0, 0, /* 0x28 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x30 */ + 0, 0, 0, 0, 0, 0, 0, KEY_HOME, /* 0x40 */ + KEY_UP, KEY_PAGEUP, 0, KEY_LEFT, 0, KEY_RIGHT, 0, KEY_END, /* 0x48 */ + KEY_DOWN, KEY_PAGEDOWN, KEY_INSERT, KEY_DELETE, 0, 0, 0, 0, /* 0x50 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x58 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x70 */ +}; + +static struct visorhid_devdata * +devdata_create(struct visor_device *dev) +{ + struct visorhid_devdata *devdata = NULL; + int devno = -1; + uuid_le guid; + + guid = visorchannel_get_uuid(dev->visorchannel); + devdata = kzalloc(sizeof(*devdata), + GFP_KERNEL | __GFP_NORETRY); + if (!devdata) + goto cleanups; + + spin_lock(&devnopool_lock); + devno = find_first_zero_bit(dev_no_pool, MAXDEVICES); + spin_unlock(&devnopool_lock); + if (devno == MAXDEVICES) + goto cleanups_set_bit; + set_bit(devno, dev_no_pool); + devdata->devno = devno; + devdata->dev = dev; + strncpy(devdata->name, dev_name(&dev->device), sizeof(devdata->name)); + + /* This is an input device in a client guest partition, + * so we need to create whatever gizmos are necessary to + * deliver our inputs to the guest OS. + */ + if (!uuid_le_cmp(guid, spar_keyboard_channel_protocol_uuid)) { + devdata->visorinput_dev = register_client_keyboard(); + if (!devdata->visorinput_dev) + goto cleanups_register; + devdata->supported_client_device = true; + } else if (!uuid_le_cmp(guid, spar_mouse_channel_protocol_uuid)) { + devdata->visorinput_dev = register_client_mouse(); + if (!devdata->visorinput_dev) + goto cleanups_register2; + devdata->visorinput_dev2 = register_client_wheel(); + if (!devdata->visorinput_dev2) + goto cleanups_register2; + devdata->supported_client_device = true; + } + + init_rwsem(&devdata->lock_visor_dev); + kref_init(&devdata->kref); + + spin_lock(&lock_all_devices); + list_add_tail(&devdata->list_all, &list_all_devices); + spin_unlock(&lock_all_devices); + return devdata; + +cleanups_register2: + unregister_client_input(devdata->visorinput_dev); + devdata->visorinput_dev = NULL; +cleanups_register: + clear_bit(devno, dev_no_pool); +cleanups_set_bit: + kfree(devdata); +cleanups: + return NULL; +} + +static void +devdata_release(struct kref *mykref) +{ + struct visorhid_devdata *devdata = + container_of(mykref, struct visorhid_devdata, kref); + clear_bit(devdata->devno, dev_no_pool); + spin_lock(&lock_all_devices); + list_del(&devdata->list_all); + spin_unlock(&lock_all_devices); +} + +static int +visorhid_probe(struct visor_device *dev) +{ + int rc = 0; + struct visorhid_devdata *devdata = NULL; + uuid_le guid; + + devdata = devdata_create(dev); + if (!devdata) { + rc = -1; + goto cleanups; + } + dev_set_drvdata(&dev->device, devdata); + guid = visorchannel_get_uuid(dev->visorchannel); + if (uuid_le_cmp(guid, spar_mouse_channel_protocol_uuid) && + uuid_le_cmp(guid, spar_keyboard_channel_protocol_uuid)) { + rc = -1; + goto cleanups; + } + + if (devdata->supported_client_device) + visorbus_enable_channel_interrupts(dev); + +cleanups: + if (rc < 0) { + if (devdata) + kref_put(&devdata->kref, devdata_release); + } + return rc; +} + +static void +host_side_disappeared(struct visorhid_devdata *devdata) +{ + down_write(&devdata->lock_visor_dev); + sprintf(devdata->name, "<dev#%d-history>", devdata->devno); + devdata->dev = NULL; /* indicate device destroyed */ + up_write(&devdata->lock_visor_dev); +} + +static void +visorhid_remove(struct visor_device *dev) +{ + struct visorhid_devdata *devdata = dev_get_drvdata(&dev->device); + + if (!devdata) + return; + + dev_set_drvdata(&dev->device, NULL); + host_side_disappeared(devdata); + unregister_client_input(devdata->visorinput_dev); + devdata->visorinput_dev = NULL; + unregister_client_input(devdata->visorinput_dev2); + devdata->visorinput_dev2 = NULL; + kref_put(&devdata->kref, devdata_release); +} + +static void +visorhid_cleanup_guts(void) +{ + visorbus_unregister_visor_driver(&visorhid_driver); + kfree(dev_no_pool); + dev_no_pool = NULL; +} + +static void +unregister_client_input(struct input_dev *visorinput_dev) +{ + if (visorinput_dev) + input_unregister_device(visorinput_dev); +} + +/* register_client_keyboard() initializes and returns a Linux gizmo that we + * can use to deliver keyboard inputs to Linux. We of course do this when + * we see keyboard inputs coming in on a keyboard channel. + */ +static struct input_dev * +register_client_keyboard(void) +{ + int i, error; + struct input_dev *visorinput_dev = NULL; + + visorinput_dev = input_allocate_device(); + if (!visorinput_dev) + return NULL; + + visorinput_dev->name = "visor Keyboard"; + visorinput_dev->phys = "visorkbd/input0"; + visorinput_dev->id.bustype = BUS_HOST; + visorinput_dev->id.vendor = 0x0001; + visorinput_dev->id.product = 0x0001; + visorinput_dev->id.version = 0x0100; + + visorinput_dev->evbit[0] = BIT_MASK(EV_KEY) | + BIT_MASK(EV_REP) | + BIT_MASK(EV_LED); + visorinput_dev->ledbit[0] = BIT_MASK(LED_CAPSL) | + BIT_MASK(LED_SCROLLL) | + BIT_MASK(LED_NUML); + visorinput_dev->keycode = visorkbd_keycode; + visorinput_dev->keycodesize = sizeof(unsigned char); + visorinput_dev->keycodemax = ARRAY_SIZE(visorkbd_keycode); + + for (i = 1; i < ARRAY_SIZE(visorkbd_keycode); i++) + set_bit(visorkbd_keycode[i], visorinput_dev->keybit); + + error = input_register_device(visorinput_dev); + if (error) { + input_free_device(visorinput_dev); + return NULL; + } + return visorinput_dev; +} + +static struct input_dev * +register_client_mouse(void) +{ + int error; + struct input_dev *visorinput_dev = NULL; + int xres, yres; + struct fb_info *fb0; + + visorinput_dev = input_allocate_device(); + if (!visorinput_dev) + return NULL; + + visorinput_dev->name = "visor Mouse"; + visorinput_dev->phys = "visormou/input0"; + visorinput_dev->id.bustype = BUS_HOST; + visorinput_dev->id.vendor = 0x0001; + visorinput_dev->id.product = 0x0001; + visorinput_dev->id.version = 0x0100; + + visorinput_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + set_bit(BTN_LEFT, visorinput_dev->keybit); + set_bit(BTN_RIGHT, visorinput_dev->keybit); + set_bit(BTN_MIDDLE, visorinput_dev->keybit); + + if (registered_fb[0]) { + fb0 = registered_fb[0]; + xres = fb0->var.xres_virtual; + yres = fb0->var.yres_virtual; + } else { + xres = PIXELS_ACROSS_DEFAULT; + yres = PIXELS_DOWN_DEFAULT; + } + input_set_abs_params(visorinput_dev, ABS_X, 0, xres, 0, 0); + input_set_abs_params(visorinput_dev, ABS_Y, 0, yres, 0, 0); + + error = input_register_device(visorinput_dev); + if (error) { + input_free_device(visorinput_dev); + return NULL; + } + + /* Sending top-left and bottom-right positions is ABSOLUTELY + * REQUIRED if we want X to move the mouse to the exact points + * we tell it. I have NO IDEA why. + */ + input_report_abs(visorinput_dev, ABS_X, 0); + input_report_abs(visorinput_dev, ABS_Y, 0); + input_sync(visorinput_dev); + input_report_abs(visorinput_dev, ABS_X, xres - 1); + input_report_abs(visorinput_dev, ABS_Y, yres - 1); + input_sync(visorinput_dev); + + return visorinput_dev; +} + +static struct input_dev * +register_client_wheel(void) +{ + int error; + struct input_dev *visorinput_dev = NULL; + + visorinput_dev = input_allocate_device(); + if (!visorinput_dev) + return NULL; + + visorinput_dev->name = "visor Wheel"; + visorinput_dev->phys = "visorwhl/input0"; + visorinput_dev->id.bustype = BUS_HOST; + visorinput_dev->id.vendor = 0x0001; + visorinput_dev->id.product = 0x0001; + visorinput_dev->id.version = 0x0100; + + /* We need to lie a little to prevent the evdev driver "Don't + * know how to use device" error. (evdev erroneously thinks + * that a device without an X and Y axis is useless.) + */ + visorinput_dev->evbit[0] = BIT_MASK(EV_REL) | + /*lie */ BIT_MASK(EV_KEY); + visorinput_dev->relbit[0] = BIT_MASK(REL_WHEEL) | + /*lie */ BIT_MASK(REL_X) | + /*lie */ BIT_MASK(REL_Y); + set_bit(BTN_LEFT, visorinput_dev->keybit); /*lie */ + set_bit(BTN_RIGHT, visorinput_dev->keybit); /*lie */ + set_bit(BTN_MIDDLE, visorinput_dev->keybit); /*lie */ + + error = input_register_device(visorinput_dev); + if (error) { + input_free_device(visorinput_dev); + return NULL; + } + return visorinput_dev; +} + +static void +do_key(struct input_dev *inpt, int keycode, int down) +{ + input_report_key(inpt, keycode, down); +} + +/* Make it so the current locking state of the locking key indicated by + * <keycode> is as indicated by <desired_state> (1=locked, 0=unlocked). + */ +static void +handle_locking_key(struct input_dev *visorinput_dev, + int keycode, int desired_state) +{ + int led; + char *sled; + + switch (keycode) { + case KEY_CAPSLOCK: + led = LED_CAPSL; + sled = "CAP"; + break; + case KEY_SCROLLLOCK: + led = LED_SCROLLL; + sled = "SCR"; + break; + case KEY_NUMLOCK: + led = LED_NUML; + sled = "NUM"; + break; + default: + led = -1; + break; + } + if (led >= 0) { + int old_state = (test_bit(led, visorinput_dev->led) != 0); + + if (old_state != desired_state) { + do_key(visorinput_dev, keycode, 1); + do_key(visorinput_dev, keycode, 0); + input_sync(visorinput_dev); + __change_bit(led, visorinput_dev->led); + } + } +} + +/* <scancode> is either a 1-byte scancode, or an extended 16-bit scancode + * with 0xE0 in the low byte and the extended scancode value in the next + * higher byte. + */ +static int +scancode_to_keycode(int scancode) +{ + int keycode; + + if (scancode > 0xff) + keycode = ext_keycode[(scancode >> 8) & 0xff]; + else + keycode = visorkbd_keycode[scancode]; + return keycode; +} + +static int +calc_button(int x) +{ + switch (x) { + case 1: + return BTN_LEFT; + case 2: + return BTN_MIDDLE; + case 3: + return BTN_RIGHT; + default: + return -1; + } +} + +/* This is used only when this driver is active as an input driver in the + * client guest partition. It is called periodically so we can obtain inputs + * from the channel, and deliver them to the guest OS. + */ +static void +visorhid_channel_interrupt(struct visor_device *dev) +{ + struct ultra_inputreport r; + int scancode, keycode; + struct input_dev *visorinput_dev; + struct input_dev *visorinput_dev2; + int xmotion, ymotion, zmotion, button; + int i; + + struct visorhid_devdata *devdata = dev_get_drvdata(&dev->device); + + if (!devdata) + return; + + down_write(&devdata->lock_visor_dev); + if (devdata->paused) /* don't touch device/channel when paused */ + goto out_locked; + + visorinput_dev = devdata->visorinput_dev; + if (!visorinput_dev) + goto out_locked; + + visorinput_dev2 = devdata->visorinput_dev2; + while (visorchannel_signalremove(dev->visorchannel, 0, &r)) { + scancode = r.activity.arg1; + keycode = scancode_to_keycode(scancode); + switch (r.activity.action) { + case inputaction_key_down: + do_key(visorinput_dev, keycode, 1); + input_sync(visorinput_dev); + break; + case inputaction_key_up: + do_key(visorinput_dev, keycode, 0); + input_sync(visorinput_dev); + break; + case inputaction_key_down_up: + do_key(visorinput_dev, keycode, 1); + do_key(visorinput_dev, keycode, 0); + input_sync(visorinput_dev); + break; + case inputaction_set_locking_key_state: + handle_locking_key(visorinput_dev, keycode, + r.activity.arg2); + break; + case inputaction_xy_motion: + xmotion = r.activity.arg1; + ymotion = r.activity.arg2; + input_report_abs(visorinput_dev, ABS_X, xmotion); + input_report_abs(visorinput_dev, ABS_Y, ymotion); + input_sync(visorinput_dev); + break; + case inputaction_mouse_button_down: + button = calc_button(r.activity.arg1); + if (button < 0) + break; + input_report_key(visorinput_dev, button, 1); + input_sync(visorinput_dev); + break; + case inputaction_mouse_button_up: + button = calc_button(r.activity.arg1); + if (button < 0) + break; + input_report_key(visorinput_dev, button, 0); + input_sync(visorinput_dev); + break; + case inputaction_mouse_button_click: + button = calc_button(r.activity.arg1); + if (button < 0) + break; + input_report_key(visorinput_dev, button, 1); + + input_sync(visorinput_dev); + input_report_key(visorinput_dev, button, 0); + input_sync(visorinput_dev); + break; + case inputaction_mouse_button_dclick: + button = calc_button(r.activity.arg1); + if (button < 0) + break; + for (i = 0; i < 2; i++) { + input_report_key(visorinput_dev, button, 1); + input_sync(visorinput_dev); + input_report_key(visorinput_dev, button, 0); + input_sync(visorinput_dev); + } + break; + case inputaction_wheel_rotate_away: + if (!visorinput_dev2) + goto out_locked; + zmotion = r.activity.arg1; + input_report_rel(visorinput_dev2, REL_WHEEL, 1); + input_sync(visorinput_dev2); + break; + case inputaction_wheel_rotate_toward: + if (!visorinput_dev2) + goto out_locked; + zmotion = r.activity.arg1; + input_report_rel(visorinput_dev2, REL_WHEEL, -1); + input_sync(visorinput_dev2); + break; + } + } +out_locked: + up_write(&devdata->lock_visor_dev); +} + +static int +visorhid_pause(struct visor_device *dev, + visorbus_state_complete_func complete_func) +{ + int rc; + struct visorhid_devdata *devdata = dev_get_drvdata(&dev->device); + + if (!devdata) { + rc = -ENODEV; + goto out; + } + + down_write(&devdata->lock_visor_dev); + if (devdata->paused) { + rc = -EBUSY; + goto out_locked; + } + devdata->paused = true; + complete_func(dev, 0); + rc = 0; +out_locked: + up_write(&devdata->lock_visor_dev); +out: + return rc; +} + +static int +visorhid_resume(struct visor_device *dev, + visorbus_state_complete_func complete_func) +{ + int rc; + struct visorhid_devdata *devdata = dev_get_drvdata(&dev->device); + + if (!devdata) { + rc = -ENODEV; + goto out; + } + down_write(&devdata->lock_visor_dev); + if (!devdata->paused) { + rc = -EBUSY; + goto out_locked; + } + devdata->paused = false; + complete_func(dev, 0); + rc = 0; +out_locked: + up_write(&devdata->lock_visor_dev); +out: + return rc; +} + +static int +visorhid_init(void) +{ + int rc = 0; + + spin_lock_init(&devnopool_lock); + dev_no_pool = kzalloc(BITS_TO_LONGS(MAXDEVICES), GFP_KERNEL); + if (!dev_no_pool) { + rc = -ENOMEM; + goto cleanups; + } + visorbus_register_visor_driver(&visorhid_driver); +cleanups: + if (rc < 0) + visorhid_cleanup_guts(); + return rc; +} + +static void +visorhid_cleanup(void) +{ + visorhid_cleanup_guts(); +} + +module_init(visorhid_init); +module_exit(visorhid_cleanup); + +MODULE_AUTHOR("Unisys"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("sPAR human input driver for guest Linux: ver " VERSION); +MODULE_VERSION(VERSION); -- 2.1.4 _______________________________________________ devel mailing list devel@xxxxxxxxxxxxxxxxxxxxxx http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel