--- /dev/null
+diff --git a/drivers/input/Makefile b/drivers/input/Makefile
+index 98c4f9a..414082b 100644
+--- a/drivers/input/Makefile
++++ b/drivers/input/Makefile
+@@ -21,6 +21,7 @@ obj-$(CONFIG_INPUT_JOYSTICK) += joystick/
+ obj-$(CONFIG_INPUT_TABLET) += tablet/
+ obj-$(CONFIG_INPUT_TOUCHSCREEN) += touchscreen/
+ obj-$(CONFIG_INPUT_MISC) += misc/
++obj-$(CONFIG_INPUT_XEN) += xen/
+
+ obj-$(CONFIG_INPUT_APMPOWER) += apm-power.o
+
+diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig
+index 27d70d3..b2f4ff7 100644
+--- a/drivers/input/serio/Kconfig
++++ b/drivers/input/serio/Kconfig
+@@ -19,7 +19,7 @@ config SERIO
+ if SERIO
+
+ config SERIO_I8042
+- tristate "i8042 PC Keyboard controller" if EMBEDDED || !X86
++ tristate "i8042 PC Keyboard controller" if EMBEDDED || X86
+ default y
+ depends on !PARISC && (!ARM || ARCH_SHARK || FOOTBRIDGE_HOST) && !M68K && !BLACKFIN
+ ---help---
+diff --git a/drivers/input/xen/Kconfig b/drivers/input/xen/Kconfig
+new file mode 100644
+index 0000000..c45f063
+--- /dev/null
++++ b/drivers/input/xen/Kconfig
+@@ -0,0 +1,25 @@
++#
++# Mouse driver configuration
++#
++menuconfig INPUT_XEN
++ bool "XEN"
++ default n
++ help
++ Say Y here for input devices specific to Xen.
++ This option doesn't affect the kernel.
++
++ If unsure, say N.
++
++if INPUT_XEN
++
++config XEN_8042
++ tristate "8042 KBD and Mouse"
++ default y
++ ---help---
++ Say Y here if you have a PS/2 keyboard and/or mouse connected to
++ your system that you wish to virtualize between Xen Client guests.
++ If unsure, say Y.
++
++ To compile this driver as a module, choose M here: the
++ module will be called pass2 (for pass-through PS/2).
++endif
+diff --git a/drivers/input/xen/Makefile b/drivers/input/xen/Makefile
+new file mode 100644
+index 0000000..25e4bce
+--- /dev/null
++++ b/drivers/input/xen/Makefile
+@@ -0,0 +1,7 @@
++#
++# Makefile for Xen Client input devices.
++#
++
++obj-$(CONFIG_XEN_8042) += pass2.o
++
++xeninput-objs := pass2.o
+diff --git a/drivers/input/xen/pass2.c b/drivers/input/xen/pass2.c
+new file mode 100644
+index 0000000..550b07f
+--- /dev/null
++++ b/drivers/input/xen/pass2.c
+@@ -0,0 +1,1153 @@
++/*
++ * Kernel-side driver of XenClient's pass-through PS/2 solution.
++ * Most the intelligence is in ioemu. This driver talks to the h/w (i8042),
++ * handles interrupts, arbitrates on which guest has focus on the devices
++ * (KBD and AUX), and passes constant data from the primary VM to the
++ * secondaries.
++ */
++
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/string.h>
++#include <linux/delay.h>
++#include <linux/interrupt.h>
++#include <linux/ioport.h>
++#include <linux/errno.h>
++#include <linux/fs.h>
++#include <linux/proc_fs.h>
++#include <linux/errno.h>
++#include <linux/proc_fs.h>
++#include <linux/stat.h>
++#include <linux/poll.h>
++#include <linux/pass2.h>
++#include <linux/irq.h>
++#include <linux/init.h>
++#include <linux/spinlock.h>
++#include <linux/cpu.h>
++
++#include <asm/io.h>
++
++/**
++ ** Compile control.
++ **/
++
++/* #define PASS2_DEBUG 1 */
++
++/*
++ * Constants.
++ */
++
++#define PA2_KBD_IRQ 1
++#define PA2_AUX_IRQ 12
++
++#define PA2_DATA_PORT 0x60
++#define PA2_CMD_PORT 0x64
++#define PA2_STATUS_PORT 0x64
++
++/*
++ * Status Register bits.
++ */
++
++#define PA2_CTL_STAT_OBF 0x01 /* KBD output buffer full */
++#define PA2_CTL_STAT_IBF 0x02 /* KBD input buffer full */
++#define PA2_CTL_STAT_SELFTEST 0x04 /* Self test successful */
++#define PA2_STAT_CMD 0x08 /* Set when last write was a
++ * cmd */
++#define PA2_CTL_STAT_UNLOCKED 0x10 /* Zero if keyboard locked */
++#define PA2_CTL_STAT_AUX_OBF 0x20 /* AUX output buffer full */
++#define PA2_CTL_STAT_GTO 0x40 /* receive/xmit timeout */
++#define PA2_CTL_STAT_PERR 0x80 /* Parity error */
++
++/*
++ * Controller Mode Register Bits.
++ */
++
++#define PA2_CTL_MODE_INT_KBD 0x01 /* KBD data generate IRQ1 */
++#define PA2_CTL_MODE_INT_AUX 0x02 /* AUX data generate IRQ12 */
++#define PA2_CTL_MODE_SYS 0x04 /* The system flag (?) */
++#define PA2_CTL_MODE_NO_KEYLOCK 0x08 /* keylock doesn't affect the
++ * KBD */
++#define PA2_CTL_MODE_DISABLE_KBD 0x10 /* Disable keyboard interface */
++#define PA2_CTL_MODE_DISABLE_AUX 0x20 /* Disable aux interface */
++#define PA2_CTL_MODE_KCC 0x40 /* Scan code conversion to PC
++ * format */
++#define PA2_CTL_MODE_RFU 0x80
++
++/*
++ * Keyboard Controller Commands
++ */
++
++#define PA2_CTL_CMD_MODE_READ 0x20 /* read mode bits */
++#define PA2_CTL_CMD_MODE_WRITE 0x60 /* write mode bits */
++
++/**
++ ** Debug
++ **/
++
++#if defined(PASS2_DEBUG)
++#define ASSERT(cond) \
++ do { \
++ if ((cond) != 0) { \
++ break; \
++ } \
++ printk(KERN_ERR "pass2: BUG on line:%d \"%s\"\n", \
++ __LINE__, #cond); \
++ panic("ASSERT\n"); \
++ } while (0)
++
++#define PRINT_DEBUG(format, args...) \
++ do { \
++ printk(KERN_ERR "pass2: " format, ## args); \
++ } while (0)
++#else
++#define ASSERT(cond) do { } while (0)
++#define PRINT_DEBUG(format, args...) do { } while (0)
++#endif
++
++/*
++ * Names of pseudo files.
++ */
++
++static const char *pa2_path[] = { "8042-kbd", "8042-aux", "8042-ctl" };
++
++#define PA2_INDEX_KBD 0
++#define PA2_INDEX_AUX 1
++#define PA2_INDEX_CTL 2
++
++/**
++ ** Queues.
++ ** Data is stored on two queues, one for KDB and one for AUX, until read
++ ** by the user-space driver.
++ ** The AUX device, when it is a Synaptic touchpad, can generate up to 80
++ ** sample packets per second with each packet being 6 bytes (480 bytes per
++ ** second). Queue is sized to store just over a seconds worth (512 entries).
++ **/
++
++#define PA2_BUF_SHIFT 9
++#define PA2_BUF_SZ (1 << PA2_BUF_SHIFT)
++
++/*
++ * Received data queue structure;
++ * pp_entry - data/status
++ * pp_start - first unused position in pp_entry
++ * pp_end - oldest, unread, data in pp_entry
++ * pp_overflow - an overflow occured since last user-space read
++ */
++
++typedef struct pa2_ps_s {
++ pa2_entry_t pp_entry[PA2_BUF_SZ];
++ unsigned int pp_start;
++ unsigned int pp_end;
++ unsigned int pp_overflow;
++} pa2_ps_t;
++
++/*
++ * The state of each device. There are two of these, one for KBD other for
++ * AUX.
++ * Only the file structure which has grabbed (PA2_IOCTL_GRAB) the device can
++ * read data from it (this should be extended to sending cmds).
++ * ps_lock - guard for data on device's queue (ps_queue), and
++ * for grabbed status (ps_grabbed).
++ * ps_index - PA2_INDEX_{AUX,KBD}
++ * ps_waiting_grab - XXX not fully implemented....
++ * ps_grabbed - pointer to file structure that owns (grabbed) the
++ * device (PA2_IOCTL_GRAB).
++ * ps_queue - pointer to received data queue
++ * ps_wait_grab - XXX not fully implemented...
++ * ps_wait_poll - waiting for incoming data..XXX
++ */
++
++typedef struct pa2_state_s {
++ spinlock_t ps_lock;
++ unsigned int ps_index;
++ unsigned int ps_waiting_grab;
++ struct file *ps_grabbed;
++ pa2_ps_t *ps_queue;
++ wait_queue_head_t ps_wait_grab;
++ wait_queue_head_t ps_wait_poll;
++} pa2_state_t;
++
++/*
++ * For the ctl file.
++ * The members must match the start of pa2_state_t.
++ */
++
++typedef struct pa2_ctl_s {
++ spinlock_t ps_lock;
++ unsigned int ps_index;
++} pa2_ctl_t;
++
++/**
++ ** Globals
++ **/
++
++static pa2_ps_t pa2_queue[PA2_INDEX_AUX + 1];
++static pa2_state_t pa2_state[PA2_INDEX_CTL + 1]; /* surely AUX? XXX */
++static pa2_ctl_t pa2_ctl;
++
++/*
++ * pa2_i8042_lock - guards reading from the data port, and some globals
++ * (pa2_open_count, XXX)
++ * pa2_open_count - number of file opens
++ * pa2_initialized - flag indicating if one time initialization has
++ * been performed by user-space
++ * pa2_init_taskp - task performing the initialization
++ * pa2_init_buffer - copy of initialization data
++ * pa2_init_buffer_tmp - temp copy of initialization data, copied from user-
++ * space. Not taken on stack due to size.
++ * pa2_proc_dev - /proc files
++ * pa2_init_sem - semaphore guard held during initilization
++ * pa2_cmd_sem - controls access to cmd register
++ * pa2_cmd_filp - file structure that holds the cmd_sem
++ */
++
++static DEFINE_SPINLOCK(pa2_i8042_lock); /* cache aligned XXX */
++static unsigned int pa2_open_count;
++static int pa2_initialized;
++static struct task_struct *pa2_init_taskp;
++static pa2_data_init_t pa2_init_buffer;
++static pa2_data_init_t pa2_init_buffer_tmp;
++static struct proc_dir_entry *pa2_proc_dev[PA2_INDEX_CTL + 1];
++
++static DECLARE_MUTEX(pa2_init_sem);
++static DECLARE_MUTEX(pa2_cmd_sem);
++static struct file *pa2_cmd_filp;
++
++/*
++ * Functions for accessing status, data, and cmd ports.
++ */
++
++static inline u8
++pa2_status_read(
++ void)
++{
++ return inb(PA2_STATUS_PORT);
++}
++
++static inline u8
++pa2_data_read(
++ void)
++{
++ return inb(PA2_DATA_PORT);
++}
++
++static inline void
++pa2_cmd_write(
++ u8 byte)
++{
++ outb(byte, PA2_CMD_PORT);
++ return;
++}
++
++/**
++ ** Support functions.
++ **/
++
++static __attribute__ ((format (printf, 1, 2))) void
++pa2_error(
++ const char *fmt,
++ ...)
++{
++ va_list ap;
++
++ printk(KERN_ERR "pa2: ");
++ va_start(ap, fmt);
++ vprintk(fmt, ap);
++ va_end(ap);
++ return;
++}
++
++/*
++ * Wait for input buffer to become empty.
++ */
++
++static int
++pa2_wait_on_input_buffer(
++ void)
++{
++ int i;
++ int ret;
++ u8 status;
++
++ ret = 1;
++ for (i = 0; i < 10000; i++) {
++ status = pa2_status_read();
++ if (status & PA2_CTL_STAT_IBF) {
++ udelay(50);
++ continue;
++ }
++ ret = 0;
++ break;
++ }
++ return ret;
++}
++
++static int
++pa2_wait_on_output_buf(
++ void)
++{
++ int i;
++ int ret;
++ u8 status;
++
++ ret = 1;
++ for (i = 0; i < 10000; i++) {
++ status = pa2_status_read();
++ if (!(status & PA2_CTL_STAT_OBF)) {
++ udelay(50);
++ continue;
++ }
++ ret = 0;
++ break;
++ }
++ return ret;
++}
++
++static int
++pa2_wait_on_aux_output_buf(
++ void)
++{
++ int i;
++ int ret;
++ u8 status;
++
++ ret = 1;
++ for (i = 0; i < 10000; i++) {
++ status = pa2_status_read();
++ if ((status & (PA2_CTL_STAT_AUX_OBF | PA2_CTL_STAT_OBF)) !=
++ (PA2_CTL_STAT_AUX_OBF | PA2_CTL_STAT_OBF)) {
++ udelay(50);
++ continue;
++ }
++ ret = 0;
++ break;
++ }
++ return ret;
++}
++
++static int
++pa2_cmd_write_do(
++ u8 cmd)
++{
++ int ret;
++
++ ret = pa2_wait_on_input_buffer();
++ pa2_cmd_write(cmd);
++ ret |= pa2_wait_on_input_buffer(); /* XXX */
++ return ret;
++}
++
++static int
++pa2_data_write_do(
++ u8 data)
++{
++ int ret;
++
++ ret = pa2_wait_on_input_buffer();
++ pa2_cmd_write(data);
++ ret |= pa2_wait_on_input_buffer(); /* XXX */
++ return ret;
++}
++
++static int
++pa2_ctl_mode_write_do(
++ u8 data)
++{
++ int ret;
++
++ ret = pa2_cmd_write_do(PA2_CTL_CMD_MODE_WRITE);
++ ret |= pa2_data_write_do(data);
++ return ret;
++}
++
++static long
++pa2_grab(
++ struct file *filp,
++ void __user *p)
++{
++ DECLARE_WAITQUEUE(wait, current);
++ pa2_state_t *sp;
++ int ret;
++ unsigned long expire;
++ unsigned char byte;
++
++ sp = filp->private_data;
++ ASSERT(sp->ps_index == PA2_INDEX_KBD ||
++ sp->ps_index == PA2_INDEX_AUX);
++ ret = 0;
++ if (copy_from_user(&byte, p, 1)) {
++ ret = -EFAULT;
++ goto out;
++ }
++ PRINT_DEBUG("%s %s\n", byte == 0 ? "Relasing" : "Grabbing",
++ sp->ps_index == PA2_INDEX_KBD ? "KBD" : "AUX");
++
++ /*
++ * Do not allow a grab unless we're seen an initialisation.
++ * Need both sem and spinlock to modify pa2_initialized, so can
++ * test with just one of these held.
++ * If currently being initialised, then wait.
++ */
++
++ spin_lock_irq(&pa2_i8042_lock);
++ if (byte == 1) {
++ /*
++ * If someone is initializing, then delay the grab until the
++ * initialization is complete.
++ * If it is us that is initializing, then this is an error (qemu
++ * is single threaded, so if we wait for the init to end we'll
++ * deadlock).
++ */
++
++ while (pa2_init_taskp != NULL) {
++ if (pa2_init_taskp == current) {
++ pa2_error("Trying to grab while initing\n");
++ ret = -EAGAIN; /* XXX */
++ break;
++ }
++ PRINT_DEBUG("Waiting for initialization to complete\n");
++ spin_unlock_irq(&pa2_i8042_lock);
++ expire = schedule_timeout_interruptible(HZ / 10);
++ spin_lock_irq(&pa2_i8042_lock);
++ if (expire == 0) {
++ continue;
++ }
++ ret = -EINTR; /* XXX */
++ break;
++ }
++ if (ret == 0 && pa2_initialized == 0) {
++ ret = -EINVAL;
++ }
++ }
++ spin_unlock_irq(&pa2_i8042_lock);
++ if (ret != 0) {
++ goto out;
++ }
++
++ /*
++ * Once initialized, cannot be intialized again. As we have an
++ * open, cannot become uninitialized, so safe to continue without lock.
++ */
++
++ spin_lock_irq(&sp->ps_lock);
++ if (byte == 0) {
++ if (sp->ps_grabbed != filp) {
++ ret = -EINVAL;
++ } else {
++ sp->ps_grabbed = NULL;
++ if (sp->ps_waiting_grab != 0) {
++ sp->ps_waiting_grab = 0;
++ wake_up(&sp->ps_wait_grab);
++ }
++ }
++ spin_unlock_irq(&sp->ps_lock);
++ goto out;
++ }
++
++
++ /*
++ * Set that we want focus, then wait for it to be given.
++ */
++
++ add_wait_queue(&sp->ps_wait_grab, &wait);
++ while (sp->ps_grabbed != NULL) {
++ sp->ps_waiting_grab = 1;
++ wake_up(&sp->ps_wait_poll);
++ set_current_state(TASK_INTERRUPTIBLE);
++ spin_unlock_irq(&sp->ps_lock);
++ PRINT_DEBUG("Waiting for device to come available...\n");
++ expire = schedule_timeout(HZ / 10);
++ spin_lock_irq(&sp->ps_lock);
++ if (expire == 0) {
++ continue;
++ }
++ ret = -EINTR; /* XXX */
++ break;
++ }
++ remove_wait_queue(&sp->ps_wait_grab, &wait);
++ if (ret == 0) {
++ sp->ps_grabbed = filp;
++ PRINT_DEBUG("Grabbed by %p\n", current);
++ }
++ spin_unlock_irq(&sp->ps_lock);
++
++ /*
++ * After opening the device, user-space driver should query
++ * us to see if the device has been initialized.
++ * On the last close, the device is considered to have been
++ * uninitialized.
++ */
++
++out:
++ return ret;
++}
++
++
++/*
++ * Write to the data port.
++ * If requested, clear the data queued for the given device. This is needed
++ * as the write could be KBD or AUX cmd, and these clear any data queued for
++ * the device. As there could be a pending data, clear this before sending
++ * the cmd. Not 100% this last part is needed....XXX
++ */
++
++static inline int
++pa2_data_write(
++ struct file *filp,
++ pa2_data_t *arg,
++ int index)
++{
++ pa2_state_t *sp;
++ pa2_ps_t *pp;
++ unsigned char status;
++ unsigned char data;
++ unsigned long flags;
++ int ret;
++
++ ASSERT(index == PA2_INDEX_KBD || index == PA2_INDEX_AUX);
++ sp = &pa2_state[index];
++
++ /*
++ * Debug only XXX
++ */
++
++ ret = 0;
++ spin_lock_irqsave(&sp->ps_lock, flags);
++ if (sp->ps_grabbed != filp && pa2_init_taskp != current) {
++ spin_unlock_irqrestore(&sp->ps_lock, flags);
++ pa2_error("Trying to write without perm: %p %p %p %p\n",
++ sp->ps_grabbed, filp, pa2_init_taskp, current);
++ ret = -EACCES;
++ goto out;
++ }
++ spin_unlock_irqrestore(&sp->ps_lock, flags);
++
++ if (arg->pd_clear != 0) {
++ spin_lock_irqsave(&pa2_i8042_lock, flags);
++ status = pa2_status_read();
++ if (unlikely(status & PA2_CTL_STAT_OBF)) {
++ data = pa2_data_read();
++ }
++ }
++ outb(arg->pd_data, PA2_DATA_PORT);
++ if (arg->pd_clear != 0) {
++ pp = sp->ps_queue;
++ spin_lock(&sp->ps_lock);
++ pp->pp_start = pp->pp_end;
++ spin_unlock(&sp->ps_lock);
++ spin_unlock_irqrestore(&pa2_i8042_lock, flags);
++ }
++
++out:
++ return ret;
++}
++
++static irqreturn_t
++pa2_irq(
++ int irq,
++ void *dev_id,
++ struct pt_regs *regs)
++{
++ pa2_state_t *sp;
++ pa2_ps_t *pp;
++ unsigned long flags;
++ unsigned int next;
++ unsigned char status;
++ unsigned char data;
++ int wake;
++ int ret;
++
++ ASSERT(irq == PA2_KBD_IRQ || irq == PA2_AUX_IRQ);
++ PRINT_DEBUG("irq: %d\n", irq);
++ ret = 0;
++ spin_lock_irqsave(&pa2_i8042_lock, flags);
++ status = pa2_status_read();
++ if (unlikely(~status & PA2_CTL_STAT_OBF)) {
++ spin_unlock_irqrestore(&pa2_i8042_lock, flags);
++ PRINT_DEBUG("Interrupt %d without any data\n", irq);
++ goto out;
++ }
++ data = pa2_data_read();
++ spin_unlock_irqrestore(&pa2_i8042_lock, flags);
++
++ /*
++ * Can't rely on the IRQ to determine where the data is from (could
++ * be a data record read by the app has raced with this interrupt
++ * and read the data it was raised for).
++ */
++
++ sp = &pa2_state[PA2_INDEX_KBD];
++ if (status & PA2_CTL_STAT_AUX_OBF) {
++ sp = &pa2_state[PA2_INDEX_AUX];
++ }
++ pp = sp->ps_queue;
++ spin_lock_irqsave(&sp->ps_lock, flags);
++ next = (pp->pp_start + 1) & (PA2_BUF_SZ - 1);
++ if (next == pp->pp_end) {
++ pp->pp_overflow++;
++ spin_unlock_irqrestore(&sp->ps_lock, flags);
++ goto out;
++ }
++ pp->pp_entry[pp->pp_start].pe_data = data;
++ pp->pp_entry[pp->pp_start].pe_status = status;
++ pp->pp_start = next;
++ wake = 0;
++ if (waitqueue_active(&sp->ps_wait_poll)) {
++ wake = 1;
++ }
++ spin_unlock_irqrestore(&sp->ps_lock, flags);
++
++ if (wake) {
++ wake_up(&sp->ps_wait_poll);
++ }
++ ret = 1;
++
++out:
++ return IRQ_RETVAL(ret);
++}
++
++
++static int
++pa2_release(
++ struct inode *inode,
++ struct file *filp)
++{
++ pa2_state_t *sp;
++ unsigned int i;
++
++ ASSERT(filp->private_data != NULL);
++
++ PRINT_DEBUG("release\n");
++ sp = filp->private_data;
++ if (sp->ps_index != PA2_INDEX_CTL) {
++ ASSERT(sp->ps_index == PA2_INDEX_KBD ||
++ sp->ps_index == PA2_INDEX_AUX);
++ spin_lock_irq(&sp->ps_lock);
++ PRINT_DEBUG("Release: %p %p...\n", sp->ps_grabbed, filp);
++ if (sp->ps_grabbed == filp) {
++ PRINT_DEBUG("Releasing grab...\n");
++ sp->ps_grabbed = NULL;
++ if (sp->ps_waiting_grab) {
++ sp->ps_waiting_grab = 0;
++ wake_up(&sp->ps_wait_grab);
++ }
++ }
++ spin_unlock_irq(&sp->ps_lock);
++ }
++
++ spin_lock_irq(&pa2_i8042_lock);
++ if (sp->ps_index == PA2_INDEX_CTL) {
++
++ /*
++ * pa2_init_taskp needs all spinlocks to be modified (and,
++ * hence, only one to be read.
++ */
++
++ PRINT_DEBUG("release CTL\n");
++ sp = &pa2_state[0];
++ for (i = 0; i < 2; i++, sp++) {
++ spin_lock(&sp->ps_lock);
++ }
++ if (pa2_init_taskp == current) {
++ /* assert sem held XXX */
++ pa2_init_taskp = NULL;
++ up(&pa2_init_sem);
++ }
++ sp = &pa2_state[0];
++ for (i = 0; i < 2; i++, sp++) {
++ spin_unlock(&sp->ps_lock);
++ }
++ }
++ ASSERT(pa2_open_count != 0);
++ pa2_open_count--;
++ if (pa2_open_count == 0) {
++ pa2_initialized = 0;
++ memset(&pa2_init_buffer, 0, sizeof (pa2_init_buffer));
++ /* XXX */
++ (void) pa2_ctl_mode_write_do(PA2_CTL_MODE_KCC);
++ }
++ spin_unlock_irq(&pa2_i8042_lock);
++
++ if (pa2_cmd_filp == filp) {
++ ASSERT(sp->ps_index == PA2_INDEX_CTL);
++ pa2_cmd_filp = NULL;
++ up(&pa2_cmd_sem);
++ }
++ filp->private_data = NULL;
++ return 0;
++}
++
++static int
++pa2_open(
++ struct inode *inode,
++ struct file *filp)
++{
++ struct proc_dir_entry *dp;
++ int ret;
++ int i;
++
++ ret = 0;
++ dp = PROC_I(inode)->pde;
++ for (i = 0; i < 3; i++) {
++ if (dp != pa2_proc_dev[i]) {
++ continue;
++ }
++ if (i == PA2_INDEX_CTL) {
++ filp->private_data = &pa2_ctl;
++ } else {
++ filp->private_data = &pa2_state[i];
++ }
++ break;
++ }
++ ASSERT(i != 3);
++ ASSERT(filp->private_data != NULL);
++ spin_lock_irq(&pa2_i8042_lock);
++ pa2_open_count++;
++ spin_unlock_irq(&pa2_i8042_lock);
++ return ret;
++}
++
++/*
++ * Read records into given user-space buffer.
++ */
++
++static long
++pa2_record_read(
++ struct file *filp,
++ void __user *p)
++{
++ pa2_state_t *sp;
++ pa2_ps_t *pp;
++ pa2_rec_hd_t phd;
++ pa2_entry_t prec;
++ pa2_entry_t __user *routp;
++ long ret;
++
++ ret = 0;
++ sp = filp->private_data;
++ ASSERT(sp->ps_index == PA2_INDEX_KBD ||
++ sp->ps_index == PA2_INDEX_AUX);
++ if (copy_from_user(&phd, p, sizeof (phd))) {
++ ret = -EFAULT;
++ goto out;
++ }
++ phd.ph_rec_num = 0;
++ phd.ph_focus = 0;
++ phd.ph_overflow = 0;
++ phd.ph_forced = 0;
++ routp = phd.ph_records;
++
++ pp = sp->ps_queue;
++ spin_lock_irq(&sp->ps_lock);
++ if (sp->ps_grabbed != filp && pa2_init_taskp != current) {
++ spin_unlock_irq(&sp->ps_lock);
++ pa2_error("Trying to access ports without perm: %p %p %p %p\n",
++ sp->ps_grabbed, filp, pa2_init_taskp, current);
++ ret = -EACCES;
++ goto out;
++ }
++
++ /*
++ * While not out of records, and user-space buffer not full, copy
++ * out record.
++ */
++
++ while (pp->pp_end != pp->pp_start &&
++ phd.ph_rec_num != phd.ph_rec_size) {
++ prec.pe_data = pp->pp_entry[pp->pp_end].pe_data;
++ prec.pe_status = pp->pp_entry[pp->pp_end].pe_status;
++ pp->pp_end = (pp->pp_end + 1) & (PA2_BUF_SZ - 1);
++ spin_unlock_irq(&sp->ps_lock);
++
++ if (copy_to_user(routp, &prec, sizeof(prec))) {
++ ret = -EFAULT;
++ break;
++ }
++ routp++;
++ phd.ph_rec_num++;
++ spin_lock_irq(&sp->ps_lock);
++ }
++ if (sp->ps_waiting_grab) {
++ phd.ph_focus = 1;
++ }
++ if (pp->pp_overflow) {
++ pp->pp_overflow = 0;
++ phd.ph_overflow = 1;
++ }
++
++ /*
++ * If no records found, and space in buffer for at least one record,
++ * then add a force record.
++ * If the status indicates there is data available, and it matches the
++ * device we are reading from, then read data.
++ */
++
++ if (phd.ph_rec_num == 0 && phd.ph_rec_size != 0 && ret == 0) {
++ /* fetch a forced record */
++ phd.ph_forced = 1;
++ prec.pe_data = 0;
++ prec.pe_status = pa2_status_read();
++ if (prec.pe_status & PA2_CTL_STAT_OBF) {
++ if (prec.pe_status & PA2_CTL_STAT_AUX_OBF) {
++ if (sp->ps_index == PA2_INDEX_KBD) {
++ prec.pe_status &= ~PA2_CTL_STAT_AUX_OBF;
++ prec.pe_status &= ~PA2_CTL_STAT_OBF;
++ } else {
++ prec.pe_data = pa2_data_read();
++ phd.ph_forced = 0;
++ }
++ } else {
++ if (sp->ps_index == PA2_INDEX_AUX) {
++ prec.pe_status &= ~PA2_CTL_STAT_OBF;
++ } else {
++ prec.pe_data = pa2_data_read();
++ phd.ph_forced = 0;
++ }
++ }
++ }
++ spin_unlock_irq(&sp->ps_lock);
++ if (copy_to_user(routp, &prec, sizeof(prec))) {
++ ret = -EFAULT;
++ } else {
++ phd.ph_rec_num++;
++ }
++ } else {
++ spin_unlock_irq(&sp->ps_lock);
++ }
++ if (copy_to_user(p, &phd, sizeof (phd))) {
++ ret = -EFAULT;
++ }
++
++out:
++ return ret;
++}
++
++static long
++pa2_ioctl_aux(
++ struct file *filp,
++ unsigned int cmd,
++ void __user *p)
++{
++ long ret;
++ pa2_data_t arg;
++
++ ret = 0;
++ switch (cmd) {
++ case PA2_IOCTL_WR_DATA:
++ /* open for writing XXX */
++
++ /* grabbed or initializing XXX */
++ if (copy_from_user(&arg, p, sizeof (arg))) {
++ return -EFAULT;
++ }
++ ret = pa2_data_write(filp, &arg, PA2_INDEX_AUX);
++ break;
++
++ case PA2_IOCTL_RD_RECORD:
++ ret = pa2_record_read(filp, p);
++ break;
++
++ case PA2_IOCTL_GRAB:
++ ret = pa2_grab(filp, p);
++ break;
++
++ default:
++ ret = -EINVAL;
++ break;
++ }
++ return ret;
++}
++
++static long
++pa2_ioctl_kbd(
++ struct file *filp,
++ unsigned int cmd,
++ void __user *p)
++{
++ long ret;
++ pa2_data_t arg;
++
++ ret = 0;
++ switch (cmd) {
++ case PA2_IOCTL_WR_DATA:
++ /* write directly from the port */
++ /* mask interrupts/spinlock? XXX */
++ if (copy_from_user(&arg, p, sizeof (arg))) {
++ return -EFAULT;
++ }
++ ret = pa2_data_write(filp, &arg, PA2_INDEX_KBD);
++ break;
++
++ case PA2_IOCTL_RD_RECORD:
++ ret = pa2_record_read(filp, p);
++ break;
++
++ case PA2_IOCTL_GRAB:
++ ret = pa2_grab(filp, p);
++ break;
++
++ default:
++ ret = -EINVAL;
++ break;
++ }
++ return ret;
++}
++
++static long
++pa2_ioctl_ctl(
++ struct file *filp,
++ unsigned int cmd,
++ void __user *p)
++{
++ pa2_state_t *sp;
++ long ret;
++ u8 byte;
++
++ sp = filp->private_data;
++ ASSERT(filp->private_data == &pa2_ctl);
++ ret = 0;
++ switch (cmd) {
++ case PA2_IOCTL_WR_CMD:
++ /* write access? XXX */
++ if (copy_from_user(&byte, p, 1)) {
++ return -EFAULT;
++ }
++ pa2_cmd_write(byte);
++ break;
++
++ case PA2_IOCTL_GRAB:
++ ret = pa2_grab(filp, p);
++ break;
++
++ case PA2_IOCTL_INIT_LOCK:
++ /*
++ * As the device cannot be grabbed until it has been
++ * initialized, XXX
++ */
++ if (copy_from_user(&byte, p, 1)) {
++ return -EFAULT;
++ }
++ if (byte == 0) {
++ if (pa2_init_taskp != current) {
++ pa2_error("bad unlock\n");
++ return -EINVAL;
++ }
++ pa2_init_taskp = NULL;
++ up(&pa2_init_sem);
++ return 0;
++ }
++ down(&pa2_init_sem);
++ /* take all spinlocks XXX */
++ pa2_init_taskp = current;
++ break;
++
++ case PA2_IOCTL_INIT_QUERY:
++ if (pa2_init_taskp != current) {
++ pa2_error("bad query\n");
++ ret = -EINVAL;
++ break;
++ }
++ if (copy_to_user(p, &pa2_init_buffer,
++ sizeof (pa2_init_buffer))) {
++ ret = -EFAULT;
++ break;
++ }
++ break;
++
++ case PA2_IOCTL_INIT_SET:
++ if (pa2_init_taskp != current) {
++ pa2_error("bad set\n");
++ ret = -EPERM;
++ break;
++ }
++ /* check if already initialized XXX */
++ if (copy_from_user(&pa2_init_buffer_tmp, p,
++ sizeof (pa2_init_buffer_tmp))) {
++ ret = -EFAULT;
++ break;
++ }
++ if (pa2_init_buffer_tmp.di_len > PA2_DATA_INIT_MAX) {
++ ret = -EINVAL;
++ break;
++ }
++ spin_lock_irq(&pa2_i8042_lock);
++ memcpy(&pa2_init_buffer, &pa2_init_buffer_tmp,
++ sizeof (pa2_init_buffer_tmp));
++ pa2_initialized = 1;
++ spin_unlock_irq(&pa2_i8042_lock);
++ break;
++
++ case PA2_IOCTL_INIT_CMD:
++ if (copy_from_user(&byte, p, 1)) {
++ ret = -EFAULT;
++ break;
++ }
++ if (byte == 1) {
++ PRINT_DEBUG("Taking init cmd: %p\n", current);
++ if (down_interruptible(&pa2_cmd_sem)) {
++ ret = -ERESTARTSYS;
++ break;
++ }
++ pa2_cmd_filp = filp;
++ break;
++ }
++ PRINT_DEBUG("Releasing init_cmd: %p\n", current);
++ pa2_cmd_filp = NULL;
++ up(&pa2_cmd_sem);
++ break;
++
++ default:
++ ret = -EINVAL;
++ break;
++ }
++ return ret;
++}
++
++static long
++pa2_ioctl(
++ struct file *filp,
++ unsigned int cmd,
++ unsigned long arg)
++{
++ pa2_state_t *sp;
++ long ret;
++
++ sp = filp->private_data;
++ switch (sp->ps_index) {
++ case PA2_INDEX_KBD:
++ ret = pa2_ioctl_kbd(filp, cmd, (void __user *)arg);
++ break;
++
++ case PA2_INDEX_AUX:
++ ret = pa2_ioctl_aux(filp, cmd, (void __user *)arg);
++ break;
++
++ case PA2_INDEX_CTL:
++ ret = pa2_ioctl_ctl(filp, cmd, (void __user *)arg);
++ break;
++
++ default:
++ ret = -EINVAL;
++ break;
++ }
++ return ret;
++}
++
++static unsigned int
++pa2_poll(
++ struct file *filp,
++ poll_table *wait)
++{
++ pa2_state_t *sp;
++ pa2_ps_t *pp;
++ unsigned int mask;
++
++ /*
++ * Should never poll the ctl.
++ */
++
++ sp = filp->private_data;
++ if (sp->ps_index != PA2_INDEX_KBD && sp->ps_index != PA2_INDEX_AUX) {
++ ASSERT(0);
++ return POLLOUT | POLLWRNORM | POLLIN | POLLRDNORM;
++ }
++ poll_wait(filp, &sp->ps_wait_poll, wait);
++ pp = sp->ps_queue;
++ mask = POLLOUT | POLLWRNORM;
++ spin_lock_irq(&sp->ps_lock);
++ if (pp->pp_start != pp->pp_end || sp->ps_waiting_grab) {
++ mask |= POLLIN | POLLRDNORM;
++ }
++ spin_unlock_irq(&sp->ps_lock);
++ return mask;
++}
++
++static struct file_operations pa2_file_ops = {
++ .owner = THIS_MODULE,
++ .open = pa2_open,
++ .unlocked_ioctl = pa2_ioctl,
++ .poll = pa2_poll,
++ .release = pa2_release,
++};
++
++static void __exit
++pa2_deinit(
++ void)
++{
++ unsigned int i;
++
++ /*
++ * Should place 8042 into a safe state; KBD and AUX devices disabled,
++ * and interrupts disabled too.
++ */
++
++ (void) pa2_ctl_mode_write_do(PA2_CTL_MODE_KCC);
++ for (i = 0; i < 3; i++) {
++ if (pa2_proc_dev[i] != NULL) {
++ remove_proc_entry(pa2_path[i], NULL);
++ pa2_proc_dev[i] = NULL;
++ }
++ }
++ free_irq(PA2_KBD_IRQ, NULL);
++ free_irq(PA2_AUX_IRQ, NULL);
++ return;
++}
++
++static int __init
++pa2_init(
++ void)
++{
++ pa2_state_t *sp;
++ unsigned int i;
++ int ret;
++
++ /*
++ * Grab keyboard and aux interrupts.
++ */
++
++ init_MUTEX(&pa2_init_sem);
++ for (i = 0; i < 3; i++) {
++ pa2_proc_dev[i] = create_proc_entry(pa2_path[i], 0644, NULL);
++ if (pa2_proc_dev[i] != NULL) {
++ pa2_proc_dev[i]->proc_fops = &pa2_file_ops;
++ }
++ }
++
++ sp = &pa2_state[0];
++ for (i = 0; i < 2; i++, sp++) {
++ spin_lock_init(&sp->ps_lock);
++ sp->ps_queue = &pa2_queue[i];
++ sp->ps_index = i;
++ init_waitqueue_head(&sp->ps_wait_grab);
++ init_waitqueue_head(&sp->ps_wait_poll);
++ }
++ spin_lock_init(&pa2_ctl.ps_lock);
++ pa2_ctl.ps_index = PA2_INDEX_CTL;
++
++ PRINT_DEBUG("Requesting interrupts...\n");
++ ret = request_irq(PA2_KBD_IRQ, pa2_irq, 0, "kbd", NULL);
++ if (ret != 0) {
++ pa2_error("unable to get keyboard interrupt\n");
++ /* remove procs XXX */
++ goto out;
++ }
++ ret = request_irq(PA2_AUX_IRQ, pa2_irq, 0, "aux", NULL);
++ if (ret != 0) {
++ pa2_error("unable to get aux interrupt\n");
++ /* remove procs XXX */
++ free_irq(PA2_KBD_IRQ, NULL); /* ugh, NULL!!! */
++ goto out;
++ }
++
++ (void) pa2_ctl_mode_write_do(PA2_CTL_MODE_KCC);
++
++out:
++ return ret;
++}
++
++module_init(pa2_init);
++module_exit(pa2_deinit);
++
++MODULE_LICENSE("Dual BSD/GPL");