aboutsummaryrefslogtreecommitdiff
path: root/drivers/input
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/input')
-rw-r--r--drivers/input/Kconfig9
-rw-r--r--drivers/input/Makefile2
-rw-r--r--drivers/input/keyboard/Kconfig9
-rw-r--r--drivers/input/keyboard/Makefile1
-rw-r--r--drivers/input/keyboard/corgikbd.c1
-rw-r--r--drivers/input/keyboard/sh_keysc.c280
-rw-r--r--drivers/input/keyboard/spitzkbd.c1
-rw-r--r--drivers/input/misc/Kconfig1
-rw-r--r--drivers/input/serio/hp_sdc_mlc.c2
-rw-r--r--drivers/input/touchscreen/ads7846.c40
-rw-r--r--drivers/input/touchscreen/corgi_ts.c1
-rw-r--r--drivers/input/xen-kbdfront.c340
12 files changed, 646 insertions, 41 deletions
diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig
index 9dea14db724..5f9d860925a 100644
--- a/drivers/input/Kconfig
+++ b/drivers/input/Kconfig
@@ -149,6 +149,15 @@ config INPUT_APMPOWER
To compile this driver as a module, choose M here: the
module will be called apm-power.
+config XEN_KBDDEV_FRONTEND
+ tristate "Xen virtual keyboard and mouse support"
+ depends on XEN_FBDEV_FRONTEND
+ default y
+ help
+ This driver implements the front-end of the Xen virtual
+ keyboard and mouse device driver. It communicates with a back-end
+ in another domain.
+
comment "Input Device Drivers"
source "drivers/input/keyboard/Kconfig"
diff --git a/drivers/input/Makefile b/drivers/input/Makefile
index 2ae87b19caa..98c4f9a7787 100644
--- a/drivers/input/Makefile
+++ b/drivers/input/Makefile
@@ -23,3 +23,5 @@ obj-$(CONFIG_INPUT_TOUCHSCREEN) += touchscreen/
obj-$(CONFIG_INPUT_MISC) += misc/
obj-$(CONFIG_INPUT_APMPOWER) += apm-power.o
+
+obj-$(CONFIG_XEN_KBDDEV_FRONTEND) += xen-kbdfront.o
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index 8ea709be330..efd70a97459 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -314,4 +314,13 @@ config KEYBOARD_BFIN
To compile this driver as a module, choose M here: the
module will be called bf54x-keys.
+config KEYBOARD_SH_KEYSC
+ tristate "SuperH KEYSC keypad support"
+ depends on SUPERH
+ help
+ Say Y here if you want to use a keypad attached to the KEYSC block
+ on SuperH processors such as sh7722 and sh7343.
+
+ To compile this driver as a module, choose M here: the
+ module will be called sh_keysc.
endif
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
index e741f403101..0edc8f285d1 100644
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -26,3 +26,4 @@ obj-$(CONFIG_KEYBOARD_HP6XX) += jornada680_kbd.o
obj-$(CONFIG_KEYBOARD_HP7XX) += jornada720_kbd.o
obj-$(CONFIG_KEYBOARD_MAPLE) += maple_keyb.o
obj-$(CONFIG_KEYBOARD_BFIN) += bf54x-keys.o
+obj-$(CONFIG_KEYBOARD_SH_KEYSC) += sh_keysc.o
diff --git a/drivers/input/keyboard/corgikbd.c b/drivers/input/keyboard/corgikbd.c
index 5187c0c7a22..29fbec6218b 100644
--- a/drivers/input/keyboard/corgikbd.c
+++ b/drivers/input/keyboard/corgikbd.c
@@ -23,6 +23,7 @@
#include <asm/arch/corgi.h>
#include <asm/arch/hardware.h>
#include <asm/arch/pxa-regs.h>
+#include <asm/arch/pxa2xx-gpio.h>
#include <asm/hardware/scoop.h>
#define KB_ROWS 8
diff --git a/drivers/input/keyboard/sh_keysc.c b/drivers/input/keyboard/sh_keysc.c
new file mode 100644
index 00000000000..8486abc457e
--- /dev/null
+++ b/drivers/input/keyboard/sh_keysc.c
@@ -0,0 +1,280 @@
+/*
+ * SuperH KEYSC Keypad Driver
+ *
+ * Copyright (C) 2008 Magnus Damm
+ *
+ * Based on gpio_keys.c, Copyright 2005 Phil Blundell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/io.h>
+#include <asm/sh_keysc.h>
+
+#define KYCR1_OFFS 0x00
+#define KYCR2_OFFS 0x04
+#define KYINDR_OFFS 0x08
+#define KYOUTDR_OFFS 0x0c
+
+#define KYCR2_IRQ_LEVEL 0x10
+#define KYCR2_IRQ_DISABLED 0x00
+
+static const struct {
+ unsigned char kymd, keyout, keyin;
+} sh_keysc_mode[] = {
+ [SH_KEYSC_MODE_1] = { 0, 6, 5 },
+ [SH_KEYSC_MODE_2] = { 1, 5, 6 },
+ [SH_KEYSC_MODE_3] = { 2, 4, 7 },
+};
+
+struct sh_keysc_priv {
+ void __iomem *iomem_base;
+ unsigned long last_keys;
+ struct input_dev *input;
+ struct sh_keysc_info pdata;
+};
+
+static irqreturn_t sh_keysc_isr(int irq, void *dev_id)
+{
+ struct platform_device *pdev = dev_id;
+ struct sh_keysc_priv *priv = platform_get_drvdata(pdev);
+ struct sh_keysc_info *pdata = &priv->pdata;
+ unsigned long keys, keys1, keys0, mask;
+ unsigned char keyin_set, tmp;
+ int i, k;
+
+ dev_dbg(&pdev->dev, "isr!\n");
+
+ keys1 = ~0;
+ keys0 = 0;
+
+ do {
+ keys = 0;
+ keyin_set = 0;
+
+ iowrite16(KYCR2_IRQ_DISABLED, priv->iomem_base + KYCR2_OFFS);
+
+ for (i = 0; i < sh_keysc_mode[pdata->mode].keyout; i++) {
+ iowrite16(0xfff ^ (3 << (i * 2)),
+ priv->iomem_base + KYOUTDR_OFFS);
+ udelay(pdata->delay);
+ tmp = ioread16(priv->iomem_base + KYINDR_OFFS);
+ keys |= tmp << (sh_keysc_mode[pdata->mode].keyin * i);
+ tmp ^= (1 << sh_keysc_mode[pdata->mode].keyin) - 1;
+ keyin_set |= tmp;
+ }
+
+ iowrite16(0, priv->iomem_base + KYOUTDR_OFFS);
+ iowrite16(KYCR2_IRQ_LEVEL | (keyin_set << 8),
+ priv->iomem_base + KYCR2_OFFS);
+
+ keys ^= ~0;
+ keys &= (1 << (sh_keysc_mode[pdata->mode].keyin *
+ sh_keysc_mode[pdata->mode].keyout)) - 1;
+ keys1 &= keys;
+ keys0 |= keys;
+
+ dev_dbg(&pdev->dev, "keys 0x%08lx\n", keys);
+
+ } while (ioread16(priv->iomem_base + KYCR2_OFFS) & 0x01);
+
+ dev_dbg(&pdev->dev, "last_keys 0x%08lx keys0 0x%08lx keys1 0x%08lx\n",
+ priv->last_keys, keys0, keys1);
+
+ for (i = 0; i < SH_KEYSC_MAXKEYS; i++) {
+ k = pdata->keycodes[i];
+ if (!k)
+ continue;
+
+ mask = 1 << i;
+
+ if (!((priv->last_keys ^ keys0) & mask))
+ continue;
+
+ if ((keys1 | keys0) & mask) {
+ input_event(priv->input, EV_KEY, k, 1);
+ priv->last_keys |= mask;
+ }
+
+ if (!(keys1 & mask)) {
+ input_event(priv->input, EV_KEY, k, 0);
+ priv->last_keys &= ~mask;
+ }
+
+ }
+ input_sync(priv->input);
+
+ return IRQ_HANDLED;
+}
+
+#define res_size(res) ((res)->end - (res)->start + 1)
+
+static int __devinit sh_keysc_probe(struct platform_device *pdev)
+{
+ struct sh_keysc_priv *priv;
+ struct sh_keysc_info *pdata;
+ struct resource *res;
+ struct input_dev *input;
+ int i, k;
+ int irq, error;
+
+ if (!pdev->dev.platform_data) {
+ dev_err(&pdev->dev, "no platform data defined\n");
+ error = -EINVAL;
+ goto err0;
+ }
+
+ error = -ENXIO;
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "failed to get I/O memory\n");
+ goto err0;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "failed to get irq\n");
+ goto err0;
+ }
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (priv == NULL) {
+ dev_err(&pdev->dev, "failed to allocate driver data\n");
+ error = -ENOMEM;
+ goto err0;
+ }
+
+ platform_set_drvdata(pdev, priv);
+ memcpy(&priv->pdata, pdev->dev.platform_data, sizeof(priv->pdata));
+ pdata = &priv->pdata;
+
+ res = request_mem_region(res->start, res_size(res), pdev->name);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "failed to request I/O memory\n");
+ error = -EBUSY;
+ goto err1;
+ }
+
+ priv->iomem_base = ioremap_nocache(res->start, res_size(res));
+ if (priv->iomem_base == NULL) {
+ dev_err(&pdev->dev, "failed to remap I/O memory\n");
+ error = -ENXIO;
+ goto err2;
+ }
+
+ priv->input = input_allocate_device();
+ if (!priv->input) {
+ dev_err(&pdev->dev, "failed to allocate input device\n");
+ error = -ENOMEM;
+ goto err3;
+ }
+
+ input = priv->input;
+ input->evbit[0] = BIT_MASK(EV_KEY);
+
+ input->name = pdev->name;
+ input->phys = "sh-keysc-keys/input0";
+ input->dev.parent = &pdev->dev;
+
+ input->id.bustype = BUS_HOST;
+ input->id.vendor = 0x0001;
+ input->id.product = 0x0001;
+ input->id.version = 0x0100;
+
+ error = request_irq(irq, sh_keysc_isr, 0, pdev->name, pdev);
+ if (error) {
+ dev_err(&pdev->dev, "failed to request IRQ\n");
+ goto err4;
+ }
+
+ for (i = 0; i < SH_KEYSC_MAXKEYS; i++) {
+ k = pdata->keycodes[i];
+ if (k)
+ input_set_capability(input, EV_KEY, k);
+ }
+
+ error = input_register_device(input);
+ if (error) {
+ dev_err(&pdev->dev, "failed to register input device\n");
+ goto err5;
+ }
+
+ iowrite16((sh_keysc_mode[pdata->mode].kymd << 8) |
+ pdata->scan_timing, priv->iomem_base + KYCR1_OFFS);
+ iowrite16(0, priv->iomem_base + KYOUTDR_OFFS);
+ iowrite16(KYCR2_IRQ_LEVEL, priv->iomem_base + KYCR2_OFFS);
+ return 0;
+ err5:
+ free_irq(irq, pdev);
+ err4:
+ input_free_device(input);
+ err3:
+ iounmap(priv->iomem_base);
+ err2:
+ release_mem_region(res->start, res_size(res));
+ err1:
+ platform_set_drvdata(pdev, NULL);
+ kfree(priv);
+ err0:
+ return error;
+}
+
+static int __devexit sh_keysc_remove(struct platform_device *pdev)
+{
+ struct sh_keysc_priv *priv = platform_get_drvdata(pdev);
+ struct resource *res;
+
+ iowrite16(KYCR2_IRQ_DISABLED, priv->iomem_base + KYCR2_OFFS);
+
+ input_unregister_device(priv->input);
+ free_irq(platform_get_irq(pdev, 0), pdev);
+ iounmap(priv->iomem_base);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(res->start, res_size(res));
+
+ platform_set_drvdata(pdev, NULL);
+ kfree(priv);
+ return 0;
+}
+
+
+#define sh_keysc_suspend NULL
+#define sh_keysc_resume NULL
+
+struct platform_driver sh_keysc_device_driver = {
+ .probe = sh_keysc_probe,
+ .remove = __devexit_p(sh_keysc_remove),
+ .suspend = sh_keysc_suspend,
+ .resume = sh_keysc_resume,
+ .driver = {
+ .name = "sh_keysc",
+ }
+};
+
+static int __init sh_keysc_init(void)
+{
+ return platform_driver_register(&sh_keysc_device_driver);
+}
+
+static void __exit sh_keysc_exit(void)
+{
+ platform_driver_unregister(&sh_keysc_device_driver);
+}
+
+module_init(sh_keysc_init);
+module_exit(sh_keysc_exit);
+
+MODULE_AUTHOR("Magnus Damm");
+MODULE_DESCRIPTION("SuperH KEYSC Keypad Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/keyboard/spitzkbd.c b/drivers/input/keyboard/spitzkbd.c
index 92102f9e4b8..61e401bc910 100644
--- a/drivers/input/keyboard/spitzkbd.c
+++ b/drivers/input/keyboard/spitzkbd.c
@@ -23,6 +23,7 @@
#include <asm/arch/spitz.h>
#include <asm/arch/hardware.h>
#include <asm/arch/pxa-regs.h>
+#include <asm/arch/pxa2xx-gpio.h>
#define KB_ROWS 7
#define KB_COLS 11
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index c5263d63aca..92b683411d5 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -15,6 +15,7 @@ if INPUT_MISC
config INPUT_PCSPKR
tristate "PC Speaker support"
depends on ALPHA || X86 || MIPS || PPC_PREP || PPC_CHRP || PPC_PSERIES
+ depends on SND_PCSP=n
help
Say Y here if you want the standard PC Speaker to be used for
bells and whistles.
diff --git a/drivers/input/serio/hp_sdc_mlc.c b/drivers/input/serio/hp_sdc_mlc.c
index c45ea74d53e..f1fd3b638a3 100644
--- a/drivers/input/serio/hp_sdc_mlc.c
+++ b/drivers/input/serio/hp_sdc_mlc.c
@@ -40,7 +40,7 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/string.h>
-#include <asm/semaphore.h>
+#include <linux/semaphore.h>
#define PREFIX "HP SDC MLC: "
diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c
index a571aa965da..907a45fe9d4 100644
--- a/drivers/input/touchscreen/ads7846.c
+++ b/drivers/input/touchscreen/ads7846.c
@@ -28,13 +28,6 @@
#include <linux/spi/ads7846.h>
#include <asm/irq.h>
-#ifdef CONFIG_ARM
-#include <asm/mach-types.h>
-#ifdef CONFIG_ARCH_OMAP
-#include <asm/arch/gpio.h>
-#endif
-#endif
-
/*
* This code has been heavily tested on a Nokia 770, and lightly
@@ -1172,31 +1165,6 @@ static struct spi_driver ads7846_driver = {
static int __init ads7846_init(void)
{
- /* grr, board-specific init should stay out of drivers!! */
-
-#ifdef CONFIG_ARCH_OMAP
- if (machine_is_omap_osk()) {
- /* GPIO4 = PENIRQ; GPIO6 = BUSY */
- omap_request_gpio(4);
- omap_set_gpio_direction(4, 1);
- omap_request_gpio(6);
- omap_set_gpio_direction(6, 1);
- }
- // also TI 1510 Innovator, bitbanging through FPGA
- // also Nokia 770
- // also Palm Tungsten T2
-#endif
-
- // PXA:
- // also Dell Axim X50
- // also HP iPaq H191x/H192x/H415x/H435x
- // also Intel Lubbock (additional to UCB1400; as temperature sensor)
- // also Sharp Zaurus C7xx, C8xx (corgi/sheperd/husky)
-
- // Atmel at91sam9261-EK uses ads7843
-
- // also various AMD Au1x00 devel boards
-
return spi_register_driver(&ads7846_driver);
}
module_init(ads7846_init);
@@ -1204,14 +1172,6 @@ module_init(ads7846_init);
static void __exit ads7846_exit(void)
{
spi_unregister_driver(&ads7846_driver);
-
-#ifdef CONFIG_ARCH_OMAP
- if (machine_is_omap_osk()) {
- omap_free_gpio(4);
- omap_free_gpio(6);
- }
-#endif
-
}
module_exit(ads7846_exit);
diff --git a/drivers/input/touchscreen/corgi_ts.c b/drivers/input/touchscreen/corgi_ts.c
index 765e964b796..4e9d8eece2e 100644
--- a/drivers/input/touchscreen/corgi_ts.c
+++ b/drivers/input/touchscreen/corgi_ts.c
@@ -22,6 +22,7 @@
#include <asm/arch/sharpsl.h>
#include <asm/arch/hardware.h>
#include <asm/arch/pxa-regs.h>
+#include <asm/arch/pxa2xx-gpio.h>
#define PWR_MODE_ACTIVE 0
diff --git a/drivers/input/xen-kbdfront.c b/drivers/input/xen-kbdfront.c
new file mode 100644
index 00000000000..0f47f4697cd
--- /dev/null
+++ b/drivers/input/xen-kbdfront.c
@@ -0,0 +1,340 @@
+/*
+ * Xen para-virtual input device
+ *
+ * Copyright (C) 2005 Anthony Liguori <aliguori@us.ibm.com>
+ * Copyright (C) 2006-2008 Red Hat, Inc., Markus Armbruster <armbru@redhat.com>
+ *
+ * Based on linux/drivers/input/mouse/sermouse.c
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive for
+ * more details.
+ */
+
+/*
+ * TODO:
+ *
+ * Switch to grant tables together with xen-fbfront.c.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <asm/xen/hypervisor.h>
+#include <xen/events.h>
+#include <xen/page.h>
+#include <xen/interface/io/fbif.h>
+#include <xen/interface/io/kbdif.h>
+#include <xen/xenbus.h>
+
+struct xenkbd_info {
+ struct input_dev *kbd;
+ struct input_dev *ptr;
+ struct xenkbd_page *page;
+ int irq;
+ struct xenbus_device *xbdev;
+ char phys[32];
+};
+
+static int xenkbd_remove(struct xenbus_device *);
+static int xenkbd_connect_backend(struct xenbus_device *, struct xenkbd_info *);
+static void xenkbd_disconnect_backend(struct xenkbd_info *);
+
+/*
+ * Note: if you need to send out events, see xenfb_do_update() for how
+ * to do that.
+ */
+
+static irqreturn_t input_handler(int rq, void *dev_id)
+{
+ struct xenkbd_info *info = dev_id;
+ struct xenkbd_page *page = info->page;
+ __u32 cons, prod;
+
+ prod = page->in_prod;
+ if (prod == page->in_cons)
+ return IRQ_HANDLED;
+ rmb(); /* ensure we see ring contents up to prod */
+ for (cons = page->in_cons; cons != prod; cons++) {
+ union xenkbd_in_event *event;
+ struct input_dev *dev;
+ event = &XENKBD_IN_RING_REF(page, cons);
+
+ dev = info->ptr;
+ switch (event->type) {
+ case XENKBD_TYPE_MOTION:
+ input_report_rel(dev, REL_X, event->motion.rel_x);
+ input_report_rel(dev, REL_Y, event->motion.rel_y);
+ break;
+ case XENKBD_TYPE_KEY:
+ dev = NULL;
+ if (test_bit(event->key.keycode, info->kbd->keybit))
+ dev = info->kbd;
+ if (test_bit(event->key.keycode, info->ptr->keybit))
+ dev = info->ptr;
+ if (dev)
+ input_report_key(dev, event->key.keycode,
+ event->key.pressed);
+ else
+ printk(KERN_WARNING
+ "xenkbd: unhandled keycode 0x%x\n",
+ event->key.keycode);
+ break;
+ case XENKBD_TYPE_POS:
+ input_report_abs(dev, ABS_X, event->pos.abs_x);
+ input_report_abs(dev, ABS_Y, event->pos.abs_y);
+ break;
+ }
+ if (dev)
+ input_sync(dev);
+ }
+ mb(); /* ensure we got ring contents */
+ page->in_cons = cons;
+ notify_remote_via_irq(info->irq);
+
+ return IRQ_HANDLED;
+}
+
+static int __devinit xenkbd_probe(struct xenbus_device *dev,
+ const struct xenbus_device_id *id)
+{
+ int ret, i;
+ struct xenkbd_info *info;
+ struct input_dev *kbd, *ptr;
+
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
+ if (!info) {
+ xenbus_dev_fatal(dev, -ENOMEM, "allocating info structure");
+ return -ENOMEM;
+ }
+ dev->dev.driver_data = info;
+ info->xbdev = dev;
+ info->irq = -1;
+ snprintf(info->phys, sizeof(info->phys), "xenbus/%s", dev->nodename);
+
+ info->page = (void *)__get_free_page(GFP_KERNEL | __GFP_ZERO);
+ if (!info->page)
+ goto error_nomem;
+
+ /* keyboard */
+ kbd = input_allocate_device();
+ if (!kbd)
+ goto error_nomem;
+ kbd->name = "Xen Virtual Keyboard";
+ kbd->phys = info->phys;
+ kbd->id.bustype = BUS_PCI;
+ kbd->id.vendor = 0x5853;
+ kbd->id.product = 0xffff;
+ kbd->evbit[0] = BIT(EV_KEY);
+ for (i = KEY_ESC; i < KEY_UNKNOWN; i++)
+ set_bit(i, kbd->keybit);
+ for (i = KEY_OK; i < KEY_MAX; i++)
+ set_bit(i, kbd->keybit);
+
+ ret = input_register_device(kbd);
+ if (ret) {
+ input_free_device(kbd);
+ xenbus_dev_fatal(dev, ret, "input_register_device(kbd)");
+ goto error;
+ }
+ info->kbd = kbd;
+
+ /* pointing device */
+ ptr = input_allocate_device();
+ if (!ptr)
+ goto error_nomem;
+ ptr->name = "Xen Virtual Pointer";
+ ptr->phys = info->phys;
+ ptr->id.bustype = BUS_PCI;
+ ptr->id.vendor = 0x5853;
+ ptr->id.product = 0xfffe;
+ ptr->evbit[0] = BIT(EV_KEY) | BIT(EV_REL) | BIT(EV_ABS);
+ for (i = BTN_LEFT; i <= BTN_TASK; i++)
+ set_bit(i, ptr->keybit);
+ ptr->relbit[0] = BIT(REL_X) | BIT(REL_Y);
+ input_set_abs_params(ptr, ABS_X, 0, XENFB_WIDTH, 0, 0);
+ input_set_abs_params(ptr, ABS_Y, 0, XENFB_HEIGHT, 0, 0);
+
+ ret = input_register_device(ptr);
+ if (ret) {
+ input_free_device(ptr);
+ xenbus_dev_fatal(dev, ret, "input_register_device(ptr)");
+ goto error;
+ }
+ info->ptr = ptr;
+
+ ret = xenkbd_connect_backend(dev, info);
+ if (ret < 0)
+ goto error;
+
+ return 0;
+
+ error_nomem:
+ ret = -ENOMEM;
+ xenbus_dev_fatal(dev, ret, "allocating device memory");
+ error:
+ xenkbd_remove(dev);
+ return ret;
+}
+
+static int xenkbd_resume(struct xenbus_device *dev)
+{
+ struct xenkbd_info *info = dev->dev.driver_data;
+
+ xenkbd_disconnect_backend(info);
+ memset(info->page, 0, PAGE_SIZE);
+ return xenkbd_connect_backend(dev, info);
+}
+
+static int xenkbd_remove(struct xenbus_device *dev)
+{
+ struct xenkbd_info *info = dev->dev.driver_data;
+
+ xenkbd_disconnect_backend(info);
+ if (info->kbd)
+ input_unregister_device(info->kbd);
+ if (info->ptr)
+ input_unregister_device(info->ptr);
+ free_page((unsigned long)info->page);
+ kfree(info);
+ return 0;
+}
+
+static int xenkbd_connect_backend(struct xenbus_device *dev,
+ struct xenkbd_info *info)
+{
+ int ret, evtchn;
+ struct xenbus_transaction xbt;
+
+ ret = xenbus_alloc_evtchn(dev, &evtchn);
+ if (ret)
+ return ret;
+ ret = bind_evtchn_to_irqhandler(evtchn, input_handler,
+ 0, dev->devicetype, info);
+ if (ret < 0) {
+ xenbus_free_evtchn(dev, evtchn);
+ xenbus_dev_fatal(dev, ret, "bind_evtchn_to_irqhandler");
+ return ret;
+ }
+ info->irq = ret;
+
+ again:
+ ret = xenbus_transaction_start(&xbt);
+ if (ret) {
+ xenbus_dev_fatal(dev, ret, "starting transaction");
+ return ret;
+ }
+ ret = xenbus_printf(xbt, dev->nodename, "page-ref", "%lu",
+ virt_to_mfn(info->page));
+ if (ret)
+ goto error_xenbus;
+ ret = xenbus_printf(xbt, dev->nodename, "event-channel", "%u",
+ evtchn);
+ if (ret)
+ goto error_xenbus;
+ ret = xenbus_transaction_end(xbt, 0);
+ if (ret) {
+ if (ret == -EAGAIN)
+ goto again;
+ xenbus_dev_fatal(dev, ret, "completing transaction");
+ return ret;
+ }
+
+ xenbus_switch_state(dev, XenbusStateInitialised);
+ return 0;
+
+ error_xenbus:
+ xenbus_transaction_end(xbt, 1);
+ xenbus_dev_fatal(dev, ret, "writing xenstore");
+ return ret;
+}
+
+static void xenkbd_disconnect_backend(struct xenkbd_info *info)
+{
+ if (info->irq >= 0)
+ unbind_from_irqhandler(info->irq, info);
+ info->irq = -1;
+}
+
+static void xenkbd_backend_changed(struct xenbus_device *dev,
+ enum xenbus_state backend_state)
+{
+ struct xenkbd_info *info = dev->dev.driver_data;
+ int ret, val;
+
+ switch (backend_state) {
+ case XenbusStateInitialising:
+ case XenbusStateInitialised:
+ case XenbusStateUnknown:
+ case XenbusStateClosed:
+ break;
+
+ case XenbusStateInitWait:
+InitWait:
+ ret = xenbus_scanf(XBT_NIL, info->xbdev->otherend,
+ "feature-abs-pointer", "%d", &val);
+ if (ret < 0)
+ val = 0;
+ if (val) {
+ ret = xenbus_printf(XBT_NIL, info->xbdev->nodename,
+ "request-abs-pointer", "1");
+ if (ret)
+ printk(KERN_WARNING
+ "xenkbd: can't request abs-pointer");
+ }
+ xenbus_switch_state(dev, XenbusStateConnected);
+ break;
+
+ case XenbusStateConnected:
+ /*
+ * Work around xenbus race condition: If backend goes
+ * through InitWait to Connected fast enough, we can
+ * get Connected twice here.
+ */
+ if (dev->state != XenbusStateConnected)
+ goto InitWait; /* no InitWait seen yet, fudge it */
+ break;
+
+ case XenbusStateClosing:
+ xenbus_frontend_closed(dev);
+ break;
+ }
+}
+
+static struct xenbus_device_id xenkbd_ids[] = {
+ { "vkbd" },
+ { "" }
+};
+
+static struct xenbus_driver xenkbd = {
+ .name = "vkbd",
+ .owner = THIS_MODULE,
+ .ids = xenkbd_ids,
+ .probe = xenkbd_probe,
+ .remove = xenkbd_remove,
+ .resume = xenkbd_resume,
+ .otherend_changed = xenkbd_backend_changed,
+};
+
+static int __init xenkbd_init(void)
+{
+ if (!is_running_on_xen())
+ return -ENODEV;
+
+ /* Nothing to do if running in dom0. */
+ if (is_initial_xendomain())
+ return -ENODEV;
+
+ return xenbus_register_frontend(&xenkbd);
+}
+
+static void __exit xenkbd_cleanup(void)
+{
+ xenbus_unregister_driver(&xenkbd);
+}
+
+module_init(xenkbd_init);
+module_exit(xenkbd_cleanup);
+
+MODULE_LICENSE("GPL");