From 8131120cacd0bec6523e5ef58ead5edb16f162e9 Mon Sep 17 00:00:00 2001
From: Mario Limonciello <superm1@ubuntu.com>
Date: Fri, 9 Oct 2009 22:55:56 -0500
Subject: [PATCH] UBUNTU: Update LIRC to 0.8.6 release

 OriginalLocation: www.lirc.org
 BugLink: http://bugs.launchpad.net/bugs/432678
 Ignore: no

Signed-off-by: Mario Limonciello <superm1@ubuntu.com>
---
 debian.master/config/config.common.ports        |    2 +-
 debian.master/config/config.common.ubuntu       |    2 +-
 ubuntu/LIST                                     |    2 +-
 ubuntu/lirc/Kconfig                             |   10 +-
 ubuntu/lirc/Makefile                            |    2 +-
 ubuntu/lirc/lirc.h                              |    9 +-
 ubuntu/lirc/lirc_dev/lirc_dev.c                 |   44 +-
 ubuntu/lirc/lirc_ene0100/Makefile               |    3 +
 ubuntu/lirc/lirc_ene0100/lirc_ene0100.c         |  653 +++++++++
 ubuntu/lirc/lirc_ene0100/lirc_ene0100.h         |  170 +++
 ubuntu/lirc/lirc_i2c/lirc_i2c.c                 |  254 ++++-
 ubuntu/lirc/lirc_igorplugusb/lirc_igorplugusb.c |   61 +-
 ubuntu/lirc/lirc_imon/lirc_imon.c               | 1747 +++++++++++++++-------
 ubuntu/lirc/lirc_it87/lirc_it87.c               |   20 +-
 ubuntu/lirc/lirc_mceusb/lirc_mceusb.c           | 1770 ++++++++++++++---------
 ubuntu/lirc/lirc_sir/lirc_sir.c                 |    8 +
 ubuntu/lirc/lirc_wpc8769l/lirc_wpc8769l.h       |   52 +-
 17 files changed, 3484 insertions(+), 1325 deletions(-)
 create mode 100644 ubuntu/lirc/lirc_ene0100/Makefile
 create mode 100644 ubuntu/lirc/lirc_ene0100/lirc_ene0100.c
 create mode 100644 ubuntu/lirc/lirc_ene0100/lirc_ene0100.h

diff --git a/debian.master/config/config.common.ports b/debian.master/config/config.common.ports
index 429bcbd..6ffb81a 100644
--- a/debian.master/config/config.common.ports
+++ b/debian.master/config/config.common.ports
@@ -1585,6 +1585,7 @@ CONFIG_LIBIPW=m
 # CONFIG_LIBIPW_DEBUG is not set
 CONFIG_LIRC_ATIUSB=m
 CONFIG_LIRC_BT829=m
+CONFIG_LIRC_ENE0100=m
 CONFIG_LIRC_CONFIG_LIRC_WPC8769L=m
 CONFIG_LIRC_DEV=m
 # CONFIG_LIRC_GPIO is not set
@@ -1594,7 +1595,6 @@ CONFIG_LIRC_IMON=m
 CONFIG_LIRC_IT87=m
 CONFIG_LIRC_ITE8709=m
 CONFIG_LIRC_MCEUSB=m
-CONFIG_LIRC_MCEUSB2=m
 # CONFIG_LIRC_PARALLEL is not set
 CONFIG_LIRC_SASEM=m
 CONFIG_LIRC_SERIAL=m
diff --git a/debian.master/config/config.common.ubuntu b/debian.master/config/config.common.ubuntu
index a43cbab..53f0183 100644
--- a/debian.master/config/config.common.ubuntu
+++ b/debian.master/config/config.common.ubuntu
@@ -1892,6 +1892,7 @@ CONFIG_LIBIPW_DEBUG=y
 CONFIG_LINE6_USB=m
 CONFIG_LIRC_ATIUSB=m
 CONFIG_LIRC_BT829=m
+CONFIG_LIRC_ENE0100=m
 CONFIG_LIRC_CONFIG_LIRC_WPC8769L=m
 CONFIG_LIRC_DEV=m
 # CONFIG_LIRC_GPIO is not set
@@ -1901,7 +1902,6 @@ CONFIG_LIRC_IMON=m
 CONFIG_LIRC_IT87=m
 CONFIG_LIRC_ITE8709=m
 CONFIG_LIRC_MCEUSB=m
-CONFIG_LIRC_MCEUSB2=m
 # CONFIG_LIRC_PARALLEL is not set
 CONFIG_LIRC_SASEM=m
 CONFIG_LIRC_SERIAL=m
diff --git a/ubuntu/LIST b/ubuntu/LIST
index 126c711..85b8099 100644
--- a/ubuntu/LIST
+++ b/ubuntu/LIST
@@ -23,6 +23,7 @@ FSAM7400
 LENOVO_SL_LAPTOP
 LIRC_ATIUSB
 LIRC_BT829
+LIRC_ENE0100
 LIRC_CONFIG_LIRC_WPC8769L
 LIRC_DEV
 LIRC_GPIO
@@ -32,7 +33,6 @@ LIRC_IMON
 LIRC_IT87
 LIRC_ITE8709
 LIRC_MCEUSB
-LIRC_MCEUSB2
 LIRC_PARALLEL
 LIRC_SASEM
 LIRC_SERIAL
diff --git a/ubuntu/lirc/Kconfig b/ubuntu/lirc/Kconfig
index f709f78..21dcc40 100644
--- a/ubuntu/lirc/Kconfig
+++ b/ubuntu/lirc/Kconfig
@@ -12,6 +12,11 @@ config LIRC_BT829
 	default m
 	depends on LIRC_DEV
 
+config LIRC_ENE0100
+	tristate "LIRC ENE0100"
+	default m
+	depends on LIRC_DEV
+
 config LIRC_I2C
 	tristate "LIRC I2C interface remote"
 	default m
@@ -42,11 +47,6 @@ config LIRC_MCEUSB
 	default m
 	depends on LIRC_DEV
 
-config LIRC_MCEUSB2
-	tristate "LIRC Microsoft Media Center Remote v2"
-	default m
-	depends on LIRC_DEV
-
 config LIRC_PARALLEL
 	tristate "LIRC Parallel port custom remote"
 	default n
diff --git a/ubuntu/lirc/Makefile b/ubuntu/lirc/Makefile
index ca80518..006a6c8 100644
--- a/ubuntu/lirc/Makefile
+++ b/ubuntu/lirc/Makefile
@@ -5,13 +5,13 @@ EXTRA_CFLAGS =-DIRCTL_DEV_MAJOR=61 -DLIRC_SERIAL_TRANSMITTER -DLIRC_SERIAL_SOFTC
 obj-$(CONFIG_LIRC_DEV)		+= lirc_dev/
 obj-$(CONFIG_LIRC_ATIUSB)	+= lirc_atiusb/
 obj-$(CONFIG_LIRC_BT829)	+= lirc_bt829/
+obj-$(CONFIG_LIRC_ENE0100)	+= lirc_ene0100/
 obj-$(CONFIG_LIRC_I2C)		+= lirc_i2c/
 obj-$(CONFIG_LIRC_IGORPLUGUSB)	+= lirc_igorplugusb/
 obj-$(CONFIG_LIRC_IMON)		+= lirc_imon/
 obj-$(CONFIG_LIRC_IT87)		+= lirc_it87/
 obj-$(CONFIG_LIRC_ITE8709)	+= lirc_ite8709/
 obj-$(CONFIG_LIRC_MCEUSB)	+= lirc_mceusb/
-obj-$(CONFIG_LIRC_MCEUSB2)	+= lirc_mceusb2/
 obj-$(CONFIG_LIRC_PARALLEL)	+= lirc_parallel/
 obj-$(CONFIG_LIRC_SASEM)	+= lirc_sasem/
 obj-$(CONFIG_LIRC_SERIAL)	+= lirc_serial/
diff --git a/ubuntu/lirc/lirc.h b/ubuntu/lirc/lirc.h
index 20495e6..7c4d09e 100644
--- a/ubuntu/lirc/lirc.h
+++ b/ubuntu/lirc/lirc.h
@@ -1,15 +1,16 @@
-/*      $Id: lirc.h,v 5.18 2009/01/30 19:39:26 lirc Exp $      */
+/*      $Id: lirc.h,v 5.19 2009/08/29 07:52:41 lirc Exp $      */
 
 #ifndef _LINUX_LIRC_H
 #define _LINUX_LIRC_H
 
 #if defined(__linux__)
 #include <linux/ioctl.h>
-#else
-#if defined(__NetBSD__)
+#elif defined(_NetBSD_)
+#include <sys/ioctl.h> 
+#elif defined(_CYGWIN_)
+#define __USE_LINUX_IOCTL_DEFS
 #include <sys/ioctl.h>
 #endif
-#endif
 
 #define PULSE_BIT  0x01000000
 #define PULSE_MASK 0x00FFFFFF
diff --git a/ubuntu/lirc/lirc_dev/lirc_dev.c b/ubuntu/lirc/lirc_dev/lirc_dev.c
index 4e1e380..7618c0a 100644
--- a/ubuntu/lirc/lirc_dev/lirc_dev.c
+++ b/ubuntu/lirc/lirc_dev/lirc_dev.c
@@ -17,7 +17,7 @@
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *
- * $Id: lirc_dev.c,v 1.93 2009/03/29 10:55:31 lirc Exp $
+ * $Id: lirc_dev.c,v 1.96 2009/08/31 16:57:55 lirc Exp $
  *
  */
 
@@ -106,7 +106,7 @@ struct irctl {
 #endif
 };
 
-static DEFINE_MUTEX(driver_lock);
+static DEFINE_MUTEX(lirc_dev_lock);
 
 static struct irctl *irctls[MAX_IRCTL_DEVICES];
 static struct file_operations fops;
@@ -307,14 +307,7 @@ int lirc_register_driver(struct lirc_driver *d)
 		}
 	}
 
-	if (d->owner == NULL) {
-		printk(KERN_ERR "lirc_dev: lirc_register_driver: "
-				    "no module owner registered\n");
-		err = -EBADRQC;
-		goto out;
-	}
-
-	mutex_lock(&driver_lock);
+	mutex_lock(&lirc_dev_lock);
 
 	minor = d->minor;
 
@@ -421,7 +414,7 @@ int lirc_register_driver(struct lirc_driver *d)
 #endif
 	}
 	ir->attached = 1;
-	mutex_unlock(&driver_lock);
+	mutex_unlock(&lirc_dev_lock);
 
 /*
  * Recent kernels should handle this autmatically by increasing/decreasing
@@ -445,7 +438,7 @@ out_sysfs:
 	devfs_remove(DEV_LIRC "/%i", ir->d.minor);
 #endif
 out_lock:
-	mutex_unlock(&driver_lock);
+	mutex_unlock(&lirc_dev_lock);
 out:
 	return err;
 }
@@ -468,12 +461,12 @@ int lirc_unregister_driver(int minor)
 
 	ir = irctls[minor];
 
-	mutex_lock(&driver_lock);
+	mutex_lock(&lirc_dev_lock);
 
 	if (ir->d.minor != minor) {
 		printk(KERN_ERR "lirc_dev: lirc_unregister_driver: "
 		       "minor (%d) device not registered!", minor);
-		mutex_unlock(&driver_lock);
+		mutex_unlock(&lirc_dev_lock);
 		return -ENOENT;
 	}
 
@@ -531,7 +524,7 @@ int lirc_unregister_driver(int minor)
 	lirc_device_destroy(lirc_class,
 			    MKDEV(IRCTL_DEV_MAJOR, ir->d.minor));
 
-	mutex_unlock(&driver_lock);
+	mutex_unlock(&lirc_dev_lock);
 
 /*
  * Recent kernels should handle this autmatically by increasing/decreasing
@@ -564,7 +557,7 @@ static int irctl_open(struct inode *inode, struct file *file)
 	if (ir->d.fops && ir->d.fops->open)
 		return ir->d.fops->open(inode, file);
 
-	if (mutex_lock_interruptible(&driver_lock))
+	if (mutex_lock_interruptible(&lirc_dev_lock))
 		return -ERESTARTSYS;
 
 	if (ir->d.minor == NOPLUG) {
@@ -577,7 +570,7 @@ static int irctl_open(struct inode *inode, struct file *file)
 		goto error;
 	}
 
-	if (ir->d.owner != NULL && try_module_get(ir->d.owner)) {
+	if (try_module_get(ir->d.owner)) {
 		++ir->open;
 		retval = ir->d.set_use_inc(ir->d.data);
 
@@ -598,20 +591,13 @@ static int irctl_open(struct inode *inode, struct file *file)
 		if (ir->task)
 			wake_up_process(ir->task);
 #endif
-	} else {
-		if (ir->d.owner == NULL)
-			dprintk(LOGHEAD "no module owner!!!\n",
-				ir->d.name, ir->d.minor);
-
-		retval = -ENODEV;
 	}
-
  error:
 	if (ir)
 		dprintk(LOGHEAD "open result = %d\n", ir->d.name, ir->d.minor,
 			retval);
 
-	mutex_unlock(&driver_lock);
+	mutex_unlock(&lirc_dev_lock);
 
 	return retval;
 }
@@ -626,7 +612,7 @@ static int irctl_close(struct inode *inode, struct file *file)
 	if (ir->d.fops && ir->d.fops->release)
 		return ir->d.fops->release(inode, file);
 
-	if (mutex_lock_interruptible(&driver_lock))
+	if (mutex_lock_interruptible(&lirc_dev_lock))
 		return -ERESTARTSYS;
 
 	--ir->open;
@@ -639,7 +625,7 @@ static int irctl_close(struct inode *inode, struct file *file)
 		kfree(ir);
 	}
 
-	mutex_unlock(&driver_lock);
+	mutex_unlock(&lirc_dev_lock);
 
 	return 0;
 }
@@ -784,7 +770,7 @@ static long irctl_compat_ioctl(struct file *file,
 
 		cmd = _IOC(_IOC_DIR(cmd32), _IOC_TYPE(cmd32), _IOC_NR(cmd32),
 			   (_IOC_TYPECHECK(unsigned long)));
-		ret = irctl_ioctl(file->f_path.dentry->d_inode, file,
+		ret = irctl_ioctl(file->f_dentry->d_inode, file,
 				  cmd, (unsigned long)(&val));
 
 		set_fs(old_fs);
@@ -818,7 +804,7 @@ static long irctl_compat_ioctl(struct file *file,
 		 */
 		lock_kernel();
 		cmd = cmd32;
-		ret = irctl_ioctl(file->f_path.dentry->d_inode,
+		ret = irctl_ioctl(file->f_dentry->d_inode,
 				  file, cmd, arg);
 		unlock_kernel();
 		return ret;
diff --git a/ubuntu/lirc/lirc_ene0100/Makefile b/ubuntu/lirc/lirc_ene0100/Makefile
new file mode 100644
index 0000000..b5f2e6f
--- /dev/null
+++ b/ubuntu/lirc/lirc_ene0100/Makefile
@@ -0,0 +1,3 @@
+EXTRA_CFLAGS =-DIRCTL_DEV_MAJOR=61 -DLIRC_SERIAL_TRANSMITTER -DLIRC_SERIAL_SOFTCARRIER -I$(src)/..
+
+obj-$(CONFIG_LIRC_ENE0100)		+= lirc_ene0100.o
diff --git a/ubuntu/lirc/lirc_ene0100/lirc_ene0100.c b/ubuntu/lirc/lirc_ene0100/lirc_ene0100.c
new file mode 100644
index 0000000..da9519f
--- /dev/null
+++ b/ubuntu/lirc/lirc_ene0100/lirc_ene0100.c
@@ -0,0 +1,653 @@
+/*
+ * driver for ENE KB3926 B/C/D CIR (also known as ENE0100)
+ *
+ * Copyright (C) 2009 Maxim Levitsky <maximlevitsky@gmail.com>
+ *
+ * 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.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pnp.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include "lirc_ene0100.h"
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 16)
+#error "Sorry, this driver needs kernel version 2.6.16 or higher"
+#else
+
+static int sample_period = 75;
+static int enable_idle = 1;
+static int enable_learning;
+
+static void ene_set_idle(struct ene_device *dev, int idle);
+static void ene_set_inputs(struct ene_device *dev, int enable);
+
+/* read a hardware register */
+static u8 ene_hw_read_reg(struct ene_device *dev, u16 reg)
+{
+	outb(reg >> 8, dev->hw_io + ENE_ADDR_HI);
+	outb(reg & 0xFF, dev->hw_io + ENE_ADDR_LO);
+	return inb(dev->hw_io + ENE_IO);
+}
+
+/* write a hardware register */
+static void ene_hw_write_reg(struct ene_device *dev, u16 reg, u8 value)
+{
+	outb(reg >> 8, dev->hw_io + ENE_ADDR_HI);
+	outb(reg & 0xFF, dev->hw_io + ENE_ADDR_LO);
+	outb(value, dev->hw_io + ENE_IO);
+}
+
+/* change specific bits in hardware register */
+static void ene_hw_write_reg_mask(struct ene_device *dev,
+				  u16 reg, u8 value, u8 mask)
+{
+	u8 regvalue;
+
+	outb(reg >> 8, dev->hw_io + ENE_ADDR_HI);
+	outb(reg & 0xFF, dev->hw_io + ENE_ADDR_LO);
+
+	regvalue = inb(dev->hw_io + ENE_IO) & ~mask;
+	regvalue |= (value & mask);
+	outb(regvalue, dev->hw_io + ENE_IO);
+}
+
+/* read irq status and ack it */
+static int ene_hw_irq_status(struct ene_device *dev, int *buffer_pointer)
+{
+	u8 irq_status;
+	u8 fw_flags1, fw_flags2;
+
+	fw_flags2 = ene_hw_read_reg(dev, ENE_FW2);
+
+	if (buffer_pointer)
+		*buffer_pointer = 4 * (fw_flags2 & ENE_FW2_BUF_HIGH);
+
+	if (dev->hw_revision < ENE_HW_C) {
+		irq_status = ene_hw_read_reg(dev, ENEB_IRQ_STATUS);
+
+		if (!irq_status & ENEB_IRQ_STATUS_IR)
+			return 0;
+		ene_hw_write_reg(dev, ENEB_IRQ_STATUS,
+				 irq_status & ~ENEB_IRQ_STATUS_IR);
+
+		/* rev B support only recieving */
+		return ENE_IRQ_RX;
+	}
+
+	irq_status = ene_hw_read_reg(dev, ENEC_IRQ);
+
+	if (!irq_status && ENEC_IRQ_STATUS)
+		return 0;
+
+	/* original driver does that twice - a workaround ? */
+	ene_hw_write_reg(dev, ENEC_IRQ, irq_status & ~ENEC_IRQ_STATUS);
+	ene_hw_write_reg(dev, ENEC_IRQ, irq_status & ~ENEC_IRQ_STATUS);
+
+	/* clear unknown flag in F8F9 */
+	if (fw_flags2 & ENE_FW2_IRQ_CLR)
+		ene_hw_write_reg(dev, ENE_FW2, fw_flags2 & ~ENE_FW2_IRQ_CLR);
+
+	/* check if this is a TX interrupt */
+	fw_flags1 = ene_hw_read_reg(dev, ENE_FW1);
+
+	if (fw_flags1 & ENE_FW1_TXIRQ) {
+		ene_hw_write_reg(dev, ENE_FW1, fw_flags1 & ~ENE_FW1_TXIRQ);
+		return ENE_IRQ_TX;
+	} else
+		return ENE_IRQ_RX;
+}
+
+static int ene_hw_detect(struct ene_device *dev)
+{
+	u8 chip_major, chip_minor;
+	u8 hw_revision, old_ver;
+	u8 tmp;
+	u8 fw_capabilities;
+
+	tmp = ene_hw_read_reg(dev, ENE_HW_UNK);
+	ene_hw_write_reg(dev, ENE_HW_UNK, tmp & ~ENE_HW_UNK_CLR);
+
+	chip_major = ene_hw_read_reg(dev, ENE_HW_VER_MAJOR);
+	chip_minor = ene_hw_read_reg(dev, ENE_HW_VER_MINOR);
+
+	ene_hw_write_reg(dev, ENE_HW_UNK, tmp);
+	hw_revision = ene_hw_read_reg(dev, ENE_HW_VERSION);
+	old_ver = ene_hw_read_reg(dev, ENE_HW_VER_OLD);
+
+	if (hw_revision == 0xFF) {
+
+		ene_printk(KERN_WARNING, "device seems to be disabled\n");
+		ene_printk(KERN_WARNING,
+			"send a mail to lirc-list@lists.sourceforge.net\n");
+		ene_printk(KERN_WARNING, "please attach output of acpidump\n");
+
+		return -ENODEV;
+	}
+
+	if (chip_major == 0x33) {
+		ene_printk(KERN_WARNING, "chips 0x33xx aren't supported yet\n");
+		return -ENODEV;
+	}
+
+	if (chip_major == 0x39 && chip_minor == 0x26 && hw_revision == 0xC0) {
+		dev->hw_revision = ENE_HW_C;
+		ene_printk(KERN_WARNING,
+		       "KB3926C detected, driver support is not complete!\n");
+
+	} else if (old_ver == 0x24 && hw_revision == 0xC0) {
+		dev->hw_revision = ENE_HW_B;
+		ene_printk(KERN_NOTICE, "KB3926B detected\n");
+	} else {
+		dev->hw_revision = ENE_HW_D;
+		ene_printk(KERN_WARNING,
+			"unknown ENE chip detected, assuming KB3926D\n");
+		ene_printk(KERN_WARNING, "driver support incomplete");
+
+	}
+
+	ene_printk(KERN_DEBUG, "chip is 0x%02x%02x - 0x%02x, 0x%02x\n",
+		chip_major, chip_minor, old_ver, hw_revision);
+
+
+	/* detect features hardware supports */
+
+	if (dev->hw_revision < ENE_HW_C)
+		return 0;
+
+	fw_capabilities = ene_hw_read_reg(dev, ENE_FW2);
+
+	dev->hw_gpio40_learning = fw_capabilities & ENE_FW2_GP40_AS_LEARN;
+	dev->hw_learning_and_tx_capable = fw_capabilities & ENE_FW2_LEARNING;
+
+	dev->hw_fan_as_normal_input = dev->hw_learning_and_tx_capable &&
+	    fw_capabilities & ENE_FW2_FAN_AS_NRML_IN;
+
+	ene_printk(KERN_NOTICE, "hardware features:\n");
+	ene_printk(KERN_NOTICE,
+		"learning and tx %s, gpio40_learn %s, fan_in %s\n",
+	       dev->hw_learning_and_tx_capable ? "on" : "off",
+	       dev->hw_gpio40_learning ? "on" : "off",
+	       dev->hw_fan_as_normal_input ? "on" : "off");
+
+	if (!dev->hw_learning_and_tx_capable && enable_learning)
+		enable_learning = 0;
+
+	if (dev->hw_learning_and_tx_capable) {
+		ene_printk(KERN_WARNING,
+		"Device supports transmitting, but the driver doesn't\n");
+		ene_printk(KERN_WARNING,
+		"due to lack of hardware to test against.\n");
+		ene_printk(KERN_WARNING,
+		"Send a mail to: lirc-list@lists.sourceforge.net\n");
+	}
+	return 0;
+}
+
+/* hardware initialization */
+static int ene_hw_init(void *data)
+{
+	u8 reg_value;
+	struct ene_device *dev = (struct ene_device *)data;
+	dev->in_use = 1;
+
+	if (dev->hw_revision < ENE_HW_C) {
+		ene_hw_write_reg(dev, ENEB_IRQ, dev->irq << 1);
+		ene_hw_write_reg(dev, ENEB_IRQ_UNK1, 0x01);
+	} else {
+		reg_value = ene_hw_read_reg(dev, ENEC_IRQ) & 0xF0;
+		reg_value |= ENEC_IRQ_UNK_EN;
+		reg_value &= ~ENEC_IRQ_STATUS;
+		reg_value |= (dev->irq & ENEC_IRQ_MASK);
+		ene_hw_write_reg(dev, ENEC_IRQ, reg_value);
+		ene_hw_write_reg(dev, ENE_TX_UNK1, 0x63);
+	}
+
+	ene_hw_write_reg(dev, ENE_CIR_CONF2, 0x00);
+	ene_set_inputs(dev, enable_learning);
+
+	/* set sampling period */
+	ene_hw_write_reg(dev, ENE_CIR_SAMPLE_PERIOD, sample_period);
+
+	/* ack any pending irqs - just in case */
+	ene_hw_irq_status(dev, NULL);
+
+	/* enter idle mode */
+	ene_set_idle(dev, 1);
+
+	/* enable firmware bits */
+	ene_hw_write_reg_mask(dev, ENE_FW1,
+			      ENE_FW1_ENABLE | ENE_FW1_IRQ,
+			      ENE_FW1_ENABLE | ENE_FW1_IRQ);
+	/* clear stats */
+	dev->sample = 0;
+	return 0;
+}
+
+/* this enables gpio40 signal, used if connected to wide band input*/
+static void ene_enable_gpio40(struct ene_device *dev, int enable)
+{
+	ene_hw_write_reg_mask(dev, ENE_CIR_CONF1, enable ?
+			      0 : ENE_CIR_CONF2_GPIO40DIS,
+			      ENE_CIR_CONF2_GPIO40DIS);
+}
+
+/* this enables the classic sampler */
+static void ene_enable_normal_recieve(struct ene_device *dev, int enable)
+{
+	ene_hw_write_reg(dev, ENE_CIR_CONF1, enable ? ENE_CIR_CONF1_ADC_ON : 0);
+}
+
+/* this enables recieve via fan input */
+static void ene_enable_fan_recieve(struct ene_device *dev, int enable)
+{
+	if (!enable)
+		ene_hw_write_reg(dev, ENE_FAN_AS_IN1, 0);
+	else {
+		ene_hw_write_reg(dev, ENE_FAN_AS_IN1, ENE_FAN_AS_IN1_EN);
+		ene_hw_write_reg(dev, ENE_FAN_AS_IN2, ENE_FAN_AS_IN2_EN);
+	}
+	dev->fan_input_inuse = enable;
+}
+
+/* determine which input to use*/
+static void ene_set_inputs(struct ene_device *dev, int learning_enable)
+{
+	ene_enable_normal_recieve(dev, 1);
+
+	/* old hardware doesn't support learning mode for sure */
+	if (dev->hw_revision <= ENE_HW_B)
+		return;
+
+	/* reciever not learning capable, still set gpio40 correctly */
+	if (!dev->hw_learning_and_tx_capable) {
+		ene_enable_gpio40(dev, !dev->hw_gpio40_learning);
+		return;
+	}
+
+	/* enable learning mode */
+	if (learning_enable) {
+		ene_enable_gpio40(dev, dev->hw_gpio40_learning);
+
+		/* fan input is not used for learning */
+		if (dev->hw_fan_as_normal_input)
+			ene_enable_fan_recieve(dev, 0);
+
+	/* disable learning mode */
+	} else {
+		if (dev->hw_fan_as_normal_input) {
+			ene_enable_fan_recieve(dev, 1);
+			ene_enable_normal_recieve(dev, 0);
+		} else
+			ene_enable_gpio40(dev, !dev->hw_gpio40_learning);
+	}
+
+	/* set few additional settings for this mode */
+	ene_hw_write_reg_mask(dev, ENE_CIR_CONF1, learning_enable ?
+			      ENE_CIR_CONF1_LEARN1 : 0, ENE_CIR_CONF1_LEARN1);
+
+	ene_hw_write_reg_mask(dev, ENE_CIR_CONF2, learning_enable ?
+			      ENE_CIR_CONF2_LEARN2 : 0, ENE_CIR_CONF2_LEARN2);
+}
+
+/* deinitialization */
+static void ene_hw_deinit(void *data)
+{
+	struct ene_device *dev = (struct ene_device *)data;
+
+	/* disable samplers */
+	ene_enable_normal_recieve(dev, 0);
+
+	if (dev->hw_fan_as_normal_input)
+		ene_enable_fan_recieve(dev, 0);
+
+	/* disable hardware IRQ and firmware flag */
+	ene_hw_write_reg_mask(dev, ENE_FW1, 0, ENE_FW1_ENABLE | ENE_FW1_IRQ);
+
+	ene_set_idle(dev, 1);
+	dev->in_use = 0;
+}
+
+/*  sends current sample to userspace */
+static void send_sample(struct ene_device *dev)
+{
+	int value = abs(dev->sample) & PULSE_MASK;
+
+	if (dev->sample > 0)
+		value |= PULSE_BIT;
+
+	if (!lirc_buffer_full(dev->lirc_driver->rbuf)) {
+		lirc_buffer_write(dev->lirc_driver->rbuf, (void *)&value);
+		wake_up(&dev->lirc_driver->rbuf->wait_poll);
+	}
+	dev->sample = 0;
+}
+
+/*  this updates current sample */
+static void update_sample(struct ene_device *dev, int sample)
+{
+	if (!dev->sample)
+		dev->sample = sample;
+	else if (same_sign(dev->sample, sample))
+		dev->sample += sample;
+	else {
+		send_sample(dev);
+		dev->sample = sample;
+	}
+}
+
+/* enable or disable idle mode */
+static void ene_set_idle(struct ene_device *dev, int idle)
+{
+	struct timeval now;
+	int disable = idle && enable_idle && (dev->hw_revision < ENE_HW_C);
+
+	ene_hw_write_reg_mask(dev, ENE_CIR_SAMPLE_PERIOD,
+			      disable ? 0 : ENE_CIR_SAMPLE_OVERFLOW,
+			      ENE_CIR_SAMPLE_OVERFLOW);
+	dev->idle = idle;
+
+	/* remember when we have entered the idle mode */
+	if (idle) {
+		do_gettimeofday(&dev->gap_start);
+		return;
+	}
+
+	/* send the gap between keypresses now */
+	do_gettimeofday(&now);
+
+	if (now.tv_sec - dev->gap_start.tv_sec > 16)
+		dev->sample = space(PULSE_MASK);
+	else
+		dev->sample = dev->sample +
+		    space(1000000ull * (now.tv_sec - dev->gap_start.tv_sec))
+		    + space(now.tv_usec - dev->gap_start.tv_usec);
+
+	if (abs(dev->sample) > PULSE_MASK)
+		dev->sample = space(PULSE_MASK);
+	send_sample(dev);
+}
+
+/* interrupt handler */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
+static irqreturn_t ene_hw_irq(int irq, void *data)
+#else
+static irqreturn_t ene_hw_irq(int irq, void *data, struct pt_regs *regs)
+#endif
+{
+	u16 hw_value;
+	int i, hw_sample;
+	int space;
+	int buffer_pointer;
+	int irq_status;
+
+	struct ene_device *dev = (struct ene_device *)data;
+	irq_status = ene_hw_irq_status(dev, &buffer_pointer);
+
+	if (!irq_status)
+		return IRQ_NONE;
+
+	/* TODO: only RX for now */
+	if (irq_status == ENE_IRQ_TX)
+		return IRQ_HANDLED;
+
+	for (i = 0; i < ENE_SAMPLES_SIZE; i++) {
+
+		hw_value = ene_hw_read_reg(dev,
+				ENE_SAMPLE_BUFFER + buffer_pointer + i);
+
+		if (dev->fan_input_inuse) {
+			/* read high part of the sample */
+			hw_value |= ene_hw_read_reg(dev,
+			    ENE_SAMPLE_BUFFER_FAN + buffer_pointer + i) << 8;
+
+			/* test for _space_ bit */
+			space = !(hw_value & ENE_FAN_SMPL_PULS_MSK);
+
+			/* clear space bit, and other unused bits */
+			hw_value &= ENE_FAN_VALUE_MASK;
+			hw_sample = hw_value * ENE_SAMPLE_PERIOD_FAN;
+
+		} else {
+			space = hw_value & ENE_SAMPLE_SPC_MASK;
+			hw_value &= ENE_SAMPLE_VALUE_MASK;
+			hw_sample = hw_value * sample_period;
+		}
+
+		/* no more data */
+		if (!(hw_value))
+			break;
+
+		if (space)
+			hw_sample *= -1;
+
+		/* overflow sample recieved, handle it */
+
+		if (!dev->fan_input_inuse && hw_value == ENE_SAMPLE_OVERFLOW) {
+
+			if (dev->idle)
+				continue;
+
+			if (dev->sample > 0 || abs(dev->sample) <= ENE_MAXGAP)
+				update_sample(dev, hw_sample);
+			else
+				ene_set_idle(dev, 1);
+
+			continue;
+		}
+
+		/* normal first sample recieved */
+		if (!dev->fan_input_inuse && dev->idle) {
+			ene_set_idle(dev, 0);
+
+			/* discard first recieved value, its random
+			   since its the time signal was off before
+			   first pulse if idle mode is enabled, HW
+			   does that for us */
+
+			if (!enable_idle)
+				continue;
+		}
+		update_sample(dev, hw_sample);
+		send_sample(dev);
+	}
+	return IRQ_HANDLED;
+}
+
+static int ene_probe(struct pnp_dev *pnp_dev,
+		     const struct pnp_device_id *dev_id)
+{
+	struct ene_device *dev;
+	struct lirc_driver *lirc_driver;
+	int error = -ENOMEM;
+
+	dev = kzalloc(sizeof(struct ene_device), GFP_KERNEL);
+
+	if (!dev)
+		goto err1;
+
+	dev->pnp_dev = pnp_dev;
+	pnp_set_drvdata(pnp_dev, dev);
+
+
+	/* prepare lirc interface */
+	error = -ENOMEM;
+	lirc_driver = kzalloc(sizeof(struct lirc_driver), GFP_KERNEL);
+
+	if (!lirc_driver)
+		goto err2;
+
+	dev->lirc_driver = lirc_driver;
+
+	strcpy(lirc_driver->name, ENE_DRIVER_NAME);
+	lirc_driver->minor = -1;
+	lirc_driver->code_length = sizeof(int) * 8;
+	lirc_driver->features = LIRC_CAN_REC_MODE2;
+	lirc_driver->data = dev;
+	lirc_driver->set_use_inc = ene_hw_init;
+	lirc_driver->set_use_dec = ene_hw_deinit;
+	lirc_driver->dev = &pnp_dev->dev;
+	lirc_driver->owner = THIS_MODULE;
+
+	lirc_driver->rbuf = kzalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
+
+	if (!lirc_driver->rbuf)
+		goto err3;
+
+	if (lirc_buffer_init(lirc_driver->rbuf, sizeof(int), sizeof(int) * 256))
+		goto err4;
+
+	error = -ENODEV;
+	if (lirc_register_driver(lirc_driver))
+		goto err5;
+
+	/* validate resources */
+	if (!pnp_port_valid(pnp_dev, 0) || pnp_port_len(pnp_dev, 0) < ENE_MAX_IO)
+		goto err6;
+
+	if (!pnp_irq_valid(pnp_dev, 0))
+		goto err6;
+
+	dev->hw_io = pnp_port_start(pnp_dev, 0);
+	dev->irq = pnp_irq(pnp_dev, 0);
+
+	/* claim the resources */
+	error = -EBUSY;
+	if (!request_region(dev->hw_io, ENE_MAX_IO, ENE_DRIVER_NAME))
+		goto err6;
+
+	if (request_irq(dev->irq, ene_hw_irq,
+			IRQF_SHARED, ENE_DRIVER_NAME, (void *)dev))
+		goto err7;
+
+	/* detect hardware version and features */
+	error = ene_hw_detect(dev);
+	if (error)
+		goto err8;
+
+	ene_printk(KERN_NOTICE, "driver has been succesfully loaded\n");
+	return 0;
+
+err8:
+	free_irq(dev->irq, dev);
+err7:
+	release_region(dev->hw_io, ENE_MAX_IO);
+err6:
+	lirc_unregister_driver(lirc_driver->minor);
+err5:
+	lirc_buffer_free(lirc_driver->rbuf);
+err4:
+	kfree(lirc_driver->rbuf);
+err3:
+	kfree(lirc_driver);
+err2:
+	kfree(dev);
+err1:
+	return error;
+}
+
+static void ene_remove(struct pnp_dev *pnp_dev)
+{
+	struct ene_device *dev = pnp_get_drvdata(pnp_dev);
+	ene_hw_deinit(dev);
+	free_irq(dev->irq, dev);
+	release_region(dev->hw_io, ENE_MAX_IO);
+	lirc_unregister_driver(dev->lirc_driver->minor);
+	lirc_buffer_free(dev->lirc_driver->rbuf);
+	kfree(dev->lirc_driver);
+	kfree(dev);
+}
+
+#ifdef CONFIG_PM
+
+/* TODO: make 'wake on IR' configurable and add .shutdown */
+/* currently impossible due to lack of kernel support */
+
+static int ene_suspend(struct pnp_dev *pnp_dev, pm_message_t state)
+{
+	struct ene_device *dev = pnp_get_drvdata(pnp_dev);
+	ene_hw_write_reg_mask(dev, ENE_FW1, ENE_FW1_WAKE, ENE_FW1_WAKE);
+	return 0;
+}
+
+static int ene_resume(struct pnp_dev *pnp_dev)
+{
+	struct ene_device *dev = pnp_get_drvdata(pnp_dev);
+	if (dev->in_use)
+		ene_hw_init(dev);
+
+	ene_hw_write_reg_mask(dev, ENE_FW1, 0, ENE_FW1_WAKE);
+	return 0;
+}
+
+#endif
+
+static const struct pnp_device_id ene_ids[] = {
+	{.id = "ENE0100",},
+	{},
+};
+
+static struct pnp_driver ene_driver = {
+	.name = ENE_DRIVER_NAME,
+	.id_table = ene_ids,
+	.flags = PNP_DRIVER_RES_DO_NOT_CHANGE,
+
+	.probe = ene_probe,
+	.remove = __devexit_p(ene_remove),
+
+#ifdef CONFIG_PM
+	.suspend = ene_suspend,
+	.resume = ene_resume,
+#endif
+};
+
+static int __init ene_init(void)
+{
+	if (sample_period < 5) {
+		ene_printk(KERN_ERR, "sample period must be at\n");
+		ene_printk(KERN_ERR, "least 5 us, (at least 30 recommended)\n");
+		return -EINVAL;
+	}
+	return pnp_register_driver(&ene_driver);
+}
+
+static void ene_exit(void)
+{
+	pnp_unregister_driver(&ene_driver);
+}
+
+module_param(sample_period, int, S_IRUGO);
+MODULE_PARM_DESC(sample_period, "Hardware sample period (75 us default)");
+
+module_param(enable_idle, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(enable_idle,
+	"Enables turning off signal sampling after long inactivity time; "
+	"if disabled might help detecting input signal (default: enabled)");
+
+module_param(enable_learning, bool, S_IRUGO);
+MODULE_PARM_DESC(enable_learning, "Use wide band (learning) reciever");
+
+MODULE_DEVICE_TABLE(pnp, ene_ids);
+MODULE_DESCRIPTION
+    ("LIRC driver for KB3926B/KB3926C/KB3926D (aka ENE0100) CIR port");
+MODULE_AUTHOR("Maxim Levitsky");
+MODULE_LICENSE("GPL");
+
+module_init(ene_init);
+module_exit(ene_exit);
+#endif
diff --git a/ubuntu/lirc/lirc_ene0100/lirc_ene0100.h b/ubuntu/lirc/lirc_ene0100/lirc_ene0100.h
new file mode 100644
index 0000000..ae19753
--- /dev/null
+++ b/ubuntu/lirc/lirc_ene0100/lirc_ene0100.h
@@ -0,0 +1,170 @@
+/*
+ * driver for ENE KB3926 B/C/D CIR (also known as ENE0100)
+ *
+ * Copyright (C) 2009 Maxim Levitsky <maximlevitsky@gmail.com>
+ *
+ * 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.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include "../kcompat.h"
+#include "../lirc.h"
+#include "../lirc_dev/lirc_dev.h"
+
+/* hardware address */
+#define ENE_STATUS		0	 /* hardware status - unused */
+#define ENE_ADDR_HI 		1	 /* hi byte of register address */
+#define ENE_ADDR_LO 		2	 /* low byte of register address */
+#define ENE_IO 			3	 /* read/write window */
+#define ENE_MAX_IO		4
+
+/* 8 bytes of samples, divided in 2 halfs*/
+#define ENE_SAMPLE_BUFFER 	0xF8F0	 /* regular sample buffer */
+#define ENE_SAMPLE_SPC_MASK 	(1 << 7) /* sample is space */
+#define ENE_SAMPLE_VALUE_MASK	0x7F
+#define ENE_SAMPLE_OVERFLOW	0x7F
+#define ENE_SAMPLES_SIZE	4
+
+/* fan input sample buffer */
+#define ENE_SAMPLE_BUFFER_FAN	0xF8FB	 /* this buffer holds high byte of */
+					 /* each sample of normal buffer */
+
+#define ENE_FAN_SMPL_PULS_MSK	0x8000	 /* this bit of combined sample */
+					 /* if set, says that sample is pulse */
+#define ENE_FAN_VALUE_MASK	0x0FFF   /* mask for valid bits of the value */
+
+/* first firmware register */
+#define ENE_FW1			0xF8F8
+#define	ENE_FW1_ENABLE		(1 << 0) /* enable fw processing */
+#define ENE_FW1_TXIRQ		(1 << 1) /* TX interrupt pending */
+#define ENE_FW1_WAKE		(1 << 6) /* enable wake from S3 */
+#define ENE_FW1_IRQ		(1 << 7) /* enable interrupt */
+
+/* second firmware register */
+#define ENE_FW2			0xF8F9
+#define ENE_FW2_BUF_HIGH	(1 << 0) /* which half of the buffer to read */
+#define ENE_FW2_IRQ_CLR		(1 << 2) /* clear this on IRQ */
+#define ENE_FW2_GP40_AS_LEARN	(1 << 4) /* normal input is used as */
+					 /* learning input */
+#define ENE_FW2_FAN_AS_NRML_IN	(1 << 6) /* fan is used as normal input */
+#define ENE_FW2_LEARNING	(1 << 7) /* hardware supports learning and TX */
+
+/* fan as input settings - only if learning capable */
+#define ENE_FAN_AS_IN1		0xFE30   /* fan init reg 1 */
+#define ENE_FAN_AS_IN1_EN	0xCD
+#define ENE_FAN_AS_IN2		0xFE31   /* fan init reg 2 */
+#define ENE_FAN_AS_IN2_EN	0x03
+#define ENE_SAMPLE_PERIOD_FAN   61	 /* fan input has fixed sample period */
+
+/* IRQ registers block (for revision B) */
+#define ENEB_IRQ		0xFD09	 /* IRQ number */
+#define ENEB_IRQ_UNK1		0xFD17	 /* unknown setting = 1 */
+#define ENEB_IRQ_STATUS		0xFD80	 /* irq status */
+#define ENEB_IRQ_STATUS_IR	(1 << 5) /* IR irq */
+
+/* IRQ registers block (for revision C,D) */
+#define ENEC_IRQ		0xFE9B	 /* new irq settings register */
+#define ENEC_IRQ_MASK		0x0F	 /* irq number mask */
+#define ENEC_IRQ_UNK_EN		(1 << 4) /* always enabled */
+#define ENEC_IRQ_STATUS		(1 << 5) /* irq status and ACK */
+
+/* CIR block settings */
+#define ENE_CIR_CONF1		0xFEC0
+#define ENE_CIR_CONF1_ADC_ON	0x7	 /* reciever on gpio40 enabled */
+#define ENE_CIR_CONF1_LEARN1	(1 << 3) /* enabled on learning mode */
+#define ENE_CIR_CONF1_TX_ON	0x30	 /* enabled on transmit */
+#define ENE_CIR_CONF1_TX_CARR	(1 << 7) /* send TX carrier or not */
+
+#define ENE_CIR_CONF2		0xFEC1	 /* unknown setting = 0 */
+#define ENE_CIR_CONF2_LEARN2	(1 << 4) /* set on enable learning */
+#define ENE_CIR_CONF2_GPIO40DIS	(1 << 5) /* disable normal input via gpio40 */
+
+#define ENE_CIR_SAMPLE_PERIOD	0xFEC8	 /* sample period in us */
+#define ENE_CIR_SAMPLE_OVERFLOW	(1 << 7) /* interrupt on overflows if set */
+
+
+/* transmitter - not implemented yet */
+/* KB3926C and higher */
+/* transmission is very similiar to recieving, a byte is written to */
+/* ENE_TX_INPUT, in same manner as it is read from sample buffer */
+/* sample period is fixed*/
+
+
+/* transmitter ports */
+#define ENE_TX_PORT1		0xFC01	 /* this enables one or both */
+#define ENE_TX_PORT1_EN		(1 << 5) /* TX ports */
+#define ENE_TX_PORT2		0xFC08
+#define ENE_TX_PORT2_EN		(1 << 1)
+
+#define ENE_TX_INPUT		0xFEC9	 /* next byte to transmit */
+#define ENE_TX_SPC_MASK 	(1 << 7) /* Transmitted sample is space */
+#define ENE_TX_UNK1		0xFECB	 /* set to 0x63 */
+#define ENE_TX_SMPL_PERIOD	50	 /* transmit sample period */
+
+
+#define ENE_TX_CARRIER		0xFECE	 /* TX carrier * 2 (khz) */
+#define ENE_TX_CARRIER_UNKBIT	0x80	 /* This bit set on transmit */
+#define ENE_TX_CARRIER_LOW	0xFECF	 /* TX carrier / 2 */
+
+/* Hardware versions */
+#define ENE_HW_VERSION		0xFF00	 /* hardware revision */
+#define ENE_HW_UNK 		0xFF1D
+#define ENE_HW_UNK_CLR		(1 << 2)
+#define ENE_HW_VER_MAJOR	0xFF1E	 /* chip version */
+#define ENE_HW_VER_MINOR	0xFF1F
+#define ENE_HW_VER_OLD		0xFD00
+
+#define same_sign(a, b) ((((a) > 0) && (b) > 0) || ((a) < 0 && (b) < 0))
+
+#define ENE_DRIVER_NAME 	"enecir"
+#define ENE_MAXGAP 		250000	 /* this is amount of time we wait
+					 before turning the sampler, chosen
+					 arbitry */
+
+#define space(len) 	       (-(len))	 /* add a space */
+
+/* software defines */
+#define ENE_IRQ_RX		1
+#define ENE_IRQ_TX		2
+
+#define  ENE_HW_B		1	/* 3926B */
+#define  ENE_HW_C		2	/* 3926C */
+#define  ENE_HW_D		3	/* 3926D */
+
+#define ene_printk(level, text, ...) \
+	printk(level ENE_DRIVER_NAME ": " text, ## __VA_ARGS__)
+
+struct ene_device {
+	struct pnp_dev *pnp_dev;
+	struct lirc_driver *lirc_driver;
+
+	/* hw settings */
+	unsigned long hw_io;
+	int irq;
+
+	int hw_revision;			/* hardware revision */
+	int hw_learning_and_tx_capable;		/* learning capable */
+	int hw_gpio40_learning;			/* gpio40 is learning */
+	int hw_fan_as_normal_input;	/* fan input is used as regular input */
+
+	/* device data */
+	int idle;
+	int fan_input_inuse;
+
+	int sample;
+	int in_use;
+
+	struct timeval gap_start;
+};
diff --git a/ubuntu/lirc/lirc_i2c/lirc_i2c.c b/ubuntu/lirc/lirc_i2c/lirc_i2c.c
index baf04fa..5acf079 100644
--- a/ubuntu/lirc/lirc_i2c/lirc_i2c.c
+++ b/ubuntu/lirc/lirc_i2c/lirc_i2c.c
@@ -1,3 +1,5 @@
+/*      $Id: lirc_i2c.c,v 1.70 2009/08/30 16:59:53 jarodwilson Exp $      */
+
 /*
  * lirc_i2c.c
  *
@@ -42,6 +44,10 @@
  */
 
 
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
 #include <linux/version.h>
 #include <linux/module.h>
 #include <linux/kmod.h>
@@ -55,6 +61,7 @@
 #include <linux/i2c.h>
 #include <linux/i2c-algo-bit.h>
 
+#include "../kcompat.h"
 #include "../lirc_dev/lirc_dev.h"
 
 struct IR {
@@ -326,12 +333,22 @@ static int add_to_buf_knc1(void *data, struct lirc_buffer *buf)
 static int set_use_inc(void *data)
 {
 	struct IR *ir = data;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25)
+	int ret;
+#endif
 
 	dprintk("%s called\n", __func__);
 
-	/* lock bttv in memory while /dev/lirc is in use  */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)
 	i2c_use_client(&ir->c);
+#else
+	/* lock bttv in memory while /dev/lirc is in use  */
+	ret = i2c_use_client(&ir->c);
+	if (ret != 0)
+		return ret;
+#endif
 
+	MOD_INC_USE_COUNT;
 	return 0;
 }
 
@@ -342,6 +359,7 @@ static void set_use_dec(void *data)
 	dprintk("%s called\n", __func__);
 
 	i2c_release_client(&ir->c);
+	MOD_DEC_USE_COUNT;
 }
 
 static struct lirc_driver lirc_template = {
@@ -352,28 +370,58 @@ static struct lirc_driver lirc_template = {
 	.owner		= THIS_MODULE,
 };
 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)
+static int ir_attach(struct i2c_adapter *adap, int addr,
+		      unsigned short flags, int kind);
+static int ir_probe(struct i2c_adapter *adap);
+# else
 static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id);
+#endif
 static int ir_remove(struct i2c_client *client);
 static int ir_command(struct i2c_client *client, unsigned int cmd, void *arg);
 
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 30)
 static const struct i2c_device_id ir_receiver_id[] = {
 	/* Generic entry for any IR receiver */
 	{ "ir_video", 0 },
 	/* IR device specific entries could be added here */
 	{ }
 };
+#endif
 
 static struct i2c_driver driver = {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 16)
+	.name		= "i2c ir driver",
+	.flags		= I2C_DF_NOTIFY,
+#else
 	.driver = {
 		.owner	= THIS_MODULE,
 		.name	= "i2c ir driver",
 	},
+#endif
+	.id		= I2C_DRIVERID_EXP3, /* FIXME */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)
+	.attach_adapter	= ir_probe,
+	.detach_client	= ir_remove,
+#else
 	.probe		= ir_probe,
 	.remove		= ir_remove,
 	.id_table	= ir_receiver_id,
+#endif
 	.command	= ir_command,
 };
 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)
+static struct i2c_client client_template = {
+	.name		= "unset",
+	.driver		= &driver
+};
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)
+static int ir_attach(struct i2c_adapter *adap, int addr,
+		     unsigned short flags, int kind)
+#else
 static void pcf_probe(struct i2c_client *client, struct IR *ir)
 {
 	int ret1, ret2, ret3, ret4;
@@ -397,22 +445,39 @@ static void pcf_probe(struct i2c_client *client, struct IR *ir)
 }
 
 static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)
+#endif
 {
 	struct IR *ir;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)
+	int err, retval;
+
+	client_template.adapter = adap;
+	client_template.addr = addr;
+#else
 	struct i2c_adapter *adap = client->adapter;
 	unsigned short addr = client->addr;
 	int retval;
+#endif
 
-	ir = kzalloc(sizeof(struct IR), GFP_KERNEL);
+	ir = kmalloc(sizeof(struct IR), GFP_KERNEL);
 	if (!ir)
 		return -ENOMEM;
 	memcpy(&ir->l, &lirc_template, sizeof(struct lirc_driver));
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)
+	memcpy(&ir->c, &client_template, sizeof(struct i2c_client));
+
+	ir->c.adapter = adap;
+	ir->c.addr    = addr;
+	i2c_set_clientdata(&ir->c, ir);
+#else
 	memcpy(&ir->c, client, sizeof(struct i2c_client));
 
 	i2c_set_clientdata(client, ir);
+#endif
 	ir->l.data    = ir;
 	ir->l.minor   = minor;
 	ir->l.sample_rate = 10;
+	ir->l.dev     = &ir->c.dev;
 	ir->nextkey   = -1;
 
 	switch (addr) {
@@ -427,8 +492,12 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)
 		ir->l.add_to_buf = add_to_buf_pv951;
 		break;
 	case 0x71:
-		if (adap->id == I2C_HW_B_BT848 ||
-		    adap->id == I2C_HW_B_CX2341X) {
+#ifdef I2C_HW_B_CX2341X
+		if (adap->id == (I2C_ALGO_BIT | I2C_HW_B_BT848) ||
+		    adap->id == (I2C_ALGO_BIT | I2C_HW_B_CX2341X)) {
+#else
+		if (adap->id == (I2C_ALGO_BIT | I2C_HW_B_BT848)) {
+#endif
 			/*
 			 * The PVR150 IR receiver uses the same protocol as
 			 * other Hauppauge cards, but the data flow is
@@ -447,8 +516,12 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)
 		break;
 	case 0x18:
 	case 0x1a:
-		if (adap->id == I2C_HW_B_BT848 ||
-		    adap->id == I2C_HW_B_CX2341X) {
+#ifdef I2C_HW_B_CX2341X
+		if (adap->id == (I2C_ALGO_BIT | I2C_HW_B_BT848) ||
+		    adap->id == (I2C_ALGO_BIT | I2C_HW_B_CX2341X)) {
+#else
+		if (adap->id == (I2C_ALGO_BIT | I2C_HW_B_BT848)) {
+#endif
 			strlcpy(ir->c.name, "Hauppauge IR", I2C_NAME_SIZE);
 			ir->l.code_length = 13;
 			ir->l.add_to_buf = add_to_buf_haup;
@@ -465,7 +538,12 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)
 		break;
 	case 0x21:
 	case 0x23:
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)
+		ir->bits = flags & 0xff;
+		ir->flag = (flags >> 8) & 0xff;
+#else
 		pcf_probe(client, ir);
+#endif
 		strlcpy(ir->c.name, "TV-Box IR", I2C_NAME_SIZE);
 		ir->l.code_length = 8;
 		ir->l.add_to_buf = add_to_buf_pcf8574;
@@ -479,10 +557,22 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)
 	printk(KERN_INFO "lirc_i2c: chip 0x%x found @ 0x%02x (%s)\n",
 	       adap->id, addr, ir->c.name);
 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)
+	/* register device */
+	err = i2c_attach_client(&ir->c);
+	if (err) {
+		kfree(ir);
+		return err;
+	}
+#endif
+
 	retval = lirc_register_driver(&ir->l);
 
 	if (retval < 0) {
 		printk(KERN_ERR "lirc_i2c: failed to register driver!\n");
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)
+		i2c_detach_client(&ir->c);
+#endif
 		kfree(ir);
 		return retval;
 	}
@@ -498,20 +588,169 @@ static int ir_remove(struct i2c_client *client)
 
 	/* unregister device */
 	lirc_unregister_driver(ir->l.minor);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)
+	i2c_detach_client(&ir->c);
+#endif
 
 	/* free memory */
 	kfree(ir);
 	return 0;
 }
 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)
+static int ir_probe(struct i2c_adapter *adap)
+{
+	/*
+	 * The external IR receiver is at i2c address 0x34 (0x35 for
+	 * reads).  Future Hauppauge cards will have an internal
+	 * receiver at 0x30 (0x31 for reads).  In theory, both can be
+	 * fitted, and Hauppauge suggest an external overrides an
+	 * internal.
+	 *
+	 * That's why we probe 0x1a (~0x34) first. CB
+	 *
+	 * The i2c address for the Hauppauge PVR-150 card is 0xe2,
+	 * so we need to probe 0x71 as well.
+	 */
+
+	static const int probe[] = {
+		0x1a, /* Hauppauge IR external */
+		0x18, /* Hauppauge IR internal */
+		0x71, /* Hauppauge IR (PVR150) */
+		0x4b, /* PV951 IR */
+		0x64, /* Pixelview IR */
+		0x30, /* KNC ONE IR */
+		0x6b, /* Adaptec IR */
+		-1};
+
+#ifdef I2C_HW_B_CX2388x
+	static const int probe_cx88[] = {
+		0x18, /* Leadtek Winfast PVR2000 */
+		0x71, /* Hauppauge HVR-IR */
+		-1};
+#endif
+
+	struct i2c_client c;
+	char buf;
+	int i, rc;
+
+	memset(&c, 0, sizeof(c));
+#ifdef I2C_HW_B_CX2341X
+	if (adap->id == (I2C_ALGO_BIT | I2C_HW_B_BT848) ||
+	    adap->id == (I2C_ALGO_BIT | I2C_HW_B_CX2341X)) {
+#else
+	if (adap->id == (I2C_ALGO_BIT | I2C_HW_B_BT848)) {
+#endif
+		c.adapter = adap;
+		for (i = 0; -1 != probe[i]; i++) {
+			c.addr = probe[i];
+			rc = i2c_master_recv(&c, &buf, 1);
+			dprintk("probe 0x%02x @ %s: %s\n",
+				probe[i], adap->name,
+				(1 == rc) ? "yes" : "no");
+			if (1 == rc) {
+				rc = ir_attach(adap, probe[i], 0, 0);
+				if (rc < 0)
+					goto attach_fail;
+			}
+		}
+	}
+
+#ifdef I2C_HW_B_CX2388x
+	/* Leadtek Winfast PVR2000 or Hauppauge HVR-1300 */
+	else if (adap->id == (I2C_ALGO_BIT | I2C_HW_B_CX2388x)) {
+		c.adapter = adap;
+		for (i = 0; -1 != probe_cx88[i]; i++) {
+			c.addr = probe_cx88[i];
+			rc = i2c_master_recv(&c, &buf, 1);
+			dprintk("probe 0x%02x @ %s: %s\n",
+				c.addr, adap->name,
+				(1 == rc) ? "yes" : "no");
+			if (1 == rc) {
+				rc = ir_attach(adap, c.addr, 0, 0);
+				if (rc < 0)
+					goto attach_fail;
+			}
+		}
+	}
+#endif
+
+	/* Asus TV-Box and Creative/VisionTek BreakOut-Box (PCF8574) */
+	else if (adap->id == (I2C_ALGO_BIT | I2C_HW_B_RIVA)) {
+		/*
+		 * addresses to probe;
+		 * leave 0x24 and 0x25 because SAA7113H possibly uses it
+		 * 0x21 and 0x22 possibly used by SAA7108E
+		 * Asus:      0x21 is a correct address (channel 1 of PCF8574)
+		 * Creative:  0x23 is a correct address (channel 3 of PCF8574)
+		 * VisionTek: 0x23 is a correct address (channel 3 of PCF8574)
+		 */
+		static const int pcf_probe[] = { 0x20, 0x21, 0x22, 0x23,
+						 0x24, 0x25, 0x26, 0x27, -1 };
+		int ret1, ret2, ret3, ret4;
+		unsigned char bits = 0, flag = 0;
+
+		c.adapter = adap;
+		for (i = 0; -1 != pcf_probe[i]; i++) {
+			c.addr = pcf_probe[i];
+			ret1 = i2c_smbus_write_byte(&c, 0xff);
+			ret2 = i2c_smbus_read_byte(&c);
+			ret3 = i2c_smbus_write_byte(&c, 0x00);
+			ret4 = i2c_smbus_read_byte(&c);
+
+			/* ensure that the writable bitmask works correctly */
+			rc = 0;
+			if (ret1 != -1 && ret2 != -1 &&
+			    ret3 != -1 && ret4 != -1) {
+				/* in the Asus TV-Box: bit 1-0 */
+				if (((ret2 & 0x03) == 0x03) &&
+				    ((ret4 & 0x03) == 0x00)) {
+					bits = (unsigned char) ~0x07;
+					flag = 0x04;
+					rc = 1;
+				}
+			/* in the Creative/VisionTek BreakOut-Box: bit 7-6 */
+				if (((ret2 & 0xc0) == 0xc0) &&
+				    ((ret4 & 0xc0) == 0x00)) {
+					bits = (unsigned char) ~0xe0;
+					flag = 0x20;
+					rc = 1;
+				}
+			}
+			dprintk("probe 0x%02x @ %s: %s\n",
+				c.addr, adap->name, rc ? "yes" : "no");
+			if (rc) {
+				rc = ir_attach(adap, pcf_probe[i],
+					       bits | (flag << 8), 0);
+				if (rc < 0)
+					goto attach_fail;
+			}
+		}
+	}
+
+	return 0;
+
+attach_fail:
+	printk(KERN_ERR "lirc_i2c: %s: ir_attach failed!\n", __func__);
+	return rc;
+
+}
+#endif
+
 static int ir_command(struct i2c_client *client, unsigned int cmd, void *arg)
 {
 	/* nothing */
 	return 0;
 }
 
+#ifdef MODULE
+
 static int __init lirc_i2c_init(void)
 {
+	request_module("bttv");
+	request_module("rivatv");
+	request_module("ivtv");
+	request_module("cx8800");
 	i2c_add_driver(&driver);
 	return 0;
 }
@@ -535,3 +774,6 @@ MODULE_PARM_DESC(debug, "Enable debugging messages");
 
 module_init(lirc_i2c_init);
 module_exit(lirc_i2c_exit);
+EXPORT_NO_SYMBOLS;
+
+#endif /* MODULE */
diff --git a/ubuntu/lirc/lirc_igorplugusb/lirc_igorplugusb.c b/ubuntu/lirc/lirc_igorplugusb/lirc_igorplugusb.c
index 4ff2725..6705570 100644
--- a/ubuntu/lirc/lirc_igorplugusb/lirc_igorplugusb.c
+++ b/ubuntu/lirc/lirc_igorplugusb/lirc_igorplugusb.c
@@ -69,7 +69,7 @@
 #endif
 
 /* module identification */
-#define DRIVER_VERSION		"0.1"
+#define DRIVER_VERSION		"0.2"
 #define DRIVER_AUTHOR		\
 	"Jan M. Hochstein <hochstein@algo.informatik.tu-darmstadt.de>"
 #define DRIVER_DESC		"USB remote driver for LIRC"
@@ -268,6 +268,23 @@ static void set_use_dec(void *data)
 	MOD_DEC_USE_COUNT;
 }
 
+static void send_fragment(struct igorplug *ir, struct lirc_buffer *buf,
+			   int i, int max)
+{
+	/* MODE2: pulse/space (PULSE_BIT) in 1us units */
+	while (i < max) {
+		/* 1 Igor-tick = 85.333333 us */
+		lirc_t code = (unsigned int)ir->buf_in[i] * 85 +
+			(unsigned int)ir->buf_in[i] / 3;
+		ir->last_time.tv_usec += code;
+		if (ir->in_space)
+			code |= PULSE_BIT;
+		lirc_buffer_write_n(buf, (unsigned char *)&code, 1);
+		/* 1 chunk = CODE_LENGTH bytes */
+		ir->in_space ^= 1;
+		++i;
+	}
+}
 
 /**
  * Called in user context.
@@ -292,24 +309,16 @@ static int usb_remote_poll(void *data, struct lirc_buffer *buf)
 	      ir->buf_in, ir->len_in,
 	      /*timeout*/HZ * USB_CTRL_GET_TIMEOUT);
 	if (ret > 0) {
-		int i = DEVICE_HEADERLEN;
 		lirc_t code, timediff;
 		struct timeval now;
 
-		if (ret <= 1)  /* ACK packet has 1 byte --> ignore */
+		/* ACK packet has 1 byte --> ignore */
+		if (ret < DEVICE_HEADERLEN)
 			return -ENODATA;
 
 		dprintk(": Got %d bytes. Header: %02x %02x %02x\n",
 			ret, ir->buf_in[0], ir->buf_in[1], ir->buf_in[2]);
 
-		if (ir->buf_in[2] != 0) {
-			printk(KERN_WARNING DRIVER_NAME
-			       "[%d]: Device buffer overrun.\n", ir->devnum);
-			/* start at earliest byte */
-			i = DEVICE_HEADERLEN + ir->buf_in[2];
-			/* where are we now? space, gap or pulse? */
-		}
-
 		do_gettimeofday(&now);
 		timediff = now.tv_sec - ir->last_time.tv_sec;
 		if (timediff + 1 > PULSE_MASK / 1000000)
@@ -326,19 +335,21 @@ static int usb_remote_poll(void *data, struct lirc_buffer *buf)
 		lirc_buffer_write(buf, (unsigned char *)&code);
 		ir->in_space = 1;   /* next comes a pulse */
 
-		/* MODE2: pulse/space (PULSE_BIT) in 1us units */
-
-		while (i < ret) {
-			/* 1 Igor-tick = 85.333333 us */
-			code = (unsigned int)ir->buf_in[i] * 85
-				+ (unsigned int)ir->buf_in[i] / 3;
-			ir->last_time.tv_usec += code;
-			if (ir->in_space)
-				code |= PULSE_BIT;
-			lirc_buffer_write(buf, (unsigned char *)&code);
-			/* 1 chunk = CODE_LENGTH bytes */
-			ir->in_space ^= 1;
-			++i;
+		if (ir->buf_in[2] == 0)
+			send_fragment(ir, buf, DEVICE_HEADERLEN, ret);
+		else {
+			printk(KERN_WARNING DRIVER_NAME
+			       "[%d]: Device buffer overrun.\n", ir->devnum);
+			/* HHHNNNNNNNNNNNOOOOOOOO H = header
+			      <---[2]--->         N = newer
+			   <---------ret--------> O = older */
+			ir->buf_in[2] %= ret - DEVICE_HEADERLEN; /* sanitize */
+			/* keep even-ness to not desync pulse/pause */
+			send_fragment(ir, buf, DEVICE_HEADERLEN +
+				      ir->buf_in[2] - (ir->buf_in[2] & 1),
+				      ret);
+			send_fragment(ir, buf, DEVICE_HEADERLEN,
+				      DEVICE_HEADERLEN + ir->buf_in[2]);
 		}
 
 		ret = usb_control_msg(
@@ -569,6 +580,8 @@ static void usb_remote_disconnect(struct usb_device *dev, void *ptr)
 static struct usb_device_id usb_remote_id_table [] = {
 	/* Igor Plug USB (Atmel's Manufact. ID) */
 	{ USB_DEVICE(0x03eb, 0x0002) },
+	/* Fit PC2 Infrared Adapter */
+	{ USB_DEVICE(0x03eb, 0x21fe) },
 
 	/* Terminating entry */
 	{ }
diff --git a/ubuntu/lirc/lirc_imon/lirc_imon.c b/ubuntu/lirc/lirc_imon/lirc_imon.c
index 52abbb3..fcaa45f 100644
--- a/ubuntu/lirc/lirc_imon/lirc_imon.c
+++ b/ubuntu/lirc/lirc_imon/lirc_imon.c
@@ -1,8 +1,8 @@
 /*
- *   lirc_imon.c:  LIRC/VFD/LCD driver for Ahanix/Soundgraph iMON IR/VFD/LCD
+ *   lirc_imon.c:  LIRC/VFD/LCD driver for SoundGraph iMON IR/VFD/LCD
  *		   including the iMON PAD model
  *
- *   $Id: lirc_imon.c,v 1.59 2009/04/10 20:51:29 jarodwilson Exp $
+ *   $Id: lirc_imon.c,v 1.111 2009/09/11 04:56:18 jarodwilson Exp $
  *
  *   Copyright(C) 2004  Venky Raju(dev@venky.ws)
  *
@@ -26,8 +26,8 @@
 #include <config.h>
 #endif
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 22)
-#error "*** Sorry, this driver requires kernel version 2.4.22 or higher"
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0)
+#error "*** Sorry, this driver requires a 2.6 kernel"
 #endif
 
 #include <linux/autoconf.h>
@@ -43,7 +43,13 @@
 #include <linux/uaccess.h>
 #endif
 #include <linux/usb.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18)
+#include <linux/usb/input.h>
+#else
+#include <linux/input.h>
+#endif
 #include <linux/time.h>
+#include <linux/timer.h>
 
 #include "../kcompat.h"
 #include "../lirc.h"
@@ -51,44 +57,47 @@
 
 
 #define MOD_AUTHOR	"Venky Raju <dev@venky.ws>"
-#define MOD_DESC	"Driver for Soundgraph iMON MultiMedia IR/Display"
+#define MOD_DESC	"Driver for SoundGraph iMON MultiMedia IR/Display"
 #define MOD_NAME	"lirc_imon"
-#define MOD_VERSION	"0.5"
+#define MOD_VERSION	"0.6"
 
-#define DISPLAY_MINOR_BASE	144	/* Same as LCD */
+#define DISPLAY_MINOR_BASE	144
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 15)
 #define DEVFS_MODE	(S_IFCHR | S_IRUSR | S_IWUSR | \
 			 S_IRGRP | S_IWGRP | S_IROTH)
 #endif
-#define DEVFS_NAME	LIRC_DEVFS_PREFIX "lcd%d"
+#define DEVICE_NAME	LIRC_DEVFS_PREFIX "lcd%d"
 
 #define BUF_CHUNK_SIZE	4
 #define BUF_SIZE	128
 
 #define BIT_DURATION	250	/* each bit received is 250us */
 
+#define IMON_CLOCK_ENABLE_PACKETS	2
+
+#define dprintk(fmt, args...)						\
+	do {								\
+		if (debug)						\
+			printk(KERN_INFO MOD_NAME ": " fmt, ## args);	\
+	} while (0)
+
 /*** P R O T O T Y P E S ***/
 
 /* USB Callback prototypes */
-#ifdef KERNEL_2_5
 static int imon_probe(struct usb_interface *interface,
 		      const struct usb_device_id *id);
 static void imon_disconnect(struct usb_interface *interface);
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
-static void usb_rx_callback(struct urb *urb, struct pt_regs *regs);
+static void usb_rx_callback_intf0(struct urb *urb, struct pt_regs *regs);
+static void usb_rx_callback_intf1(struct urb *urb, struct pt_regs *regs);
 static void usb_tx_callback(struct urb *urb, struct pt_regs *regs);
 #else
-static void usb_rx_callback(struct urb *urb);
-static void usb_tx_callback(struct urb *urb);
-#endif
-#else
-static void *imon_probe(struct usb_device *dev, unsigned int intf,
-				const struct usb_device_id *id);
-static void imon_disconnect(struct usb_device *dev, void *data);
-static void usb_rx_callback(struct urb *urb);
+static void usb_rx_callback_intf0(struct urb *urb);
+static void usb_rx_callback_intf1(struct urb *urb);
 static void usb_tx_callback(struct urb *urb);
 #endif
 
+/* suspend/resume support */
 static int imon_resume(struct usb_interface *intf);
 static int imon_suspend(struct usb_interface *intf, pm_message_t message);
 
@@ -115,26 +124,27 @@ static void __exit imon_exit(void);
 /*** G L O B A L S ***/
 
 struct imon_context {
-	struct usb_device *usbdev;
+	struct usb_device *usbdev_intf0;
+	/* Newer devices have two interfaces */
+	struct usb_device *usbdev_intf1;
 	int display_supported;		/* not all controllers do */
 	int display_isopen;		/* display port has been opened */
-#if !defined(KERNEL_2_5)
-	int subminor;			/* index into minor_table */
-	devfs_handle_t devfs;
-#endif
 	int ir_isopen;			/* IR port open	*/
 	int ir_isassociating;		/* IR port open for association */
-	int dev_present;		/* USB device presence */
+	int dev_present_intf0;		/* USB device presence, interface 0 */
+	int dev_present_intf1;		/* USB device presence, interface 1 */
 	struct mutex lock;		/* to lock this object */
 	wait_queue_head_t remove_ok;	/* For unexpected USB disconnects */
 
-	int display_proto_6p;		/* display requires 6th packet */
+	int vfd_proto_6p;		/* some VFD require a 6th packet */
 	int ir_onboard_decode;		/* IR signals decoded onboard */
 
 	struct lirc_driver *driver;
-	struct usb_endpoint_descriptor *rx_endpoint;
+	struct usb_endpoint_descriptor *rx_endpoint_intf0;
+	struct usb_endpoint_descriptor *rx_endpoint_intf1;
 	struct usb_endpoint_descriptor *tx_endpoint;
-	struct urb *rx_urb;
+	struct urb *rx_urb_intf0;
+	struct urb *rx_urb_intf1;
 	struct urb *tx_urb;
 	int tx_control;
 	unsigned char usb_rx_buf[8];
@@ -152,8 +162,26 @@ struct imon_context {
 		atomic_t busy;			/* write in progress */
 		int status;			/* status of tx completion */
 	} tx;
+
+	int ffdc_dev;			/* is this the overused ffdc ID? */
+	int ir_protocol;		/* iMON or MCE (RC6) IR protocol? */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18)
+	struct input_dev *mouse;	/* input device for iMON PAD remote */
+	struct input_dev *touch;	/* input device for touchscreen */
+#endif
+	int display_type;		/* store the display type */
+	int pad_mouse;			/* toggle kbd(0)/mouse(1) mode */
+	int touch_x;			/* x coordinate on touchscreen */
+	int touch_y;			/* y coordinate on touchscreen */
+	char name_mouse[128];
+	char phys_mouse[64];
+	char name_touch[128];
+	char phys_touch[64];
+	struct timer_list timer;
 };
 
+#define TOUCH_TIMEOUT	(HZ/30)
+
 /* display file operations. Nb: lcd_write will be subbed in as needed later */
 static struct file_operations display_fops = {
 	.owner		= THIS_MODULE,
@@ -163,30 +191,81 @@ static struct file_operations display_fops = {
 };
 
 enum {
-	IMON_DISPLAY_TYPE_AUTO,
-	IMON_DISPLAY_TYPE_VFD,
-	IMON_DISPLAY_TYPE_LCD,
-	IMON_DISPLAY_TYPE_NONE,
+	IMON_DISPLAY_TYPE_AUTO = 0,
+	IMON_DISPLAY_TYPE_VFD  = 1,
+	IMON_DISPLAY_TYPE_LCD  = 2,
+	IMON_DISPLAY_TYPE_VGA  = 3,
+	IMON_DISPLAY_TYPE_NONE = 4,
 };
 
-/* USB Device ID for iMON USB Control Board */
+enum {
+	IMON_IR_PROTOCOL_IMON       = 0,
+	IMON_IR_PROTOCOL_MCE        = 1,
+	IMON_IR_PROTOCOL_IMON_NOPAD = 2,
+};
+/*
+ * USB Device ID for iMON USB Control Boards
+ *
+ * The Windows drivers contain 6 different inf files, more or less one for
+ * each new device until the 0x0034-0x0046 devices, which all use the same
+ * driver. Some of the devices in the 34-46 range haven't been definitively
+ * identified yet. Early devices have either a TriGem Computer, Inc. or a
+ * Samsung vendor ID (0x0aa8 and 0x04e8 respectively), while all later
+ * devices use the SoundGraph vendor ID (0x15c2).
+ */
 static struct usb_device_id imon_usb_id_table[] = {
-	/* iMON USB Control Board (IR & VFD) */
-	{ USB_DEVICE(0x0aa8, 0xffda) },
-	/* iMON USB Control Board (IR only) */
+	/* TriGem iMON (IR only) -- TG_iMON.inf */
 	{ USB_DEVICE(0x0aa8, 0x8001) },
-	/* iMON USB Control Board (ext IR only) */
+
+	/* SoundGraph iMON (IR only) -- sg_imon.inf */
 	{ USB_DEVICE(0x04e8, 0xff30) },
-	/* iMON USB Control Board (IR & VFD) */
+
+	/* SoundGraph iMON VFD (IR & VFD) -- iMON_VFD.inf */
+	{ USB_DEVICE(0x0aa8, 0xffda) },
+
+	/* SoundGraph iMON SS (IR & VFD) -- iMON_SS.inf */
 	{ USB_DEVICE(0x15c2, 0xffda) },
-	/* iMON USB Control Board (IR & LCD) *and* iMON Knob (IR only) */
+
+	/*
+	 * Several devices with this same device ID, all use iMON_PAD.inf
+	 * SoundGraph iMON PAD (IR & VFD)
+	 * SoundGraph iMON PAD (IR & LCD)
+	 * SoundGraph iMON Knob (IR only)
+	 */
+	/* SoundGraph iMON PAD (IR & VFD/LCD), iMON Knob */
 	{ USB_DEVICE(0x15c2, 0xffdc) },
-	/* iMON USB Control Board (IR & LCD) */
+
+	/*
+	 * Newer devices, all driven by the latest iMON Windows driver, full
+	 * list of device IDs extracted via 'strings Setup/data1.hdr |grep 15c2'
+	 * Need user input to fill in details on unknown devices.
+	 */
+	/* SoundGraph iMON OEM Touch LCD (IR & 7" VGA LCD) */
 	{ USB_DEVICE(0x15c2, 0x0034) },
-	/* iMON USB Control Board (IR & VFD) */
+	/* SoundGraph iMON OEM Touch LCD (IR & 4.3" VGA LCD) */
+	{ USB_DEVICE(0x15c2, 0x0035) },
+	/* SoundGraph iMON OEM VFD (IR & VFD) */
 	{ USB_DEVICE(0x15c2, 0x0036) },
-	/* iMON USB Control Board (IR & LCD) */
+	/* device specifics unknown */
+	{ USB_DEVICE(0x15c2, 0x0037) },
+	/* SoundGraph iMON OEM LCD (IR & LCD) */
 	{ USB_DEVICE(0x15c2, 0x0038) },
+	/* device specifics unknown */
+	{ USB_DEVICE(0x15c2, 0x0039) },
+	/* device specifics unknown */
+	{ USB_DEVICE(0x15c2, 0x003a) },
+	/* device specifics unknown */
+	{ USB_DEVICE(0x15c2, 0x003b) },
+	/* SoundGraph iMON OEM Inside (IR only) */
+	{ USB_DEVICE(0x15c2, 0x003c) },
+	/* device specifics unknown */
+	{ USB_DEVICE(0x15c2, 0x003d) },
+	/* device specifics unknown */
+	{ USB_DEVICE(0x15c2, 0x003e) },
+	/* device specifics unknown */
+	{ USB_DEVICE(0x15c2, 0x003f) },
+	/* device specifics unknown */
+	{ USB_DEVICE(0x15c2, 0x0040) },
 	/* SoundGraph iMON MINI (IR only) */
 	{ USB_DEVICE(0x15c2, 0x0041) },
 	/* Antec Veris Multimedia Station EZ External (IR only) */
@@ -197,50 +276,56 @@ static struct usb_device_id imon_usb_id_table[] = {
 	{ USB_DEVICE(0x15c2, 0x0044) },
 	/* Antec Veris Multimedia Station Premiere (IR & LCD) */
 	{ USB_DEVICE(0x15c2, 0x0045) },
+	/* device specifics unknown */
+	{ USB_DEVICE(0x15c2, 0x0046) },
 	{}
 };
 
-/* Some iMON models requires a 6th packet */
-static struct usb_device_id display_proto_6p_list[] = {
+/* Some iMON VFD models requires a 6th packet for VFD writes */
+static struct usb_device_id vfd_proto_6p_list[] = {
 	{ USB_DEVICE(0x15c2, 0xffda) },
 	{ USB_DEVICE(0x15c2, 0xffdc) },
-	{ USB_DEVICE(0x15c2, 0x0034) },
 	{ USB_DEVICE(0x15c2, 0x0036) },
-	{ USB_DEVICE(0x15c2, 0x0038) },
-	{ USB_DEVICE(0x15c2, 0x0041) },
-	{ USB_DEVICE(0x15c2, 0x0042) },
-	{ USB_DEVICE(0x15c2, 0x0043) },
 	{ USB_DEVICE(0x15c2, 0x0044) },
-	{ USB_DEVICE(0x15c2, 0x0045) },
 	{}
 };
-static unsigned char display_packet6[] = {
-	0x01, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF };
 
 /* newer iMON models use control endpoints */
 static struct usb_device_id ctl_ep_device_list[] = {
 	{ USB_DEVICE(0x15c2, 0x0034) },
+	{ USB_DEVICE(0x15c2, 0x0035) },
 	{ USB_DEVICE(0x15c2, 0x0036) },
+	{ USB_DEVICE(0x15c2, 0x0037) },
 	{ USB_DEVICE(0x15c2, 0x0038) },
+	{ USB_DEVICE(0x15c2, 0x0039) },
+	{ USB_DEVICE(0x15c2, 0x003a) },
+	{ USB_DEVICE(0x15c2, 0x003b) },
+	{ USB_DEVICE(0x15c2, 0x003c) },
+	{ USB_DEVICE(0x15c2, 0x003d) },
+	{ USB_DEVICE(0x15c2, 0x003e) },
+	{ USB_DEVICE(0x15c2, 0x003f) },
+	{ USB_DEVICE(0x15c2, 0x0040) },
 	{ USB_DEVICE(0x15c2, 0x0041) },
 	{ USB_DEVICE(0x15c2, 0x0042) },
 	{ USB_DEVICE(0x15c2, 0x0043) },
 	{ USB_DEVICE(0x15c2, 0x0044) },
 	{ USB_DEVICE(0x15c2, 0x0045) },
+	{ USB_DEVICE(0x15c2, 0x0046) },
 	{}
 };
 
 /* iMON LCD models use a different write op */
 static struct usb_device_id lcd_device_list[] = {
 	{ USB_DEVICE(0x15c2, 0xffdc) },
-	{ USB_DEVICE(0x15c2, 0x0034) },
 	{ USB_DEVICE(0x15c2, 0x0038) },
 	{ USB_DEVICE(0x15c2, 0x0045) },
 	{}
 };
 
-/* iMON devices with front panel buttons need a larger buffer */
+/* iMON devices with front panel buttons or touchscreen need a larger buffer */
 static struct usb_device_id large_buffer_list[] = {
+	{ USB_DEVICE(0x15c2, 0x0034) },
+	{ USB_DEVICE(0x15c2, 0x0035) },
 	{ USB_DEVICE(0x15c2, 0x0038) },
 	{ USB_DEVICE(0x15c2, 0x0045) },
 };
@@ -249,13 +334,24 @@ static struct usb_device_id large_buffer_list[] = {
 static struct usb_device_id ir_onboard_decode_list[] = {
 	{ USB_DEVICE(0x15c2, 0xffdc) },
 	{ USB_DEVICE(0x15c2, 0x0034) },
+	{ USB_DEVICE(0x15c2, 0x0035) },
 	{ USB_DEVICE(0x15c2, 0x0036) },
+	{ USB_DEVICE(0x15c2, 0x0037) },
 	{ USB_DEVICE(0x15c2, 0x0038) },
+	{ USB_DEVICE(0x15c2, 0x0039) },
+	{ USB_DEVICE(0x15c2, 0x003a) },
+	{ USB_DEVICE(0x15c2, 0x003b) },
+	{ USB_DEVICE(0x15c2, 0x003c) },
+	{ USB_DEVICE(0x15c2, 0x003d) },
+	{ USB_DEVICE(0x15c2, 0x003e) },
+	{ USB_DEVICE(0x15c2, 0x003f) },
+	{ USB_DEVICE(0x15c2, 0x0040) },
 	{ USB_DEVICE(0x15c2, 0x0041) },
 	{ USB_DEVICE(0x15c2, 0x0042) },
 	{ USB_DEVICE(0x15c2, 0x0043) },
 	{ USB_DEVICE(0x15c2, 0x0044) },
 	{ USB_DEVICE(0x15c2, 0x0045) },
+	{ USB_DEVICE(0x15c2, 0x0046) },
 	{}
 };
 
@@ -265,12 +361,20 @@ static struct usb_device_id ir_only_list[] = {
 	{ USB_DEVICE(0x04e8, 0xff30) },
 	/* the first imon lcd and the knob share this device id. :\ */
 	/*{ USB_DEVICE(0x15c2, 0xffdc) },*/
+	{ USB_DEVICE(0x15c2, 0x003c) },
 	{ USB_DEVICE(0x15c2, 0x0041) },
 	{ USB_DEVICE(0x15c2, 0x0042) },
 	{ USB_DEVICE(0x15c2, 0x0043) },
 	{}
 };
 
+/* iMON devices with VGA touchscreens */
+static struct usb_device_id imon_touchscreen_list[] = {
+	{ USB_DEVICE(0x15c2, 0x0034) },
+	{ USB_DEVICE(0x15c2, 0x0035) },
+	{}
+};
+
 /* USB Device data */
 static struct usb_driver imon_driver = {
 	LIRC_THIS_MODULE(.owner = THIS_MODULE)
@@ -280,68 +384,72 @@ static struct usb_driver imon_driver = {
 	.suspend	= imon_suspend,
 	.resume		= imon_resume,
 	.id_table	= imon_usb_id_table,
-#if !defined(KERNEL_2_5)
-	.fops		= &display_fops,
-	.minor		= DISPLAY_MINOR_BASE,
-#endif
 };
 
-#ifdef KERNEL_2_5
 static struct usb_class_driver imon_class = {
-	.name		= DEVFS_NAME,
+	.name		= DEVICE_NAME,
 	.fops		= &display_fops,
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 15)
 	.mode		= DEVFS_MODE,
 #endif
 	.minor_base	= DISPLAY_MINOR_BASE,
 };
-#endif
 
-/* to prevent races between open() and disconnect() */
-static DEFINE_MUTEX(disconnect_lock);
+/* to prevent races between open() and disconnect(), probing, etc */
+static DEFINE_MUTEX(driver_lock);
 
 static int debug;
 
-/* lcd, vfd or none? should be auto-detected, but can be overridden... */
+/* lcd, vfd, vga or none? should be auto-detected, but can be overridden... */
 static int display_type;
 
-#if !defined(KERNEL_2_5)
-
-#define MAX_DEVICES	4	/* In case there's more than one iMON device */
+/* IR protocol: native iMON, Windows MCE (RC-6), or iMON w/o PAD stabilize */
+static int ir_protocol;
 
-static struct imon_context *minor_table[MAX_DEVICES];
+/*
+ * In certain use cases, mouse mode isn't really helpful, and could actually
+ * cause confusion, so allow disabling it when the IR device is open.
+ */
+static int nomouse;
 
-/* the global usb devfs handle */
-extern devfs_handle_t usb_devfs_handle;
+/* threshold at which a pad push registers as an arrow key in kbd mode */
+static int pad_thresh;
 
-#endif
 
 /***  M O D U L E   C O D E ***/
 
 MODULE_AUTHOR(MOD_AUTHOR);
 MODULE_DESCRIPTION(MOD_DESC);
+MODULE_VERSION(MOD_VERSION);
 MODULE_LICENSE("GPL");
 MODULE_DEVICE_TABLE(usb, imon_usb_id_table);
-
 module_param(debug, int, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(debug, "Debug messages: 0=no, 1=yes(default: no)");
-
 module_param(display_type, int, S_IRUGO);
 MODULE_PARM_DESC(display_type, "Type of attached display. 0=autodetect, "
-		 "1=vfd, 2=lcd, 3=none (default: autodetect)");
-
-static void delete_context(struct imon_context *context)
+		 "1=vfd, 2=lcd, 3=vga, 4=none (default: autodetect)");
+module_param(ir_protocol, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(ir_protocol, "Which IR protocol to use. 0=native iMON, "
+		 "1=Windows Media Center Ed. (RC-6), 2=iMON w/o PAD stabilize "
+		 "(default: native iMON)");
+module_param(nomouse, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(nomouse, "Disable mouse input device mode when IR device is "
+		 "open. 0=don't disable, 1=disable. (default: don't disable)");
+module_param(pad_thresh, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(pad_thresh, "Threshold at which a pad push registers as an "
+		 "arrow key in kbd mode (default: 28)");
+
+static void free_imon_context(struct imon_context *context)
 {
-	if (context->display_supported)
-		usb_free_urb(context->tx_urb);
-	usb_free_urb(context->rx_urb);
+	usb_free_urb(context->tx_urb);
+	usb_free_urb(context->rx_urb_intf0);
+	usb_free_urb(context->rx_urb_intf1);
 	lirc_buffer_free(context->driver->rbuf);
 	kfree(context->driver->rbuf);
 	kfree(context->driver);
 	kfree(context);
 
-	if (debug)
-		printk(KERN_INFO "%s: context deleted\n", __func__);
+	dprintk("%s: iMON context freed\n", __func__);
 }
 
 static void deregister_from_lirc(struct imon_context *context)
@@ -354,8 +462,8 @@ static void deregister_from_lirc(struct imon_context *context)
 		err("%s: unable to deregister from lirc(%d)",
 			__func__, retval);
 	else
-		printk(KERN_INFO "Deregistered iMON driver(minor:%d)\n",
-		       minor);
+		printk(KERN_INFO MOD_NAME ": Deregistered iMON driver "
+		       "(minor:%d)\n", minor);
 
 }
 
@@ -365,17 +473,14 @@ static void deregister_from_lirc(struct imon_context *context)
  */
 static int display_open(struct inode *inode, struct file *file)
 {
-#ifdef KERNEL_2_5
 	struct usb_interface *interface;
-#endif
 	struct imon_context *context = NULL;
 	int subminor;
 	int retval = 0;
 
 	/* prevent races with disconnect */
-	mutex_lock(&disconnect_lock);
+	mutex_lock(&driver_lock);
 
-#ifdef KERNEL_2_5
 	subminor = iminor(inode);
 	interface = usb_find_interface(&imon_driver, subminor);
 	if (!interface) {
@@ -385,15 +490,6 @@ static int display_open(struct inode *inode, struct file *file)
 		goto exit;
 	}
 	context = usb_get_intfdata(interface);
-#else
-	subminor = MINOR(inode->i_rdev) - DISPLAY_MINOR_BASE;
-	if (subminor < 0 || subminor >= MAX_DEVICES) {
-		err("%s: no record of minor %d", __func__, subminor);
-		retval = -ENODEV;
-		goto exit;
-	}
-	context = minor_table[subminor];
-#endif
 
 	if (!context) {
 		err("%s: no context found for minor %d",
@@ -420,12 +516,12 @@ static int display_open(struct inode *inode, struct file *file)
 	mutex_unlock(&context->lock);
 
 exit:
-	mutex_unlock(&disconnect_lock);
+	mutex_unlock(&driver_lock);
 	return retval;
 }
 
 /**
- * Called when the display device(e.g. /dev/lcd0)
+ * Called when the display device (e.g. /dev/lcd0)
  * is closed by the application.
  */
 static int display_close(struct inode *inode, struct file *file)
@@ -433,7 +529,7 @@ static int display_close(struct inode *inode, struct file *file)
 	struct imon_context *context = NULL;
 	int retval = 0;
 
-	context = (struct imon_context *) file->private_data;
+	context = (struct imon_context *)file->private_data;
 
 	if (!context) {
 		err("%s: no context for device", __func__);
@@ -452,14 +548,14 @@ static int display_close(struct inode *inode, struct file *file)
 		context->display_isopen = 0;
 		MOD_DEC_USE_COUNT;
 		printk(KERN_INFO "display port closed\n");
-		if (!context->dev_present && !context->ir_isopen) {
+		if (!context->dev_present_intf0 && !context->ir_isopen) {
 			/*
 			 * Device disconnected before close and IR port is not
 			 * open. If IR port is open, context will be deleted by
 			 * ir_close.
 			 */
 			mutex_unlock(&context->lock);
-			delete_context(context);
+			free_imon_context(context);
 			return retval;
 		}
 	}
@@ -469,7 +565,7 @@ static int display_close(struct inode *inode, struct file *file)
 }
 
 /**
- * Sends a packet to the display.
+ * Sends a packet to the device
  */
 static int send_packet(struct imon_context *context)
 {
@@ -480,13 +576,11 @@ static int send_packet(struct imon_context *context)
 
 	/* Check if we need to use control or interrupt urb */
 	if (!context->tx_control) {
-		pipe = usb_sndintpipe(context->usbdev,
+		pipe = usb_sndintpipe(context->usbdev_intf0,
 				      context->tx_endpoint->bEndpointAddress);
-#ifdef KERNEL_2_5
 		interval = context->tx_endpoint->bInterval;
-#endif	/* Use 0 for 2.4 kernels */
 
-		usb_fill_int_urb(context->tx_urb, context->usbdev, pipe,
+		usb_fill_int_urb(context->tx_urb, context->usbdev_intf0, pipe,
 				 context->usb_tx_buf,
 				 sizeof(context->usb_tx_buf),
 				 usb_tx_callback, context, interval);
@@ -507,10 +601,10 @@ static int send_packet(struct imon_context *context)
 		control_req->wLength = cpu_to_le16(0x0008);
 
 		/* control pipe is endpoint 0x00 */
-		pipe = usb_sndctrlpipe(context->usbdev, 0);
+		pipe = usb_sndctrlpipe(context->usbdev_intf0, 0);
 
 		/* build the control urb */
-		usb_fill_control_urb(context->tx_urb, context->usbdev, pipe,
+		usb_fill_control_urb(context->tx_urb, context->usbdev_intf0, pipe,
 				     (unsigned char *)control_req,
 				     context->usb_tx_buf,
 				     sizeof(context->usb_tx_buf),
@@ -521,18 +615,17 @@ static int send_packet(struct imon_context *context)
 	init_completion(&context->tx.finished);
 	atomic_set(&(context->tx.busy), 1);
 
-#ifdef KERNEL_2_5
 	retval = usb_submit_urb(context->tx_urb, GFP_KERNEL);
-#else
-	retval =  usb_submit_urb(context->tx_urb);
-#endif
 	if (retval) {
 		atomic_set(&(context->tx.busy), 0);
 		err("%s: error submitting urb(%d)", __func__, retval);
 	} else {
 		/* Wait for transmission to complete (or abort) */
 		mutex_unlock(&context->lock);
-		wait_for_completion(&context->tx.finished);
+		retval = wait_for_completion_interruptible(
+				&context->tx.finished);
+		if (retval)
+			err("%s: task interrupted", __func__);
 		mutex_lock(&context->lock);
 
 		retval = context->tx.status;
@@ -559,24 +652,115 @@ static int send_associate_24g(struct imon_context *context)
 	const unsigned char packet[8] = { 0x01, 0x00, 0x00, 0x00,
 					  0x00, 0x00, 0x00, 0x20 };
 
-	if (!context->dev_present) {
+	if (!context) {
+		err("%s: no context for device", __func__);
+		return -ENODEV;
+	}
+
+	if (!context->dev_present_intf0) {
 		err("%s: no iMON device present", __func__);
-		retval = -ENODEV;
-		goto exit;
+		return -ENODEV;
 	}
 
 	memcpy(context->usb_tx_buf, packet, sizeof(packet));
 	retval = send_packet(context);
 
-exit:
 	return retval;
 }
 
-#ifdef KERNEL_2_5
 /**
- * These are the sysfs functions to handle the association on the iMON 2.4G LT.
+ * Sends packets to setup and show clock on iMON display
+ *
+ * Arguments: year - last 2 digits of year, month - 1..12,
+ * day - 1..31, dow - day of the week (0-Sun...6-Sat),
+ * hour - 0..23, minute - 0..59, second - 0..59
  */
+static int send_set_imon_clock(struct imon_context *context,
+			       unsigned int year, unsigned int month,
+			       unsigned int day, unsigned int dow,
+			       unsigned int hour, unsigned int minute,
+			       unsigned int second)
+{
+	unsigned char clock_enable_pkt[IMON_CLOCK_ENABLE_PACKETS][8];
+	int retval = 0;
+	int i;
 
+	if (!context) {
+		err("%s: no context for device", __func__);
+		return -ENODEV;
+	}
+
+	switch(context->display_type) {
+	case IMON_DISPLAY_TYPE_LCD:
+		clock_enable_pkt[0][0] = 0x80;
+		clock_enable_pkt[0][1] = year;
+		clock_enable_pkt[0][2] = month-1;
+		clock_enable_pkt[0][3] = day;
+		clock_enable_pkt[0][4] = hour;
+		clock_enable_pkt[0][5] = minute;
+		clock_enable_pkt[0][6] = second;
+
+		clock_enable_pkt[1][0] = 0x80;
+		clock_enable_pkt[1][1] = 0;
+		clock_enable_pkt[1][2] = 0;
+		clock_enable_pkt[1][3] = 0;
+		clock_enable_pkt[1][4] = 0;
+		clock_enable_pkt[1][5] = 0;
+		clock_enable_pkt[1][6] = 0;
+
+		if (context->ffdc_dev) {
+			clock_enable_pkt[0][7] = 0x50;
+			clock_enable_pkt[1][7] = 0x51;
+		} else {
+			clock_enable_pkt[0][7] = 0x88;
+			clock_enable_pkt[1][7] = 0x8a;
+		}
+
+		break;
+
+	case IMON_DISPLAY_TYPE_VFD:
+		clock_enable_pkt[0][0] = year;
+		clock_enable_pkt[0][1] = month-1;
+		clock_enable_pkt[0][2] = day;
+		clock_enable_pkt[0][3] = dow;
+		clock_enable_pkt[0][4] = hour;
+		clock_enable_pkt[0][5] = minute;
+		clock_enable_pkt[0][6] = second;
+		clock_enable_pkt[0][7] = 0x40;
+
+		clock_enable_pkt[1][0] = 0;
+		clock_enable_pkt[1][1] = 0;
+		clock_enable_pkt[1][2] = 1;
+		clock_enable_pkt[1][3] = 0;
+		clock_enable_pkt[1][4] = 0;
+		clock_enable_pkt[1][5] = 0;
+		clock_enable_pkt[1][6] = 0;
+		clock_enable_pkt[1][7] = 0x42;
+
+		break;
+
+	default:
+		return -ENODEV;
+	}
+
+
+	for (i = 0; i < IMON_CLOCK_ENABLE_PACKETS; i++) {
+		memcpy(context->usb_tx_buf, clock_enable_pkt[i], 8);
+		retval = send_packet(context);
+		if (retval) {
+			err("%s: send_packet failed for packet %d",
+			    __func__, i);
+			break;
+		}
+	}
+
+	return retval;
+
+}
+
+/**
+ * These are the sysfs functions to handle the association on the iMON 2.4G LT.
+ */
 static ssize_t show_associate_remote(struct device *d,
 				     struct device_attribute *attr,
 				     char *buf)
@@ -588,16 +772,15 @@ static ssize_t show_associate_remote(struct device *d,
 
 	mutex_lock(&context->lock);
 	if (context->ir_isassociating) {
-		strcpy(buf, "The device it associating press some button "
-			    "on the remote.\n");
+		strcpy(buf, "associating\n");
 	} else if (context->ir_isopen) {
-		strcpy(buf, "Device is open and ready to associate.\n"
-			    "Echo something into this file to start "
-			    "the process.\n");
+		strcpy(buf, "open\n");
 	} else {
-		strcpy(buf, "Device is closed, you need to open it to "
-			    "associate the remote(you can use irw).\n");
+		strcpy(buf, "closed\n");
 	}
+	printk(KERN_INFO "Visit http://www.lirc.org/html/imon-24g.html for "
+	       "instructions on how to associate your iMON 2.4G DT/LT "
+	       "remote\n");
 	mutex_unlock(&context->lock);
 	return strlen(buf);
 }
@@ -628,21 +811,105 @@ static ssize_t store_associate_remote(struct device *d,
 	return count;
 }
 
+/**
+ * sysfs functions to control internal imon clock
+ */
+static ssize_t show_imon_clock(struct device *d,
+			       struct device_attribute *attr, char *buf)
+{
+	struct imon_context *context = dev_get_drvdata(d);
+	size_t len;
+
+	if (!context)
+		return -ENODEV;
+
+	mutex_lock(&context->lock);
+
+	if (!context->display_supported) {
+		len = snprintf(buf, PAGE_SIZE, "Not supported.");
+	} else {
+		len = snprintf(buf, PAGE_SIZE,
+			"To set the clock on your iMON display:\n"
+			"# date \"+%%y %%m %%d %%w %%H %%M %%S\" > imon_clock\n"
+			"%s", context->display_isopen ?
+			"\nNOTE: imon device must be closed\n" : "");
+	}
+
+	mutex_unlock(&context->lock);
+
+	return len;
+}
+
+static ssize_t store_imon_clock(struct device *d,
+				struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+	struct imon_context *context = dev_get_drvdata(d);
+	ssize_t retval;
+	unsigned int year, month, day, dow, hour, minute, second;
+
+	if (!context)
+		return -ENODEV;
+
+	mutex_lock(&context->lock);
+
+	if (!context->display_supported) {
+		retval = -ENODEV;
+		goto exit;
+	} else if (context->display_isopen) {
+		retval = -EBUSY;
+		goto exit;
+	}
+
+	if (sscanf(buf, "%u %u %u %u %u %u %u",	&year, &month, &day, &dow,
+		   &hour, &minute, &second) != 7) {
+		retval = -EINVAL;
+		goto exit;
+	}
+
+	if ((month < 1 || month > 12) ||
+	    (day < 1 || day > 31) || (dow > 6) ||
+	    (hour > 23) || (minute > 59) || (second > 59)) {
+		retval = -EINVAL;
+		goto exit;
+	}
+
+	retval = send_set_imon_clock(context, year, month, day, dow,
+				     hour, minute, second);
+	if (retval)
+		goto exit;
+
+	retval = count;
+exit:
+	mutex_unlock(&context->lock);
+
+	return retval;
+}
+
+
+static DEVICE_ATTR(imon_clock, S_IWUSR | S_IRUGO, show_imon_clock,
+		   store_imon_clock);
+
 static DEVICE_ATTR(associate_remote, S_IWUSR | S_IRUGO, show_associate_remote,
 		   store_associate_remote);
 
-static struct attribute *imon_sysfs_entries[] = {
-	&dev_attr_associate_remote.attr,
+static struct attribute *imon_display_sysfs_entries[] = {
+	&dev_attr_imon_clock.attr,
 	NULL
 };
 
-static struct attribute_group imon_attribute_group = {
-	.attrs = imon_sysfs_entries
+static struct attribute_group imon_display_attribute_group = {
+	.attrs = imon_display_sysfs_entries
 };
 
-#endif
-
+static struct attribute *imon_rf_sysfs_entries[] = {
+	&dev_attr_associate_remote.attr,
+	NULL
+};
 
+static struct attribute_group imon_rf_attribute_group = {
+	.attrs = imon_rf_sysfs_entries
+};
 
 /**
  * Writes data to the VFD.  The iMON VFD is 2x16 characters
@@ -663,8 +930,10 @@ static ssize_t vfd_write(struct file *file, const char *buf,
 	int seq;
 	int retval = 0;
 	struct imon_context *context;
+	const unsigned char vfd_packet6[] = {
+		0x01, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF };
 
-	context = (struct imon_context *) file->private_data;
+	context = (struct imon_context *)file->private_data;
 	if (!context) {
 		err("%s: no context for device", __func__);
 		return -ENODEV;
@@ -672,7 +941,7 @@ static ssize_t vfd_write(struct file *file, const char *buf,
 
 	mutex_lock(&context->lock);
 
-	if (!context->dev_present) {
+	if (!context->dev_present_intf0) {
 		err("%s: no iMON device present", __func__);
 		retval = -ENODEV;
 		goto exit;
@@ -715,9 +984,9 @@ static ssize_t vfd_write(struct file *file, const char *buf,
 
 	} while (offset < 35);
 
-	if (context->display_proto_6p) {
+	if (context->vfd_proto_6p) {
 		/* Send packet #6 */
-		memcpy(context->usb_tx_buf, display_packet6, 7);
+		memcpy(context->usb_tx_buf, &vfd_packet6, sizeof(vfd_packet6));
 		context->usb_tx_buf[7] = (unsigned char) seq;
 		retval = send_packet(context);
 		if (retval)
@@ -750,7 +1019,7 @@ static ssize_t lcd_write(struct file *file, const char *buf,
 	int retval = 0;
 	struct imon_context *context;
 
-	context = (struct imon_context *) file->private_data;
+	context = (struct imon_context *)file->private_data;
 	if (!context) {
 		err("%s: no context for device", __func__);
 		return -ENODEV;
@@ -758,8 +1027,8 @@ static ssize_t lcd_write(struct file *file, const char *buf,
 
 	mutex_lock(&context->lock);
 
-	if (!context->dev_present) {
-		err("%s: no iMON device present", __func__);
+	if (!context->display_supported) {
+		err("%s: no iMON display present", __func__);
 		retval = -ENODEV;
 		goto exit;
 	}
@@ -780,9 +1049,8 @@ static ssize_t lcd_write(struct file *file, const char *buf,
 	if (retval) {
 		err("%s: send packet failed!", __func__);
 		goto exit;
-	} else if (debug) {
-		printk(KERN_INFO "%s: write %d bytes to LCD\n",
-		       __func__, (int) n_bytes);
+	} else {
+		dprintk("%s: write %d bytes to LCD\n", __func__, (int) n_bytes);
 	}
 exit:
 	mutex_unlock(&context->lock);
@@ -792,7 +1060,7 @@ exit:
 /**
  * Callback function for USB core API: transmit data
  */
-#if defined(KERNEL_2_5) && LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
 static void usb_tx_callback(struct urb *urb, struct pt_regs *regs)
 #else
 static void usb_tx_callback(struct urb *urb)
@@ -802,7 +1070,7 @@ static void usb_tx_callback(struct urb *urb)
 
 	if (!urb)
 		return;
-	context = (struct imon_context *) urb->context;
+	context = (struct imon_context *)urb->context;
 	if (!context)
 		return;
 
@@ -816,6 +1084,56 @@ static void usb_tx_callback(struct urb *urb)
 }
 
 /**
+ * iMON IR receivers support two different signal sets -- those used by
+ * the iMON remotes, and those used by the Windows MCE remotes (which is
+ * really just RC-6), but only one or the other at a time, as the signals
+ * are decoded onboard the receiver.
+ */
+static void imon_set_ir_protocol(struct imon_context *context)
+{
+	int retval;
+	unsigned char ir_proto_packet[] =
+		{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86 };
+
+	switch (ir_protocol) {
+	case IMON_IR_PROTOCOL_MCE:
+		/* MCE proto not supported on devices without tx control */
+		if (!context->tx_control) {
+			printk(KERN_INFO "%s: MCE IR protocol not supported on "
+			       "this device, using iMON protocol\n", __func__);
+			context->ir_protocol = IMON_IR_PROTOCOL_IMON;
+			return;
+		}
+		dprintk("Configuring IR receiver for MCE protocol\n");
+		ir_proto_packet[0] = 0x01;
+		context->ir_protocol = IMON_IR_PROTOCOL_MCE;
+		break;
+	case IMON_IR_PROTOCOL_IMON:
+		dprintk("Configuring IR receiver for iMON protocol\n");
+		/* ir_proto_packet[0] = 0x00; // already the default */
+		context->ir_protocol = IMON_IR_PROTOCOL_IMON;
+		break;
+	case IMON_IR_PROTOCOL_IMON_NOPAD:
+		dprintk("Configuring IR receiver for iMON protocol without "
+			"PAD stabilize function enabled\n");
+		/* ir_proto_packet[0] = 0x00; // already the default */
+		context->ir_protocol = IMON_IR_PROTOCOL_IMON_NOPAD;
+		break;
+	default:
+		printk(KERN_INFO "%s: unknown IR protocol specified, will "
+		       "just default to iMON protocol\n", __func__);
+		context->ir_protocol = IMON_IR_PROTOCOL_IMON;
+		break;
+	}
+	memcpy(context->usb_tx_buf, &ir_proto_packet,
+	       sizeof(ir_proto_packet));
+	retval = send_packet(context);
+	if (retval)
+		printk(KERN_INFO "%s: failed to set remote type\n", __func__);
+}
+
+
+/**
  * Called by lirc_dev when the application opens /dev/lirc
  */
 static int ir_open(void *data)
@@ -824,48 +1142,23 @@ static int ir_open(void *data)
 	struct imon_context *context;
 
 	/* prevent races with disconnect */
-	mutex_lock(&disconnect_lock);
-
-	context = (struct imon_context *) data;
-
-	mutex_lock(&context->lock);
+	mutex_lock(&driver_lock);
 
-	if (context->ir_isopen) {
-		err("%s: IR port is already open", __func__);
-		retval = -EBUSY;
-		goto exit;
-	}
+	context = (struct imon_context *)data;
 
 	/* initial IR protocol decode variables */
 	context->rx.count = 0;
 	context->rx.initial_space = 1;
 	context->rx.prev_bit = 0;
 
-	usb_fill_int_urb(context->rx_urb, context->usbdev,
-		usb_rcvintpipe(context->usbdev,
-				context->rx_endpoint->bEndpointAddress),
-		context->usb_rx_buf, sizeof(context->usb_rx_buf),
-		usb_rx_callback, context, context->rx_endpoint->bInterval);
-
-#ifdef KERNEL_2_5
-	retval = usb_submit_urb(context->rx_urb, GFP_KERNEL);
-#else
-	retval = usb_submit_urb(context->rx_urb);
-#endif
-
-	if (retval)
-		err("%s: usb_submit_urb failed for ir_open(%d)",
-		    __func__, retval);
-	else {
-		MOD_INC_USE_COUNT;
-		context->ir_isopen = 1;
-		printk(KERN_INFO "IR port opened\n");
-	}
+	/* set new IR protocol if it has changed since init or last open */
+	if (ir_protocol != context->ir_protocol)
+		imon_set_ir_protocol(context);
 
-exit:
-	mutex_unlock(&context->lock);
+	context->ir_isopen = 1;
+	printk(KERN_INFO MOD_NAME ": IR port opened\n");
 
-	mutex_unlock(&disconnect_lock);
+	mutex_unlock(&driver_lock);
 	return retval;
 }
 
@@ -884,13 +1177,12 @@ static void ir_close(void *data)
 
 	mutex_lock(&context->lock);
 
-	usb_kill_urb(context->rx_urb);
 	context->ir_isopen = 0;
 	context->ir_isassociating = 0;
 	MOD_DEC_USE_COUNT;
-	printk(KERN_INFO "IR port closed\n");
+	printk(KERN_INFO MOD_NAME ": IR port closed\n");
 
-	if (!context->dev_present) {
+	if (!context->dev_present_intf0) {
 		/*
 		 * Device disconnected while IR port was still open. Driver
 		 * was not deregistered at disconnect time, so do it now.
@@ -899,7 +1191,7 @@ static void ir_close(void *data)
 
 		if (!context->display_isopen) {
 			mutex_unlock(&context->lock);
-			delete_context(context);
+			free_imon_context(context);
 			return;
 		}
 		/*
@@ -913,7 +1205,7 @@ static void ir_close(void *data)
 }
 
 /**
- * Convert bit count to time duration(in us) and submit
+ * Convert bit count to time duration (in us) and submit
  * the value to lirc_dev.
  */
 static void submit_data(struct imon_context *context)
@@ -922,8 +1214,7 @@ static void submit_data(struct imon_context *context)
 	int value = context->rx.count;
 	int i;
 
-	if (debug)
-		printk(KERN_INFO "submitting data to LIRC\n");
+	dprintk("submitting data to LIRC\n");
 
 	value *= BIT_DURATION;
 	value &= PULSE_MASK;
@@ -940,116 +1231,233 @@ static void submit_data(struct imon_context *context)
 
 static inline int tv2int(const struct timeval *a, const struct timeval *b)
 {
-       int usecs = 0;
-       int sec   = 0;
+	int usecs = 0;
+	int sec   = 0;
 
-       if (b->tv_usec > a->tv_usec) {
-	       usecs = 1000000;
-	       sec--;
-       }
+	if (b->tv_usec > a->tv_usec) {
+		usecs = 1000000;
+		sec--;
+	}
 
-       usecs += a->tv_usec - b->tv_usec;
+	usecs += a->tv_usec - b->tv_usec;
 
-       sec += a->tv_sec - b->tv_sec;
-       sec *= 1000;
-       usecs /= 1000;
-       sec += usecs;
+	sec += a->tv_sec - b->tv_sec;
+	sec *= 1000;
+	usecs /= 1000;
+	sec += usecs;
 
-       if (sec < 0)
-	       sec = 1000;
+	if (sec < 0)
+		sec = 1000;
 
-       return sec;
+	return sec;
 }
 
 /**
- * The directional pad is overly sensitive in keyboard mode, so we do some
- * interesting contortions to make it less touchy.
+ * The directional pad behaves a bit differently, depending on whether this is
+ * one of the older ffdc devices or a newer device. Newer devices appear to
+ * have a higher resolution matrix for more precise mouse movement, but it
+ * makes things overly sensitive in keyboard mode, so we do some interesting
+ * contortions to make it less touchy. Older devices run through the same
+ * routine with shorter timeout and a smaller threshold.
  */
-#define IMON_PAD_TIMEOUT       1000    /* in msecs */
-#define IMON_PAD_THRESHOLD     80      /* 160x160 square */
-static int stabilize(int a, int b)
+static int stabilize(int a, int b, u16 timeout, u16 threshold)
 {
-       struct timeval ct;
-       static struct timeval prev_time = {0, 0};
-       static struct timeval hit_time  = {0, 0};
-       static int x, y, prev_result, hits;
-       int result = 0;
-       int msec, msec_hit;
-
-       do_gettimeofday(&ct);
-       msec = tv2int(&ct, &prev_time);
-       msec_hit = tv2int(&ct, &hit_time);
-
-       if (msec > 100) {
-	       x = 0;
-	       y = 0;
-	       hits = 0;
-       }
-
-       x += a;
-       y += b;
-
-       prev_time = ct;
-
-       if (abs(x) > IMON_PAD_THRESHOLD || abs(y) > IMON_PAD_THRESHOLD) {
-	       if (abs(y) > abs(x))
-		       result = (y > 0) ? 0x7F : 0x80;
-	       else
-		       result = (x > 0) ? 0x7F00 : 0x8000;
-
-	       x = 0;
-	       y = 0;
-
-	       if (result == prev_result) {
-		       hits++;
-
-		       if (hits > 3) {
-			       switch (result) {
-			       case 0x7F:
-				       y = 17 * IMON_PAD_THRESHOLD / 30;
-				       break;
-			       case 0x80:
-				       y -= 17 * IMON_PAD_THRESHOLD / 30;
-				       break;
-			       case 0x7F00:
-				       x = 17 * IMON_PAD_THRESHOLD / 30;
-				       break;
-			       case 0x8000:
-				       x -= 17 * IMON_PAD_THRESHOLD / 30;
-				       break;
-			       }
-		       }
-
-		       if (hits == 2 && msec_hit < IMON_PAD_TIMEOUT) {
-			       result = 0;
-			       hits = 1;
-		       }
-	       } else {
-		       prev_result = result;
-		       hits = 1;
-		       hit_time = ct;
-	       }
-       }
-
-       return result;
+	struct timeval ct;
+	static struct timeval prev_time = {0, 0};
+	static struct timeval hit_time  = {0, 0};
+	static int x, y, prev_result, hits;
+	int result = 0;
+	int msec, msec_hit;
+
+	do_gettimeofday(&ct);
+	msec = tv2int(&ct, &prev_time);
+	msec_hit = tv2int(&ct, &hit_time);
+
+	if (msec > 100) {
+		x = 0;
+		y = 0;
+		hits = 0;
+	}
+
+	x += a;
+	y += b;
+
+	prev_time = ct;
+
+	if (abs(x) > threshold || abs(y) > threshold) {
+		if (abs(y) > abs(x))
+			result = (y > 0) ? 0x7F : 0x80;
+		else
+			result = (x > 0) ? 0x7F00 : 0x8000;
+
+		x = 0;
+		y = 0;
+
+		if (result == prev_result) {
+			hits++;
+
+			if (hits > 3) {
+				switch (result) {
+				case 0x7F:
+					y = 17 * threshold / 30;
+					break;
+				case 0x80:
+					y -= 17 * threshold / 30;
+					break;
+				case 0x7F00:
+					x = 17 * threshold / 30;
+					break;
+				case 0x8000:
+					x -= 17 * threshold / 30;
+					break;
+				}
+			}
+
+			if (hits == 2 && msec_hit < timeout) {
+				result = 0;
+				hits = 1;
+			}
+		} else {
+			prev_result = result;
+			hits = 1;
+			hit_time = ct;
+		}
+	}
+
+	return result;
 }
 
 /**
  * Process the incoming packet
  */
-static void incoming_packet(struct imon_context *context,
-			    struct urb *urb)
+static void imon_incoming_packet(struct imon_context *context,
+				 struct urb *urb, int intf)
 {
 	int len = urb->actual_length;
 	unsigned char *buf = urb->transfer_buffer;
+	char rel_x = 0x00, rel_y = 0x00;
 	int octet, bit;
 	unsigned char mask;
-	int chunk_num, dir;
-#ifdef DEBUG
-	int i;
+	int i, chunk_num;
+	int ts_input = 0;
+	int dir = 0;
+	u16 timeout, threshold;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18)
+	int mouse_input;
+	int right_shift = 1;
+	struct input_dev *mouse = NULL;
+	struct input_dev *touch = NULL;
+	const unsigned char toggle_button1[] = { 0x29, 0x91, 0x15, 0xb7 };
+	const unsigned char toggle_button2[] = { 0x29, 0x91, 0x35, 0xb7 };
+	const unsigned char ch_up[]   = { 0x28, 0x93, 0x95, 0xb7 };
+	const unsigned char ch_down[] = { 0x28, 0x87, 0x95, 0xb7 };
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18)
+	mouse = context->mouse;
+	if (context->display_type == IMON_DISPLAY_TYPE_VGA)
+		touch = context->touch;
+
+	/* keyboard/mouse mode toggle button */
+	if (memcmp(buf, toggle_button1, 4) == 0 ||
+	    memcmp(buf, toggle_button2, 4) == 0) {
+		if (!nomouse) {
+			context->pad_mouse = ~(context->pad_mouse) & 0x1;
+			dprintk("toggling to %s mode\n",
+				context->pad_mouse ? "mouse" : "keyboard");
+		} else {
+			context->pad_mouse = 0;
+			dprintk("mouse mode was disabled by modparam\n");
+		}
+		return;
+	}
+
+	/* send touchscreen events through input subsystem if touchpad data */
+	if (context->display_type == IMON_DISPLAY_TYPE_VGA && len == 8 &&
+	    buf[7] == 0x86) {
+		if (touch == NULL) {
+			printk(KERN_WARNING "%s: touchscreen input device is "
+			       "NULL!\n", __func__);
+			return;
+		}
+		mod_timer(&context->timer, jiffies + TOUCH_TIMEOUT);
+		context->touch_x = (buf[0] << 4) | (buf[1] >> 4);
+		context->touch_y = 0xfff - ((buf[2] << 4) | (buf[1] & 0xf));
+		input_report_abs(touch, ABS_X, context->touch_x);
+		input_report_abs(touch, ABS_Y, context->touch_y);
+		input_report_key(touch, BTN_TOUCH, 0x01);
+		input_sync(touch);
+		ts_input = 1;
+
+	/* send mouse events through input subsystem in mouse mode */
+	} else if (context->pad_mouse || !context->ir_isopen) {
+		/* newer iMON device PAD or mouse button */
+		if (!context->ffdc_dev && (buf[0] & 0x01) && len == 5) {
+			mouse_input = 1;
+			rel_x = buf[2];
+			rel_y = buf[3];
+			right_shift = 1;
+		/* 0xffdc iMON PAD or mouse button input */
+		} else if (context->ffdc_dev && (buf[0] & 0x40) &&
+			   !((buf[1] & 0x01) || ((buf[1] >> 2) & 0x01))) {
+			mouse_input = 1;
+			rel_x = (buf[1] & 0x08) | (buf[1] & 0x10) >> 2 |
+				(buf[1] & 0x20) >> 4 | (buf[1] & 0x40) >> 6;
+			if (buf[0] & 0x02)
+				rel_x |= ~0x0f;
+			rel_x = rel_x + rel_x / 2;
+			rel_y = (buf[2] & 0x08) | (buf[2] & 0x10) >> 2 |
+				(buf[2] & 0x20) >> 4 | (buf[2] & 0x40) >> 6;
+			if (buf[0] & 0x01)
+				rel_y |= ~0x0f;
+			rel_y = rel_y + rel_y / 2;
+			right_shift = 2;
+		/* some ffdc devices decode mouse buttons differently... */
+		} else if (context->ffdc_dev && (buf[0] == 0x68)) {
+			mouse_input = 1;
+			right_shift = 2;
+		/* ch+/- buttons, which we use for an emulated scroll wheel */
+		} else if (!memcmp(buf, ch_up, 4)) {
+			mouse_input = 1;
+			dir = 1;
+		} else if (!memcmp(buf, ch_down, 4)) {
+			mouse_input = 1;
+			dir = -1;
+		} else
+			mouse_input = 0;
+
+		if (mouse_input) {
+			if (mouse == NULL) {
+				printk(KERN_WARNING "%s: mouse input device "
+				       "is NULL!\n", __func__);
+				return;
+			}
+			dprintk("sending mouse data via input subsystem\n");
+
+			if (dir) {
+				input_report_rel(mouse, REL_WHEEL, dir);
+			} else if (rel_x || rel_y) {
+				input_report_rel(mouse, REL_X, rel_x);
+				input_report_rel(mouse, REL_Y, rel_y);
+			} else {
+				input_report_key(mouse, BTN_LEFT, buf[1] & 0x1);
+				input_report_key(mouse, BTN_RIGHT,
+						 buf[1] >> right_shift & 0x1);
+			}
+			input_sync(mouse);
+			return;
+		}
+	}
 #endif
 
 	/*
+	 * at this point, mouse and touchscreen input has been handled, so
+	 * anything else goes to lirc -- bail out if no listening IR client
+	 */
+	if (!context->ir_isopen)
+		return;
+
+	/*
 	 * we need to add some special handling for
 	 * the imon's IR mouse events
 	 */
@@ -1057,6 +1465,11 @@ static void incoming_packet(struct imon_context *context,
 		/* first, pad to 8 bytes so it conforms with everything else */
 		buf[5] = buf[6] = buf[7] = 0;
 		len = 8;
+		timeout = 500;	/* in msecs */
+		/* (2*threshold) x (2*threshold) square */
+		threshold = pad_thresh ? pad_thresh : 28;
+		rel_x = buf[2];
+		rel_y = buf[3];
 
 		/*
 		 * the imon directional pad functions more like a touchpad.
@@ -1068,13 +1481,25 @@ static void incoming_packet(struct imon_context *context,
 		 * diagonals, it has a tendancy to jump back and forth, so lets
 		 * try to ignore when they get too close
 		 */
-		if ((buf[1] == 0) && ((buf[2] != 0) || (buf[3] != 0))) {
-			dir = stabilize((int)(char)buf[2], (int)(char)buf[3]);
-			if (!dir)
-				return;
-			buf[2] = dir & 0xFF;
-			buf[3] = (dir >> 8) & 0xFF;
+		if (context->ir_protocol == IMON_IR_PROTOCOL_IMON) {
+			if ((buf[1] == 0) && ((rel_x != 0) || (rel_y != 0))) {
+				dir = stabilize((int)rel_x, (int)rel_y,
+						timeout, threshold);
+				if (!dir)
+					return;
+				buf[2] = dir & 0xFF;
+				buf[3] = (dir >> 8) & 0xFF;
+			}
+		} else {
+			if (abs(rel_y) > abs(rel_x)) {
+				buf[2] = (rel_y > 0) ? 0x7F : 0x80;
+				buf[3] = 0;
+			} else {
+				buf[2] = 0;
+				buf[3] = (rel_x > 0) ? 0x7F : 0x80;
+			}
 		}
+
 	} else if ((len == 8) && (buf[0] & 0x40) &&
 		   !(buf[1] & 0x01 || buf[1] >> 2 & 0x01)) {
 		/*
@@ -1089,34 +1514,60 @@ static void incoming_packet(struct imon_context *context,
 		 * stabilize(). The resulting codes will be 0x01008000,
 		 * 0x01007F00, ..., so one can use the normal imon-pad config
 		 * from the remotes dir.
-		 *
 		 */
+		timeout = 10;	/* in msecs */
+		/* (2*threshold) x (2*threshold) square */
+		threshold = pad_thresh ? pad_thresh : 15;
 
 		/* buf[1] is x */
-		int rel_x = (buf[1] & 0x08) | (buf[1] & 0x10) >> 2 |
-			    (buf[1] & 0x20) >> 4 | (buf[1] & 0x40) >> 6;
+		rel_x = (buf[1] & 0x08) | (buf[1] & 0x10) >> 2 |
+			(buf[1] & 0x20) >> 4 | (buf[1] & 0x40) >> 6;
 		if(buf[0] & 0x02)
 			rel_x |= ~0x10+1;
 		/* buf[2] is y */
-		int rel_y = (buf[2] & 0x08) | (buf[2] & 0x10) >> 2 |
-			    (buf[2] & 0x20) >> 4 | (buf[2] & 0x40) >> 6;
+		rel_y = (buf[2] & 0x08) | (buf[2] & 0x10) >> 2 |
+			(buf[2] & 0x20) >> 4 | (buf[2] & 0x40) >> 6;
 		if(buf[0] & 0x01)
 			rel_y |= ~0x10+1;
 
 		buf[0] = 0x01;
-
 		buf[1] = buf[4] = buf[5] = buf[6] = buf[7] = 0;
 
-		dir = stabilize(rel_x, rel_y);
-		if (!dir)
-			return;
-		buf[2] = dir & 0xFF;
-		buf[3] = (dir >> 8) & 0xFF;
+		if (context->ir_protocol == IMON_IR_PROTOCOL_IMON) {
+			dir = stabilize((int)rel_x, (int)rel_y,
+					timeout, threshold);
+			if (!dir)
+				return;
+			buf[2] = dir & 0xFF;
+			buf[3] = (dir >> 8) & 0xFF;
+		} else {
+			if (abs(rel_y) > abs(rel_x)) {
+				buf[2] = (rel_y > 0) ? 0x7F : 0x80;
+				buf[3] = 0;
+			} else {
+				buf[2] = 0;
+				buf[3] = (rel_x > 0) ? 0x7F : 0x80;
+			}
+		}
+
+	} else if (ts_input) {
+		/*
+		 * this is touchscreen input, which we need to down-sample
+		 * to a 64 button matrix at the moment...
+		 */
+		buf[0] = buf[0] >> 5;
+		buf[1] = 0x00;
+		buf[2] = buf[2] >> 5;
+		buf[3] = 0x00;
+		buf[4] = 0x00;
+		buf[5] = 0x00;
+		buf[6] = 0x14;
+		buf[7] = 0xff;
 	}
 
 	if (len != 8) {
-		printk(KERN_WARNING "%s: invalid incoming packet size(%d)\n",
-		     __func__, len);
+		printk(KERN_WARNING "imon %s: invalid incoming packet "
+		       "size (len = %d, intf%d)\n", __func__, len, intf);
 		return;
 	}
 
@@ -1135,7 +1586,7 @@ static void incoming_packet(struct imon_context *context,
 
 	chunk_num = buf[7];
 
-	if (chunk_num == 0xFF)
+	if (chunk_num == 0xFF && !ts_input)
 		return;		/* filler frame, no data here */
 
 	if (buf[0] == 0xFF &&
@@ -1148,11 +1599,15 @@ static void incoming_packet(struct imon_context *context,
 	     (buf[6] == 0x5E && buf[7] == 0xAF)))	/* DT */
 		return;		/* filler frame, no data here */
 
-#ifdef DEBUG
-	for (i = 0; i < 8; ++i)
-		printk(KERN_INFO "%02x ", buf[i]);
-	printk(KERN_INFO "\n");
-#endif
+	if (debug) {
+		if (context->ir_onboard_decode)
+			printk("intf%d decoded packet: ", intf);
+		else
+			printk("raw packet: ");
+		for (i = 0; i < len; ++i)
+			printk("%02x ", buf[i]);
+		printk("\n");
+	}
 
 	if (context->ir_onboard_decode) {
 		/* The signals have been decoded onboard the iMON controller */
@@ -1209,92 +1664,169 @@ static void incoming_packet(struct imon_context *context,
 }
 
 /**
+ * report touchscreen input
+ */
+static void imon_touch_display_timeout(unsigned long data)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18)
+	struct imon_context *context = (struct imon_context *)data;
+	struct input_dev *touch;
+
+	if (!context->display_type == IMON_DISPLAY_TYPE_VGA)
+		return;
+
+	touch = context->touch;
+	input_report_abs(touch, ABS_X, context->touch_x);
+	input_report_abs(touch, ABS_Y, context->touch_y);
+	input_report_key(touch, BTN_TOUCH, 0x00);
+	input_sync(touch);
+#endif
+
+	return;
+}
+
+/**
  * Callback function for USB core API: receive data
  */
-#if defined(KERNEL_2_5) && LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
-static void usb_rx_callback(struct urb *urb, struct pt_regs *regs)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
+static void usb_rx_callback_intf0(struct urb *urb, struct pt_regs *regs)
 #else
-static void usb_rx_callback(struct urb *urb)
+static void usb_rx_callback_intf0(struct urb *urb)
 #endif
 {
 	struct imon_context *context;
+	unsigned char *buf;
+	int len;
+	int intfnum = 0;
 
 	if (!urb)
 		return;
-	context = (struct imon_context *) urb->context;
+
+	context = (struct imon_context *)urb->context;
 	if (!context)
 		return;
 
+	buf = urb->transfer_buffer;
+	len = urb->actual_length;
+
 	switch (urb->status) {
 	case -ENOENT:		/* usbcore unlink successful! */
 		return;
+
 	case 0:
-		if (context->ir_isopen)
-			incoming_packet(context, urb);
+		imon_incoming_packet(context, urb, intfnum);
 		break;
+
 	default:
-		printk(KERN_WARNING "%s: status(%d): ignored\n",
+		printk(KERN_WARNING "imon %s: status(%d): ignored\n",
 		       __func__, urb->status);
 		break;
 	}
 
-#ifdef KERNEL_2_5
-	usb_submit_urb(context->rx_urb, GFP_ATOMIC);
-#endif
+	usb_submit_urb(context->rx_urb_intf0, GFP_ATOMIC);
+
 	return;
 }
 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
+static void usb_rx_callback_intf1(struct urb *urb, struct pt_regs *regs)
+#else
+static void usb_rx_callback_intf1(struct urb *urb)
+#endif
+{
+	struct imon_context *context;
+	unsigned char *buf;
+	int len;
+	int intfnum = 1;
+
+	if (!urb)
+		return;
+
+	context = (struct imon_context *)urb->context;
+	if (!context)
+		return;
+
+	buf = urb->transfer_buffer;
+	len = urb->actual_length;
+
+	switch (urb->status) {
+	case -ENOENT:		/* usbcore unlink successful! */
+		return;
 
+	case 0:
+		imon_incoming_packet(context, urb, intfnum);
+		break;
+
+	default:
+		printk(KERN_WARNING "imon %s: status(%d): ignored\n",
+		       __func__, urb->status);
+		break;
+	}
+
+	usb_submit_urb(context->rx_urb_intf1, GFP_ATOMIC);
+
+	return;
+}
 
 /**
  * Callback function for USB core API: Probe
  */
-#ifdef KERNEL_2_5
 static int imon_probe(struct usb_interface *interface,
 		      const struct usb_device_id *id)
-#else
-static void *imon_probe(struct usb_device *dev, unsigned int intf,
-			const struct usb_device_id *id)
-#endif
 {
-#ifdef KERNEL_2_5
 	struct usb_device *usbdev = NULL;
 	struct usb_host_interface *iface_desc = NULL;
-#else
-	struct usb_interface *interface = NULL;
-	struct usb_interface_descriptor *iface_desc = NULL;
-	char name[10];
-	int subminor = 0;
-#endif
 	struct usb_endpoint_descriptor *rx_endpoint = NULL;
 	struct usb_endpoint_descriptor *tx_endpoint = NULL;
 	struct urb *rx_urb = NULL;
 	struct urb *tx_urb = NULL;
 	struct lirc_driver *driver = NULL;
 	struct lirc_buffer *rbuf = NULL;
+	struct usb_interface *first_if;
+	int ifnum;
 	int lirc_minor = 0;
-	int num_endpoints;
+	int num_endpts;
 	int retval = 0;
-	int display_ep_found;
-	int ir_ep_found;
-	int alloc_status;
-	int display_proto_6p = 0;
+	int display_ep_found = 0;
+	int ir_ep_found = 0;
+	int alloc_status = 0;
+	int vfd_proto_6p = 0;
 	int ir_onboard_decode = 0;
 	int buf_chunk_size = BUF_CHUNK_SIZE;
 	int code_length;
 	int tx_control = 0;
 	struct imon_context *context = NULL;
-	int i;
+	struct imon_context *first_if_context = NULL;
+	int i, sysfs_err;
+	int configured_display_type = IMON_DISPLAY_TYPE_VFD;
+	u16 vendor, product;
+	const unsigned char fp_packet[] = { 0x40, 0x00, 0x00, 0x00,
+					    0x00, 0x00, 0x00, 0x88 };
 
-	printk(KERN_INFO "%s: found iMON device\n", __func__);
+	/*
+	 * Try to auto-detect the type of display if the user hasn't set
+	 * it by hand via the display_type modparam. Default is VFD.
+	 */
+	if (display_type == IMON_DISPLAY_TYPE_AUTO) {
+		if (usb_match_id(interface, lcd_device_list))
+			configured_display_type = IMON_DISPLAY_TYPE_LCD;
+		else if (usb_match_id(interface, imon_touchscreen_list))
+			configured_display_type = IMON_DISPLAY_TYPE_VGA;
+		else if (usb_match_id(interface, ir_only_list))
+			configured_display_type = IMON_DISPLAY_TYPE_NONE;
+		else
+			configured_display_type = IMON_DISPLAY_TYPE_VFD;
+	} else {
+		configured_display_type = display_type;
+		dprintk("%s: overriding display type to %d via modparam\n",
+			__func__, display_type);
+	}
 
 	/*
 	 * If it's the LCD, as opposed to the VFD, we just need to replace
 	 * the "write" file op.
 	 */
-	if ((display_type == IMON_DISPLAY_TYPE_AUTO &&
-	     usb_match_id(interface, lcd_device_list)) ||
-	    display_type == IMON_DISPLAY_TYPE_LCD)
+	if (configured_display_type == IMON_DISPLAY_TYPE_LCD)
 		display_fops.write = &lcd_write;
 
 	/*
@@ -1306,47 +1838,32 @@ static void *imon_probe(struct usb_device *dev, unsigned int intf,
 
 	code_length = buf_chunk_size * 8;
 
-#if !defined(KERNEL_2_5)
-	for (subminor = 0; subminor < MAX_DEVICES; ++subminor) {
-		if (minor_table[subminor] == NULL)
-			break;
-	}
-	if (subminor == MAX_DEVICES) {
-		err("%s: allowed max number of devices already present",
-		    __func__);
-		retval = -ENOMEM;
-		goto exit;
-	}
-#endif
-#ifdef KERNEL_2_5
-	usbdev = usb_get_dev(interface_to_usbdev(interface));
+	usbdev     = usb_get_dev(interface_to_usbdev(interface));
 	iface_desc = interface->cur_altsetting;
-	num_endpoints = iface_desc->desc.bNumEndpoints;
-#else
-	interface = &usbdev->actconfig->interface[intf];
-	iface_desc = &interface->altsetting[interface->act_altsetting];
-	num_endpoints = iface_desc->bNumEndpoints;
-#endif
+	num_endpts = iface_desc->desc.bNumEndpoints;
+	ifnum      = iface_desc->desc.bInterfaceNumber;
+	vendor     = le16_to_cpu(usbdev->descriptor.idVendor);
+	product    = le16_to_cpu(usbdev->descriptor.idProduct);
+
+	dprintk("%s: found iMON device (%04x:%04x, intf%d)\n",
+		__func__, vendor, product, ifnum);
+
+	/* prevent races probing devices w/multiple interfaces */
+	mutex_lock(&driver_lock);
+
+	first_if = usb_ifnum_to_if(usbdev, 0);
+	first_if_context = (struct imon_context *)usb_get_intfdata(first_if);
 
 	/*
 	 * Scan the endpoint list and set:
 	 *	first input endpoint = IR endpoint
 	 *	first output endpoint = display endpoint
 	 */
-
-	ir_ep_found = 0;
-	display_ep_found = 0;
-
-	for (i = 0; i < num_endpoints && !(ir_ep_found && display_ep_found);
-	     ++i) {
+	for (i = 0; i < num_endpts && !(ir_ep_found && display_ep_found); ++i) {
 		struct usb_endpoint_descriptor *ep;
 		int ep_dir;
 		int ep_type;
-#ifdef KERNEL_2_5
 		ep = &iface_desc->endpoint[i].desc;
-#else
-		ep = &iface_desc->endpoint[i];
-#endif
 		ep_dir = ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK;
 		ep_type = ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
 
@@ -1356,18 +1873,14 @@ static void *imon_probe(struct usb_device *dev, unsigned int intf,
 
 			rx_endpoint = ep;
 			ir_ep_found = 1;
-			if (debug)
-				printk(KERN_INFO "%s: found IR endpoint\n",
-				       __func__);
+			dprintk("%s: found IR endpoint\n", __func__);
 
 		} else if (!display_ep_found &&
 			   ep_dir == USB_DIR_OUT &&
 			   ep_type == USB_ENDPOINT_XFER_INT) {
 			tx_endpoint = ep;
 			display_ep_found = 1;
-			if (debug)
-				printk(KERN_INFO "%s: found display endpoint\n",
-				       __func__);
+			dprintk("%s: found display endpoint\n", __func__);
 		}
 	}
 
@@ -1379,10 +1892,8 @@ static void *imon_probe(struct usb_device *dev, unsigned int intf,
 		if (usb_match_id(interface, ctl_ep_device_list)) {
 			tx_control = 1;
 			display_ep_found = 1;
-			if (debug)
-				printk(KERN_INFO "%s: LCD device uses control "
-				       "endpoint, not interface OUT "
-				       "endpoint\n", __func__);
+			dprintk("%s: device uses control endpoint, not "
+				"interface OUT endpoint\n", __func__);
 		}
 	}
 
@@ -1391,14 +1902,18 @@ static void *imon_probe(struct usb_device *dev, unsigned int intf,
 	 * that SoundGraph recycles device IDs between devices both with
 	 * and without... :\
 	 */
-	if ((display_type == IMON_DISPLAY_TYPE_AUTO &&
-	     usb_match_id(interface, ir_only_list)) ||
-	    display_type == IMON_DISPLAY_TYPE_NONE) {
-		tx_control = 0;
+	if (configured_display_type == IMON_DISPLAY_TYPE_NONE) {
 		display_ep_found = 0;
-		if (debug)
-			printk(KERN_INFO "%s: device has no display\n",
-			       __func__);
+		dprintk("%s: device has no display\n", __func__);
+	}
+
+	/*
+	 * iMON Touch devices have a VGA touchscreen, but no "display", as
+	 * that refers to e.g. /dev/lcd0 (a character device LCD or VFD).
+	 */
+	if (configured_display_type == IMON_DISPLAY_TYPE_VGA) {
+		display_ep_found = 0;
+		dprintk("%s: iMON Touch device found\n", __func__);
 	}
 
 	/* Input endpoint is mandatory */
@@ -1411,171 +1926,296 @@ static void *imon_probe(struct usb_device *dev, unsigned int intf,
 		if (usb_match_id(interface, ir_onboard_decode_list))
 			ir_onboard_decode = 1;
 
-		if (debug)
-			printk(KERN_INFO "ir_onboard_decode: %d\n",
-			       ir_onboard_decode);
+		dprintk("%s: ir_onboard_decode: %d\n",
+			__func__, ir_onboard_decode);
 	}
 
 	/* Determine if display requires 6 packets */
 	if (display_ep_found) {
-		if (usb_match_id(interface, display_proto_6p_list))
-			display_proto_6p = 1;
+		if (usb_match_id(interface, vfd_proto_6p_list))
+			vfd_proto_6p = 1;
 
-		if (debug)
-			printk(KERN_INFO "display_proto_6p: %d\n",
-			       display_proto_6p);
+		dprintk("%s: vfd_proto_6p: %d\n",
+			__func__, vfd_proto_6p);
 	}
 
-	alloc_status = 0;
-
-	context = kzalloc(sizeof(struct imon_context), GFP_KERNEL);
-	if (!context) {
-		err("%s: kzalloc failed for context", __func__);
-		alloc_status = 1;
-		goto alloc_status_switch;
-	}
-	driver = kzalloc(sizeof(struct lirc_driver), GFP_KERNEL);
-	if (!driver) {
-		err("%s: kzalloc failed for lirc_driver", __func__);
-		alloc_status = 2;
-		goto alloc_status_switch;
-	}
-	rbuf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
-	if (!rbuf) {
-		err("%s: kmalloc failed for lirc_buffer", __func__);
-		alloc_status = 3;
-		goto alloc_status_switch;
-	}
-	if (lirc_buffer_init(rbuf, buf_chunk_size, BUF_SIZE)) {
-		err("%s: lirc_buffer_init failed", __func__);
-		alloc_status = 4;
-		goto alloc_status_switch;
-	}
-#ifdef KERNEL_2_5
-	rx_urb = usb_alloc_urb(0, GFP_KERNEL);
-#else
-	rx_urb = usb_alloc_urb(0);
-#endif
-	if (!rx_urb) {
-		err("%s: usb_alloc_urb failed for IR urb", __func__);
-		alloc_status = 5;
-		goto alloc_status_switch;
-	}
-	if (display_ep_found) {
-#ifdef KERNEL_2_5
+	if (ifnum == 0) {
+		context = kzalloc(sizeof(struct imon_context), GFP_KERNEL);
+		if (!context) {
+			err("%s: kzalloc failed for context", __func__);
+			alloc_status = 1;
+			goto alloc_status_switch;
+		}
+		driver = kzalloc(sizeof(struct lirc_driver), GFP_KERNEL);
+		if (!driver) {
+			err("%s: kzalloc failed for lirc_driver", __func__);
+			alloc_status = 2;
+			goto alloc_status_switch;
+		}
+		rbuf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
+		if (!rbuf) {
+			err("%s: kmalloc failed for lirc_buffer", __func__);
+			alloc_status = 3;
+			goto alloc_status_switch;
+		}
+		if (lirc_buffer_init(rbuf, buf_chunk_size, BUF_SIZE)) {
+			err("%s: lirc_buffer_init failed", __func__);
+			alloc_status = 4;
+			goto alloc_status_switch;
+		}
+		rx_urb = usb_alloc_urb(0, GFP_KERNEL);
+		if (!rx_urb) {
+			err("%s: usb_alloc_urb failed for IR urb", __func__);
+			alloc_status = 5;
+			goto alloc_status_switch;
+		}
 		tx_urb = usb_alloc_urb(0, GFP_KERNEL);
-#else
-		tx_urb = usb_alloc_urb(0);
-#endif
 		if (!tx_urb) {
 			err("%s: usb_alloc_urb failed for display urb",
 			    __func__);
 			alloc_status = 6;
 			goto alloc_status_switch;
 		}
-	}
 
-	mutex_init(&context->lock);
-	context->display_proto_6p = display_proto_6p;
-	context->ir_onboard_decode = ir_onboard_decode;
-
-	strcpy(driver->name, MOD_NAME);
-	driver->minor = -1;
-	driver->code_length = ir_onboard_decode ?
-		buf_chunk_size * 8 : sizeof(lirc_t) * 8;
-	driver->sample_rate = 0;
-	driver->features = (ir_onboard_decode) ?
-		LIRC_CAN_REC_LIRCCODE : LIRC_CAN_REC_MODE2;
-	driver->data = context;
-	driver->rbuf = rbuf;
-	driver->set_use_inc = ir_open;
-	driver->set_use_dec = ir_close;
+		mutex_init(&context->lock);
+		context->vfd_proto_6p = vfd_proto_6p;
+		context->ir_onboard_decode = ir_onboard_decode;
+
+		strcpy(driver->name, MOD_NAME);
+		driver->minor = -1;
+		driver->code_length = ir_onboard_decode ?
+			code_length : sizeof(int) * 8;
+		driver->sample_rate = 0;
+		driver->features = (ir_onboard_decode) ?
+			LIRC_CAN_REC_LIRCCODE : LIRC_CAN_REC_MODE2;
+		driver->data = context;
+		driver->rbuf = rbuf;
+		driver->set_use_inc = ir_open;
+		driver->set_use_dec = ir_close;
 #ifdef LIRC_HAVE_SYSFS
-	driver->dev = &interface->dev;
+		driver->dev = &interface->dev;
 #endif
-	driver->owner = THIS_MODULE;
+		driver->owner = THIS_MODULE;
 
-	mutex_lock(&context->lock);
+		mutex_lock(&context->lock);
 
-	lirc_minor = lirc_register_driver(driver);
-	if (lirc_minor < 0) {
-		err("%s: lirc_register_driver failed", __func__);
-		alloc_status = 7;
-		mutex_unlock(&context->lock);
-		goto alloc_status_switch;
-	} else
-		printk(KERN_INFO "%s: Registered iMON driver(minor:%d)\n",
-		     __func__, lirc_minor);
+		context->driver = driver;
+		/* start out in keyboard mode */
+		context->pad_mouse = 0;
 
-	/* Needed while unregistering! */
-	driver->minor = lirc_minor;
+		init_timer(&context->timer);
+		context->timer.data = (unsigned long)context;
+		context->timer.function = imon_touch_display_timeout;
 
-	context->usbdev = usbdev;
-	context->dev_present = 1;
-	context->rx_endpoint = rx_endpoint;
-	context->rx_urb = rx_urb;
-	if (display_ep_found) {
-		context->display_supported = 1;
+		lirc_minor = lirc_register_driver(driver);
+		if (lirc_minor < 0) {
+			err("%s: lirc_register_driver failed", __func__);
+			alloc_status = 7;
+			goto alloc_status_switch;
+		} else
+			printk(KERN_INFO MOD_NAME ": Registered iMON driver "
+			       "(lirc minor: %d)\n", lirc_minor);
+
+		/* Needed while unregistering! */
+		driver->minor = lirc_minor;
+
+	} else {
+	/* this is the secondary interface on the device */
+		if (first_if_context->driver) {
+			rx_urb = usb_alloc_urb(0, GFP_KERNEL);
+			if (!rx_urb) {
+				err("%s: usb_alloc_urb failed for IR urb",
+				    __func__);
+				alloc_status = 5;
+				goto alloc_status_switch;
+			}
+
+			context = first_if_context;
+		}
+		mutex_lock(&context->lock);
+	}
+
+	if (ifnum == 0) {
+		context->usbdev_intf0 = usbdev;
+		context->dev_present_intf0 = 1;
+		context->rx_endpoint_intf0 = rx_endpoint;
+		context->rx_urb_intf0 = rx_urb;
+
+		/*
+		 * tx is used to send characters to lcd/vfd, associate RF
+		 * remotes, set IR protocol, and maybe more...
+		 */
 		context->tx_endpoint = tx_endpoint;
 		context->tx_urb = tx_urb;
 		context->tx_control = tx_control;
+
+		if (display_ep_found)
+			context->display_supported = 1;
+
+		if (product == 0xffdc)
+			context->ffdc_dev = 1;
+
+		context->display_type = configured_display_type;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18)
+		context->mouse = input_allocate_device();
+
+		snprintf(context->name_mouse, sizeof(context->name_mouse),
+			 "iMON PAD IR Mouse (%04x:%04x)",
+			 vendor, product);
+		context->mouse->name = context->name_mouse;
+
+		usb_make_path(usbdev, context->phys_mouse, sizeof(context->phys_mouse));
+		strlcat(context->phys_mouse, "/input0", sizeof(context->phys_mouse));
+		context->mouse->phys = context->phys_mouse;
+
+		context->mouse->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
+		context->mouse->keybit[BIT_WORD(BTN_MOUSE)] =
+			BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_RIGHT) |
+			BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_SIDE) |
+			BIT_MASK(BTN_EXTRA);
+		context->mouse->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y) |
+			BIT_MASK(REL_WHEEL);
+
+		input_set_drvdata(context->mouse, context);
+
+		usb_to_input_id(usbdev, &context->mouse->id);
+		context->mouse->dev.parent = &interface->dev;
+		retval = input_register_device(context->mouse);
+		if (retval)
+			printk(KERN_INFO "%s: pad mouse input device setup failed\n",
+			       __func__);
+#endif
+
+		usb_fill_int_urb(context->rx_urb_intf0, context->usbdev_intf0,
+			usb_rcvintpipe(context->usbdev_intf0,
+					context->rx_endpoint_intf0->bEndpointAddress),
+			context->usb_rx_buf, sizeof(context->usb_rx_buf),
+			usb_rx_callback_intf0, context,
+			context->rx_endpoint_intf0->bInterval);
+
+		retval = usb_submit_urb(context->rx_urb_intf0, GFP_KERNEL);
+
+		if (retval) {
+			err("%s: usb_submit_urb failed for intf0 (%d)",
+			    __func__, retval);
+			mutex_unlock(&context->lock);
+			goto exit;
+		}
+
+	} else {
+		context->usbdev_intf1 = usbdev;
+		context->dev_present_intf1 = 1;
+		context->rx_endpoint_intf1 = rx_endpoint;
+		context->rx_urb_intf1 = rx_urb;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18)
+		if (context->display_type == IMON_DISPLAY_TYPE_VGA) {
+			context->touch = input_allocate_device();
+
+			snprintf(context->name_touch, sizeof(context->name_touch),
+				 "iMON USB Touchscreen (%04x:%04x)",
+				 vendor, product);
+			context->touch->name = context->name_touch;
+
+			usb_make_path(usbdev, context->phys_touch,
+				      sizeof(context->phys_touch));
+			strlcat(context->phys_touch, "/input1",
+				sizeof(context->phys_touch));
+			context->touch->phys = context->phys_touch;
+
+			context->touch->evbit[0] =
+				BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+			context->touch->keybit[BIT_WORD(BTN_TOUCH)] =
+				BIT_MASK(BTN_TOUCH);
+			input_set_abs_params(context->touch, ABS_X,
+					     0x00, 0xfff, 0, 0);
+			input_set_abs_params(context->touch, ABS_Y,
+					     0x00, 0xfff, 0, 0);
+
+			input_set_drvdata(context->touch, context);
+
+			usb_to_input_id(usbdev, &context->touch->id);
+			context->touch->dev.parent = &interface->dev;
+			retval = input_register_device(context->touch);
+			if (retval)
+				printk(KERN_INFO "%s: touchscreen input device setup failed\n",
+				       __func__);
+		} else
+			context->touch = NULL;
+#endif
+
+		usb_fill_int_urb(context->rx_urb_intf1, context->usbdev_intf1,
+			usb_rcvintpipe(context->usbdev_intf1,
+				context->rx_endpoint_intf1->bEndpointAddress),
+			context->usb_rx_buf, sizeof(context->usb_rx_buf),
+			usb_rx_callback_intf1, context,
+			context->rx_endpoint_intf1->bInterval);
+
+		retval = usb_submit_urb(context->rx_urb_intf1, GFP_KERNEL);
+
+		if (retval) {
+			err("%s: usb_submit_urb failed for intf1 (%d)",
+			    __func__, retval);
+			mutex_unlock(&context->lock);
+			goto exit;
+		}
 	}
-	context->driver = driver;
 
-#ifdef KERNEL_2_5
 	usb_set_intfdata(interface, context);
 
-	if (cpu_to_le16(usbdev->descriptor.idProduct) == 0xffdc) {
-		int err;
-
-		err = sysfs_create_group(&interface->dev.kobj,
-					 &imon_attribute_group);
-		if (err)
-			err("%s: Could not create sysfs entries(%d)",
-			    __func__, err);
+	/* RF products *also* use 0xffdc... sigh... */
+	if (context->ffdc_dev) {
+		sysfs_err = sysfs_create_group(&interface->dev.kobj,
+					       &imon_rf_attribute_group);
+		if (sysfs_err)
+			err("%s: Could not create RF sysfs entries(%d)",
+			    __func__, sysfs_err);
 	}
-#else
-	minor_table[subminor] = context;
-	context->subminor = subminor;
-#endif
 
-	if (display_ep_found) {
-		if (debug)
-			printk(KERN_INFO "Registering display with devfs\n");
-#ifdef KERNEL_2_5
+	if (context->display_supported && ifnum == 0) {
+		dprintk("%s: Registering iMON display with sysfs\n", __func__);
+
+		/* set up sysfs entry for built-in clock */
+		sysfs_err = sysfs_create_group(&interface->dev.kobj,
+					       &imon_display_attribute_group);
+		if (sysfs_err)
+			err("%s: Could not create display sysfs entries(%d)",
+			    __func__, sysfs_err);
+
 		if (usb_register_dev(interface, &imon_class)) {
 			/* Not a fatal error, so ignore */
 			printk(KERN_INFO "%s: could not get a minor number for "
 			       "display\n", __func__);
 		}
-#else
-		sprintf(name, DEVFS_NAME, subminor);
-		if (!(context->devfs = devfs_register(usb_devfs_handle, name,
-					DEVFS_FL_DEFAULT, USB_MAJOR,
-					DISPLAY_MINOR_BASE + subminor,
-					DEVFS_MODE, &display_fops, NULL))) {
-			/* not a fatal error so ignore */
-			printk(KERN_INFO "%s: devfs register failed for "
-			       "display\n", __func__);
-		}
-#endif
+
+		/* Enable front-panel buttons and/or knobs */
+		memcpy(context->usb_tx_buf, &fp_packet, sizeof(fp_packet));
+		retval = send_packet(context);
+		/* Not fatal, but warn about it */
+		if (retval)
+			printk(KERN_INFO "%s: failed to enable front-panel "
+			       "buttons and/or knobs\n", __func__);
 	}
 
-	printk(KERN_INFO "%s: iMON device on usb<%d:%d> initialized\n",
-			__func__, usbdev->bus->busnum, usbdev->devnum);
+	/* set IR protocol/remote type */
+	imon_set_ir_protocol(context);
 
-	mutex_unlock(&context->lock);
+	printk(KERN_INFO MOD_NAME ": iMON device (%04x:%04x, intf%d) on "
+	       "usb<%d:%d> initialized\n", vendor, product, ifnum,
+	       usbdev->bus->busnum, usbdev->devnum);
 
 alloc_status_switch:
+	mutex_unlock(&context->lock);
 
 	switch (alloc_status) {
 	case 7:
-		if (display_ep_found)
-			usb_free_urb(tx_urb);
+		usb_free_urb(tx_urb);
 	case 6:
 		usb_free_urb(rx_urb);
 	case 5:
-		lirc_buffer_free(rbuf);
+		if (rbuf)
+			lirc_buffer_free(rbuf);
 	case 4:
 		kfree(rbuf);
 	case 3:
@@ -1585,89 +2225,93 @@ alloc_status_switch:
 		context = NULL;
 	case 1:
 		retval = -ENOMEM;
+		break;
 	case 0:
-		;
+		retval = 0;
 	}
 
 exit:
-#ifdef KERNEL_2_5
+	mutex_unlock(&driver_lock);
+
 	return retval;
-#else
-	return (retval == 0) ? context : NULL;
-#endif
 }
 
 /**
  * Callback function for USB core API: disconnect
  */
-#ifdef KERNEL_2_5
 static void imon_disconnect(struct usb_interface *interface)
-#else
-static void imon_disconnect(struct usb_device *dev, void *data)
-#endif
 {
 	struct imon_context *context;
+	int ifnum;
 
 	/* prevent races with ir_open()/display_open() */
-	mutex_lock(&disconnect_lock);
+	mutex_lock(&driver_lock);
 
-#ifdef KERNEL_2_5
 	context = usb_get_intfdata(interface);
-#else
-	context = (struct imon_context *)data;
-#endif
-	mutex_lock(&context->lock);
+	ifnum = interface->cur_altsetting->desc.bInterfaceNumber;
 
-	printk(KERN_INFO "%s: iMON device disconnected\n", __func__);
+	mutex_lock(&context->lock);
 
-#ifdef KERNEL_2_5
 	/*
 	 * sysfs_remove_group is safe to call even if sysfs_create_group
 	 * hasn't been called
 	 */
 	sysfs_remove_group(&interface->dev.kobj,
-			   &imon_attribute_group);
-	usb_set_intfdata(interface, NULL);
-#else
-	minor_table[context->subminor] = NULL;
-#endif
-	context->dev_present = 0;
+			   &imon_display_attribute_group);
+	sysfs_remove_group(&interface->dev.kobj,
+			   &imon_rf_attribute_group);
 
-	/* Stop reception */
-	usb_kill_urb(context->rx_urb);
+	usb_set_intfdata(interface, NULL);
 
 	/* Abort ongoing write */
 	if (atomic_read(&context->tx.busy)) {
 		usb_kill_urb(context->tx_urb);
-		wait_for_completion(&context->tx.finished);
+		complete_all(&context->tx.finished);
 	}
 
-	/* De-register from lirc_dev if IR port is not open */
-	if (!context->ir_isopen)
-		deregister_from_lirc(context);
-
-	if (context->display_supported)
-#ifdef KERNEL_2_5
-		usb_deregister_dev(interface, &imon_class);
-#else
-		if (context->devfs)
-			devfs_unregister(context->devfs);
+	if (ifnum == 0) {
+		context->dev_present_intf0 = 0;
+		usb_kill_urb(context->rx_urb_intf0);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18)
+		input_unregister_device(context->mouse);
 #endif
+		if (context->display_supported)
+			usb_deregister_dev(interface, &imon_class);
+	} else {
+		context->dev_present_intf1 = 0;
+		usb_kill_urb(context->rx_urb_intf1);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18)
+		if (context->display_type == IMON_DISPLAY_TYPE_VGA)
+			input_unregister_device(context->touch);
+#endif
+	}
 
-	mutex_unlock(&context->lock);
+	if (!context->ir_isopen && !context->dev_present_intf0 &&
+	    !context->dev_present_intf1) {
+		del_timer_sync(&context->timer);
+		deregister_from_lirc(context);
+		mutex_unlock(&context->lock);
+		if (!context->display_isopen)
+			free_imon_context(context);
+	} else
+		mutex_unlock(&context->lock);
 
-	if (!context->ir_isopen && !context->display_isopen)
-		delete_context(context);
+	mutex_unlock(&driver_lock);
 
-	mutex_unlock(&disconnect_lock);
+	printk(KERN_INFO "%s: iMON device (intf%d) disconnected\n",
+	       __func__, ifnum);
 }
 
 static int imon_suspend(struct usb_interface *intf, pm_message_t message)
 {
 	struct imon_context *context = usb_get_intfdata(intf);
+	int ifnum = intf->cur_altsetting->desc.bInterfaceNumber;
+
+	if (ifnum == 0)
+		usb_kill_urb(context->rx_urb_intf0);
+	else
+		usb_kill_urb(context->rx_urb_intf1);
 
-	if (context->ir_isopen)
-		usb_kill_urb(context->rx_urb);
 	return 0;
 }
 
@@ -1675,9 +2319,28 @@ static int imon_resume(struct usb_interface *intf)
 {
 	int rc = 0;
 	struct imon_context *context = usb_get_intfdata(intf);
+	int ifnum = intf->cur_altsetting->desc.bInterfaceNumber;
 
-	if (context->ir_isopen)
-		rc = usb_submit_urb(context->rx_urb, GFP_ATOMIC);
+	if (ifnum == 0) {
+		usb_fill_int_urb(context->rx_urb_intf0, context->usbdev_intf0,
+			usb_rcvintpipe(context->usbdev_intf0,
+					context->rx_endpoint_intf0->bEndpointAddress),
+			context->usb_rx_buf, sizeof(context->usb_rx_buf),
+			usb_rx_callback_intf0, context,
+			context->rx_endpoint_intf0->bInterval);
+
+		rc = usb_submit_urb(context->rx_urb_intf0, GFP_ATOMIC);
+
+	} else {
+		usb_fill_int_urb(context->rx_urb_intf1, context->usbdev_intf1,
+			usb_rcvintpipe(context->usbdev_intf1,
+				context->rx_endpoint_intf1->bEndpointAddress),
+			context->usb_rx_buf, sizeof(context->usb_rx_buf),
+			usb_rx_callback_intf1, context,
+			context->rx_endpoint_intf1->bInterval);
+
+		rc = usb_submit_urb(context->rx_urb_intf1, GFP_ATOMIC);
+	}
 
 	return rc;
 }
@@ -1686,26 +2349,22 @@ static int __init imon_init(void)
 {
 	int rc;
 
-	printk(KERN_INFO MOD_DESC ", v" MOD_VERSION "\n");
-	printk(KERN_INFO MOD_AUTHOR "\n");
+	printk(KERN_INFO MOD_NAME ": " MOD_DESC ", v" MOD_VERSION "\n");
 
 	rc = usb_register(&imon_driver);
 	if (rc) {
 		err("%s: usb register failed(%d)", __func__, rc);
 		return -ENODEV;
 	}
+
 	return 0;
 }
 
 static void __exit imon_exit(void)
 {
 	usb_deregister(&imon_driver);
-	printk(KERN_INFO "module removed. Goodbye!\n");
+	printk(KERN_INFO MOD_NAME ": module removed. Goodbye!\n");
 }
 
 module_init(imon_init);
 module_exit(imon_exit);
-
-#if !defined(KERNEL_2_5)
-EXPORT_NO_SYMBOLS;
-#endif
diff --git a/ubuntu/lirc/lirc_it87/lirc_it87.c b/ubuntu/lirc/lirc_it87/lirc_it87.c
index 9fc1793..256406b 100644
--- a/ubuntu/lirc/lirc_it87/lirc_it87.c
+++ b/ubuntu/lirc/lirc_it87/lirc_it87.c
@@ -1,7 +1,7 @@
 /*
- * LIRC driver for ITE IT8712/IT8705 CIR port
+ * LIRC driver for ITE IT8712/IT8705/IT8720 CIR port
  *
- * Copyright (C) 2001 Hans-Günter Lütke Uphues <hg_lu@web.de>
+ * Copyright (C) 2001 Hans-GÃ¼nter LÃ¼tke Uphues <hg_lu@web.de>
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License as
@@ -18,7 +18,7 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
  * USA
  *
- * ITE IT8705 and IT8712(not tested) CIR-port support for lirc based
+ * ITE IT8705, IT8712(not tested) and IT8720 CIR-port support for lirc based
  * via cut and paste from lirc_sir.c (C) 2000 Milan Pikula
  *
  * Attention: Sendmode only tested with debugging logs
@@ -816,9 +816,11 @@ static int init_port(void)
 		return retval;
 	}
 	it87_chipid = it87_read(IT87_CHIP_ID2);
-	if ((it87_chipid != 0x12) && (it87_chipid != 0x05)) {
+	if ((it87_chipid != 0x12) &&
+		(it87_chipid != 0x05) &&
+		(it87_chipid != 0x20)) {
 		printk(KERN_INFO LIRC_DRIVER_NAME
-		       ": no IT8705/12 found, exiting..\n");
+		       ": no IT8705/12/20 found, exiting..\n");
 		retval = -ENXIO;
 		return retval;
 	}
@@ -908,16 +910,16 @@ static void drop_port(void)
 #if 0
 	unsigned char init_bytes[4] = IT87_INIT;
 
-	/ * Enter MB PnP Mode * /
+	/* Enter MB PnP Mode */
 	outb(init_bytes[0], IT87_ADRPORT);
 	outb(init_bytes[1], IT87_ADRPORT);
 	outb(init_bytes[2], IT87_ADRPORT);
 	outb(init_bytes[3], IT87_ADRPORT);
 
-	/ * deactivate CIR-Device * /
+	/* deactivate CIR-Device */
 	it87_write(IT87_CIR_ACT, 0x0);
 
-	/ * Leaving MB PnP Mode * /
+	/* Leaving MB PnP Mode */
 	it87_write(IT87_CFGCTRL, 0x2);
 #endif
 
@@ -969,7 +971,7 @@ module_init(lirc_it87_init);
 module_exit(lirc_it87_exit);
 
 MODULE_DESCRIPTION("LIRC driver for ITE IT8712/IT8705 CIR port");
-MODULE_AUTHOR("Hans-Günter Lütke Uphues");
+MODULE_AUTHOR("Hans-GÃ¼nter LÃ¼tke Uphues");
 MODULE_LICENSE("GPL");
 
 module_param(io, int, S_IRUGO);
diff --git a/ubuntu/lirc/lirc_mceusb/lirc_mceusb.c b/ubuntu/lirc/lirc_mceusb/lirc_mceusb.c
index df92dd3..0851769 100644
--- a/ubuntu/lirc/lirc_mceusb/lirc_mceusb.c
+++ b/ubuntu/lirc/lirc_mceusb/lirc_mceusb.c
@@ -1,867 +1,1289 @@
 /*
- * USB Microsoft IR Transceiver driver - 0.2
+ * LIRC driver for Windows Media Center Edition USB Infrared Transceivers
  *
- * Copyright (c) 2003-2004 Dan Conti (dconti@acm.wwu.edu)
+ * (C) by Martin A. Blatter <martin_a_blatter@yahoo.com>
  *
- * The Microsoft IR Transceiver is a neat little IR receiver with two
- * emitters on it designed for Windows Media Center. This driver might
- * work for all media center remotes, but I have only tested it with
- * the philips model. The first revision of this driver only supports
- * the receive function - the transmit function will be much more
- * tricky due to the nature of the hardware. Microsoft chose to build
- * this device inexpensively, therefore making it extra dumb.
- * There is no interrupt endpoint on this device; all usb traffic
- * happens over two bulk endpoints. As a result of this, poll() for
- * this device is an actual hardware poll (instead of a receive queue
- * check) and is rather expensive.
+ * Transmitter support and reception code cleanup.
+ * (C) by Daniel Melander <lirc@rajidae.se>
  *
- * All trademarks property of their respective owners.
+ * Original lirc_mceusb driver for 1st-gen device:
+ * Copyright (c) 2003-2004 Dan Conti <dconti@acm.wwu.edu>
  *
- * TODO
- *   - Fix up minor number, registration of major/minor with usb subsystem
+ * Original lirc_mceusb driver deprecated in favor of this driver, which
+ * supports the 1st-gen device now too. Transmitting on the 1st-gen device
+ * only functions on port #2 at the moment.
+ *
+ * Support for 1st-gen device added June 2009,
+ * by Jarod Wilson <jarod@wilsonet.com>
+ *
+ * Initial transmission support for 1st-gen device added August 2009,
+ * by Patrick Calhoun <phineas@ou.edu>
+ *
+ * Derived from ATI USB driver by Paul Miller and the original
+ * MCE USB driver by Dan Conti ((and now including chunks of the latter
+ * relevant to the 1st-gen device initialization)
+ *
+ * This driver will only work reliably with kernel version 2.6.10
+ * or higher, probably because of differences in USB device enumeration
+ * in the kernel code. Device initialization fails most of the time
+ * with earlier kernel versions.
+ *
+ **********************************************************************
+ *
+ * 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.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  *
  */
 
+#include <linux/version.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 5)
+#error "*******************************************************"
+#error "Sorry, this driver needs kernel version 2.6.5 or higher"
+#error "*******************************************************"
+#endif
 #include <linux/autoconf.h>
 #include <linux/kernel.h>
 #include <linux/errno.h>
 #include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/module.h>
+#include <linux/kmod.h>
 #include <linux/smp_lock.h>
-#include <linux/usb.h>
-#ifdef KERNEL_2_5
 #include <linux/completion.h>
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18)
 #include <asm/uaccess.h>
 #else
 #include <linux/uaccess.h>
 #endif
-#else
-#include <linux/spinlock.h>
-#include <linux/list.h>
-#include <linux/fcntl.h>
-#include <linux/poll.h>
-#include <linux/sched.h>
-#include <linux/signal.h>
-#endif
+#include <linux/usb.h>
+#include <linux/wait.h>
+#include <linux/time.h>
+
+#include "../lirc.h"
+#include "../kcompat.h"
+#include "../lirc_dev/lirc_dev.h"
 
+#define DRIVER_VERSION	"1.90"
+#define DRIVER_AUTHOR	"Daniel Melander <lirc@rajidae.se>, " \
+			"Martin Blatter <martin_a_blatter@yahoo.com>, " \
+			"Dan Conti <dconti@acm.wwu.edu>"
+#define DRIVER_DESC	"Windows Media Center Edition USB IR Transceiver " \
+			"driver for LIRC"
+#define DRIVER_NAME	"lirc_mceusb"
+
+#define USB_BUFLEN	32	/* USB reception buffer length */
+#define LIRCBUF_SIZE	256	/* LIRC work buffer length */
+
+/* MCE constants */
+#define MCE_CMDBUF_SIZE	384 /* MCE Command buffer length */
+#define MCE_TIME_UNIT	50 /* Approx 50us resolution */
+#define MCE_CODE_LENGTH	5 /* Normal length of packet (with header) */
+#define MCE_PACKET_SIZE	4 /* Normal length of packet (without header) */
+#define MCE_PACKET_HEADER 0x84 /* Actual header format is 0x80 + num_bytes */
+#define MCE_CONTROL_HEADER 0x9F /* MCE status header */
+#define MCE_TX_HEADER_LENGTH 3 /* # of bytes in the initializing tx header */
+#define MCE_MAX_CHANNELS 2 /* Two transmitters, hardware dependent? */
+#define MCE_DEFAULT_TX_MASK 0x03 /* Val opts: TX1=0x01, TX2=0x02, ALL=0x03 */
+#define MCE_PULSE_BIT	0x80 /* Pulse bit, MSB set == PULSE else SPACE */
+#define MCE_PULSE_MASK	0x7F /* Pulse mask */
+#define MCE_MAX_PULSE_LENGTH 0x7F /* Longest transmittable pulse symbol */
+#define MCE_PACKET_LENGTH_MASK  0x7F /* Pulse mask */
+
+
+/* module parameters */
 #ifdef CONFIG_USB_DEBUG
 static int debug = 1;
 #else
 static int debug;
 #endif
-
-#include "../kcompat.h"
-#include "../lirc.h"
-#include "../lirc_dev/lirc_dev.h"
-
-/* Use our own dbg macro */
-#define dprintk(fmt, args...)				\
-	do {						\
-		if (debug)				\
-			printk(KERN_DEBUG __FILE__ ": "	\
-			       fmt "\n", ## args);	\
+#define dprintk(fmt, args...)					\
+	do {							\
+		if (debug)					\
+			printk(KERN_DEBUG fmt, ## args);	\
 	} while (0)
 
-/* Version Information */
-#define DRIVER_VERSION "v0.2"
-#define DRIVER_AUTHOR "Dan Conti, dconti@acm.wwu.edu"
-#define DRIVER_DESC "USB Microsoft IR Transceiver Driver"
-#define DRIVER_NAME "lirc_mceusb"
-
-/* Define these values to match your device */
-#define USB_MCEUSB_VENDOR_ID	0x045e
-#define USB_MCEUSB_PRODUCT_ID	0x006d
-
-/* table of devices that work with this driver */
-static struct usb_device_id mceusb_table[] = {
-	/* USB Microsoft IR Transceiver */
-	{ USB_DEVICE(USB_MCEUSB_VENDOR_ID, USB_MCEUSB_PRODUCT_ID) },
+/* general constants */
+#define SEND_FLAG_IN_PROGRESS	1
+#define SEND_FLAG_COMPLETE	2
+#define RECV_FLAG_IN_PROGRESS	3
+#define RECV_FLAG_COMPLETE	4
+
+#define MCEUSB_INBOUND		1
+#define MCEUSB_OUTBOUND		2
+
+#define VENDOR_PHILIPS		0x0471
+#define VENDOR_SMK		0x0609
+#define VENDOR_TATUNG		0x1460
+#define VENDOR_GATEWAY		0x107b
+#define VENDOR_SHUTTLE		0x1308
+#define VENDOR_SHUTTLE2		0x051c
+#define VENDOR_MITSUMI		0x03ee
+#define VENDOR_TOPSEED		0x1784
+#define VENDOR_RICAVISION	0x179d
+#define VENDOR_ITRON		0x195d
+#define VENDOR_FIC		0x1509
+#define VENDOR_LG		0x043e
+#define VENDOR_MICROSOFT	0x045e
+#define VENDOR_FORMOSA		0x147a
+#define VENDOR_FINTEK		0x1934
+#define VENDOR_PINNACLE		0x2304
+#define VENDOR_ECS		0x1019
+#define VENDOR_WISTRON		0x0fb8
+#define VENDOR_COMPRO		0x185b
+#define VENDOR_NORTHSTAR	0x04eb
+
+static struct usb_device_id mceusb_dev_table[] = {
+	/* Original Microsoft MCE IR Transceiver (often HP-branded) */
+	{ USB_DEVICE(VENDOR_MICROSOFT, 0x006d) },
+	/* Philips Infrared Transceiver - Sahara branded */
+	{ USB_DEVICE(VENDOR_PHILIPS, 0x0608) },
+	/* Philips Infrared Transceiver - HP branded */
+	{ USB_DEVICE(VENDOR_PHILIPS, 0x060c) },
+	/* Philips SRM5100 */
+	{ USB_DEVICE(VENDOR_PHILIPS, 0x060d) },
+	/* Philips Infrared Transceiver - Omaura */
+	{ USB_DEVICE(VENDOR_PHILIPS, 0x060f) },
+	/* Philips Infrared Transceiver - Spinel plus */
+	{ USB_DEVICE(VENDOR_PHILIPS, 0x0613) },
+	/* Philips eHome Infrared Transceiver */
+	{ USB_DEVICE(VENDOR_PHILIPS, 0x0815) },
+	/* SMK/Toshiba G83C0004D410 */
+	{ USB_DEVICE(VENDOR_SMK, 0x031d) },
+	/* SMK eHome Infrared Transceiver (Sony VAIO) */
+	{ USB_DEVICE(VENDOR_SMK, 0x0322) },
+	/* bundled with Hauppauge PVR-150 */
+	{ USB_DEVICE(VENDOR_SMK, 0x0334) },
+	/* Tatung eHome Infrared Transceiver */
+	{ USB_DEVICE(VENDOR_TATUNG, 0x9150) },
+	/* Shuttle eHome Infrared Transceiver */
+	{ USB_DEVICE(VENDOR_SHUTTLE, 0xc001) },
+	/* Shuttle eHome Infrared Transceiver */
+	{ USB_DEVICE(VENDOR_SHUTTLE2, 0xc001) },
+	/* Gateway eHome Infrared Transceiver */
+	{ USB_DEVICE(VENDOR_GATEWAY, 0x3009) },
+	/* Mitsumi */
+	{ USB_DEVICE(VENDOR_MITSUMI, 0x2501) },
+	/* Topseed eHome Infrared Transceiver */
+	{ USB_DEVICE(VENDOR_TOPSEED, 0x0001) },
+	/* Topseed HP eHome Infrared Transceiver */
+	{ USB_DEVICE(VENDOR_TOPSEED, 0x0006) },
+	/* Topseed eHome Infrared Transceiver */
+	{ USB_DEVICE(VENDOR_TOPSEED, 0x0007) },
+	/* Topseed eHome Infrared Transceiver */
+	{ USB_DEVICE(VENDOR_TOPSEED, 0x0008) },
+	/* Topseed eHome Infrared Transceiver */
+	{ USB_DEVICE(VENDOR_TOPSEED, 0x000a) },
+	/* Ricavision internal Infrared Transceiver */
+	{ USB_DEVICE(VENDOR_RICAVISION, 0x0010) },
+	/* Itron ione Libra Q-11 */
+	{ USB_DEVICE(VENDOR_ITRON, 0x7002) },
+	/* FIC eHome Infrared Transceiver */
+	{ USB_DEVICE(VENDOR_FIC, 0x9242) },
+	/* LG eHome Infrared Transceiver */
+	{ USB_DEVICE(VENDOR_LG, 0x9803) },
+	/* Microsoft MCE Infrared Transceiver */
+	{ USB_DEVICE(VENDOR_MICROSOFT, 0x00a0) },
+	/* Formosa eHome Infrared Transceiver */
+	{ USB_DEVICE(VENDOR_FORMOSA, 0xe015) },
+	/* Formosa21 / eHome Infrared Receiver */
+	{ USB_DEVICE(VENDOR_FORMOSA, 0xe016) },
+	/* Formosa aim / Trust MCE Infrared Receiver */
+	{ USB_DEVICE(VENDOR_FORMOSA, 0xe017) },
+	/* Formosa Industrial Computing / Beanbag Emulation Device */
+	{ USB_DEVICE(VENDOR_FORMOSA, 0xe018) },
+	/* Formosa21 / eHome Infrared Receiver */
+	{ USB_DEVICE(VENDOR_FORMOSA, 0xe03a) },
+	/* Formosa Industrial Computing AIM IR605/A */
+	{ USB_DEVICE(VENDOR_FORMOSA, 0xe03c) },
+	/* Fintek eHome Infrared Transceiver */
+	{ USB_DEVICE(VENDOR_FINTEK, 0x0602) },
+	/* Fintek eHome Infrared Transceiver (in the AOpen MP45) */
+	{ USB_DEVICE(VENDOR_FINTEK, 0x0702) },
+	/* Pinnacle Remote Kit */
+	{ USB_DEVICE(VENDOR_PINNACLE, 0x0225) },
+	/* Elitegroup Computer Systems IR */
+	{ USB_DEVICE(VENDOR_ECS, 0x0f38) },
+	/* Wistron Corp. eHome Infrared Receiver */
+	{ USB_DEVICE(VENDOR_WISTRON, 0x0002) },
+	/* Compro K100 */
+	{ USB_DEVICE(VENDOR_COMPRO, 0x3020) },
+	/* Compro K100 v2 */
+	{ USB_DEVICE(VENDOR_COMPRO, 0x3082) },
+	/* Northstar Systems eHome Infrared Transceiver */
+	{ USB_DEVICE(VENDOR_NORTHSTAR, 0xe004) },
 	/* Terminating entry */
 	{ }
 };
 
-/* we can have up to this number of device plugged in at once */
-#define MAX_DEVICES		16
-
-/* Structure to hold all of our device specific stuff */
-struct mceusb_device {
-	struct usb_device *udev; /* save off the usb device pointer */
-	struct usb_interface *interface; /* the interface for this device */
-	unsigned char minor;	 /* the starting minor number for this device */
-	unsigned char num_ports; /* the number of ports this device has */
-	char num_interrupt_in;	 /* number of interrupt in endpoints */
-	char num_bulk_in;	 /* number of bulk in endpoints */
-	char num_bulk_out;	 /* number of bulk out endpoints */
-
-	unsigned char *bulk_in_buffer;	/* the buffer to receive data */
-	int bulk_in_size;		/* the size of the receive buffer */
-	__u8 bulk_in_endpointAddr;	/* the address of bulk in endpoint */
-
-	unsigned char *bulk_out_buffer;	/* the buffer to send data */
-	int bulk_out_size;		/* the size of the send buffer */
-	struct urb *write_urb;		/* the urb used to send data */
-	__u8 bulk_out_endpointAddr;	/* the address of bulk out endpoint */
-
-	wait_queue_head_t wait_q; /* for timeouts */
-	struct mutex lock;	/* locks this structure */
+static struct usb_device_id pinnacle_list[] = {
+	{ USB_DEVICE(VENDOR_PINNACLE, 0x0225) },
+	{}
+};
 
-	struct lirc_driver *driver;
+static struct usb_device_id microsoft_gen1_list[] = {
+	{ USB_DEVICE(VENDOR_MICROSOFT, 0x006d) },
+	{}
+};
 
-	lirc_t lircdata[256]; /* place to store data until lirc processes it */
-	int lircidx;		/* current index */
-	int lirccnt;		/* remaining values */
+static struct usb_device_id transmitter_mask_list[] = {
+	{ USB_DEVICE(VENDOR_SMK, 0x031d) },
+	{ USB_DEVICE(VENDOR_SMK, 0x0322) },
+	{ USB_DEVICE(VENDOR_SMK, 0x0334) },
+	{ USB_DEVICE(VENDOR_TOPSEED, 0x0001) },
+	{ USB_DEVICE(VENDOR_TOPSEED, 0x0006) },
+	{ USB_DEVICE(VENDOR_TOPSEED, 0x0007) },
+	{ USB_DEVICE(VENDOR_TOPSEED, 0x0008) },
+	{ USB_DEVICE(VENDOR_TOPSEED, 0x000a) },
+	{ USB_DEVICE(VENDOR_PINNACLE, 0x0225) },
+	{}
+};
 
-	int usb_valid_bytes_in_bulk_buffer; /* leftover data from prior read */
-	int mce_bytes_left_in_packet;	/* for packets split across reads */
+/* data structure for each usb transceiver */
+struct mceusb_dev {
 
-	/* Value to hold the last received space; 0 if last value
-	 * received was a pulse */
-	int last_space;
+	/* usb */
+	struct usb_device *usbdev;
+	struct urb *urb_in;
+	int devnum;
+	struct usb_endpoint_descriptor *usb_ep_in;
+	struct usb_endpoint_descriptor *usb_ep_out;
 
-#ifdef KERNEL_2_5
+	/* buffers and dma */
+	unsigned char *buf_in;
+	unsigned int len_in;
 	dma_addr_t dma_in;
 	dma_addr_t dma_out;
-#endif
+	unsigned int overflow_len;
+
+	/* lirc */
+	struct lirc_driver *d;
+	lirc_t lircdata;
+	unsigned char is_pulse;
+	struct {
+		u32 connected:1;
+		u32 pinnacle:1;
+		u32 transmitter_mask_inverted:1;
+		u32 microsoft_gen1:1;
+		u32 reserved:28;
+	} flags;
+
+	unsigned char transmitter_mask;
+	unsigned int carrier_freq;
+
+	/* handle sending (init strings) */
+	int send_flags;
+	wait_queue_head_t wait_out;
+
+	struct mutex lock;
 };
 
-#define MCE_TIME_UNIT 50
+/* init strings */
+static char init1[] = {0x00, 0xff, 0xaa, 0xff, 0x0b};
+static char init2[] = {0xff, 0x18};
 
-/* driver api */
-#ifdef KERNEL_2_5
-static int mceusb_probe(struct usb_interface *interface,
-			const struct usb_device_id *id);
-static void mceusb_disconnect(struct usb_interface *interface);
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
-static void mceusb_write_bulk_callback(struct urb *urb, struct pt_regs *regs);
+static char pin_init1[] = { 0x9f, 0x07};
+static char pin_init2[] = { 0x9f, 0x13};
+static char pin_init3[] = { 0x9f, 0x0d};
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 11)
+static unsigned long usecs_to_jiffies(const unsigned int u)
+{
+	if (u > jiffies_to_usecs(MAX_JIFFY_OFFSET))
+		return MAX_JIFFY_OFFSET;
+#if HZ <= USEC_PER_SEC && !(USEC_PER_SEC % HZ)
+	return (u + (USEC_PER_SEC / HZ) - 1) / (USEC_PER_SEC / HZ);
+#elif HZ > USEC_PER_SEC && !(HZ % USEC_PER_SEC)
+	return u * (HZ / USEC_PER_SEC);
 #else
-static void mceusb_write_bulk_callback(struct urb *urb);
+	return (u * HZ + USEC_PER_SEC - 1) / USEC_PER_SEC;
 #endif
-#else
-static void *mceusb_probe(struct usb_device *dev, unsigned int ifnum,
-			  const struct usb_device_id *id);
-static void mceusb_disconnect(struct usb_device *dev, void *ptr);
-static void mceusb_write_bulk_callback(struct urb *urb);
+}
 #endif
+static void mceusb_dev_printdata(struct mceusb_dev *ir, char *buf, int len)
+{
+	char codes[USB_BUFLEN * 3 + 1];
+	int i;
 
-/* read data from the usb bus; convert to mode2 */
-static int msir_fetch_more_data(struct mceusb_device *dev, int dont_block);
-
-/* helper functions */
-static void msir_cleanup(struct mceusb_device *dev);
-static void set_use_dec(void *data);
-static int set_use_inc(void *data);
-
-/* array of pointers to our devices that are currently connected */
-static struct mceusb_device *minor_table[MAX_DEVICES];
+	if (len <= 0)
+		return;
 
-/* lock to protect the minor_table structure */
-static DEFINE_MUTEX(minor_table_mutex);
-static void mceusb_setup(struct usb_device *udev);
+	if (ir->flags.microsoft_gen1 && len <= 2)
+		return;
 
-/* usb specific object needed to register this driver with the usb subsystem */
-static struct usb_driver mceusb_driver = {
-	LIRC_THIS_MODULE(.owner = THIS_MODULE)
-	.name		= DRIVER_NAME,
-	.probe		= mceusb_probe,
-	.disconnect	= mceusb_disconnect,
-	.id_table	= mceusb_table,
-};
+	for (i = 0; i < len && i < USB_BUFLEN; i++)
+		snprintf(codes + i * 3, 4, "%02x ", buf[i] & 0xFF);
 
-static void mceusb_delete(struct mceusb_device *dev)
-{
-	dprintk("%s", __func__);
-	minor_table[dev->minor] = NULL;
-#ifdef KERNEL_2_5
-	usb_buffer_free(dev->udev, dev->bulk_in_size,
-			dev->bulk_in_buffer, dev->dma_in);
-	usb_buffer_free(dev->udev, dev->bulk_out_size,
-			dev->bulk_out_buffer, dev->dma_out);
-#else
-	if (dev->bulk_in_buffer != NULL)
-		kfree(dev->bulk_in_buffer);
-	if (dev->bulk_out_buffer != NULL)
-		kfree(dev->bulk_out_buffer);
-#endif
-	if (dev->write_urb != NULL)
-		usb_free_urb(dev->write_urb);
-	kfree(dev);
+	printk(KERN_INFO "" DRIVER_NAME "[%d]: data received %s (length=%d)\n",
+		ir->devnum, codes, len);
 }
 
-static void mceusb_setup(struct usb_device *udev)
+static void usb_async_callback(struct urb *urb, struct pt_regs *regs)
 {
-	char data[8];
-	int res;
+	struct mceusb_dev *ir;
+	int len;
 
-	memset(data, 0, 8);
+	if (!urb)
+		return;
 
-	/* Get Status */
-	res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
-			      USB_REQ_GET_STATUS, USB_DIR_IN,
-			      0, 0, data, 2, HZ * 3);
+	ir = urb->context;
+	if (ir) {
+		len = urb->actual_length;
 
-	/*    res = usb_get_status( udev, 0, 0, data ); */
-	dprintk("%s - res = %d status = 0x%x 0x%x", __func__,
-		res, data[0], data[1]);
-
-	/*
-	 * This is a strange one. They issue a set address to the device
-	 * on the receive control pipe and expect a certain value pair back
-	 */
-	memset(data, 0, 8);
-
-	res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
-			      5, USB_TYPE_VENDOR, 0, 0,
-			      data, 2, HZ * 3);
-	dprintk("%s - res = %d, devnum = %d", __func__, res, udev->devnum);
-	dprintk("%s - data[0] = %d, data[1] = %d", __func__,
-		data[0], data[1]);
+		dprintk(DRIVER_NAME
+			"[%d]: callback called (status=%d len=%d)\n",
+			ir->devnum, urb->status, len);
 
+		if (debug)
+			mceusb_dev_printdata(ir, urb->transfer_buffer, len);
+	}
 
-	/* set feature */
-	res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
-			      USB_REQ_SET_FEATURE, USB_TYPE_VENDOR,
-			      0xc04e, 0x0000, NULL, 0, HZ * 3);
+}
 
-	dprintk("%s - res = %d", __func__, res);
+/* request incoming or send outgoing usb packet - used to initialize remote */
+static void request_packet_async(struct mceusb_dev *ir,
+				 struct usb_endpoint_descriptor *ep,
+				 unsigned char *data, int size, int urb_type)
+{
+	int res;
+	struct urb *async_urb;
+	unsigned char *async_buf;
+
+	if (urb_type) {
+		async_urb = usb_alloc_urb(0, GFP_KERNEL);
+		if (unlikely(!async_urb))
+			return;
+
+		async_buf = kmalloc(size, GFP_KERNEL);
+		if (!async_buf) {
+			usb_free_urb(async_urb);
+			return;
+		}
 
-	/*
-	 * These two are sent by the windows driver, but stall for
-	 * me. I don't have an analyzer on the Linux side so I can't
-	 * see what is actually different and why the device takes
-	 * issue with them
-	 */
-#if 0
-	/* this is some custom control message they send */
-	res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
-			      0x04, USB_TYPE_VENDOR,
-			      0x0808, 0x0000, NULL, 0, HZ * 3);
+		if (urb_type == MCEUSB_OUTBOUND) {
+			/* outbound data */
+			usb_fill_int_urb(async_urb, ir->usbdev,
+				usb_sndintpipe(ir->usbdev,
+					ep->bEndpointAddress),
+				async_buf, size,
+				(usb_complete_t) usb_async_callback,
+				ir, ep->bInterval);
+			memcpy(async_buf, data, size);
+		} else {
+			/* inbound data */
+			usb_fill_int_urb(async_urb, ir->usbdev,
+				usb_rcvintpipe(ir->usbdev,
+					ep->bEndpointAddress),
+				async_buf, size,
+				(usb_complete_t) usb_async_callback,
+				ir, ep->bInterval);
+		}
+		async_urb->transfer_flags = URB_ASYNC_UNLINK;
+	} else {
+		/* standard request */
+		async_urb = ir->urb_in;
+		ir->send_flags = RECV_FLAG_IN_PROGRESS;
+	}
 
-	dprintk("%s - res = %d", __func__, res);
+	dprintk(DRIVER_NAME "[%d]: receive request called (size=%#x)\n",
+		ir->devnum, size);
 
-	/* this is another custom control message they send */
-	res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
-			      0x02, USB_TYPE_VENDOR,
-			      0x0000, 0x0100, NULL, 0, HZ * 3);
+	async_urb->transfer_buffer_length = size;
+	async_urb->dev = ir->usbdev;
 
-	dprintk("%s - res = %d", __func__, res);
-#endif
+	res = usb_submit_urb(async_urb, GFP_ATOMIC);
+	if (res) {
+		dprintk(DRIVER_NAME "[%d]: receive request FAILED! (res=%d)\n",
+			ir->devnum, res);
+		return;
+	}
+	dprintk(DRIVER_NAME "[%d]: receive request complete (res=%d)\n",
+		ir->devnum, res);
 }
 
-static void msir_cleanup(struct mceusb_device *dev)
+static int unregister_from_lirc(struct mceusb_dev *ir)
 {
-	memset(dev->bulk_in_buffer, 0, dev->bulk_in_size);
+	struct lirc_driver *d = ir->d;
+	int devnum;
+	int rtn;
+
+	devnum = ir->devnum;
+	dprintk(DRIVER_NAME "[%d]: unregister from lirc called\n", devnum);
+
+	rtn = lirc_unregister_driver(d->minor);
+	if (rtn > 0) {
+		printk(DRIVER_NAME "[%d]: error in lirc_unregister minor: %d\n"
+			"Trying again...\n", devnum, d->minor);
+		if (rtn == -EBUSY) {
+			printk(DRIVER_NAME
+				"[%d]: device is opened, will unregister"
+				" on close\n", devnum);
+			return -EAGAIN;
+		}
+		set_current_state(TASK_INTERRUPTIBLE);
+		schedule_timeout(HZ);
+
+		rtn = lirc_unregister_driver(d->minor);
+		if (rtn > 0)
+			printk(DRIVER_NAME "[%d]: lirc_unregister failed\n",
+			devnum);
+	}
 
-	dev->usb_valid_bytes_in_bulk_buffer = 0;
+	if (rtn) {
+		printk(DRIVER_NAME "[%d]: didn't free resources\n", devnum);
+		return -EAGAIN;
+	}
 
-	dev->last_space = PULSE_MASK;
+	printk(DRIVER_NAME "[%d]: usb remote disconnected\n", devnum);
 
-	dev->mce_bytes_left_in_packet = 0;
-	dev->lircidx = 0;
-	dev->lirccnt = 0;
-	memset(dev->lircdata, 0, sizeof(dev->lircdata));
+	lirc_buffer_free(d->rbuf);
+	kfree(d->rbuf);
+	kfree(d);
+	kfree(ir);
+	return 0;
 }
 
-static int set_use_inc(void *data)
+static int mceusb_ir_open(void *data)
 {
+	struct mceusb_dev *ir = data;
+
+	if (!ir) {
+		printk(DRIVER_NAME "[?]: %s called with no context\n",
+		       __func__);
+		return -EIO;
+	}
+	dprintk(DRIVER_NAME "[%d]: mceusb IR device opened\n", ir->devnum);
+
 	MOD_INC_USE_COUNT;
+	if (!ir->flags.connected) {
+		if (!ir->usbdev)
+			return -ENOENT;
+		ir->flags.connected = 1;
+	}
+
 	return 0;
 }
 
-static void set_use_dec(void *data)
+static void mceusb_ir_close(void *data)
 {
+	struct mceusb_dev *ir = data;
+
+	if (!ir) {
+		printk(DRIVER_NAME "[?]: %s called with no context\n",
+		       __func__);
+		return;
+	}
+	dprintk(DRIVER_NAME "[%d]: mceusb IR device closed\n", ir->devnum);
+
+	if (ir->flags.connected) {
+		mutex_lock(&ir->lock);
+		ir->flags.connected = 0;
+		mutex_unlock(&ir->lock);
+	}
 	MOD_DEC_USE_COUNT;
 }
 
-/*
- * msir_fetch_more_data
- *
- * The goal here is to read in more remote codes from the remote. In
- * the event that the remote isn't sending us anything, the caller
- * will block until a key is pressed (i.e. this performs phys read,
- * filtering, and queueing of data) unless dont_block is set to 1; in
- * this situation, it will perform a few reads and will exit out if it
- * does not see any appropriate data
- *
- * dev->lock should be locked when this function is called - fine grain
- * locking isn't really important here anyways
- *
- * This routine always returns the number of words available
- *
- */
-static int msir_fetch_more_data(struct mceusb_device *dev, int dont_block)
+static void send_packet_to_lirc(struct mceusb_dev *ir)
 {
-	int retries = 0;
-	int words_to_read =
-		(sizeof(dev->lircdata)/sizeof(lirc_t)) - dev->lirccnt;
-	int partial, this_read = 0;
-	int bulkidx = 0;
-	int bytes_left_in_packet = 0;
-	signed char *signedp = (signed char *)dev->bulk_in_buffer;
+	if (ir->lircdata) {
+		lirc_buffer_write(ir->d->rbuf,
+				  (unsigned char *) &ir->lircdata);
+		wake_up(&ir->d->rbuf->wait_poll);
+		ir->lircdata = 0;
+	}
+}
 
-	if (words_to_read == 0)
-		return dev->lirccnt;
+static void mceusb_process_ir_data(struct mceusb_dev *ir, int buf_len)
+{
+	int i, j;
+	int packet_len = 0;
+	int start_index = 0;
+
+	/* skip meaningless 0xb1 0x60 header bytes on orig receiver */
+	if (ir->flags.microsoft_gen1)
+		start_index = 2;
+
+	/* this should only trigger w/the 1st-gen mce receiver */
+	for (i = start_index; i < (start_index + ir->overflow_len) &&
+	     i < buf_len; i++) {
+		/* rising/falling flank */
+		if (ir->is_pulse != (ir->buf_in[i] & MCE_PULSE_BIT)) {
+			send_packet_to_lirc(ir);
+			ir->is_pulse = ir->buf_in[i] & MCE_PULSE_BIT;
+		}
 
-	/*
-	 * this forces all existing data to be read by lirc before we
-	 * issue another usb command. this is the only form of
-	 * throttling we have
-	 */
-	if (dev->lirccnt)
-		return dev->lirccnt;
+		/* accumulate mce pulse/space values */
+		ir->lircdata += (ir->buf_in[i] & MCE_PULSE_MASK) *
+				MCE_TIME_UNIT;
+		ir->lircdata |= (ir->is_pulse ? PULSE_BIT : 0);
+	}
+	start_index += ir->overflow_len;
+	ir->overflow_len = 0;
+
+	for (i = start_index; i < buf_len; i++) {
+		/* decode mce packets of the form (84),AA,BB,CC,DD */
+		if (ir->buf_in[i] >= 0x80 && ir->buf_in[i] <= 0x9e) {
+			/* data headers */
+			/* decode packet data */
+			packet_len = ir->buf_in[i] & MCE_PACKET_LENGTH_MASK;
+			ir->overflow_len = i + 1 + packet_len - buf_len;
+			for (j = 1; j <= packet_len && (i + j < buf_len); j++) {
+				/* rising/falling flank */
+				if (ir->is_pulse !=
+				    (ir->buf_in[i + j] & MCE_PULSE_BIT)) {
+					send_packet_to_lirc(ir);
+					ir->is_pulse =
+						ir->buf_in[i + j] &
+							MCE_PULSE_BIT;
+				}
 
-	/* reserve room for our leading space */
-	if (dev->last_space)
-		words_to_read--;
+				/* accumulate mce pulse/space values */
+				ir->lircdata +=
+					(ir->buf_in[i + j] & MCE_PULSE_MASK) *
+						MCE_TIME_UNIT;
+				ir->lircdata |= (ir->is_pulse ? PULSE_BIT : 0);
+			}
 
-	while (words_to_read) {
-		/* handle signals and USB disconnects */
-		if (signal_pending(current))
-			return dev->lirccnt ? dev->lirccnt : -EINTR;
+			i += packet_len;
+		} else if (ir->buf_in[i] == MCE_CONTROL_HEADER) {
+			/* status header (0x9F) */
+			/*
+			 * A transmission containing one or more consecutive ir
+			 * commands always ends with a GAP of 100ms followed by
+			 * the sequence 0x9F 0x01 0x01 0x9F 0x15 0x00 0x00 0x80
+			 */
 
-		bulkidx = 0;
+#if 0
+	Uncomment this if the last 100ms "infinity"-space should be transmitted
+	to lirc directly instead of at the beginning of the next transmission.
+	Changes pulse/space order.
+
+			if (++i < buf_len && ir->buf_in[i]==0x01)
+				send_packet_to_lirc(ir);
 
-		/* perform data read (phys or from previous buffer) */
+#endif
 
-		/* use leftovers if present, otherwise perform a read */
-		if (dev->usb_valid_bytes_in_bulk_buffer) {
-			this_read = dev->usb_valid_bytes_in_bulk_buffer;
-			partial = this_read;
-			dev->usb_valid_bytes_in_bulk_buffer = 0;
+			/* end decode loop */
+			dprintk(DRIVER_NAME "[%d] %s: found control header\n",
+				ir->devnum, __func__);
+			ir->overflow_len = 0;
+			break;
 		} else {
-			int retval;
+			dprintk(DRIVER_NAME "[%d] %s: stray packet?\n",
+				ir->devnum, __func__);
+			ir->overflow_len = 0;
+		}
+	}
 
-			this_read = dev->bulk_in_size;
-			partial = 0;
-			retval = usb_bulk_msg(dev->udev,
-					usb_rcvbulkpipe(dev->udev,
-						dev->bulk_in_endpointAddr),
-					(unsigned char *)dev->bulk_in_buffer,
-					this_read, &partial, HZ*10);
+	return;
+}
 
-			/*
-			 * retry a few times on overruns; map all
-			 * other errors to -EIO
-			 */
-			if (retval) {
-				if (retval == -EOVERFLOW && retries < 5) {
-					retries++;
-					interruptible_sleep_on_timeout(
-						&dev->wait_q, HZ);
-					continue;
-				} else
-					return -EIO;
-			}
+static void mceusb_dev_recv(struct urb *urb, struct pt_regs *regs)
+{
+	struct mceusb_dev *ir;
+	int buf_len;
 
-			retries = 0;
-			if (partial)
-				this_read = partial;
+	if (!urb)
+		return;
 
-			/* skip the header */
-			bulkidx += 2;
+	ir = urb->context;
+	if (!ir) {
+		urb->transfer_flags |= URB_ASYNC_UNLINK;
+		usb_unlink_urb(urb);
+		return;
+	}
 
-			/* check for empty reads (header only) */
-			if (this_read == 2) {
-				/* assume no data */
-				if (dont_block)
-					break;
+	buf_len = urb->actual_length;
 
-				/*
-				 * sleep for a bit before performing
-				 * another read
-				 */
-				interruptible_sleep_on_timeout(&dev->wait_q, 1);
-				continue;
-			}
-		}
+	if (debug)
+		mceusb_dev_printdata(ir, urb->transfer_buffer, buf_len);
 
-		/* process data */
+	if (ir->send_flags == RECV_FLAG_IN_PROGRESS) {
+		ir->send_flags = SEND_FLAG_COMPLETE;
+		dprintk(DRIVER_NAME "[%d]: setup answer received %d bytes\n",
+			ir->devnum, buf_len);
+	}
 
-		/* at this point this_read is > 0 */
-		while (bulkidx < this_read &&
-		       (words_to_read > (dev->last_space ? 1 : 0))) {
-			/* while( bulkidx < this_read && words_to_read) */
-			int keycode;
-			int pulse = 0;
+	switch (urb->status) {
+	/* success */
+	case 0:
+		mceusb_process_ir_data(ir, buf_len);
+		break;
+
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		urb->transfer_flags |= URB_ASYNC_UNLINK;
+		usb_unlink_urb(urb);
+		return;
 
-			/* read packet length if needed */
-			if (!bytes_left_in_packet) {
-				/*
-				 * we assume we are on a packet length
-				 * value. it is possible, in some
-				 * cases, to get a packet that does
-				 * not start with a length, apparently
-				 * due to some sort of fragmenting,
-				 * but occasionally we do not receive
-				 * the second half of a fragment
-				 */
-				bytes_left_in_packet =
-					128 + signedp[bulkidx++];
+	case -EPIPE:
+	default:
+		break;
+	}
 
-				/*
-				 * unfortunately rather than keep all
-				 * the data in the packetized format,
-				 * the transceiver sends a trailing 8
-				 * bytes that aren't part of the
-				 * transmission from the remote,
-				 * aren't packetized, and don't really
-				 * have any value. we can basically
-				 * tell we have hit them if 1) we have
-				 * a loooong space currently stored
-				 * up, and 2) the bytes_left value for
-				 * this packet is obviously wrong
-				 */
-				if (bytes_left_in_packet > 4) {
-					if (dev->mce_bytes_left_in_packet) {
-						bytes_left_in_packet =
-						  dev->mce_bytes_left_in_packet;
-						bulkidx--;
-					}
-					bytes_left_in_packet = 0;
-					bulkidx = this_read;
-				}
+	usb_submit_urb(urb, GFP_ATOMIC);
+}
 
-				/*
-				 * always clear this if we have a
-				 * valid packet
-				 */
-				dev->mce_bytes_left_in_packet = 0;
 
-				/*
-				 * continue here to verify we haven't
-				 * hit the end of the bulk_in
-				 */
-				continue;
+static ssize_t mceusb_transmit_ir(struct file *file, const char *buf,
+				  size_t n, loff_t *ppos)
+{
+	int i, count = 0, cmdcount = 0;
+	struct mceusb_dev *ir = NULL;
+	lirc_t wbuf[LIRCBUF_SIZE]; /* Workbuffer with values from lirc */
+	unsigned char cmdbuf[MCE_CMDBUF_SIZE]; /* MCE command buffer */
+	unsigned long signal_duration = 0; /* Singnal length in us */
+	struct timeval start_time, end_time;
+
+	do_gettimeofday(&start_time);
+
+	/* Retrieve lirc_driver data for the device */
+	ir = lirc_get_pdata(file);
+	if (!ir || !ir->usb_ep_out)
+		return -EFAULT;
+
+	if (n % sizeof(lirc_t))
+		return -EINVAL;
+	count = n / sizeof(lirc_t);
+
+	/* Check if command is within limits */
+	if (count > LIRCBUF_SIZE || count%2 == 0)
+		return -EINVAL;
+	if (copy_from_user(wbuf, buf, n))
+		return -EFAULT;
+
+	/* MCE tx init header */
+	cmdbuf[cmdcount++] = MCE_CONTROL_HEADER;
+	cmdbuf[cmdcount++] = 0x08;
+	cmdbuf[cmdcount++] = ir->transmitter_mask;
+
+	/* Generate mce packet data */
+	for (i = 0; (i < count) && (cmdcount < MCE_CMDBUF_SIZE); i++) {
+		signal_duration += wbuf[i];
+		wbuf[i] = wbuf[i] / MCE_TIME_UNIT;
+
+		do { /* loop to support long pulses/spaces > 127*50us=6.35ms */
+
+			/* Insert mce packet header every 4th entry */
+			if ((cmdcount < MCE_CMDBUF_SIZE) &&
+			    (cmdcount - MCE_TX_HEADER_LENGTH) %
+			     MCE_CODE_LENGTH == 0)
+				cmdbuf[cmdcount++] = MCE_PACKET_HEADER;
+
+			/* Insert mce packet data */
+			if (cmdcount < MCE_CMDBUF_SIZE)
+				cmdbuf[cmdcount++] =
+					(wbuf[i] < MCE_PULSE_BIT ?
+					 wbuf[i] : MCE_MAX_PULSE_LENGTH) |
+					 (i & 1 ? 0x00 : MCE_PULSE_BIT);
+			else
+				return -EINVAL;
+		} while ((wbuf[i] > MCE_MAX_PULSE_LENGTH) &&
+			 (wbuf[i] -= MCE_MAX_PULSE_LENGTH));
+	}
 
-			}
+	/* Fix packet length in last header */
+	cmdbuf[cmdcount - (cmdcount - MCE_TX_HEADER_LENGTH) % MCE_CODE_LENGTH] =
+		0x80 + (cmdcount - MCE_TX_HEADER_LENGTH) % MCE_CODE_LENGTH - 1;
 
-			/* generate mode2 */
+	/* Check if we have room for the empty packet at the end */
+	if (cmdcount >= MCE_CMDBUF_SIZE)
+		return -EINVAL;
 
-			keycode = signedp[bulkidx++];
-			if (keycode < 0) {
-				pulse = 1;
-				keycode += 128;
-			}
-			keycode *= MCE_TIME_UNIT;
+	/* All mce commands end with an empty packet (0x80) */
+	cmdbuf[cmdcount++] = 0x80;
 
-			bytes_left_in_packet--;
+	/* Transmit the command to the mce device */
+	request_packet_async(ir, ir->usb_ep_out, cmdbuf,
+			     cmdcount, MCEUSB_OUTBOUND);
 
-			if (pulse) {
-				if (dev->last_space) {
-					dev->lircdata[dev->lirccnt++] =
-						dev->last_space;
-					dev->last_space = 0;
-					words_to_read--;
+	/*
+	 * The lircd gap calculation expects the write function to
+	 * wait the time it takes for the ircommand to be sent before
+	 * it returns.
+	 */
+	do_gettimeofday(&end_time);
+	signal_duration -= (end_time.tv_usec - start_time.tv_usec) +
+			   (end_time.tv_sec - start_time.tv_sec) * 1000000;
 
-					/* clear for the pulse */
-					dev->lircdata[dev->lirccnt] = 0;
-				}
-				dev->lircdata[dev->lirccnt] += keycode;
-				dev->lircdata[dev->lirccnt] |= PULSE_BIT;
-			} else {
-				/*
-				 * on pulse->space transition, add one
-				 * for the existing pulse
-				 */
-				if (dev->lircdata[dev->lirccnt] &&
-				    !dev->last_space) {
-					dev->lirccnt++;
-					words_to_read--;
-				}
+	/* delay with the closest number of ticks */
+	set_current_state(TASK_INTERRUPTIBLE);
+	schedule_timeout(usecs_to_jiffies(signal_duration));
 
-				dev->last_space += keycode;
+	return n;
+}
+
+static void set_transmitter_mask(struct mceusb_dev *ir, unsigned int mask)
+{
+	if (ir->flags.transmitter_mask_inverted)
+		/*
+		 * The mask begins at 0x02 and has an inverted
+		 * numbering scheme
+		 */
+		ir->transmitter_mask =
+			(mask != 0x03 ? mask ^ 0x03 : mask) << 1;
+	else
+		ir->transmitter_mask = mask;
+}
+
+
+/* Sets the send carrier frequency */
+static int set_send_carrier(struct mceusb_dev *ir, int carrier)
+{
+	int clk = 10000000;
+	int prescaler = 0, divisor = 0;
+	unsigned char cmdbuf[] = { 0x9F, 0x06, 0x01, 0x80 };
+
+	/* Carrier is changed */
+	if (ir->carrier_freq != carrier) {
+
+		if (carrier <= 0) {
+			ir->carrier_freq = carrier;
+			dprintk(DRIVER_NAME "[%d]: SET_CARRIER disabling "
+				"carrier modulation\n", ir->devnum);
+			request_packet_async(ir, ir->usb_ep_out,
+					     cmdbuf, sizeof(cmdbuf),
+					     MCEUSB_OUTBOUND);
+			return carrier;
+		}
+
+		for (prescaler = 0; prescaler < 4; ++prescaler) {
+			divisor = (clk >> (2 * prescaler)) / carrier;
+			if (divisor <= 0xFF) {
+				ir->carrier_freq = carrier;
+				cmdbuf[2] = prescaler;
+				cmdbuf[3] = divisor;
+				dprintk(DRIVER_NAME "[%d]: SET_CARRIER "
+					"requesting %d Hz\n",
+					ir->devnum, carrier);
+
+				/* Transmit new carrier to mce device */
+				request_packet_async(ir, ir->usb_ep_out,
+						     cmdbuf, sizeof(cmdbuf),
+						     MCEUSB_OUTBOUND);
+				return carrier;
 			}
 		}
-	}
 
-	/* save off some info if we're exiting mid-packet, or with leftovers */
-	if (bytes_left_in_packet)
-		dev->mce_bytes_left_in_packet = bytes_left_in_packet;
-	if (bulkidx < this_read) {
-		dev->usb_valid_bytes_in_bulk_buffer = (this_read - bulkidx);
-		memcpy(dev->bulk_in_buffer, &(dev->bulk_in_buffer[bulkidx]),
-		       dev->usb_valid_bytes_in_bulk_buffer);
+		return -EINVAL;
+
 	}
-	return dev->lirccnt;
+
+	return carrier;
 }
 
-/**
- * mceusb_add_to_buf: called by lirc_dev to fetch all available keys
- * this is used as a polling interface for us: since we set
- * driver->sample_rate we will periodically get the below call to
- * check for new data returns 0 on success, or -ENODATA if nothing is
- * available
- */
-static int mceusb_add_to_buf(void *data, struct lirc_buffer *buf)
+
+static int mceusb_lirc_ioctl(struct inode *node, struct file *filep,
+			     unsigned int cmd, unsigned long arg)
 {
-	struct mceusb_device *dev = (struct mceusb_device *) data;
+	int result;
+	unsigned int ivalue;
+	unsigned long lvalue;
+	struct mceusb_dev *ir = NULL;
+
+	/* Retrieve lirc_driver data for the device */
+	ir = lirc_get_pdata(filep);
+	if (!ir || !ir->usb_ep_out)
+		return -EFAULT;
+
+
+	switch (cmd) {
+	case LIRC_SET_TRANSMITTER_MASK:
+
+		result = get_user(ivalue, (unsigned int *) arg);
+		if (result)
+			return result;
+		switch (ivalue) {
+		case 0x01: /* Transmitter 1     => 0x04 */
+		case 0x02: /* Transmitter 2     => 0x02 */
+		case 0x03: /* Transmitter 1 & 2 => 0x06 */
+			set_transmitter_mask(ir, ivalue);
+			break;
 
-	mutex_lock(&dev->lock);
+		default: /* Unsupported transmitter mask */
+			return MCE_MAX_CHANNELS;
+		}
 
-	if (!dev->lirccnt) {
-		int res;
-		dev->lircidx = 0;
+		dprintk(DRIVER_NAME ": SET_TRANSMITTERS mask=%d\n", ivalue);
+		break;
 
-		res = msir_fetch_more_data(dev, 1);
+	case LIRC_GET_SEND_MODE:
 
-		if (res == 0)
-			res = -ENODATA;
-		if (res < 0) {
-			mutex_unlock(&dev->lock);
-			return res;
-		}
-	}
+		result = put_user(LIRC_SEND2MODE(LIRC_CAN_SEND_PULSE &
+						 LIRC_CAN_SEND_MASK),
+				  (unsigned long *) arg);
 
-	if (dev->lirccnt) {
-		int keys_to_copy;
+		if (result)
+			return result;
+		break;
 
-		/* determine available buffer space and available data */
-		keys_to_copy = lirc_buffer_available(buf);
-		if (keys_to_copy > dev->lirccnt)
-			keys_to_copy = dev->lirccnt;
+	case LIRC_SET_SEND_MODE:
 
-		lirc_buffer_write_n(buf,
-			(unsigned char *) &(dev->lircdata[dev->lircidx]),
-			keys_to_copy);
-		dev->lircidx += keys_to_copy;
-		dev->lirccnt -= keys_to_copy;
+		result = get_user(lvalue, (unsigned long *) arg);
 
-		mutex_unlock(&dev->lock);
-		return 0;
-	}
+		if (result)
+			return result;
+		if (lvalue != (LIRC_MODE_PULSE&LIRC_CAN_SEND_MASK))
+			return -EINVAL;
+		break;
 
-	mutex_unlock(&dev->lock);
-	return -ENODATA;
-}
+	case LIRC_SET_SEND_CARRIER:
 
-#if defined(KERNEL_2_5) && LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
-static void mceusb_write_bulk_callback(struct urb *urb, struct pt_regs *regs)
-#else
-static void mceusb_write_bulk_callback(struct urb *urb)
-#endif
-{
-	struct mceusb_device *dev = (struct mceusb_device *)urb->context;
+		result = get_user(ivalue, (unsigned int *) arg);
+		if (result)
+			return result;
 
-	dprintk("%s - minor %d", __func__, dev->minor);
+		set_send_carrier(ir, ivalue);
+		break;
 
-	if ((urb->status != -ENOENT) &&
-	    (urb->status != -ECONNRESET)) {
-		dprintk("%s - nonzero write buld status received: %d",
-			__func__, urb->status);
-		return;
+	default:
+		return -ENOIOCTLCMD;
 	}
 
-	return;
+	return 0;
 }
 
-/**
- * mceusb_probe
- *
- * Called by the usb core when a new device is connected that it
- * thinks this driver might be interested in.
- */
-#ifdef KERNEL_2_5
-static int mceusb_probe(struct usb_interface *interface,
-			const struct usb_device_id *id)
-{
-	struct usb_device *udev = interface_to_usbdev(interface);
-	struct usb_host_interface *iface_desc;
-#else
-static void *mceusb_probe(struct usb_device *udev, unsigned int ifnum,
-			  const struct usb_device_id *id)
-{
-	struct usb_interface *interface = &udev->actconfig->interface[ifnum];
-	struct usb_interface_descriptor *iface_desc;
-#endif
-	struct mceusb_device *dev = NULL;
-	struct usb_endpoint_descriptor *endpoint;
-
-	struct lirc_driver *driver;
+static struct file_operations lirc_fops = {
+	.owner	= THIS_MODULE,
+	.write	= mceusb_transmit_ir,
+	.ioctl	= mceusb_lirc_ioctl,
+};
 
-	int minor;
-	size_t buffer_size;
-	int i;
-	int retval = -ENOMEM;
-	char junk[64];
+static int mceusb_gen1_init(struct mceusb_dev *ir)
+{
+	int i, ret;
+	char junk[64], data[8];
 	int partial = 0;
 
-	/* See if the device offered us matches what we can accept */
-	if (cpu_to_le16(udev->descriptor.idVendor) != USB_MCEUSB_VENDOR_ID ||
-	    cpu_to_le16(udev->descriptor.idProduct) != USB_MCEUSB_PRODUCT_ID) {
-		dprintk("Wrong Vendor/Product IDs");
-#ifdef KERNEL_2_5
-		return -ENODEV;
-#else
-		return NULL;
-#endif
-	}
+	/*
+	 * Clear off the first few messages. These look like calibration
+	 * or test data, I can't really tell. This also flushes in case
+	 * we have random ir data queued up.
+	 */
+	for (i = 0; i < 40; i++)
+		usb_bulk_msg(ir->usbdev,
+			usb_rcvbulkpipe(ir->usbdev,
+				ir->usb_ep_in->bEndpointAddress),
+			junk, 64, &partial, HZ * 10);
 
-	/* select a "subminor" number (part of a minor number) */
-	mutex_lock(&minor_table_mutex);
-	for (minor = 0; minor < MAX_DEVICES; ++minor) {
-		if (minor_table[minor] == NULL)
-			break;
-	}
-	if (minor >= MAX_DEVICES) {
-		printk(KERN_INFO "Too many devices plugged in, "
-		       "can not handle this device.\n");
-		goto error;
-	}
+	ir->is_pulse = 1;
 
-	/* allocate memory for our device state and initialize it */
-	dev = kzalloc(sizeof(struct mceusb_device), GFP_KERNEL);
-	if (dev == NULL) {
-		err("Out of memory");
-#ifdef KERNEL_2_5
-		retval = -ENOMEM;
-#endif
-		goto error;
-	}
-	minor_table[minor] = dev;
+	memset(data, 0, 8);
+
+	/* Get Status */
+	ret = usb_control_msg(ir->usbdev, usb_rcvctrlpipe(ir->usbdev, 0),
+			      USB_REQ_GET_STATUS, USB_DIR_IN,
+			      0, 0, data, 2, HZ * 3);
 
-	mutex_init(&dev->lock);
-	dev->udev = udev;
-	dev->interface = interface;
-	dev->minor = minor;
+	/*    ret = usb_get_status( ir->usbdev, 0, 0, data ); */
+	dprintk("%s - ret = %d status = 0x%x 0x%x\n", __func__,
+		ret, data[0], data[1]);
 
 	/*
-	 * set up the endpoint information, check out the endpoints.
-	 * use only the first bulk-in and bulk-out endpoints
+	 * This is a strange one. They issue a set address to the device
+	 * on the receive control pipe and expect a certain value pair back
 	 */
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 5)
-	iface_desc = &interface->altsetting[0];
-#else
-	iface_desc = interface->cur_altsetting;
-#endif
+	memset(data, 0, 8);
 
-#ifdef KERNEL_2_5
-	for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
-		endpoint = &iface_desc->endpoint[i].desc;
-#else
-	for (i = 0; i < iface_desc->bNumEndpoints; ++i) {
-		endpoint = &iface_desc->endpoint[i];
-#endif
-		if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) &&
-		    ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
-		     USB_ENDPOINT_XFER_BULK)) {
-			dprintk("we found a bulk in endpoint");
-			buffer_size = endpoint->wMaxPacketSize;
-			dev->bulk_in_size = buffer_size;
-			dev->bulk_in_endpointAddr = endpoint->bEndpointAddress;
-#ifdef KERNEL_2_5
-			dev->bulk_in_buffer =
-				usb_buffer_alloc(udev, buffer_size,
-						 GFP_ATOMIC, &dev->dma_in);
-#else
-			dev->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL);
-#endif
-			if (!dev->bulk_in_buffer) {
-				err("Couldn't allocate bulk_in_buffer");
-				goto error;
-			}
+	ret = usb_control_msg(ir->usbdev, usb_rcvctrlpipe(ir->usbdev, 0),
+			      USB_REQ_SET_ADDRESS, USB_TYPE_VENDOR, 0, 0,
+			      data, 2, HZ * 3);
+	dprintk("%s - ret = %d, devnum = %d\n",
+		__func__, ret, ir->usbdev->devnum);
+	dprintk("%s - data[0] = %d, data[1] = %d\n",
+		__func__, data[0], data[1]);
+
+	/* set feature */
+	ret = usb_control_msg(ir->usbdev, usb_sndctrlpipe(ir->usbdev, 0),
+			      USB_REQ_SET_FEATURE, USB_TYPE_VENDOR,
+			      0xc04e, 0x0000, NULL, 0, HZ * 3);
+
+	dprintk("%s - ret = %d\n", __func__, ret);
+
+	/* strange: bRequest == 4 */
+	ret = usb_control_msg(ir->usbdev, usb_sndctrlpipe(ir->usbdev, 0),
+			      4, USB_TYPE_VENDOR,
+			      0x0808, 0x0000, NULL, 0, HZ * 3);
+	dprintk("%s - retB = %d\n", __func__, ret);
+
+	/* strange: bRequest == 2 */
+	ret = usb_control_msg(ir->usbdev, usb_sndctrlpipe(ir->usbdev, 0),
+			      2, USB_TYPE_VENDOR,
+			      0x0000, 0x0100, NULL, 0, HZ * 3);
+	dprintk("%s - retC = %d\n", __func__, ret);
+
+	return ret;
+
+};
+
+static int mceusb_dev_probe(struct usb_interface *intf,
+				const struct usb_device_id *id)
+{
+	struct usb_device *dev = interface_to_usbdev(intf);
+	struct usb_host_interface *idesc;
+	struct usb_endpoint_descriptor *ep = NULL;
+	struct usb_endpoint_descriptor *ep_in = NULL;
+	struct usb_endpoint_descriptor *ep_out = NULL;
+	struct usb_host_config *config;
+	struct mceusb_dev *ir = NULL;
+	struct lirc_driver *driver = NULL;
+	struct lirc_buffer *rbuf = NULL;
+	int devnum, pipe, maxp;
+	int minor = 0;
+	int i;
+	char buf[63], name[128] = "";
+	int mem_failure = 0;
+	int is_pinnacle;
+	int is_microsoft_gen1;
+
+	dprintk(DRIVER_NAME ": %s called\n", __func__);
+
+	usb_reset_device(dev);
+
+	config = dev->actconfig;
+
+	idesc = intf->cur_altsetting;
+
+	is_pinnacle = usb_match_id(intf, pinnacle_list) ? 1 : 0;
+
+	is_microsoft_gen1 = usb_match_id(intf, microsoft_gen1_list) ? 1 : 0;
+
+	/* step through the endpoints to find first bulk in and out endpoint */
+	for (i = 0; i < idesc->desc.bNumEndpoints; ++i) {
+		ep = &idesc->endpoint[i].desc;
+
+		if ((ep_in == NULL)
+			&& ((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
+			    == USB_DIR_IN)
+			&& (((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+			    == USB_ENDPOINT_XFER_BULK)
+			|| ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+			    == USB_ENDPOINT_XFER_INT))) {
+
+			dprintk(DRIVER_NAME ": acceptable inbound endpoint "
+				"found\n");
+			ep_in = ep;
+			ep_in->bmAttributes = USB_ENDPOINT_XFER_INT;
+			if (is_pinnacle)
+				/*
+				 * setting seems to 1 seem to cause issues with
+				 * Pinnacle timing out on transfer.
+				 */
+				ep_in->bInterval = ep->bInterval;
+			else
+				ep_in->bInterval = 1;
 		}
 
-		if (((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
-		    == 0x00)
-		    && ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
-		    USB_ENDPOINT_XFER_BULK)) {
-			dprintk("we found a bulk out endpoint");
-#ifdef KERNEL_2_5
-			dev->write_urb = usb_alloc_urb(0, GFP_KERNEL);
-#else
-			dev->write_urb = usb_alloc_urb(0);
-#endif
-			if (!dev->write_urb) {
-				err("No free urbs available");
-				goto error;
-			}
-			buffer_size = endpoint->wMaxPacketSize;
-			dev->bulk_out_size = buffer_size;
-			dev->bulk_out_endpointAddr = endpoint->bEndpointAddress;
-#ifdef KERNEL_2_5
-			dev->bulk_out_buffer =
-				usb_buffer_alloc(udev, buffer_size,
-						 GFP_ATOMIC, &dev->dma_out);
-#else
-			dev->bulk_out_buffer = kmalloc(buffer_size, GFP_KERNEL);
-#endif
-			if (!dev->bulk_out_buffer) {
-				err("Couldn't allocate bulk_out_buffer");
-				goto error;
-			}
-#ifdef KERNEL_2_5
-			usb_fill_bulk_urb(dev->write_urb, udev,
-				      usb_sndbulkpipe
-				      (udev, endpoint->bEndpointAddress),
-				      dev->bulk_out_buffer, buffer_size,
-				      mceusb_write_bulk_callback, dev);
-			dev->write_urb->transfer_dma = dev->dma_out;
-			dev->write_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
-#else
-			FILL_BULK_URB(dev->write_urb, udev,
-				      usb_sndbulkpipe(udev,
-						endpoint->bEndpointAddress),
-				      dev->bulk_out_buffer, buffer_size,
-				      mceusb_write_bulk_callback, dev);
-#endif
+		if ((ep_out == NULL)
+			&& ((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
+			    == USB_DIR_OUT)
+			&& (((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+			    == USB_ENDPOINT_XFER_BULK)
+			|| ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+			    == USB_ENDPOINT_XFER_INT))) {
+
+			dprintk(DRIVER_NAME ": acceptable outbound endpoint "
+				"found\n");
+			ep_out = ep;
+			ep_out->bmAttributes = USB_ENDPOINT_XFER_INT;
+			if (is_pinnacle)
+				/*
+				 * setting seems to 1 seem to cause issues with
+				 * Pinnacle timing out on transfer.
+				 */
+				ep_out->bInterval = ep->bInterval;
+			else
+				ep_out->bInterval = 1;
 		}
 	}
-
-	if (!(dev->bulk_in_endpointAddr && dev->bulk_out_endpointAddr)) {
-		err("Couldn't find both bulk-in and bulk-out endpoints");
-		goto error;
+	if (ep_in == NULL || ep_out == NULL) {
+		dprintk(DRIVER_NAME ": inbound and/or "
+			"outbound endpoint not found\n");
+		return -ENODEV;
 	}
 
-	/* init the waitq */
-	init_waitqueue_head(&dev->wait_q);
+	devnum = dev->devnum;
+	pipe = usb_rcvintpipe(dev, ep_in->bEndpointAddress);
+	maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
 
+	mem_failure = 0;
+	ir = kzalloc(sizeof(struct mceusb_dev), GFP_KERNEL);
+	if (!ir) {
+		mem_failure = 1;
+		goto mem_failure_switch;
+	}
 
-	/* Set up our lirc driver */
 	driver = kzalloc(sizeof(struct lirc_driver), GFP_KERNEL);
 	if (!driver) {
-		err("out of memory");
-		goto error;
+		mem_failure = 2;
+		goto mem_failure_switch;
+	}
+
+	rbuf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
+	if (!rbuf) {
+		mem_failure = 3;
+		goto mem_failure_switch;
+	}
+
+	if (lirc_buffer_init(rbuf, sizeof(lirc_t), LIRCBUF_SIZE)) {
+		mem_failure = 4;
+		goto mem_failure_switch;
+	}
+
+	ir->buf_in = usb_buffer_alloc(dev, maxp, GFP_ATOMIC, &ir->dma_in);
+	if (!ir->buf_in) {
+		mem_failure = 5;
+		goto mem_failure_switch;
+	}
+
+	ir->urb_in = usb_alloc_urb(0, GFP_KERNEL);
+	if (!ir->urb_in) {
+		mem_failure = 7;
+		goto mem_failure_switch;
 	}
 
 	strcpy(driver->name, DRIVER_NAME " ");
-	driver->minor = minor;
+	driver->minor = -1;
+	driver->features = LIRC_CAN_SEND_PULSE |
+		LIRC_CAN_SET_TRANSMITTER_MASK |
+		LIRC_CAN_REC_MODE2 |
+		LIRC_CAN_SET_SEND_CARRIER;
+	driver->data = ir;
+	driver->rbuf = rbuf;
+	driver->set_use_inc = &mceusb_ir_open;
+	driver->set_use_dec = &mceusb_ir_close;
 	driver->code_length = sizeof(lirc_t) * 8;
-	driver->features = LIRC_CAN_REC_MODE2; /* | LIRC_CAN_SEND_MODE2; */
-	driver->data = dev;
-	driver->buffer_size = 128;
-	driver->set_use_inc = &set_use_inc;
-	driver->set_use_dec = &set_use_dec;
-	driver->sample_rate = 80;   /* sample at 100hz (10ms) */
-	driver->add_to_buf = &mceusb_add_to_buf;
-#ifdef LIRC_HAVE_SYSFS
-	driver->dev = &interface->dev;
-#endif
+	driver->fops  = &lirc_fops;
+	driver->dev   = &intf->dev;
 	driver->owner = THIS_MODULE;
-	if (lirc_register_driver(driver) < 0) {
+
+	mutex_init(&ir->lock);
+	init_waitqueue_head(&ir->wait_out);
+
+	minor = lirc_register_driver(driver);
+	if (minor < 0)
+		mem_failure = 9;
+
+mem_failure_switch:
+
+	switch (mem_failure) {
+	case 9:
+		usb_free_urb(ir->urb_in);
+	case 7:
+		usb_buffer_free(dev, maxp, ir->buf_in, ir->dma_in);
+	case 5:
+		lirc_buffer_free(rbuf);
+	case 4:
+		kfree(rbuf);
+	case 3:
 		kfree(driver);
-		goto error;
+	case 2:
+		kfree(ir);
+	case 1:
+		printk(DRIVER_NAME "[%d]: out of memory (code=%d)\n",
+			devnum, mem_failure);
+		return -ENOMEM;
+	}
+
+	driver->minor = minor;
+	ir->d = driver;
+	ir->devnum = devnum;
+	ir->usbdev = dev;
+	ir->len_in = maxp;
+	ir->overflow_len = 0;
+	ir->flags.connected = 0;
+	ir->flags.pinnacle = is_pinnacle;
+	ir->flags.microsoft_gen1 = is_microsoft_gen1;
+	ir->flags.transmitter_mask_inverted =
+		usb_match_id(intf, transmitter_mask_list) ? 0 : 1;
+
+	ir->lircdata = PULSE_MASK;
+	ir->is_pulse = 0;
+
+	/* ir->flags.transmitter_mask_inverted must be set */
+	set_transmitter_mask(ir, MCE_DEFAULT_TX_MASK);
+	/* Saving usb interface data for use by the transmitter routine */
+	ir->usb_ep_in = ep_in;
+	ir->usb_ep_out = ep_out;
+
+	if (dev->descriptor.iManufacturer
+	    && usb_string(dev, dev->descriptor.iManufacturer,
+			  buf, sizeof(buf)) > 0)
+		strlcpy(name, buf, sizeof(name));
+	if (dev->descriptor.iProduct
+	    && usb_string(dev, dev->descriptor.iProduct,
+			  buf, sizeof(buf)) > 0)
+		snprintf(name + strlen(name), sizeof(name) - strlen(name),
+			 " %s", buf);
+	printk(DRIVER_NAME "[%d]: %s on usb%d:%d\n", devnum, name,
+	       dev->bus->busnum, devnum);
+
+	/* inbound data */
+	usb_fill_int_urb(ir->urb_in, dev, pipe, ir->buf_in,
+		maxp, (usb_complete_t) mceusb_dev_recv, ir, ep_in->bInterval);
+	ir->urb_in->transfer_dma = ir->dma_in;
+	ir->urb_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+	/* initialize device */
+	if (ir->flags.pinnacle) {
+		int usbret;
+
+		/*
+		 * I have no idea why but this reset seems to be crucial to
+		 * getting the device to do outbound IO correctly - without
+		 * this the device seems to hang, ignoring all input - although
+		 * IR signals are correctly sent from the device, no input is
+		 * interpreted by the device and the host never does the
+		 * completion routine
+		 */
+
+		usbret = usb_reset_configuration(dev);
+		printk(DRIVER_NAME "[%d]: usb reset config ret %x\n",
+		       devnum, usbret);
+
+		/*
+		 * its possible we really should wait for a return
+		 * for each of these...
+		 */
+		request_packet_async(ir, ep_in, NULL, maxp, MCEUSB_INBOUND);
+		request_packet_async(ir, ep_out, pin_init1, sizeof(pin_init1),
+				     MCEUSB_OUTBOUND);
+		request_packet_async(ir, ep_in, NULL, maxp, MCEUSB_INBOUND);
+		request_packet_async(ir, ep_out, pin_init2, sizeof(pin_init2),
+				     MCEUSB_OUTBOUND);
+		request_packet_async(ir, ep_in, NULL, maxp, MCEUSB_INBOUND);
+		request_packet_async(ir, ep_out, pin_init3, sizeof(pin_init3),
+				     MCEUSB_OUTBOUND);
+	} else if (ir->flags.microsoft_gen1) {
+		/* original ms mce device requires some additional setup */
+		mceusb_gen1_init(ir);
+	} else {
+
+		request_packet_async(ir, ep_in, NULL, maxp, MCEUSB_INBOUND);
+		request_packet_async(ir, ep_in, NULL, maxp, MCEUSB_INBOUND);
+		request_packet_async(ir, ep_out, init1,
+				     sizeof(init1), MCEUSB_OUTBOUND);
+		request_packet_async(ir, ep_in, NULL, maxp, MCEUSB_INBOUND);
+		request_packet_async(ir, ep_out, init2,
+				     sizeof(init2), MCEUSB_OUTBOUND);
 	}
-	dev->driver = driver;
 
 	/*
-	 * clear off the first few messages. these look like
-	 * calibration or test data, i can't really tell
-	 * this also flushes in case we have random ir data queued up
+	 * if we don't issue the correct number of receives (MCEUSB_INBOUND)
+	 * for each outbound, then the first few ir pulses will be interpreted
+	 * by the usb_async_callback routine - we should ensure we have the
+	 * right amount OR less - as the meusb_dev_recv routine will handle
+	 * the control packets OK - they start with 0x9f - but the async
+	 * callback doesn't handle ir pulse packets
 	 */
-	for (i = 0; i < 40; i++)
-		(void) usb_bulk_msg(udev,
-				    usb_rcvbulkpipe(udev,
-						    dev->bulk_in_endpointAddr),
-				    junk, 64, &partial, HZ*10);
+	request_packet_async(ir, ep_in, NULL, maxp, 0);
 
-	msir_cleanup(dev);
-	mceusb_setup(udev);
+	usb_set_intfdata(intf, ir);
 
-#ifdef KERNEL_2_5
-	/* we can register the device now, as it is ready */
-	usb_set_intfdata(interface, dev);
-#endif
-	mutex_unlock(&minor_table_mutex);
-#ifdef KERNEL_2_5
 	return 0;
-#else
-	return dev;
-#endif
-error:
-	if (likely(dev))
-		mceusb_delete(dev);
-
-	dev = NULL;
-	dprintk("%s: retval = %x", __func__, retval);
-	mutex_unlock(&minor_table_mutex);
-#ifdef KERNEL_2_5
-	return retval;
-#else
-	return NULL;
-#endif
 }
 
-/**
- * mceusb_disconnect
- *
- * Called by the usb core when the device is removed from the system.
- *
- */
-#ifdef KERNEL_2_5
-static void mceusb_disconnect(struct usb_interface *interface)
-#else
-static void mceusb_disconnect(struct usb_device *udev, void *ptr)
-#endif
+
+static void mceusb_dev_disconnect(struct usb_interface *intf)
 {
-	struct mceusb_device *dev;
-	int minor;
-#ifdef KERNEL_2_5
-	dev = usb_get_intfdata(interface);
-	usb_set_intfdata(interface, NULL);
-#else
-	dev = (struct mceusb_device *)ptr;
-#endif
+	struct usb_device *dev = interface_to_usbdev(intf);
+	struct mceusb_dev *ir = usb_get_intfdata(intf);
 
-	mutex_lock(&minor_table_mutex);
-	mutex_lock(&dev->lock);
-	minor = dev->minor;
+	usb_set_intfdata(intf, NULL);
 
-	/* unhook lirc things */
-	lirc_unregister_driver(minor);
-	lirc_buffer_free(dev->driver->rbuf);
-	kfree(dev->driver->rbuf);
-	kfree(dev->driver);
+	if (!ir || !ir->d)
+		return;
 
-	mutex_unlock(&dev->lock);
-	mceusb_delete(dev);
+	ir->usbdev = NULL;
+	wake_up_all(&ir->wait_out);
 
-	printk(KERN_INFO "Microsoft IR Transceiver #%d now disconnected\n",
-	       minor);
-	mutex_unlock(&minor_table_mutex);
-}
+	mutex_lock(&ir->lock);
+	usb_kill_urb(ir->urb_in);
+	usb_free_urb(ir->urb_in);
+	usb_buffer_free(dev, ir->len_in, ir->buf_in, ir->dma_in);
+	mutex_unlock(&ir->lock);
 
+	unregister_from_lirc(ir);
+}
 
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
+static int mceusb_dev_suspend(struct usb_interface *intf, pm_message_t message)
+{
+	struct mceusb_dev *ir = usb_get_intfdata(intf);
+	printk(DRIVER_NAME "[%d]: suspend\n", ir->devnum);
+	usb_kill_urb(ir->urb_in);
+	return 0;
+}
 
-static int __init usb_mceusb_init(void)
+static int mceusb_dev_resume(struct usb_interface *intf)
 {
-	int result;
+	struct mceusb_dev *ir = usb_get_intfdata(intf);
+	printk(DRIVER_NAME "[%d]: resume\n", ir->devnum);
+	if (usb_submit_urb(ir->urb_in, GFP_ATOMIC))
+		return -EIO;
+	return 0;
+}
+#endif
 
-	/* register this driver with the USB subsystem */
-	result = usb_register(&mceusb_driver);
-#ifdef KERNEL_2_5
-	if (result) {
-#else
-	if (result < 0) {
+static struct usb_driver mceusb_dev_driver = {
+	LIRC_THIS_MODULE(.owner = THIS_MODULE)
+	.name =		DRIVER_NAME,
+	.probe =	mceusb_dev_probe,
+	.disconnect =	mceusb_dev_disconnect,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
+	.suspend =	mceusb_dev_suspend,
+	.resume =	mceusb_dev_resume,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23)
+	.reset_resume =	mceusb_dev_resume,
 #endif
-		err("usb_register failed for the " DRIVER_NAME
-		    " driver. error number %d", result);
-#ifdef KERNEL_2_5
-		return result;
-#else
-		return -1;
 #endif
+	.id_table =	mceusb_dev_table
+};
+
+static int __init mceusb_dev_init(void)
+{
+	int i;
+
+	printk(KERN_INFO DRIVER_NAME ": " DRIVER_DESC " " DRIVER_VERSION "\n");
+	printk(KERN_INFO DRIVER_NAME ": " DRIVER_AUTHOR "\n");
+	dprintk(DRIVER_NAME ": debug mode enabled\n");
+
+	i = usb_register(&mceusb_dev_driver);
+	if (i < 0) {
+		printk(DRIVER_NAME ": usb register failed, result = %d\n", i);
+		return -ENODEV;
 	}
 
-	printk(KERN_INFO DRIVER_DESC " " DRIVER_VERSION "\n");
 	return 0;
 }
 
-
-static void __exit usb_mceusb_exit(void)
+static void __exit mceusb_dev_exit(void)
 {
-	usb_deregister(&mceusb_driver);
+	usb_deregister(&mceusb_dev_driver);
 }
 
-module_init(usb_mceusb_init);
-module_exit(usb_mceusb_exit);
+module_init(mceusb_dev_init);
+module_exit(mceusb_dev_exit);
 
 MODULE_DESCRIPTION(DRIVER_DESC);
 MODULE_AUTHOR(DRIVER_AUTHOR);
 MODULE_LICENSE("GPL");
-MODULE_DEVICE_TABLE(usb, mceusb_table);
+MODULE_DEVICE_TABLE(usb, mceusb_dev_table);
+/* this was originally lirc_mceusb2, lirc_mceusb and lirc_mceusb2 merged now */
+MODULE_ALIAS("lirc_mceusb2");
 
-module_param(debug, int, S_IRUGO | S_IWUSR);
+module_param(debug, bool, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(debug, "Debug enabled or not");
 
 EXPORT_NO_SYMBOLS;
diff --git a/ubuntu/lirc/lirc_sir/lirc_sir.c b/ubuntu/lirc/lirc_sir/lirc_sir.c
index d7c789a..ac91ec7 100644
--- a/ubuntu/lirc/lirc_sir/lirc_sir.c
+++ b/ubuntu/lirc/lirc_sir/lirc_sir.c
@@ -163,8 +163,16 @@ static unsigned int duty_cycle = 50;   /* duty cycle of 50% */
 #define LIRC_IRQ 4
 #endif
 #ifndef LIRC_PORT
+/* for external dongles, default to com1 */
+#if defined(LIRC_SIR_ACTISYS_ACT200L) || \
+    defined(LIRC_SIR_ACTISYS_ACT220L) || \
+    defined(LIRC_SIR_TEKRAM)
+#define LIRC_PORT 0x3f8
+#else
+/* onboard sir ports are typically com3 */
 #define LIRC_PORT 0x3e8
 #endif
+#endif
 
 static int io = LIRC_PORT;
 static int irq = LIRC_IRQ;
diff --git a/ubuntu/lirc/lirc_wpc8769l/lirc_wpc8769l.h b/ubuntu/lirc/lirc_wpc8769l/lirc_wpc8769l.h
index 464f3c0..d250bdc 100644
--- a/ubuntu/lirc/lirc_wpc8769l/lirc_wpc8769l.h
+++ b/ubuntu/lirc/lirc_wpc8769l/lirc_wpc8769l.h
@@ -1,4 +1,4 @@
-/*      $Id: lirc_wpc8769l.h,v 1.4 2009/02/05 20:55:20 lirc Exp $      */
+/*      $Id: lirc_wpc8769l.h,v 1.5 2009/06/15 15:11:39 jarodwilson Exp $      */
 
 /****************************************************************************
  ** lirc_wpc8769l.h ****************************************************
@@ -67,12 +67,12 @@
 /* WPC8769L register set definitions. Note that these are all wild guesses.*/
 
 /* Registers for I/O range 1. */
-#define WPC8769L_SELECT_REG				0x03
+#define WPC8769L_SELECT_REG			0x03
 
 /*------------*/
-#define WPC8769L_BANK_00				0x00
+#define WPC8769L_BANK_00			0x00
 
-#define WPC8769L_DATA_REG				0x00
+#define WPC8769L_DATA_REG			0x00
 
 #define WPC8769L_INTERRUPT_REG			0x01
 #define WPC8769L_INTERRUPT_1_MASK		0x01
@@ -83,7 +83,7 @@
 #define WPC8769L_DATA_STATUS_MASK_1		0x02
 #define WPC8769L_DATA_STATUS_MASK_2		0xd0
 
-#define WPC8769L_CONFIG_REG				0x04
+#define WPC8769L_CONFIG_REG			0x04
 #define WPC8769L_CONFIG_OFF_MASK		0xe0
 #define WPC8769L_CONFIG_ON_MASK			0xc0
 
@@ -94,43 +94,43 @@
 #define WPC8769L_TIMEOUT_RESET_MASK		0x20
 
 /*------------*/
-#define WPC8769L_BANK_E0				0xe0
+#define WPC8769L_BANK_E0			0xe0
 
 #define WPC8769L_CONFIG6_REG			0x00
 #define WPC8769L_CONFIG6_MASK			0x4b
 
 #define WPC8769L_CONFIG7_REG			0x01
 
-#define WPC8769L_HARDWARE_ENABLE1_REG	0x02
-#define WPC8769L_HARDWARE_ENABLE1_MASK	0x01
+#define WPC8769L_HARDWARE_ENABLE1_REG		0x02
+#define WPC8769L_HARDWARE_ENABLE1_MASK		0x01
 
 #define WPC8769L_CONFIG5_REG			0x04
 #define WPC8769L_CONFIG5_ON_MASK		0x30
 
-#define WPC8769L_REMAINING_RX_DATA_REG	0x07
+#define WPC8769L_REMAINING_RX_DATA_REG		0x07
 
 /*------------*/
-#define WPC8769L_BANK_E4				0xe4
+#define WPC8769L_BANK_E4			0xe4
 
-#define WPC8769L_READ_ON_STARTUP_REG	0x00
+#define WPC8769L_READ_ON_STARTUP_REG		0x00
 
 /*------------*/
-#define WPC8769L_BANK_EC				0xec
+#define WPC8769L_BANK_EC			0xec
 
 #define WPC8769L_CONFIG3_REG			0x04
 #define WPC8769L_CONFIG3_ON_MASK		0x01
 #define WPC8769L_CONFIG3_MASK_1			0x10
 
 /*------------*/
-#define WPC8769L_BANK_F0				0xf0
+#define WPC8769L_BANK_F0			0xf0
 
-#define WPC8769L_WAKEUP_STATUS_LEG_REG	0x02
-#define WPC8769L_WAKEUP_STATUS_LEG_MASK	0x04
+#define WPC8769L_WAKEUP_STATUS_LEG_REG		0x02
+#define WPC8769L_WAKEUP_STATUS_LEG_MASK		0x04
 #define WPC8769L_WAKEUP_STATUS_LEG_MASK_A	0x02
 #define WPC8769L_WAKEUP_STATUS_LEG_MASK_B	0x08
 
 /*------------*/
-#define WPC8769L_BANK_F4				0xf4
+#define WPC8769L_BANK_F4			0xf4
 
 #define WPC8769L_CONFIG9_REG			0x01
 
@@ -157,27 +157,27 @@
 #define WPC8769L_CLOCK_ON_MASK			0x01
 
 #define WPC8769L_WAKEUP_CONFIG_REG		0x1a
-#define WPC8769L_WAKEUP_CONFIG_PRE_MASK	0x80
+#define WPC8769L_WAKEUP_CONFIG_PRE_MASK		0x80
 #define WPC8769L_MAX_INFO_BITS_BIAS		0x0e
-#define WPC8769L_MAX_INFO_BITS_SHIFT	0x01
+#define WPC8769L_MAX_INFO_BITS_SHIFT		0x01
 
 #define WPC8769L_WAKEUP_CONFIG3_REG		0x13
 #define WPC8769L_WAKEUP_CONFIG3_OFF_MASK	0x10
-#define WPC8769L_WAKEUP_CONFIG3_ON_MASK	0x21
-#define WPC8769L_WAKEUP_CONFIG3_A_SHIFT	0x01
-#define WPC8769L_WAKEUP_CONFIG3_A_MASK	0x03
-#define WPC8769L_WAKEUP_CONFIG3_B_SHIFT	0x03
-#define WPC8769L_WAKEUP_CONFIG3_B_MASK	0x01
+#define WPC8769L_WAKEUP_CONFIG3_ON_MASK		0x21
+#define WPC8769L_WAKEUP_CONFIG3_A_SHIFT		0x01
+#define WPC8769L_WAKEUP_CONFIG3_A_MASK		0x03
+#define WPC8769L_WAKEUP_CONFIG3_B_SHIFT		0x03
+#define WPC8769L_WAKEUP_CONFIG3_B_MASK		0x01
 
 #define WPC8769L_WAKEUP_STATUS_REG		0x14
-#define WPC8769L_WAKEUP_WOKE_UP_MASK	0x01
+#define WPC8769L_WAKEUP_WOKE_UP_MASK		0x01
 #define WPC8769L_WAKEUP_CONFIGURING_MASK	0x17
 
 #define WPC8769L_WAKEUP_CONFIG2_REG		0x15
 #define WPC8769L_WAKEUP_CONFIG2_AND_MASK	0xf9
-#define WPC8769L_WAKEUP_CONFIG2_OR_MASK	0x01
+#define WPC8769L_WAKEUP_CONFIG2_OR_MASK		0x01
 
-#define WPC8769L_WAKEUP_DATA_PTR_REG	0x18
+#define WPC8769L_WAKEUP_DATA_PTR_REG		0x18
 #define WPC8769L_WAKEUP_DATA_BITS		0x20
 #define WPC8769L_WAKEUP_DATA_BASE		0x10
 #define WPC8769L_WAKEUP_MASK_BASE		0x20
-- 
1.6.3.3
