linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/18] linux infrared remote control drivers
@ 2008-09-09  4:05 Jarod Wilson
  2008-09-09  4:05 ` [PATCH 01/18] lirc core device driver infrastructure Jarod Wilson
                   ` (3 more replies)
  0 siblings, 4 replies; 111+ messages in thread
From: Jarod Wilson @ 2008-09-09  4:05 UTC (permalink / raw)
  To: linux-kernel
  Cc: Jarod Wilson, Janne Grunau, Christoph Bartelmus, Eric Sandeen,
	Mario Limonciello

The following patch series adds 17 new drivers for assorted infrared and/or RF
remote control receivers and/or transmitters. These drivers have long lived
out-of-tree at http://www.lirc.org/, packaged as 3rd-party modules by many
distributions, and more recently, patched into the kernels of at least Fedora
and Ubuntu. The primary maintainer of lirc, Christoph Bartelmus simply hasn't
had the time to send these bits upstream, and a few months back, gave me the
go-ahead to take on the task.

Most drivers are fairly widely tested, certainly within the MythTV community,
which relies upon this code quite a bit. However, note that in preparation
for this submission, a fair number of code changes have been made only
recently that may or may not have yet made it back into lirc cvs, and thus
might not be as widely tested. Any bugs found were probably introduced by
either myself or Janne Grunau, the primary folks working on the latest round
of whacking checkpatch.pl complaints to prepare for this submission.

Speaking of checkpatch... When I first added the lirc driver patch to the
Fedora kernels (oy, way back in August of 2007), there were tens of thousands
of lines of checkpatch warnings and errors. Through the efforts of myself,
Janne, Eric Sandeen, and misc contributions from others, I'm now quite
tickled to see the following:

--
$ scripts/checkpatch.pl /data/patches/lirc-for-upstream-combined.patch
total: 0 errors, 0 warnings, 17877 lines checked

/data/patches/lirc-for-upstream-combined.patch has no obvious style problems
and is ready for submission.
--

Earlier rounds of checkpatch cleanups have already been fed back into lirc
cvs, and Christoph has given me commit access there, so as to filter the
latest changes back in as well. For the time being, lirc cvs will
continue to be the canonical upstream for the lirc drivers, and all the
earlier kernel compat bits get stripped out via an export script[1],
though the script is still under a bit of development... I'll be taking
point on maintaining the git tree[2] and synchronizing changes back and
forth with cvs, at least until such time as everything is in the kernel,
and nobody has need for out-of-tree drivers anymore, at which time the
drivers will be dropped from the main lirc tarball.

Not all drivers have been tested with this codebase and certainly not all
devices they support, but its a solid place to start with these in-kernel.
Patches are against 2.6.27-rc5-git9 or so, and have been tested running
the same, at least in the cases where hardware was available. These patches
are also in the latest nightly Fedora rawhide kernel, for those who want
instant gratification[3]. Note that you also need the lirc userspace to
really do any meaningful testing...

Signed-off-by: Jarod Wilson <jarod@redhat.com>
Signed-off-by: Janne Grunau <j@jannau.net>
CC: Christoph Bartelmus <lirc@bartelmus.de>
CC: Eric Sandeen <esandeen@redhat.com>
CC: Mario Limonciello <superm1@ubuntu.com>

[1] http://people.redhat.com/jwilson/lirc/, in a tarball atm
[2] http://git.wilsonet.com/linux-2.6-lirc.git/ (s/http/git/ to clone)
[3] http://kojipkgs.fedoraproject.org/packages/kernel/2.6.27/0.314.rc5.git9.fc10/
    is the latest build, at least as of this submission

Combined diffstat:

 MAINTAINERS                           |    9 +
 drivers/input/Kconfig                 |    2 +
 drivers/input/Makefile                |    2 +
 drivers/input/lirc/Kconfig            |  142 ++++
 drivers/input/lirc/Makefile           |   25 +
 drivers/input/lirc/commandir.c        |  982 +++++++++++++++++++++++
 drivers/input/lirc/commandir.h        |   68 ++
 drivers/input/lirc/lirc.h             |  103 +++
 drivers/input/lirc/lirc_atiusb.c      | 1321 +++++++++++++++++++++++++++++++
 drivers/input/lirc/lirc_bt829.c       |  388 +++++++++
 drivers/input/lirc/lirc_cmdir.c       |  596 ++++++++++++++
 drivers/input/lirc/lirc_cmdir.h       |   25 +
 drivers/input/lirc/lirc_dev.c         |  809 +++++++++++++++++++
 drivers/input/lirc/lirc_dev.h         |  262 ++++++
 drivers/input/lirc/lirc_i2c.c         |  639 +++++++++++++++
 drivers/input/lirc/lirc_igorplugusb.c |  619 +++++++++++++++
 drivers/input/lirc/lirc_imon.c        | 1280 ++++++++++++++++++++++++++++++
 drivers/input/lirc/lirc_it87.c        |  999 +++++++++++++++++++++++
 drivers/input/lirc/lirc_it87.h        |  116 +++
 drivers/input/lirc/lirc_ite8709.c     |  545 +++++++++++++
 drivers/input/lirc/lirc_mceusb.c      |  890 +++++++++++++++++++++
 drivers/input/lirc/lirc_mceusb2.c     | 1119 ++++++++++++++++++++++++++
 drivers/input/lirc/lirc_parallel.c    |  728 +++++++++++++++++
 drivers/input/lirc/lirc_parallel.h    |   26 +
 drivers/input/lirc/lirc_sasem.c       |  969 +++++++++++++++++++++++
 drivers/input/lirc/lirc_serial.c      | 1312 +++++++++++++++++++++++++++++++
 drivers/input/lirc/lirc_sir.c         | 1302 ++++++++++++++++++++++++++++++
 drivers/input/lirc/lirc_streamzap.c   |  795 +++++++++++++++++++
 drivers/input/lirc/lirc_ttusbir.c     |  400 ++++++++++
 drivers/input/lirc/lirc_zilog.c       | 1395 +++++++++++++++++++++++++++++++++
 30 files changed, 17868 insertions(+), 0 deletions(-)
 create mode 100644 drivers/input/lirc/Kconfig
 create mode 100644 drivers/input/lirc/Makefile
 create mode 100644 drivers/input/lirc/commandir.c
 create mode 100644 drivers/input/lirc/commandir.h
 create mode 100644 drivers/input/lirc/lirc.h
 create mode 100644 drivers/input/lirc/lirc_atiusb.c
 create mode 100644 drivers/input/lirc/lirc_bt829.c
 create mode 100644 drivers/input/lirc/lirc_cmdir.c
 create mode 100644 drivers/input/lirc/lirc_cmdir.h
 create mode 100644 drivers/input/lirc/lirc_dev.c
 create mode 100644 drivers/input/lirc/lirc_dev.h
 create mode 100644 drivers/input/lirc/lirc_i2c.c
 create mode 100644 drivers/input/lirc/lirc_igorplugusb.c
 create mode 100644 drivers/input/lirc/lirc_imon.c
 create mode 100644 drivers/input/lirc/lirc_it87.c
 create mode 100644 drivers/input/lirc/lirc_it87.h
 create mode 100644 drivers/input/lirc/lirc_ite8709.c
 create mode 100644 drivers/input/lirc/lirc_mceusb.c
 create mode 100644 drivers/input/lirc/lirc_mceusb2.c
 create mode 100644 drivers/input/lirc/lirc_parallel.c
 create mode 100644 drivers/input/lirc/lirc_parallel.h
 create mode 100644 drivers/input/lirc/lirc_sasem.c
 create mode 100644 drivers/input/lirc/lirc_serial.c
 create mode 100644 drivers/input/lirc/lirc_sir.c
 create mode 100644 drivers/input/lirc/lirc_streamzap.c
 create mode 100644 drivers/input/lirc/lirc_ttusbir.c
 create mode 100644 drivers/input/lirc/lirc_zilog.c

^ permalink raw reply	[flat|nested] 111+ messages in thread

* [PATCH 01/18] lirc core device driver infrastructure
  2008-09-09  4:05 [PATCH 0/18] linux infrared remote control drivers Jarod Wilson
@ 2008-09-09  4:05 ` Jarod Wilson
  2008-09-09  4:05   ` [PATCH 02/18] lirc serial port receiver/transmitter device driver Jarod Wilson
                     ` (5 more replies)
  2008-09-09  4:36 ` [PATCH 0/18] linux infrared remote control drivers Chris Wedgwood
                   ` (2 subsequent siblings)
  3 siblings, 6 replies; 111+ messages in thread
From: Jarod Wilson @ 2008-09-09  4:05 UTC (permalink / raw)
  To: linux-kernel
  Cc: Jarod Wilson, Jarod Wilson, Janne Grunau, Christoph Bartelmus,
	Mario Limonciello

-Add Kconfig and Makefile bits
-Add device driver interface and headers

The initial Kconfig and Makefile bits were done by Mario Limonciello for
the Ubuntu kernel, but have been tweaked a bit since then. Any errors are
probably my doing.

Signed-off-by: Jarod Wilson <jarod@redhat.com>
Signed-off-by: Janne Grunau <j@jannau.net>
CC: Christoph Bartelmus <lirc@bartelmus.de>
CC: Mario Limonciello <superm1@ubuntu.com>
---
 MAINTAINERS                   |    9 +
 drivers/input/Kconfig         |    2 +
 drivers/input/Makefile        |    2 +
 drivers/input/lirc/Kconfig    |   21 +
 drivers/input/lirc/Makefile   |    8 +
 drivers/input/lirc/lirc.h     |  103 ++++++
 drivers/input/lirc/lirc_dev.c |  809 +++++++++++++++++++++++++++++++++++++++++
 drivers/input/lirc/lirc_dev.h |  262 +++++++++++++
 8 files changed, 1216 insertions(+), 0 deletions(-)
 create mode 100644 drivers/input/lirc/Kconfig
 create mode 100644 drivers/input/lirc/Makefile
 create mode 100644 drivers/input/lirc/lirc.h
 create mode 100644 drivers/input/lirc/lirc_dev.c
 create mode 100644 drivers/input/lirc/lirc_dev.h

diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig
index 5f9d860..2ba0904 100644
--- a/drivers/input/Kconfig
+++ b/drivers/input/Kconfig
@@ -170,6 +170,8 @@ source "drivers/input/tablet/Kconfig"
 
 source "drivers/input/touchscreen/Kconfig"
 
+source "drivers/input/lirc/Kconfig"
+
 source "drivers/input/misc/Kconfig"
 
 endif
diff --git a/drivers/input/Makefile b/drivers/input/Makefile
index 98c4f9a..4dcb852 100644
--- a/drivers/input/Makefile
+++ b/drivers/input/Makefile
@@ -25,3 +25,5 @@ obj-$(CONFIG_INPUT_MISC)	+= misc/
 obj-$(CONFIG_INPUT_APMPOWER)	+= apm-power.o
 
 obj-$(CONFIG_XEN_KBDDEV_FRONTEND)	+= xen-kbdfront.o
+
+obj-$(CONFIG_INPUT_LIRC)	+= lirc/
diff --git a/drivers/input/lirc/Kconfig b/drivers/input/lirc/Kconfig
new file mode 100644
index 0000000..2a04d9b
--- /dev/null
+++ b/drivers/input/lirc/Kconfig
@@ -0,0 +1,21 @@
+#
+# LIRC driver(s) configuration
+#
+menuconfig INPUT_LIRC
+	bool "Linux Infrared Remote Control IR receiver/transmitter drivers"
+	default n
+	help
+	  Say Y here, and all supported Linux Infrared Remote Control IR and
+	  RF receiver and transmitter drivers will be displayed. When paired
+	  with a remote control and the lirc daemon, the receiver drivers
+	  allow control of your Linux system via remote control.
+
+if INPUT_LIRC
+
+config LIRC_DEV
+	tristate "LIRC device loadable module support"
+	default n
+	help
+	  LIRC device loadable module support, required for most LIRC drivers
+
+endif
diff --git a/drivers/input/lirc/Makefile b/drivers/input/lirc/Makefile
new file mode 100644
index 0000000..cdb4c45
--- /dev/null
+++ b/drivers/input/lirc/Makefile
@@ -0,0 +1,8 @@
+# Makefile for the lirc drivers.
+#
+
+# Each configuration option enables a list of files.
+
+EXTRA_CFLAGS =-DIRCTL_DEV_MAJOR=61 -DLIRC_SERIAL_TRANSMITTER -I$(src)
+
+obj-$(CONFIG_LIRC_DEV)		+= lirc_dev.o
diff --git a/drivers/input/lirc/lirc.h b/drivers/input/lirc/lirc.h
new file mode 100644
index 0000000..dcdb6e8
--- /dev/null
+++ b/drivers/input/lirc/lirc.h
@@ -0,0 +1,103 @@
+/*
+ * lirc.h - linux infrared remote control header file
+ * last modified 2007/09/27
+ */
+
+#ifndef _LINUX_LIRC_H
+#define _LINUX_LIRC_H
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+#define PULSE_BIT  0x01000000
+#define PULSE_MASK 0x00FFFFFF
+
+/*
+ * lirc compatible hardware features
+ */
+
+
+#define LIRC_MODE2SEND(x) (x)
+#define LIRC_SEND2MODE(x) (x)
+#define LIRC_MODE2REC(x) ((x) << 16)
+#define LIRC_REC2MODE(x) ((x) >> 16)
+
+#define LIRC_MODE_RAW                  0x00000001
+#define LIRC_MODE_PULSE                0x00000002
+#define LIRC_MODE_MODE2                0x00000004
+#define LIRC_MODE_CODE                 0x00000008
+#define LIRC_MODE_LIRCCODE             0x00000010
+#define LIRC_MODE_STRING               0x00000020
+
+
+#define LIRC_CAN_SEND_RAW              LIRC_MODE2SEND(LIRC_MODE_RAW)
+#define LIRC_CAN_SEND_PULSE            LIRC_MODE2SEND(LIRC_MODE_PULSE)
+#define LIRC_CAN_SEND_MODE2            LIRC_MODE2SEND(LIRC_MODE_MODE2)
+#define LIRC_CAN_SEND_CODE             LIRC_MODE2SEND(LIRC_MODE_CODE)
+#define LIRC_CAN_SEND_LIRCCODE         LIRC_MODE2SEND(LIRC_MODE_LIRCCODE)
+#define LIRC_CAN_SEND_STRING           LIRC_MODE2SEND(LIRC_MODE_STRING)
+
+#define LIRC_CAN_SEND_MASK             0x0000003f
+
+#define LIRC_CAN_SET_SEND_CARRIER      0x00000100
+#define LIRC_CAN_SET_SEND_DUTY_CYCLE   0x00000200
+#define LIRC_CAN_SET_TRANSMITTER_MASK  0x00000400
+
+#define LIRC_CAN_REC_RAW               LIRC_MODE2REC(LIRC_MODE_RAW)
+#define LIRC_CAN_REC_PULSE             LIRC_MODE2REC(LIRC_MODE_PULSE)
+#define LIRC_CAN_REC_MODE2             LIRC_MODE2REC(LIRC_MODE_MODE2)
+#define LIRC_CAN_REC_CODE              LIRC_MODE2REC(LIRC_MODE_CODE)
+#define LIRC_CAN_REC_LIRCCODE          LIRC_MODE2REC(LIRC_MODE_LIRCCODE)
+#define LIRC_CAN_REC_STRING            LIRC_MODE2REC(LIRC_MODE_STRING)
+
+#define LIRC_CAN_REC_MASK              LIRC_MODE2REC(LIRC_CAN_SEND_MASK)
+
+#define LIRC_CAN_SET_REC_CARRIER       (LIRC_CAN_SET_SEND_CARRIER << 16)
+#define LIRC_CAN_SET_REC_DUTY_CYCLE    (LIRC_CAN_SET_SEND_DUTY_CYCLE << 16)
+
+#define LIRC_CAN_SET_REC_DUTY_CYCLE_RANGE 0x40000000
+#define LIRC_CAN_SET_REC_CARRIER_RANGE    0x80000000
+#define LIRC_CAN_GET_REC_RESOLUTION       0x20000000
+
+#define LIRC_CAN_SEND(x) ((x)&LIRC_CAN_SEND_MASK)
+#define LIRC_CAN_REC(x) ((x)&LIRC_CAN_REC_MASK)
+
+#define LIRC_CAN_NOTIFY_DECODE            0x01000000
+
+/*
+ * IOCTL commands for lirc driver
+ */
+
+#define LIRC_GET_FEATURES              _IOR('i', 0x00000000, __u32)
+
+#define LIRC_GET_SEND_MODE             _IOR('i', 0x00000001, __u32)
+#define LIRC_GET_REC_MODE              _IOR('i', 0x00000002, __u32)
+#define LIRC_GET_SEND_CARRIER          _IOR('i', 0x00000003, __u32)
+#define LIRC_GET_REC_CARRIER           _IOR('i', 0x00000004, __u32)
+#define LIRC_GET_SEND_DUTY_CYCLE       _IOR('i', 0x00000005, __u32)
+#define LIRC_GET_REC_DUTY_CYCLE        _IOR('i', 0x00000006, __u32)
+#define LIRC_GET_REC_RESOLUTION        _IOR('i', 0x00000007, __u32)
+
+/* code length in bits, currently only for LIRC_MODE_LIRCCODE */
+#define LIRC_GET_LENGTH                _IOR('i', 0x0000000f, __u32)
+
+#define LIRC_SET_SEND_MODE             _IOW('i', 0x00000011, __u32)
+#define LIRC_SET_REC_MODE              _IOW('i', 0x00000012, __u32)
+/* Note: these can reset the according pulse_width */
+#define LIRC_SET_SEND_CARRIER          _IOW('i', 0x00000013, __u32)
+#define LIRC_SET_REC_CARRIER           _IOW('i', 0x00000014, __u32)
+#define LIRC_SET_SEND_DUTY_CYCLE       _IOW('i', 0x00000015, __u32)
+#define LIRC_SET_REC_DUTY_CYCLE        _IOW('i', 0x00000016, __u32)
+#define LIRC_SET_TRANSMITTER_MASK      _IOW('i', 0x00000017, __u32)
+
+/* to set a range use
+   LIRC_SET_REC_DUTY_CYCLE_RANGE/LIRC_SET_REC_CARRIER_RANGE with the
+   lower bound first and later
+   LIRC_SET_REC_DUTY_CYCLE/LIRC_SET_REC_CARRIER with the upper bound */
+
+#define LIRC_SET_REC_DUTY_CYCLE_RANGE  _IOW('i', 0x0000001e, __u32)
+#define LIRC_SET_REC_CARRIER_RANGE     _IOW('i', 0x0000001f, __u32)
+
+#define LIRC_NOTIFY_DECODE             _IO('i', 0x00000020)
+
+#endif
diff --git a/drivers/input/lirc/lirc_dev.c b/drivers/input/lirc/lirc_dev.c
new file mode 100644
index 0000000..c8f325c
--- /dev/null
+++ b/drivers/input/lirc/lirc_dev.c
@@ -0,0 +1,809 @@
+/*
+ * LIRC base driver
+ *
+ * (L) by Artur Lipowski <alipowski@interia.pl>
+ *
+ *  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>
+
+#include <linux/autoconf.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/ioctl.h>
+#include <linux/fs.h>
+#include <linux/poll.h>
+#include <linux/smp_lock.h>
+#include <linux/completion.h>
+#include <linux/uaccess.h>
+#include <linux/errno.h>
+#include <linux/semaphore.h>
+#define __KERNEL_SYSCALLS__
+#include <linux/unistd.h>
+#include <linux/kthread.h>
+
+/* SysFS header */
+#include <linux/device.h>
+
+#include "lirc.h"
+#include "lirc_dev.h"
+
+static int debug;
+#define dprintk(fmt, args...)					\
+	do {							\
+		if (debug)					\
+			printk(KERN_DEBUG fmt, ## args);	\
+	} while (0)
+
+#define IRCTL_DEV_NAME    "BaseRemoteCtl"
+#define SUCCESS           0
+#define NOPLUG            -1
+#define LOGHEAD           "lirc_dev (%s[%d]): "
+
+struct irctl {
+	struct lirc_plugin p;
+	int attached;
+	int open;
+
+	struct mutex buffer_lock;
+	struct lirc_buffer *buf;
+
+	struct task_struct *task;
+	long jiffies_to_wait;
+
+};
+
+static DEFINE_MUTEX(plugin_lock);
+
+static struct irctl irctls[MAX_IRCTL_DEVICES];
+static struct file_operations fops;
+
+/* Only used for sysfs but defined to void otherwise */
+static struct class *lirc_class;
+
+/*  helper function
+ *  initializes the irctl structure
+ */
+static inline void init_irctl(struct irctl *ir)
+{
+	memset(&ir->p, 0, sizeof(struct lirc_plugin));
+	mutex_init(&ir->buffer_lock);
+	ir->p.minor = NOPLUG;
+
+	ir->task = NULL;
+	ir->jiffies_to_wait = 0;
+
+	ir->open = 0;
+	ir->attached = 0;
+}
+
+static void cleanup(struct irctl *ir)
+{
+	dprintk(LOGHEAD "cleaning up\n", ir->p.name, ir->p.minor);
+
+	device_destroy(lirc_class, MKDEV(IRCTL_DEV_MAJOR, ir->p.minor));
+
+	if (ir->buf != ir->p.rbuf) {
+		lirc_buffer_free(ir->buf);
+		kfree(ir->buf);
+	}
+	ir->buf = NULL;
+
+	init_irctl(ir);
+}
+
+/*  helper function
+ *  reads key codes from plugin and puts them into buffer
+ *  buffer free space is checked and locking performed
+ *  returns 0 on success
+ */
+static inline int add_to_buf(struct irctl *ir)
+{
+	if (lirc_buffer_full(ir->buf)) {
+		dprintk(LOGHEAD "buffer overflow\n",
+			ir->p.name, ir->p.minor);
+		return -EOVERFLOW;
+	}
+
+	if (ir->p.add_to_buf) {
+		int res = -ENODATA;
+		int got_data = 0;
+
+		/* service the device as long as it is returning
+		 * data and we have space
+		 */
+		while (!lirc_buffer_full(ir->buf)) {
+			res = ir->p.add_to_buf(ir->p.data, ir->buf);
+			if (res == SUCCESS)
+				got_data++;
+			else
+				break;
+		}
+
+		if (res == -ENODEV)
+			kthread_stop(ir->task);
+
+		return got_data ? SUCCESS : res;
+	}
+
+	return SUCCESS;
+}
+
+/* main function of the polling thread
+ */
+static int lirc_thread(void *irctl)
+{
+	struct irctl *ir = irctl;
+
+	/* This thread doesn't need any user-level access,
+	 * so get rid of all our resources
+	 */
+
+	dprintk(LOGHEAD "poll thread started\n", ir->p.name, ir->p.minor);
+
+	do {
+		if (ir->open) {
+			if (ir->jiffies_to_wait) {
+				set_current_state(TASK_INTERRUPTIBLE);
+				schedule_timeout(ir->jiffies_to_wait);
+			} else {
+				interruptible_sleep_on(
+					ir->p.get_queue(ir->p.data));
+			}
+			if (kthread_should_stop())
+				break;
+			if (!add_to_buf(ir))
+				wake_up_interruptible(&ir->buf->wait_poll);
+		} else {
+			/* if device not opened so we can sleep half a second */
+			set_current_state(TASK_INTERRUPTIBLE);
+			schedule_timeout(HZ/2);
+		}
+	} while (!kthread_should_stop());
+
+	dprintk(LOGHEAD "poll thread ended\n", ir->p.name, ir->p.minor);
+
+	return 0;
+}
+
+int lirc_register_plugin(struct lirc_plugin *p)
+{
+	struct irctl *ir;
+	int minor;
+	int bytes_in_key;
+	int err;
+	DECLARE_COMPLETION(tn);
+
+	if (!p) {
+		printk(KERN_ERR "lirc_dev: lirc_register_plugin: "
+		       "plugin pointer must be not NULL!\n");
+		err = -EBADRQC;
+		goto out;
+	}
+
+	if (MAX_IRCTL_DEVICES <= p->minor) {
+		printk(KERN_ERR "lirc_dev: lirc_register_plugin: "
+		       "\"minor\" must be between 0 and %d (%d)!\n",
+		       MAX_IRCTL_DEVICES-1, p->minor);
+		err = -EBADRQC;
+		goto out;
+	}
+
+	if (1 > p->code_length || (BUFLEN * 8) < p->code_length) {
+		printk(KERN_ERR "lirc_dev: lirc_register_plugin: "
+		       "code length in bits for minor (%d) "
+		       "must be less than %d!\n",
+		       p->minor, BUFLEN * 8);
+		err = -EBADRQC;
+		goto out;
+	}
+
+	printk(KERN_INFO "lirc_dev: lirc_register_plugin: sample_rate: %d\n",
+		p->sample_rate);
+	if (p->sample_rate) {
+		if (2 > p->sample_rate || HZ < p->sample_rate) {
+			printk(KERN_ERR "lirc_dev: lirc_register_plugin: "
+			       "sample_rate must be between 2 and %d!\n", HZ);
+			err = -EBADRQC;
+			goto out;
+		}
+		if (!p->add_to_buf) {
+			printk(KERN_ERR "lirc_dev: lirc_register_plugin: "
+			       "add_to_buf cannot be NULL when "
+			       "sample_rate is set\n");
+			err = -EBADRQC;
+			goto out;
+		}
+	} else if (!(p->fops && p->fops->read)
+		   && !p->get_queue && !p->rbuf) {
+		printk(KERN_ERR "lirc_dev: lirc_register_plugin: "
+		       "fops->read, get_queue and rbuf "
+		       "cannot all be NULL!\n");
+		err = -EBADRQC;
+		goto out;
+	} else if (!p->get_queue && !p->rbuf) {
+		if (!(p->fops && p->fops->read && p->fops->poll)
+		    || (!p->fops->ioctl && !p->ioctl)) {
+			printk(KERN_ERR "lirc_dev: lirc_register_plugin: "
+			       "neither read, poll nor ioctl can be NULL!\n");
+			err = -EBADRQC;
+			goto out;
+		}
+	}
+
+	if (p->owner == NULL) {
+		printk(KERN_ERR "lirc_dev: lirc_register_plugin: "
+				    "no module owner registered\n");
+		err = -EBADRQC;
+		goto out;
+	}
+
+	mutex_lock(&plugin_lock);
+
+	minor = p->minor;
+
+	if (0 > minor) {
+		/* find first free slot for plugin */
+		for (minor = 0; minor < MAX_IRCTL_DEVICES; minor++)
+			if (irctls[minor].p.minor == NOPLUG)
+				break;
+		if (MAX_IRCTL_DEVICES == minor) {
+			printk(KERN_ERR "lirc_dev: lirc_register_plugin: "
+			       "no free slots for plugins!\n");
+			err = -ENOMEM;
+			goto out_lock;
+		}
+	} else if (irctls[minor].p.minor != NOPLUG) {
+		printk(KERN_ERR "lirc_dev: lirc_register_plugin: "
+		       "minor (%d) just registered!\n", minor);
+		err = -EBUSY;
+		goto out_lock;
+	}
+
+	ir = &irctls[minor];
+
+	if (p->sample_rate) {
+		ir->jiffies_to_wait = HZ / p->sample_rate;
+	} else {
+		/* it means - wait for external event in task queue */
+		ir->jiffies_to_wait = 0;
+	}
+
+	/* some safety check 8-) */
+	p->name[sizeof(p->name)-1] = '\0';
+
+	bytes_in_key = p->code_length/8 + (p->code_length%8 ? 1 : 0);
+
+	if (p->rbuf) {
+		ir->buf = p->rbuf;
+	} else {
+		ir->buf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
+		if (!ir->buf) {
+			err = -ENOMEM;
+			goto out_lock;
+		}
+		if (lirc_buffer_init(ir->buf, bytes_in_key,
+				     BUFLEN/bytes_in_key) != 0) {
+			kfree(ir->buf);
+			err = -ENOMEM;
+			goto out_lock;
+		}
+	}
+
+	if (p->features == 0)
+		p->features = (p->code_length > 8) ?
+			LIRC_CAN_REC_LIRCCODE : LIRC_CAN_REC_CODE;
+
+	ir->p = *p;
+	ir->p.minor = minor;
+
+	device_create(lirc_class, ir->p.dev,
+		      MKDEV(IRCTL_DEV_MAJOR, ir->p.minor), NULL,
+		      "lirc%u", ir->p.minor);
+
+	if (p->sample_rate || p->get_queue) {
+		/* try to fire up polling thread */
+		ir->task = kthread_run(lirc_thread, (void *)ir, "lirc_dev");
+		if (IS_ERR(ir->task)) {
+			printk(KERN_ERR "lirc_dev: lirc_register_plugin: "
+			       "cannot run poll thread for minor = %d\n",
+			       p->minor);
+			err = -ECHILD;
+			goto out_sysfs;
+		}
+	}
+	ir->attached = 1;
+	mutex_unlock(&plugin_lock);
+
+/*
+ * Recent kernels should handle this autmatically by increasing/decreasing
+ * use count when a dependant module is loaded/unloaded.
+ */
+	dprintk("lirc_dev: plugin %s registered at minor number = %d\n",
+		ir->p.name, ir->p.minor);
+	p->minor = minor;
+	return minor;
+
+out_sysfs:
+	device_destroy(lirc_class, MKDEV(IRCTL_DEV_MAJOR, ir->p.minor));
+out_lock:
+	mutex_unlock(&plugin_lock);
+out:
+	return err;
+}
+EXPORT_SYMBOL(lirc_register_plugin);
+
+int lirc_unregister_plugin(int minor)
+{
+	struct irctl *ir;
+	DECLARE_COMPLETION(tn);
+	DECLARE_COMPLETION(tn2);
+
+	if (minor < 0 || minor >= MAX_IRCTL_DEVICES) {
+		printk(KERN_ERR "lirc_dev: lirc_unregister_plugin: "
+		       "\"minor\" must be between 0 and %d!\n",
+		       MAX_IRCTL_DEVICES-1);
+		return -EBADRQC;
+	}
+
+	ir = &irctls[minor];
+
+	mutex_lock(&plugin_lock);
+
+	if (ir->p.minor != minor) {
+		printk(KERN_ERR "lirc_dev: lirc_unregister_plugin: "
+		       "minor (%d) device not registered!", minor);
+		mutex_unlock(&plugin_lock);
+		return -ENOENT;
+	}
+
+	/* end up polling thread */
+	if (ir->task) {
+		wake_up_process(ir->task);
+		kthread_stop(ir->task);
+	}
+
+	dprintk("lirc_dev: plugin %s unregistered from minor number = %d\n",
+		ir->p.name, ir->p.minor);
+
+	ir->attached = 0;
+	if (ir->open) {
+		dprintk(LOGHEAD "releasing opened plugin\n",
+			ir->p.name, ir->p.minor);
+		wake_up_interruptible(&ir->buf->wait_poll);
+		mutex_lock(&ir->buffer_lock);
+		ir->p.set_use_dec(ir->p.data);
+		module_put(ir->p.owner);
+		mutex_unlock(&ir->buffer_lock);
+	} else
+		cleanup(ir);
+	mutex_unlock(&plugin_lock);
+
+/*
+ * Recent kernels should handle this autmatically by increasing/decreasing
+ * use count when a dependant module is loaded/unloaded.
+ */
+
+	return SUCCESS;
+}
+EXPORT_SYMBOL(lirc_unregister_plugin);
+
+/*
+ *
+ */
+static int irctl_open(struct inode *inode, struct file *file)
+{
+	struct irctl *ir;
+	int retval;
+
+	if (MINOR(inode->i_rdev) >= MAX_IRCTL_DEVICES) {
+		dprintk("lirc_dev [%d]: open result = -ENODEV\n",
+			MINOR(inode->i_rdev));
+		return -ENODEV;
+	}
+
+	ir = &irctls[MINOR(inode->i_rdev)];
+
+	dprintk(LOGHEAD "open called\n", ir->p.name, ir->p.minor);
+
+	/* if the plugin has an open function use it instead */
+	if (ir->p.fops && ir->p.fops->open)
+		return ir->p.fops->open(inode, file);
+
+	if (mutex_lock_interruptible(&plugin_lock))
+		return -ERESTARTSYS;
+
+	if (ir->p.minor == NOPLUG) {
+		mutex_unlock(&plugin_lock);
+		dprintk(LOGHEAD "open result = -ENODEV\n",
+			ir->p.name, ir->p.minor);
+		return -ENODEV;
+	}
+
+	if (ir->open) {
+		mutex_unlock(&plugin_lock);
+		dprintk(LOGHEAD "open result = -EBUSY\n",
+			ir->p.name, ir->p.minor);
+		return -EBUSY;
+	}
+
+	/* there is no need for locking here because ir->open is 0
+	 * and lirc_thread isn't using buffer
+	 * plugins which use irq's should allocate them on set_use_inc,
+	 * so there should be no problem with those either.
+	 */
+	ir->buf->head = ir->buf->tail;
+	ir->buf->fill = 0;
+
+	if (ir->p.owner != NULL && try_module_get(ir->p.owner)) {
+		++ir->open;
+		retval = ir->p.set_use_inc(ir->p.data);
+
+		if (retval != SUCCESS) {
+			module_put(ir->p.owner);
+			--ir->open;
+		}
+	} else {
+		if (ir->p.owner == NULL)
+			dprintk(LOGHEAD "no module owner!!!\n",
+				ir->p.name, ir->p.minor);
+
+		retval = -ENODEV;
+	}
+
+	dprintk(LOGHEAD "open result = %d\n", ir->p.name, ir->p.minor, retval);
+	mutex_unlock(&plugin_lock);
+
+	return retval;
+}
+
+/*
+ *
+ */
+static int irctl_close(struct inode *inode, struct file *file)
+{
+	struct irctl *ir = &irctls[MINOR(inode->i_rdev)];
+
+	dprintk(LOGHEAD "close called\n", ir->p.name, ir->p.minor);
+
+	/* if the plugin has a close function use it instead */
+	if (ir->p.fops && ir->p.fops->release)
+		return ir->p.fops->release(inode, file);
+
+	if (mutex_lock_interruptible(&plugin_lock))
+		return -ERESTARTSYS;
+
+	--ir->open;
+	if (ir->attached) {
+		ir->p.set_use_dec(ir->p.data);
+		module_put(ir->p.owner);
+	} else {
+		cleanup(ir);
+	}
+
+	mutex_unlock(&plugin_lock);
+
+	return SUCCESS;
+}
+
+/*
+ *
+ */
+static unsigned int irctl_poll(struct file *file, poll_table *wait)
+{
+	struct irctl *ir = &irctls[MINOR(file->f_dentry->d_inode->i_rdev)];
+	unsigned int ret;
+
+	dprintk(LOGHEAD "poll called\n", ir->p.name, ir->p.minor);
+
+	/* if the plugin has a poll function use it instead */
+	if (ir->p.fops && ir->p.fops->poll)
+		return ir->p.fops->poll(file, wait);
+
+	mutex_lock(&ir->buffer_lock);
+	if (!ir->attached) {
+		mutex_unlock(&ir->buffer_lock);
+		return POLLERR;
+	}
+
+	poll_wait(file, &ir->buf->wait_poll, wait);
+
+	dprintk(LOGHEAD "poll result = %s\n",
+		ir->p.name, ir->p.minor,
+		lirc_buffer_empty(ir->buf) ? "0" : "POLLIN|POLLRDNORM");
+
+	ret = lirc_buffer_empty(ir->buf) ? 0 : (POLLIN|POLLRDNORM);
+
+	mutex_unlock(&ir->buffer_lock);
+	return ret;
+}
+
+/*
+ *
+ */
+static int irctl_ioctl(struct inode *inode, struct file *file,
+		       unsigned int cmd, unsigned long arg)
+{
+	unsigned long mode;
+	int result;
+	struct irctl *ir = &irctls[MINOR(inode->i_rdev)];
+
+	dprintk(LOGHEAD "ioctl called (0x%x)\n",
+		ir->p.name, ir->p.minor, cmd);
+
+	/* if the plugin has a ioctl function use it instead */
+	if (ir->p.fops && ir->p.fops->ioctl)
+		return ir->p.fops->ioctl(inode, file, cmd, arg);
+
+	if (ir->p.minor == NOPLUG || !ir->attached) {
+		dprintk(LOGHEAD "ioctl result = -ENODEV\n",
+			ir->p.name, ir->p.minor);
+		return -ENODEV;
+	}
+
+	/* Give the plugin a chance to handle the ioctl */
+	if (ir->p.ioctl) {
+		result = ir->p.ioctl(inode, file, cmd, arg);
+		if (result != -ENOIOCTLCMD)
+			return result;
+	}
+	/* The plugin can't handle cmd */
+	result = SUCCESS;
+
+	switch (cmd) {
+	case LIRC_GET_FEATURES:
+		result = put_user(ir->p.features, (unsigned long *)arg);
+		break;
+	case LIRC_GET_REC_MODE:
+		if (!(ir->p.features&LIRC_CAN_REC_MASK))
+			return -ENOSYS;
+
+		result = put_user(LIRC_REC2MODE
+				  (ir->p.features&LIRC_CAN_REC_MASK),
+				  (unsigned long *)arg);
+		break;
+	case LIRC_SET_REC_MODE:
+		if (!(ir->p.features&LIRC_CAN_REC_MASK))
+			return -ENOSYS;
+
+		result = get_user(mode, (unsigned long *)arg);
+		if (!result && !(LIRC_MODE2REC(mode) & ir->p.features))
+			result = -EINVAL;
+		/*
+		 * FIXME: We should actually set the mode somehow but
+		 * for now, lirc_serial doesn't support mode changing either
+		 */
+		break;
+	case LIRC_GET_LENGTH:
+		result = put_user((unsigned long)ir->p.code_length,
+				  (unsigned long *)arg);
+		break;
+	default:
+		result = -ENOIOCTLCMD;
+	}
+
+	dprintk(LOGHEAD "ioctl result = %d\n",
+		ir->p.name, ir->p.minor, result);
+
+	return result;
+}
+
+/*
+ *
+ */
+static ssize_t irctl_read(struct file *file,
+			  char *buffer,
+			  size_t length,
+			  loff_t *ppos)
+{
+	struct irctl *ir = &irctls[MINOR(file->f_dentry->d_inode->i_rdev)];
+	unsigned char buf[ir->buf->chunk_size];
+	int ret = 0, written = 0;
+	DECLARE_WAITQUEUE(wait, current);
+
+	dprintk(LOGHEAD "read called\n", ir->p.name, ir->p.minor);
+
+	/* if the plugin has a specific read function use it instead */
+	if (ir->p.fops && ir->p.fops->read)
+		return ir->p.fops->read(file, buffer, length, ppos);
+
+	if (mutex_lock_interruptible(&ir->buffer_lock))
+		return -ERESTARTSYS;
+	if (!ir->attached) {
+		mutex_unlock(&ir->buffer_lock);
+		return -ENODEV;
+	}
+
+	if (length % ir->buf->chunk_size) {
+		dprintk(LOGHEAD "read result = -EINVAL\n",
+			ir->p.name, ir->p.minor);
+		mutex_unlock(&ir->buffer_lock);
+		return -EINVAL;
+	}
+
+	/*
+	 * we add ourselves to the task queue before buffer check
+	 * to avoid losing scan code (in case when queue is awaken somewhere
+	 * beetwen while condition checking and scheduling)
+	 */
+	add_wait_queue(&ir->buf->wait_poll, &wait);
+	set_current_state(TASK_INTERRUPTIBLE);
+
+	/*
+	 * while we did't provide 'length' bytes, device is opened in blocking
+	 * mode and 'copy_to_user' is happy, wait for data.
+	 */
+	while (written < length && ret == 0) {
+		if (lirc_buffer_empty(ir->buf)) {
+			/* According to the read(2) man page, 'written' can be
+			 * returned as less than 'length', instead of blocking
+			 * again, returning -EWOULDBLOCK, or returning
+			 * -ERESTARTSYS */
+			if (written)
+				break;
+			if (file->f_flags & O_NONBLOCK) {
+				ret = -EWOULDBLOCK;
+				break;
+			}
+			if (signal_pending(current)) {
+				ret = -ERESTARTSYS;
+				break;
+			}
+			schedule();
+			set_current_state(TASK_INTERRUPTIBLE);
+			if (!ir->attached) {
+				ret = -ENODEV;
+				break;
+			}
+		} else {
+			lirc_buffer_read_1(ir->buf, buf);
+			ret = copy_to_user((void *)buffer+written, buf,
+					   ir->buf->chunk_size);
+			written += ir->buf->chunk_size;
+		}
+	}
+
+	remove_wait_queue(&ir->buf->wait_poll, &wait);
+	set_current_state(TASK_RUNNING);
+	mutex_unlock(&ir->buffer_lock);
+
+	dprintk(LOGHEAD "read result = %s (%d)\n",
+		ir->p.name, ir->p.minor, ret ? "-EFAULT" : "OK", ret);
+
+	return ret ? ret : written;
+}
+
+
+void *lirc_get_pdata(struct file *file)
+{
+	void *data = NULL;
+
+	if (file && file->f_dentry && file->f_dentry->d_inode &&
+	    file->f_dentry->d_inode->i_rdev) {
+		struct irctl *ir;
+		ir = &irctls[MINOR(file->f_dentry->d_inode->i_rdev)];
+		data = ir->p.data;
+	}
+
+	return data;
+}
+EXPORT_SYMBOL(lirc_get_pdata);
+
+
+static ssize_t irctl_write(struct file *file, const char *buffer,
+			   size_t length, loff_t *ppos)
+{
+	struct irctl *ir = &irctls[MINOR(file->f_dentry->d_inode->i_rdev)];
+
+	dprintk(LOGHEAD "write called\n", ir->p.name, ir->p.minor);
+
+	/* if the plugin has a specific read function use it instead */
+	if (ir->p.fops && ir->p.fops->write)
+		return ir->p.fops->write(file, buffer, length, ppos);
+
+	if (!ir->attached)
+		return -ENODEV;
+
+	return -EINVAL;
+}
+
+
+static struct file_operations fops = {
+	.read		= irctl_read,
+	.write		= irctl_write,
+	.poll		= irctl_poll,
+	.ioctl		= irctl_ioctl,
+	.open		= irctl_open,
+	.release	= irctl_close
+};
+
+
+static int lirc_dev_init(void)
+{
+	int i;
+
+	for (i = 0; i < MAX_IRCTL_DEVICES; ++i)
+		init_irctl(&irctls[i]);
+
+	if (register_chrdev(IRCTL_DEV_MAJOR, IRCTL_DEV_NAME, &fops)) {
+		printk(KERN_ERR "lirc_dev: register_chrdev failed\n");
+		goto out;
+	}
+
+	lirc_class = class_create(THIS_MODULE, "lirc");
+	if (IS_ERR(lirc_class)) {
+		printk(KERN_ERR "lirc_dev: class_create failed\n");
+		goto out_unregister;
+	}
+
+	printk(KERN_INFO "lirc_dev: IR Remote Control driver registered, "
+	       "major %d \n", IRCTL_DEV_MAJOR);
+
+	return SUCCESS;
+
+out_unregister:
+	/* unregister_chrdev returns void now */
+	unregister_chrdev(IRCTL_DEV_MAJOR, IRCTL_DEV_NAME);
+out:
+	return -1;
+}
+
+/* ---------------------------------------------------------------------- */
+
+#ifdef MODULE
+
+/*
+ *
+ */
+int init_module(void)
+{
+	return lirc_dev_init();
+}
+
+/*
+ *
+ */
+void cleanup_module(void)
+{
+	/* unregister_chrdev returns void now */
+	unregister_chrdev(IRCTL_DEV_MAJOR, IRCTL_DEV_NAME);
+	class_destroy(lirc_class);
+	dprintk("lirc_dev: module unloaded\n");
+}
+
+MODULE_DESCRIPTION("LIRC base driver module");
+MODULE_AUTHOR("Artur Lipowski");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_CHARDEV_MAJOR(IRCTL_DEV_MAJOR);
+
+module_param(debug, bool, 0644);
+MODULE_PARM_DESC(debug, "Enable debugging messages");
+
+#else /* not a MODULE */
+subsys_initcall(lirc_dev_init);
+
+#endif /* MODULE */
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/input/lirc/lirc_dev.h b/drivers/input/lirc/lirc_dev.h
new file mode 100644
index 0000000..920dd43
--- /dev/null
+++ b/drivers/input/lirc/lirc_dev.h
@@ -0,0 +1,262 @@
+/*
+ * LIRC base driver
+ *
+ * (L) by Artur Lipowski <alipowski@interia.pl>
+ *        This code is licensed under GNU GPL
+ *
+ */
+
+#ifndef _LINUX_LIRC_DEV_H
+#define _LINUX_LIRC_DEV_H
+
+#define MAX_IRCTL_DEVICES 4
+#define BUFLEN            16
+
+/* #define LIRC_BUFF_POWER_OF_2 */
+#ifdef LIRC_BUFF_POWER_OF_2
+#define mod(n, div) ((n) & ((div) - 1))
+#else
+#define mod(n, div) ((n) % (div))
+#endif
+#include <linux/slab.h>
+#include <linux/fs.h>
+
+struct lirc_buffer {
+	wait_queue_head_t wait_poll;
+	spinlock_t lock;
+
+	unsigned char *data;
+	unsigned int chunk_size;
+	unsigned int size; /* in chunks */
+	unsigned int fill; /* in chunks */
+	int head, tail;    /* in chunks */
+	/* Using chunks instead of bytes pretends to simplify boundary checking
+	 * And should allow for some performance fine tunning later */
+};
+static inline void _lirc_buffer_clear(struct lirc_buffer *buf)
+{
+	buf->head = 0;
+	buf->tail = 0;
+	buf->fill = 0;
+}
+static inline int lirc_buffer_init(struct lirc_buffer *buf,
+				    unsigned int chunk_size,
+				    unsigned int size)
+{
+	/* Adjusting size to the next power of 2 would allow for
+	 * inconditional LIRC_BUFF_POWER_OF_2 optimization */
+	init_waitqueue_head(&buf->wait_poll);
+	spin_lock_init(&buf->lock);
+	_lirc_buffer_clear(buf);
+	buf->chunk_size = chunk_size;
+	buf->size = size;
+	buf->data = kmalloc(size*chunk_size, GFP_KERNEL);
+	if (buf->data == NULL)
+		return -1;
+	memset(buf->data, 0, size*chunk_size);
+	return 0;
+}
+static inline void lirc_buffer_free(struct lirc_buffer *buf)
+{
+	kfree(buf->data);
+	buf->data = NULL;
+	buf->head = 0;
+	buf->tail = 0;
+	buf->fill = 0;
+	buf->chunk_size = 0;
+	buf->size = 0;
+}
+static inline int  lirc_buffer_full(struct lirc_buffer *buf)
+{
+	return buf->fill >= buf->size;
+}
+static inline int  lirc_buffer_empty(struct lirc_buffer *buf)
+{
+	return !(buf->fill);
+}
+static inline int  lirc_buffer_available(struct lirc_buffer *buf)
+{
+    return buf->size - buf->fill;
+}
+static inline void lirc_buffer_lock(struct lirc_buffer *buf,
+				    unsigned long *flags)
+{
+	spin_lock_irqsave(&buf->lock, *flags);
+}
+static inline void lirc_buffer_unlock(struct lirc_buffer *buf,
+				      unsigned long *flags)
+{
+	spin_unlock_irqrestore(&buf->lock, *flags);
+}
+static inline void lirc_buffer_clear(struct lirc_buffer *buf)
+{
+	unsigned long flags;
+	lirc_buffer_lock(buf, &flags);
+	_lirc_buffer_clear(buf);
+	lirc_buffer_unlock(buf, &flags);
+}
+static inline void _lirc_buffer_remove_1(struct lirc_buffer *buf)
+{
+	buf->head = mod(buf->head+1, buf->size);
+	buf->fill -= 1;
+}
+static inline void lirc_buffer_remove_1(struct lirc_buffer *buf)
+{
+	unsigned long flags;
+	lirc_buffer_lock(buf, &flags);
+	_lirc_buffer_remove_1(buf);
+	lirc_buffer_unlock(buf, &flags);
+}
+static inline void _lirc_buffer_read_1(struct lirc_buffer *buf,
+				     unsigned char *dest)
+{
+	memcpy(dest, &buf->data[buf->head*buf->chunk_size], buf->chunk_size);
+	buf->head = mod(buf->head+1, buf->size);
+	buf->fill -= 1;
+}
+static inline void lirc_buffer_read_1(struct lirc_buffer *buf,
+				      unsigned char *dest)
+{
+	unsigned long flags;
+	lirc_buffer_lock(buf, &flags);
+	_lirc_buffer_read_1(buf, dest);
+	lirc_buffer_unlock(buf, &flags);
+}
+static inline void _lirc_buffer_write_1(struct lirc_buffer *buf,
+				      unsigned char *orig)
+{
+	memcpy(&buf->data[buf->tail*buf->chunk_size], orig, buf->chunk_size);
+	buf->tail = mod(buf->tail+1, buf->size);
+	buf->fill++;
+}
+static inline void lirc_buffer_write_1(struct lirc_buffer *buf,
+				       unsigned char *orig)
+{
+	unsigned long flags;
+	lirc_buffer_lock(buf, &flags);
+	_lirc_buffer_write_1(buf, orig);
+	lirc_buffer_unlock(buf, &flags);
+}
+static inline void _lirc_buffer_write_n(struct lirc_buffer *buf,
+					unsigned char *orig, int count)
+{
+	memcpy(&buf->data[buf->tail * buf->chunk_size], orig,
+	       count * buf->chunk_size);
+	buf->tail = mod(buf->tail + count, buf->size);
+	buf->fill += count;
+}
+static inline void lirc_buffer_write_n(struct lirc_buffer *buf,
+				       unsigned char *orig, int count)
+{
+	unsigned long flags;
+	int space1;
+
+	lirc_buffer_lock(buf, &flags);
+	if (buf->head > buf->tail)
+		space1 = buf->head - buf->tail;
+	else
+		space1 = buf->size - buf->tail;
+
+	if (count > space1) {
+		_lirc_buffer_write_n(buf, orig, space1);
+		_lirc_buffer_write_n(buf, orig+(space1*buf->chunk_size),
+				     count-space1);
+	} else {
+		_lirc_buffer_write_n(buf, orig, count);
+	}
+	lirc_buffer_unlock(buf, &flags);
+}
+
+struct lirc_plugin {
+	char name[40];
+	int minor;
+	int code_length;
+	int sample_rate;
+	unsigned long features;
+	void *data;
+	int (*add_to_buf) (void *data, struct lirc_buffer *buf);
+	wait_queue_head_t* (*get_queue) (void *data);
+	struct lirc_buffer *rbuf;
+	int (*set_use_inc) (void *data);
+	void (*set_use_dec) (void *data);
+	int (*ioctl) (struct inode *, struct file *, unsigned int,
+		      unsigned long);
+	struct file_operations *fops;
+	struct device *dev;
+	struct module *owner;
+};
+/* name:
+ * this string will be used for logs
+ *
+ * minor:
+ * indicates minor device (/dev/lirc) number for registered plugin
+ * if caller fills it with negative value, then the first free minor
+ * number will be used (if available)
+ *
+ * code_length:
+ * length of the remote control key code expressed in bits
+ *
+ * sample_rate:
+ * sample_rate equal to 0 means that no polling will be performed and
+ * add_to_buf will be triggered by external events (through task queue
+ * returned by get_queue)
+ *
+ * data:
+ * it may point to any plugin data and this pointer will be passed to
+ * all callback functions
+ *
+ * add_to_buf:
+ * add_to_buf will be called after specified period of the time or
+ * triggered by the external event, this behavior depends on value of
+ * the sample_rate this function will be called in user context. This
+ * routine should return 0 if data was added to the buffer and
+ * -ENODATA if none was available. This should add some number of bits
+ * evenly divisible by code_length to the buffer
+ *
+ * get_queue:
+ * this callback should return a pointer to the task queue which will
+ * be used for external event waiting
+ *
+ * rbuf:
+ * if not NULL, it will be used as a read buffer, you will have to
+ * write to the buffer by other means, like irq's (see also
+ * lirc_serial.c).
+ *
+ * set_use_inc:
+ * set_use_inc will be called after device is opened
+ *
+ * set_use_dec:
+ * set_use_dec will be called after device is closed
+ *
+ * ioctl:
+ * Some ioctl's can be directly handled by lirc_dev but will be
+ * forwared here if not NULL and only handled if it returns
+ * -ENOIOCTLCMD (see also lirc_serial.c).
+ *
+ * fops:
+ * file_operations for drivers which don't fit the current plugin model.
+ *
+ * owner:
+ * the module owning this struct
+ *
+ */
+
+
+/* following functions can be called ONLY from user context
+ *
+ * returns negative value on error or minor number
+ * of the registered device if success
+ * contens of the structure pointed by p is copied
+ */
+extern int lirc_register_plugin(struct lirc_plugin *p);
+
+/* returns negative value on error or 0 if success
+*/
+extern int lirc_unregister_plugin(int minor);
+
+/* Returns the private data stored in the lirc_plugin
+ * associated with the given device file pointer.
+ */
+void *lirc_get_pdata(struct file *file);
+
+#endif
diff --git a/MAINTAINERS b/MAINTAINERS
index af27945..f3f7f03 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2580,6 +2580,15 @@ W:	http://www.pasemi.com/
 L:	linuxppc-dev@ozlabs.org
 S:	Supported
 
+LINUX INFRARED REMOTE CONTROL DRIVERS (LIRC)
+P:	Jarod Wilson
+M:	jarod@redhat.com
+P:	Christoph Bartelmus
+M:	lirc@bartelmus.de
+W:	http://www.lirc.org/
+L:	lirc-list@lists.sourceforge.net
+S:	Maintained
+
 LLC (802.2)
 P:	Arnaldo Carvalho de Melo
 M:	acme@ghostprotocols.net
-- 
1.6.0.1


^ permalink raw reply related	[flat|nested] 111+ messages in thread

* [PATCH 02/18] lirc serial port receiver/transmitter device driver
  2008-09-09  4:05 ` [PATCH 01/18] lirc core device driver infrastructure Jarod Wilson
@ 2008-09-09  4:05   ` Jarod Wilson
  2008-09-09  4:05     ` [PATCH 03/18] lirc driver for 1st-gen Media Center Ed. USB IR transceivers Jarod Wilson
  2008-09-09 16:14     ` [PATCH 02/18] lirc serial port receiver/transmitter device driver Jonathan Corbet
  2008-09-09  7:40   ` [PATCH 01/18] lirc core device driver infrastructure Sebastian Siewior
                     ` (4 subsequent siblings)
  5 siblings, 2 replies; 111+ messages in thread
From: Jarod Wilson @ 2008-09-09  4:05 UTC (permalink / raw)
  To: linux-kernel
  Cc: Jarod Wilson, Jarod Wilson, Janne Grunau, Christoph Bartelmus

Signed-off-by: Jarod Wilson <jarod@redhat.com>
Signed-off-by: Janne Grunau <j@jannau.net>
CC: Christoph Bartelmus <lirc@bartelmus.de>
---
 drivers/input/lirc/Kconfig       |    7 +
 drivers/input/lirc/Makefile      |    1 +
 drivers/input/lirc/lirc_serial.c | 1312 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 1320 insertions(+), 0 deletions(-)
 create mode 100644 drivers/input/lirc/lirc_serial.c

diff --git a/drivers/input/lirc/Kconfig b/drivers/input/lirc/Kconfig
index 2a04d9b..73403b9 100644
--- a/drivers/input/lirc/Kconfig
+++ b/drivers/input/lirc/Kconfig
@@ -18,4 +18,11 @@ config LIRC_DEV
 	help
 	  LIRC device loadable module support, required for most LIRC drivers
 
+config LIRC_SERIAL
+	tristate "Homebrew Serial Port Receiver"
+	default n
+	depends on LIRC_DEV
+	help
+	  Driver for Homebrew Serial Port Receivers
+
 endif
diff --git a/drivers/input/lirc/Makefile b/drivers/input/lirc/Makefile
index cdb4c45..7d76128 100644
--- a/drivers/input/lirc/Makefile
+++ b/drivers/input/lirc/Makefile
@@ -6,3 +6,4 @@
 EXTRA_CFLAGS =-DIRCTL_DEV_MAJOR=61 -DLIRC_SERIAL_TRANSMITTER -I$(src)
 
 obj-$(CONFIG_LIRC_DEV)		+= lirc_dev.o
+obj-$(CONFIG_LIRC_SERIAL)	+= lirc_serial.o
diff --git a/drivers/input/lirc/lirc_serial.c b/drivers/input/lirc/lirc_serial.c
new file mode 100644
index 0000000..465edd9
--- /dev/null
+++ b/drivers/input/lirc/lirc_serial.c
@@ -0,0 +1,1312 @@
+/****************************************************************************
+ ** lirc_serial.c ***********************************************************
+ ****************************************************************************
+ *
+ * lirc_serial - Device driver that records pulse- and pause-lengths
+ *	       (space-lengths) between DDCD event on a serial port.
+ *
+ * Copyright (C) 1996,97 Ralph Metzler <rjkm@thp.uni-koeln.de>
+ * Copyright (C) 1998 Trent Piepho <xyzzy@u.washington.edu>
+ * Copyright (C) 1998 Ben Pfaff <blp@gnu.org>
+ * Copyright (C) 1999 Christoph Bartelmus <lirc@bartelmus.de>
+ * Copyright (C) 2007 Andrei Tanas <andrei@tanas.ca> (suspend/resume support)
+ *  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
+ *
+ */
+
+/* Steve's changes to improve transmission fidelity:
+     - for systems with the rdtsc instruction and the clock counter, a
+       send_pule that times the pulses directly using the counter.
+       This means that the LIRC_SERIAL_TRANSMITTER_LATENCY fudge is
+       not needed. Measurement shows very stable waveform, even where
+       PCI activity slows the access to the UART, which trips up other
+       versions.
+     - For other system, non-integer-microsecond pulse/space lengths,
+       done using fixed point binary. So, much more accurate carrier
+       frequency.
+     - fine tuned transmitter latency, taking advantage of fractional
+       microseconds in previous change
+     - Fixed bug in the way transmitter latency was accounted for by
+       tuning the pulse lengths down - the send_pulse routine ignored
+       this overhead as it timed the overall pulse length - so the
+       pulse frequency was right but overall pulse length was too
+       long. Fixed by accounting for latency on each pulse/space
+       iteration.
+
+   Steve Davies <steve@daviesfam.org>  July 2001
+*/
+
+#include <linux/version.h>
+
+#include <linux/autoconf.h>
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/serial_reg.h>
+#include <linux/time.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+#include <linux/mm.h>
+#include <linux/delay.h>
+#include <linux/poll.h>
+#include <linux/platform_device.h>
+
+#include <asm/system.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/fcntl.h>
+
+#if defined(LIRC_SERIAL_NSLU2)
+#include <asm/hardware.h>
+/* From Intel IXP42X Developer's Manual (#252480-005): */
+/* ftp://download.intel.com/design/network/manuals/25248005.pdf */
+#define UART_IE_IXP42X_UUE   0x40 /* IXP42X UART Unit enable */
+#define UART_IE_IXP42X_RTOIE 0x10 /* IXP42X Receiver Data Timeout int.enable */
+#ifndef NSLU2_LED_GRN_GPIO
+/* added in 2.6.22 */
+#define NSLU2_LED_GRN_GPIO NSLU2_LED_GRN
+#endif
+#endif
+
+#include "lirc.h"
+#include "lirc_dev.h"
+
+#define LIRC_DRIVER_NAME "lirc_serial"
+
+struct lirc_serial {
+	int signal_pin;
+	int signal_pin_change;
+	int on;
+	int off;
+	long (*send_pulse)(unsigned long length);
+	void (*send_space)(long length);
+	int features;
+};
+
+#define LIRC_HOMEBREW	0
+#define LIRC_IRDEO	   1
+#define LIRC_IRDEO_REMOTE    2
+#define LIRC_ANIMAX	  3
+#define LIRC_IGOR	    4
+#define LIRC_NSLU2	   5
+
+#ifdef LIRC_SERIAL_IRDEO
+static int type = LIRC_IRDEO;
+#elif defined(LIRC_SERIAL_IRDEO_REMOTE)
+static int type = LIRC_IRDEO_REMOTE;
+#elif defined(LIRC_SERIAL_ANIMAX)
+static int type = LIRC_ANIMAX;
+#elif defined(LIRC_SERIAL_IGOR)
+static int type = LIRC_IGOR;
+#elif defined(LIRC_SERIAL_NSLU2)
+static int type = LIRC_NSLU2;
+#else
+static int type = LIRC_HOMEBREW;
+#endif
+
+/* Set defaults for NSLU2 */
+#if defined(LIRC_SERIAL_NSLU2)
+#ifndef LIRC_IRQ
+#define LIRC_IRQ IRQ_IXP4XX_UART2
+#endif
+#ifndef LIRC_PORT
+#define LIRC_PORT (IXP4XX_UART2_BASE_VIRT + REG_OFFSET)
+#endif
+#ifndef LIRC_IOMMAP
+#define LIRC_IOMMAP IXP4XX_UART2_BASE_PHYS
+#endif
+#ifndef LIRC_IOSHIFT
+#define LIRC_IOSHIFT 2
+#endif
+#ifndef LIRC_ALLOW_MMAPPED_IO
+#define LIRC_ALLOW_MMAPPED_IO
+#endif
+#endif
+
+#if defined(LIRC_ALLOW_MMAPPED_IO)
+#ifndef LIRC_IOMMAP
+#define LIRC_IOMMAP 0
+#endif
+#ifndef LIRC_IOSHIFT
+#define LIRC_IOSHIFT 0
+#endif
+static int iommap = LIRC_IOMMAP;
+static int ioshift = LIRC_IOSHIFT;
+#endif
+
+#ifdef LIRC_SERIAL_SOFTCARRIER
+static int softcarrier = 1;
+#else
+static int softcarrier;
+#endif
+
+static int share_irq;
+static int debug;
+
+#define dprintk(fmt, args...)					\
+	do {							\
+		if (debug)					\
+			printk(KERN_DEBUG LIRC_DRIVER_NAME ": "	\
+			       fmt, ## args);			\
+	} while (0)
+
+/* forward declarations */
+static long send_pulse_irdeo(unsigned long length);
+static long send_pulse_homebrew(unsigned long length);
+static void send_space_irdeo(long length);
+static void send_space_homebrew(long length);
+
+static struct lirc_serial hardware[] = {
+	/* home-brew receiver/transmitter */
+	{
+		UART_MSR_DCD,
+		UART_MSR_DDCD,
+		UART_MCR_RTS|UART_MCR_OUT2|UART_MCR_DTR,
+		UART_MCR_RTS|UART_MCR_OUT2,
+		send_pulse_homebrew,
+		send_space_homebrew,
+		(
+#ifdef LIRC_SERIAL_TRANSMITTER
+		 LIRC_CAN_SET_SEND_DUTY_CYCLE|
+		 LIRC_CAN_SET_SEND_CARRIER|
+		 LIRC_CAN_SEND_PULSE|
+#endif
+		 LIRC_CAN_REC_MODE2)
+	},
+
+	/* IRdeo classic */
+	{
+		UART_MSR_DSR,
+		UART_MSR_DDSR,
+		UART_MCR_OUT2,
+		UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2,
+		send_pulse_irdeo,
+		send_space_irdeo,
+		(LIRC_CAN_SET_SEND_DUTY_CYCLE|
+		 LIRC_CAN_SEND_PULSE|
+		 LIRC_CAN_REC_MODE2)
+	},
+
+	/* IRdeo remote */
+	{
+		UART_MSR_DSR,
+		UART_MSR_DDSR,
+		UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2,
+		UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2,
+		send_pulse_irdeo,
+		send_space_irdeo,
+		(LIRC_CAN_SET_SEND_DUTY_CYCLE|
+		 LIRC_CAN_SEND_PULSE|
+		 LIRC_CAN_REC_MODE2)
+	},
+
+	/* AnimaX */
+	{
+		UART_MSR_DCD,
+		UART_MSR_DDCD,
+		0,
+		UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2,
+		NULL,
+		NULL,
+		LIRC_CAN_REC_MODE2
+	},
+
+	/* home-brew receiver/transmitter (Igor Cesko's variation) */
+	{
+		UART_MSR_DSR,
+		UART_MSR_DDSR,
+		UART_MCR_RTS|UART_MCR_OUT2|UART_MCR_DTR,
+		UART_MCR_RTS|UART_MCR_OUT2,
+		send_pulse_homebrew,
+		send_space_homebrew,
+		(
+#ifdef LIRC_SERIAL_TRANSMITTER
+		 LIRC_CAN_SET_SEND_DUTY_CYCLE|
+		 LIRC_CAN_SET_SEND_CARRIER|
+		 LIRC_CAN_SEND_PULSE|
+#endif
+		 LIRC_CAN_REC_MODE2)
+	},
+
+#if defined(LIRC_SERIAL_NSLU2)
+	/* Modified Linksys Network Storage Link USB 2.0 (NSLU2):
+	   We receive on CTS of the 2nd serial port (R142,LHS), we
+	   transmit with a IR diode between GPIO[1] (green status LED),
+	   and ground (Matthias Goebl <matthias.goebl@goebl.net>).
+	   See also http://www.nslu2-linux.org for this device */
+	{
+		UART_MSR_CTS,
+		UART_MSR_DCTS,
+		UART_MCR_RTS|UART_MCR_OUT2|UART_MCR_DTR,
+		UART_MCR_RTS|UART_MCR_OUT2,
+		send_pulse_homebrew,
+		send_space_homebrew,
+		(
+#ifdef LIRC_SERIAL_TRANSMITTER
+		 LIRC_CAN_SET_SEND_DUTY_CYCLE|
+		 LIRC_CAN_SET_SEND_CARRIER|
+		 LIRC_CAN_SEND_PULSE|
+#endif
+		 LIRC_CAN_REC_MODE2)
+	},
+#endif
+
+};
+
+#define RS_ISR_PASS_LIMIT 256
+
+/* A long pulse code from a remote might take upto 300 bytes.  The
+   daemon should read the bytes as soon as they are generated, so take
+   the number of keys you think you can push before the daemon runs
+   and multiply by 300.  The driver will warn you if you overrun this
+   buffer.  If you have a slow computer or non-busmastering IDE disks,
+   maybe you will need to increase this.  */
+
+/* This MUST be a power of two!  It has to be larger than 1 as well. */
+
+#define RBUF_LEN 256
+#define WBUF_LEN 256
+
+static int sense = -1;	/* -1 = auto, 0 = active high, 1 = active low */
+static int txsense;     /* 0 = active high, 1 = active low */
+
+#ifndef LIRC_IRQ
+#define LIRC_IRQ 4
+#endif
+#ifndef LIRC_PORT
+#define LIRC_PORT 0x3f8
+#endif
+
+static int io = LIRC_PORT;
+static int irq = LIRC_IRQ;
+
+static struct timeval lasttv = {0, 0};
+
+static struct lirc_buffer rbuf;
+
+static int wbuf[WBUF_LEN];
+
+static unsigned int freq = 38000;
+static unsigned int duty_cycle = 50;
+
+/* Initialized in init_timing_params() */
+static unsigned long period;
+static unsigned long pulse_width;
+static unsigned long space_width;
+
+#if defined(__i386__)
+/*
+  From:
+  Linux I/O port programming mini-HOWTO
+  Author: Riku Saikkonen <Riku.Saikkonen@hut.fi>
+  v, 28 December 1997
+
+  [...]
+  Actually, a port I/O instruction on most ports in the 0-0x3ff range
+  takes almost exactly 1 microsecond, so if you're, for example, using
+  the parallel port directly, just do additional inb()s from that port
+  to delay.
+  [...]
+*/
+/* transmitter latency 1.5625us 0x1.90 - this figure arrived at from
+ * comment above plus trimming to match actual measured frequency.
+ * This will be sensitive to cpu speed, though hopefully most of the 1.5us
+ * is spent in the uart access.  Still - for reference test machine was a
+ * 1.13GHz Athlon system - Steve
+ */
+
+/* changed from 400 to 450 as this works better on slower machines;
+   faster machines will use the rdtsc code anyway */
+
+#define LIRC_SERIAL_TRANSMITTER_LATENCY 450
+
+#else
+
+/* does anybody have information on other platforms ? */
+/* 256 = 1<<8 */
+#define LIRC_SERIAL_TRANSMITTER_LATENCY 256
+
+#endif  /* __i386__ */
+
+static inline unsigned int sinp(int offset)
+{
+#if defined(LIRC_ALLOW_MMAPPED_IO)
+	if (iommap != 0) {
+		/* the register is memory-mapped */
+		offset <<= ioshift;
+		return readb(io + offset);
+	}
+#endif
+	return inb(io + offset);
+}
+
+static inline void soutp(int offset, int value)
+{
+#if defined(LIRC_ALLOW_MMAPPED_IO)
+	if (iommap != 0) {
+		/* the register is memory-mapped */
+		offset <<= ioshift;
+		writeb(value, io + offset);
+	}
+#endif
+	outb(value, io + offset);
+}
+
+static inline void on(void)
+{
+#if defined(LIRC_SERIAL_NSLU2)
+	/* On NSLU2, we put the transmit diode between the output of the green
+	   status LED and ground */
+	if (type == LIRC_NSLU2) {
+		gpio_line_set(NSLU2_LED_GRN_GPIO, IXP4XX_GPIO_LOW);
+		return;
+	}
+#endif
+	if (txsense)
+		soutp(UART_MCR, hardware[type].off);
+	else
+		soutp(UART_MCR, hardware[type].on);
+}
+
+static inline void off(void)
+{
+#if defined(LIRC_SERIAL_NSLU2)
+	if (type == LIRC_NSLU2) {
+		gpio_line_set(NSLU2_LED_GRN_GPIO, IXP4XX_GPIO_HIGH);
+		return;
+	}
+#endif
+	if (txsense)
+		soutp(UART_MCR, hardware[type].on);
+	else
+		soutp(UART_MCR, hardware[type].off);
+}
+
+#ifndef MAX_UDELAY_MS
+#define MAX_UDELAY_US 5000
+#else
+#define MAX_UDELAY_US (MAX_UDELAY_MS*1000)
+#endif
+
+static inline void safe_udelay(unsigned long usecs)
+{
+	while (usecs > MAX_UDELAY_US) {
+		udelay(MAX_UDELAY_US);
+		usecs -= MAX_UDELAY_US;
+	}
+	udelay(usecs);
+}
+
+#ifdef USE_RDTSC
+/* This is an overflow/precision juggle, complicated in that we can't
+   do long long divide in the kernel */
+
+/* When we use the rdtsc instruction to measure clocks, we keep the
+ * pulse and space widths as clock cycles.  As this is CPU speed
+ * dependent, the widths must be calculated in init_port and ioctl
+ * time
+ */
+
+/* So send_pulse can quickly convert microseconds to clocks */
+static unsigned long conv_us_to_clocks;
+
+static inline int init_timing_params(unsigned int new_duty_cycle,
+		unsigned int new_freq)
+{
+	unsigned long long loops_per_sec, work;
+
+	duty_cycle = new_duty_cycle;
+	freq = new_freq;
+
+	loops_per_sec = current_cpu_data.loops_per_jiffy;
+	loops_per_sec *= HZ;
+
+	/* How many clocks in a microsecond?, avoiding long long divide */
+	work = loops_per_sec;
+	work *= 4295;  /* 4295 = 2^32 / 1e6 */
+	conv_us_to_clocks = (work>>32);
+
+	/* Carrier period in clocks, approach good up to 32GHz clock,
+	   gets carrier frequency within 8Hz */
+	period = loops_per_sec>>3;
+	period /= (freq>>3);
+
+	/* Derive pulse and space from the period */
+
+	pulse_width = period*duty_cycle/100;
+	space_width = period - pulse_width;
+	dprintk("in init_timing_params, freq=%d, duty_cycle=%d, "
+		"clk/jiffy=%ld, pulse=%ld, space=%ld, "
+		"conv_us_to_clocks=%ld\n",
+		freq, duty_cycle, current_cpu_data.loops_per_jiffy,
+		pulse_width, space_width, conv_us_to_clocks);
+	return 0;
+}
+#else /* ! USE_RDTSC */
+static inline int init_timing_params(unsigned int new_duty_cycle,
+		unsigned int new_freq)
+{
+/* period, pulse/space width are kept with 8 binary places -
+ * IE multiplied by 256. */
+	if (256*1000000L/new_freq*new_duty_cycle/100 <=
+	    LIRC_SERIAL_TRANSMITTER_LATENCY)
+		return -EINVAL;
+	if (256*1000000L/new_freq*(100-new_duty_cycle)/100 <=
+	    LIRC_SERIAL_TRANSMITTER_LATENCY)
+		return -EINVAL;
+	duty_cycle = new_duty_cycle;
+	freq = new_freq;
+	period = 256*1000000L/freq;
+	pulse_width = period*duty_cycle/100;
+	space_width = period-pulse_width;
+	dprintk("in init_timing_params, freq=%d pulse=%ld, "
+		"space=%ld\n", freq, pulse_width, space_width);
+	return 0;
+}
+#endif /* USE_RDTSC */
+
+
+/* return value: space length delta */
+
+static long send_pulse_irdeo(unsigned long length)
+{
+	long rawbits;
+	int i;
+	unsigned char output;
+	unsigned char chunk, shifted;
+
+	/* how many bits have to be sent ? */
+	rawbits = length*1152/10000;
+	if (duty_cycle > 50)
+		chunk = 3;
+	else
+		chunk = 1;
+	for (i = 0, output = 0x7f; rawbits > 0; rawbits -= 3) {
+		shifted = chunk<<(i*3);
+		shifted >>= 1;
+		output &= (~shifted);
+		i++;
+		if (i == 3) {
+			soutp(UART_TX, output);
+			while (!(sinp(UART_LSR) & UART_LSR_THRE))
+				;
+			output = 0x7f;
+			i = 0;
+		}
+	}
+	if (i != 0) {
+		soutp(UART_TX, output);
+		while (!(sinp(UART_LSR) & UART_LSR_TEMT))
+			;
+	}
+
+	if (i == 0)
+		return (-rawbits)*10000/1152;
+	else
+		return (3-i)*3*10000/1152 + (-rawbits)*10000/1152;
+}
+
+#ifdef USE_RDTSC
+/* Version that uses Pentium rdtsc instruction to measure clocks */
+
+/* This version does sub-microsecond timing using rdtsc instruction,
+ * and does away with the fudged LIRC_SERIAL_TRANSMITTER_LATENCY
+ * Implicitly i586 architecture...  - Steve
+ */
+
+static inline long send_pulse_homebrew_softcarrier(unsigned long length)
+{
+	int flag;
+	unsigned long target, start, now;
+
+	/* Get going quick as we can */
+	rdtscl(start); on();
+	/* Convert length from microseconds to clocks */
+	length *= conv_us_to_clocks;
+	/* And loop till time is up - flipping at right intervals */
+	now = start;
+	target = pulse_width;
+	flag = 1;
+	while ((now-start) < length) {
+		/* Delay till flip time */
+		do {
+			rdtscl(now);
+		} while ((now-start) < target);
+
+		/* flip */
+		if (flag) {
+			rdtscl(now); off();
+			target += space_width;
+		} else {
+			rdtscl(now); on();
+			target += pulse_width;
+		}
+		flag = !flag;
+	}
+	rdtscl(now);
+	return ((now-start)-length) / conv_us_to_clocks;
+}
+#else /* ! USE_RDTSC */
+/* Version using udelay() */
+
+/* here we use fixed point arithmetic, with 8
+   fractional bits.  that gets us within 0.1% or so of the right average
+   frequency, albeit with some jitter in pulse length - Steve */
+
+/* To match 8 fractional bits used for pulse/space length */
+
+static inline long send_pulse_homebrew_softcarrier(unsigned long length)
+{
+	int flag;
+	unsigned long actual, target, d;
+	length <<= 8;
+
+	actual = 0; target = 0; flag = 0;
+	while (actual < length) {
+		if (flag) {
+			off();
+			target += space_width;
+		} else {
+			on();
+			target += pulse_width;
+		}
+		d = (target-actual-LIRC_SERIAL_TRANSMITTER_LATENCY+128)>>8;
+		/* Note - we've checked in ioctl that the pulse/space
+		   widths are big enough so that d is > 0 */
+		udelay(d);
+		actual += (d<<8)+LIRC_SERIAL_TRANSMITTER_LATENCY;
+		flag = !flag;
+	}
+	return (actual-length)>>8;
+}
+#endif /* USE_RDTSC */
+
+static long send_pulse_homebrew(unsigned long length)
+{
+	if (length <= 0)
+		return 0;
+
+	if (softcarrier)
+		return send_pulse_homebrew_softcarrier(length);
+	else {
+		on();
+		safe_udelay(length);
+		return 0;
+	}
+}
+
+static void send_space_irdeo(long length)
+{
+	if (length <= 0)
+		return;
+
+	safe_udelay(length);
+}
+
+static void send_space_homebrew(long length)
+{
+	off();
+	if (length <= 0)
+		return;
+	safe_udelay(length);
+}
+
+static inline void rbwrite(int l)
+{
+	if (lirc_buffer_full(&rbuf)) {
+		/* no new signals will be accepted */
+		dprintk("Buffer overrun\n");
+		return;
+	}
+	_lirc_buffer_write_1(&rbuf, (void *)&l);
+}
+
+static inline void frbwrite(int l)
+{
+	/* simple noise filter */
+	static int pulse, space;
+	static unsigned int ptr;
+
+	if (ptr > 0 && (l & PULSE_BIT)) {
+		pulse += l & PULSE_MASK;
+		if (pulse > 250) {
+			rbwrite(space);
+			rbwrite(pulse | PULSE_BIT);
+			ptr = 0;
+			pulse = 0;
+		}
+		return;
+	}
+	if (!(l & PULSE_BIT)) {
+		if (ptr == 0) {
+			if (l > 20000) {
+				space = l;
+				ptr++;
+				return;
+			}
+		} else {
+			if (l > 20000) {
+				space += pulse;
+				if (space > PULSE_MASK)
+					space = PULSE_MASK;
+				space += l;
+				if (space > PULSE_MASK)
+					space = PULSE_MASK;
+				pulse = 0;
+				return;
+			}
+			rbwrite(space);
+			rbwrite(pulse | PULSE_BIT);
+			ptr = 0;
+			pulse = 0;
+		}
+	}
+	rbwrite(l);
+}
+
+static irqreturn_t irq_handler(int i, void *blah)
+{
+	struct timeval tv;
+	int status, counter, dcd;
+	long deltv;
+	int data;
+	static int last_dcd = -1;
+
+	if ((sinp(UART_IIR) & UART_IIR_NO_INT)) {
+		/* not our interrupt */
+		return IRQ_RETVAL(IRQ_NONE);
+	}
+
+	counter = 0;
+	do {
+		counter++;
+		status = sinp(UART_MSR);
+		if (counter > RS_ISR_PASS_LIMIT) {
+			printk(KERN_WARNING LIRC_DRIVER_NAME ": AIEEEE: "
+			       "We're caught!\n");
+			break;
+		}
+		if ((status&hardware[type].signal_pin_change) && sense != -1) {
+			/* get current time */
+			do_gettimeofday(&tv);
+
+			/* New mode, written by Trent Piepho
+			   <xyzzy@u.washington.edu>. */
+
+			/* The old format was not very portable.
+			   We now use an int to pass pulses
+			   and spaces to user space.
+
+			   If PULSE_BIT is set a pulse has been
+			   received, otherwise a space has been
+			   received.  The driver needs to know if your
+			   receiver is active high or active low, or
+			   the space/pulse sense could be
+			   inverted. The bits denoted by PULSE_MASK are
+			   the length in microseconds. Lengths greater
+			   than or equal to 16 seconds are clamped to
+			   PULSE_MASK.  All other bits are unused.
+			   This is a much simpler interface for user
+			   programs, as well as eliminating "out of
+			   phase" errors with space/pulse
+			   autodetection. */
+
+			/* calculate time since last interrupt in
+			   microseconds */
+			dcd = (status & hardware[type].signal_pin) ? 1 : 0;
+
+			if (dcd == last_dcd) {
+				printk(KERN_WARNING LIRC_DRIVER_NAME
+				": ignoring spike: %d %d %lx %lx %lx %lx\n",
+				dcd, sense,
+				tv.tv_sec, lasttv.tv_sec,
+				tv.tv_usec, lasttv.tv_usec);
+				continue;
+			}
+
+			deltv = tv.tv_sec-lasttv.tv_sec;
+			if (tv.tv_sec < lasttv.tv_sec ||
+			    (tv.tv_sec == lasttv.tv_sec &&
+			     tv.tv_usec < lasttv.tv_usec)) {
+				printk(KERN_WARNING LIRC_DRIVER_NAME
+				       ": AIEEEE: your clock just jumped "
+				       "backwards\n");
+				printk(KERN_WARNING LIRC_DRIVER_NAME
+				       ": %d %d %lx %lx %lx %lx\n",
+				       dcd, sense,
+				       tv.tv_sec, lasttv.tv_sec,
+				       tv.tv_usec, lasttv.tv_usec);
+				data = PULSE_MASK;
+			} else if (deltv > 15) {
+				data = PULSE_MASK; /* really long time */
+				if (!(dcd^sense)) {
+					/* sanity check */
+					printk(KERN_WARNING LIRC_DRIVER_NAME
+					       ": AIEEEE: "
+					       "%d %d %lx %lx %lx %lx\n",
+					       dcd, sense,
+					       tv.tv_sec, lasttv.tv_sec,
+					       tv.tv_usec, lasttv.tv_usec);
+					/* detecting pulse while this
+					   MUST be a space! */
+					sense = sense ? 0 : 1;
+				}
+			} else
+				data = (int) (deltv*1000000 +
+					       tv.tv_usec -
+					       lasttv.tv_usec);
+			frbwrite(dcd^sense ? data : (data|PULSE_BIT));
+			lasttv = tv;
+			last_dcd = dcd;
+			wake_up_interruptible(&rbuf.wait_poll);
+		}
+	} while (!(sinp(UART_IIR) & UART_IIR_NO_INT)); /* still pending ? */
+	return IRQ_RETVAL(IRQ_HANDLED);
+}
+
+static void hardware_init_port(void)
+{
+	unsigned long flags;
+	local_irq_save(flags);
+
+	/* Set DLAB 0. */
+	soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
+
+	/* First of all, disable all interrupts */
+	soutp(UART_IER, sinp(UART_IER) &
+	      (~(UART_IER_MSI|UART_IER_RLSI|UART_IER_THRI|UART_IER_RDI)));
+
+	/* Clear registers. */
+	sinp(UART_LSR);
+	sinp(UART_RX);
+	sinp(UART_IIR);
+	sinp(UART_MSR);
+
+#if defined(LIRC_SERIAL_NSLU2)
+	if (type == LIRC_NSLU2) {
+		/* Setup NSLU2 UART */
+
+		/* Enable UART */
+		soutp(UART_IER, sinp(UART_IER) | UART_IE_IXP42X_UUE);
+		/* Disable Receiver data Time out interrupt */
+		soutp(UART_IER, sinp(UART_IER) & ~UART_IE_IXP42X_RTOIE);
+		/* set out2 = interupt unmask; off() doesn't set MCR
+		   on NSLU2 */
+		soutp(UART_MCR, UART_MCR_RTS|UART_MCR_OUT2);
+	}
+#endif
+
+	/* Set line for power source */
+	off();
+
+	/* Clear registers again to be sure. */
+	sinp(UART_LSR);
+	sinp(UART_RX);
+	sinp(UART_IIR);
+	sinp(UART_MSR);
+
+	switch (type) {
+	case LIRC_IRDEO:
+	case LIRC_IRDEO_REMOTE:
+		/* setup port to 7N1 @ 115200 Baud */
+		/* 7N1+start = 9 bits at 115200 ~ 3 bits at 38kHz */
+
+		/* Set DLAB 1. */
+		soutp(UART_LCR, sinp(UART_LCR) | UART_LCR_DLAB);
+		/* Set divisor to 1 => 115200 Baud */
+		soutp(UART_DLM, 0);
+		soutp(UART_DLL, 1);
+		/* Set DLAB 0 +  7N1 */
+		soutp(UART_LCR, UART_LCR_WLEN7);
+		/* THR interrupt already disabled at this point */
+		break;
+	default:
+		break;
+	}
+
+	local_irq_restore(flags);
+}
+
+static int init_port(void)
+{
+	int i, nlow, nhigh;
+
+	/* Reserve io region. */
+#if defined(LIRC_ALLOW_MMAPPED_IO)
+	/* Future MMAP-Developers: Attention!
+	   For memory mapped I/O you *might* need to use ioremap() first,
+	   for the NSLU2 it's done in boot code. */
+	if (((iommap != 0)
+	     && (request_mem_region(iommap, 8<<ioshift,
+				    LIRC_DRIVER_NAME) == NULL))
+	   || ((iommap == 0)
+	       && (request_region(io, 8, LIRC_DRIVER_NAME) == NULL))) {
+#else
+	if (request_region(io, 8, LIRC_DRIVER_NAME) == NULL) {
+#endif
+		printk(KERN_ERR  LIRC_DRIVER_NAME
+		       ": port %04x already in use\n", io);
+		printk(KERN_WARNING LIRC_DRIVER_NAME
+		       ": use 'setserial /dev/ttySX uart none'\n");
+		printk(KERN_WARNING LIRC_DRIVER_NAME
+		       ": or compile the serial port driver as module and\n");
+		printk(KERN_WARNING LIRC_DRIVER_NAME
+		       ": make sure this module is loaded first\n");
+		return -EBUSY;
+	}
+
+	hardware_init_port();
+
+	/* Initialize pulse/space widths */
+	init_timing_params(duty_cycle, freq);
+
+	/* If pin is high, then this must be an active low receiver. */
+	if (sense == -1) {
+		/* wait 1/2 sec for the power supply */
+
+		set_current_state(TASK_INTERRUPTIBLE);
+		schedule_timeout(HZ/2);
+
+		/* probe 9 times every 0.04s, collect "votes" for
+		   active high/low */
+		nlow = 0;
+		nhigh = 0;
+		for (i = 0; i < 9; i++) {
+			if (sinp(UART_MSR) & hardware[type].signal_pin)
+				nlow++;
+			else
+				nhigh++;
+			schedule_timeout(HZ/25);
+		}
+		sense = (nlow >= nhigh ? 1 : 0);
+		printk(KERN_INFO  LIRC_DRIVER_NAME  ": auto-detected active "
+		       "%s receiver\n", sense ? "low" : "high");
+	} else
+		printk(KERN_INFO  LIRC_DRIVER_NAME  ": Manually using active "
+		       "%s receiver\n", sense ? "low" : "high");
+
+	return 0;
+}
+
+static int set_use_inc(void *data)
+{
+	int result;
+	unsigned long flags;
+
+	/* Init read buffer. */
+	if (lirc_buffer_init(&rbuf, sizeof(int), RBUF_LEN) < 0)
+		return -ENOMEM;
+
+	/* initialize timestamp */
+	do_gettimeofday(&lasttv);
+
+	result = request_irq(irq, irq_handler,
+			   IRQF_DISABLED | (share_irq ? IRQF_SHARED : 0),
+			   LIRC_DRIVER_NAME, (void *)&hardware);
+
+	switch (result) {
+	case -EBUSY:
+		printk(KERN_ERR LIRC_DRIVER_NAME ": IRQ %d busy\n", irq);
+		lirc_buffer_free(&rbuf);
+		return -EBUSY;
+	case -EINVAL:
+		printk(KERN_ERR LIRC_DRIVER_NAME
+		       ": Bad irq number or handler\n");
+		lirc_buffer_free(&rbuf);
+		return -EINVAL;
+	default:
+		dprintk("Interrupt %d, port %04x obtained\n", irq, io);
+		break;
+	};
+
+	local_irq_save(flags);
+
+	/* Set DLAB 0. */
+	soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
+
+	soutp(UART_IER, sinp(UART_IER)|UART_IER_MSI);
+
+	local_irq_restore(flags);
+
+	return 0;
+}
+
+static void set_use_dec(void *data)
+{	unsigned long flags;
+
+	local_irq_save(flags);
+
+	/* Set DLAB 0. */
+	soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
+
+	/* First of all, disable all interrupts */
+	soutp(UART_IER, sinp(UART_IER) &
+	      (~(UART_IER_MSI|UART_IER_RLSI|UART_IER_THRI|UART_IER_RDI)));
+	local_irq_restore(flags);
+
+	free_irq(irq, (void *)&hardware);
+
+	dprintk("freed IRQ %d\n", irq);
+	lirc_buffer_free(&rbuf);
+}
+
+static ssize_t lirc_write(struct file *file, const char *buf,
+			 size_t n, loff_t *ppos)
+{
+	int i, count;
+	unsigned long flags;
+	long delta = 0;
+
+	if (!(hardware[type].features&LIRC_CAN_SEND_PULSE))
+		return -EBADF;
+
+	if (n % sizeof(int))
+		return -EINVAL;
+	count = n / sizeof(int);
+	if (count > WBUF_LEN || count % 2 == 0)
+		return -EINVAL;
+	if (copy_from_user(wbuf, buf, n))
+		return -EFAULT;
+	local_irq_save(flags);
+	if (type == LIRC_IRDEO) {
+		/* DTR, RTS down */
+		on();
+	}
+	for (i = 0; i < count; i++) {
+		if (i%2)
+			hardware[type].send_space(wbuf[i]-delta);
+		else
+			delta = hardware[type].send_pulse(wbuf[i]);
+	}
+	off();
+	local_irq_restore(flags);
+	return n;
+}
+
+static int lirc_ioctl(struct inode *node, struct file *filep, unsigned int cmd,
+		      unsigned long arg)
+{
+	int result;
+	unsigned long value;
+	unsigned int ivalue;
+
+	switch (cmd) {
+	case LIRC_GET_SEND_MODE:
+		if (!(hardware[type].features&LIRC_CAN_SEND_MASK))
+			return -ENOIOCTLCMD;
+
+		result = put_user(LIRC_SEND2MODE
+				  (hardware[type].features&LIRC_CAN_SEND_MASK),
+				  (unsigned long *) arg);
+		if (result)
+			return result;
+		break;
+
+	case LIRC_SET_SEND_MODE:
+		if (!(hardware[type].features&LIRC_CAN_SEND_MASK))
+			return -ENOIOCTLCMD;
+
+		result = get_user(value, (unsigned long *) arg);
+		if (result)
+			return result;
+		/* only LIRC_MODE_PULSE supported */
+		if (value != LIRC_MODE_PULSE)
+			return -ENOSYS;
+		break;
+
+	case LIRC_GET_LENGTH:
+		return -ENOSYS;
+		break;
+
+	case LIRC_SET_SEND_DUTY_CYCLE:
+		dprintk("SET_SEND_DUTY_CYCLE\n");
+		if (!(hardware[type].features&LIRC_CAN_SET_SEND_DUTY_CYCLE))
+			return -ENOIOCTLCMD;
+
+		result = get_user(ivalue, (unsigned int *) arg);
+		if (result)
+			return result;
+		if (ivalue <= 0 || ivalue > 100)
+			return -EINVAL;
+		return init_timing_params(ivalue, freq);
+		break;
+
+	case LIRC_SET_SEND_CARRIER:
+		dprintk("SET_SEND_CARRIER\n");
+		if (!(hardware[type].features&LIRC_CAN_SET_SEND_CARRIER))
+			return -ENOIOCTLCMD;
+
+		result = get_user(ivalue, (unsigned int *) arg);
+		if (result)
+			return result;
+		if (ivalue > 500000 || ivalue < 20000)
+			return -EINVAL;
+		return init_timing_params(duty_cycle, ivalue);
+		break;
+
+	default:
+		return -ENOIOCTLCMD;
+	}
+	return 0;
+}
+
+static struct file_operations lirc_fops = {
+	.write	= lirc_write,
+};
+
+static struct lirc_plugin plugin = {
+	.name		= LIRC_DRIVER_NAME,
+	.minor		= -1,
+	.code_length	= 1,
+	.sample_rate	= 0,
+	.data		= NULL,
+	.add_to_buf	= NULL,
+	.get_queue	= NULL,
+	.rbuf		= &rbuf,
+	.set_use_inc	= set_use_inc,
+	.set_use_dec	= set_use_dec,
+	.ioctl		= lirc_ioctl,
+	.fops		= &lirc_fops,
+	.dev		= NULL,
+	.owner		= THIS_MODULE,
+};
+
+#ifdef MODULE
+
+static struct platform_device *lirc_serial_dev;
+
+static int __devinit lirc_serial_probe(struct platform_device *dev)
+{
+	return 0;
+}
+
+static int __devexit lirc_serial_remove(struct platform_device *dev)
+{
+	return 0;
+}
+
+static int lirc_serial_suspend(struct platform_device *dev,
+			       pm_message_t state)
+{
+	/* Set DLAB 0. */
+	soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
+
+	/* Disable all interrupts */
+	soutp(UART_IER, sinp(UART_IER) &
+	      (~(UART_IER_MSI|UART_IER_RLSI|UART_IER_THRI|UART_IER_RDI)));
+
+	/* Clear registers. */
+	sinp(UART_LSR);
+	sinp(UART_RX);
+	sinp(UART_IIR);
+	sinp(UART_MSR);
+
+	return 0;
+}
+
+static int lirc_serial_resume(struct platform_device *dev)
+{
+	unsigned long flags;
+
+	hardware_init_port();
+
+	local_irq_save(flags);
+	/* Enable Interrupt */
+	do_gettimeofday(&lasttv);
+	soutp(UART_IER, sinp(UART_IER)|UART_IER_MSI);
+	off();
+
+	lirc_buffer_clear(&rbuf);
+
+	local_irq_restore(flags);
+
+	return 0;
+}
+
+static struct platform_driver lirc_serial_driver = {
+	.probe		= lirc_serial_probe,
+	.remove		= __devexit_p(lirc_serial_remove),
+	.suspend	= lirc_serial_suspend,
+	.resume		= lirc_serial_resume,
+	.driver		= {
+		.name	= "lirc_serial",
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init lirc_serial_init(void)
+{
+	int result;
+
+	result = platform_driver_register(&lirc_serial_driver);
+	if (result) {
+		printk("lirc register returned %d\n", result);
+		return result;
+	}
+
+	lirc_serial_dev = platform_device_alloc("lirc_serial", 0);
+	if (!lirc_serial_dev) {
+		result = -ENOMEM;
+		goto exit_driver_unregister;
+	}
+
+	result = platform_device_add(lirc_serial_dev);
+	if (result)
+		goto exit_device_put;
+
+	return 0;
+
+exit_device_put:
+	platform_device_put(lirc_serial_dev);
+exit_driver_unregister:
+	platform_driver_unregister(&lirc_serial_driver);
+	return result;
+}
+
+static void __exit lirc_serial_exit(void)
+{
+	platform_device_unregister(lirc_serial_dev);
+	platform_driver_unregister(&lirc_serial_driver);
+}
+
+int __init init_module(void)
+{
+	int result;
+
+	result = lirc_serial_init();
+	if (result)
+		return result;
+	switch (type) {
+	case LIRC_HOMEBREW:
+	case LIRC_IRDEO:
+	case LIRC_IRDEO_REMOTE:
+	case LIRC_ANIMAX:
+	case LIRC_IGOR:
+#if defined(LIRC_SERIAL_NSLU2)
+	case LIRC_NSLU2:
+#endif
+		break;
+	default:
+		result = -EINVAL;
+		goto exit_serial_exit;
+	}
+	if (!softcarrier) {
+		switch (type) {
+		case LIRC_HOMEBREW:
+		case LIRC_IGOR:
+		case LIRC_NSLU2:
+			hardware[type].features &=
+				~(LIRC_CAN_SET_SEND_DUTY_CYCLE|
+				  LIRC_CAN_SET_SEND_CARRIER);
+			break;
+		}
+	}
+	result = init_port();
+	if (result < 0)
+		goto exit_serial_exit;
+	plugin.features = hardware[type].features;
+	plugin.minor = lirc_register_plugin(&plugin);
+	if (plugin.minor < 0) {
+		printk(KERN_ERR  LIRC_DRIVER_NAME
+		       ": register_chrdev failed!\n");
+		result = -EIO;
+		goto exit_release;
+	}
+	return 0;
+exit_release:
+	release_region(io, 8);
+exit_serial_exit:
+	lirc_serial_exit();
+	return result;
+}
+
+void __exit cleanup_module(void)
+{
+	lirc_serial_exit();
+#if defined(LIRC_ALLOW_MMAPPED_IO)
+	if (iommap != 0)
+		release_mem_region(iommap, 8<<ioshift);
+	else
+		release_region(io, 8);
+#else
+	release_region(io, 8);
+#endif
+	lirc_unregister_plugin(plugin.minor);
+	dprintk("cleaned up module\n");
+}
+
+MODULE_DESCRIPTION("Infra-red receiver driver for serial ports.");
+MODULE_AUTHOR("Ralph Metzler, Trent Piepho, Ben Pfaff, "
+	      "Christoph Bartelmus, Andrei Tanas");
+MODULE_LICENSE("GPL");
+
+module_param(type, int, 0444);
+#if defined(LIRC_SERIAL_NSLU2)
+MODULE_PARM_DESC(type, "Hardware type (0 = home-brew, 1 = IRdeo,"
+		 " 2 = IRdeo Remote, 3 = AnimaX, 4 = IgorPlug,"
+		 " 5 = NSLU2 RX:CTS2/TX:GreenLED)");
+#else
+MODULE_PARM_DESC(type, "Hardware type (0 = home-brew, 1 = IRdeo,"
+		 " 2 = IRdeo Remote, 3 = AnimaX, 4 = IgorPlug)");
+#endif
+
+module_param(io, int, 0444);
+MODULE_PARM_DESC(io, "I/O address base (0x3f8 or 0x2f8)");
+
+#if defined(LIRC_ALLOW_MMAPPED_IO)
+/* some architectures (e.g. intel xscale) have memory mapped registers */
+module_param(iommap, bool, 0444);
+MODULE_PARM_DESC(iommap, "physical base for memory mapped I/O"
+		" (0 = no memory mapped io)");
+
+/* some architectures (e.g. intel xscale) align the 8bit serial registers
+   on 32bit word boundaries.
+   See linux-kernel/serial/8250.c serial_in()/out() */
+module_param(ioshift, int, 0444);
+MODULE_PARM_DESC(ioshift, "shift I/O register offset (0 = no shift)");
+#endif
+
+module_param(irq, int, 0444);
+MODULE_PARM_DESC(irq, "Interrupt (4 or 3)");
+
+module_param(share_irq, bool, 0444);
+MODULE_PARM_DESC(share_irq, "Share interrupts (0 = off, 1 = on)");
+
+module_param(sense, bool, 0444);
+MODULE_PARM_DESC(sense, "Override autodetection of IR receiver circuit"
+		 " (0 = active high, 1 = active low )");
+
+#ifdef LIRC_SERIAL_TRANSMITTER
+module_param(txsense, bool, 0444);
+MODULE_PARM_DESC(txsense, "Sense of transmitter circuit"
+		 " (0 = active high, 1 = active low )");
+#endif
+
+module_param(softcarrier, bool, 0444);
+MODULE_PARM_DESC(softcarrier, "Software carrier (0 = off, 1 = on)");
+
+module_param(debug, bool, 0644);
+MODULE_PARM_DESC(debug, "Enable debugging messages");
+
+#endif /* MODULE */
-- 
1.6.0.1


^ permalink raw reply related	[flat|nested] 111+ messages in thread

* [PATCH 03/18] lirc driver for 1st-gen Media Center Ed. USB IR transceivers
  2008-09-09  4:05   ` [PATCH 02/18] lirc serial port receiver/transmitter device driver Jarod Wilson
@ 2008-09-09  4:05     ` Jarod Wilson
  2008-09-09  4:05       ` [PATCH 04/18] lirc driver for 2nd-gen and later " Jarod Wilson
  2008-09-09 19:21       ` [PATCH 03/18] lirc driver for 1st-gen " Jonathan Corbet
  2008-09-09 16:14     ` [PATCH 02/18] lirc serial port receiver/transmitter device driver Jonathan Corbet
  1 sibling, 2 replies; 111+ messages in thread
From: Jarod Wilson @ 2008-09-09  4:05 UTC (permalink / raw)
  To: linux-kernel
  Cc: Jarod Wilson, Jarod Wilson, Janne Grunau, Christoph Bartelmus

Works a treat with the lirc_mceusb receiver hooked to my own mythtv frontend,
feeding it signals from a Logitech Harmony 880 remote.

Signed-off-by: Jarod Wilson <jarod@redhat.com>
Signed-off-by: Janne Grunau <j@jannau.net>
CC: Christoph Bartelmus <lirc@bartelmus.de>
Tested-by: Jarod Wilson <jarod@redhat.com>
---
 drivers/input/lirc/Kconfig       |    7 +
 drivers/input/lirc/Makefile      |    1 +
 drivers/input/lirc/lirc_mceusb.c |  890 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 898 insertions(+), 0 deletions(-)
 create mode 100644 drivers/input/lirc/lirc_mceusb.c

diff --git a/drivers/input/lirc/Kconfig b/drivers/input/lirc/Kconfig
index 73403b9..7dbf1a5 100644
--- a/drivers/input/lirc/Kconfig
+++ b/drivers/input/lirc/Kconfig
@@ -18,6 +18,13 @@ config LIRC_DEV
 	help
 	  LIRC device loadable module support, required for most LIRC drivers
 
+config LIRC_MCEUSB
+	tristate "Microsoft Media Center Ed. Receiver, v1"
+	default n
+	depends on LIRC_DEV
+	help
+	  Driver for the Microsoft Media Center Ed. Receiver, v1
+
 config LIRC_SERIAL
 	tristate "Homebrew Serial Port Receiver"
 	default n
diff --git a/drivers/input/lirc/Makefile b/drivers/input/lirc/Makefile
index 7d76128..fafdd25 100644
--- a/drivers/input/lirc/Makefile
+++ b/drivers/input/lirc/Makefile
@@ -6,4 +6,5 @@
 EXTRA_CFLAGS =-DIRCTL_DEV_MAJOR=61 -DLIRC_SERIAL_TRANSMITTER -I$(src)
 
 obj-$(CONFIG_LIRC_DEV)		+= lirc_dev.o
+obj-$(CONFIG_LIRC_MCEUSB)	+= lirc_mceusb.o
 obj-$(CONFIG_LIRC_SERIAL)	+= lirc_serial.o
diff --git a/drivers/input/lirc/lirc_mceusb.c b/drivers/input/lirc/lirc_mceusb.c
new file mode 100644
index 0000000..f1874f3
--- /dev/null
+++ b/drivers/input/lirc/lirc_mceusb.c
@@ -0,0 +1,890 @@
+/*
+ * USB Microsoft IR Transceiver driver - 0.2
+ *
+ * Copyright (c) 2003-2004 Dan Conti (dconti@acm.wwu.edu)
+ *
+ * This driver is based on the USB skeleton driver packaged with the
+ * kernel, and the notice from that package has been retained below.
+ *
+ * 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.
+ *
+ * All trademarks property of their respective owners. This driver was
+ * originally based on the USB skeleton driver, although significant
+ * portions of that code have been removed as the driver has evolved.
+ *
+ * 2003_11_11 - Restructured to minimalize code interpretation in the
+ *	      driver. The normal use case will be with lirc.
+ *
+ * 2004_01_01 - Removed all code interpretation. Generate mode2 data
+ *	      for passing off to lirc. Cleanup
+ *
+ * 2004_01_04 - Removed devfs handle. Put in a temporary workaround
+ *	      for a known issue where repeats generate two
+ *	      sequential spaces (last_was_repeat_gap)
+ *
+ * 2004_02_17 - Changed top level api to no longer use fops, and
+ *	      instead use new interface for polling via
+ *	      lirc_thread. Restructure data read/mode2 generation to
+ *	      a single pass, reducing number of buffers. Rev to .2
+ *
+ * 2004_02_27 - Last of fixups to plugin->add_to_buf API. Properly
+ *	      handle broken fragments from the receiver. Up the
+ *	      sample rate and remove any pacing from
+ *	      fetch_more_data. Fixes all known issues.
+ *
+ * TODO
+ *   - Fix up minor number, registration of major/minor with usb subsystem
+ *
+ */
+/*
+ * USB Skeleton driver - 1.1
+ *
+ * Copyright (C) 2001-2003 Greg Kroah-Hartman (greg@kroah.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, version 2.
+ *
+ *
+ * This driver is to be used as a skeleton driver to be able to create a
+ * USB driver quickly.  The design of it is based on the usb-serial and
+ * dc2xx drivers.
+ *
+ * Thanks to Oliver Neukum, David Brownell, and Alan Stern for their help
+ * in debugging this driver.
+ *
+ *
+ * History:
+ *
+ * 2003-05-06 - 1.1 - changes due to usb core changes with usb_register_dev()
+ * 2003-02-25 - 1.0 - fix races involving urb->status, unlink_urb(), and
+ *			disconnect.  Fix transfer amount in read().  Use
+ *			macros instead of magic numbers in probe().  Change
+ *			size variables to size_t.  Show how to eliminate
+ *			DMA bounce buffer.
+ * 2002_12_12 - 0.9 - compile fixes and got rid of fixed minor array.
+ * 2002_09_26 - 0.8 - changes due to USB core conversion to struct device
+ *			driver.
+ * 2002_02_12 - 0.7 - zero out dev in probe function for devices that do
+ *			not have both a bulk in and bulk out endpoint.
+ *			Thanks to Holger Waechtler for the fix.
+ * 2001_11_05 - 0.6 - fix minor locking problem in skel_disconnect.
+ *			Thanks to Pete Zaitcev for the fix.
+ * 2001_09_04 - 0.5 - fix devfs bug in skel_disconnect. Thanks to wim delvaux
+ * 2001_08_21 - 0.4 - more small bug fixes.
+ * 2001_05_29 - 0.3 - more bug fixes based on review from linux-usb-devel
+ * 2001_05_24 - 0.2 - bug fixes based on review from linux-usb-devel people
+ * 2001_05_01 - 0.1 - first version
+ *
+ */
+
+#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/smp_lock.h>
+#include <linux/usb.h>
+#include <linux/completion.h>
+#include <linux/uaccess.h>
+
+#ifdef CONFIG_USB_DEBUG
+static int debug = 1;
+#else
+static int debug;
+#endif
+
+#include "lirc.h"
+#include "lirc_dev.h"
+
+/* Use our own dbg macro */
+#define dprintk(fmt, args...)				\
+	do {						\
+		if (debug)				\
+			printk(KERN_DEBUG __FILE__ ": "	\
+			       fmt "\n", ## 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_DEVICE(USB_MCEUSB_VENDOR_ID, USB_MCEUSB_PRODUCT_ID) },
+	{ }	/* 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 usb_skel {
+	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 */
+
+	atomic_t write_busy;		/* true iff write urb is busy */
+	struct completion write_finished; /* wait for the write to finish */
+
+	wait_queue_head_t wait_q; /* for timeouts */
+	int open_count;		/* number of times this port has been opened */
+	struct mutex sem;	/* locks this structure */
+
+	int present;		/* if the device is not disconnected */
+
+	struct lirc_plugin *plugin;
+
+	int lircdata[256]; /* place to store data until lirc processes it */
+	int lircidx;		/* current index */
+	int lirccnt;		/* remaining values */
+
+	int usb_valid_bytes_in_bulk_buffer; /* leftover data from prior read */
+	int mce_bytes_left_in_packet;	/* for packets split across reads */
+
+	/* Value to hold the last received space; 0 if last value
+	 * received was a pulse */
+	int last_space;
+
+	dma_addr_t dma_in;
+	dma_addr_t dma_out;
+};
+
+#define MCE_TIME_UNIT 50
+
+/* driver api */
+static int mceusb_probe(struct usb_interface *interface,
+			const struct usb_device_id *id);
+static void mceusb_disconnect(struct usb_interface *interface);
+static void mceusb_write_bulk_callback(struct urb *urb);
+
+/* read data from the usb bus; convert to mode2 */
+static int msir_fetch_more_data(struct usb_skel *dev, int dont_block);
+
+/* helper functions */
+static void msir_cleanup(struct usb_skel *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 usb_skel *minor_table[MAX_DEVICES];
+
+/* lock to protect the minor_table structure */
+static DECLARE_MUTEX(minor_table_mutex);
+static void mceusb_setup(struct usb_device *udev);
+
+/* usb specific object needed to register this driver with the usb subsystem */
+static struct usb_driver mceusb_driver = {
+	.name		= DRIVER_NAME,
+	.probe		= mceusb_probe,
+	.disconnect	= mceusb_disconnect,
+	.id_table	= mceusb_table,
+};
+
+
+/**
+ *	usb_mceusb_debug_data
+ */
+static inline void usb_mceusb_debug_data(const char *function, int size,
+					  const unsigned char *data)
+{
+	int i;
+	if (!debug)
+		return;
+
+	printk(KERN_DEBUG __FILE__": %s - length = %d, data = ",
+	       function, size);
+	for (i = 0; i < size; ++i)
+		printk(KERN_DEBUG "%.2x ", data[i]);
+	printk(KERN_DEBUG "\n");
+}
+
+/**
+ *mceusb_delete
+ */
+static inline void mceusb_delete(struct usb_skel *dev)
+{
+	dprintk("%s", __func__);
+	minor_table[dev->minor] = NULL;
+	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);
+	if (dev->write_urb != NULL)
+		usb_free_urb(dev->write_urb);
+	kfree(dev);
+}
+
+static void mceusb_setup(struct usb_device *udev)
+{
+	char data[8];
+	int res;
+
+	memset(data, 0, 8);
+
+	/* Get Status */
+	res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+			      USB_REQ_GET_STATUS, USB_DIR_IN,
+			      0, 0, data, 2, HZ * 3);
+
+	/*    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]);
+
+
+	/* 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);
+
+	/* These two are sent by the windows driver, but stall for
+	 * me. I dont 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);
+
+	dprintk("%s - res = %d", __func__, res);
+
+	/* 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);
+
+	dprintk("%s - res = %d", __func__, res);
+#endif
+}
+
+static void msir_cleanup(struct usb_skel *dev)
+{
+	memset(dev->bulk_in_buffer, 0, dev->bulk_in_size);
+
+	dev->usb_valid_bytes_in_bulk_buffer = 0;
+
+	dev->last_space = PULSE_MASK;
+
+	dev->mce_bytes_left_in_packet = 0;
+	dev->lircidx = 0;
+	dev->lirccnt = 0;
+	memset(dev->lircdata, 0, sizeof(dev->lircdata));
+}
+
+static int set_use_inc(void *data)
+{
+	return 0;
+}
+
+static void set_use_dec(void *data)
+{
+}
+
+/*
+ * 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->sem 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 usb_skel *dev, int dont_block)
+{
+	int retries = 0;
+	int words_to_read =
+		(sizeof(dev->lircdata)/sizeof(int)) - 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 (words_to_read == 0)
+		return dev->lirccnt;
+
+	/* 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;
+
+	/* reserve room for our leading space */
+	if (dev->last_space)
+		words_to_read--;
+
+	while (words_to_read) {
+		/* handle signals and USB disconnects */
+		if (signal_pending(current))
+			return dev->lirccnt ? dev->lirccnt : -EINTR;
+		if (!dev->udev)
+			return -ENODEV;
+
+		bulkidx = 0;
+
+		/*
+		 * perform data read (phys or from previous buffer)
+		 */
+
+		/* 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;
+		} else {
+			int retval;
+
+			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);
+
+			/* 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;
+			}
+
+			retries = 0;
+			if (partial)
+				this_read = partial;
+
+			/* skip the header */
+			bulkidx += 2;
+
+			/* check for empty reads (header only) */
+			if (this_read == 2) {
+				/* assume no data */
+				if (dont_block)
+					break;
+
+				/* sleep for a bit before performing
+				   another read */
+				interruptible_sleep_on_timeout(&dev->wait_q, 1);
+				continue;
+			}
+		}
+
+		/*
+		 * process data
+		 */
+
+		/* 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;
+
+			/* 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 occaisonally we do not receive
+				 * the second half of a fragment
+				 */
+				bytes_left_in_packet =
+					128 + signedp[bulkidx++];
+
+				/* unfortunately rather than keep all
+				 * the data in the packetized format,
+				 * the transceiver sends a trailing 8
+				 * bytes that aren't part of the
+				 * transmittion from the remote,
+				 * aren't packetized, and dont 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;
+				}
+
+				/* 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;
+
+			}
+
+			/*
+			 * generate mode2
+			 */
+
+			keycode = signedp[bulkidx++];
+			if (keycode < 0) {
+				pulse = 1;
+				keycode += 128;
+			}
+			keycode *= MCE_TIME_UNIT;
+
+			bytes_left_in_packet--;
+
+			if (pulse) {
+				if (dev->last_space) {
+					dev->lircdata[dev->lirccnt++] =
+						dev->last_space;
+					dev->last_space = 0;
+					words_to_read--;
+
+					/* 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--;
+				}
+
+				dev->last_space += keycode;
+			}
+		}
+	}
+
+	/* save off some info if we are 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 dev->lirccnt;
+}
+
+/* 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
+ * plugin->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)
+{
+	struct usb_skel *dev = (struct usb_skel *) data;
+
+	mutex_lock(&dev->sem);
+
+	/* verify device still present */
+	if (dev->udev == NULL) {
+		mutex_unlock(&dev->sem);
+		return -ENODEV;
+	}
+
+	if (!dev->lirccnt) {
+		int res;
+		dev->lircidx = 0;
+
+		res = msir_fetch_more_data(dev, 1);
+
+		if (res == 0)
+			res = -ENODATA;
+		if (res < 0) {
+			mutex_unlock(&dev->sem);
+			return res;
+		}
+	}
+
+	if (dev->lirccnt) {
+		int keys_to_copy;
+
+		/* 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;
+
+		lirc_buffer_write_n(buf,
+			(unsigned char *) &(dev->lircdata[dev->lircidx]),
+			keys_to_copy);
+		dev->lircidx += keys_to_copy;
+		dev->lirccnt -= keys_to_copy;
+
+		mutex_unlock(&dev->sem);
+		return 0;
+	}
+
+	mutex_unlock(&dev->sem);
+	return -ENODATA;
+}
+
+/**
+ *	mceusb_write_bulk_callback
+ */
+static void mceusb_write_bulk_callback(struct urb *urb)
+{
+	struct usb_skel *dev = (struct usb_skel *)urb->context;
+
+	dprintk("%s - minor %d", __func__, dev->minor);
+
+	if ((urb->status != -ENOENT) &&
+	    (urb->status != -ECONNRESET)) {
+		dprintk("%s - nonzero write buld status received: %d",
+			__func__, urb->status);
+		return;
+	}
+
+	return;
+}
+
+/**
+ *	mceusb_probe
+ *
+ *	Called by the usb core when a new device is connected that it
+ *	thinks this driver might be interested in.
+ */
+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;
+	struct usb_skel *dev = NULL;
+	struct usb_endpoint_descriptor *endpoint;
+
+	struct lirc_plugin *plugin;
+	struct lirc_buffer *rbuf;
+
+	int minor;
+	size_t buffer_size;
+	int i;
+	int retval = -ENOMEM;
+	char junk[64];
+	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");
+		return -ENODEV;
+	}
+
+	/* select a "subminor" number (part of a minor number) */
+	down(&minor_table_mutex);
+	for (minor = 0; minor < MAX_DEVICES; ++minor) {
+		if (minor_table[minor] == NULL)
+			break;
+	}
+	if (minor >= MAX_DEVICES) {
+		info("Too many devices plugged in, "
+		     "can not handle this device.");
+		goto error;
+	}
+
+	/* allocate memory for our device state and initialize it */
+	dev = kmalloc(sizeof(struct usb_skel), GFP_KERNEL);
+	if (dev == NULL) {
+		err("Out of memory");
+		retval = -ENOMEM;
+		goto error;
+	}
+	minor_table[minor] = dev;
+
+	memset(dev, 0x00, sizeof(*dev));
+	mutex_init(&dev->sem);
+	dev->udev = udev;
+	dev->interface = interface;
+	dev->minor = minor;
+
+	/* set up the endpoint information */
+	/* check out the endpoints */
+	/* use only the first bulk-in and bulk-out endpoints */
+	iface_desc = interface->cur_altsetting;
+
+	for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+		endpoint = &iface_desc->endpoint[i].desc;
+		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;
+			dev->bulk_in_buffer =
+				usb_buffer_alloc(udev, buffer_size,
+						 GFP_ATOMIC, &dev->dma_in);
+			if (!dev->bulk_in_buffer) {
+				err("Couldn't allocate bulk_in_buffer");
+				goto error;
+			}
+		}
+
+		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");
+			dev->write_urb = usb_alloc_urb(0, GFP_KERNEL);
+			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;
+			dev->bulk_out_buffer =
+				usb_buffer_alloc(udev, buffer_size,
+						 GFP_ATOMIC, &dev->dma_out);
+			if (!dev->bulk_out_buffer) {
+				err("Couldn't allocate bulk_out_buffer");
+				goto error;
+			}
+			usb_fill_bulk_urb(dev->write_urb, udev,
+				      usb_sndbulkpipe
+				      (udev, endpoint->bEndpointAddress),
+				      dev->bulk_out_buffer, buffer_size,
+				      mceusb_write_bulk_callback, dev);
+		}
+	}
+
+	if (!(dev->bulk_in_endpointAddr && dev->bulk_out_endpointAddr)) {
+		err("Couldn't find both bulk-in and bulk-out endpoints");
+		goto error;
+	}
+
+	/* init the waitq */
+	init_waitqueue_head(&dev->wait_q);
+
+
+	/* Set up our lirc plugin */
+	plugin = kmalloc(sizeof(struct lirc_plugin), GFP_KERNEL);
+	if (!plugin) {
+		err("out of memory");
+		goto error;
+	}
+	memset(plugin, 0, sizeof(struct lirc_plugin));
+
+	rbuf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
+	if (!rbuf) {
+		err("out of memory");
+		kfree(plugin);
+		goto error;
+	}
+
+	/* the lirc_atiusb module doesn't memset rbuf here ... ? */
+	if (lirc_buffer_init(rbuf, sizeof(int), 128)) {
+		err("out of memory");
+		kfree(plugin);
+		kfree(rbuf);
+		goto error;
+	}
+
+	strcpy(plugin->name, DRIVER_NAME " ");
+	plugin->minor = minor;
+	plugin->code_length = sizeof(int) * 8;
+	plugin->features = LIRC_CAN_REC_MODE2; /* | LIRC_CAN_SEND_MODE2; */
+	plugin->data = dev;
+	plugin->rbuf = rbuf;
+	plugin->ioctl = NULL;
+	plugin->set_use_inc = &set_use_inc;
+	plugin->set_use_dec = &set_use_dec;
+	plugin->sample_rate = 80;   /* sample at 100hz (10ms) */
+	plugin->add_to_buf = &mceusb_add_to_buf;
+	/* plugin->fops = &mceusb_fops; */
+	plugin->dev = &udev->dev;
+	plugin->owner = THIS_MODULE;
+	if (lirc_register_plugin(plugin) < 0) {
+		kfree(plugin);
+		lirc_buffer_free(rbuf);
+		kfree(rbuf);
+		goto error;
+	}
+	dev->plugin = plugin;
+
+	/* 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++)
+		(void) usb_bulk_msg(udev,
+				    usb_rcvbulkpipe(udev,
+						    dev->bulk_in_endpointAddr),
+				    junk, 64, &partial, HZ*10);
+
+	msir_cleanup(dev);
+	mceusb_setup(udev);
+
+	/* we can register the device now, as it is ready */
+	usb_set_intfdata(interface, dev);
+	/* let the user know what node this device is now attached to */
+	/* info("USB Microsoft IR Transceiver device now attached to msir%d",
+		dev->minor); */
+	up(&minor_table_mutex);
+	return 0;
+error:
+	mceusb_delete(dev);
+	dev = NULL;
+	dprintk("%s: retval = %x", __func__, retval);
+	up(&minor_table_mutex);
+	return retval;
+}
+
+/**
+ *	mceusb_disconnect
+ *
+ *	Called by the usb core when the device is removed from the system.
+ *
+ *	This routine guarantees that the driver will not submit any more urbs
+ *	by clearing dev->udev.  It is also supposed to terminate any currently
+ *	active urbs.  Unfortunately, usb_bulk_msg(), used in skel_read(), does
+ *	not provide any way to do this.  But at least we can cancel an active
+ *	write.
+ */
+static void mceusb_disconnect(struct usb_interface *interface)
+{
+	struct usb_skel *dev;
+	int minor;
+	dev = usb_get_intfdata(interface);
+	usb_set_intfdata(interface, NULL);
+
+	down(&minor_table_mutex);
+	mutex_lock(&dev->sem);
+	minor = dev->minor;
+
+	/* unhook lirc things */
+	lirc_unregister_plugin(minor);
+	lirc_buffer_free(dev->plugin->rbuf);
+	kfree(dev->plugin->rbuf);
+	kfree(dev->plugin);
+	/* terminate an ongoing write */
+	if (atomic_read(&dev->write_busy)) {
+		usb_kill_urb(dev->write_urb);
+		wait_for_completion(&dev->write_finished);
+	}
+
+	/* prevent device read, write and ioctl */
+	dev->present = 0;
+
+	mceusb_delete(dev);
+
+	info("Microsoft IR Transceiver #%d now disconnected", minor);
+	mutex_unlock(&dev->sem);
+	up(&minor_table_mutex);
+}
+
+
+
+/**
+ *	usb_mceusb_init
+ */
+static int __init usb_mceusb_init(void)
+{
+	int result;
+
+	/* register this driver with the USB subsystem */
+	result = usb_register(&mceusb_driver);
+	if (result) {
+		err("usb_register failed for the " DRIVER_NAME
+		    " driver. error number %d", result);
+		return result;
+	}
+
+	info(DRIVER_DESC " " DRIVER_VERSION);
+	return 0;
+}
+
+
+/**
+ *	usb_mceusb_exit
+ */
+static void __exit usb_mceusb_exit(void)
+{
+	/* deregister this driver with the USB subsystem */
+	usb_deregister(&mceusb_driver);
+}
+
+#ifdef MODULE
+module_init(usb_mceusb_init);
+module_exit(usb_mceusb_exit);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(usb, mceusb_table);
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
+
+#else /* not MODULE */
+subsys_initcall(usb_mceusb_init);
+
+#endif /* MODULE */
-- 
1.6.0.1


^ permalink raw reply related	[flat|nested] 111+ messages in thread

* [PATCH 04/18] lirc driver for 2nd-gen and later Media Center Ed. USB IR transceivers
  2008-09-09  4:05     ` [PATCH 03/18] lirc driver for 1st-gen Media Center Ed. USB IR transceivers Jarod Wilson
@ 2008-09-09  4:05       ` Jarod Wilson
  2008-09-09  4:05         ` [PATCH 05/18] lirc driver for i2c-based IR receivers Jarod Wilson
  2008-09-09 23:30         ` [PATCH 04/18] lirc driver for 2nd-gen and later Media Center Ed. USB IR transceivers Jonathan Corbet
  2008-09-09 19:21       ` [PATCH 03/18] lirc driver for 1st-gen " Jonathan Corbet
  1 sibling, 2 replies; 111+ messages in thread
From: Jarod Wilson @ 2008-09-09  4:05 UTC (permalink / raw)
  To: linux-kernel
  Cc: Jarod Wilson, Jarod Wilson, Janne Grunau, Christoph Bartelmus

Successfully tested with the mce v2 receiver and remote that shipped with my
Hauppauge HVR-1500 expresscard tuner.

Signed-off-by: Jarod Wilson <jarod@redhat.com>
Signed-off-by: Janne Grunau <j@jannau.net>
CC: Christoph Bartelmus <lirc@bartelmus.de>
Tested-by: Jarod Wilson <jarod@redhat.com>
---
 drivers/input/lirc/Kconfig        |    7 +
 drivers/input/lirc/Makefile       |    1 +
 drivers/input/lirc/lirc_mceusb2.c | 1119 +++++++++++++++++++++++++++++++++++++
 3 files changed, 1127 insertions(+), 0 deletions(-)
 create mode 100644 drivers/input/lirc/lirc_mceusb2.c

diff --git a/drivers/input/lirc/Kconfig b/drivers/input/lirc/Kconfig
index 7dbf1a5..0a0b059 100644
--- a/drivers/input/lirc/Kconfig
+++ b/drivers/input/lirc/Kconfig
@@ -25,6 +25,13 @@ config LIRC_MCEUSB
 	help
 	  Driver for the Microsoft Media Center Ed. Receiver, v1
 
+config LIRC_MCEUSB2
+	tristate "Microsoft Media Center Ed. Receiver, v2"
+	default n
+	depends on LIRC_DEV
+	help
+	  Driver for the Microsoft Media Center Ed. Receiver, v2
+
 config LIRC_SERIAL
 	tristate "Homebrew Serial Port Receiver"
 	default n
diff --git a/drivers/input/lirc/Makefile b/drivers/input/lirc/Makefile
index fafdd25..f6accb9 100644
--- a/drivers/input/lirc/Makefile
+++ b/drivers/input/lirc/Makefile
@@ -7,4 +7,5 @@ EXTRA_CFLAGS =-DIRCTL_DEV_MAJOR=61 -DLIRC_SERIAL_TRANSMITTER -I$(src)
 
 obj-$(CONFIG_LIRC_DEV)		+= lirc_dev.o
 obj-$(CONFIG_LIRC_MCEUSB)	+= lirc_mceusb.o
+obj-$(CONFIG_LIRC_MCEUSB2)	+= lirc_mceusb2.o
 obj-$(CONFIG_LIRC_SERIAL)	+= lirc_serial.o
diff --git a/drivers/input/lirc/lirc_mceusb2.c b/drivers/input/lirc/lirc_mceusb2.c
new file mode 100644
index 0000000..1e683d3
--- /dev/null
+++ b/drivers/input/lirc/lirc_mceusb2.c
@@ -0,0 +1,1119 @@
+/*
+ * LIRC driver for Philips eHome USB Infrared Transceiver
+ * and the Microsoft MCE 2005 Remote Control
+ *
+ * (C) by Martin A. Blatter <martin_a_blatter@yahoo.com>
+ *
+ * Transmitter support and reception code cleanup.
+ * (C) by Daniel Melander <lirc@rajidae.se>
+ *
+ * Derived from ATI USB driver by Paul Miller and the original
+ * MCE USB driver by Dan Corti
+ *
+ * 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>
+
+#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/completion.h>
+#include <linux/uaccess.h>
+#include <linux/usb.h>
+#include <linux/poll.h>
+#include <linux/wait.h>
+#include <linux/time.h>
+
+#include "lirc.h"
+#include "lirc_dev.h"
+
+#define DRIVER_VERSION	"1.48"
+#define DRIVER_AUTHOR	"Daniel Melander <lirc@rajidae.se>, " \
+			"Martin Blatter <martin_a_blatter@yahoo.com>"
+#define DRIVER_DESC	"Philips eHome USB IR Transceiver and Microsoft " \
+			"MCE 2005 Remote Control driver for LIRC"
+#define DRIVER_NAME	"lirc_mceusb2"
+
+#define USB_BUFLEN	16	/* 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
+#define dprintk(fmt, args...)					\
+	do {							\
+		if (debug)					\
+			printk(KERN_DEBUG fmt, ## args);	\
+	} while (0)
+
+/* lock irctl structure */
+#define IRLOCK		mutex_lock(&ir->lock)
+#define IRUNLOCK	mutex_unlock(&ir->lock)
+
+/* general constants */
+#define SUCCESS			0
+#define SEND_FLAG_IN_PROGRESS	1
+#define SEND_FLAG_COMPLETE	2
+#define RECV_FLAG_IN_PROGRESS	3
+#define RECV_FLAG_COMPLETE	4
+
+#define PHILUSB_INBOUND		1
+#define PHILUSB_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
+
+static struct usb_device_id usb_remote_table[] = {
+	/* Philips eHome Infrared Transceiver */
+	{ USB_DEVICE(VENDOR_PHILIPS, 0x0815) },
+	/* 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) },
+	/* 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) },
+	/* 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) },
+	/* Fintek eHome Infrared Transceiver */
+	{ USB_DEVICE(VENDOR_FINTEK, 0x0602) },
+	/* Pinnacle Remote Kit */
+	{ USB_DEVICE(VENDOR_PINNACLE, 0x0225) },
+	/* Terminating entry */
+	{ }
+};
+
+static struct usb_device_id pinnacle_list[] = {
+	{ USB_DEVICE(VENDOR_PINNACLE, 0x0225) },
+	{}
+};
+
+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, 0x0007) },
+	{ USB_DEVICE(VENDOR_TOPSEED, 0x0008) },
+	{ USB_DEVICE(VENDOR_PINNACLE, 0x0225) },
+	{}
+};
+
+/* data structure for each usb remote */
+struct irctl {
+
+	/* 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;
+
+	/* buffers and dma */
+	unsigned char *buf_in;
+	unsigned int len_in;
+	dma_addr_t dma_in;
+	dma_addr_t dma_out;
+
+	/* lirc */
+	struct lirc_plugin *p;
+	int lircdata;
+	unsigned char is_pulse;
+	struct {
+		u32 connected:1;
+		u32 pinnacle:1;
+		u32 transmitter_mask_inverted:1;
+		u32 reserved:29;
+	} 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;
+};
+
+/* init strings */
+static char init1[] = {0x00, 0xff, 0xaa, 0xff, 0x0b};
+static char init2[] = {0xff, 0x18};
+
+static char pin_init1[] = { 0x9f, 0x07};
+static char pin_init2[] = { 0x9f, 0x13};
+static char pin_init3[] = { 0x9f, 0x0d};
+
+static void usb_remote_printdata(struct irctl *ir, char *buf, int len)
+{
+	char codes[USB_BUFLEN*3 + 1];
+	int i;
+
+	if (len <= 0)
+		return;
+
+	for (i = 0; i < len && i < USB_BUFLEN; i++)
+		snprintf(codes+i*3, 4, "%02x ", buf[i] & 0xFF);
+
+	printk(KERN_INFO "" DRIVER_NAME "[%d]: data received %s (length=%d)\n",
+		ir->devnum, codes, len);
+}
+
+static void usb_async_callback(struct urb *urb, struct pt_regs *regs)
+{
+	struct irctl *ir;
+	int len;
+
+	if (!urb)
+		return;
+
+	ir = urb->context;
+	if (ir) {
+		len = urb->actual_length;
+
+		dprintk(DRIVER_NAME
+			"[%d]: callback called (status=%d len=%d)\n",
+			ir->devnum, urb->status, len);
+
+		if (debug)
+			usb_remote_printdata(ir, urb->transfer_buffer, len);
+	}
+
+}
+
+
+/* request incoming or send outgoing usb packet - used to initialize remote */
+static void request_packet_async(struct irctl *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 (async_urb) {
+			/* alloc buffer */
+			async_buf = kmalloc(size, GFP_KERNEL);
+			if (async_buf) {
+				if (urb_type == PHILUSB_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);
+				}
+			} else {
+				usb_free_urb(async_urb);
+				return;
+			}
+		}
+	} else {
+		/* standard request */
+		async_urb = ir->urb_in;
+		ir->send_flags = RECV_FLAG_IN_PROGRESS;
+	}
+	dprintk(DRIVER_NAME "[%d]: receive request called (size=%#x)\n",
+		ir->devnum, size);
+
+	async_urb->transfer_buffer_length = size;
+	async_urb->dev = ir->usbdev;
+
+	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 int unregister_from_lirc(struct irctl *ir)
+{
+	struct lirc_plugin *p = ir->p;
+	int devnum;
+	int rtn;
+
+	devnum = ir->devnum;
+	dprintk(DRIVER_NAME "[%d]: unregister from lirc called\n", devnum);
+
+	rtn = lirc_unregister_plugin(p->minor);
+	if (rtn > 0) {
+		printk(DRIVER_NAME "[%d]: error in lirc_unregister minor: %d\n"
+			"Trying again...\n", devnum, p->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_plugin(p->minor);
+		if (rtn > 0)
+			printk(DRIVER_NAME "[%d]: lirc_unregister failed\n",
+			devnum);
+	}
+
+	if (rtn != SUCCESS) {
+		printk(DRIVER_NAME "[%d]: didn't free resources\n", devnum);
+		return -EAGAIN;
+	}
+
+	printk(DRIVER_NAME "[%d]: usb remote disconnected\n", devnum);
+
+	lirc_buffer_free(p->rbuf);
+	kfree(p->rbuf);
+	kfree(p);
+	kfree(ir);
+	return SUCCESS;
+}
+
+static int set_use_inc(void *data)
+{
+	struct irctl *ir = data;
+
+	if (!ir) {
+		printk(DRIVER_NAME "[?]: set_use_inc called with no context\n");
+		return -EIO;
+	}
+	dprintk(DRIVER_NAME "[%d]: set use inc\n", ir->devnum);
+
+	if (!ir->flags.connected) {
+		if (!ir->usbdev)
+			return -ENOENT;
+		ir->flags.connected = 1;
+	}
+
+	return SUCCESS;
+}
+
+static void set_use_dec(void *data)
+{
+	struct irctl *ir = data;
+
+	if (!ir) {
+		printk(DRIVER_NAME "[?]: set_use_dec called with no context\n");
+		return;
+	}
+	dprintk(DRIVER_NAME "[%d]: set use dec\n", ir->devnum);
+
+	if (ir->flags.connected) {
+		IRLOCK;
+		ir->flags.connected = 0;
+		IRUNLOCK;
+	}
+}
+
+static void send_packet_to_lirc(struct irctl *ir)
+{
+	if (ir->lircdata != 0) {
+		lirc_buffer_write_1(ir->p->rbuf,
+				    (unsigned char *) &ir->lircdata);
+		wake_up(&ir->p->rbuf->wait_poll);
+		ir->lircdata = 0;
+	}
+}
+
+static void usb_remote_recv(struct urb *urb, struct pt_regs *regs)
+{
+	struct irctl *ir;
+	int buf_len, packet_len;
+	int i, j;
+
+	if (!urb)
+		return;
+
+	ir = urb->context;
+	if (!ir) {
+		usb_unlink_urb(urb);
+		return;
+	}
+
+	buf_len = urb->actual_length;
+	packet_len = 0;
+
+	if (debug)
+		usb_remote_printdata(ir, urb->transfer_buffer, buf_len);
+
+	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);
+	}
+
+	switch (urb->status) {
+	/* success */
+	case SUCCESS:
+		for (i = 0; i < buf_len; i++) {
+			/* decode mce packets of the form (84),AA,BB,CC,DD */
+			switch (ir->buf_in[i]) {
+
+			/* data headers */
+			case 0x90: /* used Pinnacle Remote Kit */
+			case 0x8F:
+			case 0x8E:
+			case 0x8D:
+			case 0x8C:
+			case 0x8B:
+			case 0x8A:
+			case 0x89:
+			case 0x88:
+			case 0x87:
+			case 0x86:
+			case 0x85:
+			case 0x84:
+			case 0x83:
+			case 0x82:
+			case 0x81:
+			case 0x80:
+				/* decode packet data */
+				packet_len = ir->buf_in[i] &
+					MCE_PACKET_LENGTH_MASK;
+				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;
+					}
+
+					/* 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);
+				}
+
+				i += packet_len;
+				break;
+
+				/* status header (0x9F) */
+			case MCE_CONTROL_HEADER:
+				/* 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 */
+
+		/*
+		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);
+
+		*/
+
+				/* end decode loop */
+				i = buf_len;
+				break;
+			default:
+				break;
+			}
+		}
+
+		break;
+
+		/* unlink */
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		usb_unlink_urb(urb);
+		return;
+
+	case -EPIPE:
+	default:
+		break;
+	}
+
+	/* resubmit urb */
+	usb_submit_urb(urb, GFP_ATOMIC);
+}
+
+
+static ssize_t lirc_write(struct file *file, const char *buf,
+			  size_t n, loff_t *ppos)
+{
+	int i, count = 0, cmdcount = 0;
+	struct irctl *ir = NULL;
+	int 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_plugin data for the device */
+	ir = lirc_get_pdata(file);
+	if (!ir && !ir->usb_ep_out)
+		return -EFAULT;
+
+	if (n % sizeof(int))
+		return -EINVAL;
+	count = n / sizeof(int);
+
+	/* 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;
+
+	/* Check if we have room for the empty packet at the end */
+	if (cmdcount >= MCE_CMDBUF_SIZE)
+		return -EINVAL;
+
+	/* All mce commands end with an empty packet (0x80) */
+	cmdbuf[cmdcount++] = 0x80;
+
+	/* Transmit the command to the mce device */
+	request_packet_async(ir, ir->usb_ep_out, cmdbuf,
+			     cmdcount, PHILUSB_OUTBOUND);
+
+	/* 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;
+
+	/* delay with the closest number of ticks */
+	set_current_state(TASK_INTERRUPTIBLE);
+	schedule_timeout(usecs_to_jiffies(signal_duration));
+
+	return n;
+}
+
+static void set_transmitter_mask(struct irctl *ir, unsigned int mask)
+{
+	if (ir->flags.transmitter_mask_inverted)
+		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 irctl *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),
+					     PHILUSB_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 the new carrier to the mce
+				   device */
+				request_packet_async(ir, ir->usb_ep_out,
+						     cmdbuf, sizeof(cmdbuf),
+						     PHILUSB_OUTBOUND);
+				return carrier;
+			}
+		}
+
+		return -EINVAL;
+
+	}
+
+	return carrier;
+}
+
+
+static int lirc_ioctl(struct inode *node, struct file *filep,
+		      unsigned int cmd, unsigned long arg)
+{
+	int result;
+	unsigned int ivalue;
+	unsigned long lvalue;
+	struct irctl *ir = NULL;
+
+	/* Retrieve lirc_plugin 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;
+
+		default: /* Unsupported transmitter mask */
+			return MCE_MAX_CHANNELS;
+		}
+
+		dprintk(DRIVER_NAME ": SET_TRANSMITTERS mask=%d\n", ivalue);
+		break;
+
+	case LIRC_GET_SEND_MODE:
+
+		result = put_user(LIRC_SEND2MODE(LIRC_CAN_SEND_PULSE &
+						 LIRC_CAN_SEND_MASK),
+				  (unsigned long *) arg);
+
+		if (result)
+			return result;
+		break;
+
+	case LIRC_SET_SEND_MODE:
+
+		result = get_user(lvalue, (unsigned long *) arg);
+
+		if (result)
+			return result;
+		if (lvalue != (LIRC_MODE_PULSE&LIRC_CAN_SEND_MASK))
+			return -EINVAL;
+		break;
+
+	case LIRC_SET_SEND_CARRIER:
+
+		result = get_user(ivalue, (unsigned int *) arg);
+		if (result)
+			return result;
+
+		set_send_carrier(ir, ivalue);
+		break;
+
+	default:
+		return -ENOIOCTLCMD;
+	}
+
+	return 0;
+}
+
+static struct file_operations lirc_fops = {
+	.write	= lirc_write,
+};
+
+
+static int usb_remote_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 irctl *ir = NULL;
+	struct lirc_plugin *plugin = 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;
+
+	dprintk(DRIVER_NAME ": usb probe called\n");
+
+	usb_reset_device(dev);
+
+	config = dev->actconfig;
+
+	idesc = intf->cur_altsetting;
+
+	is_pinnacle = usb_match_id(intf, pinnacle_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 ((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 (ep_in == NULL) {
+		dprintk(DRIVER_NAME ": inbound and/or endpoint not found\n");
+		return -ENODEV;
+	}
+
+	devnum = dev->devnum;
+	pipe = usb_rcvintpipe(dev, ep_in->bEndpointAddress);
+	maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
+
+	/* allocate kernel memory */
+	mem_failure = 0;
+	ir = kmalloc(sizeof(struct irctl), GFP_KERNEL);
+	if (!ir) {
+		mem_failure = 1;
+		goto mem_failure_switch;
+	}
+
+	memset(ir, 0, sizeof(struct irctl));
+
+	plugin = kmalloc(sizeof(struct lirc_plugin), GFP_KERNEL);
+	if (!plugin) {
+		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(int), 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;
+	}
+
+	memset(plugin, 0, sizeof(struct lirc_plugin));
+
+	strcpy(plugin->name, DRIVER_NAME " ");
+	plugin->minor = -1;
+	plugin->features = LIRC_CAN_SEND_PULSE |
+		LIRC_CAN_SET_TRANSMITTER_MASK |
+		LIRC_CAN_REC_MODE2 |
+		LIRC_CAN_SET_SEND_CARRIER;
+	plugin->data = ir;
+	plugin->rbuf = rbuf;
+	plugin->set_use_inc = &set_use_inc;
+	plugin->set_use_dec = &set_use_dec;
+	plugin->code_length = sizeof(int) * 8;
+	plugin->ioctl = lirc_ioctl;
+	plugin->fops  = &lirc_fops;
+	plugin->dev   = &dev->dev;
+	plugin->owner = THIS_MODULE;
+
+	mutex_init(&ir->lock);
+	init_waitqueue_head(&ir->wait_out);
+
+	minor = lirc_register_plugin(plugin);
+	if (minor < 0)
+		mem_failure = 9;
+
+mem_failure_switch:
+
+	/* free allocated memory incase of failure */
+	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(plugin);
+	case 2:
+		kfree(ir);
+	case 1:
+		printk(DRIVER_NAME "[%d]: out of memory (code=%d)\n",
+			devnum, mem_failure);
+		return -ENOMEM;
+	}
+
+	plugin->minor = minor;
+	ir->p = plugin;
+	ir->devnum = devnum;
+	ir->usbdev = dev;
+	ir->len_in = maxp;
+	ir->flags.connected = 0;
+	ir->flags.pinnacle = is_pinnacle;
+	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, 63) > 0)
+		strncpy(name, buf, 128);
+	if (dev->descriptor.iProduct
+		&& usb_string(dev, dev->descriptor.iProduct, buf, 63) > 0)
+		snprintf(name, 128, "%s %s", name, 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) usb_remote_recv, ir, ep_in->bInterval);
+
+	/* 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, PHILUSB_INBOUND);
+		request_packet_async(ir, ep_out, pin_init1, sizeof(pin_init1),
+				     PHILUSB_OUTBOUND);
+		request_packet_async(ir, ep_in, NULL, maxp, PHILUSB_INBOUND);
+		request_packet_async(ir, ep_out, pin_init2, sizeof(pin_init2),
+				     PHILUSB_OUTBOUND);
+		request_packet_async(ir, ep_in, NULL, maxp, PHILUSB_INBOUND);
+		request_packet_async(ir, ep_out, pin_init3, sizeof(pin_init3),
+				     PHILUSB_OUTBOUND);
+		/* if we dont issue the correct number of receives
+		 * (PHILUSB_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
+		 * usb_remote_recv routine will handle the control packets OK -
+		 * they start with 0x9f - but the async callback doesnt handle
+		 * ir pulse packets
+		 */
+		request_packet_async(ir, ep_in, NULL, maxp, 0);
+	} else {
+		request_packet_async(ir, ep_in, NULL, maxp, PHILUSB_INBOUND);
+		request_packet_async(ir, ep_in, NULL, maxp, PHILUSB_INBOUND);
+		request_packet_async(ir, ep_out, init1,
+				     sizeof(init1), PHILUSB_OUTBOUND);
+		request_packet_async(ir, ep_in, NULL, maxp, PHILUSB_INBOUND);
+		request_packet_async(ir, ep_out, init2,
+				     sizeof(init2), PHILUSB_OUTBOUND);
+		request_packet_async(ir, ep_in, NULL, maxp, 0);
+	}
+
+	usb_set_intfdata(intf, ir);
+
+	return SUCCESS;
+}
+
+
+static void usb_remote_disconnect(struct usb_interface *intf)
+{
+	struct usb_device *dev = interface_to_usbdev(intf);
+	struct irctl *ir = usb_get_intfdata(intf);
+
+	usb_set_intfdata(intf, NULL);
+
+	if (!ir || !ir->p)
+		return;
+
+	ir->usbdev = NULL;
+	wake_up_all(&ir->wait_out);
+
+	IRLOCK;
+	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);
+	IRUNLOCK;
+
+	unregister_from_lirc(ir);
+}
+
+static int usb_remote_suspend(struct usb_interface *intf, pm_message_t message)
+{
+	struct irctl *ir = usb_get_intfdata(intf);
+	printk(DRIVER_NAME "[%d]: suspend\n", ir->devnum);
+	usb_kill_urb(ir->urb_in);
+	return 0;
+}
+
+static int usb_remote_resume(struct usb_interface *intf)
+{
+	struct irctl *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;
+}
+
+static struct usb_driver usb_remote_driver = {
+	.name =		DRIVER_NAME,
+	.probe =	usb_remote_probe,
+	.disconnect =	usb_remote_disconnect,
+	.suspend =	usb_remote_suspend,
+	.resume =	usb_remote_resume,
+	.id_table =	usb_remote_table
+};
+
+#ifdef MODULE
+static int __init usb_remote_init(void)
+{
+	int i;
+
+	printk(KERN_INFO "\n");
+	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");
+
+	request_module("lirc_dev");
+
+	i = usb_register(&usb_remote_driver);
+	if (i < 0) {
+		printk(DRIVER_NAME ": usb register failed, result = %d\n", i);
+		return -ENODEV;
+	}
+
+	return SUCCESS;
+}
+
+static void __exit usb_remote_exit(void)
+{
+	usb_deregister(&usb_remote_driver);
+}
+
+module_init(usb_remote_init);
+module_exit(usb_remote_exit);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(usb, usb_remote_table);
+
+module_param(debug, bool, 0644);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
+#endif /* MODULE */
-- 
1.6.0.1


^ permalink raw reply related	[flat|nested] 111+ messages in thread

* [PATCH 05/18] lirc driver for i2c-based IR receivers
  2008-09-09  4:05       ` [PATCH 04/18] lirc driver for 2nd-gen and later " Jarod Wilson
@ 2008-09-09  4:05         ` Jarod Wilson
  2008-09-09  4:05           ` [PATCH 06/18] lirc driver for the ATI USB RF remote receiver Jarod Wilson
                             ` (2 more replies)
  2008-09-09 23:30         ` [PATCH 04/18] lirc driver for 2nd-gen and later Media Center Ed. USB IR transceivers Jonathan Corbet
  1 sibling, 3 replies; 111+ messages in thread
From: Jarod Wilson @ 2008-09-09  4:05 UTC (permalink / raw)
  To: linux-kernel
  Cc: Jarod Wilson, Jarod Wilson, Janne Grunau, Christoph Bartelmus

Successfully tested with the IR interface on a Hauppauge PVR-250 and
the flimsly little grey remote that shipped with it.

Signed-off-by: Jarod Wilson <jarod@redhat.com>
Signed-off-by: Janne Grunau <j@jannua.net>
CC: Christoph Bartelmus <lirc@bartelmus.de>
Tested-by: Jarod Wilson <jarod@redhat.com>
---
 drivers/input/lirc/Kconfig    |    8 +
 drivers/input/lirc/Makefile   |    1 +
 drivers/input/lirc/lirc_i2c.c |  639 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 648 insertions(+), 0 deletions(-)
 create mode 100644 drivers/input/lirc/lirc_i2c.c

diff --git a/drivers/input/lirc/Kconfig b/drivers/input/lirc/Kconfig
index 0a0b059..ba30d2e 100644
--- a/drivers/input/lirc/Kconfig
+++ b/drivers/input/lirc/Kconfig
@@ -18,6 +18,14 @@ config LIRC_DEV
 	help
 	  LIRC device loadable module support, required for most LIRC drivers
 
+config LIRC_I2C
+	tristate "I2C Based IR Receivers"
+	default n
+	depends on LIRC_DEV
+	help
+	  Driver for I2C-based IR receivers, such as those commonly
+	  found onboard Hauppauge PVR-150/250/350 video capture cards
+
 config LIRC_MCEUSB
 	tristate "Microsoft Media Center Ed. Receiver, v1"
 	default n
diff --git a/drivers/input/lirc/Makefile b/drivers/input/lirc/Makefile
index f6accb9..99765d9 100644
--- a/drivers/input/lirc/Makefile
+++ b/drivers/input/lirc/Makefile
@@ -6,6 +6,7 @@
 EXTRA_CFLAGS =-DIRCTL_DEV_MAJOR=61 -DLIRC_SERIAL_TRANSMITTER -I$(src)
 
 obj-$(CONFIG_LIRC_DEV)		+= lirc_dev.o
+obj-$(CONFIG_LIRC_I2C)		+= lirc_i2c.o
 obj-$(CONFIG_LIRC_MCEUSB)	+= lirc_mceusb.o
 obj-$(CONFIG_LIRC_MCEUSB2)	+= lirc_mceusb2.o
 obj-$(CONFIG_LIRC_SERIAL)	+= lirc_serial.o
diff --git a/drivers/input/lirc/lirc_i2c.c b/drivers/input/lirc/lirc_i2c.c
new file mode 100644
index 0000000..4714641
--- /dev/null
+++ b/drivers/input/lirc/lirc_i2c.c
@@ -0,0 +1,639 @@
+/*
+ * i2c IR lirc plugin for Hauppauge and Pixelview cards - new 2.3.x i2c stack
+ *
+ * Copyright (c) 2000 Gerd Knorr <kraxel@goldbach.in-berlin.de>
+ * modified for PixelView (BT878P+W/FM) by
+ *      Michal Kochanowicz <mkochano@pld.org.pl>
+ *      Christoph Bartelmus <lirc@bartelmus.de>
+ * modified for KNC ONE TV Station/Anubis Typhoon TView Tuner by
+ *      Ulrich Mueller <ulrich.mueller42@web.de>
+ * modified for Asus TV-Box and Creative/VisionTek BreakOut-Box by
+ *      Stefan Jahn <stefan@lkcc.org>
+ * modified for inclusion into kernel sources by
+ *      Jerome Brock <jbrock@users.sourceforge.net>
+ * modified for Leadtek Winfast PVR2000 by
+ *      Thomas Reitmayr (treitmayr@yahoo.com)
+ * modified for Hauppauge HVR-1300 by
+ *      Jan Frey (jfrey@gmx.de)
+ *
+ * parts are cut&pasted from the old lirc_haup.c driver
+ *
+ *  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>
+#include <linux/module.h>
+#include <linux/kmod.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+
+#include <linux/semaphore.h>
+
+#include "lirc_dev.h"
+
+struct IR {
+	struct lirc_plugin l;
+	struct i2c_client  c;
+	int nextkey;
+	unsigned char b[3];
+	unsigned char bits;
+	unsigned char flag;
+};
+
+/* ----------------------------------------------------------------------- */
+
+#define DEVICE_NAME "lirc_i2c"
+
+/* ----------------------------------------------------------------------- */
+/* insmod parameters						       */
+
+static int debug;	/* debug output */
+static int minor = -1;	/* minor number */
+
+#define dprintk(fmt, args...)						\
+	do {								\
+		if (debug)						\
+			printk(KERN_DEBUG DEVICE_NAME ": " fmt,		\
+			       ## args);				\
+	} while (0)
+
+/* ----------------------------------------------------------------------- */
+
+static inline int reverse(int data, int bits)
+{
+	int i;
+	int c;
+
+	for (c = 0, i = 0; i < bits; i++)
+		c |= ((data & (1<<i)) ? 1 : 0) << (bits-1-i);
+
+	return c;
+}
+
+static int add_to_buf_adap(void *data, struct lirc_buffer *buf)
+{
+	struct IR *ir = data;
+	unsigned char keybuf[4];
+
+	keybuf[0] = 0x00;
+	i2c_master_send(&ir->c, keybuf, 1);
+	/* poll IR chip */
+	if (i2c_master_recv(&ir->c, keybuf, sizeof(keybuf)) != sizeof(keybuf)) {
+		dprintk("read error\n");
+		return -EIO;
+	}
+
+	dprintk("key (0x%02x%02x%02x%02x)\n",
+		keybuf[0], keybuf[1], keybuf[2], keybuf[3]);
+
+	/* key pressed ? */
+	if (keybuf[2] == 0xff)
+		return -ENODATA;
+
+	/* remove repeat bit */
+	keybuf[2] &= 0x7f;
+	keybuf[3] |= 0x80;
+
+	lirc_buffer_write_1(buf, keybuf);
+	return 0;
+}
+
+static int add_to_buf_pcf8574(void *data, struct lirc_buffer *buf)
+{
+	struct IR *ir = data;
+	int rc;
+	unsigned char all, mask;
+	unsigned char key;
+
+	/* compute all valid bits (key code + pressed/release flag) */
+	all = ir->bits | ir->flag;
+
+	/* save IR writable mask bits */
+	mask = i2c_smbus_read_byte(&ir->c) & ~all;
+
+	/* send bit mask */
+	rc = i2c_smbus_write_byte(&ir->c, (0xff & all) | mask);
+
+	/* receive scan code */
+	rc = i2c_smbus_read_byte(&ir->c);
+
+	if (rc == -1) {
+		dprintk("%s read error\n", ir->c.name);
+		return -EIO;
+	}
+
+	/* drop duplicate polls */
+	if (ir->b[0] == (rc & all))
+		return -ENODATA;
+
+	ir->b[0] = rc & all;
+
+	dprintk("%s key 0x%02X %s\n", ir->c.name, rc & ir->bits,
+		(rc & ir->flag) ? "released" : "pressed");
+
+	if (rc & ir->flag) {
+		/* ignore released buttons */
+		return -ENODATA;
+	}
+
+	/* set valid key code */
+	key  = rc & ir->bits;
+	lirc_buffer_write_1(buf, &key);
+	return 0;
+}
+
+/* common for Hauppauge IR receivers */
+static int add_to_buf_haup_common(void *data, struct lirc_buffer *buf,
+		unsigned char *keybuf, int size, int offset)
+{
+	struct IR *ir = data;
+	__u16 code;
+	unsigned char codes[2];
+
+	/* poll IR chip */
+	if (size == i2c_master_recv(&ir->c, keybuf, size)) {
+		ir->b[0] = keybuf[offset];
+		ir->b[1] = keybuf[offset+1];
+		ir->b[2] = keybuf[offset+2];
+		dprintk("key (0x%02x/0x%02x)\n", ir->b[0], ir->b[1]);
+	} else {
+		dprintk("read error\n");
+		/* keep last successfull read buffer */
+	}
+
+	/* key pressed ? */
+	if ((ir->b[0] & 0x80) == 0)
+		return -ENODATA;
+
+	/* look what we have */
+	code = (((__u16)ir->b[0]&0x7f)<<6) | (ir->b[1]>>2);
+
+	codes[0] = (code >> 8) & 0xff;
+	codes[1] = code & 0xff;
+
+	/* return it */
+	lirc_buffer_write_1(buf, codes);
+	return 0;
+}
+
+/* specific for the Hauppauge PVR150 IR receiver */
+static int add_to_buf_haup_pvr150(void *data, struct lirc_buffer *buf)
+{
+	unsigned char keybuf[6];
+	/* fetch 6 bytes, first relevant is at offset 3 */
+	return add_to_buf_haup_common(data, buf, keybuf, 6, 3);
+}
+
+/* used for all Hauppauge IR receivers but the PVR150 */
+static int add_to_buf_haup(void *data, struct lirc_buffer *buf)
+{
+	unsigned char keybuf[3];
+	/* fetch 3 bytes, first relevant is at offset 0 */
+	return add_to_buf_haup_common(data, buf, keybuf, 3, 0);
+}
+
+
+static int add_to_buf_pvr2000(void *data, struct lirc_buffer *buf)
+{
+	struct IR *ir = data;
+	unsigned char key;
+	s32 flags;
+	s32 code;
+
+	/* poll IR chip */
+	flags = i2c_smbus_read_byte_data(&ir->c, 0x10);
+	if (-1 == flags) {
+		dprintk("read error\n");
+		return -ENODATA;
+	}
+	/* key pressed ? */
+	if (0 == (flags & 0x80))
+		return -ENODATA;
+
+	/* read actual key code */
+	code = i2c_smbus_read_byte_data(&ir->c, 0x00);
+	if (-1 == code) {
+		dprintk("read error\n");
+		return -ENODATA;
+	}
+
+	key = code & 0xFF;
+
+	dprintk("IR Key/Flags: (0x%02x/0x%02x)\n", key, flags & 0xFF);
+
+	/* return it */
+	lirc_buffer_write_1(buf, &key);
+	return 0;
+}
+
+static int add_to_buf_pixelview(void *data, struct lirc_buffer *buf)
+{
+	struct IR *ir = data;
+	unsigned char key;
+
+	/* poll IR chip */
+	if (1 != i2c_master_recv(&ir->c, &key, 1)) {
+		dprintk("read error\n");
+		return -1;
+	}
+	dprintk("key %02x\n", key);
+
+	/* return it */
+	lirc_buffer_write_1(buf, &key);
+	return 0;
+}
+
+static int add_to_buf_pv951(void *data, struct lirc_buffer *buf)
+{
+	struct IR *ir = data;
+	unsigned char key;
+	unsigned char codes[4];
+
+	/* poll IR chip */
+	if (1 != i2c_master_recv(&ir->c, &key, 1)) {
+		dprintk("read error\n");
+		return -ENODATA;
+	}
+	/* ignore 0xaa */
+	if (key == 0xaa)
+		return -ENODATA;
+	dprintk("key %02x\n", key);
+
+	codes[0] = 0x61;
+	codes[1] = 0xD6;
+	codes[2] = reverse(key, 8);
+	codes[3] = (~codes[2])&0xff;
+
+	lirc_buffer_write_1(buf, codes);
+	return 0;
+}
+
+static int add_to_buf_knc1(void *data, struct lirc_buffer *buf)
+{
+	static unsigned char last_key = 0xFF;
+	struct IR *ir = data;
+	unsigned char key;
+
+	/* poll IR chip */
+	if (1 != i2c_master_recv(&ir->c, &key, 1)) {
+		dprintk("read error\n");
+		return -ENODATA;
+	}
+
+	/* it seems that 0xFE indicates that a button is still hold
+	   down, while 0xFF indicates that no button is hold
+	   down. 0xFE sequences are sometimes interrupted by 0xFF */
+
+	dprintk("key %02x\n", key);
+
+	if (key == 0xFF)
+		return -ENODATA;
+
+	if (key == 0xFE)
+		key = last_key;
+
+	last_key = key;
+	lirc_buffer_write_1(buf, &key);
+
+	return 0;
+}
+
+static int set_use_inc(void *data)
+{
+	struct IR *ir = data;
+
+	/* lock bttv in memory while /dev/lirc is in use  */
+	i2c_use_client(&ir->c);
+
+	return 0;
+}
+
+static void set_use_dec(void *data)
+{
+	struct IR *ir = data;
+
+	i2c_release_client(&ir->c);
+}
+
+static struct lirc_plugin lirc_template = {
+	.name		= "lirc_i2c",
+	.set_use_inc	= set_use_inc,
+	.set_use_dec	= set_use_dec,
+	.dev		= NULL,
+	.owner		= THIS_MODULE,
+};
+
+/* ----------------------------------------------------------------------- */
+
+static int ir_attach(struct i2c_adapter *adap, int addr,
+		      unsigned short flags, int kind);
+static int ir_detach(struct i2c_client *client);
+static int ir_probe(struct i2c_adapter *adap);
+static int ir_command(struct i2c_client *client, unsigned int cmd, void *arg);
+
+static struct i2c_driver driver = {
+	.driver = {
+		.owner	= THIS_MODULE,
+		.name	= "i2c ir driver",
+	},
+	.attach_adapter	= ir_probe,
+	.detach_client	= ir_detach,
+	.command	= ir_command,
+};
+
+static struct i2c_client client_template = {
+	.name		= "unset",
+	.driver		= &driver
+};
+
+static int ir_attach(struct i2c_adapter *adap, int addr,
+		     unsigned short flags, int kind)
+{
+	struct IR *ir;
+	int err;
+
+	client_template.adapter = adap;
+	client_template.addr = addr;
+
+	ir = kmalloc(sizeof(struct IR), GFP_KERNEL);
+	if (!ir)
+		return -ENOMEM;
+	memcpy(&ir->l, &lirc_template, sizeof(struct lirc_plugin));
+	memcpy(&ir->c, &client_template, sizeof(struct i2c_client));
+
+	ir->c.adapter = adap;
+	ir->c.addr    = addr;
+	i2c_set_clientdata(&ir->c, ir);
+	ir->l.data    = ir;
+	ir->l.minor   = minor;
+	ir->l.sample_rate = 10;
+	ir->nextkey   = -1;
+
+	switch (addr) {
+	case 0x64:
+		strlcpy(ir->c.name, "Pixelview IR", I2C_NAME_SIZE);
+		ir->l.code_length = 8;
+		ir->l.add_to_buf = add_to_buf_pixelview;
+		break;
+	case 0x4b:
+		strlcpy(ir->c.name, "PV951 IR", I2C_NAME_SIZE);
+		ir->l.code_length = 32;
+		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) {
+			/* The PVR150 IR receiver uses the same protocol as
+			 * other Hauppauge cards, but the data flow is
+			 * different, so we need to deal with it by its own. */
+			strlcpy(ir->c.name, "Hauppauge PVR150", I2C_NAME_SIZE);
+		} else /* I2C_HW_B_CX2388x */
+			strlcpy(ir->c.name, "Hauppauge HVR1300", I2C_NAME_SIZE);
+		ir->l.code_length = 13;
+		ir->l.add_to_buf = add_to_buf_haup_pvr150;
+		break;
+	case 0x6b:
+		strlcpy(ir->c.name, "Adaptec IR", I2C_NAME_SIZE);
+		ir->l.code_length = 32;
+		ir->l.add_to_buf = add_to_buf_adap;
+		break;
+	case 0x18:
+	case 0x1a:
+		if (adap->id == I2C_HW_B_BT848 ||
+		    adap->id == I2C_HW_B_CX2341X) {
+			strlcpy(ir->c.name, "Hauppauge IR", I2C_NAME_SIZE);
+			ir->l.code_length = 13;
+			ir->l.add_to_buf = add_to_buf_haup;
+		} else { /* I2C_HW_B_CX2388x */
+			strlcpy(ir->c.name, "Leadtek IR", I2C_NAME_SIZE);
+			ir->l.code_length = 8;
+			ir->l.add_to_buf = add_to_buf_pvr2000;
+		}
+		break;
+	case 0x30:
+		strlcpy(ir->c.name, "KNC ONE IR", I2C_NAME_SIZE);
+		ir->l.code_length = 8;
+		ir->l.add_to_buf = add_to_buf_knc1;
+		break;
+	case 0x21:
+	case 0x23:
+		strlcpy(ir->c.name, "TV-Box IR", I2C_NAME_SIZE);
+		ir->l.code_length = 8;
+		ir->l.add_to_buf = add_to_buf_pcf8574;
+		ir->bits = flags & 0xff;
+		ir->flag = (flags >> 8) & 0xff;
+		break;
+	default:
+		/* shouldn't happen */
+		printk("lirc_i2c: Huh? unknown i2c address (0x%02x)?\n", addr);
+		kfree(ir);
+		return -1;
+	}
+	printk(KERN_INFO "lirc_i2c: chip 0x%x found @ 0x%02x (%s)\n",
+	       adap->id, addr, ir->c.name);
+
+	/* register device */
+	err = i2c_attach_client(&ir->c);
+	if (err) {
+		kfree(ir);
+		return err;
+	}
+	ir->l.minor = lirc_register_plugin(&ir->l);
+
+	return 0;
+}
+
+static int ir_detach(struct i2c_client *client)
+{
+	struct IR *ir = i2c_get_clientdata(client);
+
+	/* unregister device */
+	lirc_unregister_plugin(ir->l.minor);
+	i2c_detach_client(&ir->c);
+
+	/* free memory */
+	kfree(ir);
+	return 0;
+}
+
+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};
+
+	static const int probe_cx88[] = {
+		0x18, /* Leadtek Winfast PVR2000 */
+		0x71, /* Hauppauge HVR-IR */
+		-1};
+
+	struct i2c_client c;
+	char buf;
+	int i, rc;
+
+	if (adap->id == I2C_HW_B_BT848 ||
+	    adap->id == I2C_HW_B_CX2341X) {
+		memset(&c, 0, sizeof(c));
+		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)
+				ir_attach(adap, probe[i], 0, 0);
+		}
+	}
+
+	/* Leadtek Winfast PVR2000 or Hauppauge HVR-1300 */
+	else if (adap->id == I2C_HW_B_CX2388x) {
+		memset(&c, 0, sizeof(c));
+		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)
+				ir_attach(adap, c.addr, 0, 0);
+		}
+	}
+
+	/* Asus TV-Box and Creative/VisionTek BreakOut-Box (PCF8574) */
+	else if (adap->id == 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;
+
+		memset(&c, 0, sizeof(c));
+		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)
+				ir_attach(adap, pcf_probe[i],
+					  bits|(flag<<8), 0);
+		}
+	}
+
+	return 0;
+}
+
+static int ir_command(struct i2c_client *client, unsigned int cmd, void *arg)
+{
+	/* nothing */
+	return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+#ifdef MODULE
+
+int init_module(void)
+{
+	request_module("bttv");
+	request_module("rivatv");
+	request_module("ivtv");
+	request_module("cx8800");
+	i2c_add_driver(&driver);
+	return 0;
+}
+
+void cleanup_module(void)
+{
+	i2c_del_driver(&driver);
+}
+
+MODULE_DESCRIPTION("Infrared receiver driver for Hauppauge and "
+		   "Pixelview cards (i2c stack)");
+MODULE_AUTHOR("Gerd Knorr, Michal Kochanowicz, Christoph Bartelmus, "
+	      "Ulrich Mueller, Stefan Jahn, Jerome Brock");
+MODULE_LICENSE("GPL");
+
+module_param(minor, int, 0444);
+MODULE_PARM_DESC(minor, "Preferred minor device number");
+
+module_param(debug, bool, 0644);
+MODULE_PARM_DESC(debug, "Enable debugging messages");
+
+#endif /* MODULE */
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
-- 
1.6.0.1


^ permalink raw reply related	[flat|nested] 111+ messages in thread

* [PATCH 06/18] lirc driver for the ATI USB RF remote receiver
  2008-09-09  4:05         ` [PATCH 05/18] lirc driver for i2c-based IR receivers Jarod Wilson
@ 2008-09-09  4:05           ` Jarod Wilson
  2008-09-09  4:05             ` [PATCH 07/18] lirc driver for the CommandIR USB Transceiver Jarod Wilson
  2008-09-10  9:58             ` [PATCH 06/18] lirc driver for the ATI USB RF remote receiver Ville Syrjälä
  2008-09-09  4:13           ` [PATCH 05/18] lirc driver for i2c-based IR receivers Jarod Wilson
  2008-09-10 15:42           ` Jonathan Corbet
  2 siblings, 2 replies; 111+ messages in thread
From: Jarod Wilson @ 2008-09-09  4:05 UTC (permalink / raw)
  To: linux-kernel
  Cc: Jarod Wilson, Jarod Wilson, Janne Grunau, Christoph Bartelmus

Signed-off-by: Jarod Wilson <jarod@redhat.com>
Signed-off-by: Janne Grunau <j@jannau.net>
CC: Christoph Bartelmus <lirc@bartelmus.de>
---
 drivers/input/lirc/Kconfig       |    7 +
 drivers/input/lirc/Makefile      |    1 +
 drivers/input/lirc/lirc_atiusb.c | 1321 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 1329 insertions(+), 0 deletions(-)
 create mode 100644 drivers/input/lirc/lirc_atiusb.c

diff --git a/drivers/input/lirc/Kconfig b/drivers/input/lirc/Kconfig
index ba30d2e..e17b39a 100644
--- a/drivers/input/lirc/Kconfig
+++ b/drivers/input/lirc/Kconfig
@@ -18,6 +18,13 @@ config LIRC_DEV
 	help
 	  LIRC device loadable module support, required for most LIRC drivers
 
+config LIRC_ATIUSB
+	tristate "ATI RF USB Receiver support"
+	default n
+	depends on LIRC_DEV
+	help
+	  Driver for the ATI USB RF remote receiver
+
 config LIRC_I2C
 	tristate "I2C Based IR Receivers"
 	default n
diff --git a/drivers/input/lirc/Makefile b/drivers/input/lirc/Makefile
index 99765d9..ba8d445 100644
--- a/drivers/input/lirc/Makefile
+++ b/drivers/input/lirc/Makefile
@@ -6,6 +6,7 @@
 EXTRA_CFLAGS =-DIRCTL_DEV_MAJOR=61 -DLIRC_SERIAL_TRANSMITTER -I$(src)
 
 obj-$(CONFIG_LIRC_DEV)		+= lirc_dev.o
+obj-$(CONFIG_LIRC_ATIUSB)	+= lirc_atiusb.o
 obj-$(CONFIG_LIRC_I2C)		+= lirc_i2c.o
 obj-$(CONFIG_LIRC_MCEUSB)	+= lirc_mceusb.o
 obj-$(CONFIG_LIRC_MCEUSB2)	+= lirc_mceusb2.o
diff --git a/drivers/input/lirc/lirc_atiusb.c b/drivers/input/lirc/lirc_atiusb.c
new file mode 100644
index 0000000..0e07204
--- /dev/null
+++ b/drivers/input/lirc/lirc_atiusb.c
@@ -0,0 +1,1321 @@
+/* lirc_atiusb - USB remote support for LIRC
+ * (currently only supports X10 USB remotes)
+ * (supports ATI Remote Wonder and ATI Remote Wonder II, too)
+ *
+ * Copyright (C) 2003-2004 Paul Miller <pmiller9@users.sourceforge.net>
+ *
+ * This driver was derived from:
+ *   Vladimir Dergachev <volodya@minspring.com>'s 2002
+ *      "USB ATI Remote support" (input device)
+ *   Adrian Dewhurst <sailor-lk@sailorfrag.net>'s 2002
+ *      "USB StreamZap remote driver" (LIRC)
+ *   Artur Lipowski <alipowski@kki.net.pl>'s 2002
+ *      "lirc_dev" and "lirc_gpio" LIRC modules
+ *   Michael Wojciechowski
+ *      initial xbox support
+ *   Vassilis Virvilis <vasvir@iit.demokritos.gr> 2006
+ *      reworked the patch for lirc submission
+ *
+ */
+
+/*
+ * 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>
+
+#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/completion.h>
+#include <linux/uaccess.h>
+#include <linux/usb.h>
+#include <linux/poll.h>
+#include <linux/wait.h>
+#include <linux/list.h>
+
+#include "lirc.h"
+#include "lirc_dev.h"
+
+#define DRIVER_VERSION		"1.71"
+#define DRIVER_AUTHOR		"Paul Miller <pmiller9@users.sourceforge.net>"
+#define DRIVER_DESC		"USB remote driver for LIRC"
+#define DRIVER_NAME		"lirc_atiusb"
+
+#define CODE_LENGTH		(code_length[ir->remote_type])
+#define CODE_MIN_LENGTH		(code_min_length[ir->remote_type])
+#define DECODE_LENGTH		(decode_length[ir->remote_type])
+
+#define RW2_MODENAV_KEYCODE	0x3F
+#define RW2_NULL_MODE		0xFF
+/* Fake (virtual) keycode indicating compass mouse usage */
+#define RW2_MOUSE_KEYCODE	0xFF
+#define RW2_PRESSRELEASE_KEYCODE	0xFE
+
+#define RW2_PRESS_CODE		1
+#define RW2_HOLD_CODE		2
+#define RW2_RELEASE_CODE	0
+
+/* module parameters */
+#ifdef CONFIG_USB_DEBUG
+static int debug = 1;
+#else
+static int debug;
+#endif
+#define dprintk(fmt, args...)					\
+	do {							\
+		if (debug)					\
+			printk(KERN_DEBUG fmt, ## args);	\
+	} while (0)
+
+/* ATI, ATI2, XBOX */
+static const int code_length[] = {5, 3, 6};
+static const int code_min_length[] = {3, 3, 6};
+static const int decode_length[] = {5, 3, 1};
+/* USB_BUFF_LEN must be the maximum value of the code_length array.
+ * It is used for static arrays. */
+#define USB_BUFF_LEN 6
+
+static int mask = 0xFFFF;	/* channel acceptance bit mask */
+static int unique;		/* enable channel-specific codes */
+static int repeat = 10;		/* repeat time in 1/100 sec */
+static int emit_updown;		/* send seperate press/release codes (rw2) */
+static int emit_modekeys; /* send keycodes for aux1-4, pc, and mouse (rw2) */
+static unsigned long repeat_jiffies; /* repeat timeout */
+static int mdeadzone;		/* mouse sensitivity >= 0 */
+static int mgradient = 375;	/* 1000*gradient from cardinal direction */
+
+/* get hi and low bytes of a 16-bits int */
+#define HI(a)			((unsigned char)((a) >> 8))
+#define LO(a)			((unsigned char)((a) & 0xff))
+
+/* lock irctl structure */
+#define IRLOCK			mutex_lock(&ir->lock)
+#define IRUNLOCK		mutex_unlock(&ir->lock)
+
+/* general constants */
+#define SUCCESS			0
+#define SEND_FLAG_IN_PROGRESS	1
+#define SEND_FLAG_COMPLETE	2
+#define FREE_ALL		0xFF
+
+/* endpoints */
+#define EP_KEYS			0
+#define EP_MOUSE		1
+#define EP_MOUSE_ADDR		0x81
+#define EP_KEYS_ADDR		0x82
+
+#define VENDOR_ATI1		0x0bc7
+#define VENDOR_ATI2		0x0471
+#define VENDOR_MS1		0x040b
+#define VENDOR_MS2		0x045e
+#define VENDOR_MS3		0xFFFF
+
+static struct usb_device_id usb_remote_table[] = {
+	/* X10 USB Firecracker Interface */
+	{ USB_DEVICE(VENDOR_ATI1, 0x0002) },
+
+	/* X10 VGA Video Sender */
+	{ USB_DEVICE(VENDOR_ATI1, 0x0003) },
+
+	/* ATI Wireless Remote Receiver */
+	{ USB_DEVICE(VENDOR_ATI1, 0x0004) },
+
+	/* NVIDIA Wireless Remote Receiver */
+	{ USB_DEVICE(VENDOR_ATI1, 0x0005) },
+
+	/* ATI Wireless Remote Receiver */
+	{ USB_DEVICE(VENDOR_ATI1, 0x0006) },
+
+	/* X10 USB Wireless Transceivers */
+	{ USB_DEVICE(VENDOR_ATI1, 0x0007) },
+	{ USB_DEVICE(VENDOR_ATI1, 0x0008) },
+	{ USB_DEVICE(VENDOR_ATI1, 0x0009) },
+	{ USB_DEVICE(VENDOR_ATI1, 0x000A) },
+	{ USB_DEVICE(VENDOR_ATI1, 0x000B) },
+	{ USB_DEVICE(VENDOR_ATI1, 0x000C) },
+	{ USB_DEVICE(VENDOR_ATI1, 0x000D) },
+	{ USB_DEVICE(VENDOR_ATI1, 0x000E) },
+	{ USB_DEVICE(VENDOR_ATI1, 0x000F) },
+
+	/* ATI Remote Wonder 2: Input Device */
+	{ USB_DEVICE(VENDOR_ATI2, 0x0602) },
+
+	/* ATI Remote Wonder 2: Controller (???) */
+	{ USB_DEVICE(VENDOR_ATI2, 0x0603) },
+
+	/* Gamester Xbox DVD Movie Playback Kit IR */
+	{ USB_DEVICE(VENDOR_MS1, 0x6521) },
+
+	/* Microsoft Xbox DVD Movie Playback Kit IR */
+	{ USB_DEVICE(VENDOR_MS2, 0x0284) },
+
+	/* Some chinese manufacterer -- conflicts with the joystick from the
+	 * same manufacterer */
+	{ USB_DEVICE(VENDOR_MS3, 0xFFFF) },
+
+	/* Terminating entry */
+	{ }
+};
+
+
+/* init strings */
+#define USB_OUTLEN		7
+
+static char init1[] = {0x01, 0x00, 0x20, 0x14};
+static char init2[] = {0x01, 0x00, 0x20, 0x14, 0x20, 0x20, 0x20};
+
+struct in_endpt {
+	/* inner link in list of endpoints for the remote specified by ir */
+	struct list_head iep_list_link;
+	struct irctl *ir;
+	struct urb *urb;
+	struct usb_endpoint_descriptor *ep;
+	int type;
+
+	/* buffers and dma */
+	unsigned char *buf;
+	unsigned int len;
+	dma_addr_t dma;
+
+	/* handle repeats */
+	unsigned char old[USB_BUFF_LEN];
+	unsigned long old_jiffies;
+};
+
+struct out_endpt {
+	struct irctl *ir;
+	struct urb *urb;
+	struct usb_endpoint_descriptor *ep;
+
+	/* buffers and dma */
+	unsigned char *buf;
+	dma_addr_t dma;
+
+	/* handle sending (init strings) */
+	int send_flags;
+	wait_queue_head_t wait;
+};
+
+
+/* data structure for each usb remote */
+struct irctl {
+	/* inner link in list of all remotes managed by this module */
+	struct list_head remote_list_link;
+	/* Number of usb interfaces associated with this device */
+	int dev_refcount;
+
+	/* usb */
+	struct usb_device *usbdev;
+	/* Head link to list of all inbound endpoints in this remote */
+	struct list_head iep_listhead;
+	struct out_endpt *out_init;
+	int devnum;
+
+	/* remote type based on usb_device_id tables */
+	enum {
+		ATI1_COMPATIBLE,
+		ATI2_COMPATIBLE,
+		XBOX_COMPATIBLE
+	} remote_type;
+
+	/* rw2 current mode (mirror's remote's state) */
+	int mode;
+
+	/* lirc */
+	struct lirc_plugin *p;
+	int connected;
+
+	/* locking */
+	struct mutex lock;
+};
+
+/* list of all registered devices via the remote_list_link in irctl */
+static struct list_head remote_list;
+
+/* Convenience macros to retrieve a pointer to the surrounding struct from
+ * the given list_head reference within, pointed at by link. */
+#define get_iep_from_link(link) \
+		list_entry((link), struct in_endpt, iep_list_link);
+#define get_irctl_from_link(link) \
+		list_entry((link), struct irctl, remote_list_link);
+
+/* send packet - used to initialize remote */
+static void send_packet(struct out_endpt *oep, u16 cmd, unsigned char *data)
+{
+	struct irctl *ir = oep->ir;
+	DECLARE_WAITQUEUE(wait, current);
+	int timeout = HZ; /* 1 second */
+	unsigned char buf[USB_OUTLEN];
+
+	dprintk(DRIVER_NAME "[%d]: send called (%#x)\n", ir->devnum, cmd);
+
+	IRLOCK;
+	oep->urb->transfer_buffer_length = LO(cmd) + 1;
+	oep->urb->dev = oep->ir->usbdev;
+	oep->send_flags = SEND_FLAG_IN_PROGRESS;
+
+	memcpy(buf+1, data, LO(cmd));
+	buf[0] = HI(cmd);
+	memcpy(oep->buf, buf, LO(cmd)+1);
+
+	set_current_state(TASK_INTERRUPTIBLE);
+	add_wait_queue(&oep->wait, &wait);
+
+	if (usb_submit_urb(oep->urb, GFP_ATOMIC)) {
+		set_current_state(TASK_RUNNING);
+		remove_wait_queue(&oep->wait, &wait);
+		IRUNLOCK;
+		return;
+	}
+	IRUNLOCK;
+
+	while (timeout && (oep->urb->status == -EINPROGRESS)
+	       && !(oep->send_flags & SEND_FLAG_COMPLETE)) {
+		timeout = schedule_timeout(timeout);
+		rmb();
+	}
+
+	dprintk(DRIVER_NAME "[%d]: send complete (%#x)\n", ir->devnum, cmd);
+
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&oep->wait, &wait);
+	usb_unlink_urb(oep->urb);
+}
+
+static int unregister_from_lirc(struct irctl *ir)
+{
+	struct lirc_plugin *p = ir->p;
+	int devnum;
+
+	devnum = ir->devnum;
+	dprintk(DRIVER_NAME "[%d]: unregister from lirc called\n", devnum);
+
+	lirc_unregister_plugin(p->minor);
+
+	printk(DRIVER_NAME "[%d]: usb remote disconnected\n", devnum);
+	return SUCCESS;
+}
+
+
+static int set_use_inc(void *data)
+{
+	struct irctl *ir = data;
+	struct list_head *pos, *n;
+	struct in_endpt *iep;
+	int rtn;
+
+	if (!ir) {
+		printk(DRIVER_NAME "[?]: set_use_inc called with no context\n");
+		return -EIO;
+	}
+	dprintk(DRIVER_NAME "[%d]: set use inc\n", ir->devnum);
+
+	IRLOCK;
+	if (!ir->connected) {
+		if (!ir->usbdev) {
+			IRUNLOCK;
+			dprintk(DRIVER_NAME "[%d]: !ir->usbdev\n", ir->devnum);
+			return -ENOENT;
+		}
+
+		/* Iterate through the inbound endpoints */
+		list_for_each_safe(pos, n, &ir->iep_listhead) {
+			/* extract the current in_endpt */
+			iep = get_iep_from_link(pos);
+			iep->urb->dev = ir->usbdev;
+			dprintk(DRIVER_NAME "[%d]: linking iep 0x%02x (%p)\n",
+				ir->devnum, iep->ep->bEndpointAddress, iep);
+			rtn = usb_submit_urb(iep->urb, GFP_ATOMIC);
+			if (rtn) {
+				printk(DRIVER_NAME "[%d]: open result = %d "
+				       "error submitting urb\n",
+				       ir->devnum, rtn);
+				IRUNLOCK;
+				return -EIO;
+			}
+		}
+		ir->connected = 1;
+	}
+	IRUNLOCK;
+
+	return SUCCESS;
+}
+
+static void set_use_dec(void *data)
+{
+	struct irctl *ir = data;
+	struct list_head *pos, *n;
+	struct in_endpt *iep;
+
+	if (!ir) {
+		printk(DRIVER_NAME "[?]: set_use_dec called with no context\n");
+		return;
+	}
+	dprintk(DRIVER_NAME "[%d]: set use dec\n", ir->devnum);
+
+	IRLOCK;
+	if (ir->connected) {
+		/* Free inbound usb urbs */
+		list_for_each_safe(pos, n, &ir->iep_listhead) {
+			iep = get_iep_from_link(pos);
+			dprintk(DRIVER_NAME "[%d]: unlinking iep 0x%02x (%p)\n",
+				ir->devnum, iep->ep->bEndpointAddress, iep);
+			usb_kill_urb(iep->urb);
+		}
+		ir->connected = 0;
+	}
+	IRUNLOCK;
+}
+
+static void print_data(struct in_endpt *iep, char *buf, int len)
+{
+	const int clen = code_length[iep->ir->remote_type];
+	char codes[clen * 3 + 1];
+	int i;
+
+	if (len <= 0)
+		return;
+
+	for (i = 0; i < len && i < clen; i++)
+		snprintf(codes+i*3, 4, "%02x ", buf[i] & 0xFF);
+	printk(DRIVER_NAME "[%d]: data received %s (ep=0x%x length=%d)\n",
+		iep->ir->devnum, codes, iep->ep->bEndpointAddress, len);
+}
+
+static int code_check_ati1(struct in_endpt *iep, int len)
+{
+	struct irctl *ir = iep->ir;
+	int i, chan;
+
+	/* ATI RW1: some remotes emit both 4 and 5 byte length codes. */
+	/* ATI RW2: emit 3 byte codes */
+	if (len < CODE_MIN_LENGTH || len > CODE_LENGTH)
+		return -1;
+
+	/* *** channel not tested with 4/5-byte Dutch remotes *** */
+	chan = ((iep->buf[len-1]>>4) & 0x0F);
+
+	/* strip channel code */
+	if (!unique) {
+		iep->buf[len-1] &= 0x0F;
+		iep->buf[len-3] -= (chan<<4);
+	}
+
+	if (!((1U<<chan) & mask)) {
+		dprintk(DRIVER_NAME "[%d]: ignore channel %d\n",
+			ir->devnum, chan+1);
+		return -1;
+	}
+	dprintk(DRIVER_NAME "[%d]: accept channel %d\n", ir->devnum, chan+1);
+
+	if (ir->remote_type == ATI1_COMPATIBLE) {
+		for (i = len; i < CODE_LENGTH; i++)
+			iep->buf[i] = 0;
+		/* check for repeats */
+		if (memcmp(iep->old, iep->buf, len) == 0) {
+			if (iep->old_jiffies + repeat_jiffies > jiffies)
+				return -1;
+		} else
+			memcpy(iep->old, iep->buf, CODE_LENGTH);
+		iep->old_jiffies = jiffies;
+	}
+
+	return SUCCESS;
+}
+
+/*
+ * Since the ATI Remote Wonder II has quite a different structure from the
+ * prior version, this function was seperated out to clarify the sanitization
+ * process.
+ *
+ * Here is a summary of the main differences:
+ *
+ * a. The rw2 has no sense of a transmission channel.  But, it does have an
+ *    auxilliary mode state, which is set by the mode buttons Aux1 through
+ *    Aux4 and "PC".  These map respectively to 0-4 in the first byte of the
+ *    recv buffer.  Any subsequent button press sends this mode number as its
+ *    "channel code."  Annoyingly enough, the mode setting buttons all send
+ *    the same key code (0x3f), and can only be distinguished via their mode
+ *    byte.
+ *
+ *    Because of this, old-style "unique"-parameter-enabled channel squashing
+ *    kills the functionality of the aux1-aux4 and PC buttons.  However, to
+ *    not do so would cause each remote key to send a different code depending
+ *    on the active aux.  Further complicating matters, using the mouse norb
+ *    also sends an identical code as would pushing the active aux button.  To
+ *    handle this we need a seperate parameter, like rw2modes, with the
+ *    following values and meanings:
+ *
+ *	0: Don't squash any channel info
+ *	1: Only squash channel data for non-mode setting keys
+ *	2: Ignore aux keypresses, but don't squash channel
+ *	3: Ignore aux keypresses and squash channel data
+ *
+ *    Option 1 may seem useless since the mouse sends the same code, but one
+ *    need only ignore in userspace any press of a mode-setting code that only
+ *    reaffirms the current mode.  The 3rd party lirccd should be able to
+ *    handle this easily enough, but lircd doesn't keep the state necessary
+ *    for this.  TODO We could work around this in the driver by emitting a
+ *    single 02 (press) code for a mode key only if that mode is not currently
+ *    active.
+ *
+ *    Option 2 would be useful for those wanting super configurability,
+ *    offering the ability to program 5 times the number actions based on the
+ *    current mode.
+ *
+ * b. The rw2 has its own built in repeat handling; the keys endpoint
+ *    encodes this in the second byte as 1 for press, 2 for hold, and 0 for
+ *    release.  This is generally much more responsive than lirc's built-in
+ *    timeout handling.
+ *
+ *    The problem is that the remote can send the release-recieve pair
+ *    (0,1) while one is still holding down the same button if the
+ *    transmission is momentarilly interrupted.  (It seems that the receiver
+ *    manages this count instead of the remote.)  By default, this information
+ *    is squashed to 2.
+ *
+ *    In order to expose the built-in repeat code, set the emit_updown
+ *    parameter as described below.
+ *
+ * c. The mouse norb is much more sensitive than on the rw1.  It emulates
+ *    a joystick-like controller with the second byte representing the x-axis
+ *    and the third, the y-axis.  Treated as signed integers, these axes range
+ *    approximately as follows:
+ *
+ *	x: (left) -46 ... 46 (right) (0xd2..0x2e)
+ *	y: (up)   -46 ... 46 (down)  (0xd2..0x2e)
+ *
+ *    NB these values do not correspond to the pressure with which the mouse
+ *    norb is pushed in a given direction, but rather seems to indicate the
+ *    duration for which a given direction is held.
+ *
+ *    These are normalized to 8 cardinal directions for easy configuration via
+ *    lircd.conf.  The normalization can be fined tuned with the mdeadzone and
+ *    mgradient parameters as described below.
+ *
+ * d. The interrupt rate of the mouse vs. the normal keys is different.
+ *
+ *	mouse: ~27Hz (37ms between interrupts)
+ *	keys:  ~10Hz (100ms between interrupts)
+ *
+ *    This means that the normal gap mechanism for lircd won't work as
+ *    expected; is emit_updown>0 if you can get away with it.
+ */
+static int code_check_ati2(struct in_endpt *iep, int len)
+{
+	struct irctl *ir = iep->ir;
+	int mode, i;
+	char *buf = iep->buf;
+
+	if (len != CODE_LENGTH) {
+		dprintk(DRIVER_NAME
+			"[%d]: Huh?  Abnormal length (%d) buffer recieved.\n",
+			ir->devnum, len);
+		return -1;
+	}
+	for (i = len; i < CODE_LENGTH; i++)
+		iep->buf[i] = 0;
+
+	mode = buf[0];
+
+	/* Squash the mode indicator if unique wasn't set non-zero */
+	if (!unique)
+		buf[0] = 0;
+
+	if (iep->ep->bEndpointAddress == EP_KEYS_ADDR) {
+		/* ignore mouse navigation indicator key and
+		 * mode-set (aux) keys */
+		if (buf[2] == RW2_MODENAV_KEYCODE) {
+			if (emit_modekeys >= 2) /* emit raw */
+				buf[0] = mode;
+			else if (emit_modekeys == 1) {
+				/* translate */
+				buf[0] = mode;
+				if (ir->mode != mode) {
+					buf[1] = 0x03;
+					ir->mode = mode;
+					return SUCCESS;
+				}
+			} else {
+				dprintk(DRIVER_NAME
+					"[%d]: ignore dummy code 0x%x "
+					"(ep=0x%x)\n", ir->devnum,
+					buf[2], iep->ep->bEndpointAddress);
+				return -1;
+			}
+		}
+
+		if (buf[1] != 2) {
+			/* handle press/release codes */
+			if (emit_updown == 0) /* ignore */
+				return -1;
+			else if (emit_updown == 1) /* normalize keycode */
+				 buf[2] = RW2_PRESSRELEASE_KEYCODE;
+			/* else emit raw */
+		}
+
+	} else {
+		int x = (signed char)buf[1];
+		int y = (signed char)buf[2];
+		int code = 0x00;
+		int dir_ew, dir_ns;
+
+		buf[2] = RW2_MOUSE_KEYCODE;
+
+		/* sensitivity threshold (use L2norm^2) */
+		if (mdeadzone > (x*x+y*y)) {
+			buf[1] = 0x00;
+			return SUCCESS;
+		}
+
+/* Nybble encoding: xy, 2 is -1 (S or W); 1 (N or E) */
+#define MOUSE_N		0x01
+#define MOUSE_NE	0x11
+#define MOUSE_E		0x10
+#define MOUSE_SE	0x12
+#define MOUSE_S		0x02
+#define MOUSE_SW	0x22
+#define MOUSE_W		0x20
+#define MOUSE_NW	0x21
+
+		/* cardinal leanings: positive x -> E, positive y -> S */
+		dir_ew = (x > 0) ? MOUSE_E : MOUSE_W;
+		dir_ns = (y > 0) ? MOUSE_S : MOUSE_N;
+
+		/* convert coordintes(angle) into compass direction */
+		if (x == 0)
+			code = dir_ns;
+		else if (y == 0)
+			code = dir_ew;
+		else {
+			if (abs(1000*y/x) > mgradient)
+				code = dir_ns;
+			if (abs(1000*x/y) > mgradient)
+				code |= dir_ew;
+		}
+
+		buf[1] = code;
+		dprintk(DRIVER_NAME "[%d]: mouse compass=0x%x %s%s (%d,%d)\n",
+			ir->devnum, code,
+			(code & MOUSE_S ? "S" : (code & MOUSE_N ? "N" : "")),
+			(code & MOUSE_E ? "E" : (code & MOUSE_W ? "W" : "")),
+			x, y);
+	}
+
+	return SUCCESS;
+}
+
+static int code_check_xbox(struct in_endpt *iep, int len)
+{
+	struct irctl *ir = iep->ir;
+	const int clen = CODE_LENGTH;
+
+	if (len != clen) {
+		dprintk(DRIVER_NAME ": We got %d instead of %d bytes from xbox "
+			"ir.. ?\n", len, clen);
+		return -1;
+	}
+
+	/* check for repeats */
+	if (memcmp(iep->old, iep->buf, len) == 0) {
+		if (iep->old_jiffies + repeat_jiffies > jiffies)
+			return -1;
+	} else {
+		/* the third byte of xbox ir packet seems to contain key info
+		 * the last two bytes are.. some kind of clock? */
+		iep->buf[0] = iep->buf[2];
+		memset(iep->buf + 1, 0, len - 1);
+		memcpy(iep->old, iep->buf, len);
+	}
+	iep->old_jiffies = jiffies;
+
+	return SUCCESS;
+}
+
+static void usb_remote_recv(struct urb *urb)
+{
+	struct in_endpt *iep;
+	int len, result = -1;
+
+	if (!urb)
+		return;
+	iep = urb->context;
+	if (!iep) {
+		usb_unlink_urb(urb);
+		return;
+	}
+	if (!iep->ir->usbdev)
+		return;
+
+	len = urb->actual_length;
+	if (debug)
+		print_data(iep, urb->transfer_buffer, len);
+
+	switch (urb->status) {
+
+	/* success */
+	case SUCCESS:
+		switch (iep->ir->remote_type) {
+		case XBOX_COMPATIBLE:
+			result = code_check_xbox(iep, len);
+			break;
+		case ATI2_COMPATIBLE:
+			result = code_check_ati2(iep, len);
+			break;
+		case ATI1_COMPATIBLE:
+		default:
+			result = code_check_ati1(iep, len);
+		}
+		if (result < 0)
+			break;
+		lirc_buffer_write_1(iep->ir->p->rbuf, iep->buf);
+		wake_up(&iep->ir->p->rbuf->wait_poll);
+		break;
+
+	/* unlink */
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		usb_unlink_urb(urb);
+		return;
+
+	case -EPIPE:
+	default:
+		break;
+	}
+
+	/* resubmit urb */
+	usb_submit_urb(urb, GFP_ATOMIC);
+}
+
+static void usb_remote_send(struct urb *urb)
+{
+	struct out_endpt *oep;
+
+	if (!urb)
+		return;
+	oep = urb->context;
+	if (!oep) {
+		usb_unlink_urb(urb);
+		return;
+	}
+	if (!oep->ir->usbdev)
+		return;
+
+	dprintk(DRIVER_NAME "[%d]: usb out called\n", oep->ir->devnum);
+
+	if (urb->status)
+		return;
+
+	oep->send_flags |= SEND_FLAG_COMPLETE;
+	wmb();
+	if (waitqueue_active(&oep->wait))
+		wake_up(&oep->wait);
+}
+
+
+/***************************************************************************
+ * Initialization and removal
+ ***************************************************************************/
+
+/*
+ * Free iep according to mem_failure which specifies a checkpoint into the
+ * initialization sequence for rollback recovery.
+ */
+static void free_in_endpt(struct in_endpt *iep, int mem_failure)
+{
+	struct irctl *ir;
+	dprintk(DRIVER_NAME ": free_in_endpt(%p, %d)\n", iep, mem_failure);
+	if (!iep)
+		return;
+
+	ir = iep->ir;
+	if (!ir) {
+		dprintk(DRIVER_NAME ": free_in_endpt: WARNING! null ir\n");
+		return;
+	}
+	IRLOCK;
+	switch (mem_failure) {
+	case FREE_ALL:
+	case 5:
+		list_del(&iep->iep_list_link);
+		dprintk(DRIVER_NAME "[%d]: free_in_endpt removing ep=0x%0x "
+			"from list\n", ir->devnum, iep->ep->bEndpointAddress);
+	case 4:
+		if (iep->urb) {
+			usb_unlink_urb(iep->urb);
+			usb_free_urb(iep->urb);
+			iep->urb = 0;
+		} else
+			dprintk(DRIVER_NAME "[%d]: free_in_endpt null urb!\n",
+				ir->devnum);
+	case 3:
+		usb_buffer_free(iep->ir->usbdev, iep->len, iep->buf, iep->dma);
+		iep->buf = 0;
+	case 2:
+		kfree(iep);
+	}
+	IRUNLOCK;
+}
+
+/*
+ * Construct a new inbound endpoint for this remote, and add it to the list of
+ * in_epts in ir.
+ */
+static struct in_endpt *new_in_endpt(struct irctl *ir,
+				     struct usb_endpoint_descriptor *ep)
+{
+	struct usb_device *dev = ir->usbdev;
+	struct in_endpt *iep;
+	int pipe, maxp, len, addr;
+	int mem_failure;
+
+	addr = ep->bEndpointAddress;
+	pipe = usb_rcvintpipe(dev, addr);
+	maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
+
+/*	len = (maxp > USB_BUFLEN) ? USB_BUFLEN : maxp;
+ *	len -= (len % CODE_LENGTH); */
+	len = CODE_LENGTH;
+
+	dprintk(DRIVER_NAME "[%d]: acceptable inbound endpoint (0x%x) found "
+		"(maxp=%d len=%d)\n", ir->devnum, addr, maxp, len);
+
+	mem_failure = 0;
+	iep = kmalloc(sizeof(*iep), GFP_KERNEL);
+	if (!iep) {
+		mem_failure = 1;
+		goto new_in_endpt_failure_check;
+	}
+	memset(iep, 0, sizeof(*iep));
+	iep->ir = ir;
+	iep->ep = ep;
+	iep->len = len;
+
+	iep->buf = usb_buffer_alloc(dev, len, GFP_ATOMIC, &iep->dma);
+	if (!iep->buf) {
+		mem_failure = 2;
+		goto new_in_endpt_failure_check;
+	}
+
+	iep->urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!iep->urb)
+		mem_failure = 3;
+
+new_in_endpt_failure_check:
+
+	if (mem_failure) {
+		free_in_endpt(iep, mem_failure);
+		printk(DRIVER_NAME "[%d]: ep=0x%x out of memory (code=%d)\n",
+		       ir->devnum, addr, mem_failure);
+		return NULL;
+	}
+	list_add_tail(&iep->iep_list_link, &ir->iep_listhead);
+	dprintk(DRIVER_NAME "[%d]: adding ep=0x%0x to list\n",
+		ir->devnum, iep->ep->bEndpointAddress);
+	return iep;
+}
+
+static void free_out_endpt(struct out_endpt *oep, int mem_failure)
+{
+	struct irctl *ir;
+	dprintk(DRIVER_NAME ": free_out_endpt(%p, %d)\n", oep, mem_failure);
+	if (!oep)
+		return;
+
+	wake_up_all(&oep->wait);
+
+	ir = oep->ir;
+	if (!ir) {
+		dprintk(DRIVER_NAME ": free_out_endpt: WARNING! null ir\n");
+		return;
+	}
+	IRLOCK;
+	switch (mem_failure) {
+	case FREE_ALL:
+	case 4:
+		if (oep->urb) {
+			usb_unlink_urb(oep->urb);
+			usb_free_urb(oep->urb);
+			oep->urb = 0;
+		} else {
+			dprintk(DRIVER_NAME "[%d]: free_out_endpt: null urb!\n",
+				ir->devnum);
+		}
+	case 3:
+		usb_buffer_free(oep->ir->usbdev, USB_OUTLEN,
+				oep->buf, oep->dma);
+		oep->buf = 0;
+	case 2:
+		kfree(oep);
+	}
+	IRUNLOCK;
+}
+
+static struct out_endpt *new_out_endpt(struct irctl *ir,
+				       struct usb_endpoint_descriptor *ep)
+{
+	struct usb_device *dev = ir->usbdev;
+	struct out_endpt *oep;
+	int mem_failure;
+
+	dprintk(DRIVER_NAME "[%d]: acceptable outbound endpoint (0x%x) found\n",
+		ir->devnum, ep->bEndpointAddress);
+
+	mem_failure = 0;
+	oep = kmalloc(sizeof(*oep), GFP_KERNEL);
+	if (!oep)
+		mem_failure = 1;
+	else {
+		memset(oep, 0, sizeof(*oep));
+		oep->ir = ir;
+		oep->ep = ep;
+		init_waitqueue_head(&oep->wait);
+
+		oep->buf = usb_buffer_alloc(dev, USB_OUTLEN,
+					    GFP_ATOMIC, &oep->dma);
+		if (!oep->buf)
+			mem_failure = 2;
+		else {
+			oep->urb = usb_alloc_urb(0, GFP_KERNEL);
+			if (!oep->urb)
+				mem_failure = 3;
+		}
+	}
+	if (mem_failure) {
+		free_out_endpt(oep, mem_failure);
+		printk(DRIVER_NAME "[%d]: ep=0x%x out of memory (code=%d)\n",
+		       ir->devnum, ep->bEndpointAddress, mem_failure);
+		return NULL;
+	}
+	return oep;
+}
+
+static void free_irctl(struct irctl *ir, int mem_failure)
+{
+	struct list_head *pos, *n;
+	struct in_endpt *in;
+	dprintk(DRIVER_NAME ": free_irctl(%p, %d)\n", ir, mem_failure);
+
+	if (!ir)
+		return;
+
+	list_for_each_safe(pos, n, &ir->iep_listhead) {
+		in = get_iep_from_link(pos);
+		free_in_endpt(in, FREE_ALL);
+	}
+	if (ir->out_init) {
+		free_out_endpt(ir->out_init, FREE_ALL);
+		ir->out_init = NULL;
+	}
+
+	IRLOCK;
+	switch (mem_failure) {
+	case FREE_ALL:
+	case 6:
+		if (!--ir->dev_refcount) {
+			list_del(&ir->remote_list_link);
+			dprintk(DRIVER_NAME "[%d]: free_irctl: removing "
+				"remote from list\n", ir->devnum);
+		} else {
+			dprintk(DRIVER_NAME "[%d]: free_irctl: refcount at %d,"
+				"aborting free_irctl\n",
+				ir->devnum, ir->dev_refcount);
+			IRUNLOCK;
+			return;
+		}
+	case 5:
+	case 4:
+	case 3:
+		if (ir->p) {
+			switch (mem_failure) {
+			case 5:
+				lirc_buffer_free(ir->p->rbuf);
+			case 4:
+				kfree(ir->p->rbuf);
+			case 3:
+				kfree(ir->p);
+			}
+		} else
+			printk(DRIVER_NAME "[%d]: ir->p is a null pointer!\n",
+			       ir->devnum);
+	case 2:
+		IRUNLOCK;
+		kfree(ir);
+		return;
+	}
+	IRUNLOCK;
+}
+
+static struct irctl *new_irctl(struct usb_device *dev)
+{
+	struct irctl *ir;
+	struct lirc_plugin *plugin;
+	int type, devnum, dclen;
+	int mem_failure;
+
+	devnum = dev->devnum;
+
+	/* determine remote type */
+	switch (cpu_to_le16(dev->descriptor.idVendor)) {
+	case VENDOR_ATI1:
+		type = ATI1_COMPATIBLE;
+		break;
+	case VENDOR_ATI2:
+		type = ATI2_COMPATIBLE;
+		break;
+	case VENDOR_MS1:
+	case VENDOR_MS2:
+	case VENDOR_MS3:
+		type = XBOX_COMPATIBLE;
+		break;
+	default:
+		dprintk(DRIVER_NAME "[%d]: unknown type\n", devnum);
+		return NULL;
+	}
+	dprintk(DRIVER_NAME "[%d]: remote type = %d\n", devnum, type);
+
+	/* allocate kernel memory */
+	mem_failure = 0;
+	ir = kmalloc(sizeof(*ir), GFP_KERNEL);
+	if (!ir) {
+		mem_failure = 1;
+		goto new_irctl_failure_check;
+	}
+
+	/* at this stage we cannot use the macro [DE]CODE_LENGTH: ir
+	 * is not yet setup */
+	dclen = decode_length[type];
+	memset(ir, 0, sizeof(*ir));
+	/* add this infrared remote struct to remote_list, keeping track
+	 * of the number of drivers registered. */
+	dprintk(DRIVER_NAME "[%d]: adding remote to list\n", devnum);
+	list_add_tail(&ir->remote_list_link, &remote_list);
+	ir->dev_refcount = 1;
+
+	plugin = kmalloc(sizeof(*plugin), GFP_KERNEL);
+	if (!plugin) {
+		mem_failure = 2;
+		goto new_irctl_failure_check;
+	}
+
+	memset(plugin, 0, sizeof(*plugin));
+	ir->p = plugin;
+	plugin->rbuf = kmalloc(sizeof(*(plugin->rbuf)), GFP_KERNEL);
+	if (!plugin->rbuf) {
+		mem_failure = 3;
+		goto new_irctl_failure_check;
+	}
+
+	if (lirc_buffer_init(plugin->rbuf, dclen, 1)) {
+		mem_failure = 4;
+		goto new_irctl_failure_check;
+	}
+
+	strcpy(plugin->name, DRIVER_NAME " ");
+	plugin->minor = -1;
+	plugin->code_length = dclen * 8;
+	plugin->features = LIRC_CAN_REC_LIRCCODE;
+	plugin->data = ir;
+	plugin->set_use_inc = &set_use_inc;
+	plugin->set_use_dec = &set_use_dec;
+	plugin->dev = &dev->dev;
+	plugin->owner = THIS_MODULE;
+	ir->usbdev = dev;
+	ir->remote_type = type;
+	ir->devnum = devnum;
+	ir->mode = RW2_NULL_MODE;
+
+	mutex_init(&ir->lock);
+	INIT_LIST_HEAD(&ir->iep_listhead);
+
+new_irctl_failure_check:
+
+	if (mem_failure) {
+		free_irctl(ir, mem_failure);
+		printk(DRIVER_NAME "[%d]: out of memory (code=%d)\n",
+		       devnum, mem_failure);
+		return NULL;
+	}
+	return ir;
+}
+
+
+/*
+ * Scan the global list of remotes to see if the device listed is one of them.
+ * If it is, the corresponding irctl is returned, with its dev_refcount
+ * incremented.  Otherwise, returns null.
+ */
+static struct irctl *get_prior_reg_ir(struct usb_device *dev)
+{
+	struct list_head *pos;
+	struct irctl *ir = NULL;
+
+	dprintk(DRIVER_NAME "[%d]: scanning remote_list...\n", dev->devnum);
+	list_for_each(pos, &remote_list) {
+		ir = get_irctl_from_link(pos);
+		if (ir->usbdev != dev) {
+			dprintk(DRIVER_NAME "[%d]: device %d isn't it...",
+				dev->devnum, ir->devnum);
+		    ir = NULL;
+		} else {
+			dprintk(DRIVER_NAME "[%d]: prior instance found.\n",
+				dev->devnum);
+			ir->dev_refcount++;
+			break;
+		}
+	}
+	return ir;
+}
+
+/* If the USB interface has an out endpoint for control (eg, the first Remote
+ * Wonder) send the appropriate initialization packets. */
+static void send_outbound_init(struct irctl *ir)
+{
+	if (ir->out_init) {
+		struct out_endpt *oep = ir->out_init;
+		dprintk(DRIVER_NAME "[%d]: usb_remote_probe: initializing "
+			"outbound ep\n", ir->devnum);
+		usb_fill_int_urb(oep->urb, ir->usbdev,
+			usb_sndintpipe(ir->usbdev, oep->ep->bEndpointAddress),
+			oep->buf, USB_OUTLEN, usb_remote_send,
+			oep, oep->ep->bInterval);
+
+		send_packet(oep, 0x8004, init1);
+		send_packet(oep, 0x8007, init2);
+	}
+}
+
+/* Log driver and usb info */
+static void log_usb_dev_info(struct usb_device *dev)
+{
+	char buf[63], name[128] = "";
+	if (dev->descriptor.iManufacturer
+	    && usb_string(dev, dev->descriptor.iManufacturer, buf, 63) > 0)
+		strncpy(name, buf, 128);
+	if (dev->descriptor.iProduct
+	    && usb_string(dev, dev->descriptor.iProduct, buf, 63) > 0)
+		snprintf(name, 128, "%s %s", name, buf);
+	printk(DRIVER_NAME "[%d]: %s on usb%d:%d\n", dev->devnum, name,
+	       dev->bus->busnum, dev->devnum);
+}
+
+
+static int usb_remote_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;
+	struct in_endpt *iep;
+	struct irctl *ir;
+	int i, type;
+
+	dprintk(DRIVER_NAME "[%d]: usb_remote_probe: dev:%p, intf:%p, id:%p)\n",
+		dev->devnum, dev, intf, id);
+
+	idesc = intf->cur_altsetting;
+
+	/* Check if a usb remote has already been registered for this device */
+	ir = get_prior_reg_ir(dev);
+
+	if (!ir) {
+		ir = new_irctl(dev);
+		if (!ir)
+			return -ENOMEM;
+	}
+	type = ir->remote_type;
+
+	/* step through the endpoints to find first in and first out endpoint
+	 * of type interrupt transfer */
+	for (i = 0; i < idesc->desc.bNumEndpoints; ++i) {
+		ep = &idesc->endpoint[i].desc;
+		dprintk(DRIVER_NAME "[%d]: processing endpoint %d\n",
+			dev->devnum, i);
+		if (((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) ==
+		     USB_DIR_IN) &&
+		     ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
+		      USB_ENDPOINT_XFER_INT)) {
+
+			iep = new_in_endpt(ir, ep);
+			if (iep)
+				usb_fill_int_urb(iep->urb, dev,
+					usb_rcvintpipe(dev,
+						iep->ep->bEndpointAddress),
+					iep->buf, iep->len, usb_remote_recv,
+					iep, iep->ep->bInterval);
+		}
+
+		if (((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) ==
+		     USB_DIR_OUT) &&
+		     ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
+		      USB_ENDPOINT_XFER_INT) &&
+		      (ir->out_init == NULL))
+			ir->out_init = new_out_endpt(ir, ep);
+	}
+	if (list_empty(&ir->iep_listhead)) {
+		printk(DRIVER_NAME "[%d]: inbound endpoint not found\n",
+		       ir->devnum);
+		free_irctl(ir, FREE_ALL);
+		return -ENODEV;
+	}
+	if (ir->dev_refcount == 1) {
+		ir->p->minor = lirc_register_plugin(ir->p);
+		if (ir->p->minor < 0) {
+			free_irctl(ir, FREE_ALL);
+			return -ENODEV;
+		}
+
+		/* Note new driver registration in kernel logs */
+		log_usb_dev_info(dev);
+
+		/* outbound data (initialization) */
+		send_outbound_init(ir);
+	}
+
+	usb_set_intfdata(intf, ir);
+	return SUCCESS;
+}
+
+static void usb_remote_disconnect(struct usb_interface *intf)
+{
+/*	struct usb_device *dev = interface_to_usbdev(intf); */
+	struct irctl *ir = usb_get_intfdata(intf);
+	usb_set_intfdata(intf, NULL);
+
+	dprintk(DRIVER_NAME ": disconnecting remote %d:\n",
+		ir ? ir->devnum : -1);
+	if (!ir || !ir->p)
+		return;
+
+	if (ir->usbdev) {
+		/* Only unregister once */
+		ir->usbdev = NULL;
+		unregister_from_lirc(ir);
+	}
+
+	/* This also removes the current remote from remote_list */
+	free_irctl(ir, FREE_ALL);
+}
+
+static struct usb_driver usb_remote_driver = {
+	.name		= DRIVER_NAME,
+	.probe		= usb_remote_probe,
+	.disconnect	= usb_remote_disconnect,
+	.id_table	= usb_remote_table
+};
+
+static int __init usb_remote_init(void)
+{
+	int i;
+
+	INIT_LIST_HEAD(&remote_list);
+
+	printk(KERN_INFO "\n" DRIVER_NAME ": " DRIVER_DESC " "
+	       DRIVER_VERSION "\n");
+	printk(DRIVER_NAME ": " DRIVER_AUTHOR "\n");
+	dprintk(DRIVER_NAME ": debug mode enabled\n");
+
+#ifdef MODULE
+	request_module("lirc_dev");
+#endif
+
+	repeat_jiffies = repeat*HZ/100;
+
+	i = usb_register(&usb_remote_driver);
+	if (i) {
+		printk(DRIVER_NAME ": usb register failed, result = %d\n", i);
+		return -ENODEV;
+	}
+
+	return SUCCESS;
+}
+
+static void __exit usb_remote_exit(void)
+{
+	usb_deregister(&usb_remote_driver);
+}
+
+#ifdef MODULE
+module_init(usb_remote_init);
+module_exit(usb_remote_exit);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(usb, usb_remote_table);
+
+module_param(debug, bool, 0644);
+MODULE_PARM_DESC(debug, "Debug enabled or not (default: 0)");
+
+module_param(mask, int, 0644);
+MODULE_PARM_DESC(mask, "Set channel acceptance bit mask (default: 0xFFFF)");
+
+module_param(unique, bool, 0644);
+MODULE_PARM_DESC(unique, "Enable channel-specific codes (default: 0)");
+
+module_param(repeat, int, 0644);
+MODULE_PARM_DESC(repeat, "Repeat timeout (1/100 sec) (default: 10)");
+
+module_param(mdeadzone, int, 0644);
+MODULE_PARM_DESC(mdeadzone, "rw2 mouse sensitivity threshold (default: 0)");
+
+/*
+ * Enabling this will cause the built-in Remote Wonder II repeate coding to
+ * not be squashed.  The second byte of the keys output will then be:
+ *
+ *	1 initial press (button down)
+ *	2 holding (button remains pressed)
+ *	0 release (button up)
+ *
+ * By default, the driver emits 2 for both 1 and 2, and emits nothing for 0.
+ * This is good for people having trouble getting their rw2 to send a good
+ * consistent signal to the receiver.
+ *
+ * However, if you have no troubles with the driver outputting up-down pairs
+ * at random points while you're still holding a button, then you can enable
+ * this parameter to get finer grain repeat control out of your remote:
+ *
+ *	1 Emit a single (per-channel) virtual code for all up/down events
+ *	2 Emit the actual rw2 output
+ *
+ * 1 is easier to write lircd configs for; 2 allows full control.
+ */
+module_param(emit_updown, int, 0644);
+MODULE_PARM_DESC(emit_updown, "emit press/release codes (rw2): 0:don't "
+		 "(default), 1:emit 2 codes only, 2:code for each button");
+
+module_param(emit_modekeys, int, 0644);
+MODULE_PARM_DESC(emit_modekeys, "emit keycodes for aux1-aux4, pc, and mouse "
+		 "(rw2): 0:don't (default), 1:emit translated codes: one for "
+		 "mode switch, one for same mode, 2:raw codes");
+
+module_param(mgradient, int, 0644);
+MODULE_PARM_DESC(mgradient, "rw2 mouse: 1000*gradient from E to NE (default: "
+		 "500 => .5 => ~27 degrees)");
+
+#else /* not MODULE */
+subsys_initcall(usb_remote_init);
+
+#endif /* MODULE */
+
-- 
1.6.0.1


^ permalink raw reply related	[flat|nested] 111+ messages in thread

* [PATCH 07/18] lirc driver for the CommandIR USB Transceiver
  2008-09-09  4:05           ` [PATCH 06/18] lirc driver for the ATI USB RF remote receiver Jarod Wilson
@ 2008-09-09  4:05             ` Jarod Wilson
  2008-09-09  4:05               ` [PATCH 08/18] lirc driver for the Soundgraph IMON IR Receivers Jarod Wilson
  2008-09-10 17:09               ` [PATCH 07/18] lirc driver for the CommandIR USB Transceiver Jonathan Corbet
  2008-09-10  9:58             ` [PATCH 06/18] lirc driver for the ATI USB RF remote receiver Ville Syrjälä
  1 sibling, 2 replies; 111+ messages in thread
From: Jarod Wilson @ 2008-09-09  4:05 UTC (permalink / raw)
  To: linux-kernel
  Cc: Jarod Wilson, Jarod Wilson, Janne Grunau, Christoph Bartelmus

Signed-off-by: Jarod Wilson <jarod@redhat.com>
Signed-off-by: Janne Grunau <j@jannau.net>
CC: Christoph Bartelmus <lirc@bartelmus.de>
---
 drivers/input/lirc/Kconfig      |    7 +
 drivers/input/lirc/Makefile     |    1 +
 drivers/input/lirc/commandir.c  |  982 +++++++++++++++++++++++++++++++++++++++
 drivers/input/lirc/commandir.h  |   68 +++
 drivers/input/lirc/lirc_cmdir.c |  596 ++++++++++++++++++++++++
 drivers/input/lirc/lirc_cmdir.h |   25 +
 6 files changed, 1679 insertions(+), 0 deletions(-)
 create mode 100644 drivers/input/lirc/commandir.c
 create mode 100644 drivers/input/lirc/commandir.h
 create mode 100644 drivers/input/lirc/lirc_cmdir.c
 create mode 100644 drivers/input/lirc/lirc_cmdir.h

diff --git a/drivers/input/lirc/Kconfig b/drivers/input/lirc/Kconfig
index e17b39a..67802fe 100644
--- a/drivers/input/lirc/Kconfig
+++ b/drivers/input/lirc/Kconfig
@@ -25,6 +25,13 @@ config LIRC_ATIUSB
 	help
 	  Driver for the ATI USB RF remote receiver
 
+config LIRC_CMDIR
+	tristate "CommandIR USB Transceiver"
+	default n
+	depends on LIRC_DEV
+	help
+	  Driver for the CommandIR USB Transceiver
+
 config LIRC_I2C
 	tristate "I2C Based IR Receivers"
 	default n
diff --git a/drivers/input/lirc/Makefile b/drivers/input/lirc/Makefile
index ba8d445..86df97d 100644
--- a/drivers/input/lirc/Makefile
+++ b/drivers/input/lirc/Makefile
@@ -7,6 +7,7 @@ EXTRA_CFLAGS =-DIRCTL_DEV_MAJOR=61 -DLIRC_SERIAL_TRANSMITTER -I$(src)
 
 obj-$(CONFIG_LIRC_DEV)		+= lirc_dev.o
 obj-$(CONFIG_LIRC_ATIUSB)	+= lirc_atiusb.o
+obj-$(CONFIG_LIRC_CMDIR)	+= lirc_cmdir.o
 obj-$(CONFIG_LIRC_I2C)		+= lirc_i2c.o
 obj-$(CONFIG_LIRC_MCEUSB)	+= lirc_mceusb.o
 obj-$(CONFIG_LIRC_MCEUSB2)	+= lirc_mceusb2.o
diff --git a/drivers/input/lirc/commandir.c b/drivers/input/lirc/commandir.c
new file mode 100644
index 0000000..a05b0d6
--- /dev/null
+++ b/drivers/input/lirc/commandir.c
@@ -0,0 +1,982 @@
+
+/*
+ *
+ *	Hardware Driver for COMMANDIR USB Transceiver
+ *	2005-2007 InnovationOne - Matt Bodkin, Evelyn Yeung
+ *
+ *	Version 1.4.2
+ *	For 2.4.* or 2.6.* kernel versions
+ *	Based on the USB Skeleton driver, versions 0.7 and 2.0
+ *
+ */
+
+#include <linux/version.h>
+#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/usb.h>
+#include <linux/smp_lock.h>
+#include "commandir.h"
+
+#include <linux/kref.h>
+#include <linux/uaccess.h>
+
+#define DRIVER_VERSION "v1.1.2"
+#define DRIVER_AUTHOR "Evelyn Yeung, InnovationOne"
+#define DRIVER_DESC "CommandIR USB Transceiver Driver"
+
+#define USB_CMDIR_VENDOR_ID	0x10c4
+#define USB_CMDIR_PRODUCT_ID	0x0003
+#define USB_CMDIR_MINOR_BASE	192
+
+/* table of devices that work with this driver */
+static struct usb_device_id cmdir_table[] =
+{
+	{ USB_DEVICE(USB_CMDIR_VENDOR_ID, USB_CMDIR_PRODUCT_ID) },
+	{ }			/* Terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, cmdir_table);
+
+/* circular packet queue */
+unsigned char ourbuffers[QUEUELENGTH][64];
+int waitusecs[QUEUELENGTH];
+int ourbufferlengths[QUEUELENGTH];
+int nexttosend;
+int nexttofill;
+int send_status = SEND_IDLE;
+int last_tx_sec;
+int last_tx_usec;
+
+static int curTXFill;
+struct timeval tp;
+
+int debug_commandir;
+
+/* Structure to hold all of our device specific stuff */
+struct usb_skel {
+	struct usb_device *udev; /* the usb device for this device */
+	struct usb_interface *interface; /* the interface for this device */
+	unsigned char *bulk_in_buffer; /* the buffer to receive data */
+	size_t bulk_in_size; /* the size of the receive buffer */
+	__u8 bulk_in_endpointAddr; /* the address of the bulk in endpoint */
+	__u8 bulk_out_endpointAddr; /* the address of the bulk out endpoint */
+	struct kref kref;
+};
+#define to_skel_dev(d) container_of(d, struct usb_skel, kref)
+
+static struct file_operations cmdir_fops = {
+	.read =		cmdir_file_read,
+	.write =	cmdir_file_write,
+	.open =		cmdir_open,
+	.release =	cmdir_release,
+};
+
+/* usb class driver info in order to get a minor number from the usb core,
+ * and to have the device registered with devfs and the driver core */
+static struct usb_class_driver cmdir_class = {
+	.name =		"usb/commandir%d",
+	.fops =		&cmdir_fops,
+	/* .mode =	S_IFCHR | S_IRUSR | S_IWUSR |
+	 *		S_IRGRP | S_IWGRP | S_IROTH, */
+	.minor_base =	USB_CMDIR_MINOR_BASE,
+};
+
+static struct usb_driver cmdir_driver = {
+	.name =		"commandir",
+	.probe =	cmdir_probe,
+	.disconnect =	cmdir_disconnect,
+	.id_table =	cmdir_table,
+};
+
+
+static int lcd_device;
+static int rx_device;
+static int def_device;
+
+#define DEFAULT_TRANSMITTERS 0x0F
+static unsigned int transmitters = DEFAULT_TRANSMITTERS;
+static unsigned int next_transmitters = DEFAULT_TRANSMITTERS;
+
+#define CMDIR_VAR_LEN 68
+static char cmdir_var[] =
+"COMMANDIRx:\n TX Enabled: 1, 2, 3, 4\n RX: commandirx\n LCD: commandirx";
+
+
+static void cmdir_delete(struct kref *kref)
+{
+	struct usb_skel *dev = to_skel_dev(kref);
+
+	usb_put_dev(dev->udev);
+	kfree(dev->bulk_in_buffer);
+	kfree(dev);
+}
+
+static int __init usb_cmdir_init(void)
+{
+	int result;
+
+	/* register this driver with the USB subsystem */
+	result = usb_register(&cmdir_driver);
+
+	if (result)
+		err("usb_register failed. Error number %d", result);
+
+	return result;
+}
+
+static int cmdir_open(struct inode *inode, struct file *file)
+{
+	struct usb_skel *dev;
+	struct usb_interface *interface;
+	int subminor;
+	int retval = 0;
+
+	subminor = iminor(inode);
+	interface = usb_find_interface(&cmdir_driver, subminor);
+	if (!interface) {
+		err("%s - error, can't find device for minor %d",
+		     __func__, subminor);
+		retval = -ENODEV;
+		goto exit;
+	}
+
+	dev = usb_get_intfdata(interface);
+	if (!dev) {
+		retval = -ENODEV;
+		goto exit;
+	}
+
+	kref_get(&dev->kref);	/* increment our usage count for the device */
+	file->private_data = dev; /* save object in file's private structure */
+
+exit:
+	return retval;
+}
+
+static int cmdir_probe(struct usb_interface *interface,
+		       const struct usb_device_id *id)
+{
+	struct usb_skel *dev = NULL;
+	struct usb_host_interface *iface_desc;
+	struct usb_endpoint_descriptor *endpoint;
+	size_t buffer_size;
+
+	int i;
+	int retval = -ENOMEM;
+	int minor;
+
+	/* allocate memory for our device state and initialize it */
+	dev = kmalloc(sizeof(*dev), GFP_KERNEL);
+	if (dev == NULL) {
+		err("Out of memory");
+		goto error;
+	}
+	memset(dev, 0x00, sizeof(*dev));
+	kref_init(&dev->kref);
+	dev->udev = usb_get_dev(interface_to_usbdev(interface));
+	dev->interface = interface;
+
+	/* set up the endpoint information */
+	/* use only the first bulk-in and bulk-out endpoints */
+	iface_desc = interface->cur_altsetting;
+	for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+		endpoint = &iface_desc->endpoint[i].desc;
+
+		if (!dev->bulk_in_endpointAddr &&
+		    (endpoint->bEndpointAddress & USB_DIR_IN) &&
+		    ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+					== USB_ENDPOINT_XFER_BULK)) {
+			/* we found a bulk in endpoint */
+			buffer_size = endpoint->wMaxPacketSize;
+			dev->bulk_in_size = buffer_size;
+			dev->bulk_in_endpointAddr = endpoint->bEndpointAddress;
+			dev->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL);
+			if (!dev->bulk_in_buffer) {
+				err("Could not allocate bulk_in_buffer");
+				goto error;
+			}
+		}
+
+		if (!dev->bulk_out_endpointAddr &&
+		    !(endpoint->bEndpointAddress & USB_DIR_IN) &&
+		    ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+					== USB_ENDPOINT_XFER_BULK)) {
+			/* we found a bulk out endpoint */
+			dev->bulk_out_endpointAddr = endpoint->bEndpointAddress;
+		}
+	}
+	if (!(dev->bulk_in_endpointAddr && dev->bulk_out_endpointAddr)) {
+		err("Could not find both bulk-in and bulk-out endpoints");
+		goto error;
+	}
+
+	/* save our data pointer in this interface device */
+	usb_set_intfdata(interface, dev);
+
+	/* we can register the device now, as it is ready */
+	retval = usb_register_dev(interface, &cmdir_class);
+	if (retval) {
+		/* something prevented us from registering this driver */
+		err("Not able to get a minor for this device.");
+		usb_set_intfdata(interface, NULL);
+		goto error;
+	}
+
+	/* check whether minor already includes base */
+	minor = interface->minor;
+	if (minor >= USB_CMDIR_MINOR_BASE)
+		minor = minor-USB_CMDIR_MINOR_BASE;
+
+	/* let the user know what node this device is now attached to */
+	info("CommandIR USB device now attached to commandir%d", minor);
+
+	reset_cmdir(minor);
+
+	return 0;
+
+error:
+	if (dev)
+		kref_put(&dev->kref, cmdir_delete);
+	return retval;
+}
+
+
+static void cmdir_disconnect(struct usb_interface *interface)
+{
+	struct usb_skel *dev;
+	int minor = interface->minor;
+
+	/* prevent cmdir_open() from racing cmdir_disconnect() */
+	lock_kernel();
+
+	dev = usb_get_intfdata(interface);
+	usb_set_intfdata(interface, NULL);
+
+	/* give back our minor */
+	usb_deregister_dev(interface, &cmdir_class);
+
+	unlock_kernel();
+
+	/* decrement our usage count */
+		kref_put(&dev->kref, cmdir_delete);
+
+	/* check whether minor already includes base */
+	if (minor >= USB_CMDIR_MINOR_BASE)
+		minor = minor-USB_CMDIR_MINOR_BASE;
+
+	info("CommandIR #%d now disconnected", minor);
+
+	/* check if default RX device still exists */
+	if (minor == rx_device) {
+		/* decrement until find next valid device */
+		while (rx_device > 0) {
+			rx_device--;
+			if (cmdir_check(rx_device) == 0)
+				break;
+		}
+		if (minor > 0)
+			info("Active Receiver is on CommandIR #%d", rx_device);
+	}
+}
+
+static int cmdir_release(struct inode *inode, struct file *file)
+{
+	struct usb_skel *dev;
+	int retval = 0;
+
+	dev = (struct usb_skel *)file->private_data;
+	if (dev == NULL)
+		/*dbg(" - object is NULL");*/
+		return -ENODEV;
+
+	/* decrement the count on our device */
+		kref_put(&dev->kref, cmdir_delete);
+	return retval;
+}
+
+static void __exit usb_cmdir_exit(void)
+{
+	/* deregister this driver with the USB subsystem */
+	usb_deregister(&cmdir_driver);
+
+}
+
+static int cmdir_check(int device_num)
+{
+	struct usb_interface *interface;
+
+	interface = usb_find_interface(&cmdir_driver,
+				       USB_CMDIR_MINOR_BASE+device_num);
+	if (!interface) {
+		/* also check without adding base, for devfs */
+		interface = usb_find_interface(&cmdir_driver, rx_device);
+		if (!interface)
+			return -ENODEV;
+	}
+	return 0;
+}
+
+static void init_cmdir_var(int device_num)
+{
+	int i;
+	unsigned int multiplier = 1;
+
+	for (i = 0; i < device_num; i++)
+		multiplier = multiplier*0x10;
+	transmitters |= multiplier * 0x0F;
+	next_transmitters = transmitters;
+	info("commandir%d reset", device_num);
+	return;
+}
+
+static void reset_cmdir(int device_num)
+{
+	unsigned char ctrl_buffer[MCU_CTRL_SIZE];
+	int retval;
+	int i;
+
+	ctrl_buffer[0] = RESET_HEADER;
+	for (i = 1; i < MCU_CTRL_SIZE; i++)
+		ctrl_buffer[i] = 'j';
+	retval = write_core(ctrl_buffer, MCU_CTRL_SIZE, NULL, device_num);
+
+	init_cmdir_var(device_num);
+	print_cmdir(device_num);
+
+	return;
+}
+
+static void update_cmdir_string(int device_num)
+{
+	int next_comma = 0;
+	int next_pos = 25;
+	unsigned int multiplier;
+	int i;
+
+	/* cmdir_var[] = "COMMANDIRx:\n"
+	 * 		 " TX Enabled: 1, 2, 3, 4\n"
+	 * 		 " RX: commandirx\n"
+	 * 		 " LCD: commandirx\n" */
+
+	cmdir_var[9] = ASCII0+device_num;
+	cmdir_var[50] = ASCII0+rx_device;
+	cmdir_var[67] = ASCII0+lcd_device;
+
+	for (i = 25; i < 35; i++)
+		cmdir_var[i] = ' ';
+
+	multiplier = 1;
+	for (i = 0; i < device_num; i++)
+		multiplier = multiplier*0x10;
+
+	if (transmitters & (multiplier*0x01)) {
+		cmdir_var[next_pos] = '1';
+		next_pos += 3;
+		next_comma++;
+	}
+	if (transmitters & (multiplier*0x02)) {
+		cmdir_var[next_pos] = '2';
+		if (next_comma > 0)
+			cmdir_var[next_pos-2] = ',';
+		next_pos += 3;
+		next_comma++;
+	}
+	if (transmitters & (multiplier*0x04)) {
+		cmdir_var[next_pos] = '3';
+		if (next_comma > 0)
+			cmdir_var[next_pos-2] = ',';
+		next_pos += 3;
+		next_comma++;
+	}
+	if (transmitters & (multiplier*0x08)) {
+		cmdir_var[next_pos] = '4';
+		if (next_comma > 0)
+			cmdir_var[next_pos-2] = ',';
+		next_pos += 3;
+		next_comma++;
+	}
+	return;
+}
+
+static void print_cmdir(int device_num)
+{
+	update_cmdir_string(device_num);
+	info("%s", cmdir_var);
+	return;
+}
+
+static ssize_t cmdir_file_read(struct file *file, char *buffer,
+			       size_t count, loff_t *ppos)
+{
+	int retval = 0;
+	int minor = 0;
+	struct usb_skel *dev;
+
+	dev = (struct usb_skel *)file->private_data;
+	minor = dev->interface->minor;
+	if (minor >= USB_CMDIR_MINOR_BASE)
+		minor = minor - USB_CMDIR_MINOR_BASE;
+
+	if (((int)*ppos) == 0) {
+		update_cmdir_string(minor);
+		if (copy_to_user(buffer, cmdir_var, CMDIR_VAR_LEN))
+			retval = -EFAULT;
+		else
+			retval = CMDIR_VAR_LEN;
+		return retval;
+	} else
+		return 0;
+}
+
+/*  Read data from CommandIR  */
+ssize_t cmdir_read(unsigned char *buffer, size_t count)
+{
+	struct usb_skel *dev;
+	int length, retval = 0;
+
+	struct usb_interface *interface;
+	interface = usb_find_interface(&cmdir_driver,
+			USB_CMDIR_MINOR_BASE+rx_device);
+	if (!interface) {
+		/* also check without adding base, for devfs */
+		interface = usb_find_interface(&cmdir_driver, rx_device);
+		if (!interface)
+			return -ENODEV;
+	}
+	dev = usb_get_intfdata(interface);
+	if (!dev)
+		return -ENODEV;
+	retval = usb_bulk_msg(dev->udev, usb_rcvbulkpipe(dev->udev,
+						dev->bulk_in_endpointAddr),
+		 dev->bulk_in_buffer, min(dev->bulk_in_size, count),
+		 &length, HZ*10);
+	if (!retval) {
+		if (!memcpy(buffer, dev->bulk_in_buffer, length))
+			retval = -EFAULT;
+		else {
+			/* current status of the TX buffer */
+			curTXFill = buffer[2];
+			retval = length;
+		}
+	}
+	/* suppress errors */
+	/*
+	else {
+		err("Read from device failed, error %d",retval);
+	}
+	*/
+	/* printk(KERN_INFO "CommandIR Reporting TX buffer at %d bytes. \n",
+	 * 	  curTXFill); */
+	return retval;
+}
+EXPORT_SYMBOL(cmdir_read);
+
+static ssize_t cmdir_file_write(struct file *file, const char *buffer,
+				size_t count, loff_t *ppos)
+{
+	int retval;
+	int i;
+	int equalsign = 0;
+	int changeType = 0;
+	unsigned char ctrl_buffer[MCU_CTRL_SIZE];
+	char *local_buffer;
+	int minor;
+
+	/* set as default - if non-specific error,
+	 * won't keep calling this function */
+	retval = count;
+	local_buffer = kmalloc(count, GFP_KERNEL);
+
+	/* verify that we actually have some data to write */
+	if (count == 0) {
+		err("Write request of 0 bytes");
+		goto exit;
+	}
+	if (count > 64) {
+		err("Input too long");
+		goto exit;
+	}
+
+	/* copy the data from userspace into our local buffer */
+	if (copy_from_user(local_buffer, buffer, count)) {
+		retval = -EFAULT;
+		goto exit;
+	}
+
+	/* parse code */
+	changeType = cNothing;
+	equalsign = 0;
+	for (i = 0; i < MCU_CTRL_SIZE; i++)
+		ctrl_buffer[i] = 'j';
+
+	for (i = 0; i < count; i++) {
+		switch (local_buffer[i]) {
+		case 'X':
+		case 'x':
+			if ((i > 0) && ((local_buffer[i - 1] == 'R')
+			    || (local_buffer[i - 1] == 'r')))
+				changeType = cRX;
+			break;
+		case 'S':
+		case 's':
+			if ((i > 1) && ((local_buffer[i - 1] == 'E')
+			    || (local_buffer[i - 1] == 'e'))) {
+				if ((local_buffer[i-2] == 'R')
+				    || (local_buffer[i-2] == 'r'))
+					changeType = cRESET;
+			}
+			break;
+		case 'L':
+		case 'l':
+			if ((i > 0) && ((local_buffer[i - 1] == 'F')
+			    || (local_buffer[i - 1] == 'f')))
+				changeType = cFLASH;
+			break;
+		case 'C':
+		case 'c':
+			if ((i > 0) && ((local_buffer[i - 1] == 'L')
+			    || (local_buffer[i - 1] == 'l')))
+				changeType = cLCD;
+			break;
+		case '=':
+			if (changeType != cNothing)
+				equalsign = i;
+			break;
+		case '0':
+		case '1':
+		case '2':
+		case '3':
+		case '4':
+		case '5':
+		case '6':
+		case '7':
+			if (equalsign > 0) {
+				minor = local_buffer[i] - ASCII0;
+				switch (changeType) {
+				case cRESET:
+					ctrl_buffer[0] = RESET_HEADER;
+					retval = write_core(ctrl_buffer,
+						MCU_CTRL_SIZE,
+						cmdir_write_bulk_callback,
+						minor);
+					if (retval != MCU_CTRL_SIZE) {
+						if (retval == -ENODEV)
+							err("Device %d "
+							    "unplugged", minor);
+						else
+							err("Error on write to "
+							    "%d", minor);
+						goto exit;
+					} else
+						retval = count;
+					init_cmdir_var(minor);
+					break;
+				case cFLASH:
+					ctrl_buffer[0] = FLASH_HEADER;
+					info("Flashing indicators on device %d",
+					     minor);
+					retval = write_core(ctrl_buffer,
+						MCU_CTRL_SIZE,
+						cmdir_write_bulk_callback,
+						minor);
+					if (retval != MCU_CTRL_SIZE) {
+						if (retval == -ENODEV)
+							err("Device %d "
+							    "unplugged", minor);
+						else
+							err("Error on write to "
+							    "%d", minor);
+						goto exit;
+					} else
+						retval = count;
+					break;
+				case cRX:
+					rx_device = minor;
+					info("Default receiver set to %d",
+					     minor);
+					break;
+				case cLCD:
+					lcd_device = minor;
+					info("commandir: Default LCD set to %d",
+					     minor);
+					break;
+				default:
+					break;
+				}
+			}
+			break;
+		case ',':
+			equalsign = 0;
+			changeType = cNothing;
+			break;
+		default:
+			if ((equalsign > 0) && (local_buffer[i] > 32)) {
+				err("Non-numerical argument");
+				goto exit;
+			}
+			break;
+		}
+	}
+
+	if ((changeType != cNothing) && (equalsign == 0))
+		err("No device specified");
+	if (changeType == cNothing)
+		err("Unknown command");
+
+exit:
+	kfree(local_buffer);
+	return retval;
+}
+
+int cmdir_write(unsigned char *buffer, int count,
+		void *callback_fct, int usecdelay)
+{
+	/* Always add to queue, then send queue number
+	 * no locks
+	 * mbodkin, Sept 8, 2005 */
+	int ret = 0;
+	if (debug_commandir == 1) {
+		do_gettimeofday(&tp);
+		printk(KERN_INFO "cmdir_write at %d\n", (int)tp.tv_usec);
+	}
+	ret = add_cmdir_queue(buffer, count, callback_fct, usecdelay);
+
+	if (ret == -1)  {
+		printk(KERN_INFO "cmdir_write returning 0\n");
+		return 0;
+	}
+	return count;
+
+}
+EXPORT_SYMBOL(cmdir_write);
+
+int add_cmdir_queue(unsigned char *buffer, int count,
+		    void *callback_vct, int usecdelay)
+{
+	int ret = 0;
+	if ((nexttofill + 1) % (QUEUELENGTH - 1) == nexttosend) {
+
+		/* our buffer is full */
+		printk(KERN_INFO "Too many packets backlogged "
+		       "in CommandIR Queue.\n");
+		return -1;
+	}
+	/* go ahead and use this one: */
+	memcpy(ourbuffers[nexttofill], buffer, count);
+	ourbufferlengths[nexttofill] = count;
+	waitusecs[nexttofill] = (usecdelay == 0) ? 10000 : usecdelay;
+	/* printk(KERN_INFO "Adding %d to queue at position %d.\n",
+	 *        count, nexttofill); */
+	nexttofill = (nexttofill + 1) % (QUEUELENGTH - 1);
+	ret = nexttofill;
+	/* if (timer_running == 0) */
+	send_queue(); /* fake it if the timer's not running */
+	return ret;	/* we accepted the full packet */
+
+}
+
+int send_queue()
+{
+	int last_sent = 0;
+	int ret = 0;
+	if (debug_commandir == 1) {
+		do_gettimeofday(&tp);
+		printk(KERN_INFO "Send_queue() at %d\n", (int)tp.tv_usec);
+	}
+	/* initiate the send/callback routine if not already running. */
+	if (send_status == SEND_IDLE) {
+		if (!(nexttofill == nexttosend)) {
+			/* start it up: */
+
+			last_sent = nexttosend - 1;
+			if (last_sent < 0)
+				last_sent = QUEUELENGTH - 1;
+			/* Final check - is it TIME to send this packet yet? */
+			/* if (wait_to_tx(waitusecs[last_sent]) == 0) { */
+			/* always send if there's room,
+			 * otherwise wait until room */
+			if (curTXFill < 190) {
+				if (debug_commandir == 1) {
+					do_gettimeofday(&tp);
+					printk(KERN_INFO "Sending packet data "
+					       "at %d\n", (int)tp.tv_usec);
+				}
+				ret = cmdir_write_queue(ourbuffers[nexttosend],
+				      ourbufferlengths[nexttosend], NULL);
+				if (ret <= 0) {
+					/* send failed - the device is either
+					 * unplugged or full
+					 * nexttosend =
+					 * 	(nexttosend + 1)
+					 * 	% (QUEUELENGTH - 1); */
+					send_status = SEND_IDLE;
+					return 0; /*send_queue(); */
+				} else
+					nexttosend = (nexttosend + 1)
+						     % (QUEUELENGTH - 1);
+				return 1;
+			} else {
+				if (debug_commandir == 1) {
+					do_gettimeofday(&tp);
+					printk(KERN_INFO "Not time to send yet "
+					       "- starting timer at %d.\n",
+					       (int)tp.tv_usec);
+					printk(KERN_INFO "Enabling timer.\n");
+				}
+				return 0; /* doesn't matter anymore */
+			}
+		} else {
+			if (debug_commandir == 1) {
+				do_gettimeofday(&tp);
+				printk(KERN_INFO "No more data to send %d!\n",
+				       (int)tp.tv_usec);
+			}
+			last_tx_sec = 0; /* reset our TX counters */
+			last_tx_usec = 0;
+			return 1; /* nothing more to send! */
+		}
+	} else {
+		if (debug_commandir == 1)
+			/* will try again on the callback */
+			printk(KERN_INFO "Already sending\n");
+		return 1;  /* then the timer shouldn't be running... */
+	}
+	return 0; /* should never get here... */
+}
+
+
+int wait_to_tx(int usecs)
+{
+	/* don't return until last_time + usecs has been reached
+	 * for non-zero last_tx's. */
+	int wait_until_sec = 0, wait_until_usec = 0;
+	int now_sec = 0, now_usec = 0;
+	if (debug_commandir == 1)
+		printk(KERN_INFO "waittotx(%d)\n", usecs);
+	if (usecs == 0)
+		return 0;
+
+	if (!(last_tx_sec == 0 && last_tx_usec == 0)) {
+		/* calculate wait time: */
+		wait_until_sec = last_tx_sec + (usecs / 1000000);
+		wait_until_usec = last_tx_usec + usecs;
+
+		do_gettimeofday(&tp);
+		now_sec = tp.tv_sec;
+		now_usec = tp.tv_usec;
+
+		if (wait_until_usec > 1000000) {
+			/* we've spilled over to the next second. */
+			wait_until_sec++;
+			wait_until_usec -= 1000000;
+			/* printk(KERN_INFO "usec rollover\n"); */
+		}
+		if (debug_commandir == 1)
+			printk(KERN_INFO "Testing for the right second, now = "
+			       "%d %d, wait = %d %d\n",
+			       now_sec, now_usec,
+			       wait_until_sec, wait_until_usec);
+		/* now we are always on the same second. */
+		if (now_sec > wait_until_sec) {
+			if (debug_commandir == 1)
+				printk(KERN_INFO "Setting last_tx_sec to %d.\n",
+				       wait_until_sec);
+			last_tx_sec = wait_until_sec;
+			last_tx_usec = wait_until_usec;
+			return 0;
+		}
+
+		if ((now_sec == wait_until_sec)
+		    && (now_usec > wait_until_usec)) {
+			if (debug_commandir == 1)
+				printk(KERN_INFO "Setting last_tx_sec to %d.\n",
+				       wait_until_sec);
+			last_tx_sec = wait_until_sec;
+			last_tx_usec = wait_until_usec;
+			return 0;
+		}
+		return -1; /* didn't send */
+	}
+
+	do_gettimeofday(&tp);
+	last_tx_usec = tp.tv_usec;
+	last_tx_sec = tp.tv_sec;
+	return 0; /* if there's no last even, go ahead and send */
+}
+
+
+int cmdir_write_queue(unsigned char *buffer, int count, void *callback_fct)
+{
+	int retval = count;
+	static char prev_signal_num;
+	unsigned char next_mask;
+	unsigned int multiplier;
+	int i;
+
+	send_status = SEND_ACTIVE;
+
+	if (count < 2) {
+		err("Not enough bytes (write request of %d bytes)", count);
+		return count;
+	}
+
+	/* check data; decide which device to send to */
+	switch (buffer[0]) {
+	case TX_HEADER:
+	case TX_HEADER_NEW:
+		/* this is LIRC transmit data */
+		if (curTXFill >= 190) {
+			printk(KERN_INFO
+			       "TX buffer too full to send more TX data\n");
+			return 0;
+		}
+		if (next_transmitters != transmitters) {
+			if (buffer[1] != prev_signal_num)
+				/* this is new signal; change transmitter mask*/
+				transmitters = next_transmitters;
+		}
+		prev_signal_num = buffer[1];
+
+		multiplier = 1;
+		for (i = 0; i < MAX_DEVICES; i++) {
+			next_mask = 0;
+			if (transmitters & (0x01*multiplier))
+				next_mask |= TX1_ENABLE;
+			if (transmitters & (0x02*multiplier))
+				next_mask |= TX2_ENABLE;
+			if (transmitters & (0x04*multiplier))
+				next_mask |= TX3_ENABLE;
+			if (transmitters & (0x08*multiplier))
+				next_mask |= TX4_ENABLE;
+
+			if (next_mask > 0) {
+				buffer[1] = next_mask;
+				retval = write_core(buffer, count,
+					 callback_fct, i);
+				if (retval != count) {
+					if (retval == -ENODEV)
+						err("Device %d not plugged in",
+						    i);
+					else
+						err("Write error to device %d",
+						    i);
+					return retval;
+				}
+			}
+			multiplier = multiplier*0x10;
+		}
+		return retval;
+		break;
+	case LCD_HEADER:
+		return write_core(buffer, count, callback_fct, lcd_device);
+		break;
+	default:
+		return write_core(buffer, count, callback_fct, def_device);
+		break;
+	}
+	/* should never get here */
+	return retval;
+
+}
+
+int write_core(unsigned char *buffer, int count,
+	       void *callback_fct, int device_num)
+{
+	struct usb_skel *dev;
+	int retval = count;
+
+	struct usb_interface *interface;
+	struct urb *urb = NULL;
+	char *buf = NULL;
+	interface = usb_find_interface(&cmdir_driver,
+			USB_CMDIR_MINOR_BASE + device_num);
+	if (!interface) {
+		/* also check without adding base, for devfs */
+		interface = usb_find_interface(&cmdir_driver, device_num);
+		if (!interface)
+			return -ENODEV;
+	}
+	dev = usb_get_intfdata(interface);
+	if (!dev)
+		return -ENODEV;
+	/* create a urb, and a buffer for it, and copy the data to the urb */
+	urb = usb_alloc_urb(0, GFP_ATOMIC);	/* Now -=Atomic=- */
+	if (!urb) {
+		retval = -ENOMEM;
+		goto error;
+	}
+	buf = usb_buffer_alloc(dev->udev, count,
+		GFP_KERNEL, &urb->transfer_dma);
+	if (!buf) {
+		retval = -ENOMEM;
+		goto error;
+	}
+	if (!memcpy(buf, buffer, count)) {
+		retval = -EFAULT;
+		goto error;
+	}
+	/* initialize the urb properly */
+	if (callback_fct == NULL) {
+		usb_fill_bulk_urb(urb, dev->udev,
+			  usb_sndbulkpipe(dev->udev,
+				dev->bulk_out_endpointAddr),
+			  buf, count, (void *) cmdir_write_bulk_callback, dev);
+	} else {
+		usb_fill_bulk_urb(urb, dev->udev,
+			  usb_sndbulkpipe(dev->udev,
+				dev->bulk_out_endpointAddr),
+			  buf, count, callback_fct, dev);
+	}
+	urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;  /* double check this */
+
+	/* send the data out the bulk port */
+	retval = usb_submit_urb(urb, GFP_KERNEL);
+	if (retval) {
+		err("%s - failed submitting write urb, error %d",
+		    __func__, retval);
+		goto error;
+	}
+
+	/* release our reference to this urb, the USB
+	 * core will eventually free it entirely */
+	usb_free_urb(urb);
+	return count;
+
+error:
+	usb_buffer_free(dev->udev, count, buf, urb->transfer_dma);
+	usb_free_urb(urb);
+	return retval;
+}
+
+static void cmdir_write_bulk_callback(struct urb *urb, struct pt_regs *regs)
+{
+	struct usb_skel *dev;
+	dev = (struct usb_skel *)urb->context;
+	send_status = SEND_IDLE;
+	if (debug_commandir == 1)
+		printk(KERN_INFO "callback()\n");
+	/* free up our allocated buffer */
+
+	usb_buffer_free(urb->dev, urb->transfer_buffer_length,
+			urb->transfer_buffer, urb->transfer_dma);
+	send_queue(); /* send the next packet */
+
+}
+
+int set_tx_channels(unsigned int next_tx)
+{
+	next_transmitters = next_tx;
+	return 0;
+}
+EXPORT_SYMBOL(set_tx_channels);
+
+module_init(usb_cmdir_init);
+module_exit(usb_cmdir_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/lirc/commandir.h b/drivers/input/lirc/commandir.h
new file mode 100644
index 0000000..bed703d
--- /dev/null
+++ b/drivers/input/lirc/commandir.h
@@ -0,0 +1,68 @@
+/*
+ *  commandir.h
+ */
+
+#define ASCII0      48
+
+/* transmitter channel control */
+#define MAX_DEVICES      8
+#define MAX_CHANNELS     32
+#define TX1_ENABLE       0x80
+#define TX2_ENABLE       0x40
+#define TX3_ENABLE       0x20
+#define TX4_ENABLE       0x10
+
+/* command types */
+#define cNothing	0
+#define cRESET		1
+#define cFLASH		2
+#define cLCD		3
+#define cRX		4
+
+/* CommandIR control codes */
+#define MCU_CTRL_SIZE   3
+#define FREQ_HEADER     2
+#define RESET_HEADER    3
+#define FLASH_HEADER    4
+#define LCD_HEADER      5
+#define TX_HEADER       7
+#define TX_HEADER_NEW   8
+
+/* Queue buffering constants */
+#define SEND_IDLE	0
+#define SEND_ACTIVE	1
+
+#define QUEUELENGTH	256
+
+extern int cmdir_write(unsigned char *buffer, int count,
+		       void *callback_fct, int u);
+extern ssize_t cmdir_read(unsigned char *buffer, size_t count);
+extern int set_tx_channels(unsigned int next_tx);
+
+
+static int cmdir_open(struct inode *inode, struct file *file);
+static int cmdir_probe(struct usb_interface *interface,
+		       const struct usb_device_id *id);
+static void cmdir_disconnect(struct usb_interface *interface);
+static int cmdir_release(struct inode *inode, struct file *file);
+static int cmdir_check(int device_num);
+static void init_cmdir_var(int device_num);
+static void reset_cmdir(int device_num);
+static void update_cmdir_string(int device_num);
+static void print_cmdir(int device_num);
+static ssize_t cmdir_file_read(struct file *file, char *buffer,
+			       size_t count, loff_t *ppos);
+ssize_t cmdir_read(unsigned char *buffer, size_t count);
+static ssize_t cmdir_file_write(struct file *file, const char *buffer,
+				size_t count, loff_t *ppos);
+int cmdir_write(unsigned char *buffer, int count, void *callback_fct, int u);
+int write_core(unsigned char *buffer, int count,
+	       void *callback_fct, int device_num);
+static void cmdir_write_bulk_callback(struct urb *urb, struct pt_regs *regs);
+int set_tx_channels(unsigned int next_tx);
+
+int add_cmdir_queue(unsigned char *buffer, int count,
+		    void *callback_vct, int usecdelay);
+int cmdir_write_queue(unsigned char *buffer, int count, void *callback_vct);
+int send_queue(void);
+int wait_to_tx(int usecs);
diff --git a/drivers/input/lirc/lirc_cmdir.c b/drivers/input/lirc/lirc_cmdir.c
new file mode 100644
index 0000000..1faa8e3
--- /dev/null
+++ b/drivers/input/lirc/lirc_cmdir.c
@@ -0,0 +1,596 @@
+/*
+ * lirc_cmdir.c - Driver for InnovationOne's COMMANDIR USB Transceiver
+ *
+ *  This driver requires the COMMANDIR hardware driver, available at
+ *  http://www.commandir.com/.
+ *
+ *  Copyright (C) 2005  InnovationOne - Evelyn Yeung
+ *
+ *  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>
+
+#include <linux/autoconf.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/time.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+#include <linux/poll.h>
+#include "lirc.h"
+#include "lirc_dev.h"
+#include "lirc_cmdir.h"
+
+static int debug;
+#define dprintk(fmt, args...)					\
+	do {							\
+		if (debug)					\
+			printk(KERN_DEBUG fmt, ## args);	\
+	} while (0)
+
+struct lirc_cmdir {
+	int features;
+};
+
+struct lirc_cmdir hardware = {
+	(
+	/* LIRC_CAN_SET_SEND_DUTY_CYCLE|   */
+	LIRC_CAN_SET_SEND_CARRIER|
+	LIRC_CAN_SEND_PULSE|
+	LIRC_CAN_SET_TRANSMITTER_MASK|
+	LIRC_CAN_REC_MODE2)
+	,
+};
+
+#define LIRC_DRIVER_NAME "lirc_cmdir"
+#define RBUF_LEN   256
+#define WBUF_LEN   256
+#define MAX_PACKET 64
+
+static struct lirc_buffer rbuf;
+static int wbuf[WBUF_LEN];
+static unsigned char cmdir_char[4*WBUF_LEN];
+static unsigned char write_control[MCU_CTRL_SIZE];
+static unsigned int last_mc_time;
+static int usb_status = ON;
+static unsigned char signal_num;
+char timerval;
+
+unsigned int freq = 38000;
+/* unsigned int duty_cycle = 50; */
+
+
+#ifndef MAX_UDELAY_MS
+#define MAX_UDELAY_US 5000
+#else
+#define MAX_UDELAY_US (MAX_UDELAY_MS*1000)
+#endif
+
+static inline void safe_udelay(unsigned long usecs)
+{
+	while (usecs > MAX_UDELAY_US) {
+		udelay(MAX_UDELAY_US);
+		usecs -= MAX_UDELAY_US;
+	}
+	udelay(usecs);
+}
+
+static unsigned int get_time_value(unsigned int firstint,
+			unsigned int secondint, unsigned char overflow)
+{	/* get difference between two timestamps from MCU */
+	unsigned int t_answer = 0;
+
+	if (secondint > firstint) {
+		t_answer = secondint - firstint + overflow*65536;
+	} else {
+		if (overflow > 0)
+			t_answer = (65536 - firstint) + secondint +
+						(overflow - 1) * 65536;
+		else
+			t_answer = (65536 - firstint) + secondint;
+	}
+
+	/* clamp to long signal  */
+	if (t_answer > 16000000)
+		t_answer = PULSE_MASK;
+
+	return t_answer;
+}
+
+
+static int set_use_inc(void *data)
+{
+	/* Init read buffer. */
+	if (lirc_buffer_init(&rbuf, sizeof(int), RBUF_LEN) < 0)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static void set_use_dec(void *data)
+{
+	lirc_buffer_free(&rbuf);
+}
+
+
+static void usb_error_handle(int retval)
+{
+	switch (retval) {
+	case -ENODEV:
+		/* device has been unplugged */
+		if (usb_status == ON) {
+			usb_status = OFF;
+			printk(LIRC_DRIVER_NAME ": device is unplugged\n");
+		}
+		break;
+	default:
+		printk(LIRC_DRIVER_NAME ": usb error = %d\n", retval);
+		break;
+	}
+}
+
+static int write_to_usb(unsigned char *buffer, int count, int time_elapsed)
+{
+	int write_return;
+
+	write_return = cmdir_write(buffer, count, NULL, time_elapsed);
+	if (write_return != count) {
+		usb_error_handle(write_return);
+	} else {
+		if (usb_status == OFF) {
+			printk(LIRC_DRIVER_NAME ": device is now plugged in\n");
+			usb_status = ON;
+		}
+	}
+	return write_return;
+}
+
+static void set_freq(void)
+{
+	/* float tempfreq = 0.0; */
+	int write_return;
+
+	/*
+	 * Can't use floating point in 2.6 kernel!
+	 * May be some loss of precision
+	 */
+	timerval = (1000000 / freq) / 2;
+	write_control[0] = FREQ_HEADER;
+	write_control[1] = timerval;
+	write_control[2] = 0;
+	write_return = write_to_usb(write_control, MCU_CTRL_SIZE, 0);
+	if (write_return == MCU_CTRL_SIZE)
+		printk(LIRC_DRIVER_NAME ": freq set to %dHz\n", freq);
+	else
+		printk(LIRC_DRIVER_NAME ": freq unchanged\n");
+
+}
+
+static int cmdir_convert_RX(unsigned char *orig_rxbuffer)
+{
+	unsigned char tmp_char_buffer[80];
+	unsigned int tmp_int_buffer[20];
+	unsigned int final_data_buffer[20];
+	unsigned int num_data_values = 0;
+	unsigned char num_data_bytes = 0;
+	unsigned int orig_index = 0;
+	int i;
+
+	for (i = 0; i < 80; i++)
+		tmp_char_buffer[i] = 0;
+	for (i = 0; i < 20; i++)
+		tmp_int_buffer[i] = 0;
+
+	/*
+	 * get number of data bytes that follow the control bytes
+	 * (NOT including them)
+	 */
+	num_data_bytes = orig_rxbuffer[1];
+
+	/* check if num_bytes is multiple of 3; if not, error */
+	if (num_data_bytes % 3 > 0)
+		return -1;
+	if (num_data_bytes > 60)
+		return -3;
+	if (num_data_bytes < 3)
+		return -2;
+
+	/*
+	 * get number of ints to be returned; num_data_bytes does
+	 * NOT include control bytes
+	 */
+	num_data_values = num_data_bytes/3;
+
+	for (i = 0; i < num_data_values; i++) {
+		tmp_char_buffer[i*4] = orig_rxbuffer[(i+1)*3];
+		tmp_char_buffer[i*4+1] = orig_rxbuffer[(i+1)*3+1];
+		tmp_char_buffer[i*4+2] = 0;
+		tmp_char_buffer[i*4+3] = 0;
+	}
+
+	/* convert to int array */
+	memcpy((unsigned char *)tmp_int_buffer, tmp_char_buffer,
+						(num_data_values*4));
+
+	if (orig_rxbuffer[5] < 255) {
+		/* space */
+		final_data_buffer[0] = get_time_value(last_mc_time,
+						tmp_int_buffer[0],
+						orig_rxbuffer[5]);
+	} else { /* is pulse */
+		final_data_buffer[0] = get_time_value(last_mc_time,
+						tmp_int_buffer[0],
+						0);
+		final_data_buffer[0] |= PULSE_BIT;
+	}
+	for (i = 1; i < num_data_values; i++) {
+		/*
+		 * index of orig_rxbuffer that corresponds to
+		 * overflow/pulse/space
+		 */
+		orig_index = (i + 1)*3 + 2;
+		if (orig_rxbuffer[orig_index] < 255) {
+			final_data_buffer[i] =
+				get_time_value(tmp_int_buffer[i - 1],
+					       tmp_int_buffer[i],
+					       orig_rxbuffer[orig_index]);
+		} else {
+			final_data_buffer[i] =
+				get_time_value(tmp_int_buffer[i - 1],
+					       tmp_int_buffer[i],
+					       0);
+			final_data_buffer[i] |= PULSE_BIT;
+		}
+	}
+	last_mc_time = tmp_int_buffer[num_data_values - 1];
+
+	if (lirc_buffer_full(&rbuf)) {
+		printk(KERN_ERR  LIRC_DRIVER_NAME ": lirc_buffer is full\n");
+		return -EOVERFLOW;
+	}
+	lirc_buffer_write_n(&rbuf, (char *)final_data_buffer, num_data_values);
+
+	return 0;
+}
+
+
+static int usb_read_once(void)
+{
+	int read_retval = 0;
+	int conv_retval = 0;
+	unsigned char read_buffer[MAX_PACKET];
+	int i = 0;
+	int tooFull = 5;  /* read up to 5 packets */
+
+	for (i = 0; i < MAX_PACKET; i++)
+		read_buffer[i] = 0;
+
+	while (tooFull--) {
+		read_retval = cmdir_read(read_buffer, MAX_PACKET);
+		/* Loop until we unload the data build-up */
+		if (read_buffer[1] < 60)
+			tooFull = 0;
+		if (!(read_retval == MAX_PACKET)) {
+			if (read_retval == -ENODEV) {
+				if (usb_status == ON) {
+					printk(KERN_ALERT LIRC_DRIVER_NAME
+						": device is unplugged\n");
+					usb_status = OFF;
+				}
+			} else {
+				/* supress errors */
+				printk(KERN_ALERT LIRC_DRIVER_NAME
+					": usb error on read = %d\n",
+					read_retval);
+				return -ENODATA;
+			}
+			dprintk("Error 3\n");
+			return -ENODATA;
+		} else {
+			if (usb_status == OFF) {
+				usb_status = ON;
+				printk(LIRC_DRIVER_NAME
+					": device is now plugged in\n");
+			}
+		}
+
+		if (read_buffer[0] & 0x08) {
+			conv_retval = cmdir_convert_RX(read_buffer);
+			if (conv_retval == 0) {
+				if (!tooFull)
+					return 0;
+				else
+					dprintk("Looping for more data...\n");
+			} else {
+				dprintk("Error 2: %d\n", (int)conv_retval);
+				return -ENODATA;
+			}
+		} else {
+			/* There really is no data in their buffer */
+			dprintk("Empty RX Buffer!\n");
+			return -ENODATA;
+		}
+	}
+	return -1;
+}
+
+int add_to_buf(void *data, struct lirc_buffer *buf)
+{
+	return usb_read_once();
+}
+
+
+static ssize_t lirc_write(struct file *file, const char *buf,
+			 size_t n, loff_t *ppos)
+{
+	int i, count;
+	unsigned int mod_signal_length = 0;
+	unsigned int time_elapse = 0;
+	unsigned int total_time_elapsed = 0;
+	unsigned int num_bytes_already_sent = 0;
+	unsigned int hibyte = 0;
+	unsigned int lobyte = 0;
+	int cmdir_cnt = 0;
+	unsigned int wait_this = 0;
+	struct timeval start_time;
+	struct timeval end_time;
+	unsigned int real_time_elapsed = 0;
+
+	/* save the time we started the write: */
+	do_gettimeofday(&start_time);
+
+	if (n % sizeof(int))
+		return -EINVAL;
+
+	count = n / sizeof(int);
+	if (count > WBUF_LEN || count % 2 == 0)
+		return -EINVAL;
+	if (copy_from_user(wbuf, buf, n))
+		return -EFAULT;
+
+	/*
+	 * the first time we have to flag that this is the start of a new
+	 * signal otherwise COMMANDIR may receive 2 back-to-back pulses &
+	 * invert the signal
+	 */
+	cmdir_char[0] = TX_HEADER_NEW;
+	signal_num++;
+	cmdir_char[1] = signal_num;
+	cmdir_cnt = 2;
+	for (i = 0; i < count; i++) {
+		/* conversion to number of modulation frequency pulse edges */
+		mod_signal_length = wbuf[i] >> 3;
+		/*
+		 * account for minor rounding errors -
+		 * calculate length from this:
+		 */
+		time_elapse += mod_signal_length * timerval;
+
+		hibyte = mod_signal_length / 256;
+		lobyte = mod_signal_length % 256;
+		cmdir_char[cmdir_cnt+1] = lobyte;
+		cmdir_char[cmdir_cnt] = hibyte;
+		cmdir_cnt += 2;
+
+		/* write data to usb if full packet is collected */
+		if (cmdir_cnt % MAX_PACKET == 0) {
+			write_to_usb(cmdir_char, MAX_PACKET,  time_elapse);
+
+			total_time_elapsed += time_elapse;
+
+			num_bytes_already_sent += MAX_PACKET;
+			time_elapse = 0;
+
+			if ((i + 1) < count) {
+				/* still more to send: */
+				cmdir_char[0] =	TX_HEADER;  /* Next Packet */
+				cmdir_char[1] = signal_num;
+				cmdir_cnt = 2; /* reset the count */
+			}
+		}
+	}
+
+	/* send last chunk of data */
+	if (cmdir_cnt > 0) {
+		total_time_elapsed += time_elapse;
+		write_to_usb(cmdir_char, cmdir_cnt, time_elapse);
+	}
+	/* XXX ERS remove all this? */
+	/*
+	 * we need to _manually delay ourselves_ to remain backwards
+	 * compatible with LIRC and prevent our queue buffer from overflowing.
+	 * Queuing in this driver is about instant, and send_start for example
+	 * will fill it up quickly and prevent send_stop from taking immediate
+	 * effect.
+	 */
+	dprintk("Total elapsed time is: %d. \n", total_time_elapsed);
+	do_gettimeofday(&end_time);
+	/*
+	 * udelay for the difference between endtime and
+	 * start + total_time_elapsed
+	 */
+	if (start_time.tv_usec < end_time.tv_usec)
+		real_time_elapsed = (end_time.tv_usec - start_time.tv_usec);
+	else
+		real_time_elapsed = ((end_time.tv_usec +  1000000) -
+							start_time.tv_usec);
+	dprintk("Real time elapsed was %u.\n", real_time_elapsed);
+	if (real_time_elapsed < (total_time_elapsed - 1000))
+		wait_this = total_time_elapsed - real_time_elapsed - 1000;
+
+#if 0 /* enable this for backwards compatibility */
+	safe_udelay(wait_this);
+#endif
+
+	return n;
+}
+
+
+static int lirc_ioctl(struct inode *node, struct file *filep, unsigned int cmd,
+		      unsigned long arg)
+{
+	int result;
+	unsigned long value;
+	unsigned int ivalue;
+	unsigned int multiplier = 1;
+	unsigned int mask = 0;
+	int i;
+
+	switch (cmd) {
+	case LIRC_SET_TRANSMITTER_MASK:
+		if (!(hardware.features&LIRC_CAN_SET_TRANSMITTER_MASK))
+			return -ENOIOCTLCMD;
+		result = get_user(ivalue, (unsigned int *) arg);
+		if (result)
+			return result;
+		for (i = 0; i < MAX_CHANNELS; i++) {
+			multiplier = multiplier * 0x10;
+			mask |= multiplier;
+		}
+		if (ivalue >= mask)
+			return MAX_CHANNELS;
+		set_tx_channels(ivalue);
+		return 0;
+		break;
+
+	case LIRC_GET_SEND_MODE:
+		if (!(hardware.features & LIRC_CAN_SEND_MASK))
+			return -ENOIOCTLCMD;
+
+		result = put_user(LIRC_SEND2MODE
+				(hardware.features & LIRC_CAN_SEND_MASK),
+				(unsigned long *) arg);
+		if (result)
+			return result;
+		break;
+
+	case LIRC_SET_SEND_MODE:
+		if (!(hardware.features&LIRC_CAN_SEND_MASK))
+			return -ENOIOCTLCMD;
+
+		result = get_user(value, (unsigned long *)arg);
+		if (result)
+			return result;
+		break;
+
+	case LIRC_GET_LENGTH:
+		return -ENOSYS;
+		break;
+
+	case LIRC_SET_SEND_DUTY_CYCLE:
+		dprintk(KERN_WARNING LIRC_DRIVER_NAME
+					": SET_SEND_DUTY_CYCLE\n");
+
+		if (!(hardware.features&LIRC_CAN_SET_SEND_DUTY_CYCLE))
+			return -ENOIOCTLCMD;
+
+		result = get_user(ivalue, (unsigned int *)arg);
+		if (result)
+			return result;
+		if (ivalue <= 0 || ivalue > 100)
+			return -EINVAL;
+
+		/* TODO: */
+		dprintk(LIRC_DRIVER_NAME
+			": set_send_duty_cycle not yet supported\n");
+
+		return 0;
+		break;
+
+	case LIRC_SET_SEND_CARRIER:
+		dprintk(KERN_WARNING LIRC_DRIVER_NAME ": SET_SEND_CARRIER\n");
+
+		if (!(hardware.features & LIRC_CAN_SET_SEND_CARRIER))
+			return -ENOIOCTLCMD;
+
+		result = get_user(ivalue, (unsigned int *)arg);
+		if (result)
+			return result;
+		if (ivalue > 500000 || ivalue < 24000)
+			return -EINVAL;
+		if (ivalue != freq) {
+			freq = ivalue;
+			set_freq();
+		}
+		return 0;
+		break;
+
+	default:
+		return -ENOIOCTLCMD;
+	}
+	return 0;
+}
+
+static struct file_operations lirc_fops = {
+	.write		= lirc_write,
+};
+
+static struct lirc_plugin plugin = {
+	.name		= LIRC_DRIVER_NAME,
+	.minor		= -1,
+	.code_length	= 1,
+	.sample_rate	= 20,
+	.data		= NULL,
+	.add_to_buf	= add_to_buf,
+	.get_queue	= NULL,
+	.rbuf		= &rbuf,
+	.set_use_inc	= set_use_inc,
+	.set_use_dec	= set_use_dec,
+	.ioctl		= lirc_ioctl,
+	.fops		= &lirc_fops,
+	.dev		= NULL,
+	.owner		= THIS_MODULE,
+};
+
+#ifdef MODULE
+
+MODULE_AUTHOR("Evelyn Yeung, Matt Bodkin");
+MODULE_DESCRIPTION("InnovationOne driver for "
+		   "CommandIR USB infrared transceiver");
+#ifdef MODULE_LICENSE
+MODULE_LICENSE("GPL");
+#endif
+
+module_param(debug, bool, 0644);
+MODULE_PARM_DESC(debug, "Enable debugging messages");
+
+
+int init_module(void)
+{
+	plugin.features = hardware.features;
+	plugin.minor = lirc_register_plugin(&plugin);
+	if (plugin.minor < 0) {
+		printk(KERN_ERR  LIRC_DRIVER_NAME
+		       ": register_chrdev failed!\n");
+		return -EIO;
+	}
+	set_freq();
+	return 0;
+}
+
+void cleanup_module(void)
+{
+	lirc_unregister_plugin(plugin.minor);
+	printk(KERN_INFO  LIRC_DRIVER_NAME  ": module removed\n");
+}
+
+#endif
+
+
diff --git a/drivers/input/lirc/lirc_cmdir.h b/drivers/input/lirc/lirc_cmdir.h
new file mode 100644
index 0000000..f2400c3
--- /dev/null
+++ b/drivers/input/lirc/lirc_cmdir.h
@@ -0,0 +1,25 @@
+/*
+ *   lirc_cmdir.h
+ */
+
+#ifndef LIRC_CMDIR_H
+#define LIRC_CMDIR_H
+
+#define ON          1
+#define OFF         0
+
+/* transmitter channel control */
+#define MAX_CHANNELS     32
+
+/* CommandIR control codes */
+#define MCU_CTRL_SIZE   3
+#define FREQ_HEADER     2
+#define TX_HEADER       7
+#define TX_HEADER_NEW	8
+
+extern int cmdir_write(unsigned char *buffer, int count,
+		       void *callback_fct, int u);
+extern ssize_t cmdir_read(unsigned char *buffer, size_t count);
+extern int set_tx_channels(unsigned int next_tx);
+
+#endif
-- 
1.6.0.1


^ permalink raw reply related	[flat|nested] 111+ messages in thread

* [PATCH 08/18] lirc driver for the Soundgraph IMON IR Receivers
  2008-09-09  4:05             ` [PATCH 07/18] lirc driver for the CommandIR USB Transceiver Jarod Wilson
@ 2008-09-09  4:05               ` Jarod Wilson
  2008-09-09  4:05                 ` [PATCH 09/18] lirc driver for the Streamzap PC Receiver Jarod Wilson
  2008-09-10 21:02                 ` [PATCH 08/18] lirc driver for the Soundgraph IMON IR Receivers Jonathan Corbet
  2008-09-10 17:09               ` [PATCH 07/18] lirc driver for the CommandIR USB Transceiver Jonathan Corbet
  1 sibling, 2 replies; 111+ messages in thread
From: Jarod Wilson @ 2008-09-09  4:05 UTC (permalink / raw)
  To: linux-kernel
  Cc: Jarod Wilson, Jarod Wilson, Janne Grunau, Christoph Bartelmus

Successfully tested earlier tonight, see:
	https://bugzilla.redhat.com/show_bug.cgi?id=459523

Signed-off-by: Jarod Wilson <jarod@redhat.com>
Signed-off-by: Janne Grunau <j@jannau.net>
CC: Christoph Bartelmus <lirc@bartelmus.de>
Tested-by: Tom Horsley <tom.horsley@att.net>
---
 drivers/input/lirc/Kconfig     |    7 +
 drivers/input/lirc/Makefile    |    1 +
 drivers/input/lirc/lirc_imon.c | 1280 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 1288 insertions(+), 0 deletions(-)
 create mode 100644 drivers/input/lirc/lirc_imon.c

diff --git a/drivers/input/lirc/Kconfig b/drivers/input/lirc/Kconfig
index 67802fe..7e37b2d 100644
--- a/drivers/input/lirc/Kconfig
+++ b/drivers/input/lirc/Kconfig
@@ -40,6 +40,13 @@ config LIRC_I2C
 	  Driver for I2C-based IR receivers, such as those commonly
 	  found onboard Hauppauge PVR-150/250/350 video capture cards
 
+config LIRC_IMON
+	tristate "Soundgraph IMON Receiver"
+	default n
+	depends on LIRC_DEV
+	help
+	  Driver for the Soundgraph IMON IR Receiver
+
 config LIRC_MCEUSB
 	tristate "Microsoft Media Center Ed. Receiver, v1"
 	default n
diff --git a/drivers/input/lirc/Makefile b/drivers/input/lirc/Makefile
index 86df97d..f39fa06 100644
--- a/drivers/input/lirc/Makefile
+++ b/drivers/input/lirc/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_LIRC_DEV)		+= lirc_dev.o
 obj-$(CONFIG_LIRC_ATIUSB)	+= lirc_atiusb.o
 obj-$(CONFIG_LIRC_CMDIR)	+= lirc_cmdir.o
 obj-$(CONFIG_LIRC_I2C)		+= lirc_i2c.o
+obj-$(CONFIG_LIRC_IMON)		+= lirc_imon.o
 obj-$(CONFIG_LIRC_MCEUSB)	+= lirc_mceusb.o
 obj-$(CONFIG_LIRC_MCEUSB2)	+= lirc_mceusb2.o
 obj-$(CONFIG_LIRC_SERIAL)	+= lirc_serial.o
diff --git a/drivers/input/lirc/lirc_imon.c b/drivers/input/lirc/lirc_imon.c
new file mode 100644
index 0000000..bdfd953
--- /dev/null
+++ b/drivers/input/lirc/lirc_imon.c
@@ -0,0 +1,1280 @@
+/*
+ *   lirc_imon.c:  LIRC plugin/VFD driver for Ahanix/Soundgraph IMON IR/VFD
+ *
+ *   Version 0.3
+ *		Supports newer iMON models that send decoded IR signals.
+ *			This includes the iMON PAD model.
+ *		Removed module option for vfd_proto_6p. This driver supports
+ *			multiple iMON devices so it is meaningless to have
+ *			a global option to set protocol variants.
+ *
+ *   Version 0.2 beta 2 [January 31, 2005]
+ *		USB disconnect/reconnect no longer causes problems for lircd
+ *
+ *   Version 0.2 beta 1 [January 29, 2005]
+ *		Added support for original iMON receiver(ext USB)
+ *
+ *   Version 0.2 alpha 2 [January 24, 2005]
+ *		Added support for VFDs with 6-packet protocol
+ *
+ *   Version 0.2 alpha 1 [January 23, 2005]
+ *		Added support for 2.6 kernels
+ *		Reworked disconnect handling
+ *		Incorporated Changwoo Ryu's algorithm
+ *
+ *   Version 0.1 alpha 1[July 5, 2004]
+ *
+ *   Copyright(C) 2004  Venky Raju(dev@venky.ws)
+ *
+ *   lirc_imon 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+#include <linux/version.h>
+#include <linux/autoconf.h>
+
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/usb.h>
+
+#include "lirc.h"
+#include "lirc_dev.h"
+
+
+#define MOD_AUTHOR	"Venky Raju <dev@venky.ws>"
+#define MOD_DESC	"Driver for Soundgraph iMON MultiMedia IR/VFD"
+#define MOD_NAME	"lirc_imon"
+#define MOD_VERSION	"0.3"
+
+#define VFD_MINOR_BASE	144	/* Same as LCD */
+#define DEVICE_NAME	"lcd%d"
+
+#define BUF_CHUNK_SIZE	4
+#define BUF_SIZE	128
+
+#define BIT_DURATION	250	/* each bit received is 250us */
+
+#define SUCCESS		0
+#define	TRUE		1
+#define FALSE		0
+
+
+/* ------------------------------------------------------------
+ *		     P R O T O T Y P E S
+ * ------------------------------------------------------------
+ */
+
+/* USB Callback prototypes */
+static int imon_probe(struct usb_interface *interface,
+		      const struct usb_device_id *id);
+static void imon_disconnect(struct usb_interface *interface);
+static void usb_rx_callback(struct urb *urb);
+static void usb_tx_callback(struct urb *urb);
+
+/* VFD file_operations function prototypes */
+static int vfd_open(struct inode *inode, struct file *file);
+static int vfd_close(struct inode *inode, struct file *file);
+static ssize_t vfd_write(struct file *file, const char *buf,
+			 size_t n_bytes, loff_t *pos);
+
+/* LCD file_operations override function prototypes */
+static ssize_t lcd_write(struct file *file, const char *buf,
+			 size_t n_bytes, loff_t *pos);
+
+/* LIRC plugin function prototypes */
+static int ir_open(void *data);
+static void ir_close(void *data);
+
+/* Driver init/exit prototypes */
+static int __init imon_init(void);
+static void __exit imon_exit(void);
+
+/* ------------------------------------------------------------
+ *		     G L O B A L S
+ * ------------------------------------------------------------
+ */
+
+struct imon_context {
+	struct usb_device *dev;
+	int vfd_supported;		/* not all controllers do */
+	int vfd_isopen;			/* VFD port has been opened */
+	int ir_isopen;			/* IR port open	*/
+	int ir_isassociating;		/* IR port open for association */
+	int dev_present;		/* USB device presence */
+	struct mutex lock;		/* to lock this object */
+	wait_queue_head_t remove_ok;	/* For unexpected USB disconnects */
+
+	int vfd_proto_6p;		/* VFD requires 6th packet */
+	int ir_onboard_decode;		/* IR signals decoded onboard */
+
+	struct lirc_plugin *plugin;
+	struct usb_endpoint_descriptor *rx_endpoint;
+	struct usb_endpoint_descriptor *tx_endpoint;
+	struct urb *rx_urb;
+	struct urb *tx_urb;
+	int tx_control;
+	unsigned char usb_rx_buf[8];
+	unsigned char usb_tx_buf[8];
+
+	struct rx_data {
+		int count;		/* length of 0 or 1 sequence */
+		int prev_bit;		/* logic level of sequence */
+		int initial_space;	/* initial space flag */
+	} rx;
+
+	struct tx_t {
+		unsigned char data_buf[35];	/* user data buffer */
+		struct completion finished;	/* wait for write to finish */
+		atomic_t busy;			/* write in progress */
+		int status;			/* status of tx completion */
+	} tx;
+};
+
+#define LOCK_CONTEXT	mutex_lock(&context->lock)
+#define UNLOCK_CONTEXT	mutex_unlock(&context->lock)
+
+/* VFD file operations */
+static struct file_operations vfd_fops = {
+	.owner		= THIS_MODULE,
+	.open		= &vfd_open,
+	.write		= &vfd_write,
+	.release	= &vfd_close
+};
+
+/* USB Device ID for IMON USB Control Board */
+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) */
+	{ USB_DEVICE(0x0aa8, 0x8001) },
+	/* IMON USB Control Board (IR & VFD) */
+	{ USB_DEVICE(0x15c2, 0xffda) },
+	/* IMON USB Control Board (IR & VFD) */
+	{ USB_DEVICE(0x15c2, 0xffdc) },
+	/* IMON USB Control Board (IR & LCD) */
+	{ USB_DEVICE(0x15c2, 0x0038) },
+	/* IMON USB Control Board (ext IR only) */
+	{ USB_DEVICE(0x04e8, 0xff30) },
+	{}
+};
+
+/* Some iMON VFD models requires a 6th packet */
+static struct usb_device_id vfd_proto_6p_list[] = {
+	{ USB_DEVICE(0x15c2, 0xffda) },
+	{ USB_DEVICE(0x15c2, 0xffdc) },
+	{ USB_DEVICE(0x15c2, 0x0038) },
+	{}
+};
+static unsigned char vfd_packet6[] = {
+	0x01, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF };
+
+/* Some iMON LCD models use control endpoint */
+static struct usb_device_id lcd_control_endpoint_list[] = {
+	{ USB_DEVICE(0x15c2, 0x0038) },
+	{}
+};
+
+/* Newer iMON models decode the signal onboard */
+static struct usb_device_id ir_onboard_decode_list[] = {
+	{ USB_DEVICE(0x15c2, 0xffdc) },
+	{ USB_DEVICE(0x15c2, 0x0038) },
+	{}
+};
+
+/* USB Device data */
+static struct usb_driver imon_driver = {
+	.name		= MOD_NAME,
+	.probe		= imon_probe,
+	.disconnect	= imon_disconnect,
+	.id_table	= imon_usb_id_table,
+};
+
+static struct usb_class_driver imon_class = {
+	.name		= DEVICE_NAME,
+	.fops		= &vfd_fops,
+	.minor_base	= VFD_MINOR_BASE,
+};
+
+/* to prevent races between open() and disconnect() */
+static DECLARE_MUTEX(disconnect_sem);
+
+static int debug;
+
+/* lcd or vfd? */
+static int is_lcd;
+
+
+/* ------------------------------------------------------------
+ *		     M O D U L E   C O D E
+ * ------------------------------------------------------------
+ */
+
+MODULE_AUTHOR(MOD_AUTHOR);
+MODULE_DESCRIPTION(MOD_DESC);
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(usb, imon_usb_id_table);
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "Debug messages: 0=no, 1=yes(default: no)");
+module_param(is_lcd, int, 0);
+MODULE_PARM_DESC(is_lcd, "The device is an LCD: 0=no (it's a VFD), "
+		 "1=yes (default: no)");
+
+static inline void delete_context(struct imon_context *context)
+{
+	if (context->vfd_supported)
+		usb_free_urb(context->tx_urb);
+	usb_free_urb(context->rx_urb);
+	lirc_buffer_free(context->plugin->rbuf);
+	kfree(context->plugin->rbuf);
+	kfree(context->plugin);
+	kfree(context);
+
+	if (debug)
+		info("%s: context deleted", __func__);
+}
+
+static inline void deregister_from_lirc(struct imon_context *context)
+{
+	int retval;
+	int minor = context->plugin->minor;
+
+	retval = lirc_unregister_plugin(minor);
+	if (retval)
+		err("%s: unable to deregister from lirc(%d)",
+			__func__, retval);
+	else
+		info("Deregistered iMON plugin(minor:%d)", minor);
+
+}
+
+/**
+ * Called when the VFD device(e.g. /dev/usb/lcd)
+ * is opened by the application.
+ */
+static int vfd_open(struct inode *inode, struct file *file)
+{
+	struct usb_interface *interface;
+	struct imon_context *context = NULL;
+	int subminor;
+	int retval = SUCCESS;
+
+	/* prevent races with disconnect */
+	down(&disconnect_sem);
+
+	subminor = iminor(inode);
+	interface = usb_find_interface(&imon_driver, subminor);
+	if (!interface) {
+		err("%s: could not find interface for minor %d",
+		    __func__, subminor);
+		retval = -ENODEV;
+		goto exit;
+	}
+	context = usb_get_intfdata(interface);
+
+	if (!context) {
+		err("%s: no context found for minor %d",
+					__func__, subminor);
+		retval = -ENODEV;
+		goto exit;
+	}
+
+	LOCK_CONTEXT;
+
+	if (!context->vfd_supported) {
+		err("%s: VFD not supported by device", __func__);
+		retval = -ENODEV;
+	} else if (context->vfd_isopen) {
+		err("%s: VFD port is already open", __func__);
+		retval = -EBUSY;
+	} else {
+		context->vfd_isopen = TRUE;
+		file->private_data = context;
+		info("VFD port opened");
+	}
+
+	UNLOCK_CONTEXT;
+
+exit:
+	up(&disconnect_sem);
+	return retval;
+}
+
+/**
+ * Called when the VFD device(e.g. /dev/usb/lcd)
+ * is closed by the application.
+ */
+static int vfd_close(struct inode *inode, struct file *file)
+{
+	struct imon_context *context = NULL;
+	int retval = SUCCESS;
+
+	context = (struct imon_context *) file->private_data;
+
+	if (!context) {
+		err("%s: no context for device", __func__);
+		return -ENODEV;
+	}
+
+	LOCK_CONTEXT;
+
+	if (!context->vfd_supported) {
+		err("%s: VFD not supported by device", __func__);
+		retval = -ENODEV;
+	} else if (!context->vfd_isopen) {
+		err("%s: VFD is not open", __func__);
+		retval = -EIO;
+	} else {
+		context->vfd_isopen = FALSE;
+		info("VFD port closed");
+		if (!context->dev_present && !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. */
+			UNLOCK_CONTEXT;
+			delete_context(context);
+			return retval;
+		}
+	}
+
+	UNLOCK_CONTEXT;
+	return retval;
+}
+
+/**
+ * Sends a packet to the VFD.
+ */
+static inline int send_packet(struct imon_context *context)
+{
+	unsigned int pipe;
+	int interval = 0;
+	int retval = SUCCESS;
+	struct usb_ctrlrequest *control_req = NULL;
+
+	/* Check if we need to use control or interrupt urb */
+	if (!context->tx_control) {
+		pipe = usb_sndintpipe(context->dev,
+				      context->tx_endpoint->bEndpointAddress);
+		interval = context->tx_endpoint->bInterval;
+
+		usb_fill_int_urb(context->tx_urb, context->dev, pipe,
+				 context->usb_tx_buf,
+				 sizeof(context->usb_tx_buf),
+				 usb_tx_callback, context, interval);
+
+		context->tx_urb->actual_length = 0;
+	} else {
+		/* fill request into kmalloc'ed space: */
+		control_req = kmalloc(sizeof(struct usb_ctrlrequest), GFP_NOIO);
+		if (control_req == NULL)
+			return -ENOMEM;
+
+		/* setup packet is '21 09 0200 0001 0008' */
+		control_req->bRequestType = 0x21;
+		control_req->bRequest = 0x09;
+		control_req->wValue = cpu_to_le16(0x0002);
+		control_req->wIndex = cpu_to_le16(0x0100);
+		control_req->wLength = cpu_to_le16(0x0800);
+
+		/* control pipe is endpoint 0x00 */
+		pipe = usb_sndctrlpipe(context->dev, 0);
+
+		/* build the control urb */
+		usb_fill_control_urb(context->tx_urb, context->dev, pipe,
+				     (unsigned char *)control_req,
+				     context->usb_tx_buf,
+				     sizeof(context->usb_tx_buf),
+				     usb_tx_callback, context);
+		context->tx_urb->actual_length = 0;
+	}
+
+	init_completion(&context->tx.finished);
+	atomic_set(&(context->tx.busy), 1);
+
+	retval =  usb_submit_urb(context->tx_urb, GFP_KERNEL);
+	if (retval != SUCCESS) {
+		atomic_set(&(context->tx.busy), 0);
+		err("%s: error submitting urb(%d)", __func__, retval);
+	} else {
+		/* Wait for tranmission to complete(or abort) */
+		UNLOCK_CONTEXT;
+		wait_for_completion(&context->tx.finished);
+		LOCK_CONTEXT;
+
+		retval = context->tx.status;
+		if (retval != SUCCESS)
+			err("%s: packet tx failed(%d)", __func__, retval);
+	}
+
+	kfree(control_req);
+
+	return retval;
+}
+
+/**
+ * Sends an associate packet to the iMON 2.4G.
+ *
+ * This might not be such a good idea, since it has an id
+ * collition with some versions of the "IR & VFD" combo.
+ * The only way to determine if it is a RF version is to look
+ * at the product description string.(Which we currently do
+ * not fetch).
+ */
+static inline int send_associate_24g(struct imon_context *context)
+{
+	int retval;
+	const unsigned char packet[8] = { 0x01, 0x00, 0x00, 0x00,
+					  0x00, 0x00, 0x00, 0x20 };
+
+	if (!context) {
+		err("%s: no context for device", __func__);
+		return -ENODEV;
+	}
+
+	LOCK_CONTEXT;
+
+	if (!context->dev_present) {
+		err("%s: no iMON device present", __func__);
+		retval = -ENODEV;
+		goto exit;
+	}
+
+	memcpy(context->usb_tx_buf, packet, sizeof(packet));
+	retval = send_packet(context);
+
+exit:
+	UNLOCK_CONTEXT;
+
+	return retval;
+}
+
+/**
+ * This is the sysfs functions to handle the association og the iMON 2.4G LT.
+ *
+ *
+ */
+
+static ssize_t show_associate_remote(struct device *d,
+				     struct device_attribute *attr,
+				     char *buf)
+{
+	struct imon_context *context = dev_get_drvdata(d);
+
+	if (!context)
+		return -ENODEV;
+
+	if (context->ir_isassociating) {
+		strcpy(buf, "The device it associating press some button "
+			    "on the remote.\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");
+	} else {
+		strcpy(buf, "Device is closed, you need to open it to "
+			    "associate the remote(you can use irw).\n");
+	}
+	return strlen(buf);
+}
+
+static ssize_t store_associate_remote(struct device *d,
+				      struct device_attribute *attr,
+				      const char *buf, size_t count)
+{
+	struct imon_context *context;
+
+	context = dev_get_drvdata(d);
+
+	if (!context)
+		return -ENODEV;
+
+	if (!context->ir_isopen)
+		return -EINVAL;
+
+	if (context->ir_isopen) {
+		context->ir_isassociating = TRUE;
+		send_associate_24g(context);
+	}
+
+	return count;
+}
+
+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,
+	NULL
+};
+
+static struct attribute_group imon_attribute_group = {
+	.attrs = imon_sysfs_entries
+};
+
+
+
+
+/**
+ * Writes data to the VFD.  The IMON VFD is 2x16 characters
+ * and requires data in 5 consecutive USB interrupt packets,
+ * each packet but the last carrying 7 bytes.
+ *
+ * I don't know if the VFD board supports features such as
+ * scrolling, clearing rows, blanking, etc. so at
+ * the caller must provide a full screen of data.  If fewer
+ * than 32 bytes are provided spaces will be appended to
+ * generate a full screen.
+ */
+static ssize_t vfd_write(struct file *file, const char *buf,
+			 size_t n_bytes, loff_t *pos)
+{
+	int i;
+	int offset;
+	int seq;
+	int retval = SUCCESS;
+	struct imon_context *context;
+
+	context = (struct imon_context *) file->private_data;
+	if (!context) {
+		err("%s: no context for device", __func__);
+		return -ENODEV;
+	}
+
+	LOCK_CONTEXT;
+
+	if (!context->dev_present) {
+		err("%s: no iMON device present", __func__);
+		retval = -ENODEV;
+		goto exit;
+	}
+
+	if (n_bytes <= 0 || n_bytes > 32) {
+		err("%s: invalid payload size", __func__);
+		retval = -EINVAL;
+		goto exit;
+	}
+
+	if (copy_from_user(context->tx.data_buf, buf, n_bytes)) {
+		retval = -EFAULT;
+		goto exit;
+	}
+
+	/* Pad with spaces */
+	for (i = n_bytes; i < 32; ++i)
+		context->tx.data_buf[i] = ' ';
+
+	for (i = 32; i < 35; ++i)
+		context->tx.data_buf[i] = 0xFF;
+
+	offset = 0;
+	seq = 0;
+
+	do {
+		memcpy(context->usb_tx_buf, context->tx.data_buf + offset, 7);
+		context->usb_tx_buf[7] = (unsigned char) seq;
+
+		retval = send_packet(context);
+		if (retval != SUCCESS) {
+			err("%s: send packet failed for packet #%d",
+					__func__, seq/2);
+			goto exit;
+		} else {
+			seq += 2;
+			offset += 7;
+		}
+
+	} while (offset < 35);
+
+	if (context->vfd_proto_6p) {
+		/* Send packet #6 */
+		memcpy(context->usb_tx_buf, vfd_packet6, 7);
+		context->usb_tx_buf[7] = (unsigned char) seq;
+		retval = send_packet(context);
+		if (retval != SUCCESS)
+			err("%s: send packet failed for packet #%d",
+					__func__, seq/2);
+	}
+
+exit:
+	UNLOCK_CONTEXT;
+
+	return (retval == SUCCESS) ? n_bytes : retval;
+}
+
+/**
+ * Writes data to the LCD.  The iMON OEM LCD screen excepts 8-byte
+ * packets. We accept data as 16 hexadecimal digits, followed by a
+ * newline (to make it easy to drive the device from a command-line
+ * -- even though the actual binary data is a bit complicated).
+ *
+ * The device itself is not a "traditional" text-mode display. It's
+ * actually a 16x96 pixel bitmap display. That means if you want to
+ * display text, you've got to have your own "font" and translate the
+ * text into bitmaps for display. This is really flexible (you can
+ * display whatever diacritics you need, and so on), but it's also
+ * a lot more complicated than most LCDs...
+ */
+static ssize_t lcd_write(struct file *file, const char *buf,
+			 size_t n_bytes, loff_t *pos)
+{
+	int retval = SUCCESS;
+	struct imon_context *context;
+
+	context = (struct imon_context *) file->private_data;
+	if (!context) {
+		err("%s: no context for device", __func__);
+		return -ENODEV;
+	}
+
+	LOCK_CONTEXT;
+
+	if (!context->dev_present) {
+		err("%s: no iMON device present", __func__);
+		retval = -ENODEV;
+		goto exit;
+	}
+
+	if (n_bytes != 8) {
+		err("%s: invalid payload size: %d (expecting 8)",
+		    __func__, (int) n_bytes);
+		retval = -EINVAL;
+		goto exit;
+	}
+
+	if (copy_from_user(context->usb_tx_buf, buf, 8)) {
+		retval = -EFAULT;
+		goto exit;
+	}
+
+	retval = send_packet(context);
+	if (retval != SUCCESS) {
+		err("%s: send packet failed!", __func__);
+		goto exit;
+	} else if (debug) {
+		info("%s: write %d bytes to LCD", __func__, (int) n_bytes);
+	}
+exit:
+	UNLOCK_CONTEXT;
+	return (retval == SUCCESS) ? n_bytes : retval;
+}
+
+/**
+ * Callback function for USB core API: transmit data
+ */
+static void usb_tx_callback(struct urb *urb)
+{
+	struct imon_context *context;
+
+	if (!urb)
+		return;
+	context = (struct imon_context *) urb->context;
+	if (!context)
+		return;
+
+	context->tx.status = urb->status;
+
+	/* notify waiters that write has finished */
+	atomic_set(&context->tx.busy, 0);
+	complete(&context->tx.finished);
+
+	return;
+}
+
+/**
+ * Called by lirc_dev when the application opens /dev/lirc
+ */
+static int ir_open(void *data)
+{
+	int retval = SUCCESS;
+	struct imon_context *context;
+
+	/* prevent races with disconnect */
+	down(&disconnect_sem);
+
+	context = (struct imon_context *) data;
+
+	LOCK_CONTEXT;
+
+	if (context->ir_isopen) {
+		err("%s: IR port is already open", __func__);
+		retval = -EBUSY;
+		goto exit;
+	}
+
+	/* 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->dev,
+		usb_rcvintpipe(context->dev,
+				context->rx_endpoint->bEndpointAddress),
+		context->usb_rx_buf, sizeof(context->usb_rx_buf),
+		usb_rx_callback, context, context->rx_endpoint->bInterval);
+
+	retval = usb_submit_urb(context->rx_urb, GFP_KERNEL);
+
+	if (retval)
+		err("%s: usb_submit_urb failed for ir_open(%d)",
+		    __func__, retval);
+	else {
+		context->ir_isopen = TRUE;
+		info("IR port opened");
+	}
+
+exit:
+	UNLOCK_CONTEXT;
+
+	up(&disconnect_sem);
+	return SUCCESS;
+}
+
+/**
+ * Called by lirc_dev when the application closes /dev/lirc
+ */
+static void ir_close(void *data)
+{
+	struct imon_context *context;
+
+	context = (struct imon_context *)data;
+	if (!context) {
+		err("%s: no context for device", __func__);
+		return;
+	}
+
+	LOCK_CONTEXT;
+
+	usb_kill_urb(context->rx_urb);
+	context->ir_isopen = FALSE;
+	context->ir_isassociating = FALSE;
+	info("IR port closed");
+
+	if (!context->dev_present) {
+		/* Device disconnected while IR port was
+		 * still open. Plugin was not deregistered
+		 * at disconnect time, so do it now. */
+		deregister_from_lirc(context);
+
+		if (!context->vfd_isopen) {
+			UNLOCK_CONTEXT;
+			delete_context(context);
+			return;
+		}
+		/* If VFD port is open, context will be deleted by vfd_close */
+	}
+
+	UNLOCK_CONTEXT;
+	return;
+}
+
+/**
+ * Convert bit count to time duration(in us) and submit
+ * the value to lirc_dev.
+ */
+static inline void submit_data(struct imon_context *context)
+{
+	unsigned char buf[4];
+	int value = context->rx.count;
+	int i;
+
+	if (debug)
+		info("submitting data to LIRC\n");
+
+	value *= BIT_DURATION;
+	value &= PULSE_MASK;
+	if (context->rx.prev_bit)
+		value |= PULSE_BIT;
+
+	for (i = 0; i < 4; ++i)
+		buf[i] = value>>(i*8);
+
+	lirc_buffer_write_1(context->plugin->rbuf, buf);
+	wake_up(&context->plugin->rbuf->wait_poll);
+	return;
+}
+
+/**
+ * Process the incoming packet
+ */
+static inline void incoming_packet(struct imon_context *context,
+				   struct urb *urb)
+{
+	int len = urb->actual_length;
+	unsigned char *buf = urb->transfer_buffer;
+	int octet, bit;
+	unsigned char mask;
+	int chunk_num;
+#ifdef DEBUG
+	int i;
+#endif
+
+	if (len != 8) {
+		warn("%s: invalid incoming packet size(%d)",
+		     __func__, len);
+		return;
+	}
+
+	/* iMON 2.4G associate frame */
+	if (buf[0] == 0x00 &&
+	    buf[2] == 0xFF &&				/* REFID */
+	    buf[3] == 0xFF &&
+	    buf[4] == 0xFF &&
+	    buf[5] == 0xFF &&				/* iMON 2.4G */
+	   ((buf[6] == 0x4E && buf[7] == 0xDF) ||	/* LT */
+	    (buf[6] == 0x5E && buf[7] == 0xDF))) {	/* DT */
+		warn("%s: remote associated refid=%02X", __func__, buf[1]);
+		context->ir_isassociating = FALSE;
+	}
+
+	chunk_num = buf[7];
+
+	if (chunk_num == 0xFF)
+		return;		/* filler frame, no data here */
+
+	if (buf[0] == 0xFF &&
+	    buf[1] == 0xFF &&
+	    buf[2] == 0xFF &&
+	    buf[3] == 0xFF &&
+	    buf[4] == 0xFF &&
+	    buf[5] == 0xFF &&				/* iMON 2.4G */
+	    ((buf[6] == 0x4E && buf[7] == 0xAF) ||	/* LT */
+	     (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 (context->ir_onboard_decode) {
+		/* The signals have been decoded onboard the iMON controller */
+		lirc_buffer_write_1(context->plugin->rbuf, buf);
+		wake_up(&context->plugin->rbuf->wait_poll);
+		return;
+	}
+
+	/* Translate received data to pulse and space lengths.
+	 * Received data is active low, i.e. pulses are 0 and
+	 * spaces are 1.
+	 *
+	 * My original algorithm was essentially similar to
+	 * Changwoo Ryu's with the exception that he switched
+	 * the incoming bits to active high and also fed an
+	 * initial space to LIRC at the start of a new sequence
+	 * if the previous bit was a pulse.
+	 *
+	 * I've decided to adopt his algorithm. */
+
+	if (chunk_num == 1 && context->rx.initial_space) {
+		/* LIRC requires a leading space */
+		context->rx.prev_bit = 0;
+		context->rx.count = 4;
+		submit_data(context);
+		context->rx.count = 0;
+	}
+
+	for (octet = 0; octet < 5; ++octet) {
+		mask = 0x80;
+		for (bit = 0; bit < 8; ++bit) {
+			int curr_bit = !(buf[octet] & mask);
+			if (curr_bit != context->rx.prev_bit) {
+				if (context->rx.count) {
+					submit_data(context);
+					context->rx.count = 0;
+				}
+				context->rx.prev_bit = curr_bit;
+			}
+			++context->rx.count;
+			mask >>= 1;
+		}
+	}
+
+	if (chunk_num == 10) {
+		if (context->rx.count) {
+			submit_data(context);
+			context->rx.count = 0;
+		}
+		context->rx.initial_space = context->rx.prev_bit;
+	}
+}
+
+/**
+ * Callback function for USB core API: receive data
+ */
+static void usb_rx_callback(struct urb *urb)
+{
+	struct imon_context *context;
+
+	if (!urb)
+		return;
+	context = (struct imon_context *) urb->context;
+	if (!context)
+		return;
+
+	switch (urb->status) {
+	case -ENOENT:		/* usbcore unlink successful! */
+		return;
+	case SUCCESS:
+		if (context->ir_isopen)
+			incoming_packet(context, urb);
+		break;
+	default:
+		warn("%s: status(%d): ignored", __func__, urb->status);
+		break;
+	}
+
+	usb_submit_urb(context->rx_urb, GFP_ATOMIC);
+	return;
+}
+
+
+
+/**
+ * Callback function for USB core API: Probe
+ */
+static int imon_probe(struct usb_interface *interface,
+		      const struct usb_device_id *id)
+{
+	struct usb_device *dev = NULL;
+	struct usb_host_interface *iface_desc = NULL;
+	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_plugin *plugin = NULL;
+	struct lirc_buffer *rbuf = NULL;
+	int lirc_minor = 0;
+	int num_endpoints;
+	int retval = SUCCESS;
+	int vfd_ep_found;
+	int ir_ep_found;
+	int alloc_status;
+	int vfd_proto_6p = FALSE;
+	int ir_onboard_decode = FALSE;
+	int tx_control = FALSE;
+	struct imon_context *context = NULL;
+	int i;
+
+	info("%s: found IMON device", __func__);
+
+	/*
+	 * If it's the LCD, as opposed to the VFD, we just need to replace
+	 * the "write" file op.
+	 */
+	if (is_lcd)
+		vfd_fops.write = &lcd_write;
+
+	dev = usb_get_dev(interface_to_usbdev(interface));
+	iface_desc = interface->cur_altsetting;
+	num_endpoints = iface_desc->desc.bNumEndpoints;
+
+	/*
+	 * Scan the endpoint list and set:
+	 *	first input endpoint = IR endpoint
+	 *	first output endpoint = VFD endpoint
+	 */
+
+	ir_ep_found = FALSE;
+	vfd_ep_found = FALSE;
+
+	for (i = 0; i < num_endpoints && !(ir_ep_found && vfd_ep_found); ++i) {
+		struct usb_endpoint_descriptor *ep;
+		int ep_dir;
+		int ep_type;
+		ep = &iface_desc->endpoint[i].desc;
+		ep_dir = ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK;
+		ep_type = ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
+
+		if (!ir_ep_found &&
+			ep_dir == USB_DIR_IN &&
+			ep_type == USB_ENDPOINT_XFER_INT) {
+
+			rx_endpoint = ep;
+			ir_ep_found = TRUE;
+			if (debug)
+				info("%s: found IR endpoint", __func__);
+
+		} else if (!vfd_ep_found &&
+			   ep_dir == USB_DIR_OUT &&
+			   ep_type == USB_ENDPOINT_XFER_INT) {
+			tx_endpoint = ep;
+			vfd_ep_found = TRUE;
+			if (debug)
+				info("%s: found VFD endpoint", __func__);
+		}
+	}
+
+	/*
+	 * If we didn't find a vfd endpoint, and we have a next-gen LCD,
+	 * use control urb instead of interrupt
+	 */
+	if (!vfd_ep_found) {
+		if (usb_match_id(interface, lcd_control_endpoint_list)) {
+			tx_control = 1;
+			vfd_ep_found = TRUE;
+			if (debug)
+				info("%s: LCD device uses control endpoint, "
+				     "not interface OUT endpoint", __func__);
+		}
+	}
+
+	/* Input endpoint is mandatory */
+	if (!ir_ep_found) {
+		err("%s: no valid input(IR) endpoint found.", __func__);
+		retval = -ENODEV;
+		goto exit;
+	} else {
+		/* Determine if the IR signals are decoded onboard */
+		if (usb_match_id(interface, ir_onboard_decode_list))
+			ir_onboard_decode = TRUE;
+
+		if (debug)
+			info("ir_onboard_decode: %d", ir_onboard_decode);
+	}
+
+	/* Determine if VFD requires 6 packets */
+	if (vfd_ep_found) {
+		if (usb_match_id(interface, vfd_proto_6p_list))
+			vfd_proto_6p = TRUE;
+
+		if (debug)
+			info("vfd_proto_6p: %d", vfd_proto_6p);
+	}
+
+
+	/* Allocate memory */
+
+	alloc_status = SUCCESS;
+
+	context = kmalloc(sizeof(struct imon_context), GFP_KERNEL);
+	if (!context) {
+		err("%s: kmalloc failed for context", __func__);
+		alloc_status = 1;
+		goto alloc_status_switch;
+	}
+	plugin = kmalloc(sizeof(struct lirc_plugin), GFP_KERNEL);
+	if (!plugin) {
+		err("%s: kmalloc failed for lirc_plugin", __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;
+	}
+	if (vfd_ep_found) {
+		tx_urb = usb_alloc_urb(0, GFP_KERNEL);
+		if (!tx_urb) {
+			err("%s: usb_alloc_urb failed for VFD urb",
+			    __func__);
+			alloc_status = 6;
+			goto alloc_status_switch;
+		}
+	}
+
+	/* clear all members of imon_context and lirc_plugin */
+	memset(context, 0, sizeof(struct imon_context));
+	mutex_init(&context->lock);
+	context->vfd_proto_6p = vfd_proto_6p;
+	context->ir_onboard_decode = ir_onboard_decode;
+
+	memset(plugin, 0, sizeof(struct lirc_plugin));
+
+	strcpy(plugin->name, MOD_NAME);
+	plugin->minor = -1;
+	plugin->code_length = (ir_onboard_decode) ?
+		32 : sizeof(int) * 8;
+	plugin->sample_rate = 0;
+	plugin->features = (ir_onboard_decode) ?
+		LIRC_CAN_REC_LIRCCODE : LIRC_CAN_REC_MODE2;
+	plugin->data = context;
+	plugin->rbuf = rbuf;
+	plugin->set_use_inc = ir_open;
+	plugin->set_use_dec = ir_close;
+	plugin->dev = &dev->dev;
+	plugin->owner = THIS_MODULE;
+
+	LOCK_CONTEXT;
+
+	lirc_minor = lirc_register_plugin(plugin);
+	if (lirc_minor < 0) {
+		err("%s: lirc_register_plugin failed", __func__);
+		alloc_status = 7;
+		UNLOCK_CONTEXT;
+		goto alloc_status_switch;
+	} else
+		info("%s: Registered iMON plugin(minor:%d)",
+		     __func__, lirc_minor);
+
+	/* Needed while unregistering! */
+	plugin->minor = lirc_minor;
+
+	context->dev = dev;
+	context->dev_present = TRUE;
+	context->rx_endpoint = rx_endpoint;
+	context->rx_urb = rx_urb;
+	if (vfd_ep_found) {
+		context->vfd_supported = TRUE;
+		context->tx_endpoint = tx_endpoint;
+		context->tx_urb = tx_urb;
+		context->tx_control = tx_control;
+	}
+	context->plugin = plugin;
+
+	usb_set_intfdata(interface, context);
+
+	if (cpu_to_le16(dev->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);
+	}
+
+	if (vfd_ep_found) {
+		if (debug)
+			info("Registering VFD with sysfs");
+		if (usb_register_dev(interface, &imon_class)) {
+			/* Not a fatal error, so ignore */
+			info("%s: could not get a minor number for VFD",
+				__func__);
+		}
+	}
+
+	info("%s: iMON device on usb<%d:%d> initialized",
+			__func__, dev->bus->busnum, dev->devnum);
+
+	UNLOCK_CONTEXT;
+
+alloc_status_switch:
+
+	switch (alloc_status) {
+	case 7:
+		if (vfd_ep_found)
+			usb_free_urb(tx_urb);
+	case 6:
+		usb_free_urb(rx_urb);
+	case 5:
+		lirc_buffer_free(rbuf);
+	case 4:
+		kfree(rbuf);
+	case 3:
+		kfree(plugin);
+	case 2:
+		kfree(context);
+		context = NULL;
+	case 1:
+		retval = -ENOMEM;
+	case SUCCESS:
+		;
+	}
+
+exit:
+	return retval;
+}
+
+/**
+ * Callback function for USB core API: disonnect
+ */
+static void imon_disconnect(struct usb_interface *interface)
+{
+	struct imon_context *context;
+
+	/* prevent races with ir_open()/vfd_open() */
+	down(&disconnect_sem);
+
+	context = usb_get_intfdata(interface);
+	LOCK_CONTEXT;
+
+	info("%s: iMON device disconnected", __func__);
+
+	/* 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);
+	context->dev_present = FALSE;
+
+	/* Stop reception */
+	usb_kill_urb(context->rx_urb);
+
+	/* Abort ongoing write */
+	if (atomic_read(&context->tx.busy)) {
+		usb_kill_urb(context->tx_urb);
+		wait_for_completion(&context->tx.finished);
+	}
+
+	/* De-register from lirc_dev if IR port is not open */
+	if (!context->ir_isopen)
+		deregister_from_lirc(context);
+
+	if (context->vfd_supported)
+		usb_deregister_dev(interface, &imon_class);
+
+	UNLOCK_CONTEXT;
+
+	if (!context->ir_isopen && !context->vfd_isopen)
+		delete_context(context);
+
+	up(&disconnect_sem);
+}
+
+static int __init imon_init(void)
+{
+	int rc;
+
+	info(MOD_DESC ", v" MOD_VERSION);
+	info(MOD_AUTHOR);
+
+	rc = usb_register(&imon_driver);
+	if (rc) {
+		err("%s: usb register failed(%d)", __func__, rc);
+		return -ENODEV;
+	}
+	return SUCCESS;
+}
+
+static void __exit imon_exit(void)
+{
+	usb_deregister(&imon_driver);
+	info("module removed. Goodbye!");
+}
+
+#ifdef MODULE
+module_init(imon_init);
+module_exit(imon_exit);
+
+#else
+subsys_initcall(imon_init);
+
+#endif /* MODULE */
-- 
1.6.0.1


^ permalink raw reply related	[flat|nested] 111+ messages in thread

* [PATCH 09/18] lirc driver for the Streamzap PC Receiver
  2008-09-09  4:05               ` [PATCH 08/18] lirc driver for the Soundgraph IMON IR Receivers Jarod Wilson
@ 2008-09-09  4:05                 ` Jarod Wilson
  2008-09-09  4:05                   ` [PATCH 10/18] lirc driver for Igor Cesko's USB IR receiver Jarod Wilson
  2008-09-11 15:22                   ` [PATCH 09/18] lirc driver for the Streamzap PC Receiver Jonathan Corbet
  2008-09-10 21:02                 ` [PATCH 08/18] lirc driver for the Soundgraph IMON IR Receivers Jonathan Corbet
  1 sibling, 2 replies; 111+ messages in thread
From: Jarod Wilson @ 2008-09-09  4:05 UTC (permalink / raw)
  To: linux-kernel
  Cc: Jarod Wilson, Jarod Wilson, Janne Grunau, Christoph Bartelmus

Signed-off-by: Jarod Wilson <jarod@redhat.com>
Signed-off-by: Janne Grunau <j@jannau.net>
CC: Christoph Bartelmus <lirc@bartelmus.de>
---
 drivers/input/lirc/Kconfig          |    7 +
 drivers/input/lirc/Makefile         |    1 +
 drivers/input/lirc/lirc_streamzap.c |  795 +++++++++++++++++++++++++++++++++++
 3 files changed, 803 insertions(+), 0 deletions(-)
 create mode 100644 drivers/input/lirc/lirc_streamzap.c

diff --git a/drivers/input/lirc/Kconfig b/drivers/input/lirc/Kconfig
index 7e37b2d..c105ac6 100644
--- a/drivers/input/lirc/Kconfig
+++ b/drivers/input/lirc/Kconfig
@@ -68,4 +68,11 @@ config LIRC_SERIAL
 	help
 	  Driver for Homebrew Serial Port Receivers
 
+config LIRC_STREAMZAP
+	tristate "Streamzap PC Receiver"
+	default n
+	depends on LIRC_DEV
+	help
+	  Driver for the Streamzap PC Receiver
+
 endif
diff --git a/drivers/input/lirc/Makefile b/drivers/input/lirc/Makefile
index f39fa06..b348110 100644
--- a/drivers/input/lirc/Makefile
+++ b/drivers/input/lirc/Makefile
@@ -13,3 +13,4 @@ obj-$(CONFIG_LIRC_IMON)		+= lirc_imon.o
 obj-$(CONFIG_LIRC_MCEUSB)	+= lirc_mceusb.o
 obj-$(CONFIG_LIRC_MCEUSB2)	+= lirc_mceusb2.o
 obj-$(CONFIG_LIRC_SERIAL)	+= lirc_serial.o
+obj-$(CONFIG_LIRC_STREAMZAP)	+= lirc_streamzap.o
diff --git a/drivers/input/lirc/lirc_streamzap.c b/drivers/input/lirc/lirc_streamzap.c
new file mode 100644
index 0000000..69865cb
--- /dev/null
+++ b/drivers/input/lirc/lirc_streamzap.c
@@ -0,0 +1,795 @@
+/*
+ * Streamzap Remote Control driver
+ *
+ * Copyright (c) 2005 Christoph Bartelmus <lirc@bartelmus.de>
+ *
+ * This driver was based on the work of Greg Wickham and Adrian
+ * Dewhurst. It was substantially rewritten to support correct signal
+ * gaps and now maintains a delay buffer, which is used to present
+ * consistent timing behaviour to user space applications. Without the
+ * delay buffer an ugly hack would be required in lircd, which can
+ * cause sluggish signal decoding in certain situations.
+ *
+ * This driver is based on the USB skeleton driver packaged with the
+ * kernel; copyright (C) 2001-2003 Greg Kroah-Hartman (greg@kroah.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/version.h>
+
+#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/smp_lock.h>
+#include <linux/completion.h>
+#include <linux/uaccess.h>
+#include <linux/usb.h>
+
+#include "lirc.h"
+#include "lirc_dev.h"
+
+#define DRIVER_VERSION	"1.28"
+#define DRIVER_NAME	"lirc_streamzap"
+#define DRIVER_DESC	"Streamzap Remote Control driver"
+
+/* ------------------------------------------------------------------ */
+
+static int debug;
+
+#define USB_STREAMZAP_VENDOR_ID		0x0e9c
+#define USB_STREAMZAP_PRODUCT_ID	0x0000
+
+/* Use our own dbg macro */
+#define dprintk(fmt, args...)					\
+	do {							\
+		if (debug)					\
+			printk(KERN_DEBUG DRIVER_NAME "[%d]: "	\
+			       fmt "\n", ## args);		\
+	} while (0)
+
+/*
+ * table of devices that work with this driver
+ */
+static struct usb_device_id streamzap_table[] = {
+	/* Streamzap Remote Control */
+	{ USB_DEVICE(USB_STREAMZAP_VENDOR_ID, USB_STREAMZAP_PRODUCT_ID) },
+	/* Terminating entry */
+	{ }
+};
+
+MODULE_DEVICE_TABLE(usb, streamzap_table);
+
+#define STREAMZAP_PULSE_MASK 0xf0
+#define STREAMZAP_SPACE_MASK 0x0f
+#define STREAMZAP_RESOLUTION 256
+
+/* number of samples buffered */
+#define STREAMZAP_BUFFER_SIZE 128
+
+enum StreamzapDecoderState {
+	PulseSpace,
+	FullPulse,
+	FullSpace,
+	IgnorePulse
+};
+
+/* Structure to hold all of our device specific stuff */
+/* some remarks regarding locking:
+   theoretically this struct can be accessed from three threads:
+
+   - from lirc_dev through set_use_inc/set_use_dec
+
+   - from the USB layer throuh probe/disconnect/irq
+
+     Careful placement of lirc_register_plugin/lirc_unregister_plugin
+     calls will prevent conflicts. lirc_dev makes sure that
+     set_use_inc/set_use_dec are not being executed and will not be
+     called after lirc_unregister_plugin returns.
+
+   - by the timer callback
+
+     The timer is only running when the device is connected and the
+     LIRC device is open. Making sure the timer is deleted by
+     set_use_dec will make conflicts impossible.
+*/
+struct usb_streamzap {
+
+	/* usb */
+	/* save off the usb device pointer */
+	struct usb_device	*udev;
+	/* the interface for this device */
+	struct usb_interface	*interface;
+
+	/* buffer & dma */
+	unsigned char		*buf_in;
+	dma_addr_t		dma_in;
+	unsigned int		buf_in_len;
+
+	struct usb_endpoint_descriptor *endpoint;
+
+	/* IRQ */
+	struct urb		*urb_in;
+
+	/* lirc */
+	struct lirc_plugin	plugin;
+	struct lirc_buffer	delay_buf;
+	struct lirc_buffer	lirc_buf;
+
+	/* timer used to support delay buffering */
+	struct timer_list	delay_timer;
+	int			timer_running;
+	spinlock_t		timer_lock;
+
+	/* tracks whether we are currently receiving some signal */
+	int			idle;
+	/* sum of signal lengths received since signal start */
+	unsigned long		sum;
+	/* start time of signal; necessary for gap tracking */
+	struct timeval		signal_last;
+	struct timeval		signal_start;
+	enum StreamzapDecoderState decoder_state;
+	struct timer_list	flush_timer;
+	int			flush;
+	int			in_use;
+};
+
+
+/* local function prototypes */
+static int streamzap_probe(struct usb_interface *interface,
+			   const struct usb_device_id *id);
+static void streamzap_disconnect(struct usb_interface *interface);
+static void usb_streamzap_irq(struct urb *urb);
+static int streamzap_use_inc(void *data);
+static void streamzap_use_dec(void *data);
+static int streamzap_ioctl(struct inode *node, struct file *filep,
+			   unsigned int cmd, unsigned long arg);
+static int streamzap_suspend(struct usb_interface *intf, pm_message_t message);
+static int streamzap_resume(struct usb_interface *intf);
+
+/* usb specific object needed to register this driver with the usb subsystem */
+
+static struct usb_driver streamzap_driver = {
+	.name =		DRIVER_NAME,
+	.probe =	streamzap_probe,
+	.disconnect =	streamzap_disconnect,
+	.suspend =	streamzap_suspend,
+	.resume =	streamzap_resume,
+	.id_table =	streamzap_table,
+};
+
+static void stop_timer(struct usb_streamzap *sz)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&sz->timer_lock, flags);
+	if (sz->timer_running) {
+		sz->timer_running = 0;
+		del_timer_sync(&sz->delay_timer);
+	}
+	spin_unlock_irqrestore(&sz->timer_lock, flags);
+}
+
+static void flush_timeout(unsigned long arg)
+{
+	struct usb_streamzap *sz = (struct usb_streamzap *) arg;
+
+	/* finally start accepting data */
+	sz->flush = 0;
+}
+static void delay_timeout(unsigned long arg)
+{
+	unsigned long flags;
+	/* deliver data every 10 ms */
+	static unsigned long timer_inc =
+		(10000/(1000000/HZ)) == 0 ? 1 : (10000/(1000000/HZ));
+	struct usb_streamzap *sz = (struct usb_streamzap *) arg;
+	int data;
+
+	spin_lock_irqsave(&sz->timer_lock, flags);
+
+	if (!lirc_buffer_empty(&sz->delay_buf) &&
+	    !lirc_buffer_full(&sz->lirc_buf)) {
+		lirc_buffer_read_1(&sz->delay_buf, (unsigned char *) &data);
+		lirc_buffer_write_1(&sz->lirc_buf, (unsigned char *) &data);
+	}
+	if (!lirc_buffer_empty(&sz->delay_buf)) {
+		while (lirc_buffer_available(&sz->delay_buf) <
+		      STREAMZAP_BUFFER_SIZE/2 &&
+		      !lirc_buffer_full(&sz->lirc_buf)) {
+			lirc_buffer_read_1(&sz->delay_buf,
+					   (unsigned char *) &data);
+			lirc_buffer_write_1(&sz->lirc_buf,
+					    (unsigned char *) &data);
+		}
+		if (sz->timer_running) {
+			sz->delay_timer.expires += timer_inc;
+			add_timer(&sz->delay_timer);
+		}
+	} else {
+		sz->timer_running = 0;
+	}
+
+	if (!lirc_buffer_empty(&sz->lirc_buf))
+		wake_up(&sz->lirc_buf.wait_poll);
+
+	spin_unlock_irqrestore(&sz->timer_lock, flags);
+}
+
+static inline void flush_delay_buffer(struct usb_streamzap *sz)
+{
+	int data;
+	int empty = 1;
+
+	while (!lirc_buffer_empty(&sz->delay_buf)) {
+		empty = 0;
+		lirc_buffer_read_1(&sz->delay_buf, (unsigned char *) &data);
+		if (!lirc_buffer_full(&sz->lirc_buf)) {
+			lirc_buffer_write_1(&sz->lirc_buf,
+					    (unsigned char *) &data);
+		} else {
+			dprintk("buffer overflow\n", sz->plugin.minor);
+		}
+	}
+	if (!empty)
+		wake_up(&sz->lirc_buf.wait_poll);
+}
+
+static inline void push(struct usb_streamzap *sz, unsigned char *data)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&sz->timer_lock, flags);
+	if (lirc_buffer_full(&sz->delay_buf)) {
+		int data;
+
+		lirc_buffer_read_1(&sz->delay_buf, (unsigned char *) &data);
+		if (!lirc_buffer_full(&sz->lirc_buf)) {
+			lirc_buffer_write_1(&sz->lirc_buf,
+					    (unsigned char *) &data);
+		} else {
+			dprintk("buffer overflow", sz->plugin.minor);
+		}
+	}
+
+	lirc_buffer_write_1(&sz->delay_buf, data);
+
+	if (!sz->timer_running) {
+		sz->delay_timer.expires = jiffies + HZ/10;
+		add_timer(&sz->delay_timer);
+		sz->timer_running = 1;
+	}
+
+	spin_unlock_irqrestore(&sz->timer_lock, flags);
+}
+
+static inline void push_full_pulse(struct usb_streamzap *sz,
+				   unsigned char value)
+{
+	int pulse;
+
+	if (sz->idle) {
+		long deltv;
+		int tmp;
+
+		sz->signal_last = sz->signal_start;
+		do_gettimeofday(&sz->signal_start);
+
+		deltv = sz->signal_start.tv_sec-sz->signal_last.tv_sec;
+		if (deltv > 15) {
+			tmp = PULSE_MASK; /* really long time */
+		} else {
+			tmp = (int) (deltv*1000000+
+					sz->signal_start.tv_usec -
+					sz->signal_last.tv_usec);
+			tmp -= sz->sum;
+		}
+		dprintk("ls %u", sz->plugin.minor, tmp);
+		push(sz, (char *)&tmp);
+
+		sz->idle = 0;
+		sz->sum = 0;
+	}
+
+	pulse = ((int) value)*STREAMZAP_RESOLUTION;
+	pulse += STREAMZAP_RESOLUTION/2;
+	sz->sum += pulse;
+	pulse |= PULSE_BIT;
+
+	dprintk("p %u", sz->plugin.minor, pulse&PULSE_MASK);
+	push(sz, (char *)&pulse);
+}
+
+static inline void push_half_pulse(struct usb_streamzap *sz,
+				   unsigned char value)
+{
+	push_full_pulse(sz, (value & STREAMZAP_PULSE_MASK)>>4);
+}
+
+static inline void push_full_space(struct usb_streamzap *sz,
+				   unsigned char value)
+{
+	int space;
+
+	space = ((int) value)*STREAMZAP_RESOLUTION;
+	space += STREAMZAP_RESOLUTION/2;
+	sz->sum += space;
+	dprintk("s %u", sz->plugin.minor, space);
+	push(sz, (char *)&space);
+}
+
+static inline void push_half_space(struct usb_streamzap *sz,
+				   unsigned char value)
+{
+	push_full_space(sz, value & STREAMZAP_SPACE_MASK);
+}
+
+/*
+ * usb_streamzap_irq - IRQ handler
+ *
+ * This procedure is invoked on reception of data from
+ * the usb remote.
+ */
+static void usb_streamzap_irq(struct urb *urb)
+{
+	struct usb_streamzap *sz;
+	int		len;
+	unsigned int	i = 0;
+
+	if (!urb)
+		return;
+
+	sz = urb->context;
+	len = urb->actual_length;
+
+	switch (urb->status) {
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* this urb is terminated, clean up */
+		/* sz might already be invalid at this point */
+		dprintk("urb status: %d", -1, urb->status);
+		return;
+	default:
+		break;
+	}
+
+	dprintk("received %d", sz->plugin.minor, urb->actual_length);
+	if (!sz->flush) {
+		for (i = 0; i < urb->actual_length; i++) {
+			dprintk("%d: %x", sz->plugin.minor,
+				i, (unsigned char) sz->buf_in[i]);
+			switch (sz->decoder_state) {
+			case PulseSpace:
+				if ((sz->buf_in[i]&STREAMZAP_PULSE_MASK) ==
+				    STREAMZAP_PULSE_MASK) {
+					sz->decoder_state = FullPulse;
+					continue;
+				} else if ((sz->buf_in[i]&STREAMZAP_SPACE_MASK)
+					   == STREAMZAP_SPACE_MASK) {
+					push_half_pulse(sz, sz->buf_in[i]);
+					sz->decoder_state = FullSpace;
+					continue;
+				} else {
+					push_half_pulse(sz, sz->buf_in[i]);
+					push_half_space(sz, sz->buf_in[i]);
+				}
+				break;
+			case FullPulse:
+				push_full_pulse(sz, sz->buf_in[i]);
+				sz->decoder_state = IgnorePulse;
+				break;
+			case FullSpace:
+				if (sz->buf_in[i] == 0xff) {
+					sz->idle = 1;
+					stop_timer(sz);
+					flush_delay_buffer(sz);
+				} else
+					push_full_space(sz, sz->buf_in[i]);
+				sz->decoder_state = PulseSpace;
+				break;
+			case IgnorePulse:
+				if ((sz->buf_in[i]&STREAMZAP_SPACE_MASK) ==
+				    STREAMZAP_SPACE_MASK) {
+					sz->decoder_state = FullSpace;
+					continue;
+				}
+				push_half_space(sz, sz->buf_in[i]);
+				sz->decoder_state = PulseSpace;
+				break;
+			}
+		}
+	}
+
+	/* resubmit only for 2.6 */
+	usb_submit_urb(urb, GFP_ATOMIC);
+
+	return;
+}
+
+/**
+ *	streamzap_probe
+ *
+ *	Called by usb-core to associated with a candidate device
+ *	On any failure the return value is the ERROR
+ *	On success return 0
+ */
+static int streamzap_probe(struct usb_interface *interface,
+			   const struct usb_device_id *id)
+{
+	struct usb_device *udev = interface_to_usbdev(interface);
+	struct usb_host_interface *iface_host;
+	int retval = -ENOMEM;
+	struct usb_streamzap *sz = NULL;
+	char buf[63], name[128] = "";
+
+	/***************************************************
+	 * Allocate space for device driver specific data
+	 */
+	sz = kmalloc(sizeof(struct usb_streamzap), GFP_KERNEL);
+	if (sz == NULL)
+		goto error;
+
+	memset(sz, 0, sizeof(*sz));
+	sz->udev = udev;
+	sz->interface = interface;
+
+	/***************************************************
+	 * Check to ensure endpoint information matches requirements
+	 */
+	iface_host = interface->cur_altsetting;
+
+	if (iface_host->desc.bNumEndpoints != 1) {
+		err("%s: Unexpected desc.bNumEndpoints (%d)", __func__,
+		    iface_host->desc.bNumEndpoints);
+		retval = -ENODEV;
+		goto error;
+	}
+
+	sz->endpoint = &(iface_host->endpoint[0].desc);
+	if ((sz->endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
+	    != USB_DIR_IN) {
+		err("%s: endpoint doesn't match input device 02%02x",
+		    __func__, sz->endpoint->bEndpointAddress);
+		retval = -ENODEV;
+		goto error;
+	}
+
+	if ((sz->endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+	    != USB_ENDPOINT_XFER_INT) {
+		err("%s: endpoint attributes don't match xfer 02%02x",
+		    __func__, sz->endpoint->bmAttributes);
+		retval = -ENODEV;
+		goto error;
+	}
+
+	if (sz->endpoint->wMaxPacketSize == 0) {
+		err("%s: endpoint message size==0? ", __func__);
+		retval = -ENODEV;
+		goto error;
+	}
+
+	/***************************************************
+	 * Allocate the USB buffer and IRQ URB
+	 */
+
+	sz->buf_in_len = sz->endpoint->wMaxPacketSize;
+	sz->buf_in = usb_buffer_alloc(sz->udev, sz->buf_in_len,
+				      GFP_ATOMIC, &sz->dma_in);
+	if (sz->buf_in == NULL)
+		goto error;
+
+	sz->urb_in = usb_alloc_urb(0, GFP_KERNEL);
+	if (sz->urb_in == NULL)
+		goto error;
+
+	/***************************************************
+	 * Connect this device to the LIRC sub-system
+	 */
+
+	if (lirc_buffer_init(&sz->lirc_buf, sizeof(int),
+			     STREAMZAP_BUFFER_SIZE))
+		goto error;
+
+	if (lirc_buffer_init(&sz->delay_buf, sizeof(int),
+			     STREAMZAP_BUFFER_SIZE)) {
+		lirc_buffer_free(&sz->lirc_buf);
+		goto error;
+	}
+
+	/***************************************************
+	 * As required memory is allocated now populate the plugin structure
+	 */
+
+	memset(&sz->plugin, 0, sizeof(sz->plugin));
+
+	strcpy(sz->plugin.name, DRIVER_NAME);
+	sz->plugin.minor = -1;
+	sz->plugin.sample_rate = 0;
+	sz->plugin.code_length = sizeof(int) * 8;
+	sz->plugin.features = LIRC_CAN_REC_MODE2 | LIRC_CAN_GET_REC_RESOLUTION;
+	sz->plugin.data = sz;
+	sz->plugin.rbuf = &sz->lirc_buf;
+	sz->plugin.set_use_inc = &streamzap_use_inc;
+	sz->plugin.set_use_dec = &streamzap_use_dec;
+	sz->plugin.ioctl = streamzap_ioctl;
+	sz->plugin.dev = &udev->dev;
+	sz->plugin.owner = THIS_MODULE;
+
+	sz->idle = 1;
+	sz->decoder_state = PulseSpace;
+	init_timer(&sz->delay_timer);
+	sz->delay_timer.function = delay_timeout;
+	sz->delay_timer.data = (unsigned long) sz;
+	sz->timer_running = 0;
+	spin_lock_init(&sz->timer_lock);
+
+	init_timer(&sz->flush_timer);
+	sz->flush_timer.function = flush_timeout;
+	sz->flush_timer.data = (unsigned long) sz;
+	/***************************************************
+	 * Complete final initialisations
+	 */
+
+	usb_fill_int_urb(sz->urb_in, udev,
+		usb_rcvintpipe(udev, sz->endpoint->bEndpointAddress),
+		sz->buf_in, sz->buf_in_len, usb_streamzap_irq, sz,
+		sz->endpoint->bInterval);
+
+	if (udev->descriptor.iManufacturer
+	    && usb_string(udev, udev->descriptor.iManufacturer, buf, 63) > 0)
+		strncpy(name, buf, 128);
+
+	if (udev->descriptor.iProduct
+	    && usb_string(udev,  udev->descriptor.iProduct, buf, 63) > 0)
+		snprintf(name, 128, "%s %s", name, buf);
+
+	printk(KERN_INFO DRIVER_NAME "[%d]: %s on usb%d:%d attached\n",
+	       sz->plugin.minor, name,
+	       udev->bus->busnum, sz->udev->devnum);
+
+	usb_set_intfdata(interface, sz);
+
+	if (lirc_register_plugin(&sz->plugin) < 0) {
+		lirc_buffer_free(&sz->delay_buf);
+		lirc_buffer_free(&sz->lirc_buf);
+		goto error;
+	}
+
+	return 0;
+
+error:
+
+	/***************************************************
+	 * Premise is that a 'goto error' can be invoked from inside the
+	 * probe function and all necessary cleanup actions will be taken
+	 * including freeing any necessary memory blocks
+	 */
+
+	if (retval == -ENOMEM)
+		err("Out of memory");
+
+	if (sz) {
+		usb_free_urb(sz->urb_in);
+		usb_buffer_free(udev, sz->buf_in_len, sz->buf_in, sz->dma_in);
+		kfree(sz);
+	}
+
+	return retval;
+}
+
+static int streamzap_use_inc(void *data)
+{
+	struct usb_streamzap *sz = data;
+
+	if (!sz) {
+		dprintk("%s called with no context", -1, __func__);
+		return -EINVAL;
+	}
+	dprintk("set use inc", sz->plugin.minor);
+
+	while (!lirc_buffer_empty(&sz->lirc_buf))
+		lirc_buffer_remove_1(&sz->lirc_buf);
+	while (!lirc_buffer_empty(&sz->delay_buf))
+		lirc_buffer_remove_1(&sz->delay_buf);
+
+	sz->flush_timer.expires = jiffies + HZ;
+	sz->flush = 1;
+	add_timer(&sz->flush_timer);
+
+	sz->urb_in->dev = sz->udev;
+	if (usb_submit_urb(sz->urb_in, GFP_ATOMIC)) {
+		dprintk("open result = -EIO error submitting urb",
+			sz->plugin.minor);
+		return -EIO;
+	}
+	sz->in_use++;
+
+	return 0;
+}
+
+static void streamzap_use_dec(void *data)
+{
+	struct usb_streamzap *sz = data;
+
+	if (!sz) {
+		dprintk("%s called with no context", -1, __func__);
+		return;
+	}
+	dprintk("set use dec", sz->plugin.minor);
+
+	if (sz->flush) {
+		sz->flush = 0;
+		del_timer_sync(&sz->flush_timer);
+	}
+
+	stop_timer(sz);
+
+	usb_kill_urb(sz->urb_in);
+
+	sz->in_use--;
+}
+
+static int streamzap_ioctl(struct inode *node, struct file *filep,
+			   unsigned int cmd, unsigned long arg)
+{
+	int result;
+
+	switch (cmd) {
+	case LIRC_GET_REC_RESOLUTION:
+		result = put_user(STREAMZAP_RESOLUTION, (unsigned long *) arg);
+		if (result)
+			return result;
+		break;
+	default:
+		return -ENOIOCTLCMD;
+	}
+	return 0;
+}
+
+/**
+ *	streamzap_disconnect
+ *
+ *	Called by the usb core when the device is removed from the system.
+ *
+ *	This routine guarantees that the driver will not submit any more urbs
+ *	by clearing dev->udev.  It is also supposed to terminate any currently
+ *	active urbs.  Unfortunately, usb_bulk_msg(), used in streamzap_read(),
+ *	does not provide any way to do this.
+ */
+static void streamzap_disconnect(struct usb_interface *interface)
+{
+	struct usb_streamzap *sz;
+	int errnum;
+	int minor;
+
+	sz = usb_get_intfdata(interface);
+
+	/*
+	 * unregister from the LIRC sub-system
+	 */
+
+	errnum = lirc_unregister_plugin(sz->plugin.minor);
+	if (errnum != 0)
+		dprintk("error in lirc_unregister: (returned %d)",
+			sz->plugin.minor, errnum);
+
+	lirc_buffer_free(&sz->delay_buf);
+	lirc_buffer_free(&sz->lirc_buf);
+
+	/*
+	 * unregister from the USB sub-system
+	 */
+
+	usb_free_urb(sz->urb_in);
+
+	usb_buffer_free(sz->udev, sz->buf_in_len, sz->buf_in, sz->dma_in);
+
+	minor = sz->plugin.minor;
+	kfree(sz);
+
+	printk(KERN_INFO DRIVER_NAME "[%d]: disconnected\n", minor);
+}
+
+static int streamzap_suspend(struct usb_interface *intf, pm_message_t message)
+{
+	struct usb_streamzap *sz = usb_get_intfdata(intf);
+
+	printk(DRIVER_NAME "[%d]: suspend\n", sz->plugin.minor);
+	if (sz->in_use) {
+		if (sz->flush) {
+			sz->flush = 0;
+			del_timer_sync(&sz->flush_timer);
+		}
+
+		stop_timer(sz);
+
+		usb_kill_urb(sz->urb_in);
+	}
+	return 0;
+}
+
+static int streamzap_resume(struct usb_interface *intf)
+{
+	struct usb_streamzap *sz = usb_get_intfdata(intf);
+
+	while (!lirc_buffer_empty(&sz->lirc_buf))
+		lirc_buffer_remove_1(&sz->lirc_buf);
+	while (!lirc_buffer_empty(&sz->delay_buf))
+		lirc_buffer_remove_1(&sz->delay_buf);
+
+	if (sz->in_use) {
+		sz->flush_timer.expires = jiffies + HZ;
+		sz->flush = 1;
+		add_timer(&sz->flush_timer);
+
+		sz->urb_in->dev = sz->udev;
+		if (usb_submit_urb(sz->urb_in, GFP_ATOMIC)) {
+			dprintk("open result = -EIO error submitting urb",
+				sz->plugin.minor);
+			return -EIO;
+		}
+	}
+	return 0;
+}
+
+#ifdef MODULE
+
+/**
+ *	usb_streamzap_init
+ */
+static int __init usb_streamzap_init(void)
+{
+	int result;
+
+	/* register this driver with the USB subsystem */
+
+	result = usb_register(&streamzap_driver);
+
+	if (result) {
+		err("usb_register failed. Error number %d",
+		    result);
+		return result;
+	}
+
+	printk(KERN_INFO DRIVER_NAME " " DRIVER_VERSION " registered\n");
+	return 0;
+}
+
+/**
+ *	usb_streamzap_exit
+ */
+static void __exit usb_streamzap_exit(void)
+{
+	/* deregister this driver with the USB subsystem */
+	usb_deregister(&streamzap_driver);
+}
+
+
+module_init(usb_streamzap_init);
+module_exit(usb_streamzap_exit);
+
+MODULE_AUTHOR("Christoph Bartelmus, Greg Wickham, Adrian Dewhurst");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+module_param(debug, bool, 0644);
+MODULE_PARM_DESC(debug, "Enable debugging messages");
+
+#endif /* MODULE */
-- 
1.6.0.1


^ permalink raw reply related	[flat|nested] 111+ messages in thread

* [PATCH 10/18] lirc driver for Igor Cesko's USB IR receiver
  2008-09-09  4:05                 ` [PATCH 09/18] lirc driver for the Streamzap PC Receiver Jarod Wilson
@ 2008-09-09  4:05                   ` Jarod Wilson
  2008-09-09  4:05                     ` [PATCH 11/18] lirc driver for the Technotrend " Jarod Wilson
  2008-09-11 15:22                   ` [PATCH 09/18] lirc driver for the Streamzap PC Receiver Jonathan Corbet
  1 sibling, 1 reply; 111+ messages in thread
From: Jarod Wilson @ 2008-09-09  4:05 UTC (permalink / raw)
  To: linux-kernel
  Cc: Jarod Wilson, Jarod Wilson, Janne Grunau, Christoph Bartelmus

Signed-off-by: Jarod Wilson <jarod@redhat.com>
Signed-off-by: Janne Grunau <j@jannau.net>
CC: Christoph Bartelmus <lirc@bartelmus.de>
---
 drivers/input/lirc/Kconfig            |    7 +
 drivers/input/lirc/Makefile           |    1 +
 drivers/input/lirc/lirc_igorplugusb.c |  619 +++++++++++++++++++++++++++++++++
 3 files changed, 627 insertions(+), 0 deletions(-)
 create mode 100644 drivers/input/lirc/lirc_igorplugusb.c

diff --git a/drivers/input/lirc/Kconfig b/drivers/input/lirc/Kconfig
index c105ac6..8579f96 100644
--- a/drivers/input/lirc/Kconfig
+++ b/drivers/input/lirc/Kconfig
@@ -40,6 +40,13 @@ config LIRC_I2C
 	  Driver for I2C-based IR receivers, such as those commonly
 	  found onboard Hauppauge PVR-150/250/350 video capture cards
 
+config LIRC_IGORPLUGUSB
+	tristate "Igor Cesko's USB IR Receiver"
+	default n
+	depends on LIRC_DEV
+	help
+	  Driver for Igor Cesko's USB IR Receiver
+
 config LIRC_IMON
 	tristate "Soundgraph IMON Receiver"
 	default n
diff --git a/drivers/input/lirc/Makefile b/drivers/input/lirc/Makefile
index b348110..68f8da2 100644
--- a/drivers/input/lirc/Makefile
+++ b/drivers/input/lirc/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_LIRC_DEV)		+= lirc_dev.o
 obj-$(CONFIG_LIRC_ATIUSB)	+= lirc_atiusb.o
 obj-$(CONFIG_LIRC_CMDIR)	+= lirc_cmdir.o
 obj-$(CONFIG_LIRC_I2C)		+= lirc_i2c.o
+obj-$(CONFIG_LIRC_IGORPLUGUSB)	+= lirc_igorplugusb.o
 obj-$(CONFIG_LIRC_IMON)		+= lirc_imon.o
 obj-$(CONFIG_LIRC_MCEUSB)	+= lirc_mceusb.o
 obj-$(CONFIG_LIRC_MCEUSB2)	+= lirc_mceusb2.o
diff --git a/drivers/input/lirc/lirc_igorplugusb.c b/drivers/input/lirc/lirc_igorplugusb.c
new file mode 100644
index 0000000..ab9bdd6
--- /dev/null
+++ b/drivers/input/lirc/lirc_igorplugusb.c
@@ -0,0 +1,619 @@
+/* lirc_igorplugusb - USB remote support for LIRC
+ *
+ * Supports the standard homebrew IgorPlugUSB receiver with Igor's firmware.
+ * See http://www.cesko.host.sk/IgorPlugUSB/IgorPlug-USB%20(AVR)_eng.htm
+ *
+ * The device can only record bursts of up to 36 pulses/spaces.
+ * Works fine with RC5. Longer commands lead to device buffer overrun.
+ * (Maybe a better firmware or a microcontroller with more ram can help?)
+ *
+ * Version 0.1  [beta status]
+ *
+ * Copyright (C) 2004 Jan M. Hochstein
+ *	<hochstein@algo.informatik.tu-darmstadt.de>
+ *
+ * This driver was derived from:
+ *   Paul Miller <pmiller9@users.sourceforge.net>
+ *      "lirc_atiusb" module
+ *   Vladimir Dergachev <volodya@minspring.com>'s 2002
+ *      "USB ATI Remote support" (input device)
+ *   Adrian Dewhurst <sailor-lk@sailorfrag.net>'s 2002
+ *      "USB StreamZap remote driver" (LIRC)
+ *   Artur Lipowski <alipowski@kki.net.pl>'s 2002
+ *      "lirc_dev" and "lirc_gpio" LIRC modules
+ *
+ */
+
+/*
+ * 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>
+
+#include <linux/autoconf.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/kmod.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/ioctl.h>
+#include <linux/fs.h>
+#include <linux/usb.h>
+#include <linux/poll.h>
+#include <linux/smp_lock.h>
+#include <linux/time.h>
+
+#include "lirc.h"
+#include "lirc_dev.h"
+
+
+/* module identification */
+#define DRIVER_VERSION		"0.1"
+#define DRIVER_AUTHOR		\
+	"Jan M. Hochstein <hochstein@algo.informatik.tu-darmstadt.de>"
+#define DRIVER_DESC		"USB remote driver for LIRC"
+#define DRIVER_NAME		"lirc_igorplugusb"
+
+/* debugging support */
+#ifdef CONFIG_USB_DEBUG
+static int debug = 1;
+#else
+static int debug;
+#endif
+
+#define dprintk(fmt, args...)					\
+	do {							\
+		if (debug)					\
+			printk(KERN_DEBUG fmt, ## args);	\
+	} while (0)
+
+/* general constants */
+#define SUCCESS		 0
+
+/* One mode2 pulse/space has 4 bytes. */
+#define CODE_LENGTH	     sizeof(int)
+
+/* Igor's firmware cannot record bursts longer than 36. */
+#define DEVICE_BUFLEN	   36
+
+/** Header at the beginning of the device's buffer:
+	unsigned char data_length
+	unsigned char data_start    (!=0 means ring-buffer overrun)
+	unsigned char counter       (incremented by each burst)
+**/
+#define DEVICE_HEADERLEN	3
+
+/* This is for the gap */
+#define ADDITIONAL_LIRC_BYTES   2
+
+/* times to poll per second */
+#define SAMPLE_RATE	     100
+static int sample_rate = SAMPLE_RATE;
+
+
+/**** Igor's USB Request Codes */
+
+#define SET_INFRABUFFER_EMPTY   1
+/**
+ * Params: none
+ * Answer: empty
+ *
+**/
+
+#define GET_INFRACODE	   2
+/**
+ * Params:
+ *   wValue: offset to begin reading infra buffer
+ *
+ * Answer: infra data
+ *
+**/
+
+#define SET_DATAPORT_DIRECTION  3
+/**
+ * Params:
+ *   wValue: (byte) 1 bit for each data port pin (0=in, 1=out)
+ *
+ * Answer: empty
+ *
+**/
+
+#define GET_DATAPORT_DIRECTION  4
+/**
+ * Params: none
+ *
+ * Answer: (byte) 1 bit for each data port pin (0=in, 1=out)
+ *
+**/
+
+#define SET_OUT_DATAPORT	5
+/**
+ * Params:
+ *   wValue: byte to write to output data port
+ *
+ * Answer: empty
+ *
+**/
+
+#define GET_OUT_DATAPORT	6
+/**
+ * Params: none
+ *
+ * Answer: least significant 3 bits read from output data port
+ *
+**/
+
+#define GET_IN_DATAPORT	 7
+/**
+ * Params: none
+ *
+ * Answer: least significant 3 bits read from input data port
+ *
+**/
+
+#define READ_EEPROM	     8
+/**
+ * Params:
+ *   wValue: offset to begin reading EEPROM
+ *
+ * Answer: EEPROM bytes
+ *
+**/
+
+#define WRITE_EEPROM	    9
+/**
+ * Params:
+ *   wValue: offset to EEPROM byte
+ *   wIndex: byte to write
+ *
+ * Answer: empty
+ *
+**/
+
+#define SEND_RS232	      10
+/**
+ * Params:
+ *   wValue: byte to send
+ *
+ * Answer: empty
+ *
+**/
+
+#define RECV_RS232	      11
+/**
+ * Params: none
+ *
+ * Answer: byte received
+ *
+**/
+
+#define SET_RS232_BAUD	  12
+/**
+ * Params:
+ *   wValue: byte to write to UART bit rate register (UBRR)
+ *
+ * Answer: empty
+ *
+**/
+
+#define GET_RS232_BAUD	  13
+/**
+ * Params: none
+ *
+ * Answer: byte read from UART bit rate register (UBRR)
+ *
+**/
+
+
+/* data structure for each usb remote */
+struct irctl {
+
+	/* usb */
+	struct usb_device *usbdev;
+	struct urb *urb_in;
+	int devnum;
+
+	unsigned char *buf_in;
+	unsigned int len_in;
+	int in_space;
+	struct timeval last_time;
+
+	dma_addr_t dma_in;
+
+	/* lirc */
+	struct lirc_plugin *p;
+
+	/* handle sending (init strings) */
+	int send_flags;
+	wait_queue_head_t wait_out;
+};
+
+static int unregister_from_lirc(struct irctl *ir)
+{
+	struct lirc_plugin *p = ir->p;
+	int devnum;
+
+	if (!ir->p)
+		return -EINVAL;
+
+	devnum = ir->devnum;
+	dprintk(DRIVER_NAME "[%d]: unregister from lirc called\n", devnum);
+
+	lirc_unregister_plugin(p->minor);
+
+	printk(DRIVER_NAME "[%d]: usb remote disconnected\n", devnum);
+
+	lirc_buffer_free(p->rbuf);
+	kfree(p->rbuf);
+	kfree(p);
+	kfree(ir);
+	ir->p = NULL;
+	return SUCCESS;
+}
+
+static int set_use_inc(void *data)
+{
+	struct irctl *ir = data;
+
+	if (!ir) {
+		printk(DRIVER_NAME "[?]: set_use_inc called with no context\n");
+		return -EIO;
+	}
+	dprintk(DRIVER_NAME "[%d]: set use inc\n", ir->devnum);
+
+	if (!ir->usbdev)
+		return -ENODEV;
+
+	return SUCCESS;
+}
+
+static void set_use_dec(void *data)
+{
+	struct irctl *ir = data;
+
+	if (!ir) {
+		printk(DRIVER_NAME "[?]: set_use_dec called with no context\n");
+		return;
+	}
+	dprintk(DRIVER_NAME "[%d]: set use dec\n", ir->devnum);
+}
+
+
+/**
+ * Called in user context.
+ * return 0 if data was added to the buffer and
+ * -ENODATA if none was available. This should add some number of bits
+ * evenly divisible by code_length to the buffer
+**/
+static int usb_remote_poll(void *data, struct lirc_buffer *buf)
+{
+	int ret;
+	struct irctl *ir = (struct irctl *)data;
+
+	if (!ir->usbdev)  /* Has the device been removed? */
+		return -ENODEV;
+
+	memset(ir->buf_in, 0, ir->len_in);
+
+	ret = usb_control_msg(
+	      ir->usbdev, usb_rcvctrlpipe(ir->usbdev, 0),
+	      GET_INFRACODE, USB_TYPE_VENDOR|USB_DIR_IN,
+	      0/* offset */, /*unused*/0,
+	      ir->buf_in, ir->len_in,
+	      /*timeout*/HZ * USB_CTRL_GET_TIMEOUT);
+	if (ret > 0) {
+		int i = DEVICE_HEADERLEN;
+		int code, timediff;
+		struct timeval now;
+
+		if (ret <= 1)  /* ACK packet has 1 byte --> ignore */
+			return -ENODATA;
+
+		dprintk(DRIVER_NAME ": 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(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)
+			timediff = PULSE_MASK;
+		else {
+			timediff *= 1000000;
+			timediff += now.tv_usec - ir->last_time.tv_usec;
+		}
+		ir->last_time.tv_sec = now.tv_sec;
+		ir->last_time.tv_usec = now.tv_usec;
+
+		/* create leading gap  */
+		code = timediff;
+		lirc_buffer_write_n(buf, (unsigned char *)&code, 1);
+		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;
+			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;
+		}
+
+		ret = usb_control_msg(
+		      ir->usbdev, usb_rcvctrlpipe(ir->usbdev, 0),
+		      SET_INFRABUFFER_EMPTY, USB_TYPE_VENDOR|USB_DIR_IN,
+		      /*unused*/0, /*unused*/0,
+		      /*dummy*/ir->buf_in, /*dummy*/ir->len_in,
+		      /*timeout*/HZ * USB_CTRL_GET_TIMEOUT);
+		if (ret < 0)
+			printk(DRIVER_NAME "[%d]: SET_INFRABUFFER_EMPTY: "
+			       "error %d\n", ir->devnum, ret);
+		return SUCCESS;
+	} else
+		printk(DRIVER_NAME "[%d]: GET_INFRACODE: error %d\n",
+			ir->devnum, ret);
+
+	return -ENODATA;
+}
+
+
+
+static int usb_remote_probe(struct usb_interface *intf,
+				const struct usb_device_id *id)
+{
+	struct usb_device *dev = NULL;
+	struct usb_host_interface *idesc = NULL;
+	struct usb_host_endpoint *ep_ctl2;
+	struct irctl *ir = NULL;
+	struct lirc_plugin *plugin = NULL;
+	struct lirc_buffer *rbuf = NULL;
+	int devnum, pipe, maxp, bytes_in_key;
+	int minor = 0;
+	char buf[63], name[128] = "";
+	int mem_failure = 0;
+	int ret;
+
+	dprintk(DRIVER_NAME ": usb probe called.\n");
+
+	dev = interface_to_usbdev(intf);
+
+	idesc = intf->cur_altsetting;  /* in 2.6.6 */
+
+	if (idesc->desc.bNumEndpoints != 1)
+		return -ENODEV;
+	ep_ctl2 = idesc->endpoint;
+	if (((ep_ctl2->desc.bEndpointAddress & USB_ENDPOINT_DIR_MASK)
+	    != USB_DIR_IN)
+	    || (ep_ctl2->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+	    != USB_ENDPOINT_XFER_CONTROL)
+		return -ENODEV;
+	pipe = usb_rcvctrlpipe(dev, ep_ctl2->desc.bEndpointAddress);
+	devnum = dev->devnum;
+	maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
+
+	bytes_in_key = CODE_LENGTH;
+
+	dprintk(DRIVER_NAME "[%d]: bytes_in_key=%d maxp=%d\n",
+		devnum, bytes_in_key, maxp);
+
+
+	/* allocate kernel memory */
+	mem_failure = 0;
+	ir = kmalloc(sizeof(struct irctl), GFP_KERNEL);
+	if (!ir) {
+		mem_failure = 1;
+		goto mem_failure_switch;
+	}
+
+	memset(ir, 0, sizeof(struct irctl));
+
+	plugin = kmalloc(sizeof(struct lirc_plugin), GFP_KERNEL);
+	if (!plugin) {
+		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, bytes_in_key,
+			DEVICE_BUFLEN+ADDITIONAL_LIRC_BYTES)) {
+		mem_failure = 4;
+		goto mem_failure_switch;
+	}
+
+	ir->buf_in = usb_buffer_alloc(dev,
+			      DEVICE_BUFLEN+DEVICE_HEADERLEN,
+			      GFP_ATOMIC, &ir->dma_in);
+	if (!ir->buf_in) {
+		mem_failure = 5;
+		goto mem_failure_switch;
+	}
+
+	memset(plugin, 0, sizeof(struct lirc_plugin));
+
+	strcpy(plugin->name, DRIVER_NAME " ");
+	plugin->minor = -1;
+	plugin->code_length = bytes_in_key*8; /* in bits */
+	plugin->features = LIRC_CAN_REC_MODE2;
+	plugin->data = ir;
+	plugin->rbuf = rbuf;
+	plugin->set_use_inc = &set_use_inc;
+	plugin->set_use_dec = &set_use_dec;
+	plugin->sample_rate = sample_rate;    /* per second */
+	plugin->add_to_buf = &usb_remote_poll;
+	plugin->dev = &dev->dev;
+	plugin->owner = THIS_MODULE;
+
+	init_waitqueue_head(&ir->wait_out);
+
+	minor = lirc_register_plugin(plugin);
+	if (minor < 0)
+		mem_failure = 9;
+
+mem_failure_switch:
+
+	/* free allocated memory in case of failure */
+	switch (mem_failure) {
+	case 9:
+		usb_buffer_free(dev, DEVICE_BUFLEN+DEVICE_HEADERLEN,
+			ir->buf_in, ir->dma_in);
+	case 5:
+		lirc_buffer_free(rbuf);
+	case 4:
+		kfree(rbuf);
+	case 3:
+		kfree(plugin);
+	case 2:
+		kfree(ir);
+	case 1:
+		printk(DRIVER_NAME "[%d]: out of memory (code=%d)\n",
+			devnum, mem_failure);
+		return -ENOMEM;
+	}
+
+	plugin->minor = minor;
+	ir->p = plugin;
+	ir->devnum = devnum;
+	ir->usbdev = dev;
+	ir->len_in = DEVICE_BUFLEN+DEVICE_HEADERLEN;
+	ir->in_space = 1; /* First mode2 event is a space. */
+	do_gettimeofday(&ir->last_time);
+
+	if (dev->descriptor.iManufacturer
+		&& usb_string(dev, dev->descriptor.iManufacturer, buf, 63) > 0)
+		strncpy(name, buf, 128);
+	if (dev->descriptor.iProduct
+		&& usb_string(dev, dev->descriptor.iProduct, buf, 63) > 0)
+		snprintf(name, 128, "%s %s", name, buf);
+	printk(DRIVER_NAME "[%d]: %s on usb%d:%d\n", devnum, name,
+	       dev->bus->busnum, devnum);
+
+	/* clear device buffer */
+	ret = usb_control_msg(ir->usbdev, usb_rcvctrlpipe(ir->usbdev, 0),
+		SET_INFRABUFFER_EMPTY, USB_TYPE_VENDOR|USB_DIR_IN,
+		/*unused*/0, /*unused*/0,
+		/*dummy*/ir->buf_in, /*dummy*/ir->len_in,
+		/*timeout*/HZ * USB_CTRL_GET_TIMEOUT);
+	if (ret < 0)
+		printk(DRIVER_NAME "[%d]: SET_INFRABUFFER_EMPTY: error %d\n",
+			devnum, ret);
+
+	usb_set_intfdata(intf, ir);
+	return SUCCESS;
+}
+
+
+static void usb_remote_disconnect(struct usb_interface *intf)
+{
+	struct usb_device *dev = interface_to_usbdev(intf);
+	struct irctl *ir = usb_get_intfdata(intf);
+	usb_set_intfdata(intf, NULL);
+
+	if (!ir || !ir->p)
+		return;
+
+	ir->usbdev = NULL;
+	wake_up_all(&ir->wait_out);
+
+	usb_buffer_free(dev, ir->len_in, ir->buf_in, ir->dma_in);
+
+	unregister_from_lirc(ir);
+}
+
+static struct usb_device_id usb_remote_id_table[] = {
+	/* Igor Plug USB (Atmel's Manufact. ID) */
+	{ USB_DEVICE(0x03eb, 0x0002) },
+
+	/* Terminating entry */
+	{ }
+};
+
+static struct usb_driver usb_remote_driver = {
+	.name =		DRIVER_NAME,
+	.probe =	usb_remote_probe,
+	.disconnect =	usb_remote_disconnect,
+	.id_table =	usb_remote_id_table
+};
+
+static int __init usb_remote_init(void)
+{
+	int i;
+
+	printk(KERN_INFO "\n"
+	       DRIVER_NAME ": " DRIVER_DESC " v" DRIVER_VERSION "\n");
+	printk(DRIVER_NAME ": " DRIVER_AUTHOR "\n");
+	dprintk(DRIVER_NAME ": debug mode enabled\n");
+
+#ifdef MODULE
+	request_module("lirc_dev");
+#endif
+
+	i = usb_register(&usb_remote_driver);
+	if (i < 0) {
+		printk(DRIVER_NAME ": usb register failed, result = %d\n", i);
+		return -ENODEV;
+	}
+
+	return SUCCESS;
+}
+
+static void __exit usb_remote_exit(void)
+{
+	usb_deregister(&usb_remote_driver);
+}
+
+#ifdef MODULE
+module_init(usb_remote_init);
+module_exit(usb_remote_exit);
+
+#include <linux/vermagic.h>
+MODULE_INFO(vermagic, VERMAGIC_STRING);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(usb, usb_remote_id_table);
+
+module_param(sample_rate, int, 0644);
+MODULE_PARM_DESC(sample_rate, "Sampling rate in Hz (default: 100)");
+
+#else /* not MODULE */
+subsys_initcall(usb_remote_driver);
+
+#endif /* MODULE */
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
-- 
1.6.0.1


^ permalink raw reply related	[flat|nested] 111+ messages in thread

* [PATCH 11/18] lirc driver for the Technotrend USB IR receiver
  2008-09-09  4:05                   ` [PATCH 10/18] lirc driver for Igor Cesko's USB IR receiver Jarod Wilson
@ 2008-09-09  4:05                     ` Jarod Wilson
  2008-09-09  4:05                       ` [PATCH 12/18] lirc driver for the Sasem OnAir and Dign HV5 receivers Jarod Wilson
  0 siblings, 1 reply; 111+ messages in thread
From: Jarod Wilson @ 2008-09-09  4:05 UTC (permalink / raw)
  To: linux-kernel
  Cc: Jarod Wilson, Jarod Wilson, Janne Grunau, Christoph Bartelmus

Signed-off-by: Jarod Wilson <jarod@redhat.com>
Signed-off-by: Janne Grunau <j@jannau.net>
CC: Christoph Bartelmus <lirc@bartelmus.de>
---
 drivers/input/lirc/Kconfig        |    6 +
 drivers/input/lirc/Makefile       |    1 +
 drivers/input/lirc/lirc_ttusbir.c |  400 +++++++++++++++++++++++++++++++++++++
 3 files changed, 407 insertions(+), 0 deletions(-)
 create mode 100644 drivers/input/lirc/lirc_ttusbir.c

diff --git a/drivers/input/lirc/Kconfig b/drivers/input/lirc/Kconfig
index 8579f96..7e924fe 100644
--- a/drivers/input/lirc/Kconfig
+++ b/drivers/input/lirc/Kconfig
@@ -82,4 +82,10 @@ config LIRC_STREAMZAP
 	help
 	  Driver for the Streamzap PC Receiver
 
+config LIRC_TTUSBIR
+	tristate "Technotrend USB IR Receiver"
+	default n
+	depends on LIRC_DEV
+	help
+	  Driver for the Technotrend USB IR Receiver
 endif
diff --git a/drivers/input/lirc/Makefile b/drivers/input/lirc/Makefile
index 68f8da2..2ee00d1 100644
--- a/drivers/input/lirc/Makefile
+++ b/drivers/input/lirc/Makefile
@@ -15,3 +15,4 @@ obj-$(CONFIG_LIRC_MCEUSB)	+= lirc_mceusb.o
 obj-$(CONFIG_LIRC_MCEUSB2)	+= lirc_mceusb2.o
 obj-$(CONFIG_LIRC_SERIAL)	+= lirc_serial.o
 obj-$(CONFIG_LIRC_STREAMZAP)	+= lirc_streamzap.o
+obj-$(CONFIG_LIRC_TTUSBIR)	+= lirc_ttusbir.o
diff --git a/drivers/input/lirc/lirc_ttusbir.c b/drivers/input/lirc/lirc_ttusbir.c
new file mode 100644
index 0000000..9ed9c7b
--- /dev/null
+++ b/drivers/input/lirc/lirc_ttusbir.c
@@ -0,0 +1,400 @@
+/****************************************************************************
+ ** lirc_ttusbir.c ***********************************************************
+ ****************************************************************************
+ *
+ * lirc_ttusbir - LIRC device driver for the TechnoTrend USB IR Receiver
+ *
+ * Copyright (C) 2007 Stefan Macher <st_maker-lirc@yahoo.de>
+ *
+ *  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
+ *
+ */
+
+/* This LIRC driver provides access to the TechnoTrend USB IR Receiver.
+ * The receiver delivers the IR signal as raw sampled true/false data in
+ * isochronous USB packets each of size 128 byte.
+ * Currently the driver reduces the sampling rate by factor of 8 as this
+ * is still more than enough to decode RC-5 - others should be analyzed.
+ * But the driver does not rely on RC-5 it should be able to decode every
+ * IR signal that is not too fast.
+ */
+
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+
+#include "lirc.h"
+#include "lirc_dev.h"
+
+MODULE_DESCRIPTION("TechnoTrend USB IR device driver for LIRC");
+MODULE_AUTHOR("Stefan Macher (st_maker-lirc@yahoo.de)");
+MODULE_LICENSE("GPL");
+
+/* #define DEBUG */
+#ifdef DEBUG
+#define DPRINTK printk
+#else
+#define DPRINTK(_x_, a...)
+#endif
+
+/* function declarations */
+static int probe(struct usb_interface *intf, const struct usb_device_id *id);
+static void disconnect(struct usb_interface *intf);
+static void urb_complete(struct urb *urb);
+static int set_use_inc(void *data);
+static void set_use_dec(void *data);
+
+static int num_urbs = 2;
+module_param(num_urbs, int, 0444);
+MODULE_PARM_DESC(num_urbs,
+		 "Number of URBs in queue. Try to increase to 4 in case "
+		 "of problems (default: 2; minimum: 2)");
+
+/* table of devices that work with this driver */
+static struct usb_device_id device_id_table[] = {
+	/* TechnoTrend USB IR Receiver */
+	{ USB_DEVICE(0x0B48, 0x2003) },
+	/* Terminating entry */
+	{ }
+};
+MODULE_DEVICE_TABLE(usb, device_id_table);
+
+/* USB driver definition */
+static struct usb_driver driver = {
+	.name = "TTUSBIR",
+	.id_table = &(device_id_table[0]),
+	.probe = probe,
+	.disconnect = disconnect,
+};
+
+/* USB device definition */
+struct ttusbir_device {
+	struct usb_driver *driver;
+	struct usb_device *udev;
+	struct usb_interface *interf;
+	struct usb_class_driver class_driver;
+	unsigned int ifnum; /* Interface number to use */
+	unsigned int alt_setting; /* alternate setting to use */
+	unsigned int endpoint; /* Endpoint to use */
+	struct urb **urb; /* num_urb URB pointers*/
+	char **buffer; /* 128 byte buffer for each URB */
+	struct lirc_buffer rbuf; /* Buffer towards LIRC */
+	struct lirc_plugin plugin;
+	int minor;
+	int last_pulse; /* remembers if last received byte was pulse or space */
+	int last_num; /* remembers how many last bytes appeared */
+	int opened;
+};
+
+/*************************************
+ * LIRC specific functions
+ */
+static int set_use_inc(void *data)
+{
+	int i;
+	struct ttusbir_device *ttusbir = data;
+
+	DPRINTK("Sending first URBs\n");
+	/* @TODO Do I need to check if I am already opened */
+	ttusbir->opened = 1;
+
+	for (i = 0; i < num_urbs; i++)
+		usb_submit_urb(ttusbir->urb[i], GFP_KERNEL);
+
+	return 0;
+}
+
+static void set_use_dec(void *data)
+{
+	struct ttusbir_device *ttusbir = data;
+
+	DPRINTK("Device closed\n");
+
+	ttusbir->opened = 0;
+}
+
+/*************************************
+ * USB specific functions
+ */
+
+/* This mapping table is used to do a very simple filtering of the
+ * input signal.
+ * For a value with at least 4 bits set it returns 0xFF otherwise
+ * 0x00.  For faster IR signals this can not be used. But for RC-5 we
+ * still have about 14 samples per pulse/space, i.e. we sample with 14
+ * times higher frequency than the signal frequency */
+const unsigned char map_table[] =
+{
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
+	0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
+	0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
+	0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
+	0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
+	0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
+	0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
+	0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
+	0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
+	0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
+	0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
+	0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
+	0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
+	0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+static void urb_complete(struct urb *urb)
+{
+	struct ttusbir_device *ttusbir;
+	unsigned char *buf;
+	int i;
+	int l;
+
+	ttusbir = urb->context;
+
+	if (!ttusbir->opened)
+		return;
+
+	buf = (unsigned char *)urb->transfer_buffer;
+
+	for (i = 0; i < 128; i++) {
+		/* Here we do the filtering and some kind of down sampling */
+		buf[i] = ~map_table[buf[i]];
+		if (ttusbir->last_pulse == buf[i]) {
+			if (ttusbir->last_num < PULSE_MASK/63)
+				ttusbir->last_num++;
+		/* else we are in a idle period and do not need to
+		 * increment any longer */
+		} else {
+			l = ttusbir->last_num * 62; /* about 62 = us/byte */
+			if (ttusbir->last_pulse) /* pulse or space? */
+				l |= PULSE_BIT;
+			if (!lirc_buffer_full(&ttusbir->rbuf)) {
+				lirc_buffer_write_1(&ttusbir->rbuf, (void *)&l);
+				wake_up_interruptible(&ttusbir->rbuf.wait_poll);
+			}
+			ttusbir->last_num = 0;
+			ttusbir->last_pulse = buf[i];
+		}
+	}
+	usb_submit_urb(urb, GFP_ATOMIC); /* keep data rolling :-) */
+}
+
+/* Called whenever the USB subsystem thinks we could be the right driver
+   to handle this device
+*/
+static int probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+	int alt_set, endp;
+	int found = 0;
+	int i, j;
+	int struct_size;
+	struct usb_host_interface *host_interf;
+	struct usb_interface_descriptor *interf_desc;
+	struct usb_host_endpoint *host_endpoint;
+	struct ttusbir_device *ttusbir;
+
+	DPRINTK("Module ttusbir probe\n");
+
+	/* To reduce memory fragmentation we use only one allocation */
+	struct_size =  sizeof(struct ttusbir_device) +
+		(sizeof(struct urb *) * num_urbs) +
+		(sizeof(char *) * num_urbs) +
+		(num_urbs * 128);
+	ttusbir = kmalloc(struct_size, GFP_KERNEL);
+	if (!ttusbir)
+		return -ENOMEM;
+	memset(ttusbir, 0, struct_size);
+
+	ttusbir->urb = (struct urb **)((char *)ttusbir +
+				      sizeof(struct ttusbir_device));
+	ttusbir->buffer = (char **)((char *)ttusbir->urb +
+				   (sizeof(struct urb *) * num_urbs));
+	for (i = 0; i < num_urbs; i++)
+		ttusbir->buffer[i] = (char *)ttusbir->buffer +
+			(sizeof(char *)*num_urbs) + (i * 128);
+
+	ttusbir->driver = &driver;
+	ttusbir->alt_setting = -1;
+	/* @TODO check if error can be returned */
+	ttusbir->udev = usb_get_dev(interface_to_usbdev(intf));
+	ttusbir->interf = intf;
+	ttusbir->last_pulse = 0x00;
+	ttusbir->last_num = 0;
+
+	/* Now look for interface setting we can handle
+	   We are searching for the alt setting where end point
+	   0x82 has max packet size 16
+	*/
+	for (alt_set = 0; alt_set < intf->num_altsetting && !found; alt_set++) {
+		host_interf = &intf->altsetting[alt_set];
+		interf_desc = &host_interf->desc;
+		for (endp = 0; endp < interf_desc->bNumEndpoints; endp++) {
+			host_endpoint = &host_interf->endpoint[endp];
+			if ((host_endpoint->desc.bEndpointAddress == 0x82) &&
+			    (host_endpoint->desc.wMaxPacketSize == 0x10)) {
+				ttusbir->alt_setting = alt_set;
+				ttusbir->endpoint = endp;
+				found = 1;
+				break;
+			}
+		}
+	}
+	if (ttusbir->alt_setting != -1)
+		DPRINTK("alt setting: %d\n", ttusbir->alt_setting);
+	else {
+		err("Could not find alternate setting\n");
+		kfree(ttusbir);
+		return -EINVAL;
+	}
+
+	/* OK lets setup this interface setting */
+	usb_set_interface(ttusbir->udev, 0, ttusbir->alt_setting);
+
+	/* Store device info in interface structure */
+	usb_set_intfdata(intf, ttusbir);
+
+	/* Register as a LIRC plugin */
+	if (lirc_buffer_init(&ttusbir->rbuf, sizeof(int), 256) < 0) {
+		err("Could not get memory for LIRC data buffer\n");
+		usb_set_intfdata(intf, NULL);
+		kfree(ttusbir);
+		return -ENOMEM;
+	}
+	strcpy(ttusbir->plugin.name, "TTUSBIR");
+	ttusbir->plugin.minor = -1;
+	ttusbir->plugin.code_length = 1;
+	ttusbir->plugin.sample_rate = 0;
+	ttusbir->plugin.data = ttusbir;
+	ttusbir->plugin.add_to_buf = NULL;
+	ttusbir->plugin.get_queue = NULL;
+	ttusbir->plugin.rbuf = &ttusbir->rbuf;
+	ttusbir->plugin.set_use_inc = set_use_inc;
+	ttusbir->plugin.set_use_dec = set_use_dec;
+	ttusbir->plugin.ioctl = NULL;
+	ttusbir->plugin.fops = NULL;
+	ttusbir->plugin.owner = THIS_MODULE;
+	ttusbir->plugin.features = LIRC_CAN_REC_MODE2;
+	ttusbir->minor = lirc_register_plugin(&ttusbir->plugin);
+	if (ttusbir->minor < 0) {
+		err("Error registering as LIRC plugin\n");
+		usb_set_intfdata(intf, NULL);
+		lirc_buffer_free(&ttusbir->rbuf);
+		kfree(ttusbir);
+		return -EIO;
+	}
+
+	/* Allocate and setup the URB that we will use to talk to the device */
+	for (i = 0; i < num_urbs; i++) {
+		ttusbir->urb[i] = usb_alloc_urb(8, GFP_KERNEL);
+		if (!ttusbir->urb[i]) {
+			err("Could not allocate memory for the URB\n");
+			for (j = i - 1; j >= 0; j--)
+				kfree(ttusbir->urb[j]);
+			lirc_buffer_free(&ttusbir->rbuf);
+			lirc_unregister_plugin(ttusbir->minor);
+			kfree(ttusbir);
+			usb_set_intfdata(intf, NULL);
+			return -ENOMEM;
+		}
+		ttusbir->urb[i]->dev = ttusbir->udev;
+		ttusbir->urb[i]->context = ttusbir;
+		ttusbir->urb[i]->pipe = usb_rcvisocpipe(ttusbir->udev,
+							ttusbir->endpoint);
+		ttusbir->urb[i]->interval = 1;
+		ttusbir->urb[i]->transfer_flags = URB_ISO_ASAP;
+		ttusbir->urb[i]->transfer_buffer = &ttusbir->buffer[i][0];
+		ttusbir->urb[i]->complete = urb_complete;
+		ttusbir->urb[i]->number_of_packets = 8;
+		ttusbir->urb[i]->transfer_buffer_length = 128;
+		for (j = 0; j < 8; j++) {
+			ttusbir->urb[i]->iso_frame_desc[j].offset = j*16;
+			ttusbir->urb[i]->iso_frame_desc[j].length = 16;
+		}
+	}
+	return 0;
+}
+
+/* Called when the driver is unloaded or the device is unplugged
+ */
+static void disconnect(struct usb_interface *intf)
+{
+	int i;
+	struct ttusbir_device *ttusbir;
+
+	DPRINTK("Module ttusbir disconnect\n");
+
+	ttusbir = (struct ttusbir_device *) usb_get_intfdata(intf);
+	usb_set_intfdata(intf, NULL);
+	lirc_unregister_plugin(ttusbir->minor);
+	DPRINTK("unregistered\n");
+
+	for (i = 0; i < num_urbs; i++) {
+		usb_kill_urb(ttusbir->urb[i]);
+		usb_free_urb(ttusbir->urb[i]);
+	}
+	DPRINTK("URBs killed\n");
+	lirc_buffer_free(&ttusbir->rbuf);
+	kfree(ttusbir);
+}
+
+static int ttusbir_init_module(void)
+{
+	int result;
+
+	DPRINTK(KERN_DEBUG "Module ttusbir init\n");
+
+	/* register this driver with the USB subsystem */
+	result = usb_register(&driver);
+	if (result)
+		err("usb_register failed. Error number %d", result);
+	return result;
+}
+
+static void ttusbir_exit_module(void)
+{
+	printk(KERN_DEBUG "Module ttusbir exit\n");
+	/* deregister this driver with the USB subsystem */
+	usb_deregister(&driver);
+}
+
+#ifdef MODULE
+module_init(ttusbir_init_module);
+module_exit(ttusbir_exit_module);
+
+#else
+subsys_initcall(ttusbir_init_module);
+
+#endif /* MODULE */
-- 
1.6.0.1


^ permalink raw reply related	[flat|nested] 111+ messages in thread

* [PATCH 12/18] lirc driver for the Sasem OnAir and Dign HV5 receivers
  2008-09-09  4:05                     ` [PATCH 11/18] lirc driver for the Technotrend " Jarod Wilson
@ 2008-09-09  4:05                       ` Jarod Wilson
  2008-09-09  4:05                         ` [PATCH 13/18] lirc driver for ITE8709 CIR port receiver Jarod Wilson
  0 siblings, 1 reply; 111+ messages in thread
From: Jarod Wilson @ 2008-09-09  4:05 UTC (permalink / raw)
  To: linux-kernel
  Cc: Jarod Wilson, Jarod Wilson, Janne Grunau, Christoph Bartelmus

Signed-off-by: Jarod Wilson <jarod@redhat.com>
Signed-off-by: Janne Grunau <j@jannau.net>
CC: Christoph Bartelmus <lirc@bartelmus.de>
---
 drivers/input/lirc/Kconfig      |    7 +
 drivers/input/lirc/Makefile     |    1 +
 drivers/input/lirc/lirc_sasem.c |  969 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 977 insertions(+), 0 deletions(-)
 create mode 100644 drivers/input/lirc/lirc_sasem.c

diff --git a/drivers/input/lirc/Kconfig b/drivers/input/lirc/Kconfig
index 7e924fe..2731c40 100644
--- a/drivers/input/lirc/Kconfig
+++ b/drivers/input/lirc/Kconfig
@@ -68,6 +68,13 @@ config LIRC_MCEUSB2
 	help
 	  Driver for the Microsoft Media Center Ed. Receiver, v2
 
+config LIRC_SASEM
+	tristate "Sasem USB IR Remote"
+	default n
+	depends on LIRC_DEV
+	help
+	  Driver for the Sasem OnAir Remocon-V or Dign HV5 HTPC IR/VFD Module
+
 config LIRC_SERIAL
 	tristate "Homebrew Serial Port Receiver"
 	default n
diff --git a/drivers/input/lirc/Makefile b/drivers/input/lirc/Makefile
index 2ee00d1..1b2793d 100644
--- a/drivers/input/lirc/Makefile
+++ b/drivers/input/lirc/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_LIRC_IGORPLUGUSB)	+= lirc_igorplugusb.o
 obj-$(CONFIG_LIRC_IMON)		+= lirc_imon.o
 obj-$(CONFIG_LIRC_MCEUSB)	+= lirc_mceusb.o
 obj-$(CONFIG_LIRC_MCEUSB2)	+= lirc_mceusb2.o
+obj-$(CONFIG_LIRC_SASEM)	+= lirc_sasem.o
 obj-$(CONFIG_LIRC_SERIAL)	+= lirc_serial.o
 obj-$(CONFIG_LIRC_STREAMZAP)	+= lirc_streamzap.o
 obj-$(CONFIG_LIRC_TTUSBIR)	+= lirc_ttusbir.o
diff --git a/drivers/input/lirc/lirc_sasem.c b/drivers/input/lirc/lirc_sasem.c
new file mode 100644
index 0000000..003f492
--- /dev/null
+++ b/drivers/input/lirc/lirc_sasem.c
@@ -0,0 +1,969 @@
+/* lirc_sasem.c - USB remote support for LIRC
+ * Version 0.5
+ *
+ * Copyright (C) 2004-2005 Oliver Stabel <oliver.stabel@gmx.de>
+ *			 Tim Davies <tim@opensystems.net.au>
+ *
+ * This driver was derived from:
+ *   Venky Raju <dev@venky.ws>
+ *      "lirc_imon - "LIRC plugin/VFD driver for Ahanix/Soundgraph IMON IR/VFD"
+ *   Paul Miller <pmiller9@users.sourceforge.net>'s 2003-2004
+ *      "lirc_atiusb - USB remote support for LIRC"
+ *   Culver Consulting Services <henry@culcon.com>'s 2003
+ *      "Sasem OnAir VFD/IR USB driver"
+ *
+ *
+ * 2004/06/13   -   0.1
+ *		  initial version
+ *
+ * 2004/06/28   -   0.2
+ *		  added file system support to write data to VFD device (used
+ *		  in conjunction with LCDProc)
+ *
+ * 2004/11/22   -   0.3
+ *		  Ported to 2.6 kernel
+ *			- Tim Davies <tim@opensystems.net.au>
+ *
+ * 2005/03/29   -   0.4
+ *		  A few tidyups and keypress timings
+ *			- Tim Davies <tim@opensystems.net.au>
+ *
+ * 2005/06/23   -   0.5
+ *		  A complete rewrite (shamelessly) based on lirc_imon.c
+ *		  Tim Davies <tim@opensystems.net.au>
+ *
+ * NOTE - The LCDproc iMon driver should work with this module.  More info at
+ *	http://www.frogstorm.info/sasem
+ */
+
+/*
+ *  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>
+
+
+#include <linux/autoconf.h>
+
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/usb.h>
+
+#include "lirc.h"
+#include "lirc_dev.h"
+
+
+#define MOD_AUTHOR	"Oliver Stabel <oliver.stabel@gmx.de>, " \
+			"Tim Davies <tim@opensystems.net.au>"
+#define MOD_DESC	"USB Driver for Sasem Remote Controller V1.1"
+#define MOD_NAME	"lirc_sasem"
+#define MOD_VERSION	"0.5"
+
+#define VFD_MINOR_BASE	144	/* Same as LCD */
+#define DEVICE_NAME	"lcd%d"
+
+#define BUF_CHUNK_SIZE	8
+#define BUF_SIZE	128
+
+#define SUCCESS		0
+#define	TRUE		1
+#define FALSE		0
+
+#define IOCTL_LCD_CONTRAST 1
+
+/* ------------------------------------------------------------
+ *		     P R O T O T Y P E S
+ * ------------------------------------------------------------
+ */
+
+/* USB Callback prototypes */
+static int sasem_probe(struct usb_interface *interface,
+			const struct usb_device_id *id);
+static void sasem_disconnect(struct usb_interface *interface);
+static void usb_rx_callback(struct urb *urb);
+static void usb_tx_callback(struct urb *urb);
+
+/* VFD file_operations function prototypes */
+static int vfd_open(struct inode *inode, struct file *file);
+static int vfd_ioctl(struct inode *inode, struct file *file,
+				unsigned cmd, unsigned long arg);
+static int vfd_close(struct inode *inode, struct file *file);
+static ssize_t vfd_write(struct file *file, const char *buf,
+				size_t n_bytes, loff_t *pos);
+
+/* LIRC plugin function prototypes */
+static int ir_open(void *data);
+static void ir_close(void *data);
+
+/* Driver init/exit prototypes */
+static int __init sasem_init(void);
+static void __exit sasem_exit(void);
+
+/* ------------------------------------------------------------
+ *		     G L O B A L S
+ * ------------------------------------------------------------
+ */
+
+struct sasem_context {
+
+	struct usb_device *dev;
+	int vfd_isopen;			/* VFD port has been opened       */
+	unsigned int vfd_contrast;	/* VFD contrast		   */
+	int ir_isopen;			/* IR port has been opened	*/
+	int dev_present;		/* USB device presence	    */
+	struct mutex lock;		/* to lock this object	    */
+	wait_queue_head_t remove_ok;	/* For unexpected USB disconnects */
+
+	struct lirc_plugin *plugin;
+	struct usb_endpoint_descriptor *rx_endpoint;
+	struct usb_endpoint_descriptor *tx_endpoint;
+	struct urb *rx_urb;
+	struct urb *tx_urb;
+	unsigned char usb_rx_buf[8];
+	unsigned char usb_tx_buf[8];
+
+	struct tx_t {
+		unsigned char data_buf[32];  /* user data buffer	  */
+		struct completion finished;  /* wait for write to finish  */
+		atomic_t busy;		     /* write in progress	 */
+		int status;		     /* status of tx completion   */
+	} tx;
+
+	/* for dealing with repeat codes (wish there was a toggle bit!) */
+	struct timeval presstime;
+	char lastcode[8];
+	int codesaved;
+};
+
+#define LOCK_CONTEXT	mutex_lock(&context->lock)
+#define UNLOCK_CONTEXT	mutex_unlock(&context->lock)
+
+/* VFD file operations */
+static struct file_operations vfd_fops = {
+
+	.owner		= THIS_MODULE,
+	.open		= &vfd_open,
+	.write		= &vfd_write,
+	.ioctl		= &vfd_ioctl,
+	.release	= &vfd_close
+};
+
+/* USB Device ID for Sasem USB Control Board */
+static struct usb_device_id sasem_usb_id_table[] = {
+	/* Sasem USB Control Board */
+	{ USB_DEVICE(0x11ba, 0x0101) },
+	/* Terminiating entry */
+	{}
+};
+
+/* USB Device data */
+static struct usb_driver sasem_driver = {
+	.name		= MOD_NAME,
+	.probe		= sasem_probe,
+	.disconnect	= sasem_disconnect,
+	.id_table	= sasem_usb_id_table,
+};
+
+static struct usb_class_driver sasem_class = {
+	.name		= DEVICE_NAME,
+	.fops		= &vfd_fops,
+	.minor_base	= VFD_MINOR_BASE,
+};
+
+/* to prevent races between open() and disconnect() */
+static DECLARE_MUTEX(disconnect_sem);
+
+static int debug;
+
+
+/* ------------------------------------------------------------
+ *		     M O D U L E   C O D E
+ * ------------------------------------------------------------
+ */
+
+MODULE_AUTHOR(MOD_AUTHOR);
+MODULE_DESCRIPTION(MOD_DESC);
+MODULE_LICENSE("GPL");
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "Debug messages: 0=no, 1=yes (default: no)");
+
+static inline void delete_context(struct sasem_context *context)
+{
+	usb_free_urb(context->tx_urb);  /* VFD */
+	usb_free_urb(context->rx_urb);  /* IR */
+	lirc_buffer_free(context->plugin->rbuf);
+	kfree(context->plugin->rbuf);
+	kfree(context->plugin);
+	kfree(context);
+
+	if (debug)
+		info("%s: context deleted", __func__);
+}
+
+static inline void deregister_from_lirc(struct sasem_context *context)
+{
+	int retval;
+	int minor = context->plugin->minor;
+
+	retval = lirc_unregister_plugin(minor);
+	if (retval)
+		err("%s: unable to deregister from lirc (%d)",
+			__func__, retval);
+	else
+		info("Deregistered Sasem plugin (minor:%d)", minor);
+
+}
+
+/**
+ * Called when the VFD device (e.g. /dev/usb/lcd)
+ * is opened by the application.
+ */
+static int vfd_open(struct inode *inode, struct file *file)
+{
+	struct usb_interface *interface;
+	struct sasem_context *context = NULL;
+	int subminor;
+	int retval = SUCCESS;
+
+	/* prevent races with disconnect */
+	down(&disconnect_sem);
+
+	subminor = iminor(inode);
+	interface = usb_find_interface(&sasem_driver, subminor);
+	if (!interface) {
+		err("%s: could not find interface for minor %d",
+		    __func__, subminor);
+		retval = -ENODEV;
+		goto exit;
+	}
+	context = usb_get_intfdata(interface);
+
+	if (!context) {
+		err("%s: no context found for minor %d",
+					__func__, subminor);
+		retval = -ENODEV;
+		goto exit;
+	}
+
+	LOCK_CONTEXT;
+
+	if (context->vfd_isopen) {
+		err("%s: VFD port is already open", __func__);
+		retval = -EBUSY;
+	} else {
+		context->vfd_isopen = TRUE;
+		file->private_data = context;
+		info("VFD port opened");
+	}
+
+	UNLOCK_CONTEXT;
+
+exit:
+	up(&disconnect_sem);
+	return retval;
+}
+
+/**
+ * Called when the VFD device (e.g. /dev/usb/lcd)
+ * is closed by the application.
+ */
+static int vfd_ioctl(struct inode *inode, struct file *file,
+		     unsigned cmd, unsigned long arg)
+{
+	struct sasem_context *context = NULL;
+
+	context = (struct sasem_context *) file->private_data;
+
+	if (!context) {
+		err("%s: no context for device", __func__);
+		return -ENODEV;
+	}
+
+	LOCK_CONTEXT;
+
+	switch (cmd) {
+	case IOCTL_LCD_CONTRAST:
+		if (arg > 1000)
+			arg = 1000;
+		if (arg < 0)
+			arg = 0;
+		context->vfd_contrast = (unsigned int)arg;
+		break;
+	default:
+		info("Unknown IOCTL command");
+		UNLOCK_CONTEXT;
+		return -ENOIOCTLCMD;  /* not supported */
+	}
+
+	UNLOCK_CONTEXT;
+	return 0;
+}
+
+/**
+ * Called when the VFD device (e.g. /dev/usb/lcd)
+ * is closed by the application.
+ */
+static int vfd_close(struct inode *inode, struct file *file)
+{
+	struct sasem_context *context = NULL;
+	int retval = SUCCESS;
+
+	context = (struct sasem_context *) file->private_data;
+
+	if (!context) {
+		err("%s: no context for device", __func__);
+		return -ENODEV;
+	}
+
+	LOCK_CONTEXT;
+
+	if (!context->vfd_isopen) {
+		err("%s: VFD is not open", __func__);
+		retval = -EIO;
+	} else {
+		context->vfd_isopen = FALSE;
+		info("VFD port closed");
+		if (!context->dev_present && !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. */
+			UNLOCK_CONTEXT;
+			delete_context(context);
+			return retval;
+		}
+	}
+
+	UNLOCK_CONTEXT;
+	return retval;
+}
+
+/**
+ * Sends a packet to the VFD.
+ */
+static inline int send_packet(struct sasem_context *context)
+{
+	unsigned int pipe;
+	int interval = 0;
+	int retval = SUCCESS;
+
+	pipe = usb_sndintpipe(context->dev,
+			context->tx_endpoint->bEndpointAddress);
+	interval = context->tx_endpoint->bInterval;
+
+	usb_fill_int_urb(context->tx_urb, context->dev, pipe,
+		context->usb_tx_buf, sizeof(context->usb_tx_buf),
+		usb_tx_callback, context, interval);
+
+	context->tx_urb->actual_length = 0;
+
+	init_completion(&context->tx.finished);
+	atomic_set(&(context->tx.busy), 1);
+
+	retval =  usb_submit_urb(context->tx_urb, GFP_KERNEL);
+	if (retval != SUCCESS) {
+		atomic_set(&(context->tx.busy), 0);
+		err("%s: error submitting urb (%d)", __func__, retval);
+	} else {
+		/* Wait for tranmission to complete (or abort) */
+		UNLOCK_CONTEXT;
+		wait_for_completion(&context->tx.finished);
+		LOCK_CONTEXT;
+
+		retval = context->tx.status;
+		if (retval != SUCCESS)
+			err("%s: packet tx failed (%d)", __func__, retval);
+	}
+
+	return retval;
+}
+
+/**
+ * Writes data to the VFD.  The Sasem VFD is 2x16 characters
+ * and requires data in 9 consecutive USB interrupt packets,
+ * each packet carrying 8 bytes.
+ */
+static ssize_t vfd_write(struct file *file, const char *buf,
+				size_t n_bytes, loff_t *pos)
+{
+	int i;
+	int retval = SUCCESS;
+	struct sasem_context *context;
+
+	context = (struct sasem_context *) file->private_data;
+	if (!context) {
+		err("%s: no context for device", __func__);
+		return -ENODEV;
+	}
+
+	LOCK_CONTEXT;
+
+	if (!context->dev_present) {
+		err("%s: no Sasem device present", __func__);
+		retval = -ENODEV;
+		goto exit;
+	}
+
+	if (n_bytes <= 0 || n_bytes > 32) {
+		err("%s: invalid payload size", __func__);
+		retval = -EINVAL;
+		goto exit;
+	}
+
+	retval = copy_from_user(context->tx.data_buf, buf, n_bytes);
+	if (retval < 0)
+		goto exit;
+
+	/* Pad with spaces */
+	for (i = n_bytes; i < 32; ++i)
+		context->tx.data_buf[i] = ' ';
+
+	/* Nine 8 byte packets to be sent */
+	/* NOTE: "\x07\x01\0\0\0\0\0\0" or "\x0c\0\0\0\0\0\0\0"
+	 *       will clear the VFD */
+	for (i = 0; i < 9; i++) {
+		switch (i) {
+		case 0:
+			memcpy(context->usb_tx_buf, "\x07\0\0\0\0\0\0\0", 8);
+			context->usb_tx_buf[1] = (context->vfd_contrast) ?
+				(0x2B - (context->vfd_contrast - 1) / 250)
+				: 0x2B;
+			break;
+		case 1:
+			memcpy(context->usb_tx_buf, "\x09\x01\0\0\0\0\0\0", 8);
+			break;
+		case 2:
+			memcpy(context->usb_tx_buf, "\x0b\x01\0\0\0\0\0\0", 8);
+			break;
+		case 3:
+			memcpy(context->usb_tx_buf, context->tx.data_buf, 8);
+			break;
+		case 4:
+			memcpy(context->usb_tx_buf,
+			       context->tx.data_buf + 8, 8);
+			break;
+		case 5:
+			memcpy(context->usb_tx_buf, "\x09\x01\0\0\0\0\0\0", 8);
+			break;
+		case 6:
+			memcpy(context->usb_tx_buf, "\x0b\x02\0\0\0\0\0\0", 8);
+			break;
+		case 7:
+			memcpy(context->usb_tx_buf,
+			       context->tx.data_buf + 16, 8);
+			break;
+		case 8:
+			memcpy(context->usb_tx_buf,
+			       context->tx.data_buf + 24, 8);
+			break;
+		}
+		retval = send_packet(context);
+		if (retval != SUCCESS) {
+
+			err("%s: send packet failed for packet #%d",
+					__func__, i);
+			goto exit;
+		}
+	}
+exit:
+
+	UNLOCK_CONTEXT;
+
+	return (retval == SUCCESS) ? n_bytes : retval;
+}
+
+/**
+ * Callback function for USB core API: transmit data
+ */
+static void usb_tx_callback(struct urb *urb)
+{
+	struct sasem_context *context;
+
+	if (!urb)
+		return;
+	context = (struct sasem_context *) urb->context;
+	if (!context)
+		return;
+
+	context->tx.status = urb->status;
+
+	/* notify waiters that write has finished */
+	atomic_set(&context->tx.busy, 0);
+	complete(&context->tx.finished);
+
+	return;
+}
+
+/**
+ * Called by lirc_dev when the application opens /dev/lirc
+ */
+static int ir_open(void *data)
+{
+	int retval = SUCCESS;
+	struct sasem_context *context;
+
+	/* prevent races with disconnect */
+	down(&disconnect_sem);
+
+	context = (struct sasem_context *) data;
+
+	LOCK_CONTEXT;
+
+	if (context->ir_isopen) {
+		err("%s: IR port is already open", __func__);
+		retval = -EBUSY;
+		goto exit;
+	}
+
+	usb_fill_int_urb(context->rx_urb, context->dev,
+		usb_rcvintpipe(context->dev,
+				context->rx_endpoint->bEndpointAddress),
+		context->usb_rx_buf, sizeof(context->usb_rx_buf),
+		usb_rx_callback, context, context->rx_endpoint->bInterval);
+
+	retval = usb_submit_urb(context->rx_urb, GFP_KERNEL);
+
+	if (retval)
+		err("%s: usb_submit_urb failed for ir_open (%d)",
+		    __func__, retval);
+	else {
+		context->ir_isopen = TRUE;
+		info("IR port opened");
+	}
+
+exit:
+	UNLOCK_CONTEXT;
+
+	up(&disconnect_sem);
+	return SUCCESS;
+}
+
+/**
+ * Called by lirc_dev when the application closes /dev/lirc
+ */
+static void ir_close(void *data)
+{
+	struct sasem_context *context;
+
+	context = (struct sasem_context *)data;
+	if (!context) {
+		err("%s: no context for device", __func__);
+		return;
+	}
+
+	LOCK_CONTEXT;
+
+	usb_kill_urb(context->rx_urb);
+	context->ir_isopen = FALSE;
+	info("IR port closed");
+
+	if (!context->dev_present) {
+
+		/*
+		 * Device disconnected while IR port was
+		 * still open. Plugin was not deregistered
+		 * at disconnect time, so do it now.
+		 */
+		deregister_from_lirc(context);
+
+		if (!context->vfd_isopen) {
+
+			UNLOCK_CONTEXT;
+			delete_context(context);
+			return;
+		}
+		/* If VFD port is open, context will be deleted by vfd_close */
+	}
+
+	UNLOCK_CONTEXT;
+	return;
+}
+
+/**
+ * Process the incoming packet
+ */
+static inline void incoming_packet(struct sasem_context *context,
+				   struct urb *urb)
+{
+	int len = urb->actual_length;
+	unsigned char *buf = urb->transfer_buffer;
+	long ms;
+	struct timeval tv;
+
+	if (len != 8) {
+		warn("%s: invalid incoming packet size (%d)",
+		     __func__, len);
+		return;
+	}
+
+#ifdef DEBUG
+	int i;
+	for (i = 0; i < 8; ++i)
+		printk(KERN_INFO "%02x ", buf[i]);
+	printk(KERN_INFO "\n");
+#endif
+
+	/* Lirc could deal with the repeat code, but we really need to block it
+	 * if it arrives too late.  Otherwise we could repeat the wrong code. */
+
+	/* get the time since the last button press */
+	do_gettimeofday(&tv);
+	ms = (tv.tv_sec - context->presstime.tv_sec) * 1000 +
+	     (tv.tv_usec - context->presstime.tv_usec) / 1000;
+
+	if (memcmp(buf, "\x08\0\0\0\0\0\0\0", 8) == 0) {
+		/* the repeat code is being sent, so we copy
+		 * the old code to LIRC */
+
+		/* NOTE: Only if the last code was less than 250ms ago
+		 * - no one should be able to push another (undetected) button
+		 *   in that time and then get a false repeat of the previous
+		 *   press but it is long enough for a genuine repeat */
+		if ((ms < 250) && (context->codesaved != 0)) {
+			memcpy(buf, &context->lastcode, 8);
+			context->presstime.tv_sec = tv.tv_sec;
+			context->presstime.tv_usec = tv.tv_usec;
+		}
+	} else {
+		/* save the current valid code for repeats */
+		memcpy(&context->lastcode, buf, 8);
+		/* set flag to signal a valid code was save;
+		 * just for safety reasons */
+		context->codesaved = 1;
+		context->presstime.tv_sec = tv.tv_sec;
+		context->presstime.tv_usec = tv.tv_usec;
+	}
+
+	lirc_buffer_write_1(context->plugin->rbuf, buf);
+	wake_up(&context->plugin->rbuf->wait_poll);
+}
+
+/**
+ * Callback function for USB core API: receive data
+ */
+static void usb_rx_callback(struct urb *urb)
+{
+	struct sasem_context *context;
+
+	if (!urb)
+		return;
+	context = (struct sasem_context *) urb->context;
+	if (!context)
+		return;
+
+	switch (urb->status) {
+
+	case -ENOENT: 		/* usbcore unlink successful! */
+		return;
+
+	case SUCCESS:
+		if (context->ir_isopen)
+			incoming_packet(context, urb);
+		break;
+
+	default:
+		warn("%s: status (%d): ignored",
+			 __func__, urb->status);
+		break;
+	}
+
+	usb_submit_urb(context->rx_urb, GFP_ATOMIC);
+	return;
+}
+
+
+
+/**
+ * Callback function for USB core API: Probe
+ */
+static int sasem_probe(struct usb_interface *interface,
+			const struct usb_device_id *id)
+{
+	struct usb_device *dev = NULL;
+	struct usb_host_interface *iface_desc = NULL;
+	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_plugin *plugin = NULL;
+	struct lirc_buffer *rbuf = NULL;
+	int lirc_minor = 0;
+	int num_endpoints;
+	int retval = SUCCESS;
+	int vfd_ep_found;
+	int ir_ep_found;
+	int alloc_status;
+	struct sasem_context *context = NULL;
+	int i;
+
+	info("%s: found Sasem device", __func__);
+
+
+	dev = usb_get_dev(interface_to_usbdev(interface));
+	iface_desc = interface->cur_altsetting;
+	num_endpoints = iface_desc->desc.bNumEndpoints;
+
+	/*
+	 * Scan the endpoint list and set:
+	 * 	first input endpoint = IR endpoint
+	 * 	first output endpoint = VFD endpoint
+	 */
+
+	ir_ep_found = FALSE;
+	vfd_ep_found = FALSE;
+
+	for (i = 0; i < num_endpoints && !(ir_ep_found && vfd_ep_found); ++i) {
+
+		struct usb_endpoint_descriptor *ep;
+		int ep_dir;
+		int ep_type;
+		ep = &iface_desc->endpoint [i].desc;
+		ep_dir = ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK;
+		ep_type = ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
+
+		if (!ir_ep_found &&
+			ep_dir == USB_DIR_IN &&
+			ep_type == USB_ENDPOINT_XFER_INT) {
+
+			rx_endpoint = ep;
+			ir_ep_found = TRUE;
+			if (debug)
+				info("%s: found IR endpoint", __func__);
+
+		} else if (!vfd_ep_found &&
+			ep_dir == USB_DIR_OUT &&
+			ep_type == USB_ENDPOINT_XFER_INT) {
+
+			tx_endpoint = ep;
+			vfd_ep_found = TRUE;
+			if (debug)
+				info("%s: found VFD endpoint", __func__);
+		}
+	}
+
+	/* Input endpoint is mandatory */
+	if (!ir_ep_found) {
+
+		err("%s: no valid input (IR) endpoint found.", __func__);
+		retval = -ENODEV;
+		goto exit;
+	}
+
+	/* Warning if no VFD endpoint */
+	if (!vfd_ep_found)
+		info("%s: no valid output (VFD) endpoint found.", __func__);
+
+
+	/* Allocate memory */
+	alloc_status = SUCCESS;
+
+	context = kmalloc(sizeof(struct sasem_context), GFP_KERNEL);
+	if (!context) {
+		err("%s: kmalloc failed for context", __func__);
+		alloc_status = 1;
+		goto alloc_status_switch;
+	}
+	plugin = kmalloc(sizeof(struct lirc_plugin), GFP_KERNEL);
+	if (!plugin) {
+		err("%s: kmalloc failed for lirc_plugin", __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;
+	}
+	if (vfd_ep_found) {
+		tx_urb = usb_alloc_urb(0, GFP_KERNEL);
+		if (!tx_urb) {
+			err("%s: usb_alloc_urb failed for VFD urb",
+			    __func__);
+			alloc_status = 6;
+			goto alloc_status_switch;
+		}
+	}
+
+	/* clear all members of sasem_context and lirc_plugin */
+	memset(context, 0, sizeof(struct sasem_context));
+	mutex_init(&context->lock);
+
+	memset(plugin, 0, sizeof(struct lirc_plugin));
+
+	strcpy(plugin->name, MOD_NAME);
+	plugin->minor = -1;
+	plugin->code_length = 64;
+	plugin->sample_rate = 0;
+	plugin->features = LIRC_CAN_REC_LIRCCODE;
+	plugin->data = context;
+	plugin->rbuf = rbuf;
+	plugin->set_use_inc = ir_open;
+	plugin->set_use_dec = ir_close;
+	plugin->dev   = &dev->dev;
+	plugin->owner = THIS_MODULE;
+
+	LOCK_CONTEXT;
+
+	lirc_minor = lirc_register_plugin(plugin);
+	if (lirc_minor < 0) {
+		err("%s: lirc_register_plugin failed", __func__);
+		alloc_status = 7;
+		UNLOCK_CONTEXT;
+	} else
+		info("%s: Registered Sasem plugin (minor:%d)",
+			__func__, lirc_minor);
+
+alloc_status_switch:
+
+	switch (alloc_status) {
+
+	case 7:
+		if (vfd_ep_found)
+			usb_free_urb(tx_urb);
+	case 6:
+		usb_free_urb(rx_urb);
+	case 5:
+		lirc_buffer_free(rbuf);
+	case 4:
+		kfree(rbuf);
+	case 3:
+		kfree(plugin);
+	case 2:
+		kfree(context);
+		context = NULL;
+	case 1:
+		retval = -ENOMEM;
+		goto exit;
+	}
+
+	/* Needed while unregistering! */
+	plugin->minor = lirc_minor;
+
+	context->dev = dev;
+	context->dev_present = TRUE;
+	context->rx_endpoint = rx_endpoint;
+	context->rx_urb = rx_urb;
+	if (vfd_ep_found) {
+		context->tx_endpoint = tx_endpoint;
+		context->tx_urb = tx_urb;
+		context->vfd_contrast = 1000;   /* range 0 - 1000 */
+	}
+	context->plugin = plugin;
+
+	usb_set_intfdata(interface, context);
+
+	if (vfd_ep_found) {
+
+		if (debug)
+			info("Registering VFD with sysfs");
+		if (usb_register_dev(interface, &sasem_class))
+			/* Not a fatal error, so ignore */
+			info("%s: could not get a minor number for VFD",
+				__func__);
+	}
+
+	info("%s: Sasem device on usb<%d:%d> initialized",
+			__func__, dev->bus->busnum, dev->devnum);
+
+	UNLOCK_CONTEXT;
+exit:
+	return retval;
+}
+
+/**
+ * Callback function for USB core API: disonnect
+ */
+static void sasem_disconnect(struct usb_interface *interface)
+{
+	struct sasem_context *context;
+
+	/* prevent races with ir_open()/vfd_open() */
+	down(&disconnect_sem);
+
+	context = usb_get_intfdata(interface);
+	LOCK_CONTEXT;
+
+	info("%s: Sasem device disconnected", __func__);
+
+	usb_set_intfdata(interface, NULL);
+	context->dev_present = FALSE;
+
+	/* Stop reception */
+	usb_kill_urb(context->rx_urb);
+
+	/* Abort ongoing write */
+	if (atomic_read(&context->tx.busy)) {
+
+		usb_kill_urb(context->tx_urb);
+		wait_for_completion(&context->tx.finished);
+	}
+
+	/* De-register from lirc_dev if IR port is not open */
+	if (!context->ir_isopen)
+		deregister_from_lirc(context);
+
+	usb_deregister_dev(interface, &sasem_class);
+
+	UNLOCK_CONTEXT;
+
+	if (!context->ir_isopen && !context->vfd_isopen)
+		delete_context(context);
+
+	up(&disconnect_sem);
+}
+
+#ifdef MODULE
+static int __init sasem_init(void)
+{
+	int rc;
+
+	info(MOD_DESC ", v" MOD_VERSION);
+	info(MOD_AUTHOR);
+
+	rc = usb_register(&sasem_driver);
+	if (rc < 0) {
+		err("%s: usb register failed (%d)", __func__, rc);
+		return -ENODEV;
+	}
+	return SUCCESS;
+}
+
+static void __exit sasem_exit(void)
+{
+	usb_deregister(&sasem_driver);
+	info("module removed. Goodbye!");
+}
+
+
+module_init(sasem_init);
+module_exit(sasem_exit);
+
+#endif /* MODULE */
-- 
1.6.0.1


^ permalink raw reply related	[flat|nested] 111+ messages in thread

* [PATCH 13/18] lirc driver for ITE8709 CIR port receiver
  2008-09-09  4:05                       ` [PATCH 12/18] lirc driver for the Sasem OnAir and Dign HV5 receivers Jarod Wilson
@ 2008-09-09  4:05                         ` Jarod Wilson
  2008-09-09  4:05                           ` [PATCH 14/18] lirc driver for the ITE IT87xx CIR Port receivers Jarod Wilson
  0 siblings, 1 reply; 111+ messages in thread
From: Jarod Wilson @ 2008-09-09  4:05 UTC (permalink / raw)
  To: linux-kernel
  Cc: Jarod Wilson, Jarod Wilson, Janne Grunau, Christoph Bartelmus

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain, Size: 18179 bytes --]

Signed-off-by: Jarod Wilson <jarod@redhat.com>
Signed-off-by: Janne Grunau <j@jannau.net>
CC: Christoph Bartelmus <lirc@bartelmus.de>
---
 drivers/input/lirc/Kconfig        |    7 +
 drivers/input/lirc/Makefile       |    1 +
 drivers/input/lirc/lirc_ite8709.c |  545 +++++++++++++++++++++++++++++++++++++
 3 files changed, 553 insertions(+), 0 deletions(-)
 create mode 100644 drivers/input/lirc/lirc_ite8709.c

diff --git a/drivers/input/lirc/Kconfig b/drivers/input/lirc/Kconfig
index 2731c40..9783639 100644
--- a/drivers/input/lirc/Kconfig
+++ b/drivers/input/lirc/Kconfig
@@ -54,6 +54,13 @@ config LIRC_IMON
 	help
 	  Driver for the Soundgraph IMON IR Receiver
 
+config LIRC_ITE8709
+	tristate "ITE8709 CIR Port Receiver"
+	default n
+	depends on LIRC_DEV && PNP
+	help
+	  Driver for the ITE8709 IR Receiver
+
 config LIRC_MCEUSB
 	tristate "Microsoft Media Center Ed. Receiver, v1"
 	default n
diff --git a/drivers/input/lirc/Makefile b/drivers/input/lirc/Makefile
index 1b2793d..1da7c5b 100644
--- a/drivers/input/lirc/Makefile
+++ b/drivers/input/lirc/Makefile
@@ -11,6 +11,7 @@ obj-$(CONFIG_LIRC_CMDIR)	+= lirc_cmdir.o
 obj-$(CONFIG_LIRC_I2C)		+= lirc_i2c.o
 obj-$(CONFIG_LIRC_IGORPLUGUSB)	+= lirc_igorplugusb.o
 obj-$(CONFIG_LIRC_IMON)		+= lirc_imon.o
+obj-$(CONFIG_LIRC_ITE8709)	+= lirc_ite8709.o
 obj-$(CONFIG_LIRC_MCEUSB)	+= lirc_mceusb.o
 obj-$(CONFIG_LIRC_MCEUSB2)	+= lirc_mceusb2.o
 obj-$(CONFIG_LIRC_SASEM)	+= lirc_sasem.o
diff --git a/drivers/input/lirc/lirc_ite8709.c b/drivers/input/lirc/lirc_ite8709.c
new file mode 100644
index 0000000..d03ecf7
--- /dev/null
+++ b/drivers/input/lirc/lirc_ite8709.c
@@ -0,0 +1,545 @@
+/*
+ * LIRC driver for ITE8709 CIR port
+ *
+ * Copyright (C) 2008 Grégory Lardière <spmf2004-lirc@yahoo.fr>
+ *
+ * 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>
+#include <linux/module.h>
+
+#include <linux/autoconf.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+
+#include <linux/pnp.h>
+#include <linux/io.h>
+
+#include "lirc.h"
+#include "lirc_dev.h"
+
+#define LIRC_DRIVER_NAME "lirc_ite8709"
+
+#define BUF_CHUNK_SIZE	sizeof(int)
+#define BUF_SIZE	(128*BUF_CHUNK_SIZE)
+
+/*******************************************************************************
+* The ITE8709 device seems to be the combination of IT8512 superIO chip and    *
+* a specific firmware running on the IT8512's embedded micro-controller.       *
+* In addition of the embedded micro-controller, the IT8512 chip contains a     *
+* CIR module and several other modules. A few modules are directly accessible  *
+* by the host CPU, but most of them are only accessible by the                 *
+* micro-controller. The CIR module is only accessible by the micro-controller. *
+* The battery-backed SRAM module is accessible by the host CPU and the         *
+* micro-controller. So one of the MC's firmware role is to act as a bridge     *
+* between the host CPU and the CIR module. The firmware implements a kind of   *
+* communication protocol using the SRAM module as a shared memory. The IT8512  *
+* specification is publicly available on ITE's web site, but the communication *
+* protocol is not, so it was reverse-engineered.                               *
+*******************************************************************************/
+
+/* ITE8709 Registers addresses and values (reverse-engineered) */
+#define ITE8709_MODE		0x1a
+#define ITE8709_REG_ADR		0x1b
+#define ITE8709_REG_VAL		0x1c
+#define ITE8709_IIR		0x1e  /* Interrupt identification register */
+#define ITE8709_RFSR		0x1f  /* Receiver FIFO status register */
+#define ITE8709_FIFO_START	0x20
+
+#define ITE8709_MODE_READY	0X00
+#define ITE8709_MODE_WRITE	0X01
+#define ITE8709_MODE_READ	0X02
+#define ITE8709_IIR_RDAI	0x02  /* Receiver data available interrupt */
+#define ITE8709_IIR_RFOI	0x04  /* Receiver FIFO overrun interrupt */
+#define ITE8709_RFSR_MASK	0x3f  /* FIFO byte count mask */
+
+/* IT8512 CIR-module registers addresses and values (from IT8512 E/F */
+/* specification v0.4.1)                                             */
+#define IT8512_REG_MSTCR	0x01  /* Master control register */
+#define IT8512_REG_IER		0x02  /* Interrupt enable register */
+#define IT8512_REG_CFR		0x04  /* Carrier frequency register */
+#define IT8512_REG_RCR		0x05  /* Receive control register */
+#define IT8512_REG_BDLR		0x08  /* Baud rate divisor low byte register */
+#define IT8512_REG_BDHR		0x09  /* Baud rate divisor high byte register */
+
+#define IT8512_MSTCR_RESET	0x01  /* Reset registers to default value */
+#define IT8512_MSTCR_FIFOCLR	0x02  /* Clear FIFO */
+#define IT8512_MSTCR_FIFOTL_7	0x04  /* FIFO threshold level : 7 */
+#define IT8512_MSTCR_FIFOTL_25	0x0c  /* FIFO threshold level : 25 */
+#define IT8512_IER_RDAIE	0x02  /* Enable data interrupt request */
+#define IT8512_IER_RFOIE	0x04  /* Enable FIFO overrun interrupt req */
+#define IT8512_IER_IEC		0x80  /* Enable interrupt request */
+#define IT8512_CFR_CF_36KHZ	0x09  /* Carrier freq : low speed, 36kHz */
+#define IT8512_RCR_RXDCR_1	0x01  /* Demodulation carrier range : 1 */
+#define IT8512_RCR_RXACT	0x08  /* Receiver active */
+#define IT8512_RCR_RXEN		0x80  /* Receiver enable */
+#define IT8512_BDR_6		6     /* Baud rate divisor : 6 */
+
+/* Actual values used by this driver */
+#define CFG_FIFOTL	IT8512_MSTCR_FIFOTL_25
+#define CFG_CR_FREQ	IT8512_CFR_CF_36KHZ
+#define CFG_DCR		IT8512_RCR_RXDCR_1
+#define CFG_BDR		IT8512_BDR_6
+#define CFG_TIMEOUT	100000 /* Rearm interrupt when a space is > 100 ms */
+
+static int debug;
+
+struct ite8709_device {
+	int use_count;
+	int io;
+	int irq;
+	spinlock_t hardware_lock;
+	unsigned long long acc_pulse;
+	unsigned long long acc_space;
+	char lastbit;
+	struct timeval last_tv;
+	struct lirc_plugin plugin;
+	struct lirc_buffer buffer;
+	struct tasklet_struct tasklet;
+	char force_rearm;
+	char rearmed;
+	char device_busy;
+};
+
+#define dprintk(fmt, args...)					\
+	do {							\
+		if (debug)					\
+			printk(KERN_DEBUG LIRC_DRIVER_NAME ": "	\
+				fmt, ## args);			\
+	} while (0)
+
+
+static unsigned char ite8709_read(struct ite8709_device *dev,
+					unsigned char port)
+{
+	outb(port, dev->io);
+	return inb(dev->io+1);
+}
+
+static void ite8709_write(struct ite8709_device *dev, unsigned char port,
+				unsigned char data)
+{
+	outb(port, dev->io);
+	outb(data, dev->io+1);
+}
+
+static void ite8709_wait_device(struct ite8709_device *dev)
+{
+	int i = 0;
+	/* loop until device tells it's ready to continue */
+	/* iterations count is usually ~750 but can sometimes achieve 13000 */
+	for (i = 0; i < 15000; i++) {
+		udelay(2);
+		if (ite8709_read(dev, ITE8709_MODE) == ITE8709_MODE_READY)
+			break;
+	}
+}
+
+static void ite8709_write_register(struct ite8709_device *dev,
+				unsigned char reg_adr, unsigned char reg_value)
+{
+	ite8709_wait_device(dev);
+
+	ite8709_write(dev, ITE8709_REG_VAL, reg_value);
+	ite8709_write(dev, ITE8709_REG_ADR, reg_adr);
+	ite8709_write(dev, ITE8709_MODE, ITE8709_MODE_WRITE);
+}
+
+static void ite8709_init_hardware(struct ite8709_device *dev)
+{
+	spin_lock_irq(&dev->hardware_lock);
+	dev->device_busy = 1;
+	spin_unlock_irq(&dev->hardware_lock);
+
+	ite8709_write_register(dev, IT8512_REG_BDHR, (CFG_BDR >> 8) & 0xff);
+	ite8709_write_register(dev, IT8512_REG_BDLR, CFG_BDR & 0xff);
+	ite8709_write_register(dev, IT8512_REG_CFR, CFG_CR_FREQ);
+	ite8709_write_register(dev, IT8512_REG_IER,
+			IT8512_IER_IEC | IT8512_IER_RFOIE | IT8512_IER_RDAIE);
+	ite8709_write_register(dev, IT8512_REG_RCR, CFG_DCR);
+	ite8709_write_register(dev, IT8512_REG_MSTCR,
+					CFG_FIFOTL | IT8512_MSTCR_FIFOCLR);
+	ite8709_write_register(dev, IT8512_REG_RCR,
+				IT8512_RCR_RXEN | IT8512_RCR_RXACT | CFG_DCR);
+
+	spin_lock_irq(&dev->hardware_lock);
+	dev->device_busy = 0;
+	spin_unlock_irq(&dev->hardware_lock);
+
+	tasklet_enable(&dev->tasklet);
+}
+
+static void ite8709_drop_hardware(struct ite8709_device *dev)
+{
+	tasklet_disable(&dev->tasklet);
+
+	spin_lock_irq(&dev->hardware_lock);
+	dev->device_busy = 1;
+	spin_unlock_irq(&dev->hardware_lock);
+
+	ite8709_write_register(dev, IT8512_REG_RCR, 0);
+	ite8709_write_register(dev, IT8512_REG_MSTCR,
+				IT8512_MSTCR_RESET | IT8512_MSTCR_FIFOCLR);
+
+	spin_lock_irq(&dev->hardware_lock);
+	dev->device_busy = 0;
+	spin_unlock_irq(&dev->hardware_lock);
+}
+
+static int ite8709_set_use_inc(void *data)
+{
+	struct ite8709_device *dev;
+	dev = data;
+	if (dev->use_count == 0)
+		ite8709_init_hardware(dev);
+	dev->use_count++;
+	return 0;
+}
+
+static void ite8709_set_use_dec(void *data)
+{
+	struct ite8709_device *dev;
+	dev = data;
+	dev->use_count--;
+	if (dev->use_count == 0)
+		ite8709_drop_hardware(dev);
+}
+
+static void ite8709_add_read_queue(struct ite8709_device *dev, int flag,
+					unsigned long long val)
+{
+	int value;
+
+	dprintk("add a %llu usec %s\n", val, flag ? "pulse" : "space");
+
+	value = (val > PULSE_MASK) ? PULSE_MASK : val;
+	if (flag)
+		value |= PULSE_BIT;
+
+	if (!lirc_buffer_full(&dev->buffer)) {
+		lirc_buffer_write_1(&dev->buffer, (void *) &value);
+		wake_up(&dev->buffer.wait_poll);
+	}
+}
+
+static irqreturn_t ite8709_interrupt(int irq, void *dev_id)
+{
+	unsigned char data;
+	int iir, rfsr, i;
+	int fifo = 0;
+	char bit;
+	struct timeval curr_tv;
+
+	/* Bit duration in microseconds */
+	const unsigned long bit_duration = 1000000ul / (115200 / CFG_BDR);
+
+	struct ite8709_device *dev;
+	dev = dev_id;
+
+	/* If device is busy, we simply discard data because we are in one of */
+	/* these two cases : shutting down or rearming the device, so this    */
+	/* doesn't really matter and this avoids waiting too long in IRQ ctx  */
+	spin_lock(&dev->hardware_lock);
+	if (dev->device_busy) {
+		spin_unlock(&dev->hardware_lock);
+		return IRQ_RETVAL(IRQ_HANDLED);
+	}
+
+	iir = ite8709_read(dev, ITE8709_IIR);
+
+	switch (iir) {
+	case ITE8709_IIR_RFOI:
+		dprintk("fifo overrun, scheduling forced rearm just in case\n");
+		dev->force_rearm = 1;
+		tasklet_schedule(&dev->tasklet);
+		spin_unlock(&dev->hardware_lock);
+		return IRQ_RETVAL(IRQ_HANDLED);
+
+	case ITE8709_IIR_RDAI:
+		rfsr = ite8709_read(dev, ITE8709_RFSR);
+		fifo = rfsr & ITE8709_RFSR_MASK;
+		if (fifo > 32)
+			fifo = 32;
+		dprintk("iir: 0x%x rfsr: 0x%x fifo: %d\n", iir, rfsr, fifo);
+
+		if (dev->rearmed) {
+			do_gettimeofday(&curr_tv);
+			dev->acc_space += 1000000ull
+				* (curr_tv.tv_sec - dev->last_tv.tv_sec)
+				+ (curr_tv.tv_usec - dev->last_tv.tv_usec);
+			dev->rearmed = 0;
+		}
+		for (i = 0; i < fifo; i++) {
+			data = ite8709_read(dev, i+ITE8709_FIFO_START);
+			data = ~data;
+			/* Loop through */
+			for (bit = 0; bit < 8; ++bit) {
+				if ((data >> bit) & 1) {
+					dev->acc_pulse += bit_duration;
+					if (dev->lastbit == 0) {
+						ite8709_add_read_queue(dev, 0,
+							dev->acc_space);
+						dev->acc_space = 0;
+					}
+				} else {
+					dev->acc_space += bit_duration;
+					if (dev->lastbit == 1) {
+						ite8709_add_read_queue(dev, 1,
+							dev->acc_pulse);
+						dev->acc_pulse = 0;
+					}
+				}
+				dev->lastbit = (data >> bit) & 1;
+			}
+		}
+		ite8709_write(dev, ITE8709_RFSR, 0);
+
+		if (dev->acc_space > CFG_TIMEOUT) {
+			dprintk("scheduling rearm IRQ\n");
+			do_gettimeofday(&dev->last_tv);
+			dev->force_rearm = 0;
+			tasklet_schedule(&dev->tasklet);
+		}
+
+		spin_unlock(&dev->hardware_lock);
+		return IRQ_RETVAL(IRQ_HANDLED);
+
+	default:
+		/* not our irq */
+		dprintk("unknown IRQ (shouldn't happen) !!\n");
+		spin_unlock(&dev->hardware_lock);
+		return IRQ_RETVAL(IRQ_NONE);
+	}
+}
+
+static void ite8709_rearm_irq(unsigned long data)
+{
+	struct ite8709_device *dev;
+	unsigned long flags;
+	dev = (struct ite8709_device *) data;
+
+	spin_lock_irqsave(&dev->hardware_lock, flags);
+	dev->device_busy = 1;
+	spin_unlock_irqrestore(&dev->hardware_lock, flags);
+
+	if (dev->force_rearm || dev->acc_space > CFG_TIMEOUT) {
+		dprintk("rearming IRQ\n");
+		ite8709_write_register(dev, IT8512_REG_RCR,
+						IT8512_RCR_RXACT | CFG_DCR);
+		ite8709_write_register(dev, IT8512_REG_MSTCR,
+					CFG_FIFOTL | IT8512_MSTCR_FIFOCLR);
+		ite8709_write_register(dev, IT8512_REG_RCR,
+				IT8512_RCR_RXEN | IT8512_RCR_RXACT | CFG_DCR);
+		if (!dev->force_rearm)
+			dev->rearmed = 1;
+		dev->force_rearm = 0;
+	}
+
+	spin_lock_irqsave(&dev->hardware_lock, flags);
+	dev->device_busy = 0;
+	spin_unlock_irqrestore(&dev->hardware_lock, flags);
+}
+
+static int ite8709_cleanup(struct ite8709_device *dev, int stage, int errno,
+				char *msg)
+{
+	if (msg != NULL)
+		printk(KERN_ERR LIRC_DRIVER_NAME ": %s\n", msg);
+
+	switch (stage) {
+	case 6:
+		if (dev->use_count > 0)
+			ite8709_drop_hardware(dev);
+	case 5:
+		free_irq(dev->irq, dev);
+	case 4:
+		release_region(dev->io, 2);
+	case 3:
+		lirc_unregister_plugin(dev->plugin.minor);
+	case 2:
+		lirc_buffer_free(dev->plugin.rbuf);
+	case 1:
+		kfree(dev);
+	case 0:
+		;
+	}
+
+	return errno;
+}
+
+static int __devinit ite8709_pnp_probe(struct pnp_dev *dev,
+					const struct pnp_device_id *dev_id)
+{
+	struct lirc_plugin *plugin;
+	struct ite8709_device *ite8709_dev;
+	int ret;
+
+	/* Check resources validity */
+	if (!pnp_irq_valid(dev, 0))
+		return ite8709_cleanup(NULL, 0, -ENODEV, "invalid IRQ");
+	if (!pnp_port_valid(dev, 2))
+		return ite8709_cleanup(NULL, 0, -ENODEV, "invalid IO port");
+
+	/* Allocate memory for device struct */
+	ite8709_dev = kzalloc(sizeof(struct ite8709_device), GFP_KERNEL);
+	if (ite8709_dev == NULL)
+		return ite8709_cleanup(NULL, 0, -ENOMEM, "kzalloc failed");
+	pnp_set_drvdata(dev, ite8709_dev);
+
+	/* Initialize device struct */
+	ite8709_dev->use_count = 0;
+	ite8709_dev->irq = pnp_irq(dev, 0);
+	ite8709_dev->io = pnp_port_start(dev, 2);
+	ite8709_dev->hardware_lock = __SPIN_LOCK_UNLOCKED(
+					ite8709_dev->hardware_lock);
+	ite8709_dev->acc_pulse = 0;
+	ite8709_dev->acc_space = 0;
+	ite8709_dev->lastbit = 0;
+	do_gettimeofday(&ite8709_dev->last_tv);
+	tasklet_init(&ite8709_dev->tasklet, ite8709_rearm_irq,
+							(long) ite8709_dev);
+	ite8709_dev->force_rearm = 0;
+	ite8709_dev->rearmed = 0;
+	ite8709_dev->device_busy = 0;
+
+	/* Initialize plugin struct */
+	plugin = &ite8709_dev->plugin;
+	strcpy(plugin->name, LIRC_DRIVER_NAME);
+	plugin->minor = -1;
+	plugin->code_length = sizeof(int) * 8;
+	plugin->sample_rate = 0;
+	plugin->features = LIRC_CAN_REC_MODE2;
+	plugin->data = ite8709_dev;
+	plugin->add_to_buf = NULL;
+	plugin->get_queue = NULL;
+	plugin->rbuf = &ite8709_dev->buffer;
+	plugin->set_use_inc = ite8709_set_use_inc;
+	plugin->set_use_dec = ite8709_set_use_dec;
+	plugin->ioctl = NULL;
+	plugin->fops = NULL;
+	plugin->dev = &dev->dev;
+	plugin->owner = THIS_MODULE;
+
+	/* Initialize LIRC buffer */
+	if (lirc_buffer_init(plugin->rbuf, BUF_CHUNK_SIZE, BUF_SIZE))
+		return ite8709_cleanup(ite8709_dev, 1, -ENOMEM,
+						"lirc_buffer_init() failed");
+
+	/* Register LIRC plugin */
+	ret = lirc_register_plugin(plugin);
+	if (ret < 0)
+		return ite8709_cleanup(ite8709_dev, 2, ret,
+					"lirc_register_plugin() failed");
+
+	/* Reserve I/O port access */
+	if (!request_region(ite8709_dev->io, 2, LIRC_DRIVER_NAME))
+		return ite8709_cleanup(ite8709_dev, 3, -EBUSY,
+						"i/o port already in use");
+
+	/* Reserve IRQ line */
+	ret = request_irq(ite8709_dev->irq, ite8709_interrupt, 0,
+					LIRC_DRIVER_NAME, ite8709_dev);
+	if (ret < 0)
+		return ite8709_cleanup(ite8709_dev, 4, ret,
+						"IRQ already in use");
+
+	/* Initialize hardware */
+	ite8709_drop_hardware(ite8709_dev); /* Shutdown hw until first use */
+
+	printk(KERN_INFO LIRC_DRIVER_NAME ": device found : irq=%d io=0x%x\n",
+					ite8709_dev->irq, ite8709_dev->io);
+
+	return 0;
+}
+
+static void __devexit ite8709_pnp_remove(struct pnp_dev *dev)
+{
+	struct ite8709_device *ite8709_dev;
+	ite8709_dev = pnp_get_drvdata(dev);
+
+	ite8709_cleanup(ite8709_dev, 6, 0, NULL);
+
+	printk(KERN_INFO LIRC_DRIVER_NAME ": device removed\n");
+}
+
+#ifdef CONFIG_PM
+static int ite8709_pnp_suspend(struct pnp_dev *dev, pm_message_t state)
+{
+	struct ite8709_device *ite8709_dev;
+	ite8709_dev = pnp_get_drvdata(dev);
+
+	if (ite8709_dev->use_count > 0)
+		ite8709_drop_hardware(ite8709_dev);
+
+	return 0;
+}
+
+static int ite8709_pnp_resume(struct pnp_dev *dev)
+{
+	struct ite8709_device *ite8709_dev;
+	ite8709_dev = pnp_get_drvdata(dev);
+
+	if (ite8709_dev->use_count > 0)
+		ite8709_init_hardware(ite8709_dev);
+
+	return 0;
+}
+#else
+#define ite8709_pnp_suspend NULL
+#define ite8709_pnp_resume NULL
+#endif
+
+static const struct pnp_device_id pnp_dev_table[] = {
+	{"ITE8709", 0},
+	{}
+};
+
+MODULE_DEVICE_TABLE(pnp, pnp_dev_table);
+
+static struct pnp_driver ite8709_pnp_driver = {
+	.name           = LIRC_DRIVER_NAME,
+	.probe          = ite8709_pnp_probe,
+	.remove         = __devexit_p(ite8709_pnp_remove),
+	.suspend        = ite8709_pnp_suspend,
+	.resume         = ite8709_pnp_resume,
+	.id_table       = pnp_dev_table,
+};
+
+int init_module(void)
+{
+	return pnp_register_driver(&ite8709_pnp_driver);
+}
+
+void cleanup_module(void)
+{
+	pnp_unregister_driver(&ite8709_pnp_driver);
+}
+
+MODULE_DESCRIPTION("LIRC driver for ITE8709 CIR port");
+MODULE_AUTHOR("Grégory Lardière");
+MODULE_LICENSE("GPL");
+
+module_param(debug, bool, 0644);
+MODULE_PARM_DESC(debug, "Enable debugging messages");
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
-- 
1.6.0.1


^ permalink raw reply related	[flat|nested] 111+ messages in thread

* [PATCH 14/18] lirc driver for the ITE IT87xx CIR Port receivers
  2008-09-09  4:05                         ` [PATCH 13/18] lirc driver for ITE8709 CIR port receiver Jarod Wilson
@ 2008-09-09  4:05                           ` Jarod Wilson
  2008-09-09  4:06                             ` [PATCH 15/18] lirc driver for the SIR IrDA port Jarod Wilson
  0 siblings, 1 reply; 111+ messages in thread
From: Jarod Wilson @ 2008-09-09  4:05 UTC (permalink / raw)
  To: linux-kernel
  Cc: Jarod Wilson, Jarod Wilson, Janne Grunau, Christoph Bartelmus

Signed-off-by: Jarod Wilson <jarod@redhat.com>
Signed-off-by: Janne Grunau <j@jannau.net>
CC: Christoph Bartelmus <lirc@bartelmus.de>
---
 drivers/input/lirc/Kconfig     |    7 +
 drivers/input/lirc/Makefile    |    1 +
 drivers/input/lirc/lirc_it87.c |  999 ++++++++++++++++++++++++++++++++++++++++
 drivers/input/lirc/lirc_it87.h |  116 +++++
 4 files changed, 1123 insertions(+), 0 deletions(-)
 create mode 100644 drivers/input/lirc/lirc_it87.c
 create mode 100644 drivers/input/lirc/lirc_it87.h

diff --git a/drivers/input/lirc/Kconfig b/drivers/input/lirc/Kconfig
index 9783639..42e9bc3 100644
--- a/drivers/input/lirc/Kconfig
+++ b/drivers/input/lirc/Kconfig
@@ -54,6 +54,13 @@ config LIRC_IMON
 	help
 	  Driver for the Soundgraph IMON IR Receiver
 
+config LIRC_IT87
+	tristate "ITE IT87XX CIR Port Receiver"
+	default n
+	depends on LIRC_DEV
+	help
+	  Driver for the ITE IT87xx IR Receiver
+
 config LIRC_ITE8709
 	tristate "ITE8709 CIR Port Receiver"
 	default n
diff --git a/drivers/input/lirc/Makefile b/drivers/input/lirc/Makefile
index 1da7c5b..4ee32f3 100644
--- a/drivers/input/lirc/Makefile
+++ b/drivers/input/lirc/Makefile
@@ -11,6 +11,7 @@ obj-$(CONFIG_LIRC_CMDIR)	+= lirc_cmdir.o
 obj-$(CONFIG_LIRC_I2C)		+= lirc_i2c.o
 obj-$(CONFIG_LIRC_IGORPLUGUSB)	+= lirc_igorplugusb.o
 obj-$(CONFIG_LIRC_IMON)		+= lirc_imon.o
+obj-$(CONFIG_LIRC_IT87)		+= lirc_it87.o
 obj-$(CONFIG_LIRC_ITE8709)	+= lirc_ite8709.o
 obj-$(CONFIG_LIRC_MCEUSB)	+= lirc_mceusb.o
 obj-$(CONFIG_LIRC_MCEUSB2)	+= lirc_mceusb2.o
diff --git a/drivers/input/lirc/lirc_it87.c b/drivers/input/lirc/lirc_it87.c
new file mode 100644
index 0000000..0a64847
--- /dev/null
+++ b/drivers/input/lirc/lirc_it87.c
@@ -0,0 +1,999 @@
+/*
+ * LIRC driver for ITE IT8712/IT8705 CIR port
+ *
+ * Copyright (C) 2001 Hans-Gunter Lutke 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
+ * 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
+ *
+ * ITE IT8705 and IT8712(not tested) 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
+ *
+ * 2001/02/27 Christoph Bartelmus <lirc@bartelmus.de> :
+ *   reimplemented read function
+ * 2005/06/05 Andrew Calkin implemented support for Asus Digimatrix,
+ *   based on work of the following member of the Outertrack Digimatrix
+ *   Forum: Art103 <r_tay@hotmail.com>
+ */
+
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/autoconf.h>
+
+
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/serial_reg.h>
+#include <linux/time.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+#include <linux/mm.h>
+#include <linux/delay.h>
+#include <linux/poll.h>
+#include <asm/system.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/fcntl.h>
+
+#include <linux/timer.h>
+
+#include "lirc.h"
+#include "lirc_dev.h"
+
+#include "lirc_it87.h"
+
+#ifdef LIRC_IT87_DIGIMATRIX
+static int digimatrix = 1;
+static int it87_freq = 36; /* kHz */
+static int irq = 9;
+#else
+static int digimatrix;
+static int it87_freq = 38; /* kHz */
+static int irq = IT87_CIR_DEFAULT_IRQ;
+#endif
+
+static unsigned long it87_bits_in_byte_out;
+static unsigned long it87_send_counter;
+static unsigned char it87_RXEN_mask = IT87_CIR_RCR_RXEN;
+
+#define RBUF_LEN 1024
+#define WBUF_LEN 1024
+
+#define LIRC_DRIVER_NAME "lirc_it87"
+
+/* timeout for sequences in jiffies (=5/100s) */
+/* must be longer than TIME_CONST */
+#define IT87_TIMEOUT	(HZ*5/100)
+
+/* insmod parameters */
+static int debug;
+#define dprintk(fmt, args...)					\
+	do {							\
+		if (debug)					\
+			printk(KERN_DEBUG LIRC_DRIVER_NAME ": "	\
+			       fmt, ## args);			\
+	} while (0)
+
+static int io = IT87_CIR_DEFAULT_IOBASE;
+/* receiver demodulator default: off */
+static int it87_enable_demodulator;
+
+static int timer_enabled;
+static DEFINE_SPINLOCK(timer_lock);
+static struct timer_list timerlist;
+/* time of last signal change detected */
+static struct timeval last_tv = {0, 0};
+/* time of last UART data ready interrupt */
+static struct timeval last_intr_tv = {0, 0};
+static int last_value;
+
+static DECLARE_WAIT_QUEUE_HEAD(lirc_read_queue);
+
+static DEFINE_SPINLOCK(hardware_lock);
+static DEFINE_SPINLOCK(dev_lock);
+
+static int rx_buf[RBUF_LEN];
+unsigned int rx_tail, rx_head;
+static int tx_buf[WBUF_LEN];
+
+/* SECTION: Prototypes */
+
+/* Communication with user-space */
+static int lirc_open(struct inode *inode, struct file *file);
+static int lirc_close(struct inode *inode, struct file *file);
+static unsigned int lirc_poll(struct file *file, poll_table *wait);
+static ssize_t lirc_read(struct file *file, char *buf,
+			 size_t count, loff_t *ppos);
+static ssize_t lirc_write(struct file *file, const char *buf,
+			  size_t n, loff_t *pos);
+static int lirc_ioctl(struct inode *node, struct file *filep,
+		      unsigned int cmd, unsigned long arg);
+static void add_read_queue(int flag, unsigned long val);
+#ifdef MODULE
+static int init_chrdev(void);
+static void drop_chrdev(void);
+#endif
+	/* Hardware */
+static irqreturn_t it87_interrupt(int irq, void *dev_id);
+static void send_space(unsigned long len);
+static void send_pulse(unsigned long len);
+static void init_send(void);
+static void terminate_send(unsigned long len);
+static int init_hardware(void);
+static void drop_hardware(void);
+	/* Initialisation */
+static int init_port(void);
+static void drop_port(void);
+
+
+/* SECTION: Communication with user-space */
+
+static int lirc_open(struct inode *inode, struct file *file)
+{
+	spin_lock(&dev_lock);
+	if (module_refcount(THIS_MODULE)) {
+		spin_unlock(&dev_lock);
+		return -EBUSY;
+	}
+	spin_unlock(&dev_lock);
+	return 0;
+}
+
+
+static int lirc_close(struct inode *inode, struct file *file)
+{
+	return 0;
+}
+
+
+static unsigned int lirc_poll(struct file *file, poll_table *wait)
+{
+	poll_wait(file, &lirc_read_queue, wait);
+	if (rx_head != rx_tail)
+		return POLLIN | POLLRDNORM;
+	return 0;
+}
+
+
+static ssize_t lirc_read(struct file *file, char *buf,
+			 size_t count, loff_t *ppos)
+{
+	int n = 0;
+	int retval = 0;
+
+	while (n < count) {
+		if (file->f_flags & O_NONBLOCK && rx_head == rx_tail) {
+			retval = -EAGAIN;
+			break;
+		}
+		retval = wait_event_interruptible(lirc_read_queue,
+						  rx_head != rx_tail);
+		if (retval)
+			break;
+
+		if (copy_to_user((void *) buf + n, (void *) (rx_buf + rx_head),
+				 sizeof(int))) {
+			retval = -EFAULT;
+			break;
+		}
+		rx_head = (rx_head + 1) & (RBUF_LEN - 1);
+		n += sizeof(int);
+	}
+	if (n)
+		return n;
+	return retval;
+}
+
+
+static ssize_t lirc_write(struct file *file, const char *buf,
+			  size_t n, loff_t *pos)
+{
+	int i = 0;
+
+	if (n % sizeof(int) || (n / sizeof(int)) > WBUF_LEN)
+		return -EINVAL;
+	if (copy_from_user(tx_buf, buf, n))
+		return -EFAULT;
+	n /= sizeof(int);
+	init_send();
+	while (1) {
+		if (i >= n)
+			break;
+		if (tx_buf[i])
+			send_pulse(tx_buf[i]);
+		i++;
+		if (i >= n)
+			break;
+		if (tx_buf[i])
+			send_space(tx_buf[i]);
+		i++;
+	}
+	terminate_send(tx_buf[i - 1]);
+	return n;
+}
+
+
+static int lirc_ioctl(struct inode *node, struct file *filep,
+		      unsigned int cmd, unsigned long arg)
+{
+	int retval = 0;
+	unsigned long value = 0;
+	unsigned int ivalue;
+	unsigned long hw_flags;
+
+	if (cmd == LIRC_GET_FEATURES)
+		value = LIRC_CAN_SEND_PULSE |
+			LIRC_CAN_SET_SEND_CARRIER |
+			LIRC_CAN_REC_MODE2;
+	else if (cmd == LIRC_GET_SEND_MODE)
+		value = LIRC_MODE_PULSE;
+	else if (cmd == LIRC_GET_REC_MODE)
+		value = LIRC_MODE_MODE2;
+
+	switch (cmd) {
+	case LIRC_GET_FEATURES:
+	case LIRC_GET_SEND_MODE:
+	case LIRC_GET_REC_MODE:
+		retval = put_user(value, (unsigned long *) arg);
+		break;
+
+	case LIRC_SET_SEND_MODE:
+	case LIRC_SET_REC_MODE:
+		retval = get_user(value, (unsigned long *) arg);
+		break;
+
+	case LIRC_SET_SEND_CARRIER:
+		retval = get_user(ivalue, (unsigned int *) arg);
+		if (retval)
+			return retval;
+		ivalue /= 1000;
+		if (ivalue > IT87_CIR_FREQ_MAX ||
+		    ivalue < IT87_CIR_FREQ_MIN)
+			return -EINVAL;
+
+		it87_freq = ivalue;
+
+		spin_lock_irqsave(&hardware_lock, hw_flags);
+		outb(((inb(io + IT87_CIR_TCR2) & IT87_CIR_TCR2_TXMPW) |
+		      (it87_freq - IT87_CIR_FREQ_MIN) << 3),
+		      io + IT87_CIR_TCR2);
+		spin_unlock_irqrestore(&hardware_lock, hw_flags);
+		dprintk("demodulation frequency: %d kHz\n", it87_freq);
+
+		break;
+
+	default:
+		retval = -ENOIOCTLCMD;
+	}
+
+	if (retval)
+		return retval;
+
+	if (cmd == LIRC_SET_REC_MODE) {
+		if (value != LIRC_MODE_MODE2)
+			retval = -ENOSYS;
+	} else if (cmd == LIRC_SET_SEND_MODE) {
+		if (value != LIRC_MODE_PULSE)
+			retval = -ENOSYS;
+	}
+	return retval;
+}
+
+static void add_read_queue(int flag, unsigned long val)
+{
+	unsigned int new_rx_tail;
+	int newval;
+
+	dprintk("add flag %d with val %lu\n", flag, val);
+
+	newval = val & PULSE_MASK;
+
+	/* statistically pulses are ~TIME_CONST/2 too long: we could
+	   maybe make this more exactly but this is good enough */
+	if (flag) {
+		/* pulse */
+		if (newval > TIME_CONST / 2)
+			newval -= TIME_CONST / 2;
+		else /* should not ever happen */
+			newval = 1;
+		newval |= PULSE_BIT;
+	} else
+		newval += TIME_CONST / 2;
+	new_rx_tail = (rx_tail + 1) & (RBUF_LEN - 1);
+	if (new_rx_tail == rx_head) {
+		dprintk("Buffer overrun.\n");
+		return;
+	}
+	rx_buf[rx_tail] = newval;
+	rx_tail = new_rx_tail;
+	wake_up_interruptible(&lirc_read_queue);
+}
+
+
+static struct file_operations lirc_fops = {
+	.read		= lirc_read,
+	.write		= lirc_write,
+	.poll		= lirc_poll,
+	.ioctl		= lirc_ioctl,
+	.open		= lirc_open,
+	.release	= lirc_close,
+};
+
+static int set_use_inc(void *data)
+{
+       return 0;
+}
+
+static void set_use_dec(void *data)
+{
+}
+
+static struct lirc_plugin plugin = {
+       .name		= LIRC_DRIVER_NAME,
+       .minor		= -1,
+       .code_length	= 1,
+       .sample_rate	= 0,
+       .data		= NULL,
+       .add_to_buf	= NULL,
+       .get_queue	= NULL,
+       .set_use_inc	= set_use_inc,
+       .set_use_dec	= set_use_dec,
+       .fops		= &lirc_fops,
+       .dev		= NULL,
+       .owner		= THIS_MODULE,
+};
+
+
+#ifdef MODULE
+static int init_chrdev(void)
+{
+	plugin.minor = lirc_register_plugin(&plugin);
+
+	if (plugin.minor < 0) {
+		printk(KERN_ERR LIRC_DRIVER_NAME ": init_chrdev() failed.\n");
+		return -EIO;
+	}
+	return 0;
+}
+
+
+static void drop_chrdev(void)
+{
+	lirc_unregister_plugin(plugin.minor);
+}
+#endif
+
+
+/* SECTION: Hardware */
+static long delta(struct timeval *tv1, struct timeval *tv2)
+{
+	unsigned long deltv;
+
+	deltv = tv2->tv_sec - tv1->tv_sec;
+	if (deltv > 15)
+		deltv = 0xFFFFFF;
+	else
+		deltv = deltv*1000000 + tv2->tv_usec - tv1->tv_usec;
+	return deltv;
+}
+
+static void it87_timeout(unsigned long data)
+{
+	unsigned long flags;
+
+	/* avoid interference with interrupt */
+	spin_lock_irqsave(&timer_lock, flags);
+
+	if (digimatrix) {
+		/* We have timed out.
+		   Disable the RX mechanism.
+		*/
+
+		outb((inb(io + IT87_CIR_RCR) & ~IT87_CIR_RCR_RXEN) |
+		     IT87_CIR_RCR_RXACT, io + IT87_CIR_RCR);
+		if (it87_RXEN_mask)
+			outb(inb(io + IT87_CIR_RCR) | IT87_CIR_RCR_RXEN,
+			     io + IT87_CIR_RCR);
+		dprintk(" TIMEOUT\n");
+		timer_enabled = 0;
+
+		/* fifo clear */
+		outb(inb(io + IT87_CIR_TCR1) | IT87_CIR_TCR1_FIFOCLR,
+		     io+IT87_CIR_TCR1);
+
+	} else {
+		/* if last received signal was a pulse, but receiving
+		   stopped within the 9 bit frame, we need to finish
+		   this pulse and simulate a signal change to from
+		   pulse to space. Otherwise upper layers will receive
+		   two sequences next time. */
+
+		if (last_value) {
+			unsigned long pulse_end;
+
+			/* determine 'virtual' pulse end: */
+			pulse_end = delta(&last_tv, &last_intr_tv);
+			dprintk("timeout add %d for %lu usec\n",
+				last_value, pulse_end);
+			add_read_queue(last_value, pulse_end);
+			last_value = 0;
+			last_tv = last_intr_tv;
+		}
+	}
+	spin_unlock_irqrestore(&timer_lock, flags);
+}
+
+static irqreturn_t it87_interrupt(int irq, void *dev_id)
+{
+	unsigned char data;
+	struct timeval curr_tv;
+	static unsigned long deltv;
+	unsigned long deltintrtv;
+	unsigned long flags, hw_flags;
+	int iir, lsr;
+	int fifo = 0;
+	static char lastbit;
+	char bit;
+
+	/* Bit duration in microseconds */
+	const unsigned long bit_duration = 1000000ul /
+		(115200 / IT87_CIR_BAUDRATE_DIVISOR);
+
+
+	iir = inb(io + IT87_CIR_IIR);
+
+	switch (iir & IT87_CIR_IIR_IID) {
+	case 0x4:
+	case 0x6:
+		lsr = inb(io + IT87_CIR_RSR) & (IT87_CIR_RSR_RXFTO |
+						IT87_CIR_RSR_RXFBC);
+		fifo = lsr & IT87_CIR_RSR_RXFBC;
+		dprintk("iir: 0x%x fifo: 0x%x\n", iir, lsr);
+
+		/* avoid interference with timer */
+		spin_lock_irqsave(&timer_lock, flags);
+		spin_lock_irqsave(&hardware_lock, hw_flags);
+		if (digimatrix) {
+			static unsigned long acc_pulse;
+			static unsigned long acc_space;
+
+			do {
+				data = inb(io + IT87_CIR_DR);
+				data = ~data;
+				fifo--;
+				if (data != 0x00) {
+					if (timer_enabled)
+						del_timer(&timerlist);
+					/* start timer for end of
+					 * sequence detection */
+					timerlist.expires = jiffies +
+							    IT87_TIMEOUT;
+					add_timer(&timerlist);
+					timer_enabled = 1;
+				}
+				/* Loop through */
+				for (bit = 0; bit < 8; ++bit) {
+					if ((data >> bit) & 1) {
+						++acc_pulse;
+						if (lastbit == 0) {
+							add_read_queue(0,
+								acc_space *
+								 bit_duration);
+							acc_space = 0;
+						}
+					} else {
+						++acc_space;
+						if (lastbit == 1) {
+							add_read_queue(1,
+								acc_pulse *
+								 bit_duration);
+							acc_pulse = 0;
+						}
+					}
+					lastbit = (data >> bit) & 1;
+				}
+
+			} while (fifo != 0);
+		} else { /* Normal Operation */
+			do {
+				del_timer(&timerlist);
+				data = inb(io + IT87_CIR_DR);
+
+				dprintk("data=%.2x\n", data);
+				do_gettimeofday(&curr_tv);
+				deltv = delta(&last_tv, &curr_tv);
+				deltintrtv = delta(&last_intr_tv, &curr_tv);
+
+				dprintk("t %lu , d %d\n",
+					deltintrtv, (int)data);
+
+				/* if nothing came in last 2 cycles,
+				   it was gap */
+				if (deltintrtv > TIME_CONST * 2) {
+					if (last_value) {
+						dprintk("GAP\n");
+
+						/* simulate signal change */
+						add_read_queue(last_value,
+							       deltv -
+							       deltintrtv);
+						last_value = 0;
+						last_tv.tv_sec =
+							last_intr_tv.tv_sec;
+						last_tv.tv_usec =
+							last_intr_tv.tv_usec;
+						deltv = deltintrtv;
+					}
+				}
+				data = 1;
+				if (data ^ last_value) {
+					/* deltintrtv > 2*TIME_CONST,
+					   remember ? */
+					/* the other case is timeout */
+					add_read_queue(last_value,
+						       deltv-TIME_CONST);
+					last_value = data;
+					last_tv = curr_tv;
+					if (last_tv.tv_usec >= TIME_CONST)
+						last_tv.tv_usec -= TIME_CONST;
+					else {
+						last_tv.tv_sec--;
+						last_tv.tv_usec += 1000000 -
+							TIME_CONST;
+					}
+				}
+				last_intr_tv = curr_tv;
+				if (data) {
+					/* start timer for end of
+					 * sequence detection */
+					timerlist.expires =
+						jiffies + IT87_TIMEOUT;
+					add_timer(&timerlist);
+				}
+				outb((inb(io + IT87_CIR_RCR) &
+				     ~IT87_CIR_RCR_RXEN) |
+				     IT87_CIR_RCR_RXACT,
+				     io + IT87_CIR_RCR);
+				if (it87_RXEN_mask)
+					outb(inb(io + IT87_CIR_RCR) |
+					     IT87_CIR_RCR_RXEN,
+					     io + IT87_CIR_RCR);
+				fifo--;
+			} while (fifo != 0);
+		}
+		spin_unlock_irqrestore(&hardware_lock, hw_flags);
+		spin_unlock_irqrestore(&timer_lock, flags);
+
+		return IRQ_RETVAL(IRQ_HANDLED);
+
+	default:
+		/* not our irq */
+		dprintk("unknown IRQ (shouldn't happen) !!\n");
+		return IRQ_RETVAL(IRQ_NONE);
+	}
+}
+
+
+static void send_it87(unsigned long len, unsigned long stime,
+		      unsigned char send_byte, unsigned int count_bits)
+{
+	long count = len / stime;
+	long time_left = 0;
+	static unsigned char byte_out;
+	unsigned long hw_flags;
+
+	dprintk("%s: len=%ld, sb=%d\n", __func__, len, send_byte);
+
+	time_left = (long)len - (long)count * (long)stime;
+	count += ((2 * time_left) / stime);
+	while (count) {
+		long i = 0;
+		for (i = 0; i < count_bits; i++) {
+			byte_out = (byte_out << 1) | (send_byte & 1);
+			it87_bits_in_byte_out++;
+		}
+		if (it87_bits_in_byte_out == 8) {
+			dprintk("out=0x%x, tsr_txfbc: 0x%x\n",
+				byte_out,
+				inb(io + IT87_CIR_TSR) &
+				IT87_CIR_TSR_TXFBC);
+
+			while ((inb(io + IT87_CIR_TSR) &
+				IT87_CIR_TSR_TXFBC) >= IT87_CIR_FIFO_SIZE)
+				;
+
+			spin_lock_irqsave(&hardware_lock, hw_flags);
+			outb(byte_out, io + IT87_CIR_DR);
+			spin_unlock_irqrestore(&hardware_lock, hw_flags);
+
+			it87_bits_in_byte_out = 0;
+			it87_send_counter++;
+			byte_out = 0;
+		}
+		count--;
+	}
+}
+
+
+/*
+maybe: exchange space and pulse because
+it8705 only modulates 0-bits
+*/
+
+
+static void send_space(unsigned long len)
+{
+	send_it87(len, TIME_CONST, IT87_CIR_SPACE, IT87_CIR_BAUDRATE_DIVISOR);
+}
+
+static void send_pulse(unsigned long len)
+{
+	send_it87(len, TIME_CONST, IT87_CIR_PULSE, IT87_CIR_BAUDRATE_DIVISOR);
+}
+
+
+static void init_send()
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&hardware_lock, flags);
+	/* RXEN=0: receiver disable */
+	it87_RXEN_mask = 0;
+	outb(inb(io + IT87_CIR_RCR) & ~IT87_CIR_RCR_RXEN,
+	     io + IT87_CIR_RCR);
+	spin_unlock_irqrestore(&hardware_lock, flags);
+	it87_bits_in_byte_out = 0;
+	it87_send_counter = 0;
+}
+
+
+static void terminate_send(unsigned long len)
+{
+	unsigned long flags;
+	unsigned long last = 0;
+
+	last = it87_send_counter;
+	/* make sure all necessary data has been sent */
+	while (last == it87_send_counter)
+		send_space(len);
+	/* wait until all data sent */
+	while ((inb(io + IT87_CIR_TSR) & IT87_CIR_TSR_TXFBC) != 0)
+		;
+	/* then reenable receiver */
+	spin_lock_irqsave(&hardware_lock, flags);
+	it87_RXEN_mask = IT87_CIR_RCR_RXEN;
+	outb(inb(io + IT87_CIR_RCR) | IT87_CIR_RCR_RXEN,
+	     io + IT87_CIR_RCR);
+	spin_unlock_irqrestore(&hardware_lock, flags);
+}
+
+
+static int init_hardware(void)
+{
+	unsigned long flags;
+	unsigned char it87_rcr = 0;
+
+	spin_lock_irqsave(&hardware_lock, flags);
+	/* init cir-port */
+	/* enable r/w-access to Baudrate-Register */
+	outb(IT87_CIR_IER_BR, io + IT87_CIR_IER);
+	outb(IT87_CIR_BAUDRATE_DIVISOR % 0x100, io+IT87_CIR_BDLR);
+	outb(IT87_CIR_BAUDRATE_DIVISOR / 0x100, io+IT87_CIR_BDHR);
+	/* Baudrate Register off, define IRQs: Input only */
+	if (digimatrix) {
+		outb(IT87_CIR_IER_IEC | IT87_CIR_IER_RFOIE, io + IT87_CIR_IER);
+		/* RX: HCFS=0, RXDCR = 001b (33,75..38,25 kHz), RXEN=1 */
+	} else {
+		outb(IT87_CIR_IER_IEC | IT87_CIR_IER_RDAIE, io + IT87_CIR_IER);
+		/* RX: HCFS=0, RXDCR = 001b (35,6..40,3 kHz), RXEN=1 */
+	}
+	it87_rcr = (IT87_CIR_RCR_RXEN & it87_RXEN_mask) | 0x1;
+	if (it87_enable_demodulator)
+		it87_rcr |= IT87_CIR_RCR_RXEND;
+	outb(it87_rcr, io + IT87_CIR_RCR);
+	if (digimatrix) {
+		/* Set FIFO depth to 1 byte, and disable TX */
+		outb(inb(io + IT87_CIR_TCR1) |  0x00,
+		     io + IT87_CIR_TCR1);
+
+		/* TX: it87_freq (36kHz),
+		   'reserved' sensitivity setting (0x00) */
+		outb(((it87_freq - IT87_CIR_FREQ_MIN) << 3) | 0x00,
+		     io + IT87_CIR_TCR2);
+	} else {
+		/* TX: 38kHz, 13,3us (pulse-width */
+		outb(((it87_freq - IT87_CIR_FREQ_MIN) << 3) | 0x06,
+		     io + IT87_CIR_TCR2);
+	}
+	spin_unlock_irqrestore(&hardware_lock, flags);
+	return 0;
+}
+
+
+static void drop_hardware(void)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&hardware_lock, flags);
+	disable_irq(irq);
+	/* receiver disable */
+	it87_RXEN_mask = 0;
+	outb(0x1, io + IT87_CIR_RCR);
+	/* turn off irqs */
+	outb(0, io + IT87_CIR_IER);
+	/* fifo clear */
+	outb(IT87_CIR_TCR1_FIFOCLR, io+IT87_CIR_TCR1);
+	/* reset */
+	outb(IT87_CIR_IER_RESET, io+IT87_CIR_IER);
+	enable_irq(irq);
+	spin_unlock_irqrestore(&hardware_lock, flags);
+}
+
+
+static unsigned char it87_read(unsigned char port)
+{
+	outb(port, IT87_ADRPORT);
+	return inb(IT87_DATAPORT);
+}
+
+
+static void it87_write(unsigned char port, unsigned char data)
+{
+	outb(port, IT87_ADRPORT);
+	outb(data, IT87_DATAPORT);
+}
+
+
+/* SECTION: Initialisation */
+
+static int init_port(void)
+{
+	unsigned long hw_flags;
+	int retval = 0;
+
+	unsigned char init_bytes[4] = IT87_INIT;
+	unsigned char it87_chipid = 0;
+	unsigned char ldn = 0;
+	unsigned int  it87_io = 0;
+	unsigned int  it87_irq = 0;
+
+	/* 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);
+
+	/* 8712 or 8705 ? */
+	it87_chipid = it87_read(IT87_CHIP_ID1);
+	if (it87_chipid != 0x87) {
+		retval = -ENXIO;
+		return retval;
+	}
+	it87_chipid = it87_read(IT87_CHIP_ID2);
+	if ((it87_chipid != 0x12) && (it87_chipid != 0x05)) {
+		printk(KERN_INFO LIRC_DRIVER_NAME
+		       ": no IT8705/12 found, exiting..\n");
+		retval = -ENXIO;
+		return retval;
+	}
+	printk(KERN_INFO LIRC_DRIVER_NAME
+	       ": found IT87%.2x.\n",
+	       it87_chipid);
+
+	/* get I/O-Port and IRQ */
+	if (it87_chipid == 0x12)
+		ldn = IT8712_CIR_LDN;
+	else
+		ldn = IT8705_CIR_LDN;
+	it87_write(IT87_LDN, ldn);
+
+	it87_io = it87_read(IT87_CIR_BASE_MSB) * 256 +
+		  it87_read(IT87_CIR_BASE_LSB);
+	if (it87_io == 0) {
+		if (io == 0)
+			io = IT87_CIR_DEFAULT_IOBASE;
+		printk(KERN_INFO LIRC_DRIVER_NAME
+		       ": set default io 0x%x\n",
+		       io);
+		it87_write(IT87_CIR_BASE_MSB, io / 0x100);
+		it87_write(IT87_CIR_BASE_LSB, io % 0x100);
+	} else
+		io = it87_io;
+
+	it87_irq = it87_read(IT87_CIR_IRQ);
+	if (digimatrix || it87_irq == 0) {
+		if (irq == 0)
+			irq = IT87_CIR_DEFAULT_IRQ;
+		printk(KERN_INFO LIRC_DRIVER_NAME
+		       ": set default irq 0x%x\n",
+		       irq);
+		it87_write(IT87_CIR_IRQ, irq);
+	} else
+		irq = it87_irq;
+
+	spin_lock_irqsave(&hardware_lock, hw_flags);
+	/* reset */
+	outb(IT87_CIR_IER_RESET, io+IT87_CIR_IER);
+	/* fifo clear */
+	outb(IT87_CIR_TCR1_FIFOCLR |
+	     /*	     IT87_CIR_TCR1_ILE | */
+	     IT87_CIR_TCR1_TXRLE |
+	     IT87_CIR_TCR1_TXENDF, io+IT87_CIR_TCR1);
+	spin_unlock_irqrestore(&hardware_lock, hw_flags);
+
+	/* get I/O port access and IRQ line */
+	if (request_region(io, 8, LIRC_DRIVER_NAME) == NULL) {
+		printk(KERN_ERR LIRC_DRIVER_NAME
+		       ": i/o port 0x%.4x already in use.\n", io);
+		/* Leaving MB PnP Mode */
+		it87_write(IT87_CFGCTRL, 0x2);
+		return -EBUSY;
+	}
+
+	/* activate CIR-Device */
+	it87_write(IT87_CIR_ACT, 0x1);
+
+	/* Leaving MB PnP Mode */
+	it87_write(IT87_CFGCTRL, 0x2);
+
+	retval = request_irq(irq, it87_interrupt, 0 /*IRQF_DISABLED*/,
+			     LIRC_DRIVER_NAME, NULL);
+	if (retval < 0) {
+		printk(KERN_ERR LIRC_DRIVER_NAME
+		       ": IRQ %d already in use.\n",
+		       irq);
+		release_region(io, 8);
+		return retval;
+	}
+
+	printk(KERN_INFO LIRC_DRIVER_NAME
+	       ": I/O port 0x%.4x, IRQ %d.\n", io, irq);
+
+	init_timer(&timerlist);
+	timerlist.function = it87_timeout;
+	timerlist.data = 0xabadcafe;
+
+	return 0;
+}
+
+
+static void drop_port(void)
+{
+/*
+	unsigned char init_bytes[4] = IT87_INIT;
+
+	/ * 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 * /
+	it87_write(IT87_CIR_ACT, 0x0);
+
+	/ * Leaving MB PnP Mode * /
+	it87_write(IT87_CFGCTRL, 0x2);
+*/
+
+	del_timer_sync(&timerlist);
+	free_irq(irq, NULL);
+	release_region(io, 8);
+}
+
+
+static int init_lirc_it87(void)
+{
+	int retval;
+
+	init_waitqueue_head(&lirc_read_queue);
+	retval = init_port();
+	if (retval < 0)
+		return retval;
+	init_hardware();
+	printk(KERN_INFO LIRC_DRIVER_NAME ": Installed.\n");
+	return 0;
+}
+
+
+#ifdef MODULE
+
+static int __init lirc_it87_init(void)
+{
+	int retval;
+
+	retval = init_chrdev();
+	if (retval < 0)
+		return retval;
+	retval = init_lirc_it87();
+	if (retval) {
+		drop_chrdev();
+		return retval;
+	}
+	return 0;
+}
+
+
+static void __exit lirc_it87_exit(void)
+{
+	drop_hardware();
+	drop_chrdev();
+	drop_port();
+	printk(KERN_INFO LIRC_DRIVER_NAME ": Uninstalled.\n");
+}
+
+module_init(lirc_it87_init);
+module_exit(lirc_it87_exit);
+
+MODULE_DESCRIPTION("LIRC driver for ITE IT8712/IT8705 CIR port");
+MODULE_AUTHOR("Hans-Gunter Lutke Uphues");
+MODULE_LICENSE("GPL");
+
+module_param(io, int, 0444);
+MODULE_PARM_DESC(io, "I/O base address (default: 0x310)");
+
+module_param(irq, int, 0444);
+#ifdef LIRC_IT87_DIGIMATRIX
+MODULE_PARM_DESC(irq, "Interrupt (1,3-12) (default: 9)");
+#else
+MODULE_PARM_DESC(irq, "Interrupt (1,3-12) (default: 7)");
+#endif
+
+module_param(it87_enable_demodulator, bool, 0444);
+MODULE_PARM_DESC(it87_enable_demodulator,
+		 "Receiver demodulator enable/disable (1/0), default: 0");
+
+module_param(debug, bool, 0644);
+MODULE_PARM_DESC(debug, "Enable debugging messages");
+
+module_param(digimatrix, bool, 0644);
+#ifdef LIRC_IT87_DIGIMATRIX
+MODULE_PARM_DESC(digimatrix,
+	"Asus Digimatrix it87 compat. enable/disable (1/0), default: 1");
+#else
+MODULE_PARM_DESC(digimatrix,
+	"Asus Digimatrix it87 compat. enable/disable (1/0), default: 0");
+#endif
+
+
+module_param(it87_freq, int, 0444);
+#ifdef LIRC_IT87_DIGIMATRIX
+MODULE_PARM_DESC(it87_freq,
+    "Carrier demodulator frequency (kHz), (default: 36)");
+#else
+MODULE_PARM_DESC(it87_freq,
+    "Carrier demodulator frequency (kHz), (default: 38)");
+#endif
+
+#endif /* MODULE */
+
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/input/lirc/lirc_it87.h b/drivers/input/lirc/lirc_it87.h
new file mode 100644
index 0000000..a997204
--- /dev/null
+++ b/drivers/input/lirc/lirc_it87.h
@@ -0,0 +1,116 @@
+/* lirc_it87.h */
+/* SECTION: Definitions */
+
+/********************************* ITE IT87xx ************************/
+
+/* based on the following documentation from ITE:
+   a) IT8712F Preliminary CIR Programming Guide V0.1
+   b) IT8705F Simple LPC I/O Preliminary Specifiction V0.3
+   c) IT8712F EC-LPC I/O Preliminary Specification V0.5
+*/
+
+/* IT8712/05 Ports: */
+#define IT87_ADRPORT      0x2e
+#define IT87_DATAPORT     0x2f
+#define IT87_INIT         {0x87, 0x01, 0x55, 0x55}
+
+/* alternate Ports: */
+/*
+#define IT87_ADRPORT      0x4e
+#define IT87_DATAPORT     0x4f
+#define IT87_INIT         {0x87, 0x01, 0x55, 0xaa}
+ */
+
+/* IT8712/05 Registers */
+#define IT87_CFGCTRL      0x2
+#define IT87_LDN          0x7
+#define IT87_CHIP_ID1     0x20
+#define IT87_CHIP_ID2     0x21
+#define IT87_CFG_VERSION  0x22
+#define IT87_SWSUSPEND    0x23
+
+#define IT8712_CIR_LDN    0xa
+#define IT8705_CIR_LDN    0x7
+
+/* CIR Configuration Registers: */
+#define IT87_CIR_ACT      0x30
+#define IT87_CIR_BASE_MSB 0x60
+#define IT87_CIR_BASE_LSB 0x61
+#define IT87_CIR_IRQ      0x70
+#define IT87_CIR_CONFIG   0xf0
+
+/* List of IT87_CIR registers: offset to BaseAddr */
+#define IT87_CIR_DR   0
+#define IT87_CIR_IER  1
+#define IT87_CIR_RCR  2
+#define IT87_CIR_TCR1 3
+#define IT87_CIR_TCR2 4
+#define IT87_CIR_TSR  5
+#define IT87_CIR_RSR  6
+#define IT87_CIR_BDLR 5
+#define IT87_CIR_BDHR 6
+#define IT87_CIR_IIR  7
+
+/* Bit Definitionen */
+/* IER: */
+#define IT87_CIR_IER_TM_EN   0x80
+#define IT87_CIR_IER_RESEVED 0x40
+#define IT87_CIR_IER_RESET   0x20
+#define IT87_CIR_IER_BR      0x10
+#define IT87_CIR_IER_IEC     0x8
+#define IT87_CIR_IER_RFOIE   0x4
+#define IT87_CIR_IER_RDAIE   0x2
+#define IT87_CIR_IER_TLDLIE  0x1
+
+/* RCR: */
+#define IT87_CIR_RCR_RDWOS  0x80
+#define IT87_CIR_RCR_HCFS   0x40
+#define IT87_CIR_RCR_RXEN   0x20
+#define IT87_CIR_RCR_RXEND  0x10
+#define IT87_CIR_RCR_RXACT  0x8
+#define IT87_CIR_RCR_RXDCR  0x7
+
+/* TCR1: */
+#define IT87_CIR_TCR1_FIFOCLR 0x80
+#define IT87_CIR_TCR1_ILE     0x40
+#define IT87_CIR_TCR1_FIFOTL  0x30
+#define IT87_CIR_TCR1_TXRLE   0x8
+#define IT87_CIR_TCR1_TXENDF  0x4
+#define IT87_CIR_TCR1_TXMPM   0x3
+
+/* TCR2: */
+#define IT87_CIR_TCR2_CFQ   0xf8
+#define IT87_CIR_TCR2_TXMPW 0x7
+
+/* TSR: */
+#define IT87_CIR_TSR_RESERVED 0xc0
+#define IT87_CIR_TSR_TXFBC    0x3f
+
+/* RSR: */
+#define IT87_CIR_RSR_RXFTO    0x80
+#define IT87_CIR_RSR_RESERVED 0x40
+#define IT87_CIR_RSR_RXFBC    0x3f
+
+/* IIR: */
+#define IT87_CIR_IIR_RESERVED 0xf8
+#define IT87_CIR_IIR_IID      0x6
+#define IT87_CIR_IIR_IIP      0x1
+
+/* TM: */
+#define IT87_CIR_TM_IL_SEL    0x80
+#define IT87_CIR_TM_RESERVED  0x40
+#define IT87_CIR_TM_TM_REG    0x3f
+
+#define IT87_CIR_FIFO_SIZE 32
+
+/* Baudratedivisor for IT87: power of 2: only 1,2,4 or 8) */
+#define IT87_CIR_BAUDRATE_DIVISOR 0x1
+#define IT87_CIR_DEFAULT_IOBASE 0x310
+#define IT87_CIR_DEFAULT_IRQ    0x7
+#define IT87_CIR_SPACE 0x00
+#define IT87_CIR_PULSE 0xff
+#define IT87_CIR_FREQ_MIN 27
+#define IT87_CIR_FREQ_MAX 58
+#define TIME_CONST (IT87_CIR_BAUDRATE_DIVISOR * 8000000ul / 115200ul)
+
+/********************************* ITE IT87xx ************************/
-- 
1.6.0.1


^ permalink raw reply related	[flat|nested] 111+ messages in thread

* [PATCH 15/18] lirc driver for the SIR IrDA port
  2008-09-09  4:05                           ` [PATCH 14/18] lirc driver for the ITE IT87xx CIR Port receivers Jarod Wilson
@ 2008-09-09  4:06                             ` Jarod Wilson
  2008-09-09  4:06                               ` [PATCH 16/18] lirc driver for the IR interface on BT829-based hardware Jarod Wilson
  0 siblings, 1 reply; 111+ messages in thread
From: Jarod Wilson @ 2008-09-09  4:06 UTC (permalink / raw)
  To: linux-kernel
  Cc: Jarod Wilson, Jarod Wilson, Janne Grunau, Christoph Bartelmus

Signed-off-by: Jarod Wilson <jarod@redhat.com>
Signed-off-by: Janne Grunau <j@jannau.net>
CC: Christoph Bartelmus <lirc@bartelmus.de>
---
 drivers/input/lirc/Kconfig    |    7 +
 drivers/input/lirc/Makefile   |    1 +
 drivers/input/lirc/lirc_sir.c | 1302 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 1310 insertions(+), 0 deletions(-)
 create mode 100644 drivers/input/lirc/lirc_sir.c

diff --git a/drivers/input/lirc/Kconfig b/drivers/input/lirc/Kconfig
index 42e9bc3..b2bdbfb 100644
--- a/drivers/input/lirc/Kconfig
+++ b/drivers/input/lirc/Kconfig
@@ -96,6 +96,13 @@ config LIRC_SERIAL
 	help
 	  Driver for Homebrew Serial Port Receivers
 
+config LIRC_SIR
+	tristate "Built-in SIR IrDA port"
+	default n
+	depends on LIRC_DEV
+	help
+	  Driver for the SIR IrDA port
+
 config LIRC_STREAMZAP
 	tristate "Streamzap PC Receiver"
 	default n
diff --git a/drivers/input/lirc/Makefile b/drivers/input/lirc/Makefile
index 4ee32f3..761cb47 100644
--- a/drivers/input/lirc/Makefile
+++ b/drivers/input/lirc/Makefile
@@ -17,5 +17,6 @@ obj-$(CONFIG_LIRC_MCEUSB)	+= lirc_mceusb.o
 obj-$(CONFIG_LIRC_MCEUSB2)	+= lirc_mceusb2.o
 obj-$(CONFIG_LIRC_SASEM)	+= lirc_sasem.o
 obj-$(CONFIG_LIRC_SERIAL)	+= lirc_serial.o
+obj-$(CONFIG_LIRC_SIR)		+= lirc_sir.o
 obj-$(CONFIG_LIRC_STREAMZAP)	+= lirc_streamzap.o
 obj-$(CONFIG_LIRC_TTUSBIR)	+= lirc_ttusbir.o
diff --git a/drivers/input/lirc/lirc_sir.c b/drivers/input/lirc/lirc_sir.c
new file mode 100644
index 0000000..ea192b2
--- /dev/null
+++ b/drivers/input/lirc/lirc_sir.c
@@ -0,0 +1,1302 @@
+/*
+ * LIRC SIR driver, (C) 2000 Milan Pikula <www@fornax.sk>
+ *
+ * lirc_sir - Device driver for use with SIR (serial infra red)
+ * mode of IrDA on many notebooks.
+ *
+ *  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
+ *
+ *
+ * 2000/09/16 Frank Przybylski <mail@frankprzybylski.de> :
+ *  added timeout and relaxed pulse detection, removed gap bug
+ *
+ * 2000/12/15 Christoph Bartelmus <lirc@bartelmus.de> :
+ *   added support for Tekram Irmate 210 (sending does not work yet,
+ *   kind of disappointing that nobody was able to implement that
+ *   before),
+ *   major clean-up
+ *
+ * 2001/02/27 Christoph Bartelmus <lirc@bartelmus.de> :
+ *   added support for StrongARM SA1100 embedded microprocessor
+ *   parts cut'n'pasted from sa1100_ir.c (C) 2000 Russell King
+ */
+
+
+#include <linux/version.h>
+#include <linux/module.h>
+
+#include <linux/autoconf.h>
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/serial_reg.h>
+#include <linux/time.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+#include <linux/mm.h>
+#include <linux/delay.h>
+#include <linux/poll.h>
+#include <asm/system.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+#include <asm/irq.h>
+#include <linux/fcntl.h>
+#ifdef LIRC_ON_SA1100
+#include <asm/hardware.h>
+#ifdef CONFIG_SA1100_COLLIE
+#include <asm/arch/tc35143.h>
+#include <asm/ucb1200.h>
+#endif
+#endif
+
+#include <linux/timer.h>
+
+#include "lirc.h"
+#include "lirc_dev.h"
+
+/* SECTION: Definitions */
+
+/**************************** Tekram dongle ***************************/
+#ifdef LIRC_SIR_TEKRAM
+/* stolen from kernel source */
+/* definitions for Tekram dongle */
+#define TEKRAM_115200 0x00
+#define TEKRAM_57600  0x01
+#define TEKRAM_38400  0x02
+#define TEKRAM_19200  0x03
+#define TEKRAM_9600   0x04
+#define TEKRAM_2400   0x08
+
+#define TEKRAM_PW 0x10 /* Pulse select bit */
+
+/* 10bit * 1s/115200bit in miliseconds = 87ms*/
+#define TIME_CONST (10000000ul/115200ul)
+
+#endif
+
+#ifdef LIRC_SIR_ACTISYS_ACT200L
+static void init_act200(void);
+#elif defined(LIRC_SIR_ACTISYS_ACT220L)
+static void init_act220(void);
+#endif
+
+/******************************* SA1100 ********************************/
+#ifdef LIRC_ON_SA1100
+struct sa1100_ser2_registers {
+	/* HSSP control register */
+	unsigned char hscr0;
+	/* UART registers */
+	unsigned char utcr0;
+	unsigned char utcr1;
+	unsigned char utcr2;
+	unsigned char utcr3;
+	unsigned char utcr4;
+	unsigned char utdr;
+	unsigned char utsr0;
+	unsigned char utsr1;
+} sr;
+
+static int irq = IRQ_Ser2ICP;
+
+#define LIRC_ON_SA1100_TRANSMITTER_LATENCY 0
+
+/* pulse/space ratio of 50/50 */
+static unsigned long pulse_width = (13-LIRC_ON_SA1100_TRANSMITTER_LATENCY);
+/* 1000000/freq-pulse_width */
+static unsigned long space_width = (13-LIRC_ON_SA1100_TRANSMITTER_LATENCY);
+static unsigned int freq = 38000;      /* modulation frequency */
+static unsigned int duty_cycle = 50;   /* duty cycle of 50% */
+
+#endif
+
+#define RBUF_LEN 1024
+#define WBUF_LEN 1024
+
+#define LIRC_DRIVER_NAME "lirc_sir"
+
+#define PULSE '['
+
+#ifndef LIRC_SIR_TEKRAM
+/* 9bit * 1s/115200bit in milli seconds = 78.125ms*/
+#define TIME_CONST (9000000ul/115200ul)
+#endif
+
+
+/* timeout for sequences in jiffies (=5/100s) */
+/* must be longer than TIME_CONST */
+#define SIR_TIMEOUT	(HZ*5/100)
+
+#ifndef LIRC_ON_SA1100
+#ifndef LIRC_IRQ
+#define LIRC_IRQ 4
+#endif
+#ifndef LIRC_PORT
+#define LIRC_PORT 0x3e8
+#endif
+
+static int io = LIRC_PORT;
+static int irq = LIRC_IRQ;
+static int threshold = 3;
+#endif
+
+static DEFINE_SPINLOCK(timer_lock);
+static struct timer_list timerlist;
+/* time of last signal change detected */
+static struct timeval last_tv = {0, 0};
+/* time of last UART data ready interrupt */
+static struct timeval last_intr_tv = {0, 0};
+static int last_value;
+
+static DECLARE_WAIT_QUEUE_HEAD(lirc_read_queue);
+
+static DEFINE_SPINLOCK(hardware_lock);
+static DEFINE_SPINLOCK(dev_lock);
+
+static int rx_buf[RBUF_LEN];
+static unsigned int rx_tail, rx_head;
+static int tx_buf[WBUF_LEN];
+
+static int debug;
+#define dprintk(fmt, args...)						\
+	do {								\
+		if (debug)						\
+			printk(KERN_DEBUG LIRC_DRIVER_NAME ": "		\
+				fmt, ## args);				\
+	} while (0)
+
+/* SECTION: Prototypes */
+
+/* Communication with user-space */
+static int lirc_open(struct inode *inode, struct file *file);
+static int lirc_close(struct inode *inode, struct file *file);
+static unsigned int lirc_poll(struct file *file, poll_table *wait);
+static ssize_t lirc_read(struct file *file, char *buf, size_t count,
+		loff_t *ppos);
+static ssize_t lirc_write(struct file *file, const char *buf, size_t n,
+		loff_t *pos);
+static int lirc_ioctl(struct inode *node, struct file *filep, unsigned int cmd,
+		unsigned long arg);
+static void add_read_queue(int flag, unsigned long val);
+#ifdef MODULE
+static int init_chrdev(void);
+static void drop_chrdev(void);
+#endif
+	/* Hardware */
+static irqreturn_t sir_interrupt(int irq, void *dev_id);
+static void send_space(unsigned long len);
+static void send_pulse(unsigned long len);
+static int init_hardware(void);
+static void drop_hardware(void);
+	/* Initialisation */
+static int init_port(void);
+static void drop_port(void);
+
+#ifdef LIRC_ON_SA1100
+static inline void on(void)
+{
+	PPSR |= PPC_TXD2;
+}
+
+static inline void off(void)
+{
+	PPSR &= ~PPC_TXD2;
+}
+#else
+static inline unsigned int sinp(int offset)
+{
+	return inb(io + offset);
+}
+
+static inline void soutp(int offset, int value)
+{
+	outb(value, io + offset);
+}
+#endif
+
+#ifndef MAX_UDELAY_MS
+#define MAX_UDELAY_US 5000
+#else
+#define MAX_UDELAY_US (MAX_UDELAY_MS*1000)
+#endif
+
+static inline void safe_udelay(unsigned long usecs)
+{
+	while (usecs > MAX_UDELAY_US) {
+		udelay(MAX_UDELAY_US);
+		usecs -= MAX_UDELAY_US;
+	}
+	udelay(usecs);
+}
+
+/* SECTION: Communication with user-space */
+
+static int lirc_open(struct inode *inode, struct file *file)
+{
+	spin_lock(&dev_lock);
+	if (module_refcount(THIS_MODULE)) {
+		spin_unlock(&dev_lock);
+		return -EBUSY;
+	}
+	spin_unlock(&dev_lock);
+	return 0;
+}
+
+static int lirc_close(struct inode *inode, struct file *file)
+{
+	return 0;
+}
+
+static unsigned int lirc_poll(struct file *file, poll_table *wait)
+{
+	poll_wait(file, &lirc_read_queue, wait);
+	if (rx_head != rx_tail)
+		return POLLIN | POLLRDNORM;
+	return 0;
+}
+
+static ssize_t lirc_read(struct file *file, char *buf, size_t count,
+		loff_t *ppos)
+{
+	int n = 0;
+	int retval = 0;
+	DECLARE_WAITQUEUE(wait, current);
+
+	if (n % sizeof(int))
+		return -EINVAL;
+
+	add_wait_queue(&lirc_read_queue, &wait);
+	set_current_state(TASK_INTERRUPTIBLE);
+	while (n < count) {
+		if (rx_head != rx_tail) {
+			if (copy_to_user((void *) buf + n,
+					(void *) (rx_buf + rx_head),
+					sizeof(int))) {
+				retval = -EFAULT;
+				break;
+			}
+			rx_head = (rx_head + 1) & (RBUF_LEN - 1);
+			n += sizeof(int);
+		} else {
+			if (file->f_flags & O_NONBLOCK) {
+				retval = -EAGAIN;
+				break;
+			}
+			if (signal_pending(current)) {
+				retval = -ERESTARTSYS;
+				break;
+			}
+			schedule();
+			set_current_state(TASK_INTERRUPTIBLE);
+		}
+	}
+	remove_wait_queue(&lirc_read_queue, &wait);
+	set_current_state(TASK_RUNNING);
+	return n ? n : retval;
+}
+static ssize_t lirc_write(struct file *file, const char *buf, size_t n,
+				loff_t *pos)
+{
+	unsigned long flags;
+	int i;
+
+	if (n % sizeof(int) || (n / sizeof(int)) > WBUF_LEN)
+		return -EINVAL;
+	if (copy_from_user(tx_buf, buf, n))
+		return -EFAULT;
+	i = 0;
+	n /= sizeof(int);
+#ifdef LIRC_ON_SA1100
+	/* disable receiver */
+	Ser2UTCR3 = 0;
+#endif
+	local_irq_save(flags);
+	while (1) {
+		if (i >= n)
+			break;
+		if (tx_buf[i])
+			send_pulse(tx_buf[i]);
+		i++;
+		if (i >= n)
+			break;
+		if (tx_buf[i])
+			send_space(tx_buf[i]);
+		i++;
+	}
+	local_irq_restore(flags);
+#ifdef LIRC_ON_SA1100
+	off();
+	udelay(1000); /* wait 1ms for IR diode to recover */
+	Ser2UTCR3 = 0;
+	/* clear status register to prevent unwanted interrupts */
+	Ser2UTSR0 &= (UTSR0_RID | UTSR0_RBB | UTSR0_REB);
+	/* enable receiver */
+	Ser2UTCR3 = UTCR3_RXE|UTCR3_RIE;
+#endif
+	return n;
+}
+
+static int lirc_ioctl(struct inode *node, struct file *filep, unsigned int cmd,
+		unsigned long arg)
+{
+	int retval = 0;
+	unsigned long value = 0;
+#ifdef LIRC_ON_SA1100
+	unsigned int ivalue;
+
+	if (cmd == LIRC_GET_FEATURES)
+		value = LIRC_CAN_SEND_PULSE |
+			LIRC_CAN_SET_SEND_DUTY_CYCLE |
+			LIRC_CAN_SET_SEND_CARRIER |
+			LIRC_CAN_REC_MODE2;
+	else if (cmd == LIRC_GET_SEND_MODE)
+		value = LIRC_MODE_PULSE;
+	else if (cmd == LIRC_GET_REC_MODE)
+		value = LIRC_MODE_MODE2;
+#else
+	if (cmd == LIRC_GET_FEATURES)
+		value = LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2;
+	else if (cmd == LIRC_GET_SEND_MODE)
+		value = LIRC_MODE_PULSE;
+	else if (cmd == LIRC_GET_REC_MODE)
+		value = LIRC_MODE_MODE2;
+#endif
+
+	switch (cmd) {
+	case LIRC_GET_FEATURES:
+	case LIRC_GET_SEND_MODE:
+	case LIRC_GET_REC_MODE:
+		retval = put_user(value, (unsigned long *) arg);
+		break;
+
+	case LIRC_SET_SEND_MODE:
+	case LIRC_SET_REC_MODE:
+		retval = get_user(value, (unsigned long *) arg);
+		break;
+#ifdef LIRC_ON_SA1100
+	case LIRC_SET_SEND_DUTY_CYCLE:
+		retval = get_user(ivalue, (unsigned int *) arg);
+		if (retval)
+			return reetval;
+		if (ivalue <= 0 || ivalue > 100)
+			return -EINVAL;
+		/* (ivalue/100)*(1000000/freq) */
+		duty_cycle = ivalue;
+		pulse_width = (unsigned long) duty_cycle*10000/freq;
+		space_width = (unsigned long) 1000000L/freq-pulse_width;
+		if (pulse_width >= LIRC_ON_SA1100_TRANSMITTER_LATENCY)
+			pulse_width -= LIRC_ON_SA1100_TRANSMITTER_LATENCY;
+		if (space_width >= LIRC_ON_SA1100_TRANSMITTER_LATENCY)
+			space_width -= LIRC_ON_SA1100_TRANSMITTER_LATENCY;
+		break;
+	case LIRC_SET_SEND_CARRIER:
+		retval = get_user(ivalue, (unsigned int *) arg);
+		if (retval)
+			return retval;
+		if (ivalue > 500000 || ivalue < 20000)
+			return -EINVAL;
+		freq = ivalue;
+		pulse_width = (unsigned long) duty_cycle*10000/freq;
+		space_width = (unsigned long) 1000000L/freq-pulse_width;
+		if (pulse_width >= LIRC_ON_SA1100_TRANSMITTER_LATENCY)
+			pulse_width -= LIRC_ON_SA1100_TRANSMITTER_LATENCY;
+		if (space_width >= LIRC_ON_SA1100_TRANSMITTER_LATENCY)
+			space_width -= LIRC_ON_SA1100_TRANSMITTER_LATENCY;
+		break;
+#endif
+	default:
+		retval = -ENOIOCTLCMD;
+
+	}
+
+	if (retval)
+		return retval;
+	if (cmd == LIRC_SET_REC_MODE) {
+		if (value != LIRC_MODE_MODE2)
+			retval = -ENOSYS;
+	} else if (cmd == LIRC_SET_SEND_MODE) {
+		if (value != LIRC_MODE_PULSE)
+			retval = -ENOSYS;
+	}
+
+	return retval;
+}
+
+static void add_read_queue(int flag, unsigned long val)
+{
+	unsigned int new_rx_tail;
+	int newval;
+
+	dprintk("add flag %d with val %lu\n", flag, val);
+
+	newval = val & PULSE_MASK;
+
+	/* statistically pulses are ~TIME_CONST/2 too long: we could
+	   maybe make this more exactly but this is good enough */
+	if (flag) {
+		/* pulse */
+		if (newval > TIME_CONST/2)
+			newval -= TIME_CONST/2;
+		else /* should not ever happen */
+			newval = 1;
+		newval |= PULSE_BIT;
+	} else {
+		newval += TIME_CONST/2;
+	}
+	new_rx_tail = (rx_tail + 1) & (RBUF_LEN - 1);
+	if (new_rx_tail == rx_head) {
+		dprintk("Buffer overrun.\n");
+		return;
+	}
+	rx_buf[rx_tail] = newval;
+	rx_tail = new_rx_tail;
+	wake_up_interruptible(&lirc_read_queue);
+}
+
+static struct file_operations lirc_fops = {
+	.read		= lirc_read,
+	.write		= lirc_write,
+	.poll		= lirc_poll,
+	.ioctl		= lirc_ioctl,
+	.open		= lirc_open,
+	.release	= lirc_close,
+};
+
+static int set_use_inc(void *data)
+{
+       return 0;
+}
+
+static void set_use_dec(void *data)
+{
+}
+
+static struct lirc_plugin plugin = {
+       .name		= LIRC_DRIVER_NAME,
+       .minor		= -1,
+       .code_length	= 1,
+       .sample_rate	= 0,
+       .data		= NULL,
+       .add_to_buf	= NULL,
+       .get_queue	= NULL,
+       .set_use_inc	= set_use_inc,
+       .set_use_dec	= set_use_dec,
+       .fops		= &lirc_fops,
+       .dev		= NULL,
+       .owner		= THIS_MODULE,
+};
+
+
+#ifdef MODULE
+static int init_chrdev(void)
+{
+	plugin.minor = lirc_register_plugin(&plugin);
+	if (plugin.minor < 0) {
+		printk(KERN_ERR LIRC_DRIVER_NAME ": init_chrdev() failed.\n");
+		return -EIO;
+	}
+	return 0;
+}
+
+static void drop_chrdev(void)
+{
+	lirc_unregister_plugin(plugin.minor);
+}
+#endif
+
+/* SECTION: Hardware */
+static long delta(struct timeval *tv1, struct timeval *tv2)
+{
+	unsigned long deltv;
+
+	deltv = tv2->tv_sec - tv1->tv_sec;
+	if (deltv > 15)
+		deltv = 0xFFFFFF;
+	else
+		deltv = deltv*1000000 +
+			tv2->tv_usec -
+			tv1->tv_usec;
+	return deltv;
+}
+
+static void sir_timeout(unsigned long data)
+{
+	/* if last received signal was a pulse, but receiving stopped
+	   within the 9 bit frame, we need to finish this pulse and
+	   simulate a signal change to from pulse to space. Otherwise
+	   upper layers will receive two sequences next time. */
+
+	unsigned long flags;
+	unsigned long pulse_end;
+
+	/* avoid interference with interrupt */
+	spin_lock_irqsave(&timer_lock, flags);
+	if (last_value) {
+#ifndef LIRC_ON_SA1100
+		/* clear unread bits in UART and restart */
+		outb(UART_FCR_CLEAR_RCVR, io + UART_FCR);
+#endif
+		/* determine 'virtual' pulse end: */
+		pulse_end = delta(&last_tv, &last_intr_tv);
+		dprintk("timeout add %d for %lu usec\n", last_value, pulse_end);
+		add_read_queue(last_value, pulse_end);
+		last_value = 0;
+		last_tv = last_intr_tv;
+	}
+	spin_unlock_irqrestore(&timer_lock, flags);
+}
+
+static irqreturn_t sir_interrupt(int irq, void *dev_id)
+{
+	unsigned char data;
+	struct timeval curr_tv;
+	static unsigned long deltv;
+#ifdef LIRC_ON_SA1100
+	int status;
+	static int n;
+
+	status = Ser2UTSR0;
+	/*
+	 * Deal with any receive errors first.  The bytes in error may be
+	 * the only bytes in the receive FIFO, so we do this first.
+	 */
+	while (status & UTSR0_EIF) {
+		int bstat;
+
+		if (debug) {
+			dprintk("EIF\n");
+			bstat = Ser2UTSR1;
+
+			if (bstat & UTSR1_FRE)
+				dprintk("frame error\n");
+			if (bstat & UTSR1_ROR)
+				dprintk("receive fifo overrun\n");
+			if (bstat & UTSR1_PRE)
+				dprintk("parity error\n");
+		}
+
+		bstat = Ser2UTDR;
+		n++;
+		status = Ser2UTSR0;
+	}
+
+	if (status & (UTSR0_RFS | UTSR0_RID)) {
+		do_gettimeofday(&curr_tv);
+		deltv = delta(&last_tv, &curr_tv);
+		do {
+			data = Ser2UTDR;
+			dprintk("%d data: %u\n", n, (unsigned int) data);
+			n++;
+		} while (status & UTSR0_RID && /* do not empty fifo in
+						order to get UTSR0_RID in
+						any case */
+		      Ser2UTSR1 & UTSR1_RNE); /* data ready */
+
+		if (status&UTSR0_RID) {
+			add_read_queue(0 , deltv - n * TIME_CONST); /*space*/
+			add_read_queue(1, n * TIME_CONST); /*pulse*/
+			n = 0;
+			last_tv = curr_tv;
+		}
+	}
+
+	if (status & UTSR0_TFS)
+		printk(KERN_ERR "transmit fifo not full, shouldn't happen\n");
+
+	/*
+	 * We must clear certain bits.
+	 */
+	status &= (UTSR0_RID | UTSR0_RBB | UTSR0_REB);
+	if (status)
+		Ser2UTSR0 = status;
+#else
+	unsigned long deltintrtv;
+	unsigned long flags;
+	int iir, lsr;
+
+	while ((iir = inb(io + UART_IIR) & UART_IIR_ID)) {
+		switch (iir&UART_IIR_ID) { /* FIXME toto treba preriedit */
+		case UART_IIR_MSI:
+			(void) inb(io + UART_MSR);
+			break;
+		case UART_IIR_RLSI:
+			(void) inb(io + UART_LSR);
+			break;
+		case UART_IIR_THRI:
+#if 0
+			if (lsr & UART_LSR_THRE) /* FIFO is empty */
+				outb(data, io + UART_TX)
+#endif
+			break;
+		case UART_IIR_RDI:
+			/* avoid interference with timer */
+			spin_lock_irqsave(&timer_lock, flags);
+			do {
+				del_timer(&timerlist);
+				data = inb(io + UART_RX);
+				do_gettimeofday(&curr_tv);
+				deltv = delta(&last_tv, &curr_tv);
+				deltintrtv = delta(&last_intr_tv, &curr_tv);
+				dprintk("t %lu, d %d\n", deltintrtv, (int)data);
+				/* if nothing came in last X cycles,
+				   it was gap */
+				if (deltintrtv > TIME_CONST * threshold) {
+					if (last_value) {
+						dprintk("GAP\n");
+						/* simulate signal change */
+						add_read_queue(last_value,
+							       deltv -
+							       deltintrtv);
+						last_value = 0;
+						last_tv.tv_sec =
+							last_intr_tv.tv_sec;
+						last_tv.tv_usec =
+							last_intr_tv.tv_usec;
+						deltv = deltintrtv;
+					}
+				}
+				data = 1;
+				if (data ^ last_value) {
+					/* deltintrtv > 2*TIME_CONST,
+						remember ? */
+					/* the other case is timeout */
+					add_read_queue(last_value,
+						       deltv-TIME_CONST);
+					last_value = data;
+					last_tv = curr_tv;
+					if (last_tv.tv_usec >= TIME_CONST) {
+						last_tv.tv_usec -= TIME_CONST;
+					} else {
+						last_tv.tv_sec--;
+						last_tv.tv_usec += 1000000 -
+							TIME_CONST;
+					}
+				}
+				last_intr_tv = curr_tv;
+				if (data) {
+					/* start timer for end of
+					 * sequence detection */
+					timerlist.expires = jiffies +
+								SIR_TIMEOUT;
+					add_timer(&timerlist);
+				}
+
+				lsr = inb(io + UART_LSR);
+			} while (lsr & UART_LSR_DR); /* data ready */
+			spin_unlock_irqrestore(&timer_lock, flags);
+			break;
+		default:
+			break;
+		}
+	}
+#endif
+	return IRQ_RETVAL(IRQ_HANDLED);
+}
+
+#ifdef LIRC_ON_SA1100
+static void send_pulse(unsigned long length)
+{
+	unsigned long k, delay;
+	int flag;
+
+	if (length == 0)
+		return;
+	/* this won't give us the carrier frequency we really want
+	   due to integer arithmetic, but we can accept this inaccuracy */
+
+	for (k = flag = 0; k < length; k += delay, flag = !flag) {
+		if (flag) {
+			off();
+			delay = space_width;
+		} else {
+			on();
+			delay = pulse_width;
+		}
+		safe_udelay(delay);
+	}
+	off();
+}
+
+static void send_space(unsigned long length)
+{
+	if (length == 0)
+		return;
+	off();
+	safe_udelay(length);
+}
+#else
+static void send_space(unsigned long len)
+{
+	safe_udelay(len);
+}
+
+static void send_pulse(unsigned long len)
+{
+	long bytes_out = len / TIME_CONST;
+	long time_left;
+
+	time_left = (long)len - (long)bytes_out * (long)TIME_CONST;
+	if (bytes_out == 0) {
+		bytes_out++;
+		time_left = 0;
+	}
+	while (bytes_out--) {
+		outb(PULSE, io + UART_TX);
+		/* FIXME treba seriozne cakanie z char/serial.c */
+		while (!(inb(io + UART_LSR) & UART_LSR_THRE))
+			;
+	}
+#if 0
+	if (time_left > 0)
+		safe_udelay(time_left);
+#endif
+}
+#endif
+
+#ifdef CONFIG_SA1100_COLLIE
+static inline int sa1100_irda_set_power_collie(int state)
+{
+	if (state) {
+		/*
+		 *  0 - off
+		 *  1 - short range, lowest power
+		 *  2 - medium range, medium power
+		 *  3 - maximum range, high power
+		 */
+		ucb1200_set_io_direction(TC35143_GPIO_IR_ON,
+					 TC35143_IODIR_OUTPUT);
+		ucb1200_set_io(TC35143_GPIO_IR_ON, TC35143_IODAT_LOW);
+		udelay(100);
+	} else {
+		/* OFF */
+		ucb1200_set_io_direction(TC35143_GPIO_IR_ON,
+					 TC35143_IODIR_OUTPUT);
+		ucb1200_set_io(TC35143_GPIO_IR_ON, TC35143_IODAT_HIGH);
+	}
+	return 0;
+}
+#endif
+
+static int init_hardware(void)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&hardware_lock, flags);
+	/* reset UART */
+#ifdef LIRC_ON_SA1100
+#ifdef CONFIG_SA1100_BITSY
+	if (machine_is_bitsy()) {
+		printk(KERN_INFO "Power on IR module\n");
+		set_bitsy_egpio(EGPIO_BITSY_IR_ON);
+	}
+#endif
+#ifdef CONFIG_SA1100_COLLIE
+	sa1100_irda_set_power_collie(3);	/* power on */
+#endif
+	sr.hscr0 = Ser2HSCR0;
+
+	sr.utcr0 = Ser2UTCR0;
+	sr.utcr1 = Ser2UTCR1;
+	sr.utcr2 = Ser2UTCR2;
+	sr.utcr3 = Ser2UTCR3;
+	sr.utcr4 = Ser2UTCR4;
+
+	sr.utdr = Ser2UTDR;
+	sr.utsr0 = Ser2UTSR0;
+	sr.utsr1 = Ser2UTSR1;
+
+	/* configure GPIO */
+	/* output */
+	PPDR |= PPC_TXD2;
+	PSDR |= PPC_TXD2;
+	/* set output to 0 */
+	off();
+
+	/*
+	 * Enable HP-SIR modulation, and ensure that the port is disabled.
+	 */
+	Ser2UTCR3 = 0;
+	Ser2HSCR0 = sr.hscr0 & (~HSCR0_HSSP);
+
+	/* clear status register to prevent unwanted interrupts */
+	Ser2UTSR0 &= (UTSR0_RID | UTSR0_RBB | UTSR0_REB);
+
+	/* 7N1 */
+	Ser2UTCR0 = UTCR0_1StpBit|UTCR0_7BitData;
+	/* 115200 */
+	Ser2UTCR1 = 0;
+	Ser2UTCR2 = 1;
+	/* use HPSIR, 1.6 usec pulses */
+	Ser2UTCR4 = UTCR4_HPSIR|UTCR4_Z1_6us;
+
+	/* enable receiver, receive fifo interrupt */
+	Ser2UTCR3 = UTCR3_RXE|UTCR3_RIE;
+
+	/* clear status register to prevent unwanted interrupts */
+	Ser2UTSR0 &= (UTSR0_RID | UTSR0_RBB | UTSR0_REB);
+
+#elif defined(LIRC_SIR_TEKRAM)
+	/* disable FIFO */
+	soutp(UART_FCR,
+	      UART_FCR_CLEAR_RCVR|
+	      UART_FCR_CLEAR_XMIT|
+	      UART_FCR_TRIGGER_1);
+
+	/* Set DLAB 0. */
+	soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
+
+	/* First of all, disable all interrupts */
+	soutp(UART_IER, sinp(UART_IER) &
+	      (~(UART_IER_MSI|UART_IER_RLSI|UART_IER_THRI|UART_IER_RDI)));
+
+	/* Set DLAB 1. */
+	soutp(UART_LCR, sinp(UART_LCR) | UART_LCR_DLAB);
+
+	/* Set divisor to 12 => 9600 Baud */
+	soutp(UART_DLM, 0);
+	soutp(UART_DLL, 12);
+
+	/* Set DLAB 0. */
+	soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
+
+	/* power supply */
+	soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2);
+	safe_udelay(50*1000);
+
+	/* -DTR low -> reset PIC */
+	soutp(UART_MCR, UART_MCR_RTS|UART_MCR_OUT2);
+	udelay(1*1000);
+
+	soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2);
+	udelay(100);
+
+
+	/* -RTS low -> send control byte */
+	soutp(UART_MCR, UART_MCR_DTR|UART_MCR_OUT2);
+	udelay(7);
+	soutp(UART_TX, TEKRAM_115200|TEKRAM_PW);
+
+	/* one byte takes ~1042 usec to transmit at 9600,8N1 */
+	udelay(1500);
+
+	/* back to normal operation */
+	soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2);
+	udelay(50);
+
+	udelay(1500);
+
+	/* read previous control byte */
+	printk(KERN_INFO LIRC_DRIVER_NAME
+	       ": 0x%02x\n", sinp(UART_RX));
+
+	/* Set DLAB 1. */
+	soutp(UART_LCR, sinp(UART_LCR) | UART_LCR_DLAB);
+
+	/* Set divisor to 1 => 115200 Baud */
+	soutp(UART_DLM, 0);
+	soutp(UART_DLL, 1);
+
+	/* Set DLAB 0, 8 Bit */
+	soutp(UART_LCR, UART_LCR_WLEN8);
+	/* enable interrupts */
+	soutp(UART_IER, sinp(UART_IER)|UART_IER_RDI);
+#else
+	outb(0, io + UART_MCR);
+	outb(0, io + UART_IER);
+	/* init UART */
+		/* set DLAB, speed = 115200 */
+	outb(UART_LCR_DLAB | UART_LCR_WLEN7, io + UART_LCR);
+	outb(1, io + UART_DLL); outb(0, io + UART_DLM);
+		/* 7N1+start = 9 bits at 115200 ~ 3 bits at 44000 */
+	outb(UART_LCR_WLEN7, io + UART_LCR);
+		/* FIFO operation */
+	outb(UART_FCR_ENABLE_FIFO, io + UART_FCR);
+		/* interrupts */
+	/* outb(UART_IER_RLSI|UART_IER_RDI|UART_IER_THRI, io + UART_IER); */
+	outb(UART_IER_RDI, io + UART_IER);
+	/* turn on UART */
+	outb(UART_MCR_DTR|UART_MCR_RTS|UART_MCR_OUT2, io + UART_MCR);
+#ifdef LIRC_SIR_ACTISYS_ACT200L
+	init_act200();
+#elif defined(LIRC_SIR_ACTISYS_ACT220L)
+	init_act220();
+#endif
+#endif
+	spin_unlock_irqrestore(&hardware_lock, flags);
+	return 0;
+}
+
+static void drop_hardware(void)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&hardware_lock, flags);
+
+#ifdef LIRC_ON_SA1100
+	Ser2UTCR3 = 0;
+
+	Ser2UTCR0 = sr.utcr0;
+	Ser2UTCR1 = sr.utcr1;
+	Ser2UTCR2 = sr.utcr2;
+	Ser2UTCR4 = sr.utcr4;
+	Ser2UTCR3 = sr.utcr3;
+
+	Ser2HSCR0 = sr.hscr0;
+#ifdef CONFIG_SA1100_BITSY
+	if (machine_is_bitsy())
+		clr_bitsy_egpio(EGPIO_BITSY_IR_ON);
+#endif
+#ifdef CONFIG_SA1100_COLLIE
+	sa1100_irda_set_power_collie(0);	/* power off */
+#endif
+#else
+	/* turn off interrupts */
+	outb(0, io + UART_IER);
+#endif
+	spin_unlock_irqrestore(&hardware_lock, flags);
+}
+
+/* SECTION: Initialisation */
+
+static int init_port(void)
+{
+	int retval;
+
+	/* get I/O port access and IRQ line */
+#ifndef LIRC_ON_SA1100
+	if (request_region(io, 8, LIRC_DRIVER_NAME) == NULL) {
+		printk(KERN_ERR LIRC_DRIVER_NAME
+		       ": i/o port 0x%.4x already in use.\n", io);
+		return -EBUSY;
+	}
+#endif
+	retval = request_irq(irq, sir_interrupt, IRQF_DISABLED,
+			     LIRC_DRIVER_NAME, NULL);
+	if (retval < 0) {
+#               ifndef LIRC_ON_SA1100
+		release_region(io, 8);
+#               endif
+		printk(KERN_ERR LIRC_DRIVER_NAME
+			": IRQ %d already in use.\n",
+			irq);
+		return retval;
+	}
+#ifndef LIRC_ON_SA1100
+	printk(KERN_INFO LIRC_DRIVER_NAME
+		": I/O port 0x%.4x, IRQ %d.\n",
+		io, irq);
+#endif
+
+	init_timer(&timerlist);
+	timerlist.function = sir_timeout;
+	timerlist.data = 0xabadcafe;
+
+	return 0;
+}
+
+static void drop_port(void)
+{
+	free_irq(irq, NULL);
+	del_timer_sync(&timerlist);
+#ifndef LIRC_ON_SA1100
+	release_region(io, 8);
+#endif
+}
+
+#ifdef LIRC_SIR_ACTISYS_ACT200L
+/******************************************************/
+/* Crystal/Cirrus CS8130 IR transceiver, used in Actisys Act200L dongle */
+/* some code borrowed from Linux IRDA driver */
+
+/* Regsiter 0: Control register #1 */
+#define ACT200L_REG0    0x00
+#define ACT200L_TXEN    0x01 /* Enable transmitter */
+#define ACT200L_RXEN    0x02 /* Enable receiver */
+#define ACT200L_ECHO    0x08 /* Echo control chars */
+
+/* Register 1: Control register #2 */
+#define ACT200L_REG1    0x10
+#define ACT200L_LODB    0x01 /* Load new baud rate count value */
+#define ACT200L_WIDE    0x04 /* Expand the maximum allowable pulse */
+
+/* Register 3: Transmit mode register #2 */
+#define ACT200L_REG3    0x30
+#define ACT200L_B0      0x01 /* DataBits, 0=6, 1=7, 2=8, 3=9(8P)  */
+#define ACT200L_B1      0x02 /* DataBits, 0=6, 1=7, 2=8, 3=9(8P)  */
+#define ACT200L_CHSY    0x04 /* StartBit Synced 0=bittime, 1=startbit */
+
+/* Register 4: Output Power register */
+#define ACT200L_REG4    0x40
+#define ACT200L_OP0     0x01 /* Enable LED1C output */
+#define ACT200L_OP1     0x02 /* Enable LED2C output */
+#define ACT200L_BLKR    0x04
+
+/* Register 5: Receive Mode register */
+#define ACT200L_REG5    0x50
+#define ACT200L_RWIDL   0x01 /* fixed 1.6us pulse mode */
+    /*.. other various IRDA bit modes, and TV remote modes..*/
+
+/* Register 6: Receive Sensitivity register #1 */
+#define ACT200L_REG6    0x60
+#define ACT200L_RS0     0x01 /* receive threshold bit 0 */
+#define ACT200L_RS1     0x02 /* receive threshold bit 1 */
+
+/* Register 7: Receive Sensitivity register #2 */
+#define ACT200L_REG7    0x70
+#define ACT200L_ENPOS   0x04 /* Ignore the falling edge */
+
+/* Register 8,9: Baud Rate Dvider register #1,#2 */
+#define ACT200L_REG8    0x80
+#define ACT200L_REG9    0x90
+
+#define ACT200L_2400    0x5f
+#define ACT200L_9600    0x17
+#define ACT200L_19200   0x0b
+#define ACT200L_38400   0x05
+#define ACT200L_57600   0x03
+#define ACT200L_115200  0x01
+
+/* Register 13: Control register #3 */
+#define ACT200L_REG13   0xd0
+#define ACT200L_SHDW    0x01 /* Enable access to shadow registers */
+
+/* Register 15: Status register */
+#define ACT200L_REG15   0xf0
+
+/* Register 21: Control register #4 */
+#define ACT200L_REG21   0x50
+#define ACT200L_EXCK    0x02 /* Disable clock output driver */
+#define ACT200L_OSCL    0x04 /* oscillator in low power, medium accuracy mode */
+
+static void init_act200(void)
+{
+	int i;
+	__u8 control[] = {
+		ACT200L_REG15,
+		ACT200L_REG13 | ACT200L_SHDW,
+		ACT200L_REG21 | ACT200L_EXCK | ACT200L_OSCL,
+		ACT200L_REG13,
+		ACT200L_REG7  | ACT200L_ENPOS,
+		ACT200L_REG6  | ACT200L_RS0  | ACT200L_RS1,
+		ACT200L_REG5  | ACT200L_RWIDL,
+		ACT200L_REG4  | ACT200L_OP0  | ACT200L_OP1 | ACT200L_BLKR,
+		ACT200L_REG3  | ACT200L_B0,
+		ACT200L_REG0  | ACT200L_TXEN | ACT200L_RXEN,
+		ACT200L_REG8 |  (ACT200L_115200       & 0x0f),
+		ACT200L_REG9 | ((ACT200L_115200 >> 4) & 0x0f),
+		ACT200L_REG1 | ACT200L_LODB | ACT200L_WIDE
+	};
+
+	/* Set DLAB 1. */
+	soutp(UART_LCR, UART_LCR_DLAB | UART_LCR_WLEN8);
+
+	/* Set divisor to 12 => 9600 Baud */
+	soutp(UART_DLM, 0);
+	soutp(UART_DLL, 12);
+
+	/* Set DLAB 0. */
+	soutp(UART_LCR, UART_LCR_WLEN8);
+	/* Set divisor to 12 => 9600 Baud */
+
+	/* power supply */
+	soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2);
+	for (i = 0; i < 50; i++)
+		safe_udelay(1000);
+
+		/* Reset the dongle : set RTS low for 25 ms */
+	soutp(UART_MCR, UART_MCR_DTR|UART_MCR_OUT2);
+	for (i = 0; i < 25; i++)
+		udelay(1000);
+
+	soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2);
+	udelay(100);
+
+	/* Clear DTR and set RTS to enter command mode */
+	soutp(UART_MCR, UART_MCR_RTS|UART_MCR_OUT2);
+	udelay(7);
+
+/* send out the control register settings for 115K 7N1 SIR operation */
+	for (i = 0; i < sizeof(control); i++) {
+		soutp(UART_TX, control[i]);
+		/* one byte takes ~1042 usec to transmit at 9600,8N1 */
+		udelay(1500);
+	}
+
+	/* back to normal operation */
+	soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2);
+	udelay(50);
+
+	udelay(1500);
+	soutp(UART_LCR, sinp(UART_LCR) | UART_LCR_DLAB);
+
+	/* Set DLAB 1. */
+	soutp(UART_LCR, UART_LCR_DLAB | UART_LCR_WLEN7);
+
+	/* Set divisor to 1 => 115200 Baud */
+	soutp(UART_DLM, 0);
+	soutp(UART_DLL, 1);
+
+	/* Set DLAB 0. */
+	soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
+
+	/* Set DLAB 0, 7 Bit */
+	soutp(UART_LCR, UART_LCR_WLEN7);
+
+	/* enable interrupts */
+	soutp(UART_IER, sinp(UART_IER)|UART_IER_RDI);
+}
+#endif
+
+#ifdef LIRC_SIR_ACTISYS_ACT220L
+/* Derived from linux IrDA driver (net/irda/actisys.c)
+ * Drop me a mail for any kind of comment: maxx@spaceboyz.net */
+
+void init_act220(void)
+{
+	int i;
+
+	/* DLAB 1 */
+	soutp(UART_LCR, UART_LCR_DLAB|UART_LCR_WLEN7);
+
+	/* 9600 baud */
+	soutp(UART_DLM, 0);
+	soutp(UART_DLL, 12);
+
+	/* DLAB 0 */
+	soutp(UART_LCR, UART_LCR_WLEN7);
+
+	/* reset the dongle, set DTR low for 10us */
+	soutp(UART_MCR, UART_MCR_RTS|UART_MCR_OUT2);
+	udelay(10);
+
+	/* back to normal (still 9600) */
+	soutp(UART_MCR, UART_MCR_DTR|UART_MCR_RTS|UART_MCR_OUT2);
+
+	/* send RTS pulses until we reach 115200
+	 * i hope this is really the same for act220l/act220l+ */
+	for (i = 0; i < 3; i++) {
+		udelay(10);
+		/* set RTS low for 10 us */
+		soutp(UART_MCR, UART_MCR_DTR|UART_MCR_OUT2);
+		udelay(10);
+		/* set RTS high for 10 us */
+		soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2);
+	}
+
+	/* back to normal operation */
+	udelay(1500); /* better safe than sorry ;) */
+
+	/* Set DLAB 1. */
+	soutp(UART_LCR, UART_LCR_DLAB | UART_LCR_WLEN7);
+
+	/* Set divisor to 1 => 115200 Baud */
+	soutp(UART_DLM, 0);
+	soutp(UART_DLL, 1);
+
+	/* Set DLAB 0, 7 Bit */
+	/* The dongle doesn't seem to have any problems with operation
+	   at 7N1 */
+	soutp(UART_LCR, UART_LCR_WLEN7);
+
+	/* enable interrupts */
+	soutp(UART_IER, UART_IER_RDI);
+}
+#endif
+
+static int init_lirc_sir(void)
+{
+	int retval;
+
+	init_waitqueue_head(&lirc_read_queue);
+	retval = init_port();
+	if (retval < 0)
+		return retval;
+	init_hardware();
+	printk(KERN_INFO LIRC_DRIVER_NAME
+		": Installed.\n");
+	return 0;
+}
+
+#ifdef MODULE
+
+static int __init lirc_sir_init(void)
+{
+	int retval;
+
+	retval = init_chrdev();
+	if (retval < 0)
+		return retval;
+	retval = init_lirc_sir();
+	if (retval) {
+		drop_chrdev();
+		return retval;
+	}
+	return 0;
+}
+
+static void __exit lirc_sir_exit(void)
+{
+	drop_hardware();
+	drop_chrdev();
+	drop_port();
+	printk(KERN_INFO LIRC_DRIVER_NAME ": Uninstalled.\n");
+}
+
+module_init(lirc_sir_init);
+module_exit(lirc_sir_exit);
+
+#ifdef LIRC_SIR_TEKRAM
+MODULE_DESCRIPTION("Infrared receiver driver for Tekram Irmate 210");
+MODULE_AUTHOR("Christoph Bartelmus");
+#elif defined(LIRC_ON_SA1100)
+MODULE_DESCRIPTION("LIRC driver for StrongARM SA1100 embedded microprocessor");
+MODULE_AUTHOR("Christoph Bartelmus");
+#elif defined(LIRC_SIR_ACTISYS_ACT200L)
+MODULE_DESCRIPTION("LIRC driver for Actisys Act200L");
+MODULE_AUTHOR("Karl Bongers");
+#elif defined(LIRC_SIR_ACTISYS_ACT220L)
+MODULE_DESCRIPTION("LIRC driver for Actisys Act220L(+)");
+MODULE_AUTHOR("Jan Roemisch");
+#else
+MODULE_DESCRIPTION("Infrared receiver driver for SIR type serial ports");
+MODULE_AUTHOR("Milan Pikula");
+#endif
+MODULE_LICENSE("GPL");
+
+#ifdef LIRC_ON_SA1100
+module_param(irq, int, 0444);
+MODULE_PARM_DESC(irq, "Interrupt (16)");
+#else
+module_param(io, int, 0444);
+MODULE_PARM_DESC(io, "I/O address base (0x3f8 or 0x2f8)");
+
+module_param(irq, int, 0444);
+MODULE_PARM_DESC(irq, "Interrupt (4 or 3)");
+
+module_param(threshold, int, 0444);
+MODULE_PARM_DESC(threshold, "space detection threshold (3)");
+#endif
+
+module_param(debug, bool, 0644);
+MODULE_PARM_DESC(debug, "Enable debugging messages");
+
+#endif /* MODULE */
-- 
1.6.0.1


^ permalink raw reply related	[flat|nested] 111+ messages in thread

* [PATCH 16/18] lirc driver for the IR interface on BT829-based hardware
  2008-09-09  4:06                             ` [PATCH 15/18] lirc driver for the SIR IrDA port Jarod Wilson
@ 2008-09-09  4:06                               ` Jarod Wilson
  2008-09-09  4:06                                 ` [PATCH 17/18] lirc driver for homebrew parallel port receivers Jarod Wilson
  0 siblings, 1 reply; 111+ messages in thread
From: Jarod Wilson @ 2008-09-09  4:06 UTC (permalink / raw)
  To: linux-kernel
  Cc: Jarod Wilson, Jarod Wilson, Janne Grunau, Christoph Bartelmus

Signed-off-by: Jarod Wilson <jarod@redhat.com>
Signed-off-by: Janne Grunau <j@jannau.net>
CC: Christoph Bartelmus <lirc@bartelmus.de>
---
 drivers/input/lirc/Kconfig      |    7 +
 drivers/input/lirc/Makefile     |    1 +
 drivers/input/lirc/lirc_bt829.c |  388 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 396 insertions(+), 0 deletions(-)
 create mode 100644 drivers/input/lirc/lirc_bt829.c

diff --git a/drivers/input/lirc/Kconfig b/drivers/input/lirc/Kconfig
index b2bdbfb..2af6278 100644
--- a/drivers/input/lirc/Kconfig
+++ b/drivers/input/lirc/Kconfig
@@ -25,6 +25,13 @@ config LIRC_ATIUSB
 	help
 	  Driver for the ATI USB RF remote receiver
 
+config LIRC_BT829
+	tristate "BT829 based hardware"
+	default n
+	depends on LIRC_DEV
+	help
+	  Driver for the IR interface on BT829-based hardware
+
 config LIRC_CMDIR
 	tristate "CommandIR USB Transceiver"
 	default n
diff --git a/drivers/input/lirc/Makefile b/drivers/input/lirc/Makefile
index 761cb47..c5f71fa 100644
--- a/drivers/input/lirc/Makefile
+++ b/drivers/input/lirc/Makefile
@@ -7,6 +7,7 @@ EXTRA_CFLAGS =-DIRCTL_DEV_MAJOR=61 -DLIRC_SERIAL_TRANSMITTER -I$(src)
 
 obj-$(CONFIG_LIRC_DEV)		+= lirc_dev.o
 obj-$(CONFIG_LIRC_ATIUSB)	+= lirc_atiusb.o
+obj-$(CONFIG_LIRC_BT829)	+= lirc_bt829.o
 obj-$(CONFIG_LIRC_CMDIR)	+= lirc_cmdir.o
 obj-$(CONFIG_LIRC_I2C)		+= lirc_i2c.o
 obj-$(CONFIG_LIRC_IGORPLUGUSB)	+= lirc_igorplugusb.o
diff --git a/drivers/input/lirc/lirc_bt829.c b/drivers/input/lirc/lirc_bt829.c
new file mode 100644
index 0000000..01cbdfe
--- /dev/null
+++ b/drivers/input/lirc/lirc_bt829.c
@@ -0,0 +1,388 @@
+/*
+ * Remote control driver for the TV-card based on bt829
+ *
+ *  by Leonid Froenchenko <lfroen@galileo.co.il>
+ *
+ *  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>
+
+#include <linux/autoconf.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/threads.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+
+#include "lirc_dev.h"
+
+static int poll_main(void);
+static int atir_init_start(void);
+
+static void write_index(unsigned char index, unsigned int value);
+static unsigned int read_index(unsigned char index);
+
+static void do_i2c_start(void);
+static void do_i2c_stop(void);
+
+static void seems_wr_byte(unsigned char al);
+static unsigned char seems_rd_byte(void);
+
+static unsigned int read_index(unsigned char al);
+static void write_index(unsigned char ah, unsigned int edx);
+
+static void cycle_delay(int cycle);
+
+static void do_set_bits(unsigned char bl);
+static unsigned char do_get_bits(void);
+
+#define DATA_PCI_OFF 0x7FFC00
+#define WAIT_CYCLE   20
+
+static int debug;
+#define dprintk(fmt, args...)					\
+	do {							\
+		if (debug)					\
+			printk(KERN_DEBUG fmt, ## args);	\
+	} while (0)
+
+static int atir_minor;
+static unsigned long pci_addr_phys;
+static unsigned char *pci_addr_lin;
+
+static struct lirc_plugin atir_plugin;
+
+static struct pci_dev *do_pci_probe(void)
+{
+	struct pci_dev *my_dev;
+	my_dev = pci_get_device(PCI_VENDOR_ID_ATI,
+				PCI_DEVICE_ID_ATI_264VT, NULL);
+	if (my_dev) {
+		printk(KERN_ERR "ATIR: Using device: %s\n",
+		       pci_name(my_dev));
+		pci_addr_phys = 0;
+		if (my_dev->resource[0].flags & IORESOURCE_MEM) {
+			pci_addr_phys = my_dev->resource[0].start;
+			printk(KERN_INFO "ATIR memory at 0x%08X \n",
+			       (unsigned int)pci_addr_phys);
+		}
+		if (pci_addr_phys == 0) {
+			printk(KERN_ERR "ATIR no memory resource ?\n");
+			return NULL;
+		}
+	} else {
+		printk(KERN_ERR "ATIR: pci_prob failed\n");
+		return NULL;
+	}
+	return my_dev;
+}
+
+static int atir_add_to_buf(void *data, struct lirc_buffer *buf)
+{
+	unsigned char key;
+	int status;
+	status = poll_main();
+	key = (status >> 8) & 0xFF;
+	if (status & 0xFF) {
+		dprintk("ATIR reading key %02X\n", key);
+		lirc_buffer_write_1(buf, &key);
+		return 0;
+	}
+	return -ENODATA;
+}
+
+static int atir_set_use_inc(void *data)
+{
+	dprintk("ATIR driver is opened\n");
+	return 0;
+}
+
+static void atir_set_use_dec(void *data)
+{
+	dprintk("ATIR driver is closed\n");
+}
+
+int init_module(void)
+{
+	struct pci_dev *pdev;
+
+	pdev = do_pci_probe();
+	if (pdev == NULL)
+		return 1;
+
+	if (!atir_init_start())
+		return 1;
+
+	strcpy(atir_plugin.name, "ATIR");
+	atir_plugin.minor       = -1;
+	atir_plugin.code_length = 8;
+	atir_plugin.sample_rate = 10;
+	atir_plugin.data        = 0;
+	atir_plugin.add_to_buf  = atir_add_to_buf;
+	atir_plugin.set_use_inc = atir_set_use_inc;
+	atir_plugin.set_use_dec = atir_set_use_dec;
+	atir_plugin.dev         = &pdev->dev;
+	atir_plugin.owner       = THIS_MODULE;
+
+	atir_minor = lirc_register_plugin(&atir_plugin);
+	dprintk("ATIR driver is registered on minor %d\n", atir_minor);
+
+	return 0;
+}
+
+
+void cleanup_module(void)
+{
+	lirc_unregister_plugin(atir_minor);
+}
+
+
+static int atir_init_start(void)
+{
+	pci_addr_lin = ioremap(pci_addr_phys + DATA_PCI_OFF, 0x400);
+	if (pci_addr_lin == 0) {
+		printk(KERN_INFO "atir: pci mem must be mapped\n");
+		return 0;
+	}
+	return 1;
+}
+
+static void cycle_delay(int cycle)
+{
+	udelay(WAIT_CYCLE*cycle);
+}
+
+
+static int poll_main()
+{
+	unsigned char status_high, status_low;
+
+	do_i2c_start();
+
+	seems_wr_byte(0xAA);
+	seems_wr_byte(0x01);
+
+	do_i2c_start();
+
+	seems_wr_byte(0xAB);
+
+	status_low = seems_rd_byte();
+	status_high = seems_rd_byte();
+
+	do_i2c_stop();
+
+	return (status_high << 8) | status_low;
+}
+
+static void do_i2c_start(void)
+{
+	do_set_bits(3);
+	cycle_delay(4);
+
+	do_set_bits(1);
+	cycle_delay(7);
+
+	do_set_bits(0);
+	cycle_delay(2);
+}
+
+static void do_i2c_stop(void)
+{
+	unsigned char bits;
+	bits =  do_get_bits() & 0xFD;
+	do_set_bits(bits);
+	cycle_delay(1);
+
+	bits |= 1;
+	do_set_bits(bits);
+	cycle_delay(2);
+
+	bits |= 2;
+	do_set_bits(bits);
+	bits = 3;
+	do_set_bits(bits);
+	cycle_delay(2);
+}
+
+static void seems_wr_byte(unsigned char value)
+{
+	int i;
+	unsigned char reg;
+
+	reg = do_get_bits();
+	for (i = 0; i < 8; i++) {
+		if (value & 0x80)
+			reg |= 0x02;
+		else
+			reg &= 0xFD;
+
+		do_set_bits(reg);
+		cycle_delay(1);
+
+		reg |= 1;
+		do_set_bits(reg);
+		cycle_delay(1);
+
+		reg &= 0xFE;
+		do_set_bits(reg);
+		cycle_delay(1);
+		value <<= 1;
+	}
+	cycle_delay(2);
+
+	reg |= 2;
+	do_set_bits(reg);
+
+	reg |= 1;
+	do_set_bits(reg);
+
+	cycle_delay(1);
+	do_get_bits();
+
+	reg &= 0xFE;
+	do_set_bits(reg);
+	cycle_delay(3);
+}
+
+static unsigned char seems_rd_byte(void)
+{
+	int i;
+	int rd_byte;
+	unsigned char bits_2, bits_1;
+
+	bits_1 = do_get_bits() | 2;
+	do_set_bits(bits_1);
+
+	rd_byte = 0;
+	for (i = 0; i < 8; i++) {
+		bits_1 &= 0xFE;
+		do_set_bits(bits_1);
+		cycle_delay(2);
+
+		bits_1 |= 1;
+		do_set_bits(bits_1);
+		cycle_delay(1);
+
+		bits_2 = do_get_bits();
+		if (bits_2 & 2)
+			rd_byte |= 1;
+
+		rd_byte <<= 1;
+	}
+
+	bits_1 = 0;
+	if (bits_2 == 0)
+		bits_1 |= 2;
+
+	do_set_bits(bits_1);
+	cycle_delay(2);
+
+	bits_1 |= 1;
+	do_set_bits(bits_1);
+	cycle_delay(3);
+
+	bits_1 &= 0xFE;
+	do_set_bits(bits_1);
+	cycle_delay(2);
+
+	rd_byte >>= 1;
+	rd_byte &= 0xFF;
+	return rd_byte;
+}
+
+static void do_set_bits(unsigned char new_bits)
+{
+	int reg_val;
+	reg_val = read_index(0x34);
+	if (new_bits & 2) {
+		reg_val &= 0xFFFFFFDF;
+		reg_val |= 1;
+	} else {
+		reg_val &= 0xFFFFFFFE;
+		reg_val |= 0x20;
+	}
+	reg_val |= 0x10;
+	write_index(0x34, reg_val);
+
+	reg_val = read_index(0x31);
+	if (new_bits & 1)
+		reg_val |= 0x1000000;
+	else
+		reg_val &= 0xFEFFFFFF;
+
+	reg_val |= 0x8000000;
+	write_index(0x31, reg_val);
+}
+
+static unsigned char do_get_bits(void)
+{
+	unsigned char bits;
+	int reg_val;
+
+	reg_val = read_index(0x34);
+	reg_val |= 0x10;
+	reg_val &= 0xFFFFFFDF;
+	write_index(0x34, reg_val);
+
+	reg_val = read_index(0x34);
+	bits = 0;
+	if (reg_val & 8)
+		bits |= 2;
+	else
+		bits &= 0xFD;
+
+	reg_val = read_index(0x31);
+	if (reg_val & 0x1000000)
+		bits |= 1;
+	else
+		bits &= 0xFE;
+
+	return bits;
+}
+
+static unsigned int read_index(unsigned char index)
+{
+	unsigned char *addr;
+	unsigned int value;
+	/*  addr = pci_addr_lin + DATA_PCI_OFF + ((index & 0xFF) << 2); */
+	addr = pci_addr_lin + ((index & 0xFF) << 2);
+	value = readl(addr);
+	return value;
+}
+
+static void write_index(unsigned char index, unsigned int reg_val)
+{
+	unsigned char *addr;
+	addr = pci_addr_lin + ((index & 0xFF) << 2);
+	writel(reg_val, addr);
+}
+
+MODULE_AUTHOR("Froenchenko Leonid");
+MODULE_DESCRIPTION("IR remote driver for bt829 based TV cards");
+MODULE_LICENSE("GPL");
+
+module_param(debug, bool, 0644);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
-- 
1.6.0.1


^ permalink raw reply related	[flat|nested] 111+ messages in thread

* [PATCH 17/18] lirc driver for homebrew parallel port receivers
  2008-09-09  4:06                               ` [PATCH 16/18] lirc driver for the IR interface on BT829-based hardware Jarod Wilson
@ 2008-09-09  4:06                                 ` Jarod Wilson
  2008-09-09  4:06                                   ` [PATCH 18/18] lirc driver for the zilog/haupauge IR transceiver Jarod Wilson
  0 siblings, 1 reply; 111+ messages in thread
From: Jarod Wilson @ 2008-09-09  4:06 UTC (permalink / raw)
  To: linux-kernel
  Cc: Jarod Wilson, Jarod Wilson, Janne Grunau, Christoph Bartelmus

Signed-off-by: Jarod Wilson <jarod@redhat.com>
Signed-off-by: Janne Grunau <j@jannau.net>
CC: Christoph Bartelmus <lirc@bartelmus.de>
---
 drivers/input/lirc/Kconfig         |    7 +
 drivers/input/lirc/Makefile        |    1 +
 drivers/input/lirc/lirc_parallel.c |  728 ++++++++++++++++++++++++++++++++++++
 drivers/input/lirc/lirc_parallel.h |   26 ++
 4 files changed, 762 insertions(+), 0 deletions(-)
 create mode 100644 drivers/input/lirc/lirc_parallel.c
 create mode 100644 drivers/input/lirc/lirc_parallel.h

diff --git a/drivers/input/lirc/Kconfig b/drivers/input/lirc/Kconfig
index 2af6278..fb8ba38 100644
--- a/drivers/input/lirc/Kconfig
+++ b/drivers/input/lirc/Kconfig
@@ -89,6 +89,13 @@ config LIRC_MCEUSB2
 	help
 	  Driver for the Microsoft Media Center Ed. Receiver, v2
 
+config LIRC_PARALLEL
+	tristate "Homebrew Parallel Port Receiver"
+	default n
+	depends on LIRC_DEV && !SMP
+	help
+	  Driver for Homebrew Parallel Port Receivers
+
 config LIRC_SASEM
 	tristate "Sasem USB IR Remote"
 	default n
diff --git a/drivers/input/lirc/Makefile b/drivers/input/lirc/Makefile
index c5f71fa..3a1469c 100644
--- a/drivers/input/lirc/Makefile
+++ b/drivers/input/lirc/Makefile
@@ -16,6 +16,7 @@ obj-$(CONFIG_LIRC_IT87)		+= lirc_it87.o
 obj-$(CONFIG_LIRC_ITE8709)	+= lirc_ite8709.o
 obj-$(CONFIG_LIRC_MCEUSB)	+= lirc_mceusb.o
 obj-$(CONFIG_LIRC_MCEUSB2)	+= lirc_mceusb2.o
+obj-$(CONFIG_LIRC_PARALLEL)	+= lirc_parallel.o
 obj-$(CONFIG_LIRC_SASEM)	+= lirc_sasem.o
 obj-$(CONFIG_LIRC_SERIAL)	+= lirc_serial.o
 obj-$(CONFIG_LIRC_SIR)		+= lirc_sir.o
diff --git a/drivers/input/lirc/lirc_parallel.c b/drivers/input/lirc/lirc_parallel.c
new file mode 100644
index 0000000..912cad2
--- /dev/null
+++ b/drivers/input/lirc/lirc_parallel.c
@@ -0,0 +1,728 @@
+/****************************************************************************
+ ** lirc_parallel.c *********************************************************
+ ****************************************************************************
+ *
+ * lirc_parallel - device driver for infra-red signal receiving and
+ *                 transmitting unit built by the author
+ *
+ * Copyright (C) 1998 Christoph Bartelmus <lirc@bartelmus.de>
+ *
+ *  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
+ *
+ */
+
+/***********************************************************************
+ *************************       Includes        ***********************
+ ***********************************************************************/
+
+#include <linux/version.h>
+
+#include <linux/autoconf.h>
+
+#ifdef CONFIG_SMP
+#error "--- Sorry, this driver is not SMP safe. ---"
+#endif
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/autoconf.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/time.h>
+#include <linux/mm.h>
+#include <linux/delay.h>
+
+#include <linux/io.h>
+#include <linux/signal.h>
+#include <linux/irq.h>
+#include <linux/uaccess.h>
+#include <asm/div64.h>
+
+#include <linux/poll.h>
+#include <linux/parport.h>
+
+#include "lirc.h"
+#include "lirc_dev.h"
+
+#include "lirc_parallel.h"
+
+#define LIRC_DRIVER_NAME "lirc_parallel"
+
+#ifndef LIRC_IRQ
+#define LIRC_IRQ 7
+#endif
+#ifndef LIRC_PORT
+#define LIRC_PORT 0x378
+#endif
+#ifndef LIRC_TIMER
+#define LIRC_TIMER 65536
+#endif
+
+/***********************************************************************
+ *************************   Globale Variablen   ***********************
+ ***********************************************************************/
+
+static int debug;
+static int check_pselecd;
+
+unsigned int irq = LIRC_IRQ;
+unsigned int io = LIRC_PORT;
+#ifdef LIRC_TIMER
+unsigned int timer;
+unsigned int default_timer = LIRC_TIMER;
+#endif
+
+#define WBUF_SIZE (256)
+#define RBUF_SIZE (256) /* this must be a power of 2 larger than 1 */
+
+static int wbuf[WBUF_SIZE];
+static int rbuf[RBUF_SIZE];
+
+DECLARE_WAIT_QUEUE_HEAD(lirc_wait);
+
+unsigned int rptr;
+unsigned int wptr;
+unsigned int lost_irqs;
+int is_open;
+
+struct parport *pport;
+struct pardevice *ppdevice;
+int is_claimed;
+
+unsigned int tx_mask = 1;
+
+/***********************************************************************
+ *************************   Interne Funktionen  ***********************
+ ***********************************************************************/
+
+static inline unsigned int in(int offset)
+{
+	switch (offset) {
+	case LIRC_LP_BASE:
+		return parport_read_data(pport);
+	case LIRC_LP_STATUS:
+		return parport_read_status(pport);
+	case LIRC_LP_CONTROL:
+		return parport_read_control(pport);
+	}
+	return 0; /* make compiler happy */
+}
+
+static inline void out(int offset, int value)
+{
+	switch (offset) {
+	case LIRC_LP_BASE:
+		parport_write_data(pport, value);
+		break;
+	case LIRC_LP_CONTROL:
+		parport_write_control(pport, value);
+		break;
+	case LIRC_LP_STATUS:
+		printk(KERN_INFO "%s: attempt to write to status register\n",
+		       LIRC_DRIVER_NAME);
+		break;
+	}
+}
+
+static inline unsigned int lirc_get_timer(void)
+{
+	return in(LIRC_PORT_TIMER) & LIRC_PORT_TIMER_BIT;
+}
+
+static inline  unsigned int lirc_get_signal(void)
+{
+	return in(LIRC_PORT_SIGNAL) & LIRC_PORT_SIGNAL_BIT;
+}
+
+static inline void lirc_on(void)
+{
+	out(LIRC_PORT_DATA, tx_mask);
+}
+
+static inline void lirc_off(void)
+{
+	out(LIRC_PORT_DATA, 0);
+}
+
+static unsigned int init_lirc_timer(void)
+{
+	struct timeval tv, now;
+	unsigned int level, newlevel, timeelapsed, newtimer;
+	int count = 0;
+
+	do_gettimeofday(&tv);
+	tv.tv_sec++;                     /* wait max. 1 sec. */
+	level = lirc_get_timer();
+	do {
+		newlevel = lirc_get_timer();
+		if (level == 0 && newlevel != 0)
+			count++;
+		level = newlevel;
+		do_gettimeofday(&now);
+	} while (count < 1000 && (now.tv_sec < tv.tv_sec
+			     || (now.tv_sec == tv.tv_sec
+				 && now.tv_usec < tv.tv_usec)));
+
+	timeelapsed = ((now.tv_sec + 1 - tv.tv_sec)*1000000
+		     + (now.tv_usec - tv.tv_usec));
+	if (count >= 1000 && timeelapsed > 0) {
+		if (default_timer == 0) {
+			/* autodetect timer */
+			newtimer = (1000000*count)/timeelapsed;
+			printk(KERN_INFO "%s: %u Hz timer detected\n",
+			       LIRC_DRIVER_NAME, newtimer);
+			return newtimer;
+		}  else {
+			newtimer = (1000000*count)/timeelapsed;
+			if (abs(newtimer - default_timer) > default_timer/10) {
+				/* bad timer */
+				printk(KERN_NOTICE "%s: bad timer: %u Hz\n",
+				       LIRC_DRIVER_NAME, newtimer);
+				printk(KERN_NOTICE "%s: using default timer: "
+				       "%u Hz\n",
+				       LIRC_DRIVER_NAME, default_timer);
+				return default_timer;
+			} else {
+				printk(KERN_INFO "%s: %u Hz timer detected\n",
+				       LIRC_DRIVER_NAME, newtimer);
+				return newtimer; /* use detected value */
+			}
+		}
+	} else {
+		printk(KERN_NOTICE "%s: no timer detected\n", LIRC_DRIVER_NAME);
+		return 0;
+	}
+}
+
+static int lirc_claim(void)
+{
+	if (parport_claim(ppdevice) != 0) {
+		printk(KERN_WARNING "%s: could not claim port\n",
+		       LIRC_DRIVER_NAME);
+		printk(KERN_WARNING "%s: waiting for port becoming available"
+		       "\n", LIRC_DRIVER_NAME);
+		if (parport_claim_or_block(ppdevice) < 0) {
+			printk(KERN_NOTICE "%s: could not claim port, giving"
+			       " up\n", LIRC_DRIVER_NAME);
+			return 0;
+		}
+	}
+	out(LIRC_LP_CONTROL, LP_PSELECP|LP_PINITP);
+	is_claimed = 1;
+	return 1;
+}
+
+/***********************************************************************
+ *************************   interrupt handler  ************************
+ ***********************************************************************/
+
+static inline void rbuf_write(int signal)
+{
+	unsigned int nwptr;
+
+	nwptr = (wptr + 1) & (RBUF_SIZE - 1);
+	if (nwptr == rptr) {
+		/* no new signals will be accepted */
+		lost_irqs++;
+		printk(KERN_NOTICE "%s: buffer overrun\n", LIRC_DRIVER_NAME);
+		return;
+	}
+	rbuf[wptr] = signal;
+	wptr = nwptr;
+}
+
+static void irq_handler(void *blah)
+{
+	struct timeval tv;
+	static struct timeval lasttv;
+	static int init;
+	long signal;
+	int data;
+	unsigned int level, newlevel;
+	unsigned int timeout;
+
+	if (!module_refcount(THIS_MODULE))
+		return;
+
+	if (!is_claimed)
+		return;
+
+	/* disable interrupt */
+	/*
+	  disable_irq(irq);
+	  out(LIRC_PORT_IRQ, in(LIRC_PORT_IRQ) & (~LP_PINTEN));
+	*/
+	if (check_pselecd && (in(1) & LP_PSELECD))
+		return;
+
+#ifdef LIRC_TIMER
+	if (init) {
+		do_gettimeofday(&tv);
+
+		signal = tv.tv_sec - lasttv.tv_sec;
+		if (signal > 15)
+			/* really long time */
+			data = PULSE_MASK;
+		else
+			data = (int) (signal*1000000 +
+					 tv.tv_usec - lasttv.tv_usec +
+					 LIRC_SFH506_DELAY);
+
+		rbuf_write(data); /* space */
+	} else {
+		if (timer == 0) {
+			/* wake up; we'll lose this signal
+			 * but it will be garbage if the device
+			 * is turned on anyway */
+			timer = init_lirc_timer();
+			/* enable_irq(irq); */
+			return;
+		}
+		init = 1;
+	}
+
+	timeout = timer/10;	/* timeout after 1/10 sec. */
+	signal = 1;
+	level = lirc_get_timer();
+	do {
+		newlevel = lirc_get_timer();
+		if (level == 0 && newlevel != 0)
+			signal++;
+		level = newlevel;
+
+		/* giving up */
+		if (signal > timeout
+		    || (check_pselecd && (in(1) & LP_PSELECD))) {
+			signal = 0;
+			printk(KERN_NOTICE "%s: timeout\n", LIRC_DRIVER_NAME);
+			break;
+		}
+	} while (lirc_get_signal());
+
+	if (signal != 0) {
+		/* ajust value to usecs */
+		unsigned long long helper;
+
+		helper = ((unsigned long long) signal)*1000000;
+		do_div(helper, timer);
+		signal = (long) helper;
+
+		if (signal > LIRC_SFH506_DELAY)
+			data = signal - LIRC_SFH506_DELAY;
+		else
+			data = 1;
+		rbuf_write(PULSE_BIT|data); /* pulse */
+	}
+	do_gettimeofday(&lasttv);
+#else
+	/* add your code here */
+#endif
+
+	wake_up_interruptible(&lirc_wait);
+
+	/* enable interrupt */
+	/*
+	  enable_irq(irq);
+	  out(LIRC_PORT_IRQ, in(LIRC_PORT_IRQ)|LP_PINTEN);
+	*/
+}
+
+/***********************************************************************
+ **************************   file_operations   ************************
+ ***********************************************************************/
+
+static loff_t lirc_lseek(struct file *filep, loff_t offset, int orig)
+{
+	return -ESPIPE;
+}
+
+static ssize_t lirc_read(struct file *filep, char *buf, size_t n, loff_t *ppos)
+{
+	int result = 0;
+	int count = 0;
+	DECLARE_WAITQUEUE(wait, current);
+
+	if (n % sizeof(int))
+		return -EINVAL;
+
+	add_wait_queue(&lirc_wait, &wait);
+	set_current_state(TASK_INTERRUPTIBLE);
+	while (count < n) {
+		if (rptr != wptr) {
+			if (copy_to_user(buf+count, (char *) &rbuf[rptr],
+					 sizeof(int))) {
+				result = -EFAULT;
+				break;
+			}
+			rptr = (rptr + 1) & (RBUF_SIZE - 1);
+			count += sizeof(int);
+		} else {
+			if (filep->f_flags & O_NONBLOCK) {
+				result = -EAGAIN;
+				break;
+			}
+			if (signal_pending(current)) {
+				result = -ERESTARTSYS;
+				break;
+			}
+			schedule();
+			set_current_state(TASK_INTERRUPTIBLE);
+		}
+	}
+	remove_wait_queue(&lirc_wait, &wait);
+	set_current_state(TASK_RUNNING);
+	return count ? count : result;
+}
+
+static ssize_t lirc_write(struct file *filep, const char *buf, size_t n,
+			  loff_t *ppos)
+{
+	int count;
+	unsigned int i;
+	unsigned int level, newlevel;
+	unsigned long flags;
+	int counttimer;
+
+	if (!is_claimed)
+		return -EBUSY;
+
+	if (n % sizeof(int))
+		return -EINVAL;
+
+	count = n / sizeof(int);
+
+	if (count > WBUF_SIZE || count % 2 == 0)
+		return -EINVAL;
+
+	if (copy_from_user(wbuf, buf, n))
+		return -EFAULT;
+
+#ifdef LIRC_TIMER
+	if (timer == 0) {
+		/* try again if device is ready */
+		timer = init_lirc_timer();
+		if (timer == 0)
+			return -EIO;
+	}
+
+	/* ajust values from usecs */
+	for (i = 0; i < count; i++) {
+		unsigned long long helper;
+
+		helper = ((unsigned long long) wbuf[i])*timer;
+		do_div(helper, 1000000);
+		wbuf[i] = (int) helper;
+	}
+
+	local_irq_save(flags);
+	i = 0;
+	while (i < count) {
+		level = lirc_get_timer();
+		counttimer = 0;
+		lirc_on();
+		do {
+			newlevel = lirc_get_timer();
+			if (level == 0 && newlevel != 0)
+				counttimer++;
+			level = newlevel;
+			if (check_pselecd && (in(1) & LP_PSELECD)) {
+				lirc_off();
+				local_irq_restore(flags);
+				return -EIO;
+			}
+		} while (counttimer < wbuf[i]);
+		i++;
+
+		lirc_off();
+		if (i == count)
+			break;
+		counttimer = 0;
+		do {
+			newlevel = lirc_get_timer();
+			if (level == 0 && newlevel != 0)
+				counttimer++;
+			level = newlevel;
+			if (check_pselecd && (in(1) & LP_PSELECD)) {
+				local_irq_restore(flags);
+				return -EIO;
+			}
+		} while (counttimer < wbuf[i]);
+		i++;
+	}
+	local_irq_restore(flags);
+#else
+	/* place code that handles write
+	 * without external timer here */
+#endif
+	return n;
+}
+
+static unsigned int lirc_poll(struct file *file, poll_table *wait)
+{
+	poll_wait(file, &lirc_wait, wait);
+	if (rptr != wptr)
+		return POLLIN | POLLRDNORM;
+	return 0;
+}
+
+static int lirc_ioctl(struct inode *node, struct file *filep, unsigned int cmd,
+		      unsigned long arg)
+{
+	int result;
+	unsigned long features = LIRC_CAN_SET_TRANSMITTER_MASK |
+				 LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2;
+	unsigned long mode;
+	unsigned int ivalue;
+
+	switch (cmd) {
+	case LIRC_GET_FEATURES:
+		result = put_user(features, (unsigned long *) arg);
+		if (result)
+			return result;
+		break;
+	case LIRC_GET_SEND_MODE:
+		result = put_user(LIRC_MODE_PULSE, (unsigned long *) arg);
+		if (result)
+			return result;
+		break;
+	case LIRC_GET_REC_MODE:
+		result = put_user(LIRC_MODE_MODE2, (unsigned long *) arg);
+		if (result)
+			return result;
+		break;
+	case LIRC_SET_SEND_MODE:
+		result = get_user(mode, (unsigned long *) arg);
+		if (result)
+			return result;
+		if (mode != LIRC_MODE_PULSE)
+			return -EINVAL;
+		break;
+	case LIRC_SET_REC_MODE:
+		result = get_user(mode, (unsigned long *) arg);
+		if (result)
+			return result;
+		if (mode != LIRC_MODE_MODE2)
+			return -ENOSYS;
+		break;
+	case LIRC_SET_TRANSMITTER_MASK:
+		result = get_user(ivalue, (unsigned int *) arg);
+		if (result)
+			return result;
+		if ((ivalue & LIRC_PARALLEL_TRANSMITTER_MASK) != ivalue)
+			return LIRC_PARALLEL_MAX_TRANSMITTERS;
+		tx_mask = ivalue;
+		break;
+	default:
+		return -ENOIOCTLCMD;
+	}
+	return 0;
+}
+
+static int lirc_open(struct inode *node, struct file *filep)
+{
+	if (module_refcount(THIS_MODULE) || !lirc_claim())
+		return -EBUSY;
+
+	parport_enable_irq(pport);
+
+	/* init read ptr */
+	rptr = 0;
+	wptr = 0;
+	lost_irqs = 0;
+
+	is_open = 1;
+	return 0;
+}
+
+static int lirc_close(struct inode *node, struct file *filep)
+{
+	if (is_claimed) {
+		is_claimed = 0;
+		parport_release(ppdevice);
+	}
+	is_open = 0;
+	return 0;
+}
+
+static struct file_operations lirc_fops = {
+	.llseek		= lirc_lseek,
+	.read		= lirc_read,
+	.write		= lirc_write,
+	.poll		= lirc_poll,
+	.ioctl		= lirc_ioctl,
+	.open		= lirc_open,
+	.release	= lirc_close
+};
+
+static int set_use_inc(void *data)
+{
+	return 0;
+}
+
+static void set_use_dec(void *data)
+{
+}
+
+static struct lirc_plugin plugin = {
+       .name		= LIRC_DRIVER_NAME,
+       .minor		= -1,
+       .code_length	= 1,
+       .sample_rate	= 0,
+       .data		= NULL,
+       .add_to_buf	= NULL,
+       .get_queue	= NULL,
+       .set_use_inc	= set_use_inc,
+       .set_use_dec	= set_use_dec,
+       .fops		= &lirc_fops,
+       .dev		= NULL,
+       .owner		= THIS_MODULE,
+};
+
+#ifdef MODULE
+
+static int pf(void *handle);
+static void kf(void *handle);
+
+static struct timer_list poll_timer;
+static void poll_state(unsigned long ignored);
+
+static void poll_state(unsigned long ignored)
+{
+	printk(KERN_NOTICE "%s: time\n",
+	       LIRC_DRIVER_NAME);
+	del_timer(&poll_timer);
+	if (is_claimed)
+		return;
+	kf(NULL);
+	if (!is_claimed) {
+		printk(KERN_NOTICE "%s: could not claim port, giving up\n",
+		       LIRC_DRIVER_NAME);
+		init_timer(&poll_timer);
+		poll_timer.expires = jiffies + HZ;
+		poll_timer.data = (unsigned long)current;
+		poll_timer.function = poll_state;
+		add_timer(&poll_timer);
+	}
+}
+
+static int pf(void *handle)
+{
+	parport_disable_irq(pport);
+	is_claimed = 0;
+	return 0;
+}
+
+static void kf(void *handle)
+{
+	if (!is_open)
+		return;
+	if (!lirc_claim())
+		return;
+	parport_enable_irq(pport);
+	lirc_off();
+	/* this is a bit annoying when you actually print...*/
+	/*
+	printk(KERN_INFO "%s: reclaimed port\n", LIRC_DRIVER_NAME);
+	*/
+}
+
+/***********************************************************************
+ ******************   init_module()/cleanup_module()  ******************
+ ***********************************************************************/
+
+int init_module(void)
+{
+	pport = parport_find_base(io);
+	if (pport == NULL) {
+		printk(KERN_NOTICE "%s: no port at %x found\n",
+		       LIRC_DRIVER_NAME, io);
+		return -ENXIO;
+	}
+	ppdevice = parport_register_device(pport, LIRC_DRIVER_NAME,
+					   pf, kf, irq_handler, 0, NULL);
+	parport_put_port(pport);
+	if (ppdevice == NULL) {
+		printk(KERN_NOTICE "%s: parport_register_device() failed\n",
+		       LIRC_DRIVER_NAME);
+		return -ENXIO;
+	}
+	if (parport_claim(ppdevice) != 0)
+		goto skip_init;
+	is_claimed = 1;
+	out(LIRC_LP_CONTROL, LP_PSELECP|LP_PINITP);
+
+#ifdef LIRC_TIMER
+	if (debug)
+		out(LIRC_PORT_DATA, tx_mask);
+
+	timer = init_lirc_timer();
+
+#if 0	/* continue even if device is offline */
+	if (timer == 0) {
+		is_claimed = 0;
+		parport_release(pport);
+		parport_unregister_device(ppdevice);
+		return -EIO;
+	}
+
+#endif
+	if (debug)
+		out(LIRC_PORT_DATA, 0);
+#endif
+
+	is_claimed = 0;
+	parport_release(ppdevice);
+ skip_init:
+	plugin.minor = lirc_register_plugin(&plugin);
+	if (plugin.minor < 0) {
+		printk(KERN_NOTICE "%s: register_chrdev() failed\n",
+		       LIRC_DRIVER_NAME);
+		parport_unregister_device(ppdevice);
+		return -EIO;
+	}
+	printk(KERN_INFO "%s: installed using port 0x%04x irq %d\n",
+	       LIRC_DRIVER_NAME, io, irq);
+	return 0;
+}
+
+void cleanup_module(void)
+{
+	parport_unregister_device(ppdevice);
+	lirc_unregister_plugin(plugin.minor);
+}
+
+MODULE_DESCRIPTION("Infrared receiver driver for parallel ports.");
+MODULE_AUTHOR("Christoph Bartelmus");
+MODULE_LICENSE("GPL");
+
+module_param(io, int, 0444);
+MODULE_PARM_DESC(io, "I/O address base (0x3bc, 0x378 or 0x278)");
+
+module_param(irq, int, 0444);
+MODULE_PARM_DESC(irq, "Interrupt (7 or 5)");
+
+module_param(tx_mask, int, 0444);
+MODULE_PARM_DESC(tx_maxk, "Transmitter mask (default: 0x01)");
+
+module_param(debug, bool, 0644);
+MODULE_PARM_DESC(debug, "Enable debugging messages");
+
+module_param(check_pselecd, bool, 0644);
+MODULE_PARM_DESC(debug, "Check for printer (default: 0)");
+
+#endif /* MODULE */
diff --git a/drivers/input/lirc/lirc_parallel.h b/drivers/input/lirc/lirc_parallel.h
new file mode 100644
index 0000000..4bed6af
--- /dev/null
+++ b/drivers/input/lirc/lirc_parallel.h
@@ -0,0 +1,26 @@
+/* lirc_parallel.h */
+
+#ifndef _LIRC_PARALLEL_H
+#define _LIRC_PARALLEL_H
+
+#include <linux/lp.h>
+
+#define LIRC_PORT_LEN 3
+
+#define LIRC_LP_BASE    0
+#define LIRC_LP_STATUS  1
+#define LIRC_LP_CONTROL 2
+
+#define LIRC_PORT_DATA           LIRC_LP_BASE    /* base */
+#define LIRC_PORT_TIMER        LIRC_LP_STATUS    /* status port */
+#define LIRC_PORT_TIMER_BIT          LP_PBUSY    /* busy signal */
+#define LIRC_PORT_SIGNAL       LIRC_LP_STATUS    /* status port */
+#define LIRC_PORT_SIGNAL_BIT          LP_PACK    /* ack signal */
+#define LIRC_PORT_IRQ         LIRC_LP_CONTROL    /* control port */
+
+#define LIRC_SFH506_DELAY 0             /* delay t_phl in usecs */
+
+#define LIRC_PARALLEL_MAX_TRANSMITTERS 8
+#define LIRC_PARALLEL_TRANSMITTER_MASK ((1<<LIRC_PARALLEL_MAX_TRANSMITTERS) - 1)
+
+#endif
-- 
1.6.0.1


^ permalink raw reply related	[flat|nested] 111+ messages in thread

* [PATCH 18/18] lirc driver for the zilog/haupauge IR transceiver
  2008-09-09  4:06                                 ` [PATCH 17/18] lirc driver for homebrew parallel port receivers Jarod Wilson
@ 2008-09-09  4:06                                   ` Jarod Wilson
  2008-09-09  4:06                                     ` Jarod Wilson
  0 siblings, 1 reply; 111+ messages in thread
From: Jarod Wilson @ 2008-09-09  4:06 UTC (permalink / raw)
  To: linux-kernel
  Cc: Jarod Wilson, Jarod Wilson, Janne Grunau, Christoph Bartelmus,
	Mike Krufky, Steven Toth, Mark Weaver

Hopefully, I get this summary more or less correct...

A number of Hauppauge devices contain both an IR receiver and an IR
transmitter. The RX side is rather similar to those handled by the
lirc_i2c driver, but the TX side is quite different. Devices with both
have to be handled a bit carefully...

The TX side is serviced by a zilog processor, running some zilog firmware,
but is interfaced with via a software db/table apparently written by a
Hauppauge sub-contractor. This is provided via a binary blob, loaded via
a request_firmware() call, and the binary blob was generated via i2c
sniffing, so general consensus between myself, Janne, Steve and Mike was
that there shouldn't be any reason it can't be redistributed, but Mark
(the original author) and Christoph had concerns about redistribution, so
this originally lived outside of lirc cvs as lirc_pvr150.c. Either way,
the "firmware" lives out of tree at the moment anyhow...

See http://www.blushingpenguin.com/mark/blog/?p=24 for more details.

Signed-off-by: Jarod Wilson <jarod@redhat.com>
Signed-off-by: Janne Grunau <j@jannau.net>
CC: Christoph Bartelmus <lirc@bartelmus.de>
CC: Mike Krufky <mkrufky@linuxtv.org>
CC: Steven Toth <stoth@hauppauge.com>
CC: Mark Weaver <mark@npsl.co.uk>
---
 drivers/input/lirc/Kconfig      |    9 +
 drivers/input/lirc/Makefile     |    1 +
 drivers/input/lirc/lirc_zilog.c | 1395 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 1405 insertions(+), 0 deletions(-)
 create mode 100644 drivers/input/lirc/lirc_zilog.c

diff --git a/drivers/input/lirc/Kconfig b/drivers/input/lirc/Kconfig
index fb8ba38..47183ee 100644
--- a/drivers/input/lirc/Kconfig
+++ b/drivers/input/lirc/Kconfig
@@ -130,4 +130,13 @@ config LIRC_TTUSBIR
 	depends on LIRC_DEV
 	help
 	  Driver for the Technotrend USB IR Receiver
+
+config LIRC_ZILOG
+	tristate "Zilog/Hauppauge IR Transmitter"
+	default n
+	depends on LIRC_DEV
+	help
+	  Driver for the Zilog/Hauppauge IR Transmitter, found on
+	  PVR-150/500, HVR-1200/1250/1700/1800, HD-PVR and other cards
+
 endif
diff --git a/drivers/input/lirc/Makefile b/drivers/input/lirc/Makefile
index 3a1469c..411db5e 100644
--- a/drivers/input/lirc/Makefile
+++ b/drivers/input/lirc/Makefile
@@ -22,3 +22,4 @@ obj-$(CONFIG_LIRC_SERIAL)	+= lirc_serial.o
 obj-$(CONFIG_LIRC_SIR)		+= lirc_sir.o
 obj-$(CONFIG_LIRC_STREAMZAP)	+= lirc_streamzap.o
 obj-$(CONFIG_LIRC_TTUSBIR)	+= lirc_ttusbir.o
+obj-$(CONFIG_LIRC_ZILOG)	+= lirc_zilog.o
diff --git a/drivers/input/lirc/lirc_zilog.c b/drivers/input/lirc/lirc_zilog.c
new file mode 100644
index 0000000..20e6b27
--- /dev/null
+++ b/drivers/input/lirc/lirc_zilog.c
@@ -0,0 +1,1395 @@
+/*
+ * i2c IR lirc plugin for devices with zilog IR processors
+ *
+ * Copyright (c) 2000 Gerd Knorr <kraxel@goldbach.in-berlin.de>
+ * modified for PixelView (BT878P+W/FM) by
+ *      Michal Kochanowicz <mkochano@pld.org.pl>
+ *      Christoph Bartelmus <lirc@bartelmus.de>
+ * modified for KNC ONE TV Station/Anubis Typhoon TView Tuner by
+ *      Ulrich Mueller <ulrich.mueller42@web.de>
+ * modified for Asus TV-Box and Creative/VisionTek BreakOut-Box by
+ *      Stefan Jahn <stefan@lkcc.org>
+ * modified for inclusion into kernel sources by
+ *      Jerome Brock <jbrock@users.sourceforge.net>
+ * modified for Leadtek Winfast PVR2000 by
+ *      Thomas Reitmayr (treitmayr@yahoo.com)
+ * modified for Hauppauge PVR-150 IR TX device by
+ *      Mark Weaver <mark@npsl.co.uk>
+ * changed name from lirc_pvr150 to lirc_zilog, works on more than pvr-150
+ *	Jarod Wilson <jarod@redhat.com>
+ *
+ * parts are cut&pasted from the lirc_i2c.c driver
+ *
+ *  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>
+#include <linux/module.h>
+#include <linux/kmod.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/poll.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/delay.h>
+#include <linux/completion.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include <linux/firmware.h>
+#include <linux/vmalloc.h>
+
+#include <linux/mutex.h>
+#include <linux/kthread.h>
+
+#include "lirc_dev.h"
+#include "lirc.h"
+
+struct IR {
+	struct lirc_plugin l;
+
+	/* Device info */
+	struct mutex       lock;
+	int		   open;
+
+	/* RX device */
+	struct i2c_client  c_rx;
+
+	/* RX device buffer & lock */
+	struct lirc_buffer buf;
+	struct mutex       buf_lock;
+
+	/* RX polling thread data */
+	struct completion  *t_notify;
+	struct completion  *t_notify2;
+	int		   shutdown;
+	struct task_struct *task;
+
+	/* RX read data */
+	unsigned char      b[3];
+
+	/* TX device */
+	struct i2c_client  c_tx;
+	int		   need_boot;
+
+	/* # devices, for shutdown */
+	int		   devs;
+};
+
+/* Minor -> data mapping */
+static struct IR *ir_devices[MAX_IRCTL_DEVICES];
+
+/* Block size for IR transmitter */
+#define TX_BLOCK_SIZE	99
+
+/* Hauppuage IR transmitter data */
+struct tx_data_struct {
+	/* Boot block */
+	unsigned char *boot_data;
+
+	/* Start of binary data block */
+	unsigned char *datap;
+
+	/* End of binary data block */
+	unsigned char *endp;
+
+	/* Number of installed codesets */
+	unsigned int num_code_sets;
+
+	/* Pointers to codesets */
+	unsigned char **code_sets;
+
+	/* Global fixed data template */
+	int fixed[TX_BLOCK_SIZE];
+};
+
+static struct tx_data_struct *tx_data;
+struct mutex tx_data_lock;
+
+/* ----------------------------------------------------------------------- */
+
+#define DEVICE_NAME "lirc_zilog"
+#define zilog_notify(s, args...) printk(KERN_NOTICE KBUILD_MODNAME ": " s, \
+					## args)
+#define zilog_error(s, args...) printk(KERN_ERR KBUILD_MODNAME ": " s, ## args)
+
+/* ----------------------------------------------------------------------- */
+/* insmod parameters						       */
+
+static int debug;	/* debug output */
+static int disable_rx;	/* disable RX device */
+static int disable_tx;	/* disable TX device */
+static int minor = -1;	/* minor number */
+
+#define dprintk(fmt, args...)						\
+	do {								\
+		if (debug)						\
+			printk(KERN_DEBUG DEVICE_NAME ": " fmt,		\
+				 ## args);				\
+	} while (0)
+
+/* ----------------------------------------------------------------------- */
+
+static int add_to_buf(struct IR *ir)
+{
+	__u16 code;
+	unsigned char codes[2];
+	unsigned char keybuf[6];
+	int got_data = 0;
+	int ret;
+	int failures = 0;
+	unsigned char sendbuf[1] = { 0 };
+
+	if (lirc_buffer_full(&ir->buf)) {
+		dprintk("buffer overflow\n");
+		return -EOVERFLOW;
+	}
+
+	/* service the device as long as it is returning
+	 * data and we have space
+	 */
+	do {
+		/* Lock i2c bus for the duration.  RX/TX chips interfere so
+		   this is worth it
+		 */
+		mutex_lock(&ir->lock);
+
+		/* Send random "poll command" (?)  Windows driver does this
+		   and it  is a good point to detect chip failure.
+		 */
+		ret = i2c_master_send(&ir->c_rx, sendbuf, 1);
+		if (ret != 1) {
+			zilog_error("i2c_master_send failed with %d\n",	ret);
+			if (failures >= 3) {
+				mutex_unlock(&ir->lock);
+				zilog_error("unable to read from the IR chip "
+					    "after 3 resets, giving up\n");
+				return ret;
+			}
+
+			/* Looks like the chip crashed, reset it */
+			zilog_error("polling the IR receiver chip failed, "
+				    "trying reset\n");
+
+			set_current_state(TASK_UNINTERRUPTIBLE);
+			schedule_timeout((100 * HZ + 999) / 1000);
+			ir->need_boot = 1;
+
+			++failures;
+			mutex_unlock(&ir->lock);
+			continue;
+		}
+
+		ret = i2c_master_recv(&ir->c_rx, keybuf, sizeof(keybuf));
+		mutex_unlock(&ir->lock);
+		if (ret != sizeof(keybuf)) {
+			zilog_error("i2c_master_recv failed with %d -- "
+				    "keeping last read buffer\n", ret);
+		} else {
+			ir->b[0] = keybuf[3];
+			ir->b[1] = keybuf[4];
+			ir->b[2] = keybuf[5];
+			dprintk("key (0x%02x/0x%02x)\n", ir->b[0], ir->b[1]);
+		}
+
+		/* key pressed ? */
+		if ((ir->b[0] & 0x80) == 0)
+			return got_data ? 0 : -ENODATA;
+
+		/* look what we have */
+		code = (((__u16)ir->b[0]&0x7f)<<6) | (ir->b[1]>>2);
+
+		codes[0] = (code >> 8) & 0xff;
+		codes[1] = code & 0xff;
+
+		/* return it */
+		lirc_buffer_write_1(&ir->buf, codes);
+		++got_data;
+	} while (!lirc_buffer_full(&ir->buf));
+	return 0;
+}
+
+/* Main function of the polling thread -- from lirc_dev.
+ * We don't fit the LIRC model at all anymore.  This is horrible, but
+ * basically we have a single RX/TX device with a nasty failure mode
+ * that needs to be accounted for across the pair.  lirc lets us provide
+ * fops, but prevents us from using the internal polling, etc. if we do
+ * so.  Hence the replication.  Might be neater to extend the LIRC model
+ * to account for this but I'd think it's a very special case of seriously
+ * messed up hardware.
+ */
+static int lirc_thread(void *arg)
+{
+	struct IR *ir = arg;
+
+	if (ir->t_notify != NULL)
+		complete(ir->t_notify);
+
+	dprintk("poll thread started\n");
+
+	do {
+		if (ir->open) {
+			set_current_state(TASK_INTERRUPTIBLE);
+
+			/* This is ~113*2 + 24 + jitter (2*repeat gap +
+			   code length).  We use this interval as the chip
+			   resets every time you poll it (bad!).  This is
+			   therefore just sufficient to catch all of the
+			   button presses.  It makes the remote much more
+			   responsive.  You can see the difference by
+			   running irw and holding down a button.  With
+			   100ms, the old polling interval, you'll notice
+			   breaks in the repeat sequence corresponding to
+			   lost keypresses.
+			*/
+			schedule_timeout((260 * HZ) / 1000);
+			if (ir->shutdown)
+				break;
+			if (!add_to_buf(ir))
+				wake_up_interruptible(&ir->buf.wait_poll);
+		} else {
+			/* if device not opened so we can sleep half a second */
+			set_current_state(TASK_INTERRUPTIBLE);
+			schedule_timeout(HZ/2);
+		}
+	} while (!ir->shutdown);
+
+	if (ir->t_notify2 != NULL)
+		wait_for_completion(ir->t_notify2);
+
+	ir->task = NULL;
+	if (ir->t_notify != NULL)
+		complete(ir->t_notify);
+
+	dprintk("poll thread ended\n");
+	return 0;
+}
+
+static int set_use_inc(void *data)
+{
+	struct IR *ir = data;
+
+	if (ir->l.owner == NULL || try_module_get(ir->l.owner) == 0)
+		return -ENODEV;
+
+	/* lock bttv in memory while /dev/lirc is in use  */
+	/* this is completely broken code. lirc_unregister_plugin()
+	   must be possible even when the device is open */
+	if (ir->c_rx.addr)
+		i2c_use_client(&ir->c_rx);
+	if (ir->c_tx.addr)
+		i2c_use_client(&ir->c_tx);
+
+	return 0;
+}
+
+static void set_use_dec(void *data)
+{
+	struct IR *ir = data;
+
+	if (ir->c_rx.addr)
+		i2c_release_client(&ir->c_rx);
+	if (ir->c_tx.addr)
+		i2c_release_client(&ir->c_tx);
+	if (ir->l.owner != NULL)
+		module_put(ir->l.owner);
+}
+
+/* safe read of a uint32 (always network byte order) */
+static inline int read_uint32(unsigned char **data,
+				     unsigned char *endp, unsigned int *val)
+{
+	if (*data + 4 > endp)
+		return 0;
+	*val = ((*data)[0] << 24) | ((*data)[1] << 16) |
+	       ((*data)[2] << 8) | (*data)[3];
+	*data += 4;
+	return 1;
+}
+
+/* safe read of a uint8 */
+static inline int read_uint8(unsigned char **data,
+				    unsigned char *endp, unsigned char *val)
+{
+	if (*data + 1 > endp)
+		return 0;
+	*val = *((*data)++);
+	return 1;
+}
+
+/* safe skipping of N bytes */
+static inline int skip(unsigned char **data,
+			      unsigned char *endp, unsigned int distance)
+{
+	if (*data + distance > endp)
+		return 0;
+	*data += distance;
+	return 1;
+}
+
+/* decompress key data into the given buffer */
+static int get_key_data(unsigned char *buf,
+			     unsigned int codeset, unsigned int key)
+{
+	unsigned char *data, *endp, *diffs, *key_block;
+	unsigned char keys, ndiffs, id;
+	unsigned int base, lim, pos, i;
+
+	/* Binary search for the codeset */
+	for (base = 0, lim = tx_data->num_code_sets; lim; lim >>= 1) {
+		pos = base + (lim >> 1);
+		data = tx_data->code_sets[pos];
+
+		if (!read_uint32(&data, tx_data->endp, &i))
+			goto corrupt;
+
+		if (i == codeset)
+			break;
+		else if (codeset > i) {
+			base = pos + 1;
+			--lim;
+		}
+	}
+	/* Not found? */
+	if (!lim)
+		return -EPROTO;
+
+	/* Set end of data block */
+	endp = pos < tx_data->num_code_sets - 1 ?
+		tx_data->code_sets[pos + 1] : tx_data->endp;
+
+	/* Read the block header */
+	if (!read_uint8(&data, endp, &keys) ||
+	    !read_uint8(&data, endp, &ndiffs) ||
+	    ndiffs > TX_BLOCK_SIZE || keys == 0)
+		goto corrupt;
+
+	/* Save diffs & skip */
+	diffs = data;
+	if (!skip(&data, endp, ndiffs))
+		goto corrupt;
+
+	/* Read the id of the first key */
+	if (!read_uint8(&data, endp, &id))
+		goto corrupt;
+
+	/* Unpack the first key's data */
+	for (i = 0; i < TX_BLOCK_SIZE; ++i) {
+		if (tx_data->fixed[i] == -1) {
+			if (!read_uint8(&data, endp, &buf[i]))
+				goto corrupt;
+		} else {
+			buf[i] = (unsigned char)tx_data->fixed[i];
+		}
+	}
+
+	/* Early out key found/not found */
+	if (key == id)
+		return 0;
+	if (keys == 1)
+		return -EPROTO;
+
+	/* Sanity check */
+	key_block = data;
+	if (!skip(&data, endp, (keys - 1) * (ndiffs + 1)))
+		goto corrupt;
+
+	/* Binary search for the key */
+	for (base = 0, lim = keys - 1; lim; lim >>= 1) {
+		/* Seek to block */
+		unsigned char *key_data;
+		pos = base + (lim >> 1);
+		key_data = key_block + (ndiffs + 1) * pos;
+
+		if (*key_data == key) {
+			/* skip key id */
+			++key_data;
+
+			/* found, so unpack the diffs */
+			for (i = 0; i < ndiffs; ++i) {
+				unsigned char val;
+				if (!read_uint8(&key_data, endp, &val) ||
+				    diffs[i] >= TX_BLOCK_SIZE)
+					goto corrupt;
+				buf[diffs[i]] = val;
+			}
+
+			return 0;
+		} else if (key > *key_data) {
+			base = pos + 1;
+			--lim;
+		}
+	}
+	/* Key not found */
+	return -EPROTO;
+
+corrupt:
+	zilog_error("firmware is corrupt\n");
+	return -EFAULT;
+}
+
+/* send a block of data to the IR TX device */
+static int send_data_block(struct IR *ir, unsigned char *data_block)
+{
+	int i, j, ret;
+	unsigned char buf[5];
+
+	for (i = 0; i < TX_BLOCK_SIZE;) {
+		int tosend = TX_BLOCK_SIZE - i;
+		if (tosend > 4)
+			tosend = 4;
+		buf[0] = (unsigned char)(i + 1);
+		for (j = 0; j < tosend; ++j)
+			buf[1 + j] = data_block[i + j];
+		dprintk("%02x %02x %02x %02x %02x",
+			buf[0], buf[1], buf[2], buf[3], buf[4]);
+		ret = i2c_master_send(&ir->c_tx, buf, tosend + 1);
+		if (ret != tosend + 1) {
+			zilog_error("i2c_master_send failed with %d\n", ret);
+			return ret < 0 ? ret : -EFAULT;
+		}
+		i += tosend;
+	}
+	return 0;
+}
+
+/* send boot data to the IR TX device */
+static int send_boot_data(struct IR *ir)
+{
+	int ret;
+	unsigned char buf[4];
+
+	/* send the boot block */
+	ret = send_data_block(ir, tx_data->boot_data);
+	if (ret != 0)
+		return ret;
+
+	/* kick it off? */
+	buf[0] = 0x00;
+	buf[1] = 0x20;
+	ret = i2c_master_send(&ir->c_tx, buf, 2);
+	if (ret != 2) {
+		zilog_error("i2c_master_send failed with %d\n", ret);
+		return ret < 0 ? ret : -EFAULT;
+	}
+	ret = i2c_master_send(&ir->c_tx, buf, 1);
+	if (ret != 1) {
+		zilog_error("i2c_master_send failed with %d\n", ret);
+		return ret < 0 ? ret : -EFAULT;
+	}
+
+	/* Here comes the firmware version... (hopefully) */
+	ret = i2c_master_recv(&ir->c_tx, buf, 4);
+	if (ret != 4) {
+		zilog_error("i2c_master_recv failed with %d\n", ret);
+		return 0;
+	}
+	if (buf[0] != 0x80) {
+		zilog_error("unexpected IR TX response: %02x\n", buf[0]);
+		return 0;
+	}
+	zilog_notify("Zilog/Hauppauge IR blaster: firmware version "
+		     "%d.%d.%d\n", buf[1], buf[2], buf[3]);
+
+	return 0;
+}
+
+/* unload "firmware", lock held */
+static void fw_unload_locked(void)
+{
+	if (tx_data) {
+		if (tx_data->code_sets)
+			vfree(tx_data->code_sets);
+
+		if (tx_data->datap)
+			vfree(tx_data->datap);
+
+		vfree(tx_data);
+		tx_data = NULL;
+		dprintk("successfully unloaded IR blaster firmware\n");
+	}
+}
+
+/* unload "firmware" for the IR TX device */
+static void fw_unload(void)
+{
+	mutex_lock(&tx_data_lock);
+	fw_unload_locked();
+	mutex_unlock(&tx_data_lock);
+}
+
+/* load "firmware" for the IR TX device */
+static int fw_load(struct IR *ir)
+{
+	int ret;
+	unsigned int i;
+	unsigned char *data, version, num_global_fixed;
+	const struct firmware *fw_entry = NULL;
+
+	/* Already loaded? */
+	mutex_lock(&tx_data_lock);
+	if (tx_data) {
+		ret = 0;
+		goto out;
+	}
+
+	/* Request codeset data file */
+	ret = request_firmware(&fw_entry, "haup-ir-blaster.bin", &ir->c_tx.dev);
+	if (ret != 0) {
+		zilog_error("firmware haup-ir-blaster.bin not available "
+			    "(%d)\n", ret);
+		ret = ret < 0 ? ret : -EFAULT;
+		goto out;
+	}
+	zilog_notify("firmware of size %zu loaded\n", fw_entry->size);
+
+	/* Parse the file */
+	tx_data = vmalloc(sizeof(*tx_data));
+	if (tx_data == NULL) {
+		zilog_error("out of memory\n");
+		release_firmware(fw_entry);
+		ret = -ENOMEM;
+		goto out;
+	}
+	tx_data->code_sets = NULL;
+
+	/* Copy the data so hotplug doesn't get confused and timeout */
+	tx_data->datap = vmalloc(fw_entry->size);
+	if (tx_data->datap == NULL) {
+		zilog_error("out of memory\n");
+		release_firmware(fw_entry);
+		vfree(tx_data);
+		ret = -ENOMEM;
+		goto out;
+	}
+	memcpy(tx_data->datap, fw_entry->data, fw_entry->size);
+	tx_data->endp = tx_data->datap + fw_entry->size;
+	release_firmware(fw_entry); fw_entry = NULL;
+
+	/* Check version */
+	data = tx_data->datap;
+	if (!read_uint8(&data, tx_data->endp, &version))
+		goto corrupt;
+	if (version != 1) {
+		zilog_error("unsupported code set file version (%u, expected"
+			    "1) -- please upgrade to a newer driver",
+			    version);
+		fw_unload_locked();
+		ret = -EFAULT;
+		goto out;
+	}
+
+	/* Save boot block for later */
+	tx_data->boot_data = data;
+	if (!skip(&data, tx_data->endp, TX_BLOCK_SIZE))
+		goto corrupt;
+
+	if (!read_uint32(&data, tx_data->endp,
+			      &tx_data->num_code_sets))
+		goto corrupt;
+
+	zilog_notify("%u codesets loaded\n", tx_data->num_code_sets);
+
+	tx_data->code_sets = vmalloc(
+		tx_data->num_code_sets * sizeof(char *));
+	if (tx_data->code_sets == NULL) {
+		fw_unload_locked();
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	for (i = 0; i < TX_BLOCK_SIZE; ++i)
+		tx_data->fixed[i] = -1;
+
+	/* Read global fixed data template */
+	if (!read_uint8(&data, tx_data->endp, &num_global_fixed) ||
+	    num_global_fixed > TX_BLOCK_SIZE)
+		goto corrupt;
+	for (i = 0; i < num_global_fixed; ++i) {
+		unsigned char pos, val;
+		if (!read_uint8(&data, tx_data->endp, &pos) ||
+		    !read_uint8(&data, tx_data->endp, &val) ||
+		    pos >= TX_BLOCK_SIZE)
+			goto corrupt;
+		tx_data->fixed[pos] = (int)val;
+	}
+
+	/* Filch out the position of each code set */
+	for (i = 0; i < tx_data->num_code_sets; ++i) {
+		unsigned int id;
+		unsigned char keys;
+		unsigned char ndiffs;
+
+		/* Save the codeset position */
+		tx_data->code_sets[i] = data;
+
+		/* Read header */
+		if (!read_uint32(&data, tx_data->endp, &id) ||
+		    !read_uint8(&data, tx_data->endp, &keys) ||
+		    !read_uint8(&data, tx_data->endp, &ndiffs) ||
+		    ndiffs > TX_BLOCK_SIZE || keys == 0)
+			goto corrupt;
+
+		/* skip diff positions */
+		if (!skip(&data, tx_data->endp, ndiffs))
+			goto corrupt;
+
+		/* After the diffs we have the first key id + data -
+		   global fixed */
+		if (!skip(&data, tx_data->endp,
+			       1 + TX_BLOCK_SIZE - num_global_fixed))
+			goto corrupt;
+
+		/* Then we have keys-1 blocks of key id+diffs */
+		if (!skip(&data, tx_data->endp,
+			       (ndiffs + 1) * (keys - 1)))
+			goto corrupt;
+	}
+	ret = 0;
+	goto out;
+
+corrupt:
+	zilog_error("firmware is corrupt\n");
+	fw_unload_locked();
+	ret = -EFAULT;
+
+out:
+	mutex_unlock(&tx_data_lock);
+	return ret;
+}
+
+/* initialise the IR TX device */
+static int tx_init(struct IR *ir)
+{
+	int ret;
+
+	/* Load 'firmware' */
+	ret = fw_load(ir);
+	if (ret != 0)
+		return ret;
+
+	/* Send boot block */
+	ret = send_boot_data(ir);
+	if (ret != 0)
+		return ret;
+	ir->need_boot = 0;
+
+	/* Looks good */
+	return 0;
+}
+
+/* do nothing stub to make LIRC happy */
+static loff_t lseek(struct file *filep, loff_t offset, int orig)
+{
+	return -ESPIPE;
+}
+
+/* copied from lirc_dev */
+static ssize_t read(struct file *filep, char *outbuf, size_t n, loff_t *ppos)
+{
+	struct IR *ir = (struct IR *)filep->private_data;
+	unsigned char buf[ir->buf.chunk_size];
+	int ret = 0, written = 0;
+	DECLARE_WAITQUEUE(wait, current);
+
+	dprintk("read called\n");
+	if (ir->c_rx.addr == 0)
+		return -ENODEV;
+
+	if (mutex_lock_interruptible(&ir->buf_lock))
+		return -ERESTARTSYS;
+
+	if (n % ir->buf.chunk_size) {
+		dprintk("read result = -EINVAL\n");
+		mutex_unlock(&ir->buf_lock);
+		return -EINVAL;
+	}
+
+	/* we add ourselves to the task queue before buffer check
+	 * to avoid losing scan code (in case when queue is awaken somewhere
+	 * beetwen while condition checking and scheduling)
+	 */
+	add_wait_queue(&ir->buf.wait_poll, &wait);
+	set_current_state(TASK_INTERRUPTIBLE);
+
+	/* while we did't provide 'length' bytes, device is opened in blocking
+	 * mode and 'copy_to_user' is happy, wait for data.
+	 */
+	while (written < n && ret == 0) {
+		if (lirc_buffer_empty(&ir->buf)) {
+			/* According to the read(2) man page, 'written' can be
+			 * returned as less than 'n', instead of blocking
+			 * again, returning -EWOULDBLOCK, or returning
+			 * -ERESTARTSYS */
+			if (written)
+				break;
+			if (filep->f_flags & O_NONBLOCK) {
+				ret = -EWOULDBLOCK;
+				break;
+			}
+			if (signal_pending(current)) {
+				ret = -ERESTARTSYS;
+				break;
+			}
+			schedule();
+			set_current_state(TASK_INTERRUPTIBLE);
+		} else {
+			lirc_buffer_read_1(&ir->buf, buf);
+			ret = copy_to_user((void *)outbuf+written, buf,
+					   ir->buf.chunk_size);
+			written += ir->buf.chunk_size;
+		}
+	}
+
+	remove_wait_queue(&ir->buf.wait_poll, &wait);
+	set_current_state(TASK_RUNNING);
+	mutex_unlock(&ir->buf_lock);
+
+	dprintk("read result = %s (%d)\n",
+		ret ? "-EFAULT" : "OK", ret);
+
+	return ret ? ret : written;
+}
+
+/* send a keypress to the IR TX device */
+static int send_code(struct IR *ir, unsigned int code, unsigned int key)
+{
+	unsigned char data_block[TX_BLOCK_SIZE];
+	unsigned char buf[2];
+	int i, ret;
+
+	/* Get data for the codeset/key */
+	ret = get_key_data(data_block, code, key);
+
+	if (ret == -EPROTO) {
+		zilog_error("failed to get data for code %u, key %u -- check "
+			    "lircd.conf entries\n", code, key);
+		return ret;
+	} else if (ret != 0)
+		return ret;
+
+	/* Send the data block */
+	ret = send_data_block(ir, data_block);
+	if (ret != 0)
+		return ret;
+
+	/* Send data block length? */
+	buf[0] = 0x00;
+	buf[1] = 0x40;
+	ret = i2c_master_send(&ir->c_tx, buf, 2);
+	if (ret != 2) {
+		zilog_error("i2c_master_send failed with %d\n", ret);
+		return ret < 0 ? ret : -EFAULT;
+	}
+	ret = i2c_master_send(&ir->c_tx, buf, 1);
+	if (ret != 1) {
+		zilog_error("i2c_master_send failed with %d\n", ret);
+		return ret < 0 ? ret : -EFAULT;
+	}
+
+	/* Send finished download? */
+	ret = i2c_master_recv(&ir->c_tx, buf, 1);
+	if (ret != 1) {
+		zilog_error("i2c_master_recv failed with %d\n", ret);
+		return ret < 0 ? ret : -EFAULT;
+	}
+	if (buf[0] != 0xA0) {
+		zilog_error("unexpected IR TX response #1: %02x\n",
+			buf[0]);
+		return ret < 0 ? ret : -EFAULT;
+	}
+
+	/* Send prepare command? */
+	buf[0] = 0x00;
+	buf[1] = 0x80;
+	ret = i2c_master_send(&ir->c_tx, buf, 2);
+	if (ret != 2) {
+		zilog_error("i2c_master_send failed with %d\n", ret);
+		return ret < 0 ? ret : -EFAULT;
+	}
+
+	/* This bit NAKs until the device is ready, so we retry it
+	   sleeping a bit each time.  This seems to be what the windows
+	   driver does, approximately.
+	   Try for up to 1s.
+	*/
+	for (i = 0; i < 20; ++i) {
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout((50 * HZ + 999) / 1000);
+		ret = i2c_master_send(&ir->c_tx, buf, 1);
+		if (ret == 1)
+			break;
+		dprintk("NAK expected: i2c_master_send "
+			"failed with %d (try %d)\n", ret, i+1);
+	}
+	if (ret != 1) {
+		zilog_error("IR TX chip never got ready: last i2c_master_send "
+			    "failed with %d\n", ret);
+		return ret < 0 ? ret : -EFAULT;
+	}
+
+	/* Seems to be an 'ok' response */
+	i = i2c_master_recv(&ir->c_tx, buf, 1);
+	if (i != 1) {
+		zilog_error("i2c_master_recv failed with %d\n", ret);
+		return ret < 0 ? ret : -EFAULT;
+	}
+	if (buf[0] != 0x80) {
+		zilog_error("unexpected IR TX response #2: %02x\n", buf[0]);
+		return -EFAULT;
+	}
+
+	/* Oh good, it worked */
+	dprintk("sent code %u, key %u\n", code, key);
+	return 0;
+}
+
+/*
+ * Write a code to the device.  We take in a 32-bit number (an int) and then
+ * decode this to a codeset/key index.  The key data is then decompressed and
+ * sent to the device.  We have a spin lock as per i2c documentation to prevent
+ * multiple concurrent sends which would probably cause the device to explode.
+ */
+static ssize_t write(struct file *filep, const char *buf, size_t n,
+			  loff_t *ppos)
+{
+	struct IR *ir = (struct IR *)filep->private_data;
+	size_t i;
+	int failures = 0;
+
+	if (ir->c_tx.addr == 0)
+		return -ENODEV;
+
+	/* Validate user parameters */
+	if (n % sizeof(int))
+		return -EINVAL;
+
+	/* Lock i2c bus for the duration */
+	mutex_lock(&ir->lock);
+
+	/* Send each keypress */
+	for (i = 0; i < n;) {
+		int ret = 0;
+		int command;
+
+		if (copy_from_user(&command, buf + i, sizeof(command))) {
+			mutex_unlock(&ir->lock);
+			return -EFAULT;
+		}
+
+		/* Send boot data first if required */
+		if (ir->need_boot == 1) {
+			ret = send_boot_data(ir);
+			if (ret == 0)
+				ir->need_boot = 0;
+		}
+
+		/* Send the code */
+		if (ret == 0) {
+			ret = send_code(ir, (unsigned)command >> 16,
+					    (unsigned)command & 0xFFFF);
+			if (ret == -EPROTO) {
+				mutex_unlock(&ir->lock);
+				return ret;
+			}
+		}
+
+		/* Hmm, a failure.  If we've had a few then give up, otherwise
+		   try a reset
+		 */
+		if (ret != 0) {
+			/* Looks like the chip crashed, reset it */
+			zilog_error("sending to the IR transmitter chip "
+				    "failed, trying reset\n");
+
+			if (failures >= 3) {
+				zilog_error("unable to send to the IR chip "
+					    "after 3 resets, giving up\n");
+				mutex_unlock(&ir->lock);
+				return ret;
+			}
+			set_current_state(TASK_UNINTERRUPTIBLE);
+			schedule_timeout((100 * HZ + 999) / 1000);
+			ir->need_boot = 1;
+			++failures;
+		} else
+			i += sizeof(int);
+	}
+
+	/* Release i2c bus */
+	mutex_unlock(&ir->lock);
+
+	/* All looks good */
+	return n;
+}
+
+/* copied from lirc_dev */
+static unsigned int poll(struct file *filep, poll_table *wait)
+{
+	struct IR *ir = (struct IR *)filep->private_data;
+	unsigned int ret;
+
+	dprintk("poll called\n");
+	if (ir->c_rx.addr == 0)
+		return -ENODEV;
+
+	mutex_lock(&ir->buf_lock);
+
+	poll_wait(filep, &ir->buf.wait_poll, wait);
+
+	dprintk("poll result = %s\n",
+		lirc_buffer_empty(&ir->buf) ? "0" : "POLLIN|POLLRDNORM");
+
+	ret = lirc_buffer_empty(&ir->buf) ? 0 : (POLLIN|POLLRDNORM);
+
+	mutex_unlock(&ir->buf_lock);
+	return ret;
+}
+
+static int ioctl(struct inode *node, struct file *filep, unsigned int cmd,
+		      unsigned long arg)
+{
+	struct IR *ir = (struct IR *)filep->private_data;
+	int result;
+	unsigned long mode, features = 0;
+
+	if (ir->c_rx.addr != 0)
+		features |= LIRC_CAN_REC_LIRCCODE;
+	if (ir->c_tx.addr != 0)
+		features |= LIRC_CAN_SEND_PULSE;
+
+	switch (cmd) {
+	case LIRC_GET_LENGTH:
+		result = put_user((unsigned long)13,
+				  (unsigned long *)arg);
+		break;
+	case LIRC_GET_FEATURES:
+		result = put_user(features, (unsigned long *) arg);
+		if (result)
+			return result;
+		break;
+	case LIRC_GET_REC_MODE:
+		if (!(features&LIRC_CAN_REC_MASK))
+			return -ENOSYS;
+
+		result = put_user(LIRC_REC2MODE
+				  (features&LIRC_CAN_REC_MASK),
+				  (unsigned long *)arg);
+		break;
+	case LIRC_SET_REC_MODE:
+		if (!(features&LIRC_CAN_REC_MASK))
+			return -ENOSYS;
+
+		result = get_user(mode, (unsigned long *)arg);
+		if (!result && !(LIRC_MODE2REC(mode) & features))
+			result = -EINVAL;
+		break;
+	case LIRC_GET_SEND_MODE:
+		if (!(features&LIRC_CAN_SEND_MASK))
+			return -ENOSYS;
+
+		result = put_user(LIRC_MODE_PULSE, (unsigned long *) arg);
+		if (result)
+			return result;
+		break;
+	case LIRC_SET_SEND_MODE:
+		if (!(features&LIRC_CAN_SEND_MASK))
+			return -ENOSYS;
+
+		result = get_user(mode, (unsigned long *) arg);
+		if (result)
+			return result;
+		if (mode != LIRC_MODE_PULSE)
+			return -EINVAL;
+		break;
+	default:
+		return -ENOIOCTLCMD;
+	}
+	return 0;
+}
+
+/* Open the IR device.  Get hold of our IR structure and
+   stash it in private_data for the file */
+static int open(struct inode *node, struct file *filep)
+{
+	struct IR *ir;
+	int ret;
+
+	/* find our IR struct */
+	unsigned minor = MINOR(node->i_rdev);
+	if (minor >= MAX_IRCTL_DEVICES) {
+		dprintk("minor %d: open result = -ENODEV\n",
+			minor);
+		return -ENODEV;
+	}
+	ir = ir_devices[minor];
+
+	/* increment in use count */
+	mutex_lock(&ir->lock);
+	++ir->open;
+	ret = set_use_inc(ir);
+	if (ret != 0) {
+		--ir->open;
+		mutex_unlock(&ir->lock);
+		return ret;
+	}
+	mutex_unlock(&ir->lock);
+
+	/* stash our IR struct */
+	filep->private_data = ir;
+
+	return 0;
+}
+
+/* Close the IR device */
+static int close(struct inode *node, struct file *filep)
+{
+	/* find our IR struct */
+	struct IR *ir = (struct IR *)filep->private_data;
+	if (ir == NULL) {
+		zilog_error("close: no private_data attached to the file!\n");
+		return -ENODEV;
+	}
+
+	/* decrement in use count */
+	mutex_lock(&ir->lock);
+	--ir->open;
+	set_use_dec(ir);
+	mutex_unlock(&ir->lock);
+
+	return 0;
+}
+
+static struct lirc_plugin lirc_template = {
+	.name		= "lirc_zilog",
+	.set_use_inc	= set_use_inc,
+	.set_use_dec	= set_use_dec,
+	.owner		= THIS_MODULE
+};
+
+/* ----------------------------------------------------------------------- */
+
+static int ir_attach(struct i2c_adapter *adap, int have_rx, int have_tx);
+static int ir_detach(struct i2c_client *client);
+static int ir_probe(struct i2c_adapter *adap);
+static int ir_command(struct i2c_client *client, unsigned int cmd, void *arg);
+
+static struct i2c_driver driver = {
+	.driver = {
+		.owner	= THIS_MODULE,
+		.name	= "i2c ir driver",
+	},
+	.attach_adapter	= ir_probe,
+	.detach_client	= ir_detach,
+	.command	= ir_command,
+};
+
+static struct i2c_client client_template = {
+	.name	= "unset",
+	.driver	= &driver
+};
+
+static struct file_operations lirc_fops = {
+	.llseek		= lseek,
+	.read		= read,
+	.write		= write,
+	.poll		= poll,
+	.ioctl		= ioctl,
+	.open		= open,
+	.release	= close
+};
+
+static int i2c_attach(struct i2c_client *client, struct IR *ir)
+{
+	int ret;
+
+	i2c_set_clientdata(client, ir);
+
+	ret = i2c_attach_client(client);
+	if (ret != 0) {
+		client->addr = 0;
+		return ret;
+	}
+	if (!i2c_use_client(client)) {
+		i2c_detach_client(client);
+		client->addr = 0;
+		return -EFAULT;
+	}
+	++ir->devs;
+	return 0;
+}
+
+static int ir_attach(struct i2c_adapter *adap, int have_rx, int have_tx)
+{
+	struct IR *ir;
+	int ret, i;
+
+	printk("lirc_zilog: chip found with %s\n",
+		have_rx && have_tx ? "RX and TX" :
+			have_rx ? "RX only" : "TX only");
+
+	ir = kmalloc(sizeof(struct IR), GFP_KERNEL);
+	if (ir == NULL)
+		return -ENOMEM;
+	if (lirc_buffer_init(&ir->buf, 2, BUFLEN/2) != 0) {
+		kfree(ir);
+		return -ENOMEM;
+	}
+	mutex_init(&ir->lock);
+	mutex_init(&ir->buf_lock);
+	ir->open = 0;
+	ir->devs = 0;
+	ir->task = NULL;
+	ir->need_boot = 1;
+	ir->shutdown = 0;
+	ir->t_notify = ir->t_notify2 = NULL;
+	for (i = 0; i < sizeof(ir->b); ++i)
+		ir->b[0] = 0;
+
+	memcpy(&ir->l, &lirc_template, sizeof(struct lirc_plugin));
+	ir->l.minor = -1;
+
+	/* initialise RX device */
+	client_template.adapter = adap;
+	memcpy(&ir->c_rx, &client_template, sizeof(struct i2c_client));
+	if (have_rx) {
+		DECLARE_COMPLETION(tn);
+
+		/* I2C attach to device */
+		ir->c_rx.addr = 0x71;
+		strncpy(ir->c_rx.name, "Zilog/Hauppauge RX", I2C_NAME_SIZE);
+		ret = i2c_attach(&ir->c_rx, ir);
+		if (ret != 0)
+			goto err;
+
+		/* try to fire up polling thread */
+		ir->t_notify = &tn;
+		ir->task = kthread_run(lirc_thread, ir, "lirc_zilog");
+		ret = PTR_ERR(ir->task);
+		if (ret <= 0) {
+			zilog_error("lirc_register_plugin: cannot run "
+				    "poll thread\n");
+			goto err;
+		}
+		wait_for_completion(&tn);
+		ir->t_notify = NULL;
+	}
+
+	/* initialise TX device */
+	memcpy(&ir->c_tx, &client_template, sizeof(struct i2c_client));
+	if (have_tx) {
+		/* I2C attach to device */
+		ir->c_tx.addr = 0x70;
+		strncpy(ir->c_tx.name, "Zilog/Hauppauge TX", I2C_NAME_SIZE);
+		ret = i2c_attach(&ir->c_tx, ir);
+		if (ret != 0)
+			goto err;
+	}
+
+	/* set lirc_dev stuff */
+	ir->l.code_length = 13;
+	ir->l.rbuf	  = &ir->buf;
+	ir->l.fops	= &lirc_fops;
+	ir->l.data	= ir;
+	ir->l.minor       = minor;
+	ir->l.sample_rate = 0;
+
+	/* register with lirc */
+	ir->l.minor = lirc_register_plugin(&ir->l);
+	if (ir->l.minor < 0 || ir->l.minor >= MAX_IRCTL_DEVICES) {
+		zilog_error("ir_attach: \"minor\" must be between 0 and %d "
+			    "(%d)!\n", MAX_IRCTL_DEVICES-1, ir->l.minor);
+		ret = -EBADRQC;
+		goto err;
+	}
+
+	/* store this for getting back in open() later on */
+	ir_devices[ir->l.minor] = ir;
+
+	/* if we have the tx device, load the 'firmware'.  We do this
+	   after registering with lirc as otherwise hotplug seems to take
+	   10s to create the lirc device.
+	 */
+	if (have_tx) {
+		/* Special TX init */
+		ret = tx_init(ir);
+		if (ret != 0)
+			goto err;
+	}
+	return 0;
+
+err:
+	/* undo everything, hopefully... */
+	if (ir->c_rx.addr)
+		ir_detach(&ir->c_rx);
+	if (ir->c_tx.addr)
+		ir_detach(&ir->c_tx);
+	return ret;
+}
+
+static int ir_detach(struct i2c_client *client)
+{
+	struct IR *ir = i2c_get_clientdata(client);
+	mutex_lock(&ir->lock);
+
+	if (client == &ir->c_rx) {
+		DECLARE_COMPLETION(tn);
+		DECLARE_COMPLETION(tn2);
+
+		/* end up polling thread */
+		if (ir->task && !IS_ERR(ir->task)) {
+			ir->t_notify = &tn;
+			ir->t_notify2 = &tn2;
+			ir->shutdown = 1;
+			wake_up_process(ir->task);
+			complete(&tn2);
+			wait_for_completion(&tn);
+			ir->t_notify = NULL;
+			ir->t_notify2 = NULL;
+		}
+
+		/* unregister device */
+		i2c_detach_client(&ir->c_rx);
+	} else if (client == &ir->c_tx) {
+		i2c_detach_client(&ir->c_tx);
+	} else {
+		mutex_unlock(&ir->lock);
+		zilog_error("ir_detach: detached from something we didn't "
+			    "attach to\n");
+		return -ENODEV;
+	}
+
+	--ir->devs;
+	if (ir->devs < 0) {
+		mutex_unlock(&ir->lock);
+		zilog_error("ir_detach: invalid device count\n");
+		return -ENODEV;
+	} else if (ir->devs == 0) {
+		/* unregister lirc plugin */
+		if (ir->l.minor >= 0 && ir->l.minor < MAX_IRCTL_DEVICES) {
+			lirc_unregister_plugin(ir->l.minor);
+			ir_devices[ir->l.minor] = NULL;
+		}
+
+		/* free memory */
+		lirc_buffer_free(&ir->buf);
+		mutex_unlock(&ir->lock);
+		kfree(ir);
+		return 0;
+	}
+	mutex_unlock(&ir->lock);
+	return 0;
+}
+
+static int ir_probe(struct i2c_adapter *adap)
+{
+	struct i2c_client c;
+	char buf;
+
+	if (adap->id == I2C_HW_B_BT848 ||
+	    adap->id == I2C_HW_B_CX2341X) {
+		int have_rx = 0, have_tx = 0;
+
+		/*
+		 * The external IR receiver is at i2c address 0x71.
+		 * The IR transmitter is at 0x70.
+		 */
+		memset(&c, 0, sizeof(c));
+		c.adapter = adap;
+		c.addr = 0x70;
+
+		if (!disable_rx) {
+			if (i2c_master_recv(&c, &buf, 1) == 1)
+				have_rx = 1;
+			dprintk("probe 0x70 @ %s: %s\n",
+				adap->name,
+				have_rx ? "yes" : "no");
+		}
+
+		if (!disable_tx) {
+			c.addr = 0x71;
+			if (i2c_master_recv(&c, &buf, 1) == 1)
+				have_tx = 1;
+			dprintk("probe 0x71 @ %s: %s\n",
+				adap->name,
+				have_tx ? "yes" : "no");
+		}
+
+		if (have_rx || have_tx)
+			return ir_attach(adap, have_rx, have_tx);
+		else
+			zilog_error("%s: no devices found\n", adap->name);
+	}
+
+	return 0;
+}
+
+static int ir_command(struct i2c_client *client, unsigned int cmd, void *arg)
+{
+	/* nothing */
+	return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+#ifdef MODULE
+
+int init_module(void)
+{
+	mutex_init(&tx_data_lock);
+	request_module("ivtv");
+	request_module("firmware_class");
+	i2c_add_driver(&driver);
+	return 0;
+}
+
+void cleanup_module(void)
+{
+	i2c_del_driver(&driver);
+	/* if loaded */
+	fw_unload();
+}
+
+MODULE_DESCRIPTION("Zilog/Hauppauge infrared transmitter driver (i2c stack)");
+MODULE_AUTHOR("Gerd Knorr, Michal Kochanowicz, Christoph Bartelmus, "
+	      "Ulrich Mueller, Stefan Jahn, Jerome Brock, Mark Weaver");
+MODULE_LICENSE("GPL");
+/* for compat with old name, which isn't all that accurate anymore */
+MODULE_ALIAS("lirc_pvr150");
+
+module_param(minor, int, 0444);
+MODULE_PARM_DESC(minor, "Preferred minor device number");
+
+module_param(debug, bool, 0644);
+MODULE_PARM_DESC(debug, "Enable debugging messages");
+
+module_param(disable_rx, bool, 0644);
+MODULE_PARM_DESC(disable_rx, "Disable the IR receiver device");
+
+module_param(disable_tx, bool, 0644);
+MODULE_PARM_DESC(disable_tx, "Disable the IR transmitter device");
+
+#endif /* MODULE */
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
-- 
1.6.0.1


^ permalink raw reply related	[flat|nested] 111+ messages in thread

* [PATCH 18/18] lirc driver for the zilog/haupauge IR transceiver
  2008-09-09  4:06                                   ` [PATCH 18/18] lirc driver for the zilog/haupauge IR transceiver Jarod Wilson
@ 2008-09-09  4:06                                     ` Jarod Wilson
  0 siblings, 0 replies; 111+ messages in thread
From: Jarod Wilson @ 2008-09-09  4:06 UTC (permalink / raw)
  To: linux-kernel

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain, Size: 470069 bytes --]

---
diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig
index 5f9d860..2ba0904 100644
--- a/drivers/input/Kconfig
+++ b/drivers/input/Kconfig
@@ -170,6 +170,8 @@ source "drivers/input/tablet/Kconfig"
 
 source "drivers/input/touchscreen/Kconfig"
 
+source "drivers/input/lirc/Kconfig"
+
 source "drivers/input/misc/Kconfig"
 
 endif
diff --git a/drivers/input/Makefile b/drivers/input/Makefile
index 98c4f9a..4dcb852 100644
--- a/drivers/input/Makefile
+++ b/drivers/input/Makefile
@@ -25,3 +25,5 @@ obj-$(CONFIG_INPUT_MISC)	+= misc/
 obj-$(CONFIG_INPUT_APMPOWER)	+= apm-power.o
 
 obj-$(CONFIG_XEN_KBDDEV_FRONTEND)	+= xen-kbdfront.o
+
+obj-$(CONFIG_INPUT_LIRC)	+= lirc/
diff --git a/drivers/input/lirc/Kconfig b/drivers/input/lirc/Kconfig
new file mode 100644
index 0000000..47183ee
--- /dev/null
+++ b/drivers/input/lirc/Kconfig
@@ -0,0 +1,142 @@
+#
+# LIRC driver(s) configuration
+#
+menuconfig INPUT_LIRC
+	bool "Linux Infrared Remote Control IR receiver/transmitter drivers"
+	default n
+	help
+	  Say Y here, and all supported Linux Infrared Remote Control IR and
+	  RF receiver and transmitter drivers will be displayed. When paired
+	  with a remote control and the lirc daemon, the receiver drivers
+	  allow control of your Linux system via remote control.
+
+if INPUT_LIRC
+
+config LIRC_DEV
+	tristate "LIRC device loadable module support"
+	default m
+	help
+	  LIRC device loadable module support, required for most LIRC drivers
+
+config LIRC_ATIUSB
+	tristate "ATI RF USB Receiver support"
+	default m
+	depends on LIRC_DEV
+	help
+	  Driver for the ATI USB RF remote receiver
+
+config LIRC_BT829
+	tristate "BT829 based hardware"
+	default m
+	depends on LIRC_DEV
+	help
+	  Driver for the IR interface on BT829-based hardware
+
+config LIRC_CMDIR
+	tristate "CommandIR USB Transceiver"
+	default m
+	depends on LIRC_DEV
+	help
+	  Driver for the CommandIR USB Transceiver
+
+config LIRC_I2C
+	tristate "I2C Based IR Receivers"
+	default m
+	depends on LIRC_DEV
+	help
+	  Driver for I2C-based IR receivers, such as those commonly
+	  found onboard Hauppauge PVR-150/250/350 video capture cards
+
+config LIRC_IGORPLUGUSB
+	tristate "Igor Cesko's USB IR Receiver"
+	default m
+	depends on LIRC_DEV
+	help
+	  Driver for Igor Cesko's USB IR Receiver
+
+config LIRC_IMON
+	tristate "Soundgraph IMON Receiver"
+	default m
+	depends on LIRC_DEV
+	help
+	  Driver for the Soundgraph IMON IR Receiver
+
+config LIRC_IT87
+	tristate "ITE IT87XX CIR Port Receiver"
+	default m
+	depends on LIRC_DEV
+	help
+	  Driver for the ITE IT87xx IR Receiver
+
+config LIRC_ITE8709
+	tristate "ITE8709 CIR Port Receiver"
+	default m
+	depends on LIRC_DEV && PNP
+	help
+	  Driver for the ITE8709 IR Receiver
+
+config LIRC_MCEUSB
+	tristate "Microsoft Media Center Ed. Receiver, v1"
+	default m
+	depends on LIRC_DEV
+	help
+	  Driver for the Microsoft Media Center Ed. Receiver, v1
+
+config LIRC_MCEUSB2
+	tristate "Microsoft Media Center Ed. Receiver, v2"
+	default m
+	depends on LIRC_DEV
+	help
+	  Driver for the Microsoft Media Center Ed. Receiver, v2
+
+config LIRC_PARALLEL
+	tristate "Homebrew Parallel Port Receiver"
+	default m
+	depends on LIRC_DEV && !SMP
+	help
+	  Driver for Homebrew Parallel Port Receivers
+
+config LIRC_SASEM
+	tristate "Sasem USB IR Remote"
+	default m
+	depends on LIRC_DEV
+	help
+	  Driver for the Sasem OnAir Remocon-V or Dign HV5 HTPC IR/VFD Module
+
+config LIRC_SERIAL
+	tristate "Homebrew Serial Port Receiver"
+	default m
+	depends on LIRC_DEV
+	help
+	  Driver for Homebrew Serial Port Receivers
+
+config LIRC_SIR
+	tristate "Built-in SIR IrDA port"
+	default m
+	depends on LIRC_DEV
+	help
+	  Driver for the SIR IrDA port
+
+config LIRC_STREAMZAP
+	tristate "Streamzap PC Receiver"
+	default m
+	depends on LIRC_DEV
+	help
+	  Driver for the Streamzap PC Receiver
+
+config LIRC_TTUSBIR
+	tristate "Technotrend USB IR Receiver"
+	default m
+	depends on LIRC_DEV
+	help
+	  Driver for the Technotrend USB IR Receiver
+
+config LIRC_ZILOG
+	tristate "Zilog/Hauppauge IR Transmitter"
+	default m
+	depends on LIRC_DEV
+	help
+	  Driver for the Zilog/Hauppauge IR Transmitter, found on
+	  PVR-150/500, HVR-1200/1250/1700/1800, HD-PVR and other cards
+
+endif
diff --git a/drivers/input/lirc/Makefile b/drivers/input/lirc/Makefile
new file mode 100644
index 0000000..411db5e
--- /dev/null
+++ b/drivers/input/lirc/Makefile
@@ -0,0 +1,25 @@
+# Makefile for the lirc drivers.
+#
+
+# Each configuration option enables a list of files.
+
+EXTRA_CFLAGS =-DIRCTL_DEV_MAJOR=61 -DLIRC_SERIAL_TRANSMITTER -I$(src)
+
+obj-$(CONFIG_LIRC_DEV)		+= lirc_dev.o
+obj-$(CONFIG_LIRC_ATIUSB)	+= lirc_atiusb.o
+obj-$(CONFIG_LIRC_BT829)	+= lirc_bt829.o
+obj-$(CONFIG_LIRC_CMDIR)	+= lirc_cmdir.o
+obj-$(CONFIG_LIRC_I2C)		+= lirc_i2c.o
+obj-$(CONFIG_LIRC_IGORPLUGUSB)	+= lirc_igorplugusb.o
+obj-$(CONFIG_LIRC_IMON)		+= lirc_imon.o
+obj-$(CONFIG_LIRC_IT87)		+= lirc_it87.o
+obj-$(CONFIG_LIRC_ITE8709)	+= lirc_ite8709.o
+obj-$(CONFIG_LIRC_MCEUSB)	+= lirc_mceusb.o
+obj-$(CONFIG_LIRC_MCEUSB2)	+= lirc_mceusb2.o
+obj-$(CONFIG_LIRC_PARALLEL)	+= lirc_parallel.o
+obj-$(CONFIG_LIRC_SASEM)	+= lirc_sasem.o
+obj-$(CONFIG_LIRC_SERIAL)	+= lirc_serial.o
+obj-$(CONFIG_LIRC_SIR)		+= lirc_sir.o
+obj-$(CONFIG_LIRC_STREAMZAP)	+= lirc_streamzap.o
+obj-$(CONFIG_LIRC_TTUSBIR)	+= lirc_ttusbir.o
+obj-$(CONFIG_LIRC_ZILOG)	+= lirc_zilog.o
diff --git a/drivers/input/lirc/commandir.c b/drivers/input/lirc/commandir.c
new file mode 100644
index 0000000..a05b0d6
--- /dev/null
+++ b/drivers/input/lirc/commandir.c
@@ -0,0 +1,982 @@
+
+/*
+ *
+ *	Hardware Driver for COMMANDIR USB Transceiver
+ *	2005-2007 InnovationOne - Matt Bodkin, Evelyn Yeung
+ *
+ *	Version 1.4.2
+ *	For 2.4.* or 2.6.* kernel versions
+ *	Based on the USB Skeleton driver, versions 0.7 and 2.0
+ *
+ */
+
+#include <linux/version.h>
+#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/usb.h>
+#include <linux/smp_lock.h>
+#include "commandir.h"
+
+#include <linux/kref.h>
+#include <linux/uaccess.h>
+
+#define DRIVER_VERSION "v1.1.2"
+#define DRIVER_AUTHOR "Evelyn Yeung, InnovationOne"
+#define DRIVER_DESC "CommandIR USB Transceiver Driver"
+
+#define USB_CMDIR_VENDOR_ID	0x10c4
+#define USB_CMDIR_PRODUCT_ID	0x0003
+#define USB_CMDIR_MINOR_BASE	192
+
+/* table of devices that work with this driver */
+static struct usb_device_id cmdir_table[] =
+{
+	{ USB_DEVICE(USB_CMDIR_VENDOR_ID, USB_CMDIR_PRODUCT_ID) },
+	{ }			/* Terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, cmdir_table);
+
+/* circular packet queue */
+unsigned char ourbuffers[QUEUELENGTH][64];
+int waitusecs[QUEUELENGTH];
+int ourbufferlengths[QUEUELENGTH];
+int nexttosend;
+int nexttofill;
+int send_status = SEND_IDLE;
+int last_tx_sec;
+int last_tx_usec;
+
+static int curTXFill;
+struct timeval tp;
+
+int debug_commandir;
+
+/* Structure to hold all of our device specific stuff */
+struct usb_skel {
+	struct usb_device *udev; /* the usb device for this device */
+	struct usb_interface *interface; /* the interface for this device */
+	unsigned char *bulk_in_buffer; /* the buffer to receive data */
+	size_t bulk_in_size; /* the size of the receive buffer */
+	__u8 bulk_in_endpointAddr; /* the address of the bulk in endpoint */
+	__u8 bulk_out_endpointAddr; /* the address of the bulk out endpoint */
+	struct kref kref;
+};
+#define to_skel_dev(d) container_of(d, struct usb_skel, kref)
+
+static struct file_operations cmdir_fops = {
+	.read =		cmdir_file_read,
+	.write =	cmdir_file_write,
+	.open =		cmdir_open,
+	.release =	cmdir_release,
+};
+
+/* usb class driver info in order to get a minor number from the usb core,
+ * and to have the device registered with devfs and the driver core */
+static struct usb_class_driver cmdir_class = {
+	.name =		"usb/commandir%d",
+	.fops =		&cmdir_fops,
+	/* .mode =	S_IFCHR | S_IRUSR | S_IWUSR |
+	 *		S_IRGRP | S_IWGRP | S_IROTH, */
+	.minor_base =	USB_CMDIR_MINOR_BASE,
+};
+
+static struct usb_driver cmdir_driver = {
+	.name =		"commandir",
+	.probe =	cmdir_probe,
+	.disconnect =	cmdir_disconnect,
+	.id_table =	cmdir_table,
+};
+
+
+static int lcd_device;
+static int rx_device;
+static int def_device;
+
+#define DEFAULT_TRANSMITTERS 0x0F
+static unsigned int transmitters = DEFAULT_TRANSMITTERS;
+static unsigned int next_transmitters = DEFAULT_TRANSMITTERS;
+
+#define CMDIR_VAR_LEN 68
+static char cmdir_var[] =
+"COMMANDIRx:\n TX Enabled: 1, 2, 3, 4\n RX: commandirx\n LCD: commandirx";
+
+
+static void cmdir_delete(struct kref *kref)
+{
+	struct usb_skel *dev = to_skel_dev(kref);
+
+	usb_put_dev(dev->udev);
+	kfree(dev->bulk_in_buffer);
+	kfree(dev);
+}
+
+static int __init usb_cmdir_init(void)
+{
+	int result;
+
+	/* register this driver with the USB subsystem */
+	result = usb_register(&cmdir_driver);
+
+	if (result)
+		err("usb_register failed. Error number %d", result);
+
+	return result;
+}
+
+static int cmdir_open(struct inode *inode, struct file *file)
+{
+	struct usb_skel *dev;
+	struct usb_interface *interface;
+	int subminor;
+	int retval = 0;
+
+	subminor = iminor(inode);
+	interface = usb_find_interface(&cmdir_driver, subminor);
+	if (!interface) {
+		err("%s - error, can't find device for minor %d",
+		     __func__, subminor);
+		retval = -ENODEV;
+		goto exit;
+	}
+
+	dev = usb_get_intfdata(interface);
+	if (!dev) {
+		retval = -ENODEV;
+		goto exit;
+	}
+
+	kref_get(&dev->kref);	/* increment our usage count for the device */
+	file->private_data = dev; /* save object in file's private structure */
+
+exit:
+	return retval;
+}
+
+static int cmdir_probe(struct usb_interface *interface,
+		       const struct usb_device_id *id)
+{
+	struct usb_skel *dev = NULL;
+	struct usb_host_interface *iface_desc;
+	struct usb_endpoint_descriptor *endpoint;
+	size_t buffer_size;
+
+	int i;
+	int retval = -ENOMEM;
+	int minor;
+
+	/* allocate memory for our device state and initialize it */
+	dev = kmalloc(sizeof(*dev), GFP_KERNEL);
+	if (dev == NULL) {
+		err("Out of memory");
+		goto error;
+	}
+	memset(dev, 0x00, sizeof(*dev));
+	kref_init(&dev->kref);
+	dev->udev = usb_get_dev(interface_to_usbdev(interface));
+	dev->interface = interface;
+
+	/* set up the endpoint information */
+	/* use only the first bulk-in and bulk-out endpoints */
+	iface_desc = interface->cur_altsetting;
+	for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+		endpoint = &iface_desc->endpoint[i].desc;
+
+		if (!dev->bulk_in_endpointAddr &&
+		    (endpoint->bEndpointAddress & USB_DIR_IN) &&
+		    ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+					== USB_ENDPOINT_XFER_BULK)) {
+			/* we found a bulk in endpoint */
+			buffer_size = endpoint->wMaxPacketSize;
+			dev->bulk_in_size = buffer_size;
+			dev->bulk_in_endpointAddr = endpoint->bEndpointAddress;
+			dev->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL);
+			if (!dev->bulk_in_buffer) {
+				err("Could not allocate bulk_in_buffer");
+				goto error;
+			}
+		}
+
+		if (!dev->bulk_out_endpointAddr &&
+		    !(endpoint->bEndpointAddress & USB_DIR_IN) &&
+		    ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+					== USB_ENDPOINT_XFER_BULK)) {
+			/* we found a bulk out endpoint */
+			dev->bulk_out_endpointAddr = endpoint->bEndpointAddress;
+		}
+	}
+	if (!(dev->bulk_in_endpointAddr && dev->bulk_out_endpointAddr)) {
+		err("Could not find both bulk-in and bulk-out endpoints");
+		goto error;
+	}
+
+	/* save our data pointer in this interface device */
+	usb_set_intfdata(interface, dev);
+
+	/* we can register the device now, as it is ready */
+	retval = usb_register_dev(interface, &cmdir_class);
+	if (retval) {
+		/* something prevented us from registering this driver */
+		err("Not able to get a minor for this device.");
+		usb_set_intfdata(interface, NULL);
+		goto error;
+	}
+
+	/* check whether minor already includes base */
+	minor = interface->minor;
+	if (minor >= USB_CMDIR_MINOR_BASE)
+		minor = minor-USB_CMDIR_MINOR_BASE;
+
+	/* let the user know what node this device is now attached to */
+	info("CommandIR USB device now attached to commandir%d", minor);
+
+	reset_cmdir(minor);
+
+	return 0;
+
+error:
+	if (dev)
+		kref_put(&dev->kref, cmdir_delete);
+	return retval;
+}
+
+
+static void cmdir_disconnect(struct usb_interface *interface)
+{
+	struct usb_skel *dev;
+	int minor = interface->minor;
+
+	/* prevent cmdir_open() from racing cmdir_disconnect() */
+	lock_kernel();
+
+	dev = usb_get_intfdata(interface);
+	usb_set_intfdata(interface, NULL);
+
+	/* give back our minor */
+	usb_deregister_dev(interface, &cmdir_class);
+
+	unlock_kernel();
+
+	/* decrement our usage count */
+		kref_put(&dev->kref, cmdir_delete);
+
+	/* check whether minor already includes base */
+	if (minor >= USB_CMDIR_MINOR_BASE)
+		minor = minor-USB_CMDIR_MINOR_BASE;
+
+	info("CommandIR #%d now disconnected", minor);
+
+	/* check if default RX device still exists */
+	if (minor == rx_device) {
+		/* decrement until find next valid device */
+		while (rx_device > 0) {
+			rx_device--;
+			if (cmdir_check(rx_device) == 0)
+				break;
+		}
+		if (minor > 0)
+			info("Active Receiver is on CommandIR #%d", rx_device);
+	}
+}
+
+static int cmdir_release(struct inode *inode, struct file *file)
+{
+	struct usb_skel *dev;
+	int retval = 0;
+
+	dev = (struct usb_skel *)file->private_data;
+	if (dev == NULL)
+		/*dbg(" - object is NULL");*/
+		return -ENODEV;
+
+	/* decrement the count on our device */
+		kref_put(&dev->kref, cmdir_delete);
+	return retval;
+}
+
+static void __exit usb_cmdir_exit(void)
+{
+	/* deregister this driver with the USB subsystem */
+	usb_deregister(&cmdir_driver);
+
+}
+
+static int cmdir_check(int device_num)
+{
+	struct usb_interface *interface;
+
+	interface = usb_find_interface(&cmdir_driver,
+				       USB_CMDIR_MINOR_BASE+device_num);
+	if (!interface) {
+		/* also check without adding base, for devfs */
+		interface = usb_find_interface(&cmdir_driver, rx_device);
+		if (!interface)
+			return -ENODEV;
+	}
+	return 0;
+}
+
+static void init_cmdir_var(int device_num)
+{
+	int i;
+	unsigned int multiplier = 1;
+
+	for (i = 0; i < device_num; i++)
+		multiplier = multiplier*0x10;
+	transmitters |= multiplier * 0x0F;
+	next_transmitters = transmitters;
+	info("commandir%d reset", device_num);
+	return;
+}
+
+static void reset_cmdir(int device_num)
+{
+	unsigned char ctrl_buffer[MCU_CTRL_SIZE];
+	int retval;
+	int i;
+
+	ctrl_buffer[0] = RESET_HEADER;
+	for (i = 1; i < MCU_CTRL_SIZE; i++)
+		ctrl_buffer[i] = 'j';
+	retval = write_core(ctrl_buffer, MCU_CTRL_SIZE, NULL, device_num);
+
+	init_cmdir_var(device_num);
+	print_cmdir(device_num);
+
+	return;
+}
+
+static void update_cmdir_string(int device_num)
+{
+	int next_comma = 0;
+	int next_pos = 25;
+	unsigned int multiplier;
+	int i;
+
+	/* cmdir_var[] = "COMMANDIRx:\n"
+	 * 		 " TX Enabled: 1, 2, 3, 4\n"
+	 * 		 " RX: commandirx\n"
+	 * 		 " LCD: commandirx\n" */
+
+	cmdir_var[9] = ASCII0+device_num;
+	cmdir_var[50] = ASCII0+rx_device;
+	cmdir_var[67] = ASCII0+lcd_device;
+
+	for (i = 25; i < 35; i++)
+		cmdir_var[i] = ' ';
+
+	multiplier = 1;
+	for (i = 0; i < device_num; i++)
+		multiplier = multiplier*0x10;
+
+	if (transmitters & (multiplier*0x01)) {
+		cmdir_var[next_pos] = '1';
+		next_pos += 3;
+		next_comma++;
+	}
+	if (transmitters & (multiplier*0x02)) {
+		cmdir_var[next_pos] = '2';
+		if (next_comma > 0)
+			cmdir_var[next_pos-2] = ',';
+		next_pos += 3;
+		next_comma++;
+	}
+	if (transmitters & (multiplier*0x04)) {
+		cmdir_var[next_pos] = '3';
+		if (next_comma > 0)
+			cmdir_var[next_pos-2] = ',';
+		next_pos += 3;
+		next_comma++;
+	}
+	if (transmitters & (multiplier*0x08)) {
+		cmdir_var[next_pos] = '4';
+		if (next_comma > 0)
+			cmdir_var[next_pos-2] = ',';
+		next_pos += 3;
+		next_comma++;
+	}
+	return;
+}
+
+static void print_cmdir(int device_num)
+{
+	update_cmdir_string(device_num);
+	info("%s", cmdir_var);
+	return;
+}
+
+static ssize_t cmdir_file_read(struct file *file, char *buffer,
+			       size_t count, loff_t *ppos)
+{
+	int retval = 0;
+	int minor = 0;
+	struct usb_skel *dev;
+
+	dev = (struct usb_skel *)file->private_data;
+	minor = dev->interface->minor;
+	if (minor >= USB_CMDIR_MINOR_BASE)
+		minor = minor - USB_CMDIR_MINOR_BASE;
+
+	if (((int)*ppos) == 0) {
+		update_cmdir_string(minor);
+		if (copy_to_user(buffer, cmdir_var, CMDIR_VAR_LEN))
+			retval = -EFAULT;
+		else
+			retval = CMDIR_VAR_LEN;
+		return retval;
+	} else
+		return 0;
+}
+
+/*  Read data from CommandIR  */
+ssize_t cmdir_read(unsigned char *buffer, size_t count)
+{
+	struct usb_skel *dev;
+	int length, retval = 0;
+
+	struct usb_interface *interface;
+	interface = usb_find_interface(&cmdir_driver,
+			USB_CMDIR_MINOR_BASE+rx_device);
+	if (!interface) {
+		/* also check without adding base, for devfs */
+		interface = usb_find_interface(&cmdir_driver, rx_device);
+		if (!interface)
+			return -ENODEV;
+	}
+	dev = usb_get_intfdata(interface);
+	if (!dev)
+		return -ENODEV;
+	retval = usb_bulk_msg(dev->udev, usb_rcvbulkpipe(dev->udev,
+						dev->bulk_in_endpointAddr),
+		 dev->bulk_in_buffer, min(dev->bulk_in_size, count),
+		 &length, HZ*10);
+	if (!retval) {
+		if (!memcpy(buffer, dev->bulk_in_buffer, length))
+			retval = -EFAULT;
+		else {
+			/* current status of the TX buffer */
+			curTXFill = buffer[2];
+			retval = length;
+		}
+	}
+	/* suppress errors */
+	/*
+	else {
+		err("Read from device failed, error %d",retval);
+	}
+	*/
+	/* printk(KERN_INFO "CommandIR Reporting TX buffer at %d bytes. \n",
+	 * 	  curTXFill); */
+	return retval;
+}
+EXPORT_SYMBOL(cmdir_read);
+
+static ssize_t cmdir_file_write(struct file *file, const char *buffer,
+				size_t count, loff_t *ppos)
+{
+	int retval;
+	int i;
+	int equalsign = 0;
+	int changeType = 0;
+	unsigned char ctrl_buffer[MCU_CTRL_SIZE];
+	char *local_buffer;
+	int minor;
+
+	/* set as default - if non-specific error,
+	 * won't keep calling this function */
+	retval = count;
+	local_buffer = kmalloc(count, GFP_KERNEL);
+
+	/* verify that we actually have some data to write */
+	if (count == 0) {
+		err("Write request of 0 bytes");
+		goto exit;
+	}
+	if (count > 64) {
+		err("Input too long");
+		goto exit;
+	}
+
+	/* copy the data from userspace into our local buffer */
+	if (copy_from_user(local_buffer, buffer, count)) {
+		retval = -EFAULT;
+		goto exit;
+	}
+
+	/* parse code */
+	changeType = cNothing;
+	equalsign = 0;
+	for (i = 0; i < MCU_CTRL_SIZE; i++)
+		ctrl_buffer[i] = 'j';
+
+	for (i = 0; i < count; i++) {
+		switch (local_buffer[i]) {
+		case 'X':
+		case 'x':
+			if ((i > 0) && ((local_buffer[i - 1] == 'R')
+			    || (local_buffer[i - 1] == 'r')))
+				changeType = cRX;
+			break;
+		case 'S':
+		case 's':
+			if ((i > 1) && ((local_buffer[i - 1] == 'E')
+			    || (local_buffer[i - 1] == 'e'))) {
+				if ((local_buffer[i-2] == 'R')
+				    || (local_buffer[i-2] == 'r'))
+					changeType = cRESET;
+			}
+			break;
+		case 'L':
+		case 'l':
+			if ((i > 0) && ((local_buffer[i - 1] == 'F')
+			    || (local_buffer[i - 1] == 'f')))
+				changeType = cFLASH;
+			break;
+		case 'C':
+		case 'c':
+			if ((i > 0) && ((local_buffer[i - 1] == 'L')
+			    || (local_buffer[i - 1] == 'l')))
+				changeType = cLCD;
+			break;
+		case '=':
+			if (changeType != cNothing)
+				equalsign = i;
+			break;
+		case '0':
+		case '1':
+		case '2':
+		case '3':
+		case '4':
+		case '5':
+		case '6':
+		case '7':
+			if (equalsign > 0) {
+				minor = local_buffer[i] - ASCII0;
+				switch (changeType) {
+				case cRESET:
+					ctrl_buffer[0] = RESET_HEADER;
+					retval = write_core(ctrl_buffer,
+						MCU_CTRL_SIZE,
+						cmdir_write_bulk_callback,
+						minor);
+					if (retval != MCU_CTRL_SIZE) {
+						if (retval == -ENODEV)
+							err("Device %d "
+							    "unplugged", minor);
+						else
+							err("Error on write to "
+							    "%d", minor);
+						goto exit;
+					} else
+						retval = count;
+					init_cmdir_var(minor);
+					break;
+				case cFLASH:
+					ctrl_buffer[0] = FLASH_HEADER;
+					info("Flashing indicators on device %d",
+					     minor);
+					retval = write_core(ctrl_buffer,
+						MCU_CTRL_SIZE,
+						cmdir_write_bulk_callback,
+						minor);
+					if (retval != MCU_CTRL_SIZE) {
+						if (retval == -ENODEV)
+							err("Device %d "
+							    "unplugged", minor);
+						else
+							err("Error on write to "
+							    "%d", minor);
+						goto exit;
+					} else
+						retval = count;
+					break;
+				case cRX:
+					rx_device = minor;
+					info("Default receiver set to %d",
+					     minor);
+					break;
+				case cLCD:
+					lcd_device = minor;
+					info("commandir: Default LCD set to %d",
+					     minor);
+					break;
+				default:
+					break;
+				}
+			}
+			break;
+		case ',':
+			equalsign = 0;
+			changeType = cNothing;
+			break;
+		default:
+			if ((equalsign > 0) && (local_buffer[i] > 32)) {
+				err("Non-numerical argument");
+				goto exit;
+			}
+			break;
+		}
+	}
+
+	if ((changeType != cNothing) && (equalsign == 0))
+		err("No device specified");
+	if (changeType == cNothing)
+		err("Unknown command");
+
+exit:
+	kfree(local_buffer);
+	return retval;
+}
+
+int cmdir_write(unsigned char *buffer, int count,
+		void *callback_fct, int usecdelay)
+{
+	/* Always add to queue, then send queue number
+	 * no locks
+	 * mbodkin, Sept 8, 2005 */
+	int ret = 0;
+	if (debug_commandir == 1) {
+		do_gettimeofday(&tp);
+		printk(KERN_INFO "cmdir_write at %d\n", (int)tp.tv_usec);
+	}
+	ret = add_cmdir_queue(buffer, count, callback_fct, usecdelay);
+
+	if (ret == -1)  {
+		printk(KERN_INFO "cmdir_write returning 0\n");
+		return 0;
+	}
+	return count;
+
+}
+EXPORT_SYMBOL(cmdir_write);
+
+int add_cmdir_queue(unsigned char *buffer, int count,
+		    void *callback_vct, int usecdelay)
+{
+	int ret = 0;
+	if ((nexttofill + 1) % (QUEUELENGTH - 1) == nexttosend) {
+
+		/* our buffer is full */
+		printk(KERN_INFO "Too many packets backlogged "
+		       "in CommandIR Queue.\n");
+		return -1;
+	}
+	/* go ahead and use this one: */
+	memcpy(ourbuffers[nexttofill], buffer, count);
+	ourbufferlengths[nexttofill] = count;
+	waitusecs[nexttofill] = (usecdelay == 0) ? 10000 : usecdelay;
+	/* printk(KERN_INFO "Adding %d to queue at position %d.\n",
+	 *        count, nexttofill); */
+	nexttofill = (nexttofill + 1) % (QUEUELENGTH - 1);
+	ret = nexttofill;
+	/* if (timer_running == 0) */
+	send_queue(); /* fake it if the timer's not running */
+	return ret;	/* we accepted the full packet */
+
+}
+
+int send_queue()
+{
+	int last_sent = 0;
+	int ret = 0;
+	if (debug_commandir == 1) {
+		do_gettimeofday(&tp);
+		printk(KERN_INFO "Send_queue() at %d\n", (int)tp.tv_usec);
+	}
+	/* initiate the send/callback routine if not already running. */
+	if (send_status == SEND_IDLE) {
+		if (!(nexttofill == nexttosend)) {
+			/* start it up: */
+
+			last_sent = nexttosend - 1;
+			if (last_sent < 0)
+				last_sent = QUEUELENGTH - 1;
+			/* Final check - is it TIME to send this packet yet? */
+			/* if (wait_to_tx(waitusecs[last_sent]) == 0) { */
+			/* always send if there's room,
+			 * otherwise wait until room */
+			if (curTXFill < 190) {
+				if (debug_commandir == 1) {
+					do_gettimeofday(&tp);
+					printk(KERN_INFO "Sending packet data "
+					       "at %d\n", (int)tp.tv_usec);
+				}
+				ret = cmdir_write_queue(ourbuffers[nexttosend],
+				      ourbufferlengths[nexttosend], NULL);
+				if (ret <= 0) {
+					/* send failed - the device is either
+					 * unplugged or full
+					 * nexttosend =
+					 * 	(nexttosend + 1)
+					 * 	% (QUEUELENGTH - 1); */
+					send_status = SEND_IDLE;
+					return 0; /*send_queue(); */
+				} else
+					nexttosend = (nexttosend + 1)
+						     % (QUEUELENGTH - 1);
+				return 1;
+			} else {
+				if (debug_commandir == 1) {
+					do_gettimeofday(&tp);
+					printk(KERN_INFO "Not time to send yet "
+					       "- starting timer at %d.\n",
+					       (int)tp.tv_usec);
+					printk(KERN_INFO "Enabling timer.\n");
+				}
+				return 0; /* doesn't matter anymore */
+			}
+		} else {
+			if (debug_commandir == 1) {
+				do_gettimeofday(&tp);
+				printk(KERN_INFO "No more data to send %d!\n",
+				       (int)tp.tv_usec);
+			}
+			last_tx_sec = 0; /* reset our TX counters */
+			last_tx_usec = 0;
+			return 1; /* nothing more to send! */
+		}
+	} else {
+		if (debug_commandir == 1)
+			/* will try again on the callback */
+			printk(KERN_INFO "Already sending\n");
+		return 1;  /* then the timer shouldn't be running... */
+	}
+	return 0; /* should never get here... */
+}
+
+
+int wait_to_tx(int usecs)
+{
+	/* don't return until last_time + usecs has been reached
+	 * for non-zero last_tx's. */
+	int wait_until_sec = 0, wait_until_usec = 0;
+	int now_sec = 0, now_usec = 0;
+	if (debug_commandir == 1)
+		printk(KERN_INFO "waittotx(%d)\n", usecs);
+	if (usecs == 0)
+		return 0;
+
+	if (!(last_tx_sec == 0 && last_tx_usec == 0)) {
+		/* calculate wait time: */
+		wait_until_sec = last_tx_sec + (usecs / 1000000);
+		wait_until_usec = last_tx_usec + usecs;
+
+		do_gettimeofday(&tp);
+		now_sec = tp.tv_sec;
+		now_usec = tp.tv_usec;
+
+		if (wait_until_usec > 1000000) {
+			/* we've spilled over to the next second. */
+			wait_until_sec++;
+			wait_until_usec -= 1000000;
+			/* printk(KERN_INFO "usec rollover\n"); */
+		}
+		if (debug_commandir == 1)
+			printk(KERN_INFO "Testing for the right second, now = "
+			       "%d %d, wait = %d %d\n",
+			       now_sec, now_usec,
+			       wait_until_sec, wait_until_usec);
+		/* now we are always on the same second. */
+		if (now_sec > wait_until_sec) {
+			if (debug_commandir == 1)
+				printk(KERN_INFO "Setting last_tx_sec to %d.\n",
+				       wait_until_sec);
+			last_tx_sec = wait_until_sec;
+			last_tx_usec = wait_until_usec;
+			return 0;
+		}
+
+		if ((now_sec == wait_until_sec)
+		    && (now_usec > wait_until_usec)) {
+			if (debug_commandir == 1)
+				printk(KERN_INFO "Setting last_tx_sec to %d.\n",
+				       wait_until_sec);
+			last_tx_sec = wait_until_sec;
+			last_tx_usec = wait_until_usec;
+			return 0;
+		}
+		return -1; /* didn't send */
+	}
+
+	do_gettimeofday(&tp);
+	last_tx_usec = tp.tv_usec;
+	last_tx_sec = tp.tv_sec;
+	return 0; /* if there's no last even, go ahead and send */
+}
+
+
+int cmdir_write_queue(unsigned char *buffer, int count, void *callback_fct)
+{
+	int retval = count;
+	static char prev_signal_num;
+	unsigned char next_mask;
+	unsigned int multiplier;
+	int i;
+
+	send_status = SEND_ACTIVE;
+
+	if (count < 2) {
+		err("Not enough bytes (write request of %d bytes)", count);
+		return count;
+	}
+
+	/* check data; decide which device to send to */
+	switch (buffer[0]) {
+	case TX_HEADER:
+	case TX_HEADER_NEW:
+		/* this is LIRC transmit data */
+		if (curTXFill >= 190) {
+			printk(KERN_INFO
+			       "TX buffer too full to send more TX data\n");
+			return 0;
+		}
+		if (next_transmitters != transmitters) {
+			if (buffer[1] != prev_signal_num)
+				/* this is new signal; change transmitter mask*/
+				transmitters = next_transmitters;
+		}
+		prev_signal_num = buffer[1];
+
+		multiplier = 1;
+		for (i = 0; i < MAX_DEVICES; i++) {
+			next_mask = 0;
+			if (transmitters & (0x01*multiplier))
+				next_mask |= TX1_ENABLE;
+			if (transmitters & (0x02*multiplier))
+				next_mask |= TX2_ENABLE;
+			if (transmitters & (0x04*multiplier))
+				next_mask |= TX3_ENABLE;
+			if (transmitters & (0x08*multiplier))
+				next_mask |= TX4_ENABLE;
+
+			if (next_mask > 0) {
+				buffer[1] = next_mask;
+				retval = write_core(buffer, count,
+					 callback_fct, i);
+				if (retval != count) {
+					if (retval == -ENODEV)
+						err("Device %d not plugged in",
+						    i);
+					else
+						err("Write error to device %d",
+						    i);
+					return retval;
+				}
+			}
+			multiplier = multiplier*0x10;
+		}
+		return retval;
+		break;
+	case LCD_HEADER:
+		return write_core(buffer, count, callback_fct, lcd_device);
+		break;
+	default:
+		return write_core(buffer, count, callback_fct, def_device);
+		break;
+	}
+	/* should never get here */
+	return retval;
+
+}
+
+int write_core(unsigned char *buffer, int count,
+	       void *callback_fct, int device_num)
+{
+	struct usb_skel *dev;
+	int retval = count;
+
+	struct usb_interface *interface;
+	struct urb *urb = NULL;
+	char *buf = NULL;
+	interface = usb_find_interface(&cmdir_driver,
+			USB_CMDIR_MINOR_BASE + device_num);
+	if (!interface) {
+		/* also check without adding base, for devfs */
+		interface = usb_find_interface(&cmdir_driver, device_num);
+		if (!interface)
+			return -ENODEV;
+	}
+	dev = usb_get_intfdata(interface);
+	if (!dev)
+		return -ENODEV;
+	/* create a urb, and a buffer for it, and copy the data to the urb */
+	urb = usb_alloc_urb(0, GFP_ATOMIC);	/* Now -=Atomic=- */
+	if (!urb) {
+		retval = -ENOMEM;
+		goto error;
+	}
+	buf = usb_buffer_alloc(dev->udev, count,
+		GFP_KERNEL, &urb->transfer_dma);
+	if (!buf) {
+		retval = -ENOMEM;
+		goto error;
+	}
+	if (!memcpy(buf, buffer, count)) {
+		retval = -EFAULT;
+		goto error;
+	}
+	/* initialize the urb properly */
+	if (callback_fct == NULL) {
+		usb_fill_bulk_urb(urb, dev->udev,
+			  usb_sndbulkpipe(dev->udev,
+				dev->bulk_out_endpointAddr),
+			  buf, count, (void *) cmdir_write_bulk_callback, dev);
+	} else {
+		usb_fill_bulk_urb(urb, dev->udev,
+			  usb_sndbulkpipe(dev->udev,
+				dev->bulk_out_endpointAddr),
+			  buf, count, callback_fct, dev);
+	}
+	urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;  /* double check this */
+
+	/* send the data out the bulk port */
+	retval = usb_submit_urb(urb, GFP_KERNEL);
+	if (retval) {
+		err("%s - failed submitting write urb, error %d",
+		    __func__, retval);
+		goto error;
+	}
+
+	/* release our reference to this urb, the USB
+	 * core will eventually free it entirely */
+	usb_free_urb(urb);
+	return count;
+
+error:
+	usb_buffer_free(dev->udev, count, buf, urb->transfer_dma);
+	usb_free_urb(urb);
+	return retval;
+}
+
+static void cmdir_write_bulk_callback(struct urb *urb, struct pt_regs *regs)
+{
+	struct usb_skel *dev;
+	dev = (struct usb_skel *)urb->context;
+	send_status = SEND_IDLE;
+	if (debug_commandir == 1)
+		printk(KERN_INFO "callback()\n");
+	/* free up our allocated buffer */
+
+	usb_buffer_free(urb->dev, urb->transfer_buffer_length,
+			urb->transfer_buffer, urb->transfer_dma);
+	send_queue(); /* send the next packet */
+
+}
+
+int set_tx_channels(unsigned int next_tx)
+{
+	next_transmitters = next_tx;
+	return 0;
+}
+EXPORT_SYMBOL(set_tx_channels);
+
+module_init(usb_cmdir_init);
+module_exit(usb_cmdir_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/lirc/commandir.h b/drivers/input/lirc/commandir.h
new file mode 100644
index 0000000..bed703d
--- /dev/null
+++ b/drivers/input/lirc/commandir.h
@@ -0,0 +1,68 @@
+/*
+ *  commandir.h
+ */
+
+#define ASCII0      48
+
+/* transmitter channel control */
+#define MAX_DEVICES      8
+#define MAX_CHANNELS     32
+#define TX1_ENABLE       0x80
+#define TX2_ENABLE       0x40
+#define TX3_ENABLE       0x20
+#define TX4_ENABLE       0x10
+
+/* command types */
+#define cNothing	0
+#define cRESET		1
+#define cFLASH		2
+#define cLCD		3
+#define cRX		4
+
+/* CommandIR control codes */
+#define MCU_CTRL_SIZE   3
+#define FREQ_HEADER     2
+#define RESET_HEADER    3
+#define FLASH_HEADER    4
+#define LCD_HEADER      5
+#define TX_HEADER       7
+#define TX_HEADER_NEW   8
+
+/* Queue buffering constants */
+#define SEND_IDLE	0
+#define SEND_ACTIVE	1
+
+#define QUEUELENGTH	256
+
+extern int cmdir_write(unsigned char *buffer, int count,
+		       void *callback_fct, int u);
+extern ssize_t cmdir_read(unsigned char *buffer, size_t count);
+extern int set_tx_channels(unsigned int next_tx);
+
+
+static int cmdir_open(struct inode *inode, struct file *file);
+static int cmdir_probe(struct usb_interface *interface,
+		       const struct usb_device_id *id);
+static void cmdir_disconnect(struct usb_interface *interface);
+static int cmdir_release(struct inode *inode, struct file *file);
+static int cmdir_check(int device_num);
+static void init_cmdir_var(int device_num);
+static void reset_cmdir(int device_num);
+static void update_cmdir_string(int device_num);
+static void print_cmdir(int device_num);
+static ssize_t cmdir_file_read(struct file *file, char *buffer,
+			       size_t count, loff_t *ppos);
+ssize_t cmdir_read(unsigned char *buffer, size_t count);
+static ssize_t cmdir_file_write(struct file *file, const char *buffer,
+				size_t count, loff_t *ppos);
+int cmdir_write(unsigned char *buffer, int count, void *callback_fct, int u);
+int write_core(unsigned char *buffer, int count,
+	       void *callback_fct, int device_num);
+static void cmdir_write_bulk_callback(struct urb *urb, struct pt_regs *regs);
+int set_tx_channels(unsigned int next_tx);
+
+int add_cmdir_queue(unsigned char *buffer, int count,
+		    void *callback_vct, int usecdelay);
+int cmdir_write_queue(unsigned char *buffer, int count, void *callback_vct);
+int send_queue(void);
+int wait_to_tx(int usecs);
diff --git a/drivers/input/lirc/lirc.h b/drivers/input/lirc/lirc.h
new file mode 100644
index 0000000..dcdb6e8
--- /dev/null
+++ b/drivers/input/lirc/lirc.h
@@ -0,0 +1,103 @@
+/*
+ * lirc.h - linux infrared remote control header file
+ * last modified 2007/09/27
+ */
+
+#ifndef _LINUX_LIRC_H
+#define _LINUX_LIRC_H
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+#define PULSE_BIT  0x01000000
+#define PULSE_MASK 0x00FFFFFF
+
+/*
+ * lirc compatible hardware features
+ */
+
+
+#define LIRC_MODE2SEND(x) (x)
+#define LIRC_SEND2MODE(x) (x)
+#define LIRC_MODE2REC(x) ((x) << 16)
+#define LIRC_REC2MODE(x) ((x) >> 16)
+
+#define LIRC_MODE_RAW                  0x00000001
+#define LIRC_MODE_PULSE                0x00000002
+#define LIRC_MODE_MODE2                0x00000004
+#define LIRC_MODE_CODE                 0x00000008
+#define LIRC_MODE_LIRCCODE             0x00000010
+#define LIRC_MODE_STRING               0x00000020
+
+
+#define LIRC_CAN_SEND_RAW              LIRC_MODE2SEND(LIRC_MODE_RAW)
+#define LIRC_CAN_SEND_PULSE            LIRC_MODE2SEND(LIRC_MODE_PULSE)
+#define LIRC_CAN_SEND_MODE2            LIRC_MODE2SEND(LIRC_MODE_MODE2)
+#define LIRC_CAN_SEND_CODE             LIRC_MODE2SEND(LIRC_MODE_CODE)
+#define LIRC_CAN_SEND_LIRCCODE         LIRC_MODE2SEND(LIRC_MODE_LIRCCODE)
+#define LIRC_CAN_SEND_STRING           LIRC_MODE2SEND(LIRC_MODE_STRING)
+
+#define LIRC_CAN_SEND_MASK             0x0000003f
+
+#define LIRC_CAN_SET_SEND_CARRIER      0x00000100
+#define LIRC_CAN_SET_SEND_DUTY_CYCLE   0x00000200
+#define LIRC_CAN_SET_TRANSMITTER_MASK  0x00000400
+
+#define LIRC_CAN_REC_RAW               LIRC_MODE2REC(LIRC_MODE_RAW)
+#define LIRC_CAN_REC_PULSE             LIRC_MODE2REC(LIRC_MODE_PULSE)
+#define LIRC_CAN_REC_MODE2             LIRC_MODE2REC(LIRC_MODE_MODE2)
+#define LIRC_CAN_REC_CODE              LIRC_MODE2REC(LIRC_MODE_CODE)
+#define LIRC_CAN_REC_LIRCCODE          LIRC_MODE2REC(LIRC_MODE_LIRCCODE)
+#define LIRC_CAN_REC_STRING            LIRC_MODE2REC(LIRC_MODE_STRING)
+
+#define LIRC_CAN_REC_MASK              LIRC_MODE2REC(LIRC_CAN_SEND_MASK)
+
+#define LIRC_CAN_SET_REC_CARRIER       (LIRC_CAN_SET_SEND_CARRIER << 16)
+#define LIRC_CAN_SET_REC_DUTY_CYCLE    (LIRC_CAN_SET_SEND_DUTY_CYCLE << 16)
+
+#define LIRC_CAN_SET_REC_DUTY_CYCLE_RANGE 0x40000000
+#define LIRC_CAN_SET_REC_CARRIER_RANGE    0x80000000
+#define LIRC_CAN_GET_REC_RESOLUTION       0x20000000
+
+#define LIRC_CAN_SEND(x) ((x)&LIRC_CAN_SEND_MASK)
+#define LIRC_CAN_REC(x) ((x)&LIRC_CAN_REC_MASK)
+
+#define LIRC_CAN_NOTIFY_DECODE            0x01000000
+
+/*
+ * IOCTL commands for lirc driver
+ */
+
+#define LIRC_GET_FEATURES              _IOR('i', 0x00000000, __u32)
+
+#define LIRC_GET_SEND_MODE             _IOR('i', 0x00000001, __u32)
+#define LIRC_GET_REC_MODE              _IOR('i', 0x00000002, __u32)
+#define LIRC_GET_SEND_CARRIER          _IOR('i', 0x00000003, __u32)
+#define LIRC_GET_REC_CARRIER           _IOR('i', 0x00000004, __u32)
+#define LIRC_GET_SEND_DUTY_CYCLE       _IOR('i', 0x00000005, __u32)
+#define LIRC_GET_REC_DUTY_CYCLE        _IOR('i', 0x00000006, __u32)
+#define LIRC_GET_REC_RESOLUTION        _IOR('i', 0x00000007, __u32)
+
+/* code length in bits, currently only for LIRC_MODE_LIRCCODE */
+#define LIRC_GET_LENGTH                _IOR('i', 0x0000000f, __u32)
+
+#define LIRC_SET_SEND_MODE             _IOW('i', 0x00000011, __u32)
+#define LIRC_SET_REC_MODE              _IOW('i', 0x00000012, __u32)
+/* Note: these can reset the according pulse_width */
+#define LIRC_SET_SEND_CARRIER          _IOW('i', 0x00000013, __u32)
+#define LIRC_SET_REC_CARRIER           _IOW('i', 0x00000014, __u32)
+#define LIRC_SET_SEND_DUTY_CYCLE       _IOW('i', 0x00000015, __u32)
+#define LIRC_SET_REC_DUTY_CYCLE        _IOW('i', 0x00000016, __u32)
+#define LIRC_SET_TRANSMITTER_MASK      _IOW('i', 0x00000017, __u32)
+
+/* to set a range use
+   LIRC_SET_REC_DUTY_CYCLE_RANGE/LIRC_SET_REC_CARRIER_RANGE with the
+   lower bound first and later
+   LIRC_SET_REC_DUTY_CYCLE/LIRC_SET_REC_CARRIER with the upper bound */
+
+#define LIRC_SET_REC_DUTY_CYCLE_RANGE  _IOW('i', 0x0000001e, __u32)
+#define LIRC_SET_REC_CARRIER_RANGE     _IOW('i', 0x0000001f, __u32)
+
+#define LIRC_NOTIFY_DECODE             _IO('i', 0x00000020)
+
+#endif
diff --git a/drivers/input/lirc/lirc_atiusb.c b/drivers/input/lirc/lirc_atiusb.c
new file mode 100644
index 0000000..0e07204
--- /dev/null
+++ b/drivers/input/lirc/lirc_atiusb.c
@@ -0,0 +1,1321 @@
+/* lirc_atiusb - USB remote support for LIRC
+ * (currently only supports X10 USB remotes)
+ * (supports ATI Remote Wonder and ATI Remote Wonder II, too)
+ *
+ * Copyright (C) 2003-2004 Paul Miller <pmiller9@users.sourceforge.net>
+ *
+ * This driver was derived from:
+ *   Vladimir Dergachev <volodya@minspring.com>'s 2002
+ *      "USB ATI Remote support" (input device)
+ *   Adrian Dewhurst <sailor-lk@sailorfrag.net>'s 2002
+ *      "USB StreamZap remote driver" (LIRC)
+ *   Artur Lipowski <alipowski@kki.net.pl>'s 2002
+ *      "lirc_dev" and "lirc_gpio" LIRC modules
+ *   Michael Wojciechowski
+ *      initial xbox support
+ *   Vassilis Virvilis <vasvir@iit.demokritos.gr> 2006
+ *      reworked the patch for lirc submission
+ *
+ */
+
+/*
+ * 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>
+
+#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/completion.h>
+#include <linux/uaccess.h>
+#include <linux/usb.h>
+#include <linux/poll.h>
+#include <linux/wait.h>
+#include <linux/list.h>
+
+#include "lirc.h"
+#include "lirc_dev.h"
+
+#define DRIVER_VERSION		"1.71"
+#define DRIVER_AUTHOR		"Paul Miller <pmiller9@users.sourceforge.net>"
+#define DRIVER_DESC		"USB remote driver for LIRC"
+#define DRIVER_NAME		"lirc_atiusb"
+
+#define CODE_LENGTH		(code_length[ir->remote_type])
+#define CODE_MIN_LENGTH		(code_min_length[ir->remote_type])
+#define DECODE_LENGTH		(decode_length[ir->remote_type])
+
+#define RW2_MODENAV_KEYCODE	0x3F
+#define RW2_NULL_MODE		0xFF
+/* Fake (virtual) keycode indicating compass mouse usage */
+#define RW2_MOUSE_KEYCODE	0xFF
+#define RW2_PRESSRELEASE_KEYCODE	0xFE
+
+#define RW2_PRESS_CODE		1
+#define RW2_HOLD_CODE		2
+#define RW2_RELEASE_CODE	0
+
+/* module parameters */
+#ifdef CONFIG_USB_DEBUG
+static int debug = 1;
+#else
+static int debug;
+#endif
+#define dprintk(fmt, args...)					\
+	do {							\
+		if (debug)					\
+			printk(KERN_DEBUG fmt, ## args);	\
+	} while (0)
+
+/* ATI, ATI2, XBOX */
+static const int code_length[] = {5, 3, 6};
+static const int code_min_length[] = {3, 3, 6};
+static const int decode_length[] = {5, 3, 1};
+/* USB_BUFF_LEN must be the maximum value of the code_length array.
+ * It is used for static arrays. */
+#define USB_BUFF_LEN 6
+
+static int mask = 0xFFFF;	/* channel acceptance bit mask */
+static int unique;		/* enable channel-specific codes */
+static int repeat = 10;		/* repeat time in 1/100 sec */
+static int emit_updown;		/* send seperate press/release codes (rw2) */
+static int emit_modekeys; /* send keycodes for aux1-4, pc, and mouse (rw2) */
+static unsigned long repeat_jiffies; /* repeat timeout */
+static int mdeadzone;		/* mouse sensitivity >= 0 */
+static int mgradient = 375;	/* 1000*gradient from cardinal direction */
+
+/* get hi and low bytes of a 16-bits int */
+#define HI(a)			((unsigned char)((a) >> 8))
+#define LO(a)			((unsigned char)((a) & 0xff))
+
+/* lock irctl structure */
+#define IRLOCK			mutex_lock(&ir->lock)
+#define IRUNLOCK		mutex_unlock(&ir->lock)
+
+/* general constants */
+#define SUCCESS			0
+#define SEND_FLAG_IN_PROGRESS	1
+#define SEND_FLAG_COMPLETE	2
+#define FREE_ALL		0xFF
+
+/* endpoints */
+#define EP_KEYS			0
+#define EP_MOUSE		1
+#define EP_MOUSE_ADDR		0x81
+#define EP_KEYS_ADDR		0x82
+
+#define VENDOR_ATI1		0x0bc7
+#define VENDOR_ATI2		0x0471
+#define VENDOR_MS1		0x040b
+#define VENDOR_MS2		0x045e
+#define VENDOR_MS3		0xFFFF
+
+static struct usb_device_id usb_remote_table[] = {
+	/* X10 USB Firecracker Interface */
+	{ USB_DEVICE(VENDOR_ATI1, 0x0002) },
+
+	/* X10 VGA Video Sender */
+	{ USB_DEVICE(VENDOR_ATI1, 0x0003) },
+
+	/* ATI Wireless Remote Receiver */
+	{ USB_DEVICE(VENDOR_ATI1, 0x0004) },
+
+	/* NVIDIA Wireless Remote Receiver */
+	{ USB_DEVICE(VENDOR_ATI1, 0x0005) },
+
+	/* ATI Wireless Remote Receiver */
+	{ USB_DEVICE(VENDOR_ATI1, 0x0006) },
+
+	/* X10 USB Wireless Transceivers */
+	{ USB_DEVICE(VENDOR_ATI1, 0x0007) },
+	{ USB_DEVICE(VENDOR_ATI1, 0x0008) },
+	{ USB_DEVICE(VENDOR_ATI1, 0x0009) },
+	{ USB_DEVICE(VENDOR_ATI1, 0x000A) },
+	{ USB_DEVICE(VENDOR_ATI1, 0x000B) },
+	{ USB_DEVICE(VENDOR_ATI1, 0x000C) },
+	{ USB_DEVICE(VENDOR_ATI1, 0x000D) },
+	{ USB_DEVICE(VENDOR_ATI1, 0x000E) },
+	{ USB_DEVICE(VENDOR_ATI1, 0x000F) },
+
+	/* ATI Remote Wonder 2: Input Device */
+	{ USB_DEVICE(VENDOR_ATI2, 0x0602) },
+
+	/* ATI Remote Wonder 2: Controller (???) */
+	{ USB_DEVICE(VENDOR_ATI2, 0x0603) },
+
+	/* Gamester Xbox DVD Movie Playback Kit IR */
+	{ USB_DEVICE(VENDOR_MS1, 0x6521) },
+
+	/* Microsoft Xbox DVD Movie Playback Kit IR */
+	{ USB_DEVICE(VENDOR_MS2, 0x0284) },
+
+	/* Some chinese manufacterer -- conflicts with the joystick from the
+	 * same manufacterer */
+	{ USB_DEVICE(VENDOR_MS3, 0xFFFF) },
+
+	/* Terminating entry */
+	{ }
+};
+
+
+/* init strings */
+#define USB_OUTLEN		7
+
+static char init1[] = {0x01, 0x00, 0x20, 0x14};
+static char init2[] = {0x01, 0x00, 0x20, 0x14, 0x20, 0x20, 0x20};
+
+struct in_endpt {
+	/* inner link in list of endpoints for the remote specified by ir */
+	struct list_head iep_list_link;
+	struct irctl *ir;
+	struct urb *urb;
+	struct usb_endpoint_descriptor *ep;
+	int type;
+
+	/* buffers and dma */
+	unsigned char *buf;
+	unsigned int len;
+	dma_addr_t dma;
+
+	/* handle repeats */
+	unsigned char old[USB_BUFF_LEN];
+	unsigned long old_jiffies;
+};
+
+struct out_endpt {
+	struct irctl *ir;
+	struct urb *urb;
+	struct usb_endpoint_descriptor *ep;
+
+	/* buffers and dma */
+	unsigned char *buf;
+	dma_addr_t dma;
+
+	/* handle sending (init strings) */
+	int send_flags;
+	wait_queue_head_t wait;
+};
+
+
+/* data structure for each usb remote */
+struct irctl {
+	/* inner link in list of all remotes managed by this module */
+	struct list_head remote_list_link;
+	/* Number of usb interfaces associated with this device */
+	int dev_refcount;
+
+	/* usb */
+	struct usb_device *usbdev;
+	/* Head link to list of all inbound endpoints in this remote */
+	struct list_head iep_listhead;
+	struct out_endpt *out_init;
+	int devnum;
+
+	/* remote type based on usb_device_id tables */
+	enum {
+		ATI1_COMPATIBLE,
+		ATI2_COMPATIBLE,
+		XBOX_COMPATIBLE
+	} remote_type;
+
+	/* rw2 current mode (mirror's remote's state) */
+	int mode;
+
+	/* lirc */
+	struct lirc_plugin *p;
+	int connected;
+
+	/* locking */
+	struct mutex lock;
+};
+
+/* list of all registered devices via the remote_list_link in irctl */
+static struct list_head remote_list;
+
+/* Convenience macros to retrieve a pointer to the surrounding struct from
+ * the given list_head reference within, pointed at by link. */
+#define get_iep_from_link(link) \
+		list_entry((link), struct in_endpt, iep_list_link);
+#define get_irctl_from_link(link) \
+		list_entry((link), struct irctl, remote_list_link);
+
+/* send packet - used to initialize remote */
+static void send_packet(struct out_endpt *oep, u16 cmd, unsigned char *data)
+{
+	struct irctl *ir = oep->ir;
+	DECLARE_WAITQUEUE(wait, current);
+	int timeout = HZ; /* 1 second */
+	unsigned char buf[USB_OUTLEN];
+
+	dprintk(DRIVER_NAME "[%d]: send called (%#x)\n", ir->devnum, cmd);
+
+	IRLOCK;
+	oep->urb->transfer_buffer_length = LO(cmd) + 1;
+	oep->urb->dev = oep->ir->usbdev;
+	oep->send_flags = SEND_FLAG_IN_PROGRESS;
+
+	memcpy(buf+1, data, LO(cmd));
+	buf[0] = HI(cmd);
+	memcpy(oep->buf, buf, LO(cmd)+1);
+
+	set_current_state(TASK_INTERRUPTIBLE);
+	add_wait_queue(&oep->wait, &wait);
+
+	if (usb_submit_urb(oep->urb, GFP_ATOMIC)) {
+		set_current_state(TASK_RUNNING);
+		remove_wait_queue(&oep->wait, &wait);
+		IRUNLOCK;
+		return;
+	}
+	IRUNLOCK;
+
+	while (timeout && (oep->urb->status == -EINPROGRESS)
+	       && !(oep->send_flags & SEND_FLAG_COMPLETE)) {
+		timeout = schedule_timeout(timeout);
+		rmb();
+	}
+
+	dprintk(DRIVER_NAME "[%d]: send complete (%#x)\n", ir->devnum, cmd);
+
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&oep->wait, &wait);
+	usb_unlink_urb(oep->urb);
+}
+
+static int unregister_from_lirc(struct irctl *ir)
+{
+	struct lirc_plugin *p = ir->p;
+	int devnum;
+
+	devnum = ir->devnum;
+	dprintk(DRIVER_NAME "[%d]: unregister from lirc called\n", devnum);
+
+	lirc_unregister_plugin(p->minor);
+
+	printk(DRIVER_NAME "[%d]: usb remote disconnected\n", devnum);
+	return SUCCESS;
+}
+
+
+static int set_use_inc(void *data)
+{
+	struct irctl *ir = data;
+	struct list_head *pos, *n;
+	struct in_endpt *iep;
+	int rtn;
+
+	if (!ir) {
+		printk(DRIVER_NAME "[?]: set_use_inc called with no context\n");
+		return -EIO;
+	}
+	dprintk(DRIVER_NAME "[%d]: set use inc\n", ir->devnum);
+
+	IRLOCK;
+	if (!ir->connected) {
+		if (!ir->usbdev) {
+			IRUNLOCK;
+			dprintk(DRIVER_NAME "[%d]: !ir->usbdev\n", ir->devnum);
+			return -ENOENT;
+		}
+
+		/* Iterate through the inbound endpoints */
+		list_for_each_safe(pos, n, &ir->iep_listhead) {
+			/* extract the current in_endpt */
+			iep = get_iep_from_link(pos);
+			iep->urb->dev = ir->usbdev;
+			dprintk(DRIVER_NAME "[%d]: linking iep 0x%02x (%p)\n",
+				ir->devnum, iep->ep->bEndpointAddress, iep);
+			rtn = usb_submit_urb(iep->urb, GFP_ATOMIC);
+			if (rtn) {
+				printk(DRIVER_NAME "[%d]: open result = %d "
+				       "error submitting urb\n",
+				       ir->devnum, rtn);
+				IRUNLOCK;
+				return -EIO;
+			}
+		}
+		ir->connected = 1;
+	}
+	IRUNLOCK;
+
+	return SUCCESS;
+}
+
+static void set_use_dec(void *data)
+{
+	struct irctl *ir = data;
+	struct list_head *pos, *n;
+	struct in_endpt *iep;
+
+	if (!ir) {
+		printk(DRIVER_NAME "[?]: set_use_dec called with no context\n");
+		return;
+	}
+	dprintk(DRIVER_NAME "[%d]: set use dec\n", ir->devnum);
+
+	IRLOCK;
+	if (ir->connected) {
+		/* Free inbound usb urbs */
+		list_for_each_safe(pos, n, &ir->iep_listhead) {
+			iep = get_iep_from_link(pos);
+			dprintk(DRIVER_NAME "[%d]: unlinking iep 0x%02x (%p)\n",
+				ir->devnum, iep->ep->bEndpointAddress, iep);
+			usb_kill_urb(iep->urb);
+		}
+		ir->connected = 0;
+	}
+	IRUNLOCK;
+}
+
+static void print_data(struct in_endpt *iep, char *buf, int len)
+{
+	const int clen = code_length[iep->ir->remote_type];
+	char codes[clen * 3 + 1];
+	int i;
+
+	if (len <= 0)
+		return;
+
+	for (i = 0; i < len && i < clen; i++)
+		snprintf(codes+i*3, 4, "%02x ", buf[i] & 0xFF);
+	printk(DRIVER_NAME "[%d]: data received %s (ep=0x%x length=%d)\n",
+		iep->ir->devnum, codes, iep->ep->bEndpointAddress, len);
+}
+
+static int code_check_ati1(struct in_endpt *iep, int len)
+{
+	struct irctl *ir = iep->ir;
+	int i, chan;
+
+	/* ATI RW1: some remotes emit both 4 and 5 byte length codes. */
+	/* ATI RW2: emit 3 byte codes */
+	if (len < CODE_MIN_LENGTH || len > CODE_LENGTH)
+		return -1;
+
+	/* *** channel not tested with 4/5-byte Dutch remotes *** */
+	chan = ((iep->buf[len-1]>>4) & 0x0F);
+
+	/* strip channel code */
+	if (!unique) {
+		iep->buf[len-1] &= 0x0F;
+		iep->buf[len-3] -= (chan<<4);
+	}
+
+	if (!((1U<<chan) & mask)) {
+		dprintk(DRIVER_NAME "[%d]: ignore channel %d\n",
+			ir->devnum, chan+1);
+		return -1;
+	}
+	dprintk(DRIVER_NAME "[%d]: accept channel %d\n", ir->devnum, chan+1);
+
+	if (ir->remote_type == ATI1_COMPATIBLE) {
+		for (i = len; i < CODE_LENGTH; i++)
+			iep->buf[i] = 0;
+		/* check for repeats */
+		if (memcmp(iep->old, iep->buf, len) == 0) {
+			if (iep->old_jiffies + repeat_jiffies > jiffies)
+				return -1;
+		} else
+			memcpy(iep->old, iep->buf, CODE_LENGTH);
+		iep->old_jiffies = jiffies;
+	}
+
+	return SUCCESS;
+}
+
+/*
+ * Since the ATI Remote Wonder II has quite a different structure from the
+ * prior version, this function was seperated out to clarify the sanitization
+ * process.
+ *
+ * Here is a summary of the main differences:
+ *
+ * a. The rw2 has no sense of a transmission channel.  But, it does have an
+ *    auxilliary mode state, which is set by the mode buttons Aux1 through
+ *    Aux4 and "PC".  These map respectively to 0-4 in the first byte of the
+ *    recv buffer.  Any subsequent button press sends this mode number as its
+ *    "channel code."  Annoyingly enough, the mode setting buttons all send
+ *    the same key code (0x3f), and can only be distinguished via their mode
+ *    byte.
+ *
+ *    Because of this, old-style "unique"-parameter-enabled channel squashing
+ *    kills the functionality of the aux1-aux4 and PC buttons.  However, to
+ *    not do so would cause each remote key to send a different code depending
+ *    on the active aux.  Further complicating matters, using the mouse norb
+ *    also sends an identical code as would pushing the active aux button.  To
+ *    handle this we need a seperate parameter, like rw2modes, with the
+ *    following values and meanings:
+ *
+ *	0: Don't squash any channel info
+ *	1: Only squash channel data for non-mode setting keys
+ *	2: Ignore aux keypresses, but don't squash channel
+ *	3: Ignore aux keypresses and squash channel data
+ *
+ *    Option 1 may seem useless since the mouse sends the same code, but one
+ *    need only ignore in userspace any press of a mode-setting code that only
+ *    reaffirms the current mode.  The 3rd party lirccd should be able to
+ *    handle this easily enough, but lircd doesn't keep the state necessary
+ *    for this.  TODO We could work around this in the driver by emitting a
+ *    single 02 (press) code for a mode key only if that mode is not currently
+ *    active.
+ *
+ *    Option 2 would be useful for those wanting super configurability,
+ *    offering the ability to program 5 times the number actions based on the
+ *    current mode.
+ *
+ * b. The rw2 has its own built in repeat handling; the keys endpoint
+ *    encodes this in the second byte as 1 for press, 2 for hold, and 0 for
+ *    release.  This is generally much more responsive than lirc's built-in
+ *    timeout handling.
+ *
+ *    The problem is that the remote can send the release-recieve pair
+ *    (0,1) while one is still holding down the same button if the
+ *    transmission is momentarilly interrupted.  (It seems that the receiver
+ *    manages this count instead of the remote.)  By default, this information
+ *    is squashed to 2.
+ *
+ *    In order to expose the built-in repeat code, set the emit_updown
+ *    parameter as described below.
+ *
+ * c. The mouse norb is much more sensitive than on the rw1.  It emulates
+ *    a joystick-like controller with the second byte representing the x-axis
+ *    and the third, the y-axis.  Treated as signed integers, these axes range
+ *    approximately as follows:
+ *
+ *	x: (left) -46 ... 46 (right) (0xd2..0x2e)
+ *	y: (up)   -46 ... 46 (down)  (0xd2..0x2e)
+ *
+ *    NB these values do not correspond to the pressure with which the mouse
+ *    norb is pushed in a given direction, but rather seems to indicate the
+ *    duration for which a given direction is held.
+ *
+ *    These are normalized to 8 cardinal directions for easy configuration via
+ *    lircd.conf.  The normalization can be fined tuned with the mdeadzone and
+ *    mgradient parameters as described below.
+ *
+ * d. The interrupt rate of the mouse vs. the normal keys is different.
+ *
+ *	mouse: ~27Hz (37ms between interrupts)
+ *	keys:  ~10Hz (100ms between interrupts)
+ *
+ *    This means that the normal gap mechanism for lircd won't work as
+ *    expected; is emit_updown>0 if you can get away with it.
+ */
+static int code_check_ati2(struct in_endpt *iep, int len)
+{
+	struct irctl *ir = iep->ir;
+	int mode, i;
+	char *buf = iep->buf;
+
+	if (len != CODE_LENGTH) {
+		dprintk(DRIVER_NAME
+			"[%d]: Huh?  Abnormal length (%d) buffer recieved.\n",
+			ir->devnum, len);
+		return -1;
+	}
+	for (i = len; i < CODE_LENGTH; i++)
+		iep->buf[i] = 0;
+
+	mode = buf[0];
+
+	/* Squash the mode indicator if unique wasn't set non-zero */
+	if (!unique)
+		buf[0] = 0;
+
+	if (iep->ep->bEndpointAddress == EP_KEYS_ADDR) {
+		/* ignore mouse navigation indicator key and
+		 * mode-set (aux) keys */
+		if (buf[2] == RW2_MODENAV_KEYCODE) {
+			if (emit_modekeys >= 2) /* emit raw */
+				buf[0] = mode;
+			else if (emit_modekeys == 1) {
+				/* translate */
+				buf[0] = mode;
+				if (ir->mode != mode) {
+					buf[1] = 0x03;
+					ir->mode = mode;
+					return SUCCESS;
+				}
+			} else {
+				dprintk(DRIVER_NAME
+					"[%d]: ignore dummy code 0x%x "
+					"(ep=0x%x)\n", ir->devnum,
+					buf[2], iep->ep->bEndpointAddress);
+				return -1;
+			}
+		}
+
+		if (buf[1] != 2) {
+			/* handle press/release codes */
+			if (emit_updown == 0) /* ignore */
+				return -1;
+			else if (emit_updown == 1) /* normalize keycode */
+				 buf[2] = RW2_PRESSRELEASE_KEYCODE;
+			/* else emit raw */
+		}
+
+	} else {
+		int x = (signed char)buf[1];
+		int y = (signed char)buf[2];
+		int code = 0x00;
+		int dir_ew, dir_ns;
+
+		buf[2] = RW2_MOUSE_KEYCODE;
+
+		/* sensitivity threshold (use L2norm^2) */
+		if (mdeadzone > (x*x+y*y)) {
+			buf[1] = 0x00;
+			return SUCCESS;
+		}
+
+/* Nybble encoding: xy, 2 is -1 (S or W); 1 (N or E) */
+#define MOUSE_N		0x01
+#define MOUSE_NE	0x11
+#define MOUSE_E		0x10
+#define MOUSE_SE	0x12
+#define MOUSE_S		0x02
+#define MOUSE_SW	0x22
+#define MOUSE_W		0x20
+#define MOUSE_NW	0x21
+
+		/* cardinal leanings: positive x -> E, positive y -> S */
+		dir_ew = (x > 0) ? MOUSE_E : MOUSE_W;
+		dir_ns = (y > 0) ? MOUSE_S : MOUSE_N;
+
+		/* convert coordintes(angle) into compass direction */
+		if (x == 0)
+			code = dir_ns;
+		else if (y == 0)
+			code = dir_ew;
+		else {
+			if (abs(1000*y/x) > mgradient)
+				code = dir_ns;
+			if (abs(1000*x/y) > mgradient)
+				code |= dir_ew;
+		}
+
+		buf[1] = code;
+		dprintk(DRIVER_NAME "[%d]: mouse compass=0x%x %s%s (%d,%d)\n",
+			ir->devnum, code,
+			(code & MOUSE_S ? "S" : (code & MOUSE_N ? "N" : "")),
+			(code & MOUSE_E ? "E" : (code & MOUSE_W ? "W" : "")),
+			x, y);
+	}
+
+	return SUCCESS;
+}
+
+static int code_check_xbox(struct in_endpt *iep, int len)
+{
+	struct irctl *ir = iep->ir;
+	const int clen = CODE_LENGTH;
+
+	if (len != clen) {
+		dprintk(DRIVER_NAME ": We got %d instead of %d bytes from xbox "
+			"ir.. ?\n", len, clen);
+		return -1;
+	}
+
+	/* check for repeats */
+	if (memcmp(iep->old, iep->buf, len) == 0) {
+		if (iep->old_jiffies + repeat_jiffies > jiffies)
+			return -1;
+	} else {
+		/* the third byte of xbox ir packet seems to contain key info
+		 * the last two bytes are.. some kind of clock? */
+		iep->buf[0] = iep->buf[2];
+		memset(iep->buf + 1, 0, len - 1);
+		memcpy(iep->old, iep->buf, len);
+	}
+	iep->old_jiffies = jiffies;
+
+	return SUCCESS;
+}
+
+static void usb_remote_recv(struct urb *urb)
+{
+	struct in_endpt *iep;
+	int len, result = -1;
+
+	if (!urb)
+		return;
+	iep = urb->context;
+	if (!iep) {
+		usb_unlink_urb(urb);
+		return;
+	}
+	if (!iep->ir->usbdev)
+		return;
+
+	len = urb->actual_length;
+	if (debug)
+		print_data(iep, urb->transfer_buffer, len);
+
+	switch (urb->status) {
+
+	/* success */
+	case SUCCESS:
+		switch (iep->ir->remote_type) {
+		case XBOX_COMPATIBLE:
+			result = code_check_xbox(iep, len);
+			break;
+		case ATI2_COMPATIBLE:
+			result = code_check_ati2(iep, len);
+			break;
+		case ATI1_COMPATIBLE:
+		default:
+			result = code_check_ati1(iep, len);
+		}
+		if (result < 0)
+			break;
+		lirc_buffer_write_1(iep->ir->p->rbuf, iep->buf);
+		wake_up(&iep->ir->p->rbuf->wait_poll);
+		break;
+
+	/* unlink */
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		usb_unlink_urb(urb);
+		return;
+
+	case -EPIPE:
+	default:
+		break;
+	}
+
+	/* resubmit urb */
+	usb_submit_urb(urb, GFP_ATOMIC);
+}
+
+static void usb_remote_send(struct urb *urb)
+{
+	struct out_endpt *oep;
+
+	if (!urb)
+		return;
+	oep = urb->context;
+	if (!oep) {
+		usb_unlink_urb(urb);
+		return;
+	}
+	if (!oep->ir->usbdev)
+		return;
+
+	dprintk(DRIVER_NAME "[%d]: usb out called\n", oep->ir->devnum);
+
+	if (urb->status)
+		return;
+
+	oep->send_flags |= SEND_FLAG_COMPLETE;
+	wmb();
+	if (waitqueue_active(&oep->wait))
+		wake_up(&oep->wait);
+}
+
+
+/***************************************************************************
+ * Initialization and removal
+ ***************************************************************************/
+
+/*
+ * Free iep according to mem_failure which specifies a checkpoint into the
+ * initialization sequence for rollback recovery.
+ */
+static void free_in_endpt(struct in_endpt *iep, int mem_failure)
+{
+	struct irctl *ir;
+	dprintk(DRIVER_NAME ": free_in_endpt(%p, %d)\n", iep, mem_failure);
+	if (!iep)
+		return;
+
+	ir = iep->ir;
+	if (!ir) {
+		dprintk(DRIVER_NAME ": free_in_endpt: WARNING! null ir\n");
+		return;
+	}
+	IRLOCK;
+	switch (mem_failure) {
+	case FREE_ALL:
+	case 5:
+		list_del(&iep->iep_list_link);
+		dprintk(DRIVER_NAME "[%d]: free_in_endpt removing ep=0x%0x "
+			"from list\n", ir->devnum, iep->ep->bEndpointAddress);
+	case 4:
+		if (iep->urb) {
+			usb_unlink_urb(iep->urb);
+			usb_free_urb(iep->urb);
+			iep->urb = 0;
+		} else
+			dprintk(DRIVER_NAME "[%d]: free_in_endpt null urb!\n",
+				ir->devnum);
+	case 3:
+		usb_buffer_free(iep->ir->usbdev, iep->len, iep->buf, iep->dma);
+		iep->buf = 0;
+	case 2:
+		kfree(iep);
+	}
+	IRUNLOCK;
+}
+
+/*
+ * Construct a new inbound endpoint for this remote, and add it to the list of
+ * in_epts in ir.
+ */
+static struct in_endpt *new_in_endpt(struct irctl *ir,
+				     struct usb_endpoint_descriptor *ep)
+{
+	struct usb_device *dev = ir->usbdev;
+	struct in_endpt *iep;
+	int pipe, maxp, len, addr;
+	int mem_failure;
+
+	addr = ep->bEndpointAddress;
+	pipe = usb_rcvintpipe(dev, addr);
+	maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
+
+/*	len = (maxp > USB_BUFLEN) ? USB_BUFLEN : maxp;
+ *	len -= (len % CODE_LENGTH); */
+	len = CODE_LENGTH;
+
+	dprintk(DRIVER_NAME "[%d]: acceptable inbound endpoint (0x%x) found "
+		"(maxp=%d len=%d)\n", ir->devnum, addr, maxp, len);
+
+	mem_failure = 0;
+	iep = kmalloc(sizeof(*iep), GFP_KERNEL);
+	if (!iep) {
+		mem_failure = 1;
+		goto new_in_endpt_failure_check;
+	}
+	memset(iep, 0, sizeof(*iep));
+	iep->ir = ir;
+	iep->ep = ep;
+	iep->len = len;
+
+	iep->buf = usb_buffer_alloc(dev, len, GFP_ATOMIC, &iep->dma);
+	if (!iep->buf) {
+		mem_failure = 2;
+		goto new_in_endpt_failure_check;
+	}
+
+	iep->urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!iep->urb)
+		mem_failure = 3;
+
+new_in_endpt_failure_check:
+
+	if (mem_failure) {
+		free_in_endpt(iep, mem_failure);
+		printk(DRIVER_NAME "[%d]: ep=0x%x out of memory (code=%d)\n",
+		       ir->devnum, addr, mem_failure);
+		return NULL;
+	}
+	list_add_tail(&iep->iep_list_link, &ir->iep_listhead);
+	dprintk(DRIVER_NAME "[%d]: adding ep=0x%0x to list\n",
+		ir->devnum, iep->ep->bEndpointAddress);
+	return iep;
+}
+
+static void free_out_endpt(struct out_endpt *oep, int mem_failure)
+{
+	struct irctl *ir;
+	dprintk(DRIVER_NAME ": free_out_endpt(%p, %d)\n", oep, mem_failure);
+	if (!oep)
+		return;
+
+	wake_up_all(&oep->wait);
+
+	ir = oep->ir;
+	if (!ir) {
+		dprintk(DRIVER_NAME ": free_out_endpt: WARNING! null ir\n");
+		return;
+	}
+	IRLOCK;
+	switch (mem_failure) {
+	case FREE_ALL:
+	case 4:
+		if (oep->urb) {
+			usb_unlink_urb(oep->urb);
+			usb_free_urb(oep->urb);
+			oep->urb = 0;
+		} else {
+			dprintk(DRIVER_NAME "[%d]: free_out_endpt: null urb!\n",
+				ir->devnum);
+		}
+	case 3:
+		usb_buffer_free(oep->ir->usbdev, USB_OUTLEN,
+				oep->buf, oep->dma);
+		oep->buf = 0;
+	case 2:
+		kfree(oep);
+	}
+	IRUNLOCK;
+}
+
+static struct out_endpt *new_out_endpt(struct irctl *ir,
+				       struct usb_endpoint_descriptor *ep)
+{
+	struct usb_device *dev = ir->usbdev;
+	struct out_endpt *oep;
+	int mem_failure;
+
+	dprintk(DRIVER_NAME "[%d]: acceptable outbound endpoint (0x%x) found\n",
+		ir->devnum, ep->bEndpointAddress);
+
+	mem_failure = 0;
+	oep = kmalloc(sizeof(*oep), GFP_KERNEL);
+	if (!oep)
+		mem_failure = 1;
+	else {
+		memset(oep, 0, sizeof(*oep));
+		oep->ir = ir;
+		oep->ep = ep;
+		init_waitqueue_head(&oep->wait);
+
+		oep->buf = usb_buffer_alloc(dev, USB_OUTLEN,
+					    GFP_ATOMIC, &oep->dma);
+		if (!oep->buf)
+			mem_failure = 2;
+		else {
+			oep->urb = usb_alloc_urb(0, GFP_KERNEL);
+			if (!oep->urb)
+				mem_failure = 3;
+		}
+	}
+	if (mem_failure) {
+		free_out_endpt(oep, mem_failure);
+		printk(DRIVER_NAME "[%d]: ep=0x%x out of memory (code=%d)\n",
+		       ir->devnum, ep->bEndpointAddress, mem_failure);
+		return NULL;
+	}
+	return oep;
+}
+
+static void free_irctl(struct irctl *ir, int mem_failure)
+{
+	struct list_head *pos, *n;
+	struct in_endpt *in;
+	dprintk(DRIVER_NAME ": free_irctl(%p, %d)\n", ir, mem_failure);
+
+	if (!ir)
+		return;
+
+	list_for_each_safe(pos, n, &ir->iep_listhead) {
+		in = get_iep_from_link(pos);
+		free_in_endpt(in, FREE_ALL);
+	}
+	if (ir->out_init) {
+		free_out_endpt(ir->out_init, FREE_ALL);
+		ir->out_init = NULL;
+	}
+
+	IRLOCK;
+	switch (mem_failure) {
+	case FREE_ALL:
+	case 6:
+		if (!--ir->dev_refcount) {
+			list_del(&ir->remote_list_link);
+			dprintk(DRIVER_NAME "[%d]: free_irctl: removing "
+				"remote from list\n", ir->devnum);
+		} else {
+			dprintk(DRIVER_NAME "[%d]: free_irctl: refcount at %d,"
+				"aborting free_irctl\n",
+				ir->devnum, ir->dev_refcount);
+			IRUNLOCK;
+			return;
+		}
+	case 5:
+	case 4:
+	case 3:
+		if (ir->p) {
+			switch (mem_failure) {
+			case 5:
+				lirc_buffer_free(ir->p->rbuf);
+			case 4:
+				kfree(ir->p->rbuf);
+			case 3:
+				kfree(ir->p);
+			}
+		} else
+			printk(DRIVER_NAME "[%d]: ir->p is a null pointer!\n",
+			       ir->devnum);
+	case 2:
+		IRUNLOCK;
+		kfree(ir);
+		return;
+	}
+	IRUNLOCK;
+}
+
+static struct irctl *new_irctl(struct usb_device *dev)
+{
+	struct irctl *ir;
+	struct lirc_plugin *plugin;
+	int type, devnum, dclen;
+	int mem_failure;
+
+	devnum = dev->devnum;
+
+	/* determine remote type */
+	switch (cpu_to_le16(dev->descriptor.idVendor)) {
+	case VENDOR_ATI1:
+		type = ATI1_COMPATIBLE;
+		break;
+	case VENDOR_ATI2:
+		type = ATI2_COMPATIBLE;
+		break;
+	case VENDOR_MS1:
+	case VENDOR_MS2:
+	case VENDOR_MS3:
+		type = XBOX_COMPATIBLE;
+		break;
+	default:
+		dprintk(DRIVER_NAME "[%d]: unknown type\n", devnum);
+		return NULL;
+	}
+	dprintk(DRIVER_NAME "[%d]: remote type = %d\n", devnum, type);
+
+	/* allocate kernel memory */
+	mem_failure = 0;
+	ir = kmalloc(sizeof(*ir), GFP_KERNEL);
+	if (!ir) {
+		mem_failure = 1;
+		goto new_irctl_failure_check;
+	}
+
+	/* at this stage we cannot use the macro [DE]CODE_LENGTH: ir
+	 * is not yet setup */
+	dclen = decode_length[type];
+	memset(ir, 0, sizeof(*ir));
+	/* add this infrared remote struct to remote_list, keeping track
+	 * of the number of drivers registered. */
+	dprintk(DRIVER_NAME "[%d]: adding remote to list\n", devnum);
+	list_add_tail(&ir->remote_list_link, &remote_list);
+	ir->dev_refcount = 1;
+
+	plugin = kmalloc(sizeof(*plugin), GFP_KERNEL);
+	if (!plugin) {
+		mem_failure = 2;
+		goto new_irctl_failure_check;
+	}
+
+	memset(plugin, 0, sizeof(*plugin));
+	ir->p = plugin;
+	plugin->rbuf = kmalloc(sizeof(*(plugin->rbuf)), GFP_KERNEL);
+	if (!plugin->rbuf) {
+		mem_failure = 3;
+		goto new_irctl_failure_check;
+	}
+
+	if (lirc_buffer_init(plugin->rbuf, dclen, 1)) {
+		mem_failure = 4;
+		goto new_irctl_failure_check;
+	}
+
+	strcpy(plugin->name, DRIVER_NAME " ");
+	plugin->minor = -1;
+	plugin->code_length = dclen * 8;
+	plugin->features = LIRC_CAN_REC_LIRCCODE;
+	plugin->data = ir;
+	plugin->set_use_inc = &set_use_inc;
+	plugin->set_use_dec = &set_use_dec;
+	plugin->dev = &dev->dev;
+	plugin->owner = THIS_MODULE;
+	ir->usbdev = dev;
+	ir->remote_type = type;
+	ir->devnum = devnum;
+	ir->mode = RW2_NULL_MODE;
+
+	mutex_init(&ir->lock);
+	INIT_LIST_HEAD(&ir->iep_listhead);
+
+new_irctl_failure_check:
+
+	if (mem_failure) {
+		free_irctl(ir, mem_failure);
+		printk(DRIVER_NAME "[%d]: out of memory (code=%d)\n",
+		       devnum, mem_failure);
+		return NULL;
+	}
+	return ir;
+}
+
+
+/*
+ * Scan the global list of remotes to see if the device listed is one of them.
+ * If it is, the corresponding irctl is returned, with its dev_refcount
+ * incremented.  Otherwise, returns null.
+ */
+static struct irctl *get_prior_reg_ir(struct usb_device *dev)
+{
+	struct list_head *pos;
+	struct irctl *ir = NULL;
+
+	dprintk(DRIVER_NAME "[%d]: scanning remote_list...\n", dev->devnum);
+	list_for_each(pos, &remote_list) {
+		ir = get_irctl_from_link(pos);
+		if (ir->usbdev != dev) {
+			dprintk(DRIVER_NAME "[%d]: device %d isn't it...",
+				dev->devnum, ir->devnum);
+		    ir = NULL;
+		} else {
+			dprintk(DRIVER_NAME "[%d]: prior instance found.\n",
+				dev->devnum);
+			ir->dev_refcount++;
+			break;
+		}
+	}
+	return ir;
+}
+
+/* If the USB interface has an out endpoint for control (eg, the first Remote
+ * Wonder) send the appropriate initialization packets. */
+static void send_outbound_init(struct irctl *ir)
+{
+	if (ir->out_init) {
+		struct out_endpt *oep = ir->out_init;
+		dprintk(DRIVER_NAME "[%d]: usb_remote_probe: initializing "
+			"outbound ep\n", ir->devnum);
+		usb_fill_int_urb(oep->urb, ir->usbdev,
+			usb_sndintpipe(ir->usbdev, oep->ep->bEndpointAddress),
+			oep->buf, USB_OUTLEN, usb_remote_send,
+			oep, oep->ep->bInterval);
+
+		send_packet(oep, 0x8004, init1);
+		send_packet(oep, 0x8007, init2);
+	}
+}
+
+/* Log driver and usb info */
+static void log_usb_dev_info(struct usb_device *dev)
+{
+	char buf[63], name[128] = "";
+	if (dev->descriptor.iManufacturer
+	    && usb_string(dev, dev->descriptor.iManufacturer, buf, 63) > 0)
+		strncpy(name, buf, 128);
+	if (dev->descriptor.iProduct
+	    && usb_string(dev, dev->descriptor.iProduct, buf, 63) > 0)
+		snprintf(name, 128, "%s %s", name, buf);
+	printk(DRIVER_NAME "[%d]: %s on usb%d:%d\n", dev->devnum, name,
+	       dev->bus->busnum, dev->devnum);
+}
+
+
+static int usb_remote_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;
+	struct in_endpt *iep;
+	struct irctl *ir;
+	int i, type;
+
+	dprintk(DRIVER_NAME "[%d]: usb_remote_probe: dev:%p, intf:%p, id:%p)\n",
+		dev->devnum, dev, intf, id);
+
+	idesc = intf->cur_altsetting;
+
+	/* Check if a usb remote has already been registered for this device */
+	ir = get_prior_reg_ir(dev);
+
+	if (!ir) {
+		ir = new_irctl(dev);
+		if (!ir)
+			return -ENOMEM;
+	}
+	type = ir->remote_type;
+
+	/* step through the endpoints to find first in and first out endpoint
+	 * of type interrupt transfer */
+	for (i = 0; i < idesc->desc.bNumEndpoints; ++i) {
+		ep = &idesc->endpoint[i].desc;
+		dprintk(DRIVER_NAME "[%d]: processing endpoint %d\n",
+			dev->devnum, i);
+		if (((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) ==
+		     USB_DIR_IN) &&
+		     ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
+		      USB_ENDPOINT_XFER_INT)) {
+
+			iep = new_in_endpt(ir, ep);
+			if (iep)
+				usb_fill_int_urb(iep->urb, dev,
+					usb_rcvintpipe(dev,
+						iep->ep->bEndpointAddress),
+					iep->buf, iep->len, usb_remote_recv,
+					iep, iep->ep->bInterval);
+		}
+
+		if (((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) ==
+		     USB_DIR_OUT) &&
+		     ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
+		      USB_ENDPOINT_XFER_INT) &&
+		      (ir->out_init == NULL))
+			ir->out_init = new_out_endpt(ir, ep);
+	}
+	if (list_empty(&ir->iep_listhead)) {
+		printk(DRIVER_NAME "[%d]: inbound endpoint not found\n",
+		       ir->devnum);
+		free_irctl(ir, FREE_ALL);
+		return -ENODEV;
+	}
+	if (ir->dev_refcount == 1) {
+		ir->p->minor = lirc_register_plugin(ir->p);
+		if (ir->p->minor < 0) {
+			free_irctl(ir, FREE_ALL);
+			return -ENODEV;
+		}
+
+		/* Note new driver registration in kernel logs */
+		log_usb_dev_info(dev);
+
+		/* outbound data (initialization) */
+		send_outbound_init(ir);
+	}
+
+	usb_set_intfdata(intf, ir);
+	return SUCCESS;
+}
+
+static void usb_remote_disconnect(struct usb_interface *intf)
+{
+/*	struct usb_device *dev = interface_to_usbdev(intf); */
+	struct irctl *ir = usb_get_intfdata(intf);
+	usb_set_intfdata(intf, NULL);
+
+	dprintk(DRIVER_NAME ": disconnecting remote %d:\n",
+		ir ? ir->devnum : -1);
+	if (!ir || !ir->p)
+		return;
+
+	if (ir->usbdev) {
+		/* Only unregister once */
+		ir->usbdev = NULL;
+		unregister_from_lirc(ir);
+	}
+
+	/* This also removes the current remote from remote_list */
+	free_irctl(ir, FREE_ALL);
+}
+
+static struct usb_driver usb_remote_driver = {
+	.name		= DRIVER_NAME,
+	.probe		= usb_remote_probe,
+	.disconnect	= usb_remote_disconnect,
+	.id_table	= usb_remote_table
+};
+
+static int __init usb_remote_init(void)
+{
+	int i;
+
+	INIT_LIST_HEAD(&remote_list);
+
+	printk(KERN_INFO "\n" DRIVER_NAME ": " DRIVER_DESC " "
+	       DRIVER_VERSION "\n");
+	printk(DRIVER_NAME ": " DRIVER_AUTHOR "\n");
+	dprintk(DRIVER_NAME ": debug mode enabled\n");
+
+#ifdef MODULE
+	request_module("lirc_dev");
+#endif
+
+	repeat_jiffies = repeat*HZ/100;
+
+	i = usb_register(&usb_remote_driver);
+	if (i) {
+		printk(DRIVER_NAME ": usb register failed, result = %d\n", i);
+		return -ENODEV;
+	}
+
+	return SUCCESS;
+}
+
+static void __exit usb_remote_exit(void)
+{
+	usb_deregister(&usb_remote_driver);
+}
+
+#ifdef MODULE
+module_init(usb_remote_init);
+module_exit(usb_remote_exit);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(usb, usb_remote_table);
+
+module_param(debug, bool, 0644);
+MODULE_PARM_DESC(debug, "Debug enabled or not (default: 0)");
+
+module_param(mask, int, 0644);
+MODULE_PARM_DESC(mask, "Set channel acceptance bit mask (default: 0xFFFF)");
+
+module_param(unique, bool, 0644);
+MODULE_PARM_DESC(unique, "Enable channel-specific codes (default: 0)");
+
+module_param(repeat, int, 0644);
+MODULE_PARM_DESC(repeat, "Repeat timeout (1/100 sec) (default: 10)");
+
+module_param(mdeadzone, int, 0644);
+MODULE_PARM_DESC(mdeadzone, "rw2 mouse sensitivity threshold (default: 0)");
+
+/*
+ * Enabling this will cause the built-in Remote Wonder II repeate coding to
+ * not be squashed.  The second byte of the keys output will then be:
+ *
+ *	1 initial press (button down)
+ *	2 holding (button remains pressed)
+ *	0 release (button up)
+ *
+ * By default, the driver emits 2 for both 1 and 2, and emits nothing for 0.
+ * This is good for people having trouble getting their rw2 to send a good
+ * consistent signal to the receiver.
+ *
+ * However, if you have no troubles with the driver outputting up-down pairs
+ * at random points while you're still holding a button, then you can enable
+ * this parameter to get finer grain repeat control out of your remote:
+ *
+ *	1 Emit a single (per-channel) virtual code for all up/down events
+ *	2 Emit the actual rw2 output
+ *
+ * 1 is easier to write lircd configs for; 2 allows full control.
+ */
+module_param(emit_updown, int, 0644);
+MODULE_PARM_DESC(emit_updown, "emit press/release codes (rw2): 0:don't "
+		 "(default), 1:emit 2 codes only, 2:code for each button");
+
+module_param(emit_modekeys, int, 0644);
+MODULE_PARM_DESC(emit_modekeys, "emit keycodes for aux1-aux4, pc, and mouse "
+		 "(rw2): 0:don't (default), 1:emit translated codes: one for "
+		 "mode switch, one for same mode, 2:raw codes");
+
+module_param(mgradient, int, 0644);
+MODULE_PARM_DESC(mgradient, "rw2 mouse: 1000*gradient from E to NE (default: "
+		 "500 => .5 => ~27 degrees)");
+
+#else /* not MODULE */
+subsys_initcall(usb_remote_init);
+
+#endif /* MODULE */
+
diff --git a/drivers/input/lirc/lirc_bt829.c b/drivers/input/lirc/lirc_bt829.c
new file mode 100644
index 0000000..01cbdfe
--- /dev/null
+++ b/drivers/input/lirc/lirc_bt829.c
@@ -0,0 +1,388 @@
+/*
+ * Remote control driver for the TV-card based on bt829
+ *
+ *  by Leonid Froenchenko <lfroen@galileo.co.il>
+ *
+ *  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>
+
+#include <linux/autoconf.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/threads.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+
+#include "lirc_dev.h"
+
+static int poll_main(void);
+static int atir_init_start(void);
+
+static void write_index(unsigned char index, unsigned int value);
+static unsigned int read_index(unsigned char index);
+
+static void do_i2c_start(void);
+static void do_i2c_stop(void);
+
+static void seems_wr_byte(unsigned char al);
+static unsigned char seems_rd_byte(void);
+
+static unsigned int read_index(unsigned char al);
+static void write_index(unsigned char ah, unsigned int edx);
+
+static void cycle_delay(int cycle);
+
+static void do_set_bits(unsigned char bl);
+static unsigned char do_get_bits(void);
+
+#define DATA_PCI_OFF 0x7FFC00
+#define WAIT_CYCLE   20
+
+static int debug;
+#define dprintk(fmt, args...)					\
+	do {							\
+		if (debug)					\
+			printk(KERN_DEBUG fmt, ## args);	\
+	} while (0)
+
+static int atir_minor;
+static unsigned long pci_addr_phys;
+static unsigned char *pci_addr_lin;
+
+static struct lirc_plugin atir_plugin;
+
+static struct pci_dev *do_pci_probe(void)
+{
+	struct pci_dev *my_dev;
+	my_dev = pci_get_device(PCI_VENDOR_ID_ATI,
+				PCI_DEVICE_ID_ATI_264VT, NULL);
+	if (my_dev) {
+		printk(KERN_ERR "ATIR: Using device: %s\n",
+		       pci_name(my_dev));
+		pci_addr_phys = 0;
+		if (my_dev->resource[0].flags & IORESOURCE_MEM) {
+			pci_addr_phys = my_dev->resource[0].start;
+			printk(KERN_INFO "ATIR memory at 0x%08X \n",
+			       (unsigned int)pci_addr_phys);
+		}
+		if (pci_addr_phys == 0) {
+			printk(KERN_ERR "ATIR no memory resource ?\n");
+			return NULL;
+		}
+	} else {
+		printk(KERN_ERR "ATIR: pci_prob failed\n");
+		return NULL;
+	}
+	return my_dev;
+}
+
+static int atir_add_to_buf(void *data, struct lirc_buffer *buf)
+{
+	unsigned char key;
+	int status;
+	status = poll_main();
+	key = (status >> 8) & 0xFF;
+	if (status & 0xFF) {
+		dprintk("ATIR reading key %02X\n", key);
+		lirc_buffer_write_1(buf, &key);
+		return 0;
+	}
+	return -ENODATA;
+}
+
+static int atir_set_use_inc(void *data)
+{
+	dprintk("ATIR driver is opened\n");
+	return 0;
+}
+
+static void atir_set_use_dec(void *data)
+{
+	dprintk("ATIR driver is closed\n");
+}
+
+int init_module(void)
+{
+	struct pci_dev *pdev;
+
+	pdev = do_pci_probe();
+	if (pdev == NULL)
+		return 1;
+
+	if (!atir_init_start())
+		return 1;
+
+	strcpy(atir_plugin.name, "ATIR");
+	atir_plugin.minor       = -1;
+	atir_plugin.code_length = 8;
+	atir_plugin.sample_rate = 10;
+	atir_plugin.data        = 0;
+	atir_plugin.add_to_buf  = atir_add_to_buf;
+	atir_plugin.set_use_inc = atir_set_use_inc;
+	atir_plugin.set_use_dec = atir_set_use_dec;
+	atir_plugin.dev         = &pdev->dev;
+	atir_plugin.owner       = THIS_MODULE;
+
+	atir_minor = lirc_register_plugin(&atir_plugin);
+	dprintk("ATIR driver is registered on minor %d\n", atir_minor);
+
+	return 0;
+}
+
+
+void cleanup_module(void)
+{
+	lirc_unregister_plugin(atir_minor);
+}
+
+
+static int atir_init_start(void)
+{
+	pci_addr_lin = ioremap(pci_addr_phys + DATA_PCI_OFF, 0x400);
+	if (pci_addr_lin == 0) {
+		printk(KERN_INFO "atir: pci mem must be mapped\n");
+		return 0;
+	}
+	return 1;
+}
+
+static void cycle_delay(int cycle)
+{
+	udelay(WAIT_CYCLE*cycle);
+}
+
+
+static int poll_main()
+{
+	unsigned char status_high, status_low;
+
+	do_i2c_start();
+
+	seems_wr_byte(0xAA);
+	seems_wr_byte(0x01);
+
+	do_i2c_start();
+
+	seems_wr_byte(0xAB);
+
+	status_low = seems_rd_byte();
+	status_high = seems_rd_byte();
+
+	do_i2c_stop();
+
+	return (status_high << 8) | status_low;
+}
+
+static void do_i2c_start(void)
+{
+	do_set_bits(3);
+	cycle_delay(4);
+
+	do_set_bits(1);
+	cycle_delay(7);
+
+	do_set_bits(0);
+	cycle_delay(2);
+}
+
+static void do_i2c_stop(void)
+{
+	unsigned char bits;
+	bits =  do_get_bits() & 0xFD;
+	do_set_bits(bits);
+	cycle_delay(1);
+
+	bits |= 1;
+	do_set_bits(bits);
+	cycle_delay(2);
+
+	bits |= 2;
+	do_set_bits(bits);
+	bits = 3;
+	do_set_bits(bits);
+	cycle_delay(2);
+}
+
+static void seems_wr_byte(unsigned char value)
+{
+	int i;
+	unsigned char reg;
+
+	reg = do_get_bits();
+	for (i = 0; i < 8; i++) {
+		if (value & 0x80)
+			reg |= 0x02;
+		else
+			reg &= 0xFD;
+
+		do_set_bits(reg);
+		cycle_delay(1);
+
+		reg |= 1;
+		do_set_bits(reg);
+		cycle_delay(1);
+
+		reg &= 0xFE;
+		do_set_bits(reg);
+		cycle_delay(1);
+		value <<= 1;
+	}
+	cycle_delay(2);
+
+	reg |= 2;
+	do_set_bits(reg);
+
+	reg |= 1;
+	do_set_bits(reg);
+
+	cycle_delay(1);
+	do_get_bits();
+
+	reg &= 0xFE;
+	do_set_bits(reg);
+	cycle_delay(3);
+}
+
+static unsigned char seems_rd_byte(void)
+{
+	int i;
+	int rd_byte;
+	unsigned char bits_2, bits_1;
+
+	bits_1 = do_get_bits() | 2;
+	do_set_bits(bits_1);
+
+	rd_byte = 0;
+	for (i = 0; i < 8; i++) {
+		bits_1 &= 0xFE;
+		do_set_bits(bits_1);
+		cycle_delay(2);
+
+		bits_1 |= 1;
+		do_set_bits(bits_1);
+		cycle_delay(1);
+
+		bits_2 = do_get_bits();
+		if (bits_2 & 2)
+			rd_byte |= 1;
+
+		rd_byte <<= 1;
+	}
+
+	bits_1 = 0;
+	if (bits_2 == 0)
+		bits_1 |= 2;
+
+	do_set_bits(bits_1);
+	cycle_delay(2);
+
+	bits_1 |= 1;
+	do_set_bits(bits_1);
+	cycle_delay(3);
+
+	bits_1 &= 0xFE;
+	do_set_bits(bits_1);
+	cycle_delay(2);
+
+	rd_byte >>= 1;
+	rd_byte &= 0xFF;
+	return rd_byte;
+}
+
+static void do_set_bits(unsigned char new_bits)
+{
+	int reg_val;
+	reg_val = read_index(0x34);
+	if (new_bits & 2) {
+		reg_val &= 0xFFFFFFDF;
+		reg_val |= 1;
+	} else {
+		reg_val &= 0xFFFFFFFE;
+		reg_val |= 0x20;
+	}
+	reg_val |= 0x10;
+	write_index(0x34, reg_val);
+
+	reg_val = read_index(0x31);
+	if (new_bits & 1)
+		reg_val |= 0x1000000;
+	else
+		reg_val &= 0xFEFFFFFF;
+
+	reg_val |= 0x8000000;
+	write_index(0x31, reg_val);
+}
+
+static unsigned char do_get_bits(void)
+{
+	unsigned char bits;
+	int reg_val;
+
+	reg_val = read_index(0x34);
+	reg_val |= 0x10;
+	reg_val &= 0xFFFFFFDF;
+	write_index(0x34, reg_val);
+
+	reg_val = read_index(0x34);
+	bits = 0;
+	if (reg_val & 8)
+		bits |= 2;
+	else
+		bits &= 0xFD;
+
+	reg_val = read_index(0x31);
+	if (reg_val & 0x1000000)
+		bits |= 1;
+	else
+		bits &= 0xFE;
+
+	return bits;
+}
+
+static unsigned int read_index(unsigned char index)
+{
+	unsigned char *addr;
+	unsigned int value;
+	/*  addr = pci_addr_lin + DATA_PCI_OFF + ((index & 0xFF) << 2); */
+	addr = pci_addr_lin + ((index & 0xFF) << 2);
+	value = readl(addr);
+	return value;
+}
+
+static void write_index(unsigned char index, unsigned int reg_val)
+{
+	unsigned char *addr;
+	addr = pci_addr_lin + ((index & 0xFF) << 2);
+	writel(reg_val, addr);
+}
+
+MODULE_AUTHOR("Froenchenko Leonid");
+MODULE_DESCRIPTION("IR remote driver for bt829 based TV cards");
+MODULE_LICENSE("GPL");
+
+module_param(debug, bool, 0644);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/input/lirc/lirc_cmdir.c b/drivers/input/lirc/lirc_cmdir.c
new file mode 100644
index 0000000..1faa8e3
--- /dev/null
+++ b/drivers/input/lirc/lirc_cmdir.c
@@ -0,0 +1,596 @@
+/*
+ * lirc_cmdir.c - Driver for InnovationOne's COMMANDIR USB Transceiver
+ *
+ *  This driver requires the COMMANDIR hardware driver, available at
+ *  http://www.commandir.com/.
+ *
+ *  Copyright (C) 2005  InnovationOne - Evelyn Yeung
+ *
+ *  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>
+
+#include <linux/autoconf.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/time.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+#include <linux/poll.h>
+#include "lirc.h"
+#include "lirc_dev.h"
+#include "lirc_cmdir.h"
+
+static int debug;
+#define dprintk(fmt, args...)					\
+	do {							\
+		if (debug)					\
+			printk(KERN_DEBUG fmt, ## args);	\
+	} while (0)
+
+struct lirc_cmdir {
+	int features;
+};
+
+struct lirc_cmdir hardware = {
+	(
+	/* LIRC_CAN_SET_SEND_DUTY_CYCLE|   */
+	LIRC_CAN_SET_SEND_CARRIER|
+	LIRC_CAN_SEND_PULSE|
+	LIRC_CAN_SET_TRANSMITTER_MASK|
+	LIRC_CAN_REC_MODE2)
+	,
+};
+
+#define LIRC_DRIVER_NAME "lirc_cmdir"
+#define RBUF_LEN   256
+#define WBUF_LEN   256
+#define MAX_PACKET 64
+
+static struct lirc_buffer rbuf;
+static int wbuf[WBUF_LEN];
+static unsigned char cmdir_char[4*WBUF_LEN];
+static unsigned char write_control[MCU_CTRL_SIZE];
+static unsigned int last_mc_time;
+static int usb_status = ON;
+static unsigned char signal_num;
+char timerval;
+
+unsigned int freq = 38000;
+/* unsigned int duty_cycle = 50; */
+
+
+#ifndef MAX_UDELAY_MS
+#define MAX_UDELAY_US 5000
+#else
+#define MAX_UDELAY_US (MAX_UDELAY_MS*1000)
+#endif
+
+static inline void safe_udelay(unsigned long usecs)
+{
+	while (usecs > MAX_UDELAY_US) {
+		udelay(MAX_UDELAY_US);
+		usecs -= MAX_UDELAY_US;
+	}
+	udelay(usecs);
+}
+
+static unsigned int get_time_value(unsigned int firstint,
+			unsigned int secondint, unsigned char overflow)
+{	/* get difference between two timestamps from MCU */
+	unsigned int t_answer = 0;
+
+	if (secondint > firstint) {
+		t_answer = secondint - firstint + overflow*65536;
+	} else {
+		if (overflow > 0)
+			t_answer = (65536 - firstint) + secondint +
+						(overflow - 1) * 65536;
+		else
+			t_answer = (65536 - firstint) + secondint;
+	}
+
+	/* clamp to long signal  */
+	if (t_answer > 16000000)
+		t_answer = PULSE_MASK;
+
+	return t_answer;
+}
+
+
+static int set_use_inc(void *data)
+{
+	/* Init read buffer. */
+	if (lirc_buffer_init(&rbuf, sizeof(int), RBUF_LEN) < 0)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static void set_use_dec(void *data)
+{
+	lirc_buffer_free(&rbuf);
+}
+
+
+static void usb_error_handle(int retval)
+{
+	switch (retval) {
+	case -ENODEV:
+		/* device has been unplugged */
+		if (usb_status == ON) {
+			usb_status = OFF;
+			printk(LIRC_DRIVER_NAME ": device is unplugged\n");
+		}
+		break;
+	default:
+		printk(LIRC_DRIVER_NAME ": usb error = %d\n", retval);
+		break;
+	}
+}
+
+static int write_to_usb(unsigned char *buffer, int count, int time_elapsed)
+{
+	int write_return;
+
+	write_return = cmdir_write(buffer, count, NULL, time_elapsed);
+	if (write_return != count) {
+		usb_error_handle(write_return);
+	} else {
+		if (usb_status == OFF) {
+			printk(LIRC_DRIVER_NAME ": device is now plugged in\n");
+			usb_status = ON;
+		}
+	}
+	return write_return;
+}
+
+static void set_freq(void)
+{
+	/* float tempfreq = 0.0; */
+	int write_return;
+
+	/*
+	 * Can't use floating point in 2.6 kernel!
+	 * May be some loss of precision
+	 */
+	timerval = (1000000 / freq) / 2;
+	write_control[0] = FREQ_HEADER;
+	write_control[1] = timerval;
+	write_control[2] = 0;
+	write_return = write_to_usb(write_control, MCU_CTRL_SIZE, 0);
+	if (write_return == MCU_CTRL_SIZE)
+		printk(LIRC_DRIVER_NAME ": freq set to %dHz\n", freq);
+	else
+		printk(LIRC_DRIVER_NAME ": freq unchanged\n");
+
+}
+
+static int cmdir_convert_RX(unsigned char *orig_rxbuffer)
+{
+	unsigned char tmp_char_buffer[80];
+	unsigned int tmp_int_buffer[20];
+	unsigned int final_data_buffer[20];
+	unsigned int num_data_values = 0;
+	unsigned char num_data_bytes = 0;
+	unsigned int orig_index = 0;
+	int i;
+
+	for (i = 0; i < 80; i++)
+		tmp_char_buffer[i] = 0;
+	for (i = 0; i < 20; i++)
+		tmp_int_buffer[i] = 0;
+
+	/*
+	 * get number of data bytes that follow the control bytes
+	 * (NOT including them)
+	 */
+	num_data_bytes = orig_rxbuffer[1];
+
+	/* check if num_bytes is multiple of 3; if not, error */
+	if (num_data_bytes % 3 > 0)
+		return -1;
+	if (num_data_bytes > 60)
+		return -3;
+	if (num_data_bytes < 3)
+		return -2;
+
+	/*
+	 * get number of ints to be returned; num_data_bytes does
+	 * NOT include control bytes
+	 */
+	num_data_values = num_data_bytes/3;
+
+	for (i = 0; i < num_data_values; i++) {
+		tmp_char_buffer[i*4] = orig_rxbuffer[(i+1)*3];
+		tmp_char_buffer[i*4+1] = orig_rxbuffer[(i+1)*3+1];
+		tmp_char_buffer[i*4+2] = 0;
+		tmp_char_buffer[i*4+3] = 0;
+	}
+
+	/* convert to int array */
+	memcpy((unsigned char *)tmp_int_buffer, tmp_char_buffer,
+						(num_data_values*4));
+
+	if (orig_rxbuffer[5] < 255) {
+		/* space */
+		final_data_buffer[0] = get_time_value(last_mc_time,
+						tmp_int_buffer[0],
+						orig_rxbuffer[5]);
+	} else { /* is pulse */
+		final_data_buffer[0] = get_time_value(last_mc_time,
+						tmp_int_buffer[0],
+						0);
+		final_data_buffer[0] |= PULSE_BIT;
+	}
+	for (i = 1; i < num_data_values; i++) {
+		/*
+		 * index of orig_rxbuffer that corresponds to
+		 * overflow/pulse/space
+		 */
+		orig_index = (i + 1)*3 + 2;
+		if (orig_rxbuffer[orig_index] < 255) {
+			final_data_buffer[i] =
+				get_time_value(tmp_int_buffer[i - 1],
+					       tmp_int_buffer[i],
+					       orig_rxbuffer[orig_index]);
+		} else {
+			final_data_buffer[i] =
+				get_time_value(tmp_int_buffer[i - 1],
+					       tmp_int_buffer[i],
+					       0);
+			final_data_buffer[i] |= PULSE_BIT;
+		}
+	}
+	last_mc_time = tmp_int_buffer[num_data_values - 1];
+
+	if (lirc_buffer_full(&rbuf)) {
+		printk(KERN_ERR  LIRC_DRIVER_NAME ": lirc_buffer is full\n");
+		return -EOVERFLOW;
+	}
+	lirc_buffer_write_n(&rbuf, (char *)final_data_buffer, num_data_values);
+
+	return 0;
+}
+
+
+static int usb_read_once(void)
+{
+	int read_retval = 0;
+	int conv_retval = 0;
+	unsigned char read_buffer[MAX_PACKET];
+	int i = 0;
+	int tooFull = 5;  /* read up to 5 packets */
+
+	for (i = 0; i < MAX_PACKET; i++)
+		read_buffer[i] = 0;
+
+	while (tooFull--) {
+		read_retval = cmdir_read(read_buffer, MAX_PACKET);
+		/* Loop until we unload the data build-up */
+		if (read_buffer[1] < 60)
+			tooFull = 0;
+		if (!(read_retval == MAX_PACKET)) {
+			if (read_retval == -ENODEV) {
+				if (usb_status == ON) {
+					printk(KERN_ALERT LIRC_DRIVER_NAME
+						": device is unplugged\n");
+					usb_status = OFF;
+				}
+			} else {
+				/* supress errors */
+				printk(KERN_ALERT LIRC_DRIVER_NAME
+					": usb error on read = %d\n",
+					read_retval);
+				return -ENODATA;
+			}
+			dprintk("Error 3\n");
+			return -ENODATA;
+		} else {
+			if (usb_status == OFF) {
+				usb_status = ON;
+				printk(LIRC_DRIVER_NAME
+					": device is now plugged in\n");
+			}
+		}
+
+		if (read_buffer[0] & 0x08) {
+			conv_retval = cmdir_convert_RX(read_buffer);
+			if (conv_retval == 0) {
+				if (!tooFull)
+					return 0;
+				else
+					dprintk("Looping for more data...\n");
+			} else {
+				dprintk("Error 2: %d\n", (int)conv_retval);
+				return -ENODATA;
+			}
+		} else {
+			/* There really is no data in their buffer */
+			dprintk("Empty RX Buffer!\n");
+			return -ENODATA;
+		}
+	}
+	return -1;
+}
+
+int add_to_buf(void *data, struct lirc_buffer *buf)
+{
+	return usb_read_once();
+}
+
+
+static ssize_t lirc_write(struct file *file, const char *buf,
+			 size_t n, loff_t *ppos)
+{
+	int i, count;
+	unsigned int mod_signal_length = 0;
+	unsigned int time_elapse = 0;
+	unsigned int total_time_elapsed = 0;
+	unsigned int num_bytes_already_sent = 0;
+	unsigned int hibyte = 0;
+	unsigned int lobyte = 0;
+	int cmdir_cnt = 0;
+	unsigned int wait_this = 0;
+	struct timeval start_time;
+	struct timeval end_time;
+	unsigned int real_time_elapsed = 0;
+
+	/* save the time we started the write: */
+	do_gettimeofday(&start_time);
+
+	if (n % sizeof(int))
+		return -EINVAL;
+
+	count = n / sizeof(int);
+	if (count > WBUF_LEN || count % 2 == 0)
+		return -EINVAL;
+	if (copy_from_user(wbuf, buf, n))
+		return -EFAULT;
+
+	/*
+	 * the first time we have to flag that this is the start of a new
+	 * signal otherwise COMMANDIR may receive 2 back-to-back pulses &
+	 * invert the signal
+	 */
+	cmdir_char[0] = TX_HEADER_NEW;
+	signal_num++;
+	cmdir_char[1] = signal_num;
+	cmdir_cnt = 2;
+	for (i = 0; i < count; i++) {
+		/* conversion to number of modulation frequency pulse edges */
+		mod_signal_length = wbuf[i] >> 3;
+		/*
+		 * account for minor rounding errors -
+		 * calculate length from this:
+		 */
+		time_elapse += mod_signal_length * timerval;
+
+		hibyte = mod_signal_length / 256;
+		lobyte = mod_signal_length % 256;
+		cmdir_char[cmdir_cnt+1] = lobyte;
+		cmdir_char[cmdir_cnt] = hibyte;
+		cmdir_cnt += 2;
+
+		/* write data to usb if full packet is collected */
+		if (cmdir_cnt % MAX_PACKET == 0) {
+			write_to_usb(cmdir_char, MAX_PACKET,  time_elapse);
+
+			total_time_elapsed += time_elapse;
+
+			num_bytes_already_sent += MAX_PACKET;
+			time_elapse = 0;
+
+			if ((i + 1) < count) {
+				/* still more to send: */
+				cmdir_char[0] =	TX_HEADER;  /* Next Packet */
+				cmdir_char[1] = signal_num;
+				cmdir_cnt = 2; /* reset the count */
+			}
+		}
+	}
+
+	/* send last chunk of data */
+	if (cmdir_cnt > 0) {
+		total_time_elapsed += time_elapse;
+		write_to_usb(cmdir_char, cmdir_cnt, time_elapse);
+	}
+	/* XXX ERS remove all this? */
+	/*
+	 * we need to _manually delay ourselves_ to remain backwards
+	 * compatible with LIRC and prevent our queue buffer from overflowing.
+	 * Queuing in this driver is about instant, and send_start for example
+	 * will fill it up quickly and prevent send_stop from taking immediate
+	 * effect.
+	 */
+	dprintk("Total elapsed time is: %d. \n", total_time_elapsed);
+	do_gettimeofday(&end_time);
+	/*
+	 * udelay for the difference between endtime and
+	 * start + total_time_elapsed
+	 */
+	if (start_time.tv_usec < end_time.tv_usec)
+		real_time_elapsed = (end_time.tv_usec - start_time.tv_usec);
+	else
+		real_time_elapsed = ((end_time.tv_usec +  1000000) -
+							start_time.tv_usec);
+	dprintk("Real time elapsed was %u.\n", real_time_elapsed);
+	if (real_time_elapsed < (total_time_elapsed - 1000))
+		wait_this = total_time_elapsed - real_time_elapsed - 1000;
+
+#if 0 /* enable this for backwards compatibility */
+	safe_udelay(wait_this);
+#endif
+
+	return n;
+}
+
+
+static int lirc_ioctl(struct inode *node, struct file *filep, unsigned int cmd,
+		      unsigned long arg)
+{
+	int result;
+	unsigned long value;
+	unsigned int ivalue;
+	unsigned int multiplier = 1;
+	unsigned int mask = 0;
+	int i;
+
+	switch (cmd) {
+	case LIRC_SET_TRANSMITTER_MASK:
+		if (!(hardware.features&LIRC_CAN_SET_TRANSMITTER_MASK))
+			return -ENOIOCTLCMD;
+		result = get_user(ivalue, (unsigned int *) arg);
+		if (result)
+			return result;
+		for (i = 0; i < MAX_CHANNELS; i++) {
+			multiplier = multiplier * 0x10;
+			mask |= multiplier;
+		}
+		if (ivalue >= mask)
+			return MAX_CHANNELS;
+		set_tx_channels(ivalue);
+		return 0;
+		break;
+
+	case LIRC_GET_SEND_MODE:
+		if (!(hardware.features & LIRC_CAN_SEND_MASK))
+			return -ENOIOCTLCMD;
+
+		result = put_user(LIRC_SEND2MODE
+				(hardware.features & LIRC_CAN_SEND_MASK),
+				(unsigned long *) arg);
+		if (result)
+			return result;
+		break;
+
+	case LIRC_SET_SEND_MODE:
+		if (!(hardware.features&LIRC_CAN_SEND_MASK))
+			return -ENOIOCTLCMD;
+
+		result = get_user(value, (unsigned long *)arg);
+		if (result)
+			return result;
+		break;
+
+	case LIRC_GET_LENGTH:
+		return -ENOSYS;
+		break;
+
+	case LIRC_SET_SEND_DUTY_CYCLE:
+		dprintk(KERN_WARNING LIRC_DRIVER_NAME
+					": SET_SEND_DUTY_CYCLE\n");
+
+		if (!(hardware.features&LIRC_CAN_SET_SEND_DUTY_CYCLE))
+			return -ENOIOCTLCMD;
+
+		result = get_user(ivalue, (unsigned int *)arg);
+		if (result)
+			return result;
+		if (ivalue <= 0 || ivalue > 100)
+			return -EINVAL;
+
+		/* TODO: */
+		dprintk(LIRC_DRIVER_NAME
+			": set_send_duty_cycle not yet supported\n");
+
+		return 0;
+		break;
+
+	case LIRC_SET_SEND_CARRIER:
+		dprintk(KERN_WARNING LIRC_DRIVER_NAME ": SET_SEND_CARRIER\n");
+
+		if (!(hardware.features & LIRC_CAN_SET_SEND_CARRIER))
+			return -ENOIOCTLCMD;
+
+		result = get_user(ivalue, (unsigned int *)arg);
+		if (result)
+			return result;
+		if (ivalue > 500000 || ivalue < 24000)
+			return -EINVAL;
+		if (ivalue != freq) {
+			freq = ivalue;
+			set_freq();
+		}
+		return 0;
+		break;
+
+	default:
+		return -ENOIOCTLCMD;
+	}
+	return 0;
+}
+
+static struct file_operations lirc_fops = {
+	.write		= lirc_write,
+};
+
+static struct lirc_plugin plugin = {
+	.name		= LIRC_DRIVER_NAME,
+	.minor		= -1,
+	.code_length	= 1,
+	.sample_rate	= 20,
+	.data		= NULL,
+	.add_to_buf	= add_to_buf,
+	.get_queue	= NULL,
+	.rbuf		= &rbuf,
+	.set_use_inc	= set_use_inc,
+	.set_use_dec	= set_use_dec,
+	.ioctl		= lirc_ioctl,
+	.fops		= &lirc_fops,
+	.dev		= NULL,
+	.owner		= THIS_MODULE,
+};
+
+#ifdef MODULE
+
+MODULE_AUTHOR("Evelyn Yeung, Matt Bodkin");
+MODULE_DESCRIPTION("InnovationOne driver for "
+		   "CommandIR USB infrared transceiver");
+#ifdef MODULE_LICENSE
+MODULE_LICENSE("GPL");
+#endif
+
+module_param(debug, bool, 0644);
+MODULE_PARM_DESC(debug, "Enable debugging messages");
+
+
+int init_module(void)
+{
+	plugin.features = hardware.features;
+	plugin.minor = lirc_register_plugin(&plugin);
+	if (plugin.minor < 0) {
+		printk(KERN_ERR  LIRC_DRIVER_NAME
+		       ": register_chrdev failed!\n");
+		return -EIO;
+	}
+	set_freq();
+	return 0;
+}
+
+void cleanup_module(void)
+{
+	lirc_unregister_plugin(plugin.minor);
+	printk(KERN_INFO  LIRC_DRIVER_NAME  ": module removed\n");
+}
+
+#endif
+
+
diff --git a/drivers/input/lirc/lirc_cmdir.h b/drivers/input/lirc/lirc_cmdir.h
new file mode 100644
index 0000000..f2400c3
--- /dev/null
+++ b/drivers/input/lirc/lirc_cmdir.h
@@ -0,0 +1,25 @@
+/*
+ *   lirc_cmdir.h
+ */
+
+#ifndef LIRC_CMDIR_H
+#define LIRC_CMDIR_H
+
+#define ON          1
+#define OFF         0
+
+/* transmitter channel control */
+#define MAX_CHANNELS     32
+
+/* CommandIR control codes */
+#define MCU_CTRL_SIZE   3
+#define FREQ_HEADER     2
+#define TX_HEADER       7
+#define TX_HEADER_NEW	8
+
+extern int cmdir_write(unsigned char *buffer, int count,
+		       void *callback_fct, int u);
+extern ssize_t cmdir_read(unsigned char *buffer, size_t count);
+extern int set_tx_channels(unsigned int next_tx);
+
+#endif
diff --git a/drivers/input/lirc/lirc_dev.c b/drivers/input/lirc/lirc_dev.c
new file mode 100644
index 0000000..c8f325c
--- /dev/null
+++ b/drivers/input/lirc/lirc_dev.c
@@ -0,0 +1,809 @@
+/*
+ * LIRC base driver
+ *
+ * (L) by Artur Lipowski <alipowski@interia.pl>
+ *
+ *  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>
+
+#include <linux/autoconf.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/ioctl.h>
+#include <linux/fs.h>
+#include <linux/poll.h>
+#include <linux/smp_lock.h>
+#include <linux/completion.h>
+#include <linux/uaccess.h>
+#include <linux/errno.h>
+#include <linux/semaphore.h>
+#define __KERNEL_SYSCALLS__
+#include <linux/unistd.h>
+#include <linux/kthread.h>
+
+/* SysFS header */
+#include <linux/device.h>
+
+#include "lirc.h"
+#include "lirc_dev.h"
+
+static int debug;
+#define dprintk(fmt, args...)					\
+	do {							\
+		if (debug)					\
+			printk(KERN_DEBUG fmt, ## args);	\
+	} while (0)
+
+#define IRCTL_DEV_NAME    "BaseRemoteCtl"
+#define SUCCESS           0
+#define NOPLUG            -1
+#define LOGHEAD           "lirc_dev (%s[%d]): "
+
+struct irctl {
+	struct lirc_plugin p;
+	int attached;
+	int open;
+
+	struct mutex buffer_lock;
+	struct lirc_buffer *buf;
+
+	struct task_struct *task;
+	long jiffies_to_wait;
+
+};
+
+static DEFINE_MUTEX(plugin_lock);
+
+static struct irctl irctls[MAX_IRCTL_DEVICES];
+static struct file_operations fops;
+
+/* Only used for sysfs but defined to void otherwise */
+static struct class *lirc_class;
+
+/*  helper function
+ *  initializes the irctl structure
+ */
+static inline void init_irctl(struct irctl *ir)
+{
+	memset(&ir->p, 0, sizeof(struct lirc_plugin));
+	mutex_init(&ir->buffer_lock);
+	ir->p.minor = NOPLUG;
+
+	ir->task = NULL;
+	ir->jiffies_to_wait = 0;
+
+	ir->open = 0;
+	ir->attached = 0;
+}
+
+static void cleanup(struct irctl *ir)
+{
+	dprintk(LOGHEAD "cleaning up\n", ir->p.name, ir->p.minor);
+
+	device_destroy(lirc_class, MKDEV(IRCTL_DEV_MAJOR, ir->p.minor));
+
+	if (ir->buf != ir->p.rbuf) {
+		lirc_buffer_free(ir->buf);
+		kfree(ir->buf);
+	}
+	ir->buf = NULL;
+
+	init_irctl(ir);
+}
+
+/*  helper function
+ *  reads key codes from plugin and puts them into buffer
+ *  buffer free space is checked and locking performed
+ *  returns 0 on success
+ */
+static inline int add_to_buf(struct irctl *ir)
+{
+	if (lirc_buffer_full(ir->buf)) {
+		dprintk(LOGHEAD "buffer overflow\n",
+			ir->p.name, ir->p.minor);
+		return -EOVERFLOW;
+	}
+
+	if (ir->p.add_to_buf) {
+		int res = -ENODATA;
+		int got_data = 0;
+
+		/* service the device as long as it is returning
+		 * data and we have space
+		 */
+		while (!lirc_buffer_full(ir->buf)) {
+			res = ir->p.add_to_buf(ir->p.data, ir->buf);
+			if (res == SUCCESS)
+				got_data++;
+			else
+				break;
+		}
+
+		if (res == -ENODEV)
+			kthread_stop(ir->task);
+
+		return got_data ? SUCCESS : res;
+	}
+
+	return SUCCESS;
+}
+
+/* main function of the polling thread
+ */
+static int lirc_thread(void *irctl)
+{
+	struct irctl *ir = irctl;
+
+	/* This thread doesn't need any user-level access,
+	 * so get rid of all our resources
+	 */
+
+	dprintk(LOGHEAD "poll thread started\n", ir->p.name, ir->p.minor);
+
+	do {
+		if (ir->open) {
+			if (ir->jiffies_to_wait) {
+				set_current_state(TASK_INTERRUPTIBLE);
+				schedule_timeout(ir->jiffies_to_wait);
+			} else {
+				interruptible_sleep_on(
+					ir->p.get_queue(ir->p.data));
+			}
+			if (kthread_should_stop())
+				break;
+			if (!add_to_buf(ir))
+				wake_up_interruptible(&ir->buf->wait_poll);
+		} else {
+			/* if device not opened so we can sleep half a second */
+			set_current_state(TASK_INTERRUPTIBLE);
+			schedule_timeout(HZ/2);
+		}
+	} while (!kthread_should_stop());
+
+	dprintk(LOGHEAD "poll thread ended\n", ir->p.name, ir->p.minor);
+
+	return 0;
+}
+
+int lirc_register_plugin(struct lirc_plugin *p)
+{
+	struct irctl *ir;
+	int minor;
+	int bytes_in_key;
+	int err;
+	DECLARE_COMPLETION(tn);
+
+	if (!p) {
+		printk(KERN_ERR "lirc_dev: lirc_register_plugin: "
+		       "plugin pointer must be not NULL!\n");
+		err = -EBADRQC;
+		goto out;
+	}
+
+	if (MAX_IRCTL_DEVICES <= p->minor) {
+		printk(KERN_ERR "lirc_dev: lirc_register_plugin: "
+		       "\"minor\" must be between 0 and %d (%d)!\n",
+		       MAX_IRCTL_DEVICES-1, p->minor);
+		err = -EBADRQC;
+		goto out;
+	}
+
+	if (1 > p->code_length || (BUFLEN * 8) < p->code_length) {
+		printk(KERN_ERR "lirc_dev: lirc_register_plugin: "
+		       "code length in bits for minor (%d) "
+		       "must be less than %d!\n",
+		       p->minor, BUFLEN * 8);
+		err = -EBADRQC;
+		goto out;
+	}
+
+	printk(KERN_INFO "lirc_dev: lirc_register_plugin: sample_rate: %d\n",
+		p->sample_rate);
+	if (p->sample_rate) {
+		if (2 > p->sample_rate || HZ < p->sample_rate) {
+			printk(KERN_ERR "lirc_dev: lirc_register_plugin: "
+			       "sample_rate must be between 2 and %d!\n", HZ);
+			err = -EBADRQC;
+			goto out;
+		}
+		if (!p->add_to_buf) {
+			printk(KERN_ERR "lirc_dev: lirc_register_plugin: "
+			       "add_to_buf cannot be NULL when "
+			       "sample_rate is set\n");
+			err = -EBADRQC;
+			goto out;
+		}
+	} else if (!(p->fops && p->fops->read)
+		   && !p->get_queue && !p->rbuf) {
+		printk(KERN_ERR "lirc_dev: lirc_register_plugin: "
+		       "fops->read, get_queue and rbuf "
+		       "cannot all be NULL!\n");
+		err = -EBADRQC;
+		goto out;
+	} else if (!p->get_queue && !p->rbuf) {
+		if (!(p->fops && p->fops->read && p->fops->poll)
+		    || (!p->fops->ioctl && !p->ioctl)) {
+			printk(KERN_ERR "lirc_dev: lirc_register_plugin: "
+			       "neither read, poll nor ioctl can be NULL!\n");
+			err = -EBADRQC;
+			goto out;
+		}
+	}
+
+	if (p->owner == NULL) {
+		printk(KERN_ERR "lirc_dev: lirc_register_plugin: "
+				    "no module owner registered\n");
+		err = -EBADRQC;
+		goto out;
+	}
+
+	mutex_lock(&plugin_lock);
+
+	minor = p->minor;
+
+	if (0 > minor) {
+		/* find first free slot for plugin */
+		for (minor = 0; minor < MAX_IRCTL_DEVICES; minor++)
+			if (irctls[minor].p.minor == NOPLUG)
+				break;
+		if (MAX_IRCTL_DEVICES == minor) {
+			printk(KERN_ERR "lirc_dev: lirc_register_plugin: "
+			       "no free slots for plugins!\n");
+			err = -ENOMEM;
+			goto out_lock;
+		}
+	} else if (irctls[minor].p.minor != NOPLUG) {
+		printk(KERN_ERR "lirc_dev: lirc_register_plugin: "
+		       "minor (%d) just registered!\n", minor);
+		err = -EBUSY;
+		goto out_lock;
+	}
+
+	ir = &irctls[minor];
+
+	if (p->sample_rate) {
+		ir->jiffies_to_wait = HZ / p->sample_rate;
+	} else {
+		/* it means - wait for external event in task queue */
+		ir->jiffies_to_wait = 0;
+	}
+
+	/* some safety check 8-) */
+	p->name[sizeof(p->name)-1] = '\0';
+
+	bytes_in_key = p->code_length/8 + (p->code_length%8 ? 1 : 0);
+
+	if (p->rbuf) {
+		ir->buf = p->rbuf;
+	} else {
+		ir->buf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
+		if (!ir->buf) {
+			err = -ENOMEM;
+			goto out_lock;
+		}
+		if (lirc_buffer_init(ir->buf, bytes_in_key,
+				     BUFLEN/bytes_in_key) != 0) {
+			kfree(ir->buf);
+			err = -ENOMEM;
+			goto out_lock;
+		}
+	}
+
+	if (p->features == 0)
+		p->features = (p->code_length > 8) ?
+			LIRC_CAN_REC_LIRCCODE : LIRC_CAN_REC_CODE;
+
+	ir->p = *p;
+	ir->p.minor = minor;
+
+	device_create(lirc_class, ir->p.dev,
+		      MKDEV(IRCTL_DEV_MAJOR, ir->p.minor), NULL,
+		      "lirc%u", ir->p.minor);
+
+	if (p->sample_rate || p->get_queue) {
+		/* try to fire up polling thread */
+		ir->task = kthread_run(lirc_thread, (void *)ir, "lirc_dev");
+		if (IS_ERR(ir->task)) {
+			printk(KERN_ERR "lirc_dev: lirc_register_plugin: "
+			       "cannot run poll thread for minor = %d\n",
+			       p->minor);
+			err = -ECHILD;
+			goto out_sysfs;
+		}
+	}
+	ir->attached = 1;
+	mutex_unlock(&plugin_lock);
+
+/*
+ * Recent kernels should handle this autmatically by increasing/decreasing
+ * use count when a dependant module is loaded/unloaded.
+ */
+	dprintk("lirc_dev: plugin %s registered at minor number = %d\n",
+		ir->p.name, ir->p.minor);
+	p->minor = minor;
+	return minor;
+
+out_sysfs:
+	device_destroy(lirc_class, MKDEV(IRCTL_DEV_MAJOR, ir->p.minor));
+out_lock:
+	mutex_unlock(&plugin_lock);
+out:
+	return err;
+}
+EXPORT_SYMBOL(lirc_register_plugin);
+
+int lirc_unregister_plugin(int minor)
+{
+	struct irctl *ir;
+	DECLARE_COMPLETION(tn);
+	DECLARE_COMPLETION(tn2);
+
+	if (minor < 0 || minor >= MAX_IRCTL_DEVICES) {
+		printk(KERN_ERR "lirc_dev: lirc_unregister_plugin: "
+		       "\"minor\" must be between 0 and %d!\n",
+		       MAX_IRCTL_DEVICES-1);
+		return -EBADRQC;
+	}
+
+	ir = &irctls[minor];
+
+	mutex_lock(&plugin_lock);
+
+	if (ir->p.minor != minor) {
+		printk(KERN_ERR "lirc_dev: lirc_unregister_plugin: "
+		       "minor (%d) device not registered!", minor);
+		mutex_unlock(&plugin_lock);
+		return -ENOENT;
+	}
+
+	/* end up polling thread */
+	if (ir->task) {
+		wake_up_process(ir->task);
+		kthread_stop(ir->task);
+	}
+
+	dprintk("lirc_dev: plugin %s unregistered from minor number = %d\n",
+		ir->p.name, ir->p.minor);
+
+	ir->attached = 0;
+	if (ir->open) {
+		dprintk(LOGHEAD "releasing opened plugin\n",
+			ir->p.name, ir->p.minor);
+		wake_up_interruptible(&ir->buf->wait_poll);
+		mutex_lock(&ir->buffer_lock);
+		ir->p.set_use_dec(ir->p.data);
+		module_put(ir->p.owner);
+		mutex_unlock(&ir->buffer_lock);
+	} else
+		cleanup(ir);
+	mutex_unlock(&plugin_lock);
+
+/*
+ * Recent kernels should handle this autmatically by increasing/decreasing
+ * use count when a dependant module is loaded/unloaded.
+ */
+
+	return SUCCESS;
+}
+EXPORT_SYMBOL(lirc_unregister_plugin);
+
+/*
+ *
+ */
+static int irctl_open(struct inode *inode, struct file *file)
+{
+	struct irctl *ir;
+	int retval;
+
+	if (MINOR(inode->i_rdev) >= MAX_IRCTL_DEVICES) {
+		dprintk("lirc_dev [%d]: open result = -ENODEV\n",
+			MINOR(inode->i_rdev));
+		return -ENODEV;
+	}
+
+	ir = &irctls[MINOR(inode->i_rdev)];
+
+	dprintk(LOGHEAD "open called\n", ir->p.name, ir->p.minor);
+
+	/* if the plugin has an open function use it instead */
+	if (ir->p.fops && ir->p.fops->open)
+		return ir->p.fops->open(inode, file);
+
+	if (mutex_lock_interruptible(&plugin_lock))
+		return -ERESTARTSYS;
+
+	if (ir->p.minor == NOPLUG) {
+		mutex_unlock(&plugin_lock);
+		dprintk(LOGHEAD "open result = -ENODEV\n",
+			ir->p.name, ir->p.minor);
+		return -ENODEV;
+	}
+
+	if (ir->open) {
+		mutex_unlock(&plugin_lock);
+		dprintk(LOGHEAD "open result = -EBUSY\n",
+			ir->p.name, ir->p.minor);
+		return -EBUSY;
+	}
+
+	/* there is no need for locking here because ir->open is 0
+	 * and lirc_thread isn't using buffer
+	 * plugins which use irq's should allocate them on set_use_inc,
+	 * so there should be no problem with those either.
+	 */
+	ir->buf->head = ir->buf->tail;
+	ir->buf->fill = 0;
+
+	if (ir->p.owner != NULL && try_module_get(ir->p.owner)) {
+		++ir->open;
+		retval = ir->p.set_use_inc(ir->p.data);
+
+		if (retval != SUCCESS) {
+			module_put(ir->p.owner);
+			--ir->open;
+		}
+	} else {
+		if (ir->p.owner == NULL)
+			dprintk(LOGHEAD "no module owner!!!\n",
+				ir->p.name, ir->p.minor);
+
+		retval = -ENODEV;
+	}
+
+	dprintk(LOGHEAD "open result = %d\n", ir->p.name, ir->p.minor, retval);
+	mutex_unlock(&plugin_lock);
+
+	return retval;
+}
+
+/*
+ *
+ */
+static int irctl_close(struct inode *inode, struct file *file)
+{
+	struct irctl *ir = &irctls[MINOR(inode->i_rdev)];
+
+	dprintk(LOGHEAD "close called\n", ir->p.name, ir->p.minor);
+
+	/* if the plugin has a close function use it instead */
+	if (ir->p.fops && ir->p.fops->release)
+		return ir->p.fops->release(inode, file);
+
+	if (mutex_lock_interruptible(&plugin_lock))
+		return -ERESTARTSYS;
+
+	--ir->open;
+	if (ir->attached) {
+		ir->p.set_use_dec(ir->p.data);
+		module_put(ir->p.owner);
+	} else {
+		cleanup(ir);
+	}
+
+	mutex_unlock(&plugin_lock);
+
+	return SUCCESS;
+}
+
+/*
+ *
+ */
+static unsigned int irctl_poll(struct file *file, poll_table *wait)
+{
+	struct irctl *ir = &irctls[MINOR(file->f_dentry->d_inode->i_rdev)];
+	unsigned int ret;
+
+	dprintk(LOGHEAD "poll called\n", ir->p.name, ir->p.minor);
+
+	/* if the plugin has a poll function use it instead */
+	if (ir->p.fops && ir->p.fops->poll)
+		return ir->p.fops->poll(file, wait);
+
+	mutex_lock(&ir->buffer_lock);
+	if (!ir->attached) {
+		mutex_unlock(&ir->buffer_lock);
+		return POLLERR;
+	}
+
+	poll_wait(file, &ir->buf->wait_poll, wait);
+
+	dprintk(LOGHEAD "poll result = %s\n",
+		ir->p.name, ir->p.minor,
+		lirc_buffer_empty(ir->buf) ? "0" : "POLLIN|POLLRDNORM");
+
+	ret = lirc_buffer_empty(ir->buf) ? 0 : (POLLIN|POLLRDNORM);
+
+	mutex_unlock(&ir->buffer_lock);
+	return ret;
+}
+
+/*
+ *
+ */
+static int irctl_ioctl(struct inode *inode, struct file *file,
+		       unsigned int cmd, unsigned long arg)
+{
+	unsigned long mode;
+	int result;
+	struct irctl *ir = &irctls[MINOR(inode->i_rdev)];
+
+	dprintk(LOGHEAD "ioctl called (0x%x)\n",
+		ir->p.name, ir->p.minor, cmd);
+
+	/* if the plugin has a ioctl function use it instead */
+	if (ir->p.fops && ir->p.fops->ioctl)
+		return ir->p.fops->ioctl(inode, file, cmd, arg);
+
+	if (ir->p.minor == NOPLUG || !ir->attached) {
+		dprintk(LOGHEAD "ioctl result = -ENODEV\n",
+			ir->p.name, ir->p.minor);
+		return -ENODEV;
+	}
+
+	/* Give the plugin a chance to handle the ioctl */
+	if (ir->p.ioctl) {
+		result = ir->p.ioctl(inode, file, cmd, arg);
+		if (result != -ENOIOCTLCMD)
+			return result;
+	}
+	/* The plugin can't handle cmd */
+	result = SUCCESS;
+
+	switch (cmd) {
+	case LIRC_GET_FEATURES:
+		result = put_user(ir->p.features, (unsigned long *)arg);
+		break;
+	case LIRC_GET_REC_MODE:
+		if (!(ir->p.features&LIRC_CAN_REC_MASK))
+			return -ENOSYS;
+
+		result = put_user(LIRC_REC2MODE
+				  (ir->p.features&LIRC_CAN_REC_MASK),
+				  (unsigned long *)arg);
+		break;
+	case LIRC_SET_REC_MODE:
+		if (!(ir->p.features&LIRC_CAN_REC_MASK))
+			return -ENOSYS;
+
+		result = get_user(mode, (unsigned long *)arg);
+		if (!result && !(LIRC_MODE2REC(mode) & ir->p.features))
+			result = -EINVAL;
+		/*
+		 * FIXME: We should actually set the mode somehow but
+		 * for now, lirc_serial doesn't support mode changing either
+		 */
+		break;
+	case LIRC_GET_LENGTH:
+		result = put_user((unsigned long)ir->p.code_length,
+				  (unsigned long *)arg);
+		break;
+	default:
+		result = -ENOIOCTLCMD;
+	}
+
+	dprintk(LOGHEAD "ioctl result = %d\n",
+		ir->p.name, ir->p.minor, result);
+
+	return result;
+}
+
+/*
+ *
+ */
+static ssize_t irctl_read(struct file *file,
+			  char *buffer,
+			  size_t length,
+			  loff_t *ppos)
+{
+	struct irctl *ir = &irctls[MINOR(file->f_dentry->d_inode->i_rdev)];
+	unsigned char buf[ir->buf->chunk_size];
+	int ret = 0, written = 0;
+	DECLARE_WAITQUEUE(wait, current);
+
+	dprintk(LOGHEAD "read called\n", ir->p.name, ir->p.minor);
+
+	/* if the plugin has a specific read function use it instead */
+	if (ir->p.fops && ir->p.fops->read)
+		return ir->p.fops->read(file, buffer, length, ppos);
+
+	if (mutex_lock_interruptible(&ir->buffer_lock))
+		return -ERESTARTSYS;
+	if (!ir->attached) {
+		mutex_unlock(&ir->buffer_lock);
+		return -ENODEV;
+	}
+
+	if (length % ir->buf->chunk_size) {
+		dprintk(LOGHEAD "read result = -EINVAL\n",
+			ir->p.name, ir->p.minor);
+		mutex_unlock(&ir->buffer_lock);
+		return -EINVAL;
+	}
+
+	/*
+	 * we add ourselves to the task queue before buffer check
+	 * to avoid losing scan code (in case when queue is awaken somewhere
+	 * beetwen while condition checking and scheduling)
+	 */
+	add_wait_queue(&ir->buf->wait_poll, &wait);
+	set_current_state(TASK_INTERRUPTIBLE);
+
+	/*
+	 * while we did't provide 'length' bytes, device is opened in blocking
+	 * mode and 'copy_to_user' is happy, wait for data.
+	 */
+	while (written < length && ret == 0) {
+		if (lirc_buffer_empty(ir->buf)) {
+			/* According to the read(2) man page, 'written' can be
+			 * returned as less than 'length', instead of blocking
+			 * again, returning -EWOULDBLOCK, or returning
+			 * -ERESTARTSYS */
+			if (written)
+				break;
+			if (file->f_flags & O_NONBLOCK) {
+				ret = -EWOULDBLOCK;
+				break;
+			}
+			if (signal_pending(current)) {
+				ret = -ERESTARTSYS;
+				break;
+			}
+			schedule();
+			set_current_state(TASK_INTERRUPTIBLE);
+			if (!ir->attached) {
+				ret = -ENODEV;
+				break;
+			}
+		} else {
+			lirc_buffer_read_1(ir->buf, buf);
+			ret = copy_to_user((void *)buffer+written, buf,
+					   ir->buf->chunk_size);
+			written += ir->buf->chunk_size;
+		}
+	}
+
+	remove_wait_queue(&ir->buf->wait_poll, &wait);
+	set_current_state(TASK_RUNNING);
+	mutex_unlock(&ir->buffer_lock);
+
+	dprintk(LOGHEAD "read result = %s (%d)\n",
+		ir->p.name, ir->p.minor, ret ? "-EFAULT" : "OK", ret);
+
+	return ret ? ret : written;
+}
+
+
+void *lirc_get_pdata(struct file *file)
+{
+	void *data = NULL;
+
+	if (file && file->f_dentry && file->f_dentry->d_inode &&
+	    file->f_dentry->d_inode->i_rdev) {
+		struct irctl *ir;
+		ir = &irctls[MINOR(file->f_dentry->d_inode->i_rdev)];
+		data = ir->p.data;
+	}
+
+	return data;
+}
+EXPORT_SYMBOL(lirc_get_pdata);
+
+
+static ssize_t irctl_write(struct file *file, const char *buffer,
+			   size_t length, loff_t *ppos)
+{
+	struct irctl *ir = &irctls[MINOR(file->f_dentry->d_inode->i_rdev)];
+
+	dprintk(LOGHEAD "write called\n", ir->p.name, ir->p.minor);
+
+	/* if the plugin has a specific read function use it instead */
+	if (ir->p.fops && ir->p.fops->write)
+		return ir->p.fops->write(file, buffer, length, ppos);
+
+	if (!ir->attached)
+		return -ENODEV;
+
+	return -EINVAL;
+}
+
+
+static struct file_operations fops = {
+	.read		= irctl_read,
+	.write		= irctl_write,
+	.poll		= irctl_poll,
+	.ioctl		= irctl_ioctl,
+	.open		= irctl_open,
+	.release	= irctl_close
+};
+
+
+static int lirc_dev_init(void)
+{
+	int i;
+
+	for (i = 0; i < MAX_IRCTL_DEVICES; ++i)
+		init_irctl(&irctls[i]);
+
+	if (register_chrdev(IRCTL_DEV_MAJOR, IRCTL_DEV_NAME, &fops)) {
+		printk(KERN_ERR "lirc_dev: register_chrdev failed\n");
+		goto out;
+	}
+
+	lirc_class = class_create(THIS_MODULE, "lirc");
+	if (IS_ERR(lirc_class)) {
+		printk(KERN_ERR "lirc_dev: class_create failed\n");
+		goto out_unregister;
+	}
+
+	printk(KERN_INFO "lirc_dev: IR Remote Control driver registered, "
+	       "major %d \n", IRCTL_DEV_MAJOR);
+
+	return SUCCESS;
+
+out_unregister:
+	/* unregister_chrdev returns void now */
+	unregister_chrdev(IRCTL_DEV_MAJOR, IRCTL_DEV_NAME);
+out:
+	return -1;
+}
+
+/* ---------------------------------------------------------------------- */
+
+#ifdef MODULE
+
+/*
+ *
+ */
+int init_module(void)
+{
+	return lirc_dev_init();
+}
+
+/*
+ *
+ */
+void cleanup_module(void)
+{
+	/* unregister_chrdev returns void now */
+	unregister_chrdev(IRCTL_DEV_MAJOR, IRCTL_DEV_NAME);
+	class_destroy(lirc_class);
+	dprintk("lirc_dev: module unloaded\n");
+}
+
+MODULE_DESCRIPTION("LIRC base driver module");
+MODULE_AUTHOR("Artur Lipowski");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_CHARDEV_MAJOR(IRCTL_DEV_MAJOR);
+
+module_param(debug, bool, 0644);
+MODULE_PARM_DESC(debug, "Enable debugging messages");
+
+#else /* not a MODULE */
+subsys_initcall(lirc_dev_init);
+
+#endif /* MODULE */
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/input/lirc/lirc_dev.h b/drivers/input/lirc/lirc_dev.h
new file mode 100644
index 0000000..920dd43
--- /dev/null
+++ b/drivers/input/lirc/lirc_dev.h
@@ -0,0 +1,262 @@
+/*
+ * LIRC base driver
+ *
+ * (L) by Artur Lipowski <alipowski@interia.pl>
+ *        This code is licensed under GNU GPL
+ *
+ */
+
+#ifndef _LINUX_LIRC_DEV_H
+#define _LINUX_LIRC_DEV_H
+
+#define MAX_IRCTL_DEVICES 4
+#define BUFLEN            16
+
+/* #define LIRC_BUFF_POWER_OF_2 */
+#ifdef LIRC_BUFF_POWER_OF_2
+#define mod(n, div) ((n) & ((div) - 1))
+#else
+#define mod(n, div) ((n) % (div))
+#endif
+#include <linux/slab.h>
+#include <linux/fs.h>
+
+struct lirc_buffer {
+	wait_queue_head_t wait_poll;
+	spinlock_t lock;
+
+	unsigned char *data;
+	unsigned int chunk_size;
+	unsigned int size; /* in chunks */
+	unsigned int fill; /* in chunks */
+	int head, tail;    /* in chunks */
+	/* Using chunks instead of bytes pretends to simplify boundary checking
+	 * And should allow for some performance fine tunning later */
+};
+static inline void _lirc_buffer_clear(struct lirc_buffer *buf)
+{
+	buf->head = 0;
+	buf->tail = 0;
+	buf->fill = 0;
+}
+static inline int lirc_buffer_init(struct lirc_buffer *buf,
+				    unsigned int chunk_size,
+				    unsigned int size)
+{
+	/* Adjusting size to the next power of 2 would allow for
+	 * inconditional LIRC_BUFF_POWER_OF_2 optimization */
+	init_waitqueue_head(&buf->wait_poll);
+	spin_lock_init(&buf->lock);
+	_lirc_buffer_clear(buf);
+	buf->chunk_size = chunk_size;
+	buf->size = size;
+	buf->data = kmalloc(size*chunk_size, GFP_KERNEL);
+	if (buf->data == NULL)
+		return -1;
+	memset(buf->data, 0, size*chunk_size);
+	return 0;
+}
+static inline void lirc_buffer_free(struct lirc_buffer *buf)
+{
+	kfree(buf->data);
+	buf->data = NULL;
+	buf->head = 0;
+	buf->tail = 0;
+	buf->fill = 0;
+	buf->chunk_size = 0;
+	buf->size = 0;
+}
+static inline int  lirc_buffer_full(struct lirc_buffer *buf)
+{
+	return buf->fill >= buf->size;
+}
+static inline int  lirc_buffer_empty(struct lirc_buffer *buf)
+{
+	return !(buf->fill);
+}
+static inline int  lirc_buffer_available(struct lirc_buffer *buf)
+{
+    return buf->size - buf->fill;
+}
+static inline void lirc_buffer_lock(struct lirc_buffer *buf,
+				    unsigned long *flags)
+{
+	spin_lock_irqsave(&buf->lock, *flags);
+}
+static inline void lirc_buffer_unlock(struct lirc_buffer *buf,
+				      unsigned long *flags)
+{
+	spin_unlock_irqrestore(&buf->lock, *flags);
+}
+static inline void lirc_buffer_clear(struct lirc_buffer *buf)
+{
+	unsigned long flags;
+	lirc_buffer_lock(buf, &flags);
+	_lirc_buffer_clear(buf);
+	lirc_buffer_unlock(buf, &flags);
+}
+static inline void _lirc_buffer_remove_1(struct lirc_buffer *buf)
+{
+	buf->head = mod(buf->head+1, buf->size);
+	buf->fill -= 1;
+}
+static inline void lirc_buffer_remove_1(struct lirc_buffer *buf)
+{
+	unsigned long flags;
+	lirc_buffer_lock(buf, &flags);
+	_lirc_buffer_remove_1(buf);
+	lirc_buffer_unlock(buf, &flags);
+}
+static inline void _lirc_buffer_read_1(struct lirc_buffer *buf,
+				     unsigned char *dest)
+{
+	memcpy(dest, &buf->data[buf->head*buf->chunk_size], buf->chunk_size);
+	buf->head = mod(buf->head+1, buf->size);
+	buf->fill -= 1;
+}
+static inline void lirc_buffer_read_1(struct lirc_buffer *buf,
+				      unsigned char *dest)
+{
+	unsigned long flags;
+	lirc_buffer_lock(buf, &flags);
+	_lirc_buffer_read_1(buf, dest);
+	lirc_buffer_unlock(buf, &flags);
+}
+static inline void _lirc_buffer_write_1(struct lirc_buffer *buf,
+				      unsigned char *orig)
+{
+	memcpy(&buf->data[buf->tail*buf->chunk_size], orig, buf->chunk_size);
+	buf->tail = mod(buf->tail+1, buf->size);
+	buf->fill++;
+}
+static inline void lirc_buffer_write_1(struct lirc_buffer *buf,
+				       unsigned char *orig)
+{
+	unsigned long flags;
+	lirc_buffer_lock(buf, &flags);
+	_lirc_buffer_write_1(buf, orig);
+	lirc_buffer_unlock(buf, &flags);
+}
+static inline void _lirc_buffer_write_n(struct lirc_buffer *buf,
+					unsigned char *orig, int count)
+{
+	memcpy(&buf->data[buf->tail * buf->chunk_size], orig,
+	       count * buf->chunk_size);
+	buf->tail = mod(buf->tail + count, buf->size);
+	buf->fill += count;
+}
+static inline void lirc_buffer_write_n(struct lirc_buffer *buf,
+				       unsigned char *orig, int count)
+{
+	unsigned long flags;
+	int space1;
+
+	lirc_buffer_lock(buf, &flags);
+	if (buf->head > buf->tail)
+		space1 = buf->head - buf->tail;
+	else
+		space1 = buf->size - buf->tail;
+
+	if (count > space1) {
+		_lirc_buffer_write_n(buf, orig, space1);
+		_lirc_buffer_write_n(buf, orig+(space1*buf->chunk_size),
+				     count-space1);
+	} else {
+		_lirc_buffer_write_n(buf, orig, count);
+	}
+	lirc_buffer_unlock(buf, &flags);
+}
+
+struct lirc_plugin {
+	char name[40];
+	int minor;
+	int code_length;
+	int sample_rate;
+	unsigned long features;
+	void *data;
+	int (*add_to_buf) (void *data, struct lirc_buffer *buf);
+	wait_queue_head_t* (*get_queue) (void *data);
+	struct lirc_buffer *rbuf;
+	int (*set_use_inc) (void *data);
+	void (*set_use_dec) (void *data);
+	int (*ioctl) (struct inode *, struct file *, unsigned int,
+		      unsigned long);
+	struct file_operations *fops;
+	struct device *dev;
+	struct module *owner;
+};
+/* name:
+ * this string will be used for logs
+ *
+ * minor:
+ * indicates minor device (/dev/lirc) number for registered plugin
+ * if caller fills it with negative value, then the first free minor
+ * number will be used (if available)
+ *
+ * code_length:
+ * length of the remote control key code expressed in bits
+ *
+ * sample_rate:
+ * sample_rate equal to 0 means that no polling will be performed and
+ * add_to_buf will be triggered by external events (through task queue
+ * returned by get_queue)
+ *
+ * data:
+ * it may point to any plugin data and this pointer will be passed to
+ * all callback functions
+ *
+ * add_to_buf:
+ * add_to_buf will be called after specified period of the time or
+ * triggered by the external event, this behavior depends on value of
+ * the sample_rate this function will be called in user context. This
+ * routine should return 0 if data was added to the buffer and
+ * -ENODATA if none was available. This should add some number of bits
+ * evenly divisible by code_length to the buffer
+ *
+ * get_queue:
+ * this callback should return a pointer to the task queue which will
+ * be used for external event waiting
+ *
+ * rbuf:
+ * if not NULL, it will be used as a read buffer, you will have to
+ * write to the buffer by other means, like irq's (see also
+ * lirc_serial.c).
+ *
+ * set_use_inc:
+ * set_use_inc will be called after device is opened
+ *
+ * set_use_dec:
+ * set_use_dec will be called after device is closed
+ *
+ * ioctl:
+ * Some ioctl's can be directly handled by lirc_dev but will be
+ * forwared here if not NULL and only handled if it returns
+ * -ENOIOCTLCMD (see also lirc_serial.c).
+ *
+ * fops:
+ * file_operations for drivers which don't fit the current plugin model.
+ *
+ * owner:
+ * the module owning this struct
+ *
+ */
+
+
+/* following functions can be called ONLY from user context
+ *
+ * returns negative value on error or minor number
+ * of the registered device if success
+ * contens of the structure pointed by p is copied
+ */
+extern int lirc_register_plugin(struct lirc_plugin *p);
+
+/* returns negative value on error or 0 if success
+*/
+extern int lirc_unregister_plugin(int minor);
+
+/* Returns the private data stored in the lirc_plugin
+ * associated with the given device file pointer.
+ */
+void *lirc_get_pdata(struct file *file);
+
+#endif
diff --git a/drivers/input/lirc/lirc_i2c.c b/drivers/input/lirc/lirc_i2c.c
new file mode 100644
index 0000000..4714641
--- /dev/null
+++ b/drivers/input/lirc/lirc_i2c.c
@@ -0,0 +1,639 @@
+/*
+ * i2c IR lirc plugin for Hauppauge and Pixelview cards - new 2.3.x i2c stack
+ *
+ * Copyright (c) 2000 Gerd Knorr <kraxel@goldbach.in-berlin.de>
+ * modified for PixelView (BT878P+W/FM) by
+ *      Michal Kochanowicz <mkochano@pld.org.pl>
+ *      Christoph Bartelmus <lirc@bartelmus.de>
+ * modified for KNC ONE TV Station/Anubis Typhoon TView Tuner by
+ *      Ulrich Mueller <ulrich.mueller42@web.de>
+ * modified for Asus TV-Box and Creative/VisionTek BreakOut-Box by
+ *      Stefan Jahn <stefan@lkcc.org>
+ * modified for inclusion into kernel sources by
+ *      Jerome Brock <jbrock@users.sourceforge.net>
+ * modified for Leadtek Winfast PVR2000 by
+ *      Thomas Reitmayr (treitmayr@yahoo.com)
+ * modified for Hauppauge HVR-1300 by
+ *      Jan Frey (jfrey@gmx.de)
+ *
+ * parts are cut&pasted from the old lirc_haup.c driver
+ *
+ *  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>
+#include <linux/module.h>
+#include <linux/kmod.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+
+#include <linux/semaphore.h>
+
+#include "lirc_dev.h"
+
+struct IR {
+	struct lirc_plugin l;
+	struct i2c_client  c;
+	int nextkey;
+	unsigned char b[3];
+	unsigned char bits;
+	unsigned char flag;
+};
+
+/* ----------------------------------------------------------------------- */
+
+#define DEVICE_NAME "lirc_i2c"
+
+/* ----------------------------------------------------------------------- */
+/* insmod parameters						       */
+
+static int debug;	/* debug output */
+static int minor = -1;	/* minor number */
+
+#define dprintk(fmt, args...)						\
+	do {								\
+		if (debug)						\
+			printk(KERN_DEBUG DEVICE_NAME ": " fmt,		\
+			       ## args);				\
+	} while (0)
+
+/* ----------------------------------------------------------------------- */
+
+static inline int reverse(int data, int bits)
+{
+	int i;
+	int c;
+
+	for (c = 0, i = 0; i < bits; i++)
+		c |= ((data & (1<<i)) ? 1 : 0) << (bits-1-i);
+
+	return c;
+}
+
+static int add_to_buf_adap(void *data, struct lirc_buffer *buf)
+{
+	struct IR *ir = data;
+	unsigned char keybuf[4];
+
+	keybuf[0] = 0x00;
+	i2c_master_send(&ir->c, keybuf, 1);
+	/* poll IR chip */
+	if (i2c_master_recv(&ir->c, keybuf, sizeof(keybuf)) != sizeof(keybuf)) {
+		dprintk("read error\n");
+		return -EIO;
+	}
+
+	dprintk("key (0x%02x%02x%02x%02x)\n",
+		keybuf[0], keybuf[1], keybuf[2], keybuf[3]);
+
+	/* key pressed ? */
+	if (keybuf[2] == 0xff)
+		return -ENODATA;
+
+	/* remove repeat bit */
+	keybuf[2] &= 0x7f;
+	keybuf[3] |= 0x80;
+
+	lirc_buffer_write_1(buf, keybuf);
+	return 0;
+}
+
+static int add_to_buf_pcf8574(void *data, struct lirc_buffer *buf)
+{
+	struct IR *ir = data;
+	int rc;
+	unsigned char all, mask;
+	unsigned char key;
+
+	/* compute all valid bits (key code + pressed/release flag) */
+	all = ir->bits | ir->flag;
+
+	/* save IR writable mask bits */
+	mask = i2c_smbus_read_byte(&ir->c) & ~all;
+
+	/* send bit mask */
+	rc = i2c_smbus_write_byte(&ir->c, (0xff & all) | mask);
+
+	/* receive scan code */
+	rc = i2c_smbus_read_byte(&ir->c);
+
+	if (rc == -1) {
+		dprintk("%s read error\n", ir->c.name);
+		return -EIO;
+	}
+
+	/* drop duplicate polls */
+	if (ir->b[0] == (rc & all))
+		return -ENODATA;
+
+	ir->b[0] = rc & all;
+
+	dprintk("%s key 0x%02X %s\n", ir->c.name, rc & ir->bits,
+		(rc & ir->flag) ? "released" : "pressed");
+
+	if (rc & ir->flag) {
+		/* ignore released buttons */
+		return -ENODATA;
+	}
+
+	/* set valid key code */
+	key  = rc & ir->bits;
+	lirc_buffer_write_1(buf, &key);
+	return 0;
+}
+
+/* common for Hauppauge IR receivers */
+static int add_to_buf_haup_common(void *data, struct lirc_buffer *buf,
+		unsigned char *keybuf, int size, int offset)
+{
+	struct IR *ir = data;
+	__u16 code;
+	unsigned char codes[2];
+
+	/* poll IR chip */
+	if (size == i2c_master_recv(&ir->c, keybuf, size)) {
+		ir->b[0] = keybuf[offset];
+		ir->b[1] = keybuf[offset+1];
+		ir->b[2] = keybuf[offset+2];
+		dprintk("key (0x%02x/0x%02x)\n", ir->b[0], ir->b[1]);
+	} else {
+		dprintk("read error\n");
+		/* keep last successfull read buffer */
+	}
+
+	/* key pressed ? */
+	if ((ir->b[0] & 0x80) == 0)
+		return -ENODATA;
+
+	/* look what we have */
+	code = (((__u16)ir->b[0]&0x7f)<<6) | (ir->b[1]>>2);
+
+	codes[0] = (code >> 8) & 0xff;
+	codes[1] = code & 0xff;
+
+	/* return it */
+	lirc_buffer_write_1(buf, codes);
+	return 0;
+}
+
+/* specific for the Hauppauge PVR150 IR receiver */
+static int add_to_buf_haup_pvr150(void *data, struct lirc_buffer *buf)
+{
+	unsigned char keybuf[6];
+	/* fetch 6 bytes, first relevant is at offset 3 */
+	return add_to_buf_haup_common(data, buf, keybuf, 6, 3);
+}
+
+/* used for all Hauppauge IR receivers but the PVR150 */
+static int add_to_buf_haup(void *data, struct lirc_buffer *buf)
+{
+	unsigned char keybuf[3];
+	/* fetch 3 bytes, first relevant is at offset 0 */
+	return add_to_buf_haup_common(data, buf, keybuf, 3, 0);
+}
+
+
+static int add_to_buf_pvr2000(void *data, struct lirc_buffer *buf)
+{
+	struct IR *ir = data;
+	unsigned char key;
+	s32 flags;
+	s32 code;
+
+	/* poll IR chip */
+	flags = i2c_smbus_read_byte_data(&ir->c, 0x10);
+	if (-1 == flags) {
+		dprintk("read error\n");
+		return -ENODATA;
+	}
+	/* key pressed ? */
+	if (0 == (flags & 0x80))
+		return -ENODATA;
+
+	/* read actual key code */
+	code = i2c_smbus_read_byte_data(&ir->c, 0x00);
+	if (-1 == code) {
+		dprintk("read error\n");
+		return -ENODATA;
+	}
+
+	key = code & 0xFF;
+
+	dprintk("IR Key/Flags: (0x%02x/0x%02x)\n", key, flags & 0xFF);
+
+	/* return it */
+	lirc_buffer_write_1(buf, &key);
+	return 0;
+}
+
+static int add_to_buf_pixelview(void *data, struct lirc_buffer *buf)
+{
+	struct IR *ir = data;
+	unsigned char key;
+
+	/* poll IR chip */
+	if (1 != i2c_master_recv(&ir->c, &key, 1)) {
+		dprintk("read error\n");
+		return -1;
+	}
+	dprintk("key %02x\n", key);
+
+	/* return it */
+	lirc_buffer_write_1(buf, &key);
+	return 0;
+}
+
+static int add_to_buf_pv951(void *data, struct lirc_buffer *buf)
+{
+	struct IR *ir = data;
+	unsigned char key;
+	unsigned char codes[4];
+
+	/* poll IR chip */
+	if (1 != i2c_master_recv(&ir->c, &key, 1)) {
+		dprintk("read error\n");
+		return -ENODATA;
+	}
+	/* ignore 0xaa */
+	if (key == 0xaa)
+		return -ENODATA;
+	dprintk("key %02x\n", key);
+
+	codes[0] = 0x61;
+	codes[1] = 0xD6;
+	codes[2] = reverse(key, 8);
+	codes[3] = (~codes[2])&0xff;
+
+	lirc_buffer_write_1(buf, codes);
+	return 0;
+}
+
+static int add_to_buf_knc1(void *data, struct lirc_buffer *buf)
+{
+	static unsigned char last_key = 0xFF;
+	struct IR *ir = data;
+	unsigned char key;
+
+	/* poll IR chip */
+	if (1 != i2c_master_recv(&ir->c, &key, 1)) {
+		dprintk("read error\n");
+		return -ENODATA;
+	}
+
+	/* it seems that 0xFE indicates that a button is still hold
+	   down, while 0xFF indicates that no button is hold
+	   down. 0xFE sequences are sometimes interrupted by 0xFF */
+
+	dprintk("key %02x\n", key);
+
+	if (key == 0xFF)
+		return -ENODATA;
+
+	if (key == 0xFE)
+		key = last_key;
+
+	last_key = key;
+	lirc_buffer_write_1(buf, &key);
+
+	return 0;
+}
+
+static int set_use_inc(void *data)
+{
+	struct IR *ir = data;
+
+	/* lock bttv in memory while /dev/lirc is in use  */
+	i2c_use_client(&ir->c);
+
+	return 0;
+}
+
+static void set_use_dec(void *data)
+{
+	struct IR *ir = data;
+
+	i2c_release_client(&ir->c);
+}
+
+static struct lirc_plugin lirc_template = {
+	.name		= "lirc_i2c",
+	.set_use_inc	= set_use_inc,
+	.set_use_dec	= set_use_dec,
+	.dev		= NULL,
+	.owner		= THIS_MODULE,
+};
+
+/* ----------------------------------------------------------------------- */
+
+static int ir_attach(struct i2c_adapter *adap, int addr,
+		      unsigned short flags, int kind);
+static int ir_detach(struct i2c_client *client);
+static int ir_probe(struct i2c_adapter *adap);
+static int ir_command(struct i2c_client *client, unsigned int cmd, void *arg);
+
+static struct i2c_driver driver = {
+	.driver = {
+		.owner	= THIS_MODULE,
+		.name	= "i2c ir driver",
+	},
+	.attach_adapter	= ir_probe,
+	.detach_client	= ir_detach,
+	.command	= ir_command,
+};
+
+static struct i2c_client client_template = {
+	.name		= "unset",
+	.driver		= &driver
+};
+
+static int ir_attach(struct i2c_adapter *adap, int addr,
+		     unsigned short flags, int kind)
+{
+	struct IR *ir;
+	int err;
+
+	client_template.adapter = adap;
+	client_template.addr = addr;
+
+	ir = kmalloc(sizeof(struct IR), GFP_KERNEL);
+	if (!ir)
+		return -ENOMEM;
+	memcpy(&ir->l, &lirc_template, sizeof(struct lirc_plugin));
+	memcpy(&ir->c, &client_template, sizeof(struct i2c_client));
+
+	ir->c.adapter = adap;
+	ir->c.addr    = addr;
+	i2c_set_clientdata(&ir->c, ir);
+	ir->l.data    = ir;
+	ir->l.minor   = minor;
+	ir->l.sample_rate = 10;
+	ir->nextkey   = -1;
+
+	switch (addr) {
+	case 0x64:
+		strlcpy(ir->c.name, "Pixelview IR", I2C_NAME_SIZE);
+		ir->l.code_length = 8;
+		ir->l.add_to_buf = add_to_buf_pixelview;
+		break;
+	case 0x4b:
+		strlcpy(ir->c.name, "PV951 IR", I2C_NAME_SIZE);
+		ir->l.code_length = 32;
+		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) {
+			/* The PVR150 IR receiver uses the same protocol as
+			 * other Hauppauge cards, but the data flow is
+			 * different, so we need to deal with it by its own. */
+			strlcpy(ir->c.name, "Hauppauge PVR150", I2C_NAME_SIZE);
+		} else /* I2C_HW_B_CX2388x */
+			strlcpy(ir->c.name, "Hauppauge HVR1300", I2C_NAME_SIZE);
+		ir->l.code_length = 13;
+		ir->l.add_to_buf = add_to_buf_haup_pvr150;
+		break;
+	case 0x6b:
+		strlcpy(ir->c.name, "Adaptec IR", I2C_NAME_SIZE);
+		ir->l.code_length = 32;
+		ir->l.add_to_buf = add_to_buf_adap;
+		break;
+	case 0x18:
+	case 0x1a:
+		if (adap->id == I2C_HW_B_BT848 ||
+		    adap->id == I2C_HW_B_CX2341X) {
+			strlcpy(ir->c.name, "Hauppauge IR", I2C_NAME_SIZE);
+			ir->l.code_length = 13;
+			ir->l.add_to_buf = add_to_buf_haup;
+		} else { /* I2C_HW_B_CX2388x */
+			strlcpy(ir->c.name, "Leadtek IR", I2C_NAME_SIZE);
+			ir->l.code_length = 8;
+			ir->l.add_to_buf = add_to_buf_pvr2000;
+		}
+		break;
+	case 0x30:
+		strlcpy(ir->c.name, "KNC ONE IR", I2C_NAME_SIZE);
+		ir->l.code_length = 8;
+		ir->l.add_to_buf = add_to_buf_knc1;
+		break;
+	case 0x21:
+	case 0x23:
+		strlcpy(ir->c.name, "TV-Box IR", I2C_NAME_SIZE);
+		ir->l.code_length = 8;
+		ir->l.add_to_buf = add_to_buf_pcf8574;
+		ir->bits = flags & 0xff;
+		ir->flag = (flags >> 8) & 0xff;
+		break;
+	default:
+		/* shouldn't happen */
+		printk("lirc_i2c: Huh? unknown i2c address (0x%02x)?\n", addr);
+		kfree(ir);
+		return -1;
+	}
+	printk(KERN_INFO "lirc_i2c: chip 0x%x found @ 0x%02x (%s)\n",
+	       adap->id, addr, ir->c.name);
+
+	/* register device */
+	err = i2c_attach_client(&ir->c);
+	if (err) {
+		kfree(ir);
+		return err;
+	}
+	ir->l.minor = lirc_register_plugin(&ir->l);
+
+	return 0;
+}
+
+static int ir_detach(struct i2c_client *client)
+{
+	struct IR *ir = i2c_get_clientdata(client);
+
+	/* unregister device */
+	lirc_unregister_plugin(ir->l.minor);
+	i2c_detach_client(&ir->c);
+
+	/* free memory */
+	kfree(ir);
+	return 0;
+}
+
+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};
+
+	static const int probe_cx88[] = {
+		0x18, /* Leadtek Winfast PVR2000 */
+		0x71, /* Hauppauge HVR-IR */
+		-1};
+
+	struct i2c_client c;
+	char buf;
+	int i, rc;
+
+	if (adap->id == I2C_HW_B_BT848 ||
+	    adap->id == I2C_HW_B_CX2341X) {
+		memset(&c, 0, sizeof(c));
+		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)
+				ir_attach(adap, probe[i], 0, 0);
+		}
+	}
+
+	/* Leadtek Winfast PVR2000 or Hauppauge HVR-1300 */
+	else if (adap->id == I2C_HW_B_CX2388x) {
+		memset(&c, 0, sizeof(c));
+		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)
+				ir_attach(adap, c.addr, 0, 0);
+		}
+	}
+
+	/* Asus TV-Box and Creative/VisionTek BreakOut-Box (PCF8574) */
+	else if (adap->id == 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;
+
+		memset(&c, 0, sizeof(c));
+		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)
+				ir_attach(adap, pcf_probe[i],
+					  bits|(flag<<8), 0);
+		}
+	}
+
+	return 0;
+}
+
+static int ir_command(struct i2c_client *client, unsigned int cmd, void *arg)
+{
+	/* nothing */
+	return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+#ifdef MODULE
+
+int init_module(void)
+{
+	request_module("bttv");
+	request_module("rivatv");
+	request_module("ivtv");
+	request_module("cx8800");
+	i2c_add_driver(&driver);
+	return 0;
+}
+
+void cleanup_module(void)
+{
+	i2c_del_driver(&driver);
+}
+
+MODULE_DESCRIPTION("Infrared receiver driver for Hauppauge and "
+		   "Pixelview cards (i2c stack)");
+MODULE_AUTHOR("Gerd Knorr, Michal Kochanowicz, Christoph Bartelmus, "
+	      "Ulrich Mueller, Stefan Jahn, Jerome Brock");
+MODULE_LICENSE("GPL");
+
+module_param(minor, int, 0444);
+MODULE_PARM_DESC(minor, "Preferred minor device number");
+
+module_param(debug, bool, 0644);
+MODULE_PARM_DESC(debug, "Enable debugging messages");
+
+#endif /* MODULE */
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/input/lirc/lirc_igorplugusb.c b/drivers/input/lirc/lirc_igorplugusb.c
new file mode 100644
index 0000000..ab9bdd6
--- /dev/null
+++ b/drivers/input/lirc/lirc_igorplugusb.c
@@ -0,0 +1,619 @@
+/* lirc_igorplugusb - USB remote support for LIRC
+ *
+ * Supports the standard homebrew IgorPlugUSB receiver with Igor's firmware.
+ * See http://www.cesko.host.sk/IgorPlugUSB/IgorPlug-USB%20(AVR)_eng.htm
+ *
+ * The device can only record bursts of up to 36 pulses/spaces.
+ * Works fine with RC5. Longer commands lead to device buffer overrun.
+ * (Maybe a better firmware or a microcontroller with more ram can help?)
+ *
+ * Version 0.1  [beta status]
+ *
+ * Copyright (C) 2004 Jan M. Hochstein
+ *	<hochstein@algo.informatik.tu-darmstadt.de>
+ *
+ * This driver was derived from:
+ *   Paul Miller <pmiller9@users.sourceforge.net>
+ *      "lirc_atiusb" module
+ *   Vladimir Dergachev <volodya@minspring.com>'s 2002
+ *      "USB ATI Remote support" (input device)
+ *   Adrian Dewhurst <sailor-lk@sailorfrag.net>'s 2002
+ *      "USB StreamZap remote driver" (LIRC)
+ *   Artur Lipowski <alipowski@kki.net.pl>'s 2002
+ *      "lirc_dev" and "lirc_gpio" LIRC modules
+ *
+ */
+
+/*
+ * 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>
+
+#include <linux/autoconf.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/kmod.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/ioctl.h>
+#include <linux/fs.h>
+#include <linux/usb.h>
+#include <linux/poll.h>
+#include <linux/smp_lock.h>
+#include <linux/time.h>
+
+#include "lirc.h"
+#include "lirc_dev.h"
+
+
+/* module identification */
+#define DRIVER_VERSION		"0.1"
+#define DRIVER_AUTHOR		\
+	"Jan M. Hochstein <hochstein@algo.informatik.tu-darmstadt.de>"
+#define DRIVER_DESC		"USB remote driver for LIRC"
+#define DRIVER_NAME		"lirc_igorplugusb"
+
+/* debugging support */
+#ifdef CONFIG_USB_DEBUG
+static int debug = 1;
+#else
+static int debug;
+#endif
+
+#define dprintk(fmt, args...)					\
+	do {							\
+		if (debug)					\
+			printk(KERN_DEBUG fmt, ## args);	\
+	} while (0)
+
+/* general constants */
+#define SUCCESS		 0
+
+/* One mode2 pulse/space has 4 bytes. */
+#define CODE_LENGTH	     sizeof(int)
+
+/* Igor's firmware cannot record bursts longer than 36. */
+#define DEVICE_BUFLEN	   36
+
+/** Header at the beginning of the device's buffer:
+	unsigned char data_length
+	unsigned char data_start    (!=0 means ring-buffer overrun)
+	unsigned char counter       (incremented by each burst)
+**/
+#define DEVICE_HEADERLEN	3
+
+/* This is for the gap */
+#define ADDITIONAL_LIRC_BYTES   2
+
+/* times to poll per second */
+#define SAMPLE_RATE	     100
+static int sample_rate = SAMPLE_RATE;
+
+
+/**** Igor's USB Request Codes */
+
+#define SET_INFRABUFFER_EMPTY   1
+/**
+ * Params: none
+ * Answer: empty
+ *
+**/
+
+#define GET_INFRACODE	   2
+/**
+ * Params:
+ *   wValue: offset to begin reading infra buffer
+ *
+ * Answer: infra data
+ *
+**/
+
+#define SET_DATAPORT_DIRECTION  3
+/**
+ * Params:
+ *   wValue: (byte) 1 bit for each data port pin (0=in, 1=out)
+ *
+ * Answer: empty
+ *
+**/
+
+#define GET_DATAPORT_DIRECTION  4
+/**
+ * Params: none
+ *
+ * Answer: (byte) 1 bit for each data port pin (0=in, 1=out)
+ *
+**/
+
+#define SET_OUT_DATAPORT	5
+/**
+ * Params:
+ *   wValue: byte to write to output data port
+ *
+ * Answer: empty
+ *
+**/
+
+#define GET_OUT_DATAPORT	6
+/**
+ * Params: none
+ *
+ * Answer: least significant 3 bits read from output data port
+ *
+**/
+
+#define GET_IN_DATAPORT	 7
+/**
+ * Params: none
+ *
+ * Answer: least significant 3 bits read from input data port
+ *
+**/
+
+#define READ_EEPROM	     8
+/**
+ * Params:
+ *   wValue: offset to begin reading EEPROM
+ *
+ * Answer: EEPROM bytes
+ *
+**/
+
+#define WRITE_EEPROM	    9
+/**
+ * Params:
+ *   wValue: offset to EEPROM byte
+ *   wIndex: byte to write
+ *
+ * Answer: empty
+ *
+**/
+
+#define SEND_RS232	      10
+/**
+ * Params:
+ *   wValue: byte to send
+ *
+ * Answer: empty
+ *
+**/
+
+#define RECV_RS232	      11
+/**
+ * Params: none
+ *
+ * Answer: byte received
+ *
+**/
+
+#define SET_RS232_BAUD	  12
+/**
+ * Params:
+ *   wValue: byte to write to UART bit rate register (UBRR)
+ *
+ * Answer: empty
+ *
+**/
+
+#define GET_RS232_BAUD	  13
+/**
+ * Params: none
+ *
+ * Answer: byte read from UART bit rate register (UBRR)
+ *
+**/
+
+
+/* data structure for each usb remote */
+struct irctl {
+
+	/* usb */
+	struct usb_device *usbdev;
+	struct urb *urb_in;
+	int devnum;
+
+	unsigned char *buf_in;
+	unsigned int len_in;
+	int in_space;
+	struct timeval last_time;
+
+	dma_addr_t dma_in;
+
+	/* lirc */
+	struct lirc_plugin *p;
+
+	/* handle sending (init strings) */
+	int send_flags;
+	wait_queue_head_t wait_out;
+};
+
+static int unregister_from_lirc(struct irctl *ir)
+{
+	struct lirc_plugin *p = ir->p;
+	int devnum;
+
+	if (!ir->p)
+		return -EINVAL;
+
+	devnum = ir->devnum;
+	dprintk(DRIVER_NAME "[%d]: unregister from lirc called\n", devnum);
+
+	lirc_unregister_plugin(p->minor);
+
+	printk(DRIVER_NAME "[%d]: usb remote disconnected\n", devnum);
+
+	lirc_buffer_free(p->rbuf);
+	kfree(p->rbuf);
+	kfree(p);
+	kfree(ir);
+	ir->p = NULL;
+	return SUCCESS;
+}
+
+static int set_use_inc(void *data)
+{
+	struct irctl *ir = data;
+
+	if (!ir) {
+		printk(DRIVER_NAME "[?]: set_use_inc called with no context\n");
+		return -EIO;
+	}
+	dprintk(DRIVER_NAME "[%d]: set use inc\n", ir->devnum);
+
+	if (!ir->usbdev)
+		return -ENODEV;
+
+	return SUCCESS;
+}
+
+static void set_use_dec(void *data)
+{
+	struct irctl *ir = data;
+
+	if (!ir) {
+		printk(DRIVER_NAME "[?]: set_use_dec called with no context\n");
+		return;
+	}
+	dprintk(DRIVER_NAME "[%d]: set use dec\n", ir->devnum);
+}
+
+
+/**
+ * Called in user context.
+ * return 0 if data was added to the buffer and
+ * -ENODATA if none was available. This should add some number of bits
+ * evenly divisible by code_length to the buffer
+**/
+static int usb_remote_poll(void *data, struct lirc_buffer *buf)
+{
+	int ret;
+	struct irctl *ir = (struct irctl *)data;
+
+	if (!ir->usbdev)  /* Has the device been removed? */
+		return -ENODEV;
+
+	memset(ir->buf_in, 0, ir->len_in);
+
+	ret = usb_control_msg(
+	      ir->usbdev, usb_rcvctrlpipe(ir->usbdev, 0),
+	      GET_INFRACODE, USB_TYPE_VENDOR|USB_DIR_IN,
+	      0/* offset */, /*unused*/0,
+	      ir->buf_in, ir->len_in,
+	      /*timeout*/HZ * USB_CTRL_GET_TIMEOUT);
+	if (ret > 0) {
+		int i = DEVICE_HEADERLEN;
+		int code, timediff;
+		struct timeval now;
+
+		if (ret <= 1)  /* ACK packet has 1 byte --> ignore */
+			return -ENODATA;
+
+		dprintk(DRIVER_NAME ": 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(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)
+			timediff = PULSE_MASK;
+		else {
+			timediff *= 1000000;
+			timediff += now.tv_usec - ir->last_time.tv_usec;
+		}
+		ir->last_time.tv_sec = now.tv_sec;
+		ir->last_time.tv_usec = now.tv_usec;
+
+		/* create leading gap  */
+		code = timediff;
+		lirc_buffer_write_n(buf, (unsigned char *)&code, 1);
+		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;
+			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;
+		}
+
+		ret = usb_control_msg(
+		      ir->usbdev, usb_rcvctrlpipe(ir->usbdev, 0),
+		      SET_INFRABUFFER_EMPTY, USB_TYPE_VENDOR|USB_DIR_IN,
+		      /*unused*/0, /*unused*/0,
+		      /*dummy*/ir->buf_in, /*dummy*/ir->len_in,
+		      /*timeout*/HZ * USB_CTRL_GET_TIMEOUT);
+		if (ret < 0)
+			printk(DRIVER_NAME "[%d]: SET_INFRABUFFER_EMPTY: "
+			       "error %d\n", ir->devnum, ret);
+		return SUCCESS;
+	} else
+		printk(DRIVER_NAME "[%d]: GET_INFRACODE: error %d\n",
+			ir->devnum, ret);
+
+	return -ENODATA;
+}
+
+
+
+static int usb_remote_probe(struct usb_interface *intf,
+				const struct usb_device_id *id)
+{
+	struct usb_device *dev = NULL;
+	struct usb_host_interface *idesc = NULL;
+	struct usb_host_endpoint *ep_ctl2;
+	struct irctl *ir = NULL;
+	struct lirc_plugin *plugin = NULL;
+	struct lirc_buffer *rbuf = NULL;
+	int devnum, pipe, maxp, bytes_in_key;
+	int minor = 0;
+	char buf[63], name[128] = "";
+	int mem_failure = 0;
+	int ret;
+
+	dprintk(DRIVER_NAME ": usb probe called.\n");
+
+	dev = interface_to_usbdev(intf);
+
+	idesc = intf->cur_altsetting;  /* in 2.6.6 */
+
+	if (idesc->desc.bNumEndpoints != 1)
+		return -ENODEV;
+	ep_ctl2 = idesc->endpoint;
+	if (((ep_ctl2->desc.bEndpointAddress & USB_ENDPOINT_DIR_MASK)
+	    != USB_DIR_IN)
+	    || (ep_ctl2->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+	    != USB_ENDPOINT_XFER_CONTROL)
+		return -ENODEV;
+	pipe = usb_rcvctrlpipe(dev, ep_ctl2->desc.bEndpointAddress);
+	devnum = dev->devnum;
+	maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
+
+	bytes_in_key = CODE_LENGTH;
+
+	dprintk(DRIVER_NAME "[%d]: bytes_in_key=%d maxp=%d\n",
+		devnum, bytes_in_key, maxp);
+
+
+	/* allocate kernel memory */
+	mem_failure = 0;
+	ir = kmalloc(sizeof(struct irctl), GFP_KERNEL);
+	if (!ir) {
+		mem_failure = 1;
+		goto mem_failure_switch;
+	}
+
+	memset(ir, 0, sizeof(struct irctl));
+
+	plugin = kmalloc(sizeof(struct lirc_plugin), GFP_KERNEL);
+	if (!plugin) {
+		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, bytes_in_key,
+			DEVICE_BUFLEN+ADDITIONAL_LIRC_BYTES)) {
+		mem_failure = 4;
+		goto mem_failure_switch;
+	}
+
+	ir->buf_in = usb_buffer_alloc(dev,
+			      DEVICE_BUFLEN+DEVICE_HEADERLEN,
+			      GFP_ATOMIC, &ir->dma_in);
+	if (!ir->buf_in) {
+		mem_failure = 5;
+		goto mem_failure_switch;
+	}
+
+	memset(plugin, 0, sizeof(struct lirc_plugin));
+
+	strcpy(plugin->name, DRIVER_NAME " ");
+	plugin->minor = -1;
+	plugin->code_length = bytes_in_key*8; /* in bits */
+	plugin->features = LIRC_CAN_REC_MODE2;
+	plugin->data = ir;
+	plugin->rbuf = rbuf;
+	plugin->set_use_inc = &set_use_inc;
+	plugin->set_use_dec = &set_use_dec;
+	plugin->sample_rate = sample_rate;    /* per second */
+	plugin->add_to_buf = &usb_remote_poll;
+	plugin->dev = &dev->dev;
+	plugin->owner = THIS_MODULE;
+
+	init_waitqueue_head(&ir->wait_out);
+
+	minor = lirc_register_plugin(plugin);
+	if (minor < 0)
+		mem_failure = 9;
+
+mem_failure_switch:
+
+	/* free allocated memory in case of failure */
+	switch (mem_failure) {
+	case 9:
+		usb_buffer_free(dev, DEVICE_BUFLEN+DEVICE_HEADERLEN,
+			ir->buf_in, ir->dma_in);
+	case 5:
+		lirc_buffer_free(rbuf);
+	case 4:
+		kfree(rbuf);
+	case 3:
+		kfree(plugin);
+	case 2:
+		kfree(ir);
+	case 1:
+		printk(DRIVER_NAME "[%d]: out of memory (code=%d)\n",
+			devnum, mem_failure);
+		return -ENOMEM;
+	}
+
+	plugin->minor = minor;
+	ir->p = plugin;
+	ir->devnum = devnum;
+	ir->usbdev = dev;
+	ir->len_in = DEVICE_BUFLEN+DEVICE_HEADERLEN;
+	ir->in_space = 1; /* First mode2 event is a space. */
+	do_gettimeofday(&ir->last_time);
+
+	if (dev->descriptor.iManufacturer
+		&& usb_string(dev, dev->descriptor.iManufacturer, buf, 63) > 0)
+		strncpy(name, buf, 128);
+	if (dev->descriptor.iProduct
+		&& usb_string(dev, dev->descriptor.iProduct, buf, 63) > 0)
+		snprintf(name, 128, "%s %s", name, buf);
+	printk(DRIVER_NAME "[%d]: %s on usb%d:%d\n", devnum, name,
+	       dev->bus->busnum, devnum);
+
+	/* clear device buffer */
+	ret = usb_control_msg(ir->usbdev, usb_rcvctrlpipe(ir->usbdev, 0),
+		SET_INFRABUFFER_EMPTY, USB_TYPE_VENDOR|USB_DIR_IN,
+		/*unused*/0, /*unused*/0,
+		/*dummy*/ir->buf_in, /*dummy*/ir->len_in,
+		/*timeout*/HZ * USB_CTRL_GET_TIMEOUT);
+	if (ret < 0)
+		printk(DRIVER_NAME "[%d]: SET_INFRABUFFER_EMPTY: error %d\n",
+			devnum, ret);
+
+	usb_set_intfdata(intf, ir);
+	return SUCCESS;
+}
+
+
+static void usb_remote_disconnect(struct usb_interface *intf)
+{
+	struct usb_device *dev = interface_to_usbdev(intf);
+	struct irctl *ir = usb_get_intfdata(intf);
+	usb_set_intfdata(intf, NULL);
+
+	if (!ir || !ir->p)
+		return;
+
+	ir->usbdev = NULL;
+	wake_up_all(&ir->wait_out);
+
+	usb_buffer_free(dev, ir->len_in, ir->buf_in, ir->dma_in);
+
+	unregister_from_lirc(ir);
+}
+
+static struct usb_device_id usb_remote_id_table[] = {
+	/* Igor Plug USB (Atmel's Manufact. ID) */
+	{ USB_DEVICE(0x03eb, 0x0002) },
+
+	/* Terminating entry */
+	{ }
+};
+
+static struct usb_driver usb_remote_driver = {
+	.name =		DRIVER_NAME,
+	.probe =	usb_remote_probe,
+	.disconnect =	usb_remote_disconnect,
+	.id_table =	usb_remote_id_table
+};
+
+static int __init usb_remote_init(void)
+{
+	int i;
+
+	printk(KERN_INFO "\n"
+	       DRIVER_NAME ": " DRIVER_DESC " v" DRIVER_VERSION "\n");
+	printk(DRIVER_NAME ": " DRIVER_AUTHOR "\n");
+	dprintk(DRIVER_NAME ": debug mode enabled\n");
+
+#ifdef MODULE
+	request_module("lirc_dev");
+#endif
+
+	i = usb_register(&usb_remote_driver);
+	if (i < 0) {
+		printk(DRIVER_NAME ": usb register failed, result = %d\n", i);
+		return -ENODEV;
+	}
+
+	return SUCCESS;
+}
+
+static void __exit usb_remote_exit(void)
+{
+	usb_deregister(&usb_remote_driver);
+}
+
+#ifdef MODULE
+module_init(usb_remote_init);
+module_exit(usb_remote_exit);
+
+#include <linux/vermagic.h>
+MODULE_INFO(vermagic, VERMAGIC_STRING);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(usb, usb_remote_id_table);
+
+module_param(sample_rate, int, 0644);
+MODULE_PARM_DESC(sample_rate, "Sampling rate in Hz (default: 100)");
+
+#else /* not MODULE */
+subsys_initcall(usb_remote_driver);
+
+#endif /* MODULE */
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/input/lirc/lirc_imon.c b/drivers/input/lirc/lirc_imon.c
new file mode 100644
index 0000000..bdfd953
--- /dev/null
+++ b/drivers/input/lirc/lirc_imon.c
@@ -0,0 +1,1280 @@
+/*
+ *   lirc_imon.c:  LIRC plugin/VFD driver for Ahanix/Soundgraph IMON IR/VFD
+ *
+ *   Version 0.3
+ *		Supports newer iMON models that send decoded IR signals.
+ *			This includes the iMON PAD model.
+ *		Removed module option for vfd_proto_6p. This driver supports
+ *			multiple iMON devices so it is meaningless to have
+ *			a global option to set protocol variants.
+ *
+ *   Version 0.2 beta 2 [January 31, 2005]
+ *		USB disconnect/reconnect no longer causes problems for lircd
+ *
+ *   Version 0.2 beta 1 [January 29, 2005]
+ *		Added support for original iMON receiver(ext USB)
+ *
+ *   Version 0.2 alpha 2 [January 24, 2005]
+ *		Added support for VFDs with 6-packet protocol
+ *
+ *   Version 0.2 alpha 1 [January 23, 2005]
+ *		Added support for 2.6 kernels
+ *		Reworked disconnect handling
+ *		Incorporated Changwoo Ryu's algorithm
+ *
+ *   Version 0.1 alpha 1[July 5, 2004]
+ *
+ *   Copyright(C) 2004  Venky Raju(dev@venky.ws)
+ *
+ *   lirc_imon 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+#include <linux/version.h>
+#include <linux/autoconf.h>
+
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/usb.h>
+
+#include "lirc.h"
+#include "lirc_dev.h"
+
+
+#define MOD_AUTHOR	"Venky Raju <dev@venky.ws>"
+#define MOD_DESC	"Driver for Soundgraph iMON MultiMedia IR/VFD"
+#define MOD_NAME	"lirc_imon"
+#define MOD_VERSION	"0.3"
+
+#define VFD_MINOR_BASE	144	/* Same as LCD */
+#define DEVICE_NAME	"lcd%d"
+
+#define BUF_CHUNK_SIZE	4
+#define BUF_SIZE	128
+
+#define BIT_DURATION	250	/* each bit received is 250us */
+
+#define SUCCESS		0
+#define	TRUE		1
+#define FALSE		0
+
+
+/* ------------------------------------------------------------
+ *		     P R O T O T Y P E S
+ * ------------------------------------------------------------
+ */
+
+/* USB Callback prototypes */
+static int imon_probe(struct usb_interface *interface,
+		      const struct usb_device_id *id);
+static void imon_disconnect(struct usb_interface *interface);
+static void usb_rx_callback(struct urb *urb);
+static void usb_tx_callback(struct urb *urb);
+
+/* VFD file_operations function prototypes */
+static int vfd_open(struct inode *inode, struct file *file);
+static int vfd_close(struct inode *inode, struct file *file);
+static ssize_t vfd_write(struct file *file, const char *buf,
+			 size_t n_bytes, loff_t *pos);
+
+/* LCD file_operations override function prototypes */
+static ssize_t lcd_write(struct file *file, const char *buf,
+			 size_t n_bytes, loff_t *pos);
+
+/* LIRC plugin function prototypes */
+static int ir_open(void *data);
+static void ir_close(void *data);
+
+/* Driver init/exit prototypes */
+static int __init imon_init(void);
+static void __exit imon_exit(void);
+
+/* ------------------------------------------------------------
+ *		     G L O B A L S
+ * ------------------------------------------------------------
+ */
+
+struct imon_context {
+	struct usb_device *dev;
+	int vfd_supported;		/* not all controllers do */
+	int vfd_isopen;			/* VFD port has been opened */
+	int ir_isopen;			/* IR port open	*/
+	int ir_isassociating;		/* IR port open for association */
+	int dev_present;		/* USB device presence */
+	struct mutex lock;		/* to lock this object */
+	wait_queue_head_t remove_ok;	/* For unexpected USB disconnects */
+
+	int vfd_proto_6p;		/* VFD requires 6th packet */
+	int ir_onboard_decode;		/* IR signals decoded onboard */
+
+	struct lirc_plugin *plugin;
+	struct usb_endpoint_descriptor *rx_endpoint;
+	struct usb_endpoint_descriptor *tx_endpoint;
+	struct urb *rx_urb;
+	struct urb *tx_urb;
+	int tx_control;
+	unsigned char usb_rx_buf[8];
+	unsigned char usb_tx_buf[8];
+
+	struct rx_data {
+		int count;		/* length of 0 or 1 sequence */
+		int prev_bit;		/* logic level of sequence */
+		int initial_space;	/* initial space flag */
+	} rx;
+
+	struct tx_t {
+		unsigned char data_buf[35];	/* user data buffer */
+		struct completion finished;	/* wait for write to finish */
+		atomic_t busy;			/* write in progress */
+		int status;			/* status of tx completion */
+	} tx;
+};
+
+#define LOCK_CONTEXT	mutex_lock(&context->lock)
+#define UNLOCK_CONTEXT	mutex_unlock(&context->lock)
+
+/* VFD file operations */
+static struct file_operations vfd_fops = {
+	.owner		= THIS_MODULE,
+	.open		= &vfd_open,
+	.write		= &vfd_write,
+	.release	= &vfd_close
+};
+
+/* USB Device ID for IMON USB Control Board */
+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) */
+	{ USB_DEVICE(0x0aa8, 0x8001) },
+	/* IMON USB Control Board (IR & VFD) */
+	{ USB_DEVICE(0x15c2, 0xffda) },
+	/* IMON USB Control Board (IR & VFD) */
+	{ USB_DEVICE(0x15c2, 0xffdc) },
+	/* IMON USB Control Board (IR & LCD) */
+	{ USB_DEVICE(0x15c2, 0x0038) },
+	/* IMON USB Control Board (ext IR only) */
+	{ USB_DEVICE(0x04e8, 0xff30) },
+	{}
+};
+
+/* Some iMON VFD models requires a 6th packet */
+static struct usb_device_id vfd_proto_6p_list[] = {
+	{ USB_DEVICE(0x15c2, 0xffda) },
+	{ USB_DEVICE(0x15c2, 0xffdc) },
+	{ USB_DEVICE(0x15c2, 0x0038) },
+	{}
+};
+static unsigned char vfd_packet6[] = {
+	0x01, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF };
+
+/* Some iMON LCD models use control endpoint */
+static struct usb_device_id lcd_control_endpoint_list[] = {
+	{ USB_DEVICE(0x15c2, 0x0038) },
+	{}
+};
+
+/* Newer iMON models decode the signal onboard */
+static struct usb_device_id ir_onboard_decode_list[] = {
+	{ USB_DEVICE(0x15c2, 0xffdc) },
+	{ USB_DEVICE(0x15c2, 0x0038) },
+	{}
+};
+
+/* USB Device data */
+static struct usb_driver imon_driver = {
+	.name		= MOD_NAME,
+	.probe		= imon_probe,
+	.disconnect	= imon_disconnect,
+	.id_table	= imon_usb_id_table,
+};
+
+static struct usb_class_driver imon_class = {
+	.name		= DEVICE_NAME,
+	.fops		= &vfd_fops,
+	.minor_base	= VFD_MINOR_BASE,
+};
+
+/* to prevent races between open() and disconnect() */
+static DECLARE_MUTEX(disconnect_sem);
+
+static int debug;
+
+/* lcd or vfd? */
+static int is_lcd;
+
+
+/* ------------------------------------------------------------
+ *		     M O D U L E   C O D E
+ * ------------------------------------------------------------
+ */
+
+MODULE_AUTHOR(MOD_AUTHOR);
+MODULE_DESCRIPTION(MOD_DESC);
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(usb, imon_usb_id_table);
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "Debug messages: 0=no, 1=yes(default: no)");
+module_param(is_lcd, int, 0);
+MODULE_PARM_DESC(is_lcd, "The device is an LCD: 0=no (it's a VFD), "
+		 "1=yes (default: no)");
+
+static inline void delete_context(struct imon_context *context)
+{
+	if (context->vfd_supported)
+		usb_free_urb(context->tx_urb);
+	usb_free_urb(context->rx_urb);
+	lirc_buffer_free(context->plugin->rbuf);
+	kfree(context->plugin->rbuf);
+	kfree(context->plugin);
+	kfree(context);
+
+	if (debug)
+		info("%s: context deleted", __func__);
+}
+
+static inline void deregister_from_lirc(struct imon_context *context)
+{
+	int retval;
+	int minor = context->plugin->minor;
+
+	retval = lirc_unregister_plugin(minor);
+	if (retval)
+		err("%s: unable to deregister from lirc(%d)",
+			__func__, retval);
+	else
+		info("Deregistered iMON plugin(minor:%d)", minor);
+
+}
+
+/**
+ * Called when the VFD device(e.g. /dev/usb/lcd)
+ * is opened by the application.
+ */
+static int vfd_open(struct inode *inode, struct file *file)
+{
+	struct usb_interface *interface;
+	struct imon_context *context = NULL;
+	int subminor;
+	int retval = SUCCESS;
+
+	/* prevent races with disconnect */
+	down(&disconnect_sem);
+
+	subminor = iminor(inode);
+	interface = usb_find_interface(&imon_driver, subminor);
+	if (!interface) {
+		err("%s: could not find interface for minor %d",
+		    __func__, subminor);
+		retval = -ENODEV;
+		goto exit;
+	}
+	context = usb_get_intfdata(interface);
+
+	if (!context) {
+		err("%s: no context found for minor %d",
+					__func__, subminor);
+		retval = -ENODEV;
+		goto exit;
+	}
+
+	LOCK_CONTEXT;
+
+	if (!context->vfd_supported) {
+		err("%s: VFD not supported by device", __func__);
+		retval = -ENODEV;
+	} else if (context->vfd_isopen) {
+		err("%s: VFD port is already open", __func__);
+		retval = -EBUSY;
+	} else {
+		context->vfd_isopen = TRUE;
+		file->private_data = context;
+		info("VFD port opened");
+	}
+
+	UNLOCK_CONTEXT;
+
+exit:
+	up(&disconnect_sem);
+	return retval;
+}
+
+/**
+ * Called when the VFD device(e.g. /dev/usb/lcd)
+ * is closed by the application.
+ */
+static int vfd_close(struct inode *inode, struct file *file)
+{
+	struct imon_context *context = NULL;
+	int retval = SUCCESS;
+
+	context = (struct imon_context *) file->private_data;
+
+	if (!context) {
+		err("%s: no context for device", __func__);
+		return -ENODEV;
+	}
+
+	LOCK_CONTEXT;
+
+	if (!context->vfd_supported) {
+		err("%s: VFD not supported by device", __func__);
+		retval = -ENODEV;
+	} else if (!context->vfd_isopen) {
+		err("%s: VFD is not open", __func__);
+		retval = -EIO;
+	} else {
+		context->vfd_isopen = FALSE;
+		info("VFD port closed");
+		if (!context->dev_present && !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. */
+			UNLOCK_CONTEXT;
+			delete_context(context);
+			return retval;
+		}
+	}
+
+	UNLOCK_CONTEXT;
+	return retval;
+}
+
+/**
+ * Sends a packet to the VFD.
+ */
+static inline int send_packet(struct imon_context *context)
+{
+	unsigned int pipe;
+	int interval = 0;
+	int retval = SUCCESS;
+	struct usb_ctrlrequest *control_req = NULL;
+
+	/* Check if we need to use control or interrupt urb */
+	if (!context->tx_control) {
+		pipe = usb_sndintpipe(context->dev,
+				      context->tx_endpoint->bEndpointAddress);
+		interval = context->tx_endpoint->bInterval;
+
+		usb_fill_int_urb(context->tx_urb, context->dev, pipe,
+				 context->usb_tx_buf,
+				 sizeof(context->usb_tx_buf),
+				 usb_tx_callback, context, interval);
+
+		context->tx_urb->actual_length = 0;
+	} else {
+		/* fill request into kmalloc'ed space: */
+		control_req = kmalloc(sizeof(struct usb_ctrlrequest), GFP_NOIO);
+		if (control_req == NULL)
+			return -ENOMEM;
+
+		/* setup packet is '21 09 0200 0001 0008' */
+		control_req->bRequestType = 0x21;
+		control_req->bRequest = 0x09;
+		control_req->wValue = cpu_to_le16(0x0002);
+		control_req->wIndex = cpu_to_le16(0x0100);
+		control_req->wLength = cpu_to_le16(0x0800);
+
+		/* control pipe is endpoint 0x00 */
+		pipe = usb_sndctrlpipe(context->dev, 0);
+
+		/* build the control urb */
+		usb_fill_control_urb(context->tx_urb, context->dev, pipe,
+				     (unsigned char *)control_req,
+				     context->usb_tx_buf,
+				     sizeof(context->usb_tx_buf),
+				     usb_tx_callback, context);
+		context->tx_urb->actual_length = 0;
+	}
+
+	init_completion(&context->tx.finished);
+	atomic_set(&(context->tx.busy), 1);
+
+	retval =  usb_submit_urb(context->tx_urb, GFP_KERNEL);
+	if (retval != SUCCESS) {
+		atomic_set(&(context->tx.busy), 0);
+		err("%s: error submitting urb(%d)", __func__, retval);
+	} else {
+		/* Wait for tranmission to complete(or abort) */
+		UNLOCK_CONTEXT;
+		wait_for_completion(&context->tx.finished);
+		LOCK_CONTEXT;
+
+		retval = context->tx.status;
+		if (retval != SUCCESS)
+			err("%s: packet tx failed(%d)", __func__, retval);
+	}
+
+	kfree(control_req);
+
+	return retval;
+}
+
+/**
+ * Sends an associate packet to the iMON 2.4G.
+ *
+ * This might not be such a good idea, since it has an id
+ * collition with some versions of the "IR & VFD" combo.
+ * The only way to determine if it is a RF version is to look
+ * at the product description string.(Which we currently do
+ * not fetch).
+ */
+static inline int send_associate_24g(struct imon_context *context)
+{
+	int retval;
+	const unsigned char packet[8] = { 0x01, 0x00, 0x00, 0x00,
+					  0x00, 0x00, 0x00, 0x20 };
+
+	if (!context) {
+		err("%s: no context for device", __func__);
+		return -ENODEV;
+	}
+
+	LOCK_CONTEXT;
+
+	if (!context->dev_present) {
+		err("%s: no iMON device present", __func__);
+		retval = -ENODEV;
+		goto exit;
+	}
+
+	memcpy(context->usb_tx_buf, packet, sizeof(packet));
+	retval = send_packet(context);
+
+exit:
+	UNLOCK_CONTEXT;
+
+	return retval;
+}
+
+/**
+ * This is the sysfs functions to handle the association og the iMON 2.4G LT.
+ *
+ *
+ */
+
+static ssize_t show_associate_remote(struct device *d,
+				     struct device_attribute *attr,
+				     char *buf)
+{
+	struct imon_context *context = dev_get_drvdata(d);
+
+	if (!context)
+		return -ENODEV;
+
+	if (context->ir_isassociating) {
+		strcpy(buf, "The device it associating press some button "
+			    "on the remote.\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");
+	} else {
+		strcpy(buf, "Device is closed, you need to open it to "
+			    "associate the remote(you can use irw).\n");
+	}
+	return strlen(buf);
+}
+
+static ssize_t store_associate_remote(struct device *d,
+				      struct device_attribute *attr,
+				      const char *buf, size_t count)
+{
+	struct imon_context *context;
+
+	context = dev_get_drvdata(d);
+
+	if (!context)
+		return -ENODEV;
+
+	if (!context->ir_isopen)
+		return -EINVAL;
+
+	if (context->ir_isopen) {
+		context->ir_isassociating = TRUE;
+		send_associate_24g(context);
+	}
+
+	return count;
+}
+
+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,
+	NULL
+};
+
+static struct attribute_group imon_attribute_group = {
+	.attrs = imon_sysfs_entries
+};
+
+
+
+
+/**
+ * Writes data to the VFD.  The IMON VFD is 2x16 characters
+ * and requires data in 5 consecutive USB interrupt packets,
+ * each packet but the last carrying 7 bytes.
+ *
+ * I don't know if the VFD board supports features such as
+ * scrolling, clearing rows, blanking, etc. so at
+ * the caller must provide a full screen of data.  If fewer
+ * than 32 bytes are provided spaces will be appended to
+ * generate a full screen.
+ */
+static ssize_t vfd_write(struct file *file, const char *buf,
+			 size_t n_bytes, loff_t *pos)
+{
+	int i;
+	int offset;
+	int seq;
+	int retval = SUCCESS;
+	struct imon_context *context;
+
+	context = (struct imon_context *) file->private_data;
+	if (!context) {
+		err("%s: no context for device", __func__);
+		return -ENODEV;
+	}
+
+	LOCK_CONTEXT;
+
+	if (!context->dev_present) {
+		err("%s: no iMON device present", __func__);
+		retval = -ENODEV;
+		goto exit;
+	}
+
+	if (n_bytes <= 0 || n_bytes > 32) {
+		err("%s: invalid payload size", __func__);
+		retval = -EINVAL;
+		goto exit;
+	}
+
+	if (copy_from_user(context->tx.data_buf, buf, n_bytes)) {
+		retval = -EFAULT;
+		goto exit;
+	}
+
+	/* Pad with spaces */
+	for (i = n_bytes; i < 32; ++i)
+		context->tx.data_buf[i] = ' ';
+
+	for (i = 32; i < 35; ++i)
+		context->tx.data_buf[i] = 0xFF;
+
+	offset = 0;
+	seq = 0;
+
+	do {
+		memcpy(context->usb_tx_buf, context->tx.data_buf + offset, 7);
+		context->usb_tx_buf[7] = (unsigned char) seq;
+
+		retval = send_packet(context);
+		if (retval != SUCCESS) {
+			err("%s: send packet failed for packet #%d",
+					__func__, seq/2);
+			goto exit;
+		} else {
+			seq += 2;
+			offset += 7;
+		}
+
+	} while (offset < 35);
+
+	if (context->vfd_proto_6p) {
+		/* Send packet #6 */
+		memcpy(context->usb_tx_buf, vfd_packet6, 7);
+		context->usb_tx_buf[7] = (unsigned char) seq;
+		retval = send_packet(context);
+		if (retval != SUCCESS)
+			err("%s: send packet failed for packet #%d",
+					__func__, seq/2);
+	}
+
+exit:
+	UNLOCK_CONTEXT;
+
+	return (retval == SUCCESS) ? n_bytes : retval;
+}
+
+/**
+ * Writes data to the LCD.  The iMON OEM LCD screen excepts 8-byte
+ * packets. We accept data as 16 hexadecimal digits, followed by a
+ * newline (to make it easy to drive the device from a command-line
+ * -- even though the actual binary data is a bit complicated).
+ *
+ * The device itself is not a "traditional" text-mode display. It's
+ * actually a 16x96 pixel bitmap display. That means if you want to
+ * display text, you've got to have your own "font" and translate the
+ * text into bitmaps for display. This is really flexible (you can
+ * display whatever diacritics you need, and so on), but it's also
+ * a lot more complicated than most LCDs...
+ */
+static ssize_t lcd_write(struct file *file, const char *buf,
+			 size_t n_bytes, loff_t *pos)
+{
+	int retval = SUCCESS;
+	struct imon_context *context;
+
+	context = (struct imon_context *) file->private_data;
+	if (!context) {
+		err("%s: no context for device", __func__);
+		return -ENODEV;
+	}
+
+	LOCK_CONTEXT;
+
+	if (!context->dev_present) {
+		err("%s: no iMON device present", __func__);
+		retval = -ENODEV;
+		goto exit;
+	}
+
+	if (n_bytes != 8) {
+		err("%s: invalid payload size: %d (expecting 8)",
+		    __func__, (int) n_bytes);
+		retval = -EINVAL;
+		goto exit;
+	}
+
+	if (copy_from_user(context->usb_tx_buf, buf, 8)) {
+		retval = -EFAULT;
+		goto exit;
+	}
+
+	retval = send_packet(context);
+	if (retval != SUCCESS) {
+		err("%s: send packet failed!", __func__);
+		goto exit;
+	} else if (debug) {
+		info("%s: write %d bytes to LCD", __func__, (int) n_bytes);
+	}
+exit:
+	UNLOCK_CONTEXT;
+	return (retval == SUCCESS) ? n_bytes : retval;
+}
+
+/**
+ * Callback function for USB core API: transmit data
+ */
+static void usb_tx_callback(struct urb *urb)
+{
+	struct imon_context *context;
+
+	if (!urb)
+		return;
+	context = (struct imon_context *) urb->context;
+	if (!context)
+		return;
+
+	context->tx.status = urb->status;
+
+	/* notify waiters that write has finished */
+	atomic_set(&context->tx.busy, 0);
+	complete(&context->tx.finished);
+
+	return;
+}
+
+/**
+ * Called by lirc_dev when the application opens /dev/lirc
+ */
+static int ir_open(void *data)
+{
+	int retval = SUCCESS;
+	struct imon_context *context;
+
+	/* prevent races with disconnect */
+	down(&disconnect_sem);
+
+	context = (struct imon_context *) data;
+
+	LOCK_CONTEXT;
+
+	if (context->ir_isopen) {
+		err("%s: IR port is already open", __func__);
+		retval = -EBUSY;
+		goto exit;
+	}
+
+	/* 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->dev,
+		usb_rcvintpipe(context->dev,
+				context->rx_endpoint->bEndpointAddress),
+		context->usb_rx_buf, sizeof(context->usb_rx_buf),
+		usb_rx_callback, context, context->rx_endpoint->bInterval);
+
+	retval = usb_submit_urb(context->rx_urb, GFP_KERNEL);
+
+	if (retval)
+		err("%s: usb_submit_urb failed for ir_open(%d)",
+		    __func__, retval);
+	else {
+		context->ir_isopen = TRUE;
+		info("IR port opened");
+	}
+
+exit:
+	UNLOCK_CONTEXT;
+
+	up(&disconnect_sem);
+	return SUCCESS;
+}
+
+/**
+ * Called by lirc_dev when the application closes /dev/lirc
+ */
+static void ir_close(void *data)
+{
+	struct imon_context *context;
+
+	context = (struct imon_context *)data;
+	if (!context) {
+		err("%s: no context for device", __func__);
+		return;
+	}
+
+	LOCK_CONTEXT;
+
+	usb_kill_urb(context->rx_urb);
+	context->ir_isopen = FALSE;
+	context->ir_isassociating = FALSE;
+	info("IR port closed");
+
+	if (!context->dev_present) {
+		/* Device disconnected while IR port was
+		 * still open. Plugin was not deregistered
+		 * at disconnect time, so do it now. */
+		deregister_from_lirc(context);
+
+		if (!context->vfd_isopen) {
+			UNLOCK_CONTEXT;
+			delete_context(context);
+			return;
+		}
+		/* If VFD port is open, context will be deleted by vfd_close */
+	}
+
+	UNLOCK_CONTEXT;
+	return;
+}
+
+/**
+ * Convert bit count to time duration(in us) and submit
+ * the value to lirc_dev.
+ */
+static inline void submit_data(struct imon_context *context)
+{
+	unsigned char buf[4];
+	int value = context->rx.count;
+	int i;
+
+	if (debug)
+		info("submitting data to LIRC\n");
+
+	value *= BIT_DURATION;
+	value &= PULSE_MASK;
+	if (context->rx.prev_bit)
+		value |= PULSE_BIT;
+
+	for (i = 0; i < 4; ++i)
+		buf[i] = value>>(i*8);
+
+	lirc_buffer_write_1(context->plugin->rbuf, buf);
+	wake_up(&context->plugin->rbuf->wait_poll);
+	return;
+}
+
+/**
+ * Process the incoming packet
+ */
+static inline void incoming_packet(struct imon_context *context,
+				   struct urb *urb)
+{
+	int len = urb->actual_length;
+	unsigned char *buf = urb->transfer_buffer;
+	int octet, bit;
+	unsigned char mask;
+	int chunk_num;
+#ifdef DEBUG
+	int i;
+#endif
+
+	if (len != 8) {
+		warn("%s: invalid incoming packet size(%d)",
+		     __func__, len);
+		return;
+	}
+
+	/* iMON 2.4G associate frame */
+	if (buf[0] == 0x00 &&
+	    buf[2] == 0xFF &&				/* REFID */
+	    buf[3] == 0xFF &&
+	    buf[4] == 0xFF &&
+	    buf[5] == 0xFF &&				/* iMON 2.4G */
+	   ((buf[6] == 0x4E && buf[7] == 0xDF) ||	/* LT */
+	    (buf[6] == 0x5E && buf[7] == 0xDF))) {	/* DT */
+		warn("%s: remote associated refid=%02X", __func__, buf[1]);
+		context->ir_isassociating = FALSE;
+	}
+
+	chunk_num = buf[7];
+
+	if (chunk_num == 0xFF)
+		return;		/* filler frame, no data here */
+
+	if (buf[0] == 0xFF &&
+	    buf[1] == 0xFF &&
+	    buf[2] == 0xFF &&
+	    buf[3] == 0xFF &&
+	    buf[4] == 0xFF &&
+	    buf[5] == 0xFF &&				/* iMON 2.4G */
+	    ((buf[6] == 0x4E && buf[7] == 0xAF) ||	/* LT */
+	     (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 (context->ir_onboard_decode) {
+		/* The signals have been decoded onboard the iMON controller */
+		lirc_buffer_write_1(context->plugin->rbuf, buf);
+		wake_up(&context->plugin->rbuf->wait_poll);
+		return;
+	}
+
+	/* Translate received data to pulse and space lengths.
+	 * Received data is active low, i.e. pulses are 0 and
+	 * spaces are 1.
+	 *
+	 * My original algorithm was essentially similar to
+	 * Changwoo Ryu's with the exception that he switched
+	 * the incoming bits to active high and also fed an
+	 * initial space to LIRC at the start of a new sequence
+	 * if the previous bit was a pulse.
+	 *
+	 * I've decided to adopt his algorithm. */
+
+	if (chunk_num == 1 && context->rx.initial_space) {
+		/* LIRC requires a leading space */
+		context->rx.prev_bit = 0;
+		context->rx.count = 4;
+		submit_data(context);
+		context->rx.count = 0;
+	}
+
+	for (octet = 0; octet < 5; ++octet) {
+		mask = 0x80;
+		for (bit = 0; bit < 8; ++bit) {
+			int curr_bit = !(buf[octet] & mask);
+			if (curr_bit != context->rx.prev_bit) {
+				if (context->rx.count) {
+					submit_data(context);
+					context->rx.count = 0;
+				}
+				context->rx.prev_bit = curr_bit;
+			}
+			++context->rx.count;
+			mask >>= 1;
+		}
+	}
+
+	if (chunk_num == 10) {
+		if (context->rx.count) {
+			submit_data(context);
+			context->rx.count = 0;
+		}
+		context->rx.initial_space = context->rx.prev_bit;
+	}
+}
+
+/**
+ * Callback function for USB core API: receive data
+ */
+static void usb_rx_callback(struct urb *urb)
+{
+	struct imon_context *context;
+
+	if (!urb)
+		return;
+	context = (struct imon_context *) urb->context;
+	if (!context)
+		return;
+
+	switch (urb->status) {
+	case -ENOENT:		/* usbcore unlink successful! */
+		return;
+	case SUCCESS:
+		if (context->ir_isopen)
+			incoming_packet(context, urb);
+		break;
+	default:
+		warn("%s: status(%d): ignored", __func__, urb->status);
+		break;
+	}
+
+	usb_submit_urb(context->rx_urb, GFP_ATOMIC);
+	return;
+}
+
+
+
+/**
+ * Callback function for USB core API: Probe
+ */
+static int imon_probe(struct usb_interface *interface,
+		      const struct usb_device_id *id)
+{
+	struct usb_device *dev = NULL;
+	struct usb_host_interface *iface_desc = NULL;
+	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_plugin *plugin = NULL;
+	struct lirc_buffer *rbuf = NULL;
+	int lirc_minor = 0;
+	int num_endpoints;
+	int retval = SUCCESS;
+	int vfd_ep_found;
+	int ir_ep_found;
+	int alloc_status;
+	int vfd_proto_6p = FALSE;
+	int ir_onboard_decode = FALSE;
+	int tx_control = FALSE;
+	struct imon_context *context = NULL;
+	int i;
+
+	info("%s: found IMON device", __func__);
+
+	/*
+	 * If it's the LCD, as opposed to the VFD, we just need to replace
+	 * the "write" file op.
+	 */
+	if (is_lcd)
+		vfd_fops.write = &lcd_write;
+
+	dev = usb_get_dev(interface_to_usbdev(interface));
+	iface_desc = interface->cur_altsetting;
+	num_endpoints = iface_desc->desc.bNumEndpoints;
+
+	/*
+	 * Scan the endpoint list and set:
+	 *	first input endpoint = IR endpoint
+	 *	first output endpoint = VFD endpoint
+	 */
+
+	ir_ep_found = FALSE;
+	vfd_ep_found = FALSE;
+
+	for (i = 0; i < num_endpoints && !(ir_ep_found && vfd_ep_found); ++i) {
+		struct usb_endpoint_descriptor *ep;
+		int ep_dir;
+		int ep_type;
+		ep = &iface_desc->endpoint[i].desc;
+		ep_dir = ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK;
+		ep_type = ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
+
+		if (!ir_ep_found &&
+			ep_dir == USB_DIR_IN &&
+			ep_type == USB_ENDPOINT_XFER_INT) {
+
+			rx_endpoint = ep;
+			ir_ep_found = TRUE;
+			if (debug)
+				info("%s: found IR endpoint", __func__);
+
+		} else if (!vfd_ep_found &&
+			   ep_dir == USB_DIR_OUT &&
+			   ep_type == USB_ENDPOINT_XFER_INT) {
+			tx_endpoint = ep;
+			vfd_ep_found = TRUE;
+			if (debug)
+				info("%s: found VFD endpoint", __func__);
+		}
+	}
+
+	/*
+	 * If we didn't find a vfd endpoint, and we have a next-gen LCD,
+	 * use control urb instead of interrupt
+	 */
+	if (!vfd_ep_found) {
+		if (usb_match_id(interface, lcd_control_endpoint_list)) {
+			tx_control = 1;
+			vfd_ep_found = TRUE;
+			if (debug)
+				info("%s: LCD device uses control endpoint, "
+				     "not interface OUT endpoint", __func__);
+		}
+	}
+
+	/* Input endpoint is mandatory */
+	if (!ir_ep_found) {
+		err("%s: no valid input(IR) endpoint found.", __func__);
+		retval = -ENODEV;
+		goto exit;
+	} else {
+		/* Determine if the IR signals are decoded onboard */
+		if (usb_match_id(interface, ir_onboard_decode_list))
+			ir_onboard_decode = TRUE;
+
+		if (debug)
+			info("ir_onboard_decode: %d", ir_onboard_decode);
+	}
+
+	/* Determine if VFD requires 6 packets */
+	if (vfd_ep_found) {
+		if (usb_match_id(interface, vfd_proto_6p_list))
+			vfd_proto_6p = TRUE;
+
+		if (debug)
+			info("vfd_proto_6p: %d", vfd_proto_6p);
+	}
+
+
+	/* Allocate memory */
+
+	alloc_status = SUCCESS;
+
+	context = kmalloc(sizeof(struct imon_context), GFP_KERNEL);
+	if (!context) {
+		err("%s: kmalloc failed for context", __func__);
+		alloc_status = 1;
+		goto alloc_status_switch;
+	}
+	plugin = kmalloc(sizeof(struct lirc_plugin), GFP_KERNEL);
+	if (!plugin) {
+		err("%s: kmalloc failed for lirc_plugin", __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;
+	}
+	if (vfd_ep_found) {
+		tx_urb = usb_alloc_urb(0, GFP_KERNEL);
+		if (!tx_urb) {
+			err("%s: usb_alloc_urb failed for VFD urb",
+			    __func__);
+			alloc_status = 6;
+			goto alloc_status_switch;
+		}
+	}
+
+	/* clear all members of imon_context and lirc_plugin */
+	memset(context, 0, sizeof(struct imon_context));
+	mutex_init(&context->lock);
+	context->vfd_proto_6p = vfd_proto_6p;
+	context->ir_onboard_decode = ir_onboard_decode;
+
+	memset(plugin, 0, sizeof(struct lirc_plugin));
+
+	strcpy(plugin->name, MOD_NAME);
+	plugin->minor = -1;
+	plugin->code_length = (ir_onboard_decode) ?
+		32 : sizeof(int) * 8;
+	plugin->sample_rate = 0;
+	plugin->features = (ir_onboard_decode) ?
+		LIRC_CAN_REC_LIRCCODE : LIRC_CAN_REC_MODE2;
+	plugin->data = context;
+	plugin->rbuf = rbuf;
+	plugin->set_use_inc = ir_open;
+	plugin->set_use_dec = ir_close;
+	plugin->dev = &dev->dev;
+	plugin->owner = THIS_MODULE;
+
+	LOCK_CONTEXT;
+
+	lirc_minor = lirc_register_plugin(plugin);
+	if (lirc_minor < 0) {
+		err("%s: lirc_register_plugin failed", __func__);
+		alloc_status = 7;
+		UNLOCK_CONTEXT;
+		goto alloc_status_switch;
+	} else
+		info("%s: Registered iMON plugin(minor:%d)",
+		     __func__, lirc_minor);
+
+	/* Needed while unregistering! */
+	plugin->minor = lirc_minor;
+
+	context->dev = dev;
+	context->dev_present = TRUE;
+	context->rx_endpoint = rx_endpoint;
+	context->rx_urb = rx_urb;
+	if (vfd_ep_found) {
+		context->vfd_supported = TRUE;
+		context->tx_endpoint = tx_endpoint;
+		context->tx_urb = tx_urb;
+		context->tx_control = tx_control;
+	}
+	context->plugin = plugin;
+
+	usb_set_intfdata(interface, context);
+
+	if (cpu_to_le16(dev->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);
+	}
+
+	if (vfd_ep_found) {
+		if (debug)
+			info("Registering VFD with sysfs");
+		if (usb_register_dev(interface, &imon_class)) {
+			/* Not a fatal error, so ignore */
+			info("%s: could not get a minor number for VFD",
+				__func__);
+		}
+	}
+
+	info("%s: iMON device on usb<%d:%d> initialized",
+			__func__, dev->bus->busnum, dev->devnum);
+
+	UNLOCK_CONTEXT;
+
+alloc_status_switch:
+
+	switch (alloc_status) {
+	case 7:
+		if (vfd_ep_found)
+			usb_free_urb(tx_urb);
+	case 6:
+		usb_free_urb(rx_urb);
+	case 5:
+		lirc_buffer_free(rbuf);
+	case 4:
+		kfree(rbuf);
+	case 3:
+		kfree(plugin);
+	case 2:
+		kfree(context);
+		context = NULL;
+	case 1:
+		retval = -ENOMEM;
+	case SUCCESS:
+		;
+	}
+
+exit:
+	return retval;
+}
+
+/**
+ * Callback function for USB core API: disonnect
+ */
+static void imon_disconnect(struct usb_interface *interface)
+{
+	struct imon_context *context;
+
+	/* prevent races with ir_open()/vfd_open() */
+	down(&disconnect_sem);
+
+	context = usb_get_intfdata(interface);
+	LOCK_CONTEXT;
+
+	info("%s: iMON device disconnected", __func__);
+
+	/* 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);
+	context->dev_present = FALSE;
+
+	/* Stop reception */
+	usb_kill_urb(context->rx_urb);
+
+	/* Abort ongoing write */
+	if (atomic_read(&context->tx.busy)) {
+		usb_kill_urb(context->tx_urb);
+		wait_for_completion(&context->tx.finished);
+	}
+
+	/* De-register from lirc_dev if IR port is not open */
+	if (!context->ir_isopen)
+		deregister_from_lirc(context);
+
+	if (context->vfd_supported)
+		usb_deregister_dev(interface, &imon_class);
+
+	UNLOCK_CONTEXT;
+
+	if (!context->ir_isopen && !context->vfd_isopen)
+		delete_context(context);
+
+	up(&disconnect_sem);
+}
+
+static int __init imon_init(void)
+{
+	int rc;
+
+	info(MOD_DESC ", v" MOD_VERSION);
+	info(MOD_AUTHOR);
+
+	rc = usb_register(&imon_driver);
+	if (rc) {
+		err("%s: usb register failed(%d)", __func__, rc);
+		return -ENODEV;
+	}
+	return SUCCESS;
+}
+
+static void __exit imon_exit(void)
+{
+	usb_deregister(&imon_driver);
+	info("module removed. Goodbye!");
+}
+
+#ifdef MODULE
+module_init(imon_init);
+module_exit(imon_exit);
+
+#else
+subsys_initcall(imon_init);
+
+#endif /* MODULE */
diff --git a/drivers/input/lirc/lirc_it87.c b/drivers/input/lirc/lirc_it87.c
new file mode 100644
index 0000000..0a64847
--- /dev/null
+++ b/drivers/input/lirc/lirc_it87.c
@@ -0,0 +1,999 @@
+/*
+ * LIRC driver for ITE IT8712/IT8705 CIR port
+ *
+ * Copyright (C) 2001 Hans-Gunter Lutke 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
+ * 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
+ *
+ * ITE IT8705 and IT8712(not tested) 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
+ *
+ * 2001/02/27 Christoph Bartelmus <lirc@bartelmus.de> :
+ *   reimplemented read function
+ * 2005/06/05 Andrew Calkin implemented support for Asus Digimatrix,
+ *   based on work of the following member of the Outertrack Digimatrix
+ *   Forum: Art103 <r_tay@hotmail.com>
+ */
+
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/autoconf.h>
+
+
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/serial_reg.h>
+#include <linux/time.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+#include <linux/mm.h>
+#include <linux/delay.h>
+#include <linux/poll.h>
+#include <asm/system.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/fcntl.h>
+
+#include <linux/timer.h>
+
+#include "lirc.h"
+#include "lirc_dev.h"
+
+#include "lirc_it87.h"
+
+#ifdef LIRC_IT87_DIGIMATRIX
+static int digimatrix = 1;
+static int it87_freq = 36; /* kHz */
+static int irq = 9;
+#else
+static int digimatrix;
+static int it87_freq = 38; /* kHz */
+static int irq = IT87_CIR_DEFAULT_IRQ;
+#endif
+
+static unsigned long it87_bits_in_byte_out;
+static unsigned long it87_send_counter;
+static unsigned char it87_RXEN_mask = IT87_CIR_RCR_RXEN;
+
+#define RBUF_LEN 1024
+#define WBUF_LEN 1024
+
+#define LIRC_DRIVER_NAME "lirc_it87"
+
+/* timeout for sequences in jiffies (=5/100s) */
+/* must be longer than TIME_CONST */
+#define IT87_TIMEOUT	(HZ*5/100)
+
+/* insmod parameters */
+static int debug;
+#define dprintk(fmt, args...)					\
+	do {							\
+		if (debug)					\
+			printk(KERN_DEBUG LIRC_DRIVER_NAME ": "	\
+			       fmt, ## args);			\
+	} while (0)
+
+static int io = IT87_CIR_DEFAULT_IOBASE;
+/* receiver demodulator default: off */
+static int it87_enable_demodulator;
+
+static int timer_enabled;
+static DEFINE_SPINLOCK(timer_lock);
+static struct timer_list timerlist;
+/* time of last signal change detected */
+static struct timeval last_tv = {0, 0};
+/* time of last UART data ready interrupt */
+static struct timeval last_intr_tv = {0, 0};
+static int last_value;
+
+static DECLARE_WAIT_QUEUE_HEAD(lirc_read_queue);
+
+static DEFINE_SPINLOCK(hardware_lock);
+static DEFINE_SPINLOCK(dev_lock);
+
+static int rx_buf[RBUF_LEN];
+unsigned int rx_tail, rx_head;
+static int tx_buf[WBUF_LEN];
+
+/* SECTION: Prototypes */
+
+/* Communication with user-space */
+static int lirc_open(struct inode *inode, struct file *file);
+static int lirc_close(struct inode *inode, struct file *file);
+static unsigned int lirc_poll(struct file *file, poll_table *wait);
+static ssize_t lirc_read(struct file *file, char *buf,
+			 size_t count, loff_t *ppos);
+static ssize_t lirc_write(struct file *file, const char *buf,
+			  size_t n, loff_t *pos);
+static int lirc_ioctl(struct inode *node, struct file *filep,
+		      unsigned int cmd, unsigned long arg);
+static void add_read_queue(int flag, unsigned long val);
+#ifdef MODULE
+static int init_chrdev(void);
+static void drop_chrdev(void);
+#endif
+	/* Hardware */
+static irqreturn_t it87_interrupt(int irq, void *dev_id);
+static void send_space(unsigned long len);
+static void send_pulse(unsigned long len);
+static void init_send(void);
+static void terminate_send(unsigned long len);
+static int init_hardware(void);
+static void drop_hardware(void);
+	/* Initialisation */
+static int init_port(void);
+static void drop_port(void);
+
+
+/* SECTION: Communication with user-space */
+
+static int lirc_open(struct inode *inode, struct file *file)
+{
+	spin_lock(&dev_lock);
+	if (module_refcount(THIS_MODULE)) {
+		spin_unlock(&dev_lock);
+		return -EBUSY;
+	}
+	spin_unlock(&dev_lock);
+	return 0;
+}
+
+
+static int lirc_close(struct inode *inode, struct file *file)
+{
+	return 0;
+}
+
+
+static unsigned int lirc_poll(struct file *file, poll_table *wait)
+{
+	poll_wait(file, &lirc_read_queue, wait);
+	if (rx_head != rx_tail)
+		return POLLIN | POLLRDNORM;
+	return 0;
+}
+
+
+static ssize_t lirc_read(struct file *file, char *buf,
+			 size_t count, loff_t *ppos)
+{
+	int n = 0;
+	int retval = 0;
+
+	while (n < count) {
+		if (file->f_flags & O_NONBLOCK && rx_head == rx_tail) {
+			retval = -EAGAIN;
+			break;
+		}
+		retval = wait_event_interruptible(lirc_read_queue,
+						  rx_head != rx_tail);
+		if (retval)
+			break;
+
+		if (copy_to_user((void *) buf + n, (void *) (rx_buf + rx_head),
+				 sizeof(int))) {
+			retval = -EFAULT;
+			break;
+		}
+		rx_head = (rx_head + 1) & (RBUF_LEN - 1);
+		n += sizeof(int);
+	}
+	if (n)
+		return n;
+	return retval;
+}
+
+
+static ssize_t lirc_write(struct file *file, const char *buf,
+			  size_t n, loff_t *pos)
+{
+	int i = 0;
+
+	if (n % sizeof(int) || (n / sizeof(int)) > WBUF_LEN)
+		return -EINVAL;
+	if (copy_from_user(tx_buf, buf, n))
+		return -EFAULT;
+	n /= sizeof(int);
+	init_send();
+	while (1) {
+		if (i >= n)
+			break;
+		if (tx_buf[i])
+			send_pulse(tx_buf[i]);
+		i++;
+		if (i >= n)
+			break;
+		if (tx_buf[i])
+			send_space(tx_buf[i]);
+		i++;
+	}
+	terminate_send(tx_buf[i - 1]);
+	return n;
+}
+
+
+static int lirc_ioctl(struct inode *node, struct file *filep,
+		      unsigned int cmd, unsigned long arg)
+{
+	int retval = 0;
+	unsigned long value = 0;
+	unsigned int ivalue;
+	unsigned long hw_flags;
+
+	if (cmd == LIRC_GET_FEATURES)
+		value = LIRC_CAN_SEND_PULSE |
+			LIRC_CAN_SET_SEND_CARRIER |
+			LIRC_CAN_REC_MODE2;
+	else if (cmd == LIRC_GET_SEND_MODE)
+		value = LIRC_MODE_PULSE;
+	else if (cmd == LIRC_GET_REC_MODE)
+		value = LIRC_MODE_MODE2;
+
+	switch (cmd) {
+	case LIRC_GET_FEATURES:
+	case LIRC_GET_SEND_MODE:
+	case LIRC_GET_REC_MODE:
+		retval = put_user(value, (unsigned long *) arg);
+		break;
+
+	case LIRC_SET_SEND_MODE:
+	case LIRC_SET_REC_MODE:
+		retval = get_user(value, (unsigned long *) arg);
+		break;
+
+	case LIRC_SET_SEND_CARRIER:
+		retval = get_user(ivalue, (unsigned int *) arg);
+		if (retval)
+			return retval;
+		ivalue /= 1000;
+		if (ivalue > IT87_CIR_FREQ_MAX ||
+		    ivalue < IT87_CIR_FREQ_MIN)
+			return -EINVAL;
+
+		it87_freq = ivalue;
+
+		spin_lock_irqsave(&hardware_lock, hw_flags);
+		outb(((inb(io + IT87_CIR_TCR2) & IT87_CIR_TCR2_TXMPW) |
+		      (it87_freq - IT87_CIR_FREQ_MIN) << 3),
+		      io + IT87_CIR_TCR2);
+		spin_unlock_irqrestore(&hardware_lock, hw_flags);
+		dprintk("demodulation frequency: %d kHz\n", it87_freq);
+
+		break;
+
+	default:
+		retval = -ENOIOCTLCMD;
+	}
+
+	if (retval)
+		return retval;
+
+	if (cmd == LIRC_SET_REC_MODE) {
+		if (value != LIRC_MODE_MODE2)
+			retval = -ENOSYS;
+	} else if (cmd == LIRC_SET_SEND_MODE) {
+		if (value != LIRC_MODE_PULSE)
+			retval = -ENOSYS;
+	}
+	return retval;
+}
+
+static void add_read_queue(int flag, unsigned long val)
+{
+	unsigned int new_rx_tail;
+	int newval;
+
+	dprintk("add flag %d with val %lu\n", flag, val);
+
+	newval = val & PULSE_MASK;
+
+	/* statistically pulses are ~TIME_CONST/2 too long: we could
+	   maybe make this more exactly but this is good enough */
+	if (flag) {
+		/* pulse */
+		if (newval > TIME_CONST / 2)
+			newval -= TIME_CONST / 2;
+		else /* should not ever happen */
+			newval = 1;
+		newval |= PULSE_BIT;
+	} else
+		newval += TIME_CONST / 2;
+	new_rx_tail = (rx_tail + 1) & (RBUF_LEN - 1);
+	if (new_rx_tail == rx_head) {
+		dprintk("Buffer overrun.\n");
+		return;
+	}
+	rx_buf[rx_tail] = newval;
+	rx_tail = new_rx_tail;
+	wake_up_interruptible(&lirc_read_queue);
+}
+
+
+static struct file_operations lirc_fops = {
+	.read		= lirc_read,
+	.write		= lirc_write,
+	.poll		= lirc_poll,
+	.ioctl		= lirc_ioctl,
+	.open		= lirc_open,
+	.release	= lirc_close,
+};
+
+static int set_use_inc(void *data)
+{
+       return 0;
+}
+
+static void set_use_dec(void *data)
+{
+}
+
+static struct lirc_plugin plugin = {
+       .name		= LIRC_DRIVER_NAME,
+       .minor		= -1,
+       .code_length	= 1,
+       .sample_rate	= 0,
+       .data		= NULL,
+       .add_to_buf	= NULL,
+       .get_queue	= NULL,
+       .set_use_inc	= set_use_inc,
+       .set_use_dec	= set_use_dec,
+       .fops		= &lirc_fops,
+       .dev		= NULL,
+       .owner		= THIS_MODULE,
+};
+
+
+#ifdef MODULE
+static int init_chrdev(void)
+{
+	plugin.minor = lirc_register_plugin(&plugin);
+
+	if (plugin.minor < 0) {
+		printk(KERN_ERR LIRC_DRIVER_NAME ": init_chrdev() failed.\n");
+		return -EIO;
+	}
+	return 0;
+}
+
+
+static void drop_chrdev(void)
+{
+	lirc_unregister_plugin(plugin.minor);
+}
+#endif
+
+
+/* SECTION: Hardware */
+static long delta(struct timeval *tv1, struct timeval *tv2)
+{
+	unsigned long deltv;
+
+	deltv = tv2->tv_sec - tv1->tv_sec;
+	if (deltv > 15)
+		deltv = 0xFFFFFF;
+	else
+		deltv = deltv*1000000 + tv2->tv_usec - tv1->tv_usec;
+	return deltv;
+}
+
+static void it87_timeout(unsigned long data)
+{
+	unsigned long flags;
+
+	/* avoid interference with interrupt */
+	spin_lock_irqsave(&timer_lock, flags);
+
+	if (digimatrix) {
+		/* We have timed out.
+		   Disable the RX mechanism.
+		*/
+
+		outb((inb(io + IT87_CIR_RCR) & ~IT87_CIR_RCR_RXEN) |
+		     IT87_CIR_RCR_RXACT, io + IT87_CIR_RCR);
+		if (it87_RXEN_mask)
+			outb(inb(io + IT87_CIR_RCR) | IT87_CIR_RCR_RXEN,
+			     io + IT87_CIR_RCR);
+		dprintk(" TIMEOUT\n");
+		timer_enabled = 0;
+
+		/* fifo clear */
+		outb(inb(io + IT87_CIR_TCR1) | IT87_CIR_TCR1_FIFOCLR,
+		     io+IT87_CIR_TCR1);
+
+	} else {
+		/* if last received signal was a pulse, but receiving
+		   stopped within the 9 bit frame, we need to finish
+		   this pulse and simulate a signal change to from
+		   pulse to space. Otherwise upper layers will receive
+		   two sequences next time. */
+
+		if (last_value) {
+			unsigned long pulse_end;
+
+			/* determine 'virtual' pulse end: */
+			pulse_end = delta(&last_tv, &last_intr_tv);
+			dprintk("timeout add %d for %lu usec\n",
+				last_value, pulse_end);
+			add_read_queue(last_value, pulse_end);
+			last_value = 0;
+			last_tv = last_intr_tv;
+		}
+	}
+	spin_unlock_irqrestore(&timer_lock, flags);
+}
+
+static irqreturn_t it87_interrupt(int irq, void *dev_id)
+{
+	unsigned char data;
+	struct timeval curr_tv;
+	static unsigned long deltv;
+	unsigned long deltintrtv;
+	unsigned long flags, hw_flags;
+	int iir, lsr;
+	int fifo = 0;
+	static char lastbit;
+	char bit;
+
+	/* Bit duration in microseconds */
+	const unsigned long bit_duration = 1000000ul /
+		(115200 / IT87_CIR_BAUDRATE_DIVISOR);
+
+
+	iir = inb(io + IT87_CIR_IIR);
+
+	switch (iir & IT87_CIR_IIR_IID) {
+	case 0x4:
+	case 0x6:
+		lsr = inb(io + IT87_CIR_RSR) & (IT87_CIR_RSR_RXFTO |
+						IT87_CIR_RSR_RXFBC);
+		fifo = lsr & IT87_CIR_RSR_RXFBC;
+		dprintk("iir: 0x%x fifo: 0x%x\n", iir, lsr);
+
+		/* avoid interference with timer */
+		spin_lock_irqsave(&timer_lock, flags);
+		spin_lock_irqsave(&hardware_lock, hw_flags);
+		if (digimatrix) {
+			static unsigned long acc_pulse;
+			static unsigned long acc_space;
+
+			do {
+				data = inb(io + IT87_CIR_DR);
+				data = ~data;
+				fifo--;
+				if (data != 0x00) {
+					if (timer_enabled)
+						del_timer(&timerlist);
+					/* start timer for end of
+					 * sequence detection */
+					timerlist.expires = jiffies +
+							    IT87_TIMEOUT;
+					add_timer(&timerlist);
+					timer_enabled = 1;
+				}
+				/* Loop through */
+				for (bit = 0; bit < 8; ++bit) {
+					if ((data >> bit) & 1) {
+						++acc_pulse;
+						if (lastbit == 0) {
+							add_read_queue(0,
+								acc_space *
+								 bit_duration);
+							acc_space = 0;
+						}
+					} else {
+						++acc_space;
+						if (lastbit == 1) {
+							add_read_queue(1,
+								acc_pulse *
+								 bit_duration);
+							acc_pulse = 0;
+						}
+					}
+					lastbit = (data >> bit) & 1;
+				}
+
+			} while (fifo != 0);
+		} else { /* Normal Operation */
+			do {
+				del_timer(&timerlist);
+				data = inb(io + IT87_CIR_DR);
+
+				dprintk("data=%.2x\n", data);
+				do_gettimeofday(&curr_tv);
+				deltv = delta(&last_tv, &curr_tv);
+				deltintrtv = delta(&last_intr_tv, &curr_tv);
+
+				dprintk("t %lu , d %d\n",
+					deltintrtv, (int)data);
+
+				/* if nothing came in last 2 cycles,
+				   it was gap */
+				if (deltintrtv > TIME_CONST * 2) {
+					if (last_value) {
+						dprintk("GAP\n");
+
+						/* simulate signal change */
+						add_read_queue(last_value,
+							       deltv -
+							       deltintrtv);
+						last_value = 0;
+						last_tv.tv_sec =
+							last_intr_tv.tv_sec;
+						last_tv.tv_usec =
+							last_intr_tv.tv_usec;
+						deltv = deltintrtv;
+					}
+				}
+				data = 1;
+				if (data ^ last_value) {
+					/* deltintrtv > 2*TIME_CONST,
+					   remember ? */
+					/* the other case is timeout */
+					add_read_queue(last_value,
+						       deltv-TIME_CONST);
+					last_value = data;
+					last_tv = curr_tv;
+					if (last_tv.tv_usec >= TIME_CONST)
+						last_tv.tv_usec -= TIME_CONST;
+					else {
+						last_tv.tv_sec--;
+						last_tv.tv_usec += 1000000 -
+							TIME_CONST;
+					}
+				}
+				last_intr_tv = curr_tv;
+				if (data) {
+					/* start timer for end of
+					 * sequence detection */
+					timerlist.expires =
+						jiffies + IT87_TIMEOUT;
+					add_timer(&timerlist);
+				}
+				outb((inb(io + IT87_CIR_RCR) &
+				     ~IT87_CIR_RCR_RXEN) |
+				     IT87_CIR_RCR_RXACT,
+				     io + IT87_CIR_RCR);
+				if (it87_RXEN_mask)
+					outb(inb(io + IT87_CIR_RCR) |
+					     IT87_CIR_RCR_RXEN,
+					     io + IT87_CIR_RCR);
+				fifo--;
+			} while (fifo != 0);
+		}
+		spin_unlock_irqrestore(&hardware_lock, hw_flags);
+		spin_unlock_irqrestore(&timer_lock, flags);
+
+		return IRQ_RETVAL(IRQ_HANDLED);
+
+	default:
+		/* not our irq */
+		dprintk("unknown IRQ (shouldn't happen) !!\n");
+		return IRQ_RETVAL(IRQ_NONE);
+	}
+}
+
+
+static void send_it87(unsigned long len, unsigned long stime,
+		      unsigned char send_byte, unsigned int count_bits)
+{
+	long count = len / stime;
+	long time_left = 0;
+	static unsigned char byte_out;
+	unsigned long hw_flags;
+
+	dprintk("%s: len=%ld, sb=%d\n", __func__, len, send_byte);
+
+	time_left = (long)len - (long)count * (long)stime;
+	count += ((2 * time_left) / stime);
+	while (count) {
+		long i = 0;
+		for (i = 0; i < count_bits; i++) {
+			byte_out = (byte_out << 1) | (send_byte & 1);
+			it87_bits_in_byte_out++;
+		}
+		if (it87_bits_in_byte_out == 8) {
+			dprintk("out=0x%x, tsr_txfbc: 0x%x\n",
+				byte_out,
+				inb(io + IT87_CIR_TSR) &
+				IT87_CIR_TSR_TXFBC);
+
+			while ((inb(io + IT87_CIR_TSR) &
+				IT87_CIR_TSR_TXFBC) >= IT87_CIR_FIFO_SIZE)
+				;
+
+			spin_lock_irqsave(&hardware_lock, hw_flags);
+			outb(byte_out, io + IT87_CIR_DR);
+			spin_unlock_irqrestore(&hardware_lock, hw_flags);
+
+			it87_bits_in_byte_out = 0;
+			it87_send_counter++;
+			byte_out = 0;
+		}
+		count--;
+	}
+}
+
+
+/*
+maybe: exchange space and pulse because
+it8705 only modulates 0-bits
+*/
+
+
+static void send_space(unsigned long len)
+{
+	send_it87(len, TIME_CONST, IT87_CIR_SPACE, IT87_CIR_BAUDRATE_DIVISOR);
+}
+
+static void send_pulse(unsigned long len)
+{
+	send_it87(len, TIME_CONST, IT87_CIR_PULSE, IT87_CIR_BAUDRATE_DIVISOR);
+}
+
+
+static void init_send()
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&hardware_lock, flags);
+	/* RXEN=0: receiver disable */
+	it87_RXEN_mask = 0;
+	outb(inb(io + IT87_CIR_RCR) & ~IT87_CIR_RCR_RXEN,
+	     io + IT87_CIR_RCR);
+	spin_unlock_irqrestore(&hardware_lock, flags);
+	it87_bits_in_byte_out = 0;
+	it87_send_counter = 0;
+}
+
+
+static void terminate_send(unsigned long len)
+{
+	unsigned long flags;
+	unsigned long last = 0;
+
+	last = it87_send_counter;
+	/* make sure all necessary data has been sent */
+	while (last == it87_send_counter)
+		send_space(len);
+	/* wait until all data sent */
+	while ((inb(io + IT87_CIR_TSR) & IT87_CIR_TSR_TXFBC) != 0)
+		;
+	/* then reenable receiver */
+	spin_lock_irqsave(&hardware_lock, flags);
+	it87_RXEN_mask = IT87_CIR_RCR_RXEN;
+	outb(inb(io + IT87_CIR_RCR) | IT87_CIR_RCR_RXEN,
+	     io + IT87_CIR_RCR);
+	spin_unlock_irqrestore(&hardware_lock, flags);
+}
+
+
+static int init_hardware(void)
+{
+	unsigned long flags;
+	unsigned char it87_rcr = 0;
+
+	spin_lock_irqsave(&hardware_lock, flags);
+	/* init cir-port */
+	/* enable r/w-access to Baudrate-Register */
+	outb(IT87_CIR_IER_BR, io + IT87_CIR_IER);
+	outb(IT87_CIR_BAUDRATE_DIVISOR % 0x100, io+IT87_CIR_BDLR);
+	outb(IT87_CIR_BAUDRATE_DIVISOR / 0x100, io+IT87_CIR_BDHR);
+	/* Baudrate Register off, define IRQs: Input only */
+	if (digimatrix) {
+		outb(IT87_CIR_IER_IEC | IT87_CIR_IER_RFOIE, io + IT87_CIR_IER);
+		/* RX: HCFS=0, RXDCR = 001b (33,75..38,25 kHz), RXEN=1 */
+	} else {
+		outb(IT87_CIR_IER_IEC | IT87_CIR_IER_RDAIE, io + IT87_CIR_IER);
+		/* RX: HCFS=0, RXDCR = 001b (35,6..40,3 kHz), RXEN=1 */
+	}
+	it87_rcr = (IT87_CIR_RCR_RXEN & it87_RXEN_mask) | 0x1;
+	if (it87_enable_demodulator)
+		it87_rcr |= IT87_CIR_RCR_RXEND;
+	outb(it87_rcr, io + IT87_CIR_RCR);
+	if (digimatrix) {
+		/* Set FIFO depth to 1 byte, and disable TX */
+		outb(inb(io + IT87_CIR_TCR1) |  0x00,
+		     io + IT87_CIR_TCR1);
+
+		/* TX: it87_freq (36kHz),
+		   'reserved' sensitivity setting (0x00) */
+		outb(((it87_freq - IT87_CIR_FREQ_MIN) << 3) | 0x00,
+		     io + IT87_CIR_TCR2);
+	} else {
+		/* TX: 38kHz, 13,3us (pulse-width */
+		outb(((it87_freq - IT87_CIR_FREQ_MIN) << 3) | 0x06,
+		     io + IT87_CIR_TCR2);
+	}
+	spin_unlock_irqrestore(&hardware_lock, flags);
+	return 0;
+}
+
+
+static void drop_hardware(void)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&hardware_lock, flags);
+	disable_irq(irq);
+	/* receiver disable */
+	it87_RXEN_mask = 0;
+	outb(0x1, io + IT87_CIR_RCR);
+	/* turn off irqs */
+	outb(0, io + IT87_CIR_IER);
+	/* fifo clear */
+	outb(IT87_CIR_TCR1_FIFOCLR, io+IT87_CIR_TCR1);
+	/* reset */
+	outb(IT87_CIR_IER_RESET, io+IT87_CIR_IER);
+	enable_irq(irq);
+	spin_unlock_irqrestore(&hardware_lock, flags);
+}
+
+
+static unsigned char it87_read(unsigned char port)
+{
+	outb(port, IT87_ADRPORT);
+	return inb(IT87_DATAPORT);
+}
+
+
+static void it87_write(unsigned char port, unsigned char data)
+{
+	outb(port, IT87_ADRPORT);
+	outb(data, IT87_DATAPORT);
+}
+
+
+/* SECTION: Initialisation */
+
+static int init_port(void)
+{
+	unsigned long hw_flags;
+	int retval = 0;
+
+	unsigned char init_bytes[4] = IT87_INIT;
+	unsigned char it87_chipid = 0;
+	unsigned char ldn = 0;
+	unsigned int  it87_io = 0;
+	unsigned int  it87_irq = 0;
+
+	/* 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);
+
+	/* 8712 or 8705 ? */
+	it87_chipid = it87_read(IT87_CHIP_ID1);
+	if (it87_chipid != 0x87) {
+		retval = -ENXIO;
+		return retval;
+	}
+	it87_chipid = it87_read(IT87_CHIP_ID2);
+	if ((it87_chipid != 0x12) && (it87_chipid != 0x05)) {
+		printk(KERN_INFO LIRC_DRIVER_NAME
+		       ": no IT8705/12 found, exiting..\n");
+		retval = -ENXIO;
+		return retval;
+	}
+	printk(KERN_INFO LIRC_DRIVER_NAME
+	       ": found IT87%.2x.\n",
+	       it87_chipid);
+
+	/* get I/O-Port and IRQ */
+	if (it87_chipid == 0x12)
+		ldn = IT8712_CIR_LDN;
+	else
+		ldn = IT8705_CIR_LDN;
+	it87_write(IT87_LDN, ldn);
+
+	it87_io = it87_read(IT87_CIR_BASE_MSB) * 256 +
+		  it87_read(IT87_CIR_BASE_LSB);
+	if (it87_io == 0) {
+		if (io == 0)
+			io = IT87_CIR_DEFAULT_IOBASE;
+		printk(KERN_INFO LIRC_DRIVER_NAME
+		       ": set default io 0x%x\n",
+		       io);
+		it87_write(IT87_CIR_BASE_MSB, io / 0x100);
+		it87_write(IT87_CIR_BASE_LSB, io % 0x100);
+	} else
+		io = it87_io;
+
+	it87_irq = it87_read(IT87_CIR_IRQ);
+	if (digimatrix || it87_irq == 0) {
+		if (irq == 0)
+			irq = IT87_CIR_DEFAULT_IRQ;
+		printk(KERN_INFO LIRC_DRIVER_NAME
+		       ": set default irq 0x%x\n",
+		       irq);
+		it87_write(IT87_CIR_IRQ, irq);
+	} else
+		irq = it87_irq;
+
+	spin_lock_irqsave(&hardware_lock, hw_flags);
+	/* reset */
+	outb(IT87_CIR_IER_RESET, io+IT87_CIR_IER);
+	/* fifo clear */
+	outb(IT87_CIR_TCR1_FIFOCLR |
+	     /*	     IT87_CIR_TCR1_ILE | */
+	     IT87_CIR_TCR1_TXRLE |
+	     IT87_CIR_TCR1_TXENDF, io+IT87_CIR_TCR1);
+	spin_unlock_irqrestore(&hardware_lock, hw_flags);
+
+	/* get I/O port access and IRQ line */
+	if (request_region(io, 8, LIRC_DRIVER_NAME) == NULL) {
+		printk(KERN_ERR LIRC_DRIVER_NAME
+		       ": i/o port 0x%.4x already in use.\n", io);
+		/* Leaving MB PnP Mode */
+		it87_write(IT87_CFGCTRL, 0x2);
+		return -EBUSY;
+	}
+
+	/* activate CIR-Device */
+	it87_write(IT87_CIR_ACT, 0x1);
+
+	/* Leaving MB PnP Mode */
+	it87_write(IT87_CFGCTRL, 0x2);
+
+	retval = request_irq(irq, it87_interrupt, 0 /*IRQF_DISABLED*/,
+			     LIRC_DRIVER_NAME, NULL);
+	if (retval < 0) {
+		printk(KERN_ERR LIRC_DRIVER_NAME
+		       ": IRQ %d already in use.\n",
+		       irq);
+		release_region(io, 8);
+		return retval;
+	}
+
+	printk(KERN_INFO LIRC_DRIVER_NAME
+	       ": I/O port 0x%.4x, IRQ %d.\n", io, irq);
+
+	init_timer(&timerlist);
+	timerlist.function = it87_timeout;
+	timerlist.data = 0xabadcafe;
+
+	return 0;
+}
+
+
+static void drop_port(void)
+{
+/*
+	unsigned char init_bytes[4] = IT87_INIT;
+
+	/ * 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 * /
+	it87_write(IT87_CIR_ACT, 0x0);
+
+	/ * Leaving MB PnP Mode * /
+	it87_write(IT87_CFGCTRL, 0x2);
+*/
+
+	del_timer_sync(&timerlist);
+	free_irq(irq, NULL);
+	release_region(io, 8);
+}
+
+
+static int init_lirc_it87(void)
+{
+	int retval;
+
+	init_waitqueue_head(&lirc_read_queue);
+	retval = init_port();
+	if (retval < 0)
+		return retval;
+	init_hardware();
+	printk(KERN_INFO LIRC_DRIVER_NAME ": Installed.\n");
+	return 0;
+}
+
+
+#ifdef MODULE
+
+static int __init lirc_it87_init(void)
+{
+	int retval;
+
+	retval = init_chrdev();
+	if (retval < 0)
+		return retval;
+	retval = init_lirc_it87();
+	if (retval) {
+		drop_chrdev();
+		return retval;
+	}
+	return 0;
+}
+
+
+static void __exit lirc_it87_exit(void)
+{
+	drop_hardware();
+	drop_chrdev();
+	drop_port();
+	printk(KERN_INFO LIRC_DRIVER_NAME ": Uninstalled.\n");
+}
+
+module_init(lirc_it87_init);
+module_exit(lirc_it87_exit);
+
+MODULE_DESCRIPTION("LIRC driver for ITE IT8712/IT8705 CIR port");
+MODULE_AUTHOR("Hans-Gunter Lutke Uphues");
+MODULE_LICENSE("GPL");
+
+module_param(io, int, 0444);
+MODULE_PARM_DESC(io, "I/O base address (default: 0x310)");
+
+module_param(irq, int, 0444);
+#ifdef LIRC_IT87_DIGIMATRIX
+MODULE_PARM_DESC(irq, "Interrupt (1,3-12) (default: 9)");
+#else
+MODULE_PARM_DESC(irq, "Interrupt (1,3-12) (default: 7)");
+#endif
+
+module_param(it87_enable_demodulator, bool, 0444);
+MODULE_PARM_DESC(it87_enable_demodulator,
+		 "Receiver demodulator enable/disable (1/0), default: 0");
+
+module_param(debug, bool, 0644);
+MODULE_PARM_DESC(debug, "Enable debugging messages");
+
+module_param(digimatrix, bool, 0644);
+#ifdef LIRC_IT87_DIGIMATRIX
+MODULE_PARM_DESC(digimatrix,
+	"Asus Digimatrix it87 compat. enable/disable (1/0), default: 1");
+#else
+MODULE_PARM_DESC(digimatrix,
+	"Asus Digimatrix it87 compat. enable/disable (1/0), default: 0");
+#endif
+
+
+module_param(it87_freq, int, 0444);
+#ifdef LIRC_IT87_DIGIMATRIX
+MODULE_PARM_DESC(it87_freq,
+    "Carrier demodulator frequency (kHz), (default: 36)");
+#else
+MODULE_PARM_DESC(it87_freq,
+    "Carrier demodulator frequency (kHz), (default: 38)");
+#endif
+
+#endif /* MODULE */
+
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/input/lirc/lirc_it87.h b/drivers/input/lirc/lirc_it87.h
new file mode 100644
index 0000000..a997204
--- /dev/null
+++ b/drivers/input/lirc/lirc_it87.h
@@ -0,0 +1,116 @@
+/* lirc_it87.h */
+/* SECTION: Definitions */
+
+/********************************* ITE IT87xx ************************/
+
+/* based on the following documentation from ITE:
+   a) IT8712F Preliminary CIR Programming Guide V0.1
+   b) IT8705F Simple LPC I/O Preliminary Specifiction V0.3
+   c) IT8712F EC-LPC I/O Preliminary Specification V0.5
+*/
+
+/* IT8712/05 Ports: */
+#define IT87_ADRPORT      0x2e
+#define IT87_DATAPORT     0x2f
+#define IT87_INIT         {0x87, 0x01, 0x55, 0x55}
+
+/* alternate Ports: */
+/*
+#define IT87_ADRPORT      0x4e
+#define IT87_DATAPORT     0x4f
+#define IT87_INIT         {0x87, 0x01, 0x55, 0xaa}
+ */
+
+/* IT8712/05 Registers */
+#define IT87_CFGCTRL      0x2
+#define IT87_LDN          0x7
+#define IT87_CHIP_ID1     0x20
+#define IT87_CHIP_ID2     0x21
+#define IT87_CFG_VERSION  0x22
+#define IT87_SWSUSPEND    0x23
+
+#define IT8712_CIR_LDN    0xa
+#define IT8705_CIR_LDN    0x7
+
+/* CIR Configuration Registers: */
+#define IT87_CIR_ACT      0x30
+#define IT87_CIR_BASE_MSB 0x60
+#define IT87_CIR_BASE_LSB 0x61
+#define IT87_CIR_IRQ      0x70
+#define IT87_CIR_CONFIG   0xf0
+
+/* List of IT87_CIR registers: offset to BaseAddr */
+#define IT87_CIR_DR   0
+#define IT87_CIR_IER  1
+#define IT87_CIR_RCR  2
+#define IT87_CIR_TCR1 3
+#define IT87_CIR_TCR2 4
+#define IT87_CIR_TSR  5
+#define IT87_CIR_RSR  6
+#define IT87_CIR_BDLR 5
+#define IT87_CIR_BDHR 6
+#define IT87_CIR_IIR  7
+
+/* Bit Definitionen */
+/* IER: */
+#define IT87_CIR_IER_TM_EN   0x80
+#define IT87_CIR_IER_RESEVED 0x40
+#define IT87_CIR_IER_RESET   0x20
+#define IT87_CIR_IER_BR      0x10
+#define IT87_CIR_IER_IEC     0x8
+#define IT87_CIR_IER_RFOIE   0x4
+#define IT87_CIR_IER_RDAIE   0x2
+#define IT87_CIR_IER_TLDLIE  0x1
+
+/* RCR: */
+#define IT87_CIR_RCR_RDWOS  0x80
+#define IT87_CIR_RCR_HCFS   0x40
+#define IT87_CIR_RCR_RXEN   0x20
+#define IT87_CIR_RCR_RXEND  0x10
+#define IT87_CIR_RCR_RXACT  0x8
+#define IT87_CIR_RCR_RXDCR  0x7
+
+/* TCR1: */
+#define IT87_CIR_TCR1_FIFOCLR 0x80
+#define IT87_CIR_TCR1_ILE     0x40
+#define IT87_CIR_TCR1_FIFOTL  0x30
+#define IT87_CIR_TCR1_TXRLE   0x8
+#define IT87_CIR_TCR1_TXENDF  0x4
+#define IT87_CIR_TCR1_TXMPM   0x3
+
+/* TCR2: */
+#define IT87_CIR_TCR2_CFQ   0xf8
+#define IT87_CIR_TCR2_TXMPW 0x7
+
+/* TSR: */
+#define IT87_CIR_TSR_RESERVED 0xc0
+#define IT87_CIR_TSR_TXFBC    0x3f
+
+/* RSR: */
+#define IT87_CIR_RSR_RXFTO    0x80
+#define IT87_CIR_RSR_RESERVED 0x40
+#define IT87_CIR_RSR_RXFBC    0x3f
+
+/* IIR: */
+#define IT87_CIR_IIR_RESERVED 0xf8
+#define IT87_CIR_IIR_IID      0x6
+#define IT87_CIR_IIR_IIP      0x1
+
+/* TM: */
+#define IT87_CIR_TM_IL_SEL    0x80
+#define IT87_CIR_TM_RESERVED  0x40
+#define IT87_CIR_TM_TM_REG    0x3f
+
+#define IT87_CIR_FIFO_SIZE 32
+
+/* Baudratedivisor for IT87: power of 2: only 1,2,4 or 8) */
+#define IT87_CIR_BAUDRATE_DIVISOR 0x1
+#define IT87_CIR_DEFAULT_IOBASE 0x310
+#define IT87_CIR_DEFAULT_IRQ    0x7
+#define IT87_CIR_SPACE 0x00
+#define IT87_CIR_PULSE 0xff
+#define IT87_CIR_FREQ_MIN 27
+#define IT87_CIR_FREQ_MAX 58
+#define TIME_CONST (IT87_CIR_BAUDRATE_DIVISOR * 8000000ul / 115200ul)
+
+/********************************* ITE IT87xx ************************/
diff --git a/drivers/input/lirc/lirc_ite8709.c b/drivers/input/lirc/lirc_ite8709.c
new file mode 100644
index 0000000..d03ecf7
--- /dev/null
+++ b/drivers/input/lirc/lirc_ite8709.c
@@ -0,0 +1,545 @@
+/*
+ * LIRC driver for ITE8709 CIR port
+ *
+ * Copyright (C) 2008 Grégory Lardière <spmf2004-lirc@yahoo.fr>
+ *
+ * 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>
+#include <linux/module.h>
+
+#include <linux/autoconf.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+
+#include <linux/pnp.h>
+#include <linux/io.h>
+
+#include "lirc.h"
+#include "lirc_dev.h"
+
+#define LIRC_DRIVER_NAME "lirc_ite8709"
+
+#define BUF_CHUNK_SIZE	sizeof(int)
+#define BUF_SIZE	(128*BUF_CHUNK_SIZE)
+
+/*******************************************************************************
+* The ITE8709 device seems to be the combination of IT8512 superIO chip and    *
+* a specific firmware running on the IT8512's embedded micro-controller.       *
+* In addition of the embedded micro-controller, the IT8512 chip contains a     *
+* CIR module and several other modules. A few modules are directly accessible  *
+* by the host CPU, but most of them are only accessible by the                 *
+* micro-controller. The CIR module is only accessible by the micro-controller. *
+* The battery-backed SRAM module is accessible by the host CPU and the         *
+* micro-controller. So one of the MC's firmware role is to act as a bridge     *
+* between the host CPU and the CIR module. The firmware implements a kind of   *
+* communication protocol using the SRAM module as a shared memory. The IT8512  *
+* specification is publicly available on ITE's web site, but the communication *
+* protocol is not, so it was reverse-engineered.                               *
+*******************************************************************************/
+
+/* ITE8709 Registers addresses and values (reverse-engineered) */
+#define ITE8709_MODE		0x1a
+#define ITE8709_REG_ADR		0x1b
+#define ITE8709_REG_VAL		0x1c
+#define ITE8709_IIR		0x1e  /* Interrupt identification register */
+#define ITE8709_RFSR		0x1f  /* Receiver FIFO status register */
+#define ITE8709_FIFO_START	0x20
+
+#define ITE8709_MODE_READY	0X00
+#define ITE8709_MODE_WRITE	0X01
+#define ITE8709_MODE_READ	0X02
+#define ITE8709_IIR_RDAI	0x02  /* Receiver data available interrupt */
+#define ITE8709_IIR_RFOI	0x04  /* Receiver FIFO overrun interrupt */
+#define ITE8709_RFSR_MASK	0x3f  /* FIFO byte count mask */
+
+/* IT8512 CIR-module registers addresses and values (from IT8512 E/F */
+/* specification v0.4.1)                                             */
+#define IT8512_REG_MSTCR	0x01  /* Master control register */
+#define IT8512_REG_IER		0x02  /* Interrupt enable register */
+#define IT8512_REG_CFR		0x04  /* Carrier frequency register */
+#define IT8512_REG_RCR		0x05  /* Receive control register */
+#define IT8512_REG_BDLR		0x08  /* Baud rate divisor low byte register */
+#define IT8512_REG_BDHR		0x09  /* Baud rate divisor high byte register */
+
+#define IT8512_MSTCR_RESET	0x01  /* Reset registers to default value */
+#define IT8512_MSTCR_FIFOCLR	0x02  /* Clear FIFO */
+#define IT8512_MSTCR_FIFOTL_7	0x04  /* FIFO threshold level : 7 */
+#define IT8512_MSTCR_FIFOTL_25	0x0c  /* FIFO threshold level : 25 */
+#define IT8512_IER_RDAIE	0x02  /* Enable data interrupt request */
+#define IT8512_IER_RFOIE	0x04  /* Enable FIFO overrun interrupt req */
+#define IT8512_IER_IEC		0x80  /* Enable interrupt request */
+#define IT8512_CFR_CF_36KHZ	0x09  /* Carrier freq : low speed, 36kHz */
+#define IT8512_RCR_RXDCR_1	0x01  /* Demodulation carrier range : 1 */
+#define IT8512_RCR_RXACT	0x08  /* Receiver active */
+#define IT8512_RCR_RXEN		0x80  /* Receiver enable */
+#define IT8512_BDR_6		6     /* Baud rate divisor : 6 */
+
+/* Actual values used by this driver */
+#define CFG_FIFOTL	IT8512_MSTCR_FIFOTL_25
+#define CFG_CR_FREQ	IT8512_CFR_CF_36KHZ
+#define CFG_DCR		IT8512_RCR_RXDCR_1
+#define CFG_BDR		IT8512_BDR_6
+#define CFG_TIMEOUT	100000 /* Rearm interrupt when a space is > 100 ms */
+
+static int debug;
+
+struct ite8709_device {
+	int use_count;
+	int io;
+	int irq;
+	spinlock_t hardware_lock;
+	unsigned long long acc_pulse;
+	unsigned long long acc_space;
+	char lastbit;
+	struct timeval last_tv;
+	struct lirc_plugin plugin;
+	struct lirc_buffer buffer;
+	struct tasklet_struct tasklet;
+	char force_rearm;
+	char rearmed;
+	char device_busy;
+};
+
+#define dprintk(fmt, args...)					\
+	do {							\
+		if (debug)					\
+			printk(KERN_DEBUG LIRC_DRIVER_NAME ": "	\
+				fmt, ## args);			\
+	} while (0)
+
+
+static unsigned char ite8709_read(struct ite8709_device *dev,
+					unsigned char port)
+{
+	outb(port, dev->io);
+	return inb(dev->io+1);
+}
+
+static void ite8709_write(struct ite8709_device *dev, unsigned char port,
+				unsigned char data)
+{
+	outb(port, dev->io);
+	outb(data, dev->io+1);
+}
+
+static void ite8709_wait_device(struct ite8709_device *dev)
+{
+	int i = 0;
+	/* loop until device tells it's ready to continue */
+	/* iterations count is usually ~750 but can sometimes achieve 13000 */
+	for (i = 0; i < 15000; i++) {
+		udelay(2);
+		if (ite8709_read(dev, ITE8709_MODE) == ITE8709_MODE_READY)
+			break;
+	}
+}
+
+static void ite8709_write_register(struct ite8709_device *dev,
+				unsigned char reg_adr, unsigned char reg_value)
+{
+	ite8709_wait_device(dev);
+
+	ite8709_write(dev, ITE8709_REG_VAL, reg_value);
+	ite8709_write(dev, ITE8709_REG_ADR, reg_adr);
+	ite8709_write(dev, ITE8709_MODE, ITE8709_MODE_WRITE);
+}
+
+static void ite8709_init_hardware(struct ite8709_device *dev)
+{
+	spin_lock_irq(&dev->hardware_lock);
+	dev->device_busy = 1;
+	spin_unlock_irq(&dev->hardware_lock);
+
+	ite8709_write_register(dev, IT8512_REG_BDHR, (CFG_BDR >> 8) & 0xff);
+	ite8709_write_register(dev, IT8512_REG_BDLR, CFG_BDR & 0xff);
+	ite8709_write_register(dev, IT8512_REG_CFR, CFG_CR_FREQ);
+	ite8709_write_register(dev, IT8512_REG_IER,
+			IT8512_IER_IEC | IT8512_IER_RFOIE | IT8512_IER_RDAIE);
+	ite8709_write_register(dev, IT8512_REG_RCR, CFG_DCR);
+	ite8709_write_register(dev, IT8512_REG_MSTCR,
+					CFG_FIFOTL | IT8512_MSTCR_FIFOCLR);
+	ite8709_write_register(dev, IT8512_REG_RCR,
+				IT8512_RCR_RXEN | IT8512_RCR_RXACT | CFG_DCR);
+
+	spin_lock_irq(&dev->hardware_lock);
+	dev->device_busy = 0;
+	spin_unlock_irq(&dev->hardware_lock);
+
+	tasklet_enable(&dev->tasklet);
+}
+
+static void ite8709_drop_hardware(struct ite8709_device *dev)
+{
+	tasklet_disable(&dev->tasklet);
+
+	spin_lock_irq(&dev->hardware_lock);
+	dev->device_busy = 1;
+	spin_unlock_irq(&dev->hardware_lock);
+
+	ite8709_write_register(dev, IT8512_REG_RCR, 0);
+	ite8709_write_register(dev, IT8512_REG_MSTCR,
+				IT8512_MSTCR_RESET | IT8512_MSTCR_FIFOCLR);
+
+	spin_lock_irq(&dev->hardware_lock);
+	dev->device_busy = 0;
+	spin_unlock_irq(&dev->hardware_lock);
+}
+
+static int ite8709_set_use_inc(void *data)
+{
+	struct ite8709_device *dev;
+	dev = data;
+	if (dev->use_count == 0)
+		ite8709_init_hardware(dev);
+	dev->use_count++;
+	return 0;
+}
+
+static void ite8709_set_use_dec(void *data)
+{
+	struct ite8709_device *dev;
+	dev = data;
+	dev->use_count--;
+	if (dev->use_count == 0)
+		ite8709_drop_hardware(dev);
+}
+
+static void ite8709_add_read_queue(struct ite8709_device *dev, int flag,
+					unsigned long long val)
+{
+	int value;
+
+	dprintk("add a %llu usec %s\n", val, flag ? "pulse" : "space");
+
+	value = (val > PULSE_MASK) ? PULSE_MASK : val;
+	if (flag)
+		value |= PULSE_BIT;
+
+	if (!lirc_buffer_full(&dev->buffer)) {
+		lirc_buffer_write_1(&dev->buffer, (void *) &value);
+		wake_up(&dev->buffer.wait_poll);
+	}
+}
+
+static irqreturn_t ite8709_interrupt(int irq, void *dev_id)
+{
+	unsigned char data;
+	int iir, rfsr, i;
+	int fifo = 0;
+	char bit;
+	struct timeval curr_tv;
+
+	/* Bit duration in microseconds */
+	const unsigned long bit_duration = 1000000ul / (115200 / CFG_BDR);
+
+	struct ite8709_device *dev;
+	dev = dev_id;
+
+	/* If device is busy, we simply discard data because we are in one of */
+	/* these two cases : shutting down or rearming the device, so this    */
+	/* doesn't really matter and this avoids waiting too long in IRQ ctx  */
+	spin_lock(&dev->hardware_lock);
+	if (dev->device_busy) {
+		spin_unlock(&dev->hardware_lock);
+		return IRQ_RETVAL(IRQ_HANDLED);
+	}
+
+	iir = ite8709_read(dev, ITE8709_IIR);
+
+	switch (iir) {
+	case ITE8709_IIR_RFOI:
+		dprintk("fifo overrun, scheduling forced rearm just in case\n");
+		dev->force_rearm = 1;
+		tasklet_schedule(&dev->tasklet);
+		spin_unlock(&dev->hardware_lock);
+		return IRQ_RETVAL(IRQ_HANDLED);
+
+	case ITE8709_IIR_RDAI:
+		rfsr = ite8709_read(dev, ITE8709_RFSR);
+		fifo = rfsr & ITE8709_RFSR_MASK;
+		if (fifo > 32)
+			fifo = 32;
+		dprintk("iir: 0x%x rfsr: 0x%x fifo: %d\n", iir, rfsr, fifo);
+
+		if (dev->rearmed) {
+			do_gettimeofday(&curr_tv);
+			dev->acc_space += 1000000ull
+				* (curr_tv.tv_sec - dev->last_tv.tv_sec)
+				+ (curr_tv.tv_usec - dev->last_tv.tv_usec);
+			dev->rearmed = 0;
+		}
+		for (i = 0; i < fifo; i++) {
+			data = ite8709_read(dev, i+ITE8709_FIFO_START);
+			data = ~data;
+			/* Loop through */
+			for (bit = 0; bit < 8; ++bit) {
+				if ((data >> bit) & 1) {
+					dev->acc_pulse += bit_duration;
+					if (dev->lastbit == 0) {
+						ite8709_add_read_queue(dev, 0,
+							dev->acc_space);
+						dev->acc_space = 0;
+					}
+				} else {
+					dev->acc_space += bit_duration;
+					if (dev->lastbit == 1) {
+						ite8709_add_read_queue(dev, 1,
+							dev->acc_pulse);
+						dev->acc_pulse = 0;
+					}
+				}
+				dev->lastbit = (data >> bit) & 1;
+			}
+		}
+		ite8709_write(dev, ITE8709_RFSR, 0);
+
+		if (dev->acc_space > CFG_TIMEOUT) {
+			dprintk("scheduling rearm IRQ\n");
+			do_gettimeofday(&dev->last_tv);
+			dev->force_rearm = 0;
+			tasklet_schedule(&dev->tasklet);
+		}
+
+		spin_unlock(&dev->hardware_lock);
+		return IRQ_RETVAL(IRQ_HANDLED);
+
+	default:
+		/* not our irq */
+		dprintk("unknown IRQ (shouldn't happen) !!\n");
+		spin_unlock(&dev->hardware_lock);
+		return IRQ_RETVAL(IRQ_NONE);
+	}
+}
+
+static void ite8709_rearm_irq(unsigned long data)
+{
+	struct ite8709_device *dev;
+	unsigned long flags;
+	dev = (struct ite8709_device *) data;
+
+	spin_lock_irqsave(&dev->hardware_lock, flags);
+	dev->device_busy = 1;
+	spin_unlock_irqrestore(&dev->hardware_lock, flags);
+
+	if (dev->force_rearm || dev->acc_space > CFG_TIMEOUT) {
+		dprintk("rearming IRQ\n");
+		ite8709_write_register(dev, IT8512_REG_RCR,
+						IT8512_RCR_RXACT | CFG_DCR);
+		ite8709_write_register(dev, IT8512_REG_MSTCR,
+					CFG_FIFOTL | IT8512_MSTCR_FIFOCLR);
+		ite8709_write_register(dev, IT8512_REG_RCR,
+				IT8512_RCR_RXEN | IT8512_RCR_RXACT | CFG_DCR);
+		if (!dev->force_rearm)
+			dev->rearmed = 1;
+		dev->force_rearm = 0;
+	}
+
+	spin_lock_irqsave(&dev->hardware_lock, flags);
+	dev->device_busy = 0;
+	spin_unlock_irqrestore(&dev->hardware_lock, flags);
+}
+
+static int ite8709_cleanup(struct ite8709_device *dev, int stage, int errno,
+				char *msg)
+{
+	if (msg != NULL)
+		printk(KERN_ERR LIRC_DRIVER_NAME ": %s\n", msg);
+
+	switch (stage) {
+	case 6:
+		if (dev->use_count > 0)
+			ite8709_drop_hardware(dev);
+	case 5:
+		free_irq(dev->irq, dev);
+	case 4:
+		release_region(dev->io, 2);
+	case 3:
+		lirc_unregister_plugin(dev->plugin.minor);
+	case 2:
+		lirc_buffer_free(dev->plugin.rbuf);
+	case 1:
+		kfree(dev);
+	case 0:
+		;
+	}
+
+	return errno;
+}
+
+static int __devinit ite8709_pnp_probe(struct pnp_dev *dev,
+					const struct pnp_device_id *dev_id)
+{
+	struct lirc_plugin *plugin;
+	struct ite8709_device *ite8709_dev;
+	int ret;
+
+	/* Check resources validity */
+	if (!pnp_irq_valid(dev, 0))
+		return ite8709_cleanup(NULL, 0, -ENODEV, "invalid IRQ");
+	if (!pnp_port_valid(dev, 2))
+		return ite8709_cleanup(NULL, 0, -ENODEV, "invalid IO port");
+
+	/* Allocate memory for device struct */
+	ite8709_dev = kzalloc(sizeof(struct ite8709_device), GFP_KERNEL);
+	if (ite8709_dev == NULL)
+		return ite8709_cleanup(NULL, 0, -ENOMEM, "kzalloc failed");
+	pnp_set_drvdata(dev, ite8709_dev);
+
+	/* Initialize device struct */
+	ite8709_dev->use_count = 0;
+	ite8709_dev->irq = pnp_irq(dev, 0);
+	ite8709_dev->io = pnp_port_start(dev, 2);
+	ite8709_dev->hardware_lock = __SPIN_LOCK_UNLOCKED(
+					ite8709_dev->hardware_lock);
+	ite8709_dev->acc_pulse = 0;
+	ite8709_dev->acc_space = 0;
+	ite8709_dev->lastbit = 0;
+	do_gettimeofday(&ite8709_dev->last_tv);
+	tasklet_init(&ite8709_dev->tasklet, ite8709_rearm_irq,
+							(long) ite8709_dev);
+	ite8709_dev->force_rearm = 0;
+	ite8709_dev->rearmed = 0;
+	ite8709_dev->device_busy = 0;
+
+	/* Initialize plugin struct */
+	plugin = &ite8709_dev->plugin;
+	strcpy(plugin->name, LIRC_DRIVER_NAME);
+	plugin->minor = -1;
+	plugin->code_length = sizeof(int) * 8;
+	plugin->sample_rate = 0;
+	plugin->features = LIRC_CAN_REC_MODE2;
+	plugin->data = ite8709_dev;
+	plugin->add_to_buf = NULL;
+	plugin->get_queue = NULL;
+	plugin->rbuf = &ite8709_dev->buffer;
+	plugin->set_use_inc = ite8709_set_use_inc;
+	plugin->set_use_dec = ite8709_set_use_dec;
+	plugin->ioctl = NULL;
+	plugin->fops = NULL;
+	plugin->dev = &dev->dev;
+	plugin->owner = THIS_MODULE;
+
+	/* Initialize LIRC buffer */
+	if (lirc_buffer_init(plugin->rbuf, BUF_CHUNK_SIZE, BUF_SIZE))
+		return ite8709_cleanup(ite8709_dev, 1, -ENOMEM,
+						"lirc_buffer_init() failed");
+
+	/* Register LIRC plugin */
+	ret = lirc_register_plugin(plugin);
+	if (ret < 0)
+		return ite8709_cleanup(ite8709_dev, 2, ret,
+					"lirc_register_plugin() failed");
+
+	/* Reserve I/O port access */
+	if (!request_region(ite8709_dev->io, 2, LIRC_DRIVER_NAME))
+		return ite8709_cleanup(ite8709_dev, 3, -EBUSY,
+						"i/o port already in use");
+
+	/* Reserve IRQ line */
+	ret = request_irq(ite8709_dev->irq, ite8709_interrupt, 0,
+					LIRC_DRIVER_NAME, ite8709_dev);
+	if (ret < 0)
+		return ite8709_cleanup(ite8709_dev, 4, ret,
+						"IRQ already in use");
+
+	/* Initialize hardware */
+	ite8709_drop_hardware(ite8709_dev); /* Shutdown hw until first use */
+
+	printk(KERN_INFO LIRC_DRIVER_NAME ": device found : irq=%d io=0x%x\n",
+					ite8709_dev->irq, ite8709_dev->io);
+
+	return 0;
+}
+
+static void __devexit ite8709_pnp_remove(struct pnp_dev *dev)
+{
+	struct ite8709_device *ite8709_dev;
+	ite8709_dev = pnp_get_drvdata(dev);
+
+	ite8709_cleanup(ite8709_dev, 6, 0, NULL);
+
+	printk(KERN_INFO LIRC_DRIVER_NAME ": device removed\n");
+}
+
+#ifdef CONFIG_PM
+static int ite8709_pnp_suspend(struct pnp_dev *dev, pm_message_t state)
+{
+	struct ite8709_device *ite8709_dev;
+	ite8709_dev = pnp_get_drvdata(dev);
+
+	if (ite8709_dev->use_count > 0)
+		ite8709_drop_hardware(ite8709_dev);
+
+	return 0;
+}
+
+static int ite8709_pnp_resume(struct pnp_dev *dev)
+{
+	struct ite8709_device *ite8709_dev;
+	ite8709_dev = pnp_get_drvdata(dev);
+
+	if (ite8709_dev->use_count > 0)
+		ite8709_init_hardware(ite8709_dev);
+
+	return 0;
+}
+#else
+#define ite8709_pnp_suspend NULL
+#define ite8709_pnp_resume NULL
+#endif
+
+static const struct pnp_device_id pnp_dev_table[] = {
+	{"ITE8709", 0},
+	{}
+};
+
+MODULE_DEVICE_TABLE(pnp, pnp_dev_table);
+
+static struct pnp_driver ite8709_pnp_driver = {
+	.name           = LIRC_DRIVER_NAME,
+	.probe          = ite8709_pnp_probe,
+	.remove         = __devexit_p(ite8709_pnp_remove),
+	.suspend        = ite8709_pnp_suspend,
+	.resume         = ite8709_pnp_resume,
+	.id_table       = pnp_dev_table,
+};
+
+int init_module(void)
+{
+	return pnp_register_driver(&ite8709_pnp_driver);
+}
+
+void cleanup_module(void)
+{
+	pnp_unregister_driver(&ite8709_pnp_driver);
+}
+
+MODULE_DESCRIPTION("LIRC driver for ITE8709 CIR port");
+MODULE_AUTHOR("Grégory Lardière");
+MODULE_LICENSE("GPL");
+
+module_param(debug, bool, 0644);
+MODULE_PARM_DESC(debug, "Enable debugging messages");
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/input/lirc/lirc_mceusb.c b/drivers/input/lirc/lirc_mceusb.c
new file mode 100644
index 0000000..f1874f3
--- /dev/null
+++ b/drivers/input/lirc/lirc_mceusb.c
@@ -0,0 +1,890 @@
+/*
+ * USB Microsoft IR Transceiver driver - 0.2
+ *
+ * Copyright (c) 2003-2004 Dan Conti (dconti@acm.wwu.edu)
+ *
+ * This driver is based on the USB skeleton driver packaged with the
+ * kernel, and the notice from that package has been retained below.
+ *
+ * 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.
+ *
+ * All trademarks property of their respective owners. This driver was
+ * originally based on the USB skeleton driver, although significant
+ * portions of that code have been removed as the driver has evolved.
+ *
+ * 2003_11_11 - Restructured to minimalize code interpretation in the
+ *	      driver. The normal use case will be with lirc.
+ *
+ * 2004_01_01 - Removed all code interpretation. Generate mode2 data
+ *	      for passing off to lirc. Cleanup
+ *
+ * 2004_01_04 - Removed devfs handle. Put in a temporary workaround
+ *	      for a known issue where repeats generate two
+ *	      sequential spaces (last_was_repeat_gap)
+ *
+ * 2004_02_17 - Changed top level api to no longer use fops, and
+ *	      instead use new interface for polling via
+ *	      lirc_thread. Restructure data read/mode2 generation to
+ *	      a single pass, reducing number of buffers. Rev to .2
+ *
+ * 2004_02_27 - Last of fixups to plugin->add_to_buf API. Properly
+ *	      handle broken fragments from the receiver. Up the
+ *	      sample rate and remove any pacing from
+ *	      fetch_more_data. Fixes all known issues.
+ *
+ * TODO
+ *   - Fix up minor number, registration of major/minor with usb subsystem
+ *
+ */
+/*
+ * USB Skeleton driver - 1.1
+ *
+ * Copyright (C) 2001-2003 Greg Kroah-Hartman (greg@kroah.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, version 2.
+ *
+ *
+ * This driver is to be used as a skeleton driver to be able to create a
+ * USB driver quickly.  The design of it is based on the usb-serial and
+ * dc2xx drivers.
+ *
+ * Thanks to Oliver Neukum, David Brownell, and Alan Stern for their help
+ * in debugging this driver.
+ *
+ *
+ * History:
+ *
+ * 2003-05-06 - 1.1 - changes due to usb core changes with usb_register_dev()
+ * 2003-02-25 - 1.0 - fix races involving urb->status, unlink_urb(), and
+ *			disconnect.  Fix transfer amount in read().  Use
+ *			macros instead of magic numbers in probe().  Change
+ *			size variables to size_t.  Show how to eliminate
+ *			DMA bounce buffer.
+ * 2002_12_12 - 0.9 - compile fixes and got rid of fixed minor array.
+ * 2002_09_26 - 0.8 - changes due to USB core conversion to struct device
+ *			driver.
+ * 2002_02_12 - 0.7 - zero out dev in probe function for devices that do
+ *			not have both a bulk in and bulk out endpoint.
+ *			Thanks to Holger Waechtler for the fix.
+ * 2001_11_05 - 0.6 - fix minor locking problem in skel_disconnect.
+ *			Thanks to Pete Zaitcev for the fix.
+ * 2001_09_04 - 0.5 - fix devfs bug in skel_disconnect. Thanks to wim delvaux
+ * 2001_08_21 - 0.4 - more small bug fixes.
+ * 2001_05_29 - 0.3 - more bug fixes based on review from linux-usb-devel
+ * 2001_05_24 - 0.2 - bug fixes based on review from linux-usb-devel people
+ * 2001_05_01 - 0.1 - first version
+ *
+ */
+
+#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/smp_lock.h>
+#include <linux/usb.h>
+#include <linux/completion.h>
+#include <linux/uaccess.h>
+
+#ifdef CONFIG_USB_DEBUG
+static int debug = 1;
+#else
+static int debug;
+#endif
+
+#include "lirc.h"
+#include "lirc_dev.h"
+
+/* Use our own dbg macro */
+#define dprintk(fmt, args...)				\
+	do {						\
+		if (debug)				\
+			printk(KERN_DEBUG __FILE__ ": "	\
+			       fmt "\n", ## 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_DEVICE(USB_MCEUSB_VENDOR_ID, USB_MCEUSB_PRODUCT_ID) },
+	{ }	/* 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 usb_skel {
+	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 */
+
+	atomic_t write_busy;		/* true iff write urb is busy */
+	struct completion write_finished; /* wait for the write to finish */
+
+	wait_queue_head_t wait_q; /* for timeouts */
+	int open_count;		/* number of times this port has been opened */
+	struct mutex sem;	/* locks this structure */
+
+	int present;		/* if the device is not disconnected */
+
+	struct lirc_plugin *plugin;
+
+	int lircdata[256]; /* place to store data until lirc processes it */
+	int lircidx;		/* current index */
+	int lirccnt;		/* remaining values */
+
+	int usb_valid_bytes_in_bulk_buffer; /* leftover data from prior read */
+	int mce_bytes_left_in_packet;	/* for packets split across reads */
+
+	/* Value to hold the last received space; 0 if last value
+	 * received was a pulse */
+	int last_space;
+
+	dma_addr_t dma_in;
+	dma_addr_t dma_out;
+};
+
+#define MCE_TIME_UNIT 50
+
+/* driver api */
+static int mceusb_probe(struct usb_interface *interface,
+			const struct usb_device_id *id);
+static void mceusb_disconnect(struct usb_interface *interface);
+static void mceusb_write_bulk_callback(struct urb *urb);
+
+/* read data from the usb bus; convert to mode2 */
+static int msir_fetch_more_data(struct usb_skel *dev, int dont_block);
+
+/* helper functions */
+static void msir_cleanup(struct usb_skel *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 usb_skel *minor_table[MAX_DEVICES];
+
+/* lock to protect the minor_table structure */
+static DECLARE_MUTEX(minor_table_mutex);
+static void mceusb_setup(struct usb_device *udev);
+
+/* usb specific object needed to register this driver with the usb subsystem */
+static struct usb_driver mceusb_driver = {
+	.name		= DRIVER_NAME,
+	.probe		= mceusb_probe,
+	.disconnect	= mceusb_disconnect,
+	.id_table	= mceusb_table,
+};
+
+
+/**
+ *	usb_mceusb_debug_data
+ */
+static inline void usb_mceusb_debug_data(const char *function, int size,
+					  const unsigned char *data)
+{
+	int i;
+	if (!debug)
+		return;
+
+	printk(KERN_DEBUG __FILE__": %s - length = %d, data = ",
+	       function, size);
+	for (i = 0; i < size; ++i)
+		printk(KERN_DEBUG "%.2x ", data[i]);
+	printk(KERN_DEBUG "\n");
+}
+
+/**
+ *mceusb_delete
+ */
+static inline void mceusb_delete(struct usb_skel *dev)
+{
+	dprintk("%s", __func__);
+	minor_table[dev->minor] = NULL;
+	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);
+	if (dev->write_urb != NULL)
+		usb_free_urb(dev->write_urb);
+	kfree(dev);
+}
+
+static void mceusb_setup(struct usb_device *udev)
+{
+	char data[8];
+	int res;
+
+	memset(data, 0, 8);
+
+	/* Get Status */
+	res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+			      USB_REQ_GET_STATUS, USB_DIR_IN,
+			      0, 0, data, 2, HZ * 3);
+
+	/*    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]);
+
+
+	/* 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);
+
+	/* These two are sent by the windows driver, but stall for
+	 * me. I dont 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);
+
+	dprintk("%s - res = %d", __func__, res);
+
+	/* 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);
+
+	dprintk("%s - res = %d", __func__, res);
+#endif
+}
+
+static void msir_cleanup(struct usb_skel *dev)
+{
+	memset(dev->bulk_in_buffer, 0, dev->bulk_in_size);
+
+	dev->usb_valid_bytes_in_bulk_buffer = 0;
+
+	dev->last_space = PULSE_MASK;
+
+	dev->mce_bytes_left_in_packet = 0;
+	dev->lircidx = 0;
+	dev->lirccnt = 0;
+	memset(dev->lircdata, 0, sizeof(dev->lircdata));
+}
+
+static int set_use_inc(void *data)
+{
+	return 0;
+}
+
+static void set_use_dec(void *data)
+{
+}
+
+/*
+ * 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->sem 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 usb_skel *dev, int dont_block)
+{
+	int retries = 0;
+	int words_to_read =
+		(sizeof(dev->lircdata)/sizeof(int)) - 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 (words_to_read == 0)
+		return dev->lirccnt;
+
+	/* 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;
+
+	/* reserve room for our leading space */
+	if (dev->last_space)
+		words_to_read--;
+
+	while (words_to_read) {
+		/* handle signals and USB disconnects */
+		if (signal_pending(current))
+			return dev->lirccnt ? dev->lirccnt : -EINTR;
+		if (!dev->udev)
+			return -ENODEV;
+
+		bulkidx = 0;
+
+		/*
+		 * perform data read (phys or from previous buffer)
+		 */
+
+		/* 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;
+		} else {
+			int retval;
+
+			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);
+
+			/* 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;
+			}
+
+			retries = 0;
+			if (partial)
+				this_read = partial;
+
+			/* skip the header */
+			bulkidx += 2;
+
+			/* check for empty reads (header only) */
+			if (this_read == 2) {
+				/* assume no data */
+				if (dont_block)
+					break;
+
+				/* sleep for a bit before performing
+				   another read */
+				interruptible_sleep_on_timeout(&dev->wait_q, 1);
+				continue;
+			}
+		}
+
+		/*
+		 * process data
+		 */
+
+		/* 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;
+
+			/* 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 occaisonally we do not receive
+				 * the second half of a fragment
+				 */
+				bytes_left_in_packet =
+					128 + signedp[bulkidx++];
+
+				/* unfortunately rather than keep all
+				 * the data in the packetized format,
+				 * the transceiver sends a trailing 8
+				 * bytes that aren't part of the
+				 * transmittion from the remote,
+				 * aren't packetized, and dont 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;
+				}
+
+				/* 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;
+
+			}
+
+			/*
+			 * generate mode2
+			 */
+
+			keycode = signedp[bulkidx++];
+			if (keycode < 0) {
+				pulse = 1;
+				keycode += 128;
+			}
+			keycode *= MCE_TIME_UNIT;
+
+			bytes_left_in_packet--;
+
+			if (pulse) {
+				if (dev->last_space) {
+					dev->lircdata[dev->lirccnt++] =
+						dev->last_space;
+					dev->last_space = 0;
+					words_to_read--;
+
+					/* 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--;
+				}
+
+				dev->last_space += keycode;
+			}
+		}
+	}
+
+	/* save off some info if we are 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 dev->lirccnt;
+}
+
+/* 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
+ * plugin->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)
+{
+	struct usb_skel *dev = (struct usb_skel *) data;
+
+	mutex_lock(&dev->sem);
+
+	/* verify device still present */
+	if (dev->udev == NULL) {
+		mutex_unlock(&dev->sem);
+		return -ENODEV;
+	}
+
+	if (!dev->lirccnt) {
+		int res;
+		dev->lircidx = 0;
+
+		res = msir_fetch_more_data(dev, 1);
+
+		if (res == 0)
+			res = -ENODATA;
+		if (res < 0) {
+			mutex_unlock(&dev->sem);
+			return res;
+		}
+	}
+
+	if (dev->lirccnt) {
+		int keys_to_copy;
+
+		/* 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;
+
+		lirc_buffer_write_n(buf,
+			(unsigned char *) &(dev->lircdata[dev->lircidx]),
+			keys_to_copy);
+		dev->lircidx += keys_to_copy;
+		dev->lirccnt -= keys_to_copy;
+
+		mutex_unlock(&dev->sem);
+		return 0;
+	}
+
+	mutex_unlock(&dev->sem);
+	return -ENODATA;
+}
+
+/**
+ *	mceusb_write_bulk_callback
+ */
+static void mceusb_write_bulk_callback(struct urb *urb)
+{
+	struct usb_skel *dev = (struct usb_skel *)urb->context;
+
+	dprintk("%s - minor %d", __func__, dev->minor);
+
+	if ((urb->status != -ENOENT) &&
+	    (urb->status != -ECONNRESET)) {
+		dprintk("%s - nonzero write buld status received: %d",
+			__func__, urb->status);
+		return;
+	}
+
+	return;
+}
+
+/**
+ *	mceusb_probe
+ *
+ *	Called by the usb core when a new device is connected that it
+ *	thinks this driver might be interested in.
+ */
+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;
+	struct usb_skel *dev = NULL;
+	struct usb_endpoint_descriptor *endpoint;
+
+	struct lirc_plugin *plugin;
+	struct lirc_buffer *rbuf;
+
+	int minor;
+	size_t buffer_size;
+	int i;
+	int retval = -ENOMEM;
+	char junk[64];
+	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");
+		return -ENODEV;
+	}
+
+	/* select a "subminor" number (part of a minor number) */
+	down(&minor_table_mutex);
+	for (minor = 0; minor < MAX_DEVICES; ++minor) {
+		if (minor_table[minor] == NULL)
+			break;
+	}
+	if (minor >= MAX_DEVICES) {
+		info("Too many devices plugged in, "
+		     "can not handle this device.");
+		goto error;
+	}
+
+	/* allocate memory for our device state and initialize it */
+	dev = kmalloc(sizeof(struct usb_skel), GFP_KERNEL);
+	if (dev == NULL) {
+		err("Out of memory");
+		retval = -ENOMEM;
+		goto error;
+	}
+	minor_table[minor] = dev;
+
+	memset(dev, 0x00, sizeof(*dev));
+	mutex_init(&dev->sem);
+	dev->udev = udev;
+	dev->interface = interface;
+	dev->minor = minor;
+
+	/* set up the endpoint information */
+	/* check out the endpoints */
+	/* use only the first bulk-in and bulk-out endpoints */
+	iface_desc = interface->cur_altsetting;
+
+	for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+		endpoint = &iface_desc->endpoint[i].desc;
+		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;
+			dev->bulk_in_buffer =
+				usb_buffer_alloc(udev, buffer_size,
+						 GFP_ATOMIC, &dev->dma_in);
+			if (!dev->bulk_in_buffer) {
+				err("Couldn't allocate bulk_in_buffer");
+				goto error;
+			}
+		}
+
+		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");
+			dev->write_urb = usb_alloc_urb(0, GFP_KERNEL);
+			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;
+			dev->bulk_out_buffer =
+				usb_buffer_alloc(udev, buffer_size,
+						 GFP_ATOMIC, &dev->dma_out);
+			if (!dev->bulk_out_buffer) {
+				err("Couldn't allocate bulk_out_buffer");
+				goto error;
+			}
+			usb_fill_bulk_urb(dev->write_urb, udev,
+				      usb_sndbulkpipe
+				      (udev, endpoint->bEndpointAddress),
+				      dev->bulk_out_buffer, buffer_size,
+				      mceusb_write_bulk_callback, dev);
+		}
+	}
+
+	if (!(dev->bulk_in_endpointAddr && dev->bulk_out_endpointAddr)) {
+		err("Couldn't find both bulk-in and bulk-out endpoints");
+		goto error;
+	}
+
+	/* init the waitq */
+	init_waitqueue_head(&dev->wait_q);
+
+
+	/* Set up our lirc plugin */
+	plugin = kmalloc(sizeof(struct lirc_plugin), GFP_KERNEL);
+	if (!plugin) {
+		err("out of memory");
+		goto error;
+	}
+	memset(plugin, 0, sizeof(struct lirc_plugin));
+
+	rbuf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
+	if (!rbuf) {
+		err("out of memory");
+		kfree(plugin);
+		goto error;
+	}
+
+	/* the lirc_atiusb module doesn't memset rbuf here ... ? */
+	if (lirc_buffer_init(rbuf, sizeof(int), 128)) {
+		err("out of memory");
+		kfree(plugin);
+		kfree(rbuf);
+		goto error;
+	}
+
+	strcpy(plugin->name, DRIVER_NAME " ");
+	plugin->minor = minor;
+	plugin->code_length = sizeof(int) * 8;
+	plugin->features = LIRC_CAN_REC_MODE2; /* | LIRC_CAN_SEND_MODE2; */
+	plugin->data = dev;
+	plugin->rbuf = rbuf;
+	plugin->ioctl = NULL;
+	plugin->set_use_inc = &set_use_inc;
+	plugin->set_use_dec = &set_use_dec;
+	plugin->sample_rate = 80;   /* sample at 100hz (10ms) */
+	plugin->add_to_buf = &mceusb_add_to_buf;
+	/* plugin->fops = &mceusb_fops; */
+	plugin->dev = &udev->dev;
+	plugin->owner = THIS_MODULE;
+	if (lirc_register_plugin(plugin) < 0) {
+		kfree(plugin);
+		lirc_buffer_free(rbuf);
+		kfree(rbuf);
+		goto error;
+	}
+	dev->plugin = plugin;
+
+	/* 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++)
+		(void) usb_bulk_msg(udev,
+				    usb_rcvbulkpipe(udev,
+						    dev->bulk_in_endpointAddr),
+				    junk, 64, &partial, HZ*10);
+
+	msir_cleanup(dev);
+	mceusb_setup(udev);
+
+	/* we can register the device now, as it is ready */
+	usb_set_intfdata(interface, dev);
+	/* let the user know what node this device is now attached to */
+	/* info("USB Microsoft IR Transceiver device now attached to msir%d",
+		dev->minor); */
+	up(&minor_table_mutex);
+	return 0;
+error:
+	mceusb_delete(dev);
+	dev = NULL;
+	dprintk("%s: retval = %x", __func__, retval);
+	up(&minor_table_mutex);
+	return retval;
+}
+
+/**
+ *	mceusb_disconnect
+ *
+ *	Called by the usb core when the device is removed from the system.
+ *
+ *	This routine guarantees that the driver will not submit any more urbs
+ *	by clearing dev->udev.  It is also supposed to terminate any currently
+ *	active urbs.  Unfortunately, usb_bulk_msg(), used in skel_read(), does
+ *	not provide any way to do this.  But at least we can cancel an active
+ *	write.
+ */
+static void mceusb_disconnect(struct usb_interface *interface)
+{
+	struct usb_skel *dev;
+	int minor;
+	dev = usb_get_intfdata(interface);
+	usb_set_intfdata(interface, NULL);
+
+	down(&minor_table_mutex);
+	mutex_lock(&dev->sem);
+	minor = dev->minor;
+
+	/* unhook lirc things */
+	lirc_unregister_plugin(minor);
+	lirc_buffer_free(dev->plugin->rbuf);
+	kfree(dev->plugin->rbuf);
+	kfree(dev->plugin);
+	/* terminate an ongoing write */
+	if (atomic_read(&dev->write_busy)) {
+		usb_kill_urb(dev->write_urb);
+		wait_for_completion(&dev->write_finished);
+	}
+
+	/* prevent device read, write and ioctl */
+	dev->present = 0;
+
+	mceusb_delete(dev);
+
+	info("Microsoft IR Transceiver #%d now disconnected", minor);
+	mutex_unlock(&dev->sem);
+	up(&minor_table_mutex);
+}
+
+
+
+/**
+ *	usb_mceusb_init
+ */
+static int __init usb_mceusb_init(void)
+{
+	int result;
+
+	/* register this driver with the USB subsystem */
+	result = usb_register(&mceusb_driver);
+	if (result) {
+		err("usb_register failed for the " DRIVER_NAME
+		    " driver. error number %d", result);
+		return result;
+	}
+
+	info(DRIVER_DESC " " DRIVER_VERSION);
+	return 0;
+}
+
+
+/**
+ *	usb_mceusb_exit
+ */
+static void __exit usb_mceusb_exit(void)
+{
+	/* deregister this driver with the USB subsystem */
+	usb_deregister(&mceusb_driver);
+}
+
+#ifdef MODULE
+module_init(usb_mceusb_init);
+module_exit(usb_mceusb_exit);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(usb, mceusb_table);
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
+
+#else /* not MODULE */
+subsys_initcall(usb_mceusb_init);
+
+#endif /* MODULE */
diff --git a/drivers/input/lirc/lirc_mceusb2.c b/drivers/input/lirc/lirc_mceusb2.c
new file mode 100644
index 0000000..1e683d3
--- /dev/null
+++ b/drivers/input/lirc/lirc_mceusb2.c
@@ -0,0 +1,1119 @@
+/*
+ * LIRC driver for Philips eHome USB Infrared Transceiver
+ * and the Microsoft MCE 2005 Remote Control
+ *
+ * (C) by Martin A. Blatter <martin_a_blatter@yahoo.com>
+ *
+ * Transmitter support and reception code cleanup.
+ * (C) by Daniel Melander <lirc@rajidae.se>
+ *
+ * Derived from ATI USB driver by Paul Miller and the original
+ * MCE USB driver by Dan Corti
+ *
+ * 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>
+
+#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/completion.h>
+#include <linux/uaccess.h>
+#include <linux/usb.h>
+#include <linux/poll.h>
+#include <linux/wait.h>
+#include <linux/time.h>
+
+#include "lirc.h"
+#include "lirc_dev.h"
+
+#define DRIVER_VERSION	"1.48"
+#define DRIVER_AUTHOR	"Daniel Melander <lirc@rajidae.se>, " \
+			"Martin Blatter <martin_a_blatter@yahoo.com>"
+#define DRIVER_DESC	"Philips eHome USB IR Transceiver and Microsoft " \
+			"MCE 2005 Remote Control driver for LIRC"
+#define DRIVER_NAME	"lirc_mceusb2"
+
+#define USB_BUFLEN	16	/* 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
+#define dprintk(fmt, args...)					\
+	do {							\
+		if (debug)					\
+			printk(KERN_DEBUG fmt, ## args);	\
+	} while (0)
+
+/* lock irctl structure */
+#define IRLOCK		mutex_lock(&ir->lock)
+#define IRUNLOCK	mutex_unlock(&ir->lock)
+
+/* general constants */
+#define SUCCESS			0
+#define SEND_FLAG_IN_PROGRESS	1
+#define SEND_FLAG_COMPLETE	2
+#define RECV_FLAG_IN_PROGRESS	3
+#define RECV_FLAG_COMPLETE	4
+
+#define PHILUSB_INBOUND		1
+#define PHILUSB_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
+
+static struct usb_device_id usb_remote_table[] = {
+	/* Philips eHome Infrared Transceiver */
+	{ USB_DEVICE(VENDOR_PHILIPS, 0x0815) },
+	/* 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) },
+	/* 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) },
+	/* 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) },
+	/* Fintek eHome Infrared Transceiver */
+	{ USB_DEVICE(VENDOR_FINTEK, 0x0602) },
+	/* Pinnacle Remote Kit */
+	{ USB_DEVICE(VENDOR_PINNACLE, 0x0225) },
+	/* Terminating entry */
+	{ }
+};
+
+static struct usb_device_id pinnacle_list[] = {
+	{ USB_DEVICE(VENDOR_PINNACLE, 0x0225) },
+	{}
+};
+
+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, 0x0007) },
+	{ USB_DEVICE(VENDOR_TOPSEED, 0x0008) },
+	{ USB_DEVICE(VENDOR_PINNACLE, 0x0225) },
+	{}
+};
+
+/* data structure for each usb remote */
+struct irctl {
+
+	/* 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;
+
+	/* buffers and dma */
+	unsigned char *buf_in;
+	unsigned int len_in;
+	dma_addr_t dma_in;
+	dma_addr_t dma_out;
+
+	/* lirc */
+	struct lirc_plugin *p;
+	int lircdata;
+	unsigned char is_pulse;
+	struct {
+		u32 connected:1;
+		u32 pinnacle:1;
+		u32 transmitter_mask_inverted:1;
+		u32 reserved:29;
+	} 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;
+};
+
+/* init strings */
+static char init1[] = {0x00, 0xff, 0xaa, 0xff, 0x0b};
+static char init2[] = {0xff, 0x18};
+
+static char pin_init1[] = { 0x9f, 0x07};
+static char pin_init2[] = { 0x9f, 0x13};
+static char pin_init3[] = { 0x9f, 0x0d};
+
+static void usb_remote_printdata(struct irctl *ir, char *buf, int len)
+{
+	char codes[USB_BUFLEN*3 + 1];
+	int i;
+
+	if (len <= 0)
+		return;
+
+	for (i = 0; i < len && i < USB_BUFLEN; i++)
+		snprintf(codes+i*3, 4, "%02x ", buf[i] & 0xFF);
+
+	printk(KERN_INFO "" DRIVER_NAME "[%d]: data received %s (length=%d)\n",
+		ir->devnum, codes, len);
+}
+
+static void usb_async_callback(struct urb *urb, struct pt_regs *regs)
+{
+	struct irctl *ir;
+	int len;
+
+	if (!urb)
+		return;
+
+	ir = urb->context;
+	if (ir) {
+		len = urb->actual_length;
+
+		dprintk(DRIVER_NAME
+			"[%d]: callback called (status=%d len=%d)\n",
+			ir->devnum, urb->status, len);
+
+		if (debug)
+			usb_remote_printdata(ir, urb->transfer_buffer, len);
+	}
+
+}
+
+
+/* request incoming or send outgoing usb packet - used to initialize remote */
+static void request_packet_async(struct irctl *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 (async_urb) {
+			/* alloc buffer */
+			async_buf = kmalloc(size, GFP_KERNEL);
+			if (async_buf) {
+				if (urb_type == PHILUSB_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);
+				}
+			} else {
+				usb_free_urb(async_urb);
+				return;
+			}
+		}
+	} else {
+		/* standard request */
+		async_urb = ir->urb_in;
+		ir->send_flags = RECV_FLAG_IN_PROGRESS;
+	}
+	dprintk(DRIVER_NAME "[%d]: receive request called (size=%#x)\n",
+		ir->devnum, size);
+
+	async_urb->transfer_buffer_length = size;
+	async_urb->dev = ir->usbdev;
+
+	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 int unregister_from_lirc(struct irctl *ir)
+{
+	struct lirc_plugin *p = ir->p;
+	int devnum;
+	int rtn;
+
+	devnum = ir->devnum;
+	dprintk(DRIVER_NAME "[%d]: unregister from lirc called\n", devnum);
+
+	rtn = lirc_unregister_plugin(p->minor);
+	if (rtn > 0) {
+		printk(DRIVER_NAME "[%d]: error in lirc_unregister minor: %d\n"
+			"Trying again...\n", devnum, p->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_plugin(p->minor);
+		if (rtn > 0)
+			printk(DRIVER_NAME "[%d]: lirc_unregister failed\n",
+			devnum);
+	}
+
+	if (rtn != SUCCESS) {
+		printk(DRIVER_NAME "[%d]: didn't free resources\n", devnum);
+		return -EAGAIN;
+	}
+
+	printk(DRIVER_NAME "[%d]: usb remote disconnected\n", devnum);
+
+	lirc_buffer_free(p->rbuf);
+	kfree(p->rbuf);
+	kfree(p);
+	kfree(ir);
+	return SUCCESS;
+}
+
+static int set_use_inc(void *data)
+{
+	struct irctl *ir = data;
+
+	if (!ir) {
+		printk(DRIVER_NAME "[?]: set_use_inc called with no context\n");
+		return -EIO;
+	}
+	dprintk(DRIVER_NAME "[%d]: set use inc\n", ir->devnum);
+
+	if (!ir->flags.connected) {
+		if (!ir->usbdev)
+			return -ENOENT;
+		ir->flags.connected = 1;
+	}
+
+	return SUCCESS;
+}
+
+static void set_use_dec(void *data)
+{
+	struct irctl *ir = data;
+
+	if (!ir) {
+		printk(DRIVER_NAME "[?]: set_use_dec called with no context\n");
+		return;
+	}
+	dprintk(DRIVER_NAME "[%d]: set use dec\n", ir->devnum);
+
+	if (ir->flags.connected) {
+		IRLOCK;
+		ir->flags.connected = 0;
+		IRUNLOCK;
+	}
+}
+
+static void send_packet_to_lirc(struct irctl *ir)
+{
+	if (ir->lircdata != 0) {
+		lirc_buffer_write_1(ir->p->rbuf,
+				    (unsigned char *) &ir->lircdata);
+		wake_up(&ir->p->rbuf->wait_poll);
+		ir->lircdata = 0;
+	}
+}
+
+static void usb_remote_recv(struct urb *urb, struct pt_regs *regs)
+{
+	struct irctl *ir;
+	int buf_len, packet_len;
+	int i, j;
+
+	if (!urb)
+		return;
+
+	ir = urb->context;
+	if (!ir) {
+		usb_unlink_urb(urb);
+		return;
+	}
+
+	buf_len = urb->actual_length;
+	packet_len = 0;
+
+	if (debug)
+		usb_remote_printdata(ir, urb->transfer_buffer, buf_len);
+
+	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);
+	}
+
+	switch (urb->status) {
+	/* success */
+	case SUCCESS:
+		for (i = 0; i < buf_len; i++) {
+			/* decode mce packets of the form (84),AA,BB,CC,DD */
+			switch (ir->buf_in[i]) {
+
+			/* data headers */
+			case 0x90: /* used Pinnacle Remote Kit */
+			case 0x8F:
+			case 0x8E:
+			case 0x8D:
+			case 0x8C:
+			case 0x8B:
+			case 0x8A:
+			case 0x89:
+			case 0x88:
+			case 0x87:
+			case 0x86:
+			case 0x85:
+			case 0x84:
+			case 0x83:
+			case 0x82:
+			case 0x81:
+			case 0x80:
+				/* decode packet data */
+				packet_len = ir->buf_in[i] &
+					MCE_PACKET_LENGTH_MASK;
+				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;
+					}
+
+					/* 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);
+				}
+
+				i += packet_len;
+				break;
+
+				/* status header (0x9F) */
+			case MCE_CONTROL_HEADER:
+				/* 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 */
+
+		/*
+		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);
+
+		*/
+
+				/* end decode loop */
+				i = buf_len;
+				break;
+			default:
+				break;
+			}
+		}
+
+		break;
+
+		/* unlink */
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		usb_unlink_urb(urb);
+		return;
+
+	case -EPIPE:
+	default:
+		break;
+	}
+
+	/* resubmit urb */
+	usb_submit_urb(urb, GFP_ATOMIC);
+}
+
+
+static ssize_t lirc_write(struct file *file, const char *buf,
+			  size_t n, loff_t *ppos)
+{
+	int i, count = 0, cmdcount = 0;
+	struct irctl *ir = NULL;
+	int 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_plugin data for the device */
+	ir = lirc_get_pdata(file);
+	if (!ir && !ir->usb_ep_out)
+		return -EFAULT;
+
+	if (n % sizeof(int))
+		return -EINVAL;
+	count = n / sizeof(int);
+
+	/* 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;
+
+	/* Check if we have room for the empty packet at the end */
+	if (cmdcount >= MCE_CMDBUF_SIZE)
+		return -EINVAL;
+
+	/* All mce commands end with an empty packet (0x80) */
+	cmdbuf[cmdcount++] = 0x80;
+
+	/* Transmit the command to the mce device */
+	request_packet_async(ir, ir->usb_ep_out, cmdbuf,
+			     cmdcount, PHILUSB_OUTBOUND);
+
+	/* 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;
+
+	/* delay with the closest number of ticks */
+	set_current_state(TASK_INTERRUPTIBLE);
+	schedule_timeout(usecs_to_jiffies(signal_duration));
+
+	return n;
+}
+
+static void set_transmitter_mask(struct irctl *ir, unsigned int mask)
+{
+	if (ir->flags.transmitter_mask_inverted)
+		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 irctl *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),
+					     PHILUSB_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 the new carrier to the mce
+				   device */
+				request_packet_async(ir, ir->usb_ep_out,
+						     cmdbuf, sizeof(cmdbuf),
+						     PHILUSB_OUTBOUND);
+				return carrier;
+			}
+		}
+
+		return -EINVAL;
+
+	}
+
+	return carrier;
+}
+
+
+static int lirc_ioctl(struct inode *node, struct file *filep,
+		      unsigned int cmd, unsigned long arg)
+{
+	int result;
+	unsigned int ivalue;
+	unsigned long lvalue;
+	struct irctl *ir = NULL;
+
+	/* Retrieve lirc_plugin 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;
+
+		default: /* Unsupported transmitter mask */
+			return MCE_MAX_CHANNELS;
+		}
+
+		dprintk(DRIVER_NAME ": SET_TRANSMITTERS mask=%d\n", ivalue);
+		break;
+
+	case LIRC_GET_SEND_MODE:
+
+		result = put_user(LIRC_SEND2MODE(LIRC_CAN_SEND_PULSE &
+						 LIRC_CAN_SEND_MASK),
+				  (unsigned long *) arg);
+
+		if (result)
+			return result;
+		break;
+
+	case LIRC_SET_SEND_MODE:
+
+		result = get_user(lvalue, (unsigned long *) arg);
+
+		if (result)
+			return result;
+		if (lvalue != (LIRC_MODE_PULSE&LIRC_CAN_SEND_MASK))
+			return -EINVAL;
+		break;
+
+	case LIRC_SET_SEND_CARRIER:
+
+		result = get_user(ivalue, (unsigned int *) arg);
+		if (result)
+			return result;
+
+		set_send_carrier(ir, ivalue);
+		break;
+
+	default:
+		return -ENOIOCTLCMD;
+	}
+
+	return 0;
+}
+
+static struct file_operations lirc_fops = {
+	.write	= lirc_write,
+};
+
+
+static int usb_remote_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 irctl *ir = NULL;
+	struct lirc_plugin *plugin = 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;
+
+	dprintk(DRIVER_NAME ": usb probe called\n");
+
+	usb_reset_device(dev);
+
+	config = dev->actconfig;
+
+	idesc = intf->cur_altsetting;
+
+	is_pinnacle = usb_match_id(intf, pinnacle_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 ((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 (ep_in == NULL) {
+		dprintk(DRIVER_NAME ": inbound and/or endpoint not found\n");
+		return -ENODEV;
+	}
+
+	devnum = dev->devnum;
+	pipe = usb_rcvintpipe(dev, ep_in->bEndpointAddress);
+	maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
+
+	/* allocate kernel memory */
+	mem_failure = 0;
+	ir = kmalloc(sizeof(struct irctl), GFP_KERNEL);
+	if (!ir) {
+		mem_failure = 1;
+		goto mem_failure_switch;
+	}
+
+	memset(ir, 0, sizeof(struct irctl));
+
+	plugin = kmalloc(sizeof(struct lirc_plugin), GFP_KERNEL);
+	if (!plugin) {
+		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(int), 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;
+	}
+
+	memset(plugin, 0, sizeof(struct lirc_plugin));
+
+	strcpy(plugin->name, DRIVER_NAME " ");
+	plugin->minor = -1;
+	plugin->features = LIRC_CAN_SEND_PULSE |
+		LIRC_CAN_SET_TRANSMITTER_MASK |
+		LIRC_CAN_REC_MODE2 |
+		LIRC_CAN_SET_SEND_CARRIER;
+	plugin->data = ir;
+	plugin->rbuf = rbuf;
+	plugin->set_use_inc = &set_use_inc;
+	plugin->set_use_dec = &set_use_dec;
+	plugin->code_length = sizeof(int) * 8;
+	plugin->ioctl = lirc_ioctl;
+	plugin->fops  = &lirc_fops;
+	plugin->dev   = &dev->dev;
+	plugin->owner = THIS_MODULE;
+
+	mutex_init(&ir->lock);
+	init_waitqueue_head(&ir->wait_out);
+
+	minor = lirc_register_plugin(plugin);
+	if (minor < 0)
+		mem_failure = 9;
+
+mem_failure_switch:
+
+	/* free allocated memory incase of failure */
+	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(plugin);
+	case 2:
+		kfree(ir);
+	case 1:
+		printk(DRIVER_NAME "[%d]: out of memory (code=%d)\n",
+			devnum, mem_failure);
+		return -ENOMEM;
+	}
+
+	plugin->minor = minor;
+	ir->p = plugin;
+	ir->devnum = devnum;
+	ir->usbdev = dev;
+	ir->len_in = maxp;
+	ir->flags.connected = 0;
+	ir->flags.pinnacle = is_pinnacle;
+	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, 63) > 0)
+		strncpy(name, buf, 128);
+	if (dev->descriptor.iProduct
+		&& usb_string(dev, dev->descriptor.iProduct, buf, 63) > 0)
+		snprintf(name, 128, "%s %s", name, 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) usb_remote_recv, ir, ep_in->bInterval);
+
+	/* 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, PHILUSB_INBOUND);
+		request_packet_async(ir, ep_out, pin_init1, sizeof(pin_init1),
+				     PHILUSB_OUTBOUND);
+		request_packet_async(ir, ep_in, NULL, maxp, PHILUSB_INBOUND);
+		request_packet_async(ir, ep_out, pin_init2, sizeof(pin_init2),
+				     PHILUSB_OUTBOUND);
+		request_packet_async(ir, ep_in, NULL, maxp, PHILUSB_INBOUND);
+		request_packet_async(ir, ep_out, pin_init3, sizeof(pin_init3),
+				     PHILUSB_OUTBOUND);
+		/* if we dont issue the correct number of receives
+		 * (PHILUSB_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
+		 * usb_remote_recv routine will handle the control packets OK -
+		 * they start with 0x9f - but the async callback doesnt handle
+		 * ir pulse packets
+		 */
+		request_packet_async(ir, ep_in, NULL, maxp, 0);
+	} else {
+		request_packet_async(ir, ep_in, NULL, maxp, PHILUSB_INBOUND);
+		request_packet_async(ir, ep_in, NULL, maxp, PHILUSB_INBOUND);
+		request_packet_async(ir, ep_out, init1,
+				     sizeof(init1), PHILUSB_OUTBOUND);
+		request_packet_async(ir, ep_in, NULL, maxp, PHILUSB_INBOUND);
+		request_packet_async(ir, ep_out, init2,
+				     sizeof(init2), PHILUSB_OUTBOUND);
+		request_packet_async(ir, ep_in, NULL, maxp, 0);
+	}
+
+	usb_set_intfdata(intf, ir);
+
+	return SUCCESS;
+}
+
+
+static void usb_remote_disconnect(struct usb_interface *intf)
+{
+	struct usb_device *dev = interface_to_usbdev(intf);
+	struct irctl *ir = usb_get_intfdata(intf);
+
+	usb_set_intfdata(intf, NULL);
+
+	if (!ir || !ir->p)
+		return;
+
+	ir->usbdev = NULL;
+	wake_up_all(&ir->wait_out);
+
+	IRLOCK;
+	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);
+	IRUNLOCK;
+
+	unregister_from_lirc(ir);
+}
+
+static int usb_remote_suspend(struct usb_interface *intf, pm_message_t message)
+{
+	struct irctl *ir = usb_get_intfdata(intf);
+	printk(DRIVER_NAME "[%d]: suspend\n", ir->devnum);
+	usb_kill_urb(ir->urb_in);
+	return 0;
+}
+
+static int usb_remote_resume(struct usb_interface *intf)
+{
+	struct irctl *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;
+}
+
+static struct usb_driver usb_remote_driver = {
+	.name =		DRIVER_NAME,
+	.probe =	usb_remote_probe,
+	.disconnect =	usb_remote_disconnect,
+	.suspend =	usb_remote_suspend,
+	.resume =	usb_remote_resume,
+	.id_table =	usb_remote_table
+};
+
+#ifdef MODULE
+static int __init usb_remote_init(void)
+{
+	int i;
+
+	printk(KERN_INFO "\n");
+	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");
+
+	request_module("lirc_dev");
+
+	i = usb_register(&usb_remote_driver);
+	if (i < 0) {
+		printk(DRIVER_NAME ": usb register failed, result = %d\n", i);
+		return -ENODEV;
+	}
+
+	return SUCCESS;
+}
+
+static void __exit usb_remote_exit(void)
+{
+	usb_deregister(&usb_remote_driver);
+}
+
+module_init(usb_remote_init);
+module_exit(usb_remote_exit);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(usb, usb_remote_table);
+
+module_param(debug, bool, 0644);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
+#endif /* MODULE */
diff --git a/drivers/input/lirc/lirc_parallel.c b/drivers/input/lirc/lirc_parallel.c
new file mode 100644
index 0000000..912cad2
--- /dev/null
+++ b/drivers/input/lirc/lirc_parallel.c
@@ -0,0 +1,728 @@
+/****************************************************************************
+ ** lirc_parallel.c *********************************************************
+ ****************************************************************************
+ *
+ * lirc_parallel - device driver for infra-red signal receiving and
+ *                 transmitting unit built by the author
+ *
+ * Copyright (C) 1998 Christoph Bartelmus <lirc@bartelmus.de>
+ *
+ *  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
+ *
+ */
+
+/***********************************************************************
+ *************************       Includes        ***********************
+ ***********************************************************************/
+
+#include <linux/version.h>
+
+#include <linux/autoconf.h>
+
+#ifdef CONFIG_SMP
+#error "--- Sorry, this driver is not SMP safe. ---"
+#endif
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/autoconf.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/time.h>
+#include <linux/mm.h>
+#include <linux/delay.h>
+
+#include <linux/io.h>
+#include <linux/signal.h>
+#include <linux/irq.h>
+#include <linux/uaccess.h>
+#include <asm/div64.h>
+
+#include <linux/poll.h>
+#include <linux/parport.h>
+
+#include "lirc.h"
+#include "lirc_dev.h"
+
+#include "lirc_parallel.h"
+
+#define LIRC_DRIVER_NAME "lirc_parallel"
+
+#ifndef LIRC_IRQ
+#define LIRC_IRQ 7
+#endif
+#ifndef LIRC_PORT
+#define LIRC_PORT 0x378
+#endif
+#ifndef LIRC_TIMER
+#define LIRC_TIMER 65536
+#endif
+
+/***********************************************************************
+ *************************   Globale Variablen   ***********************
+ ***********************************************************************/
+
+static int debug;
+static int check_pselecd;
+
+unsigned int irq = LIRC_IRQ;
+unsigned int io = LIRC_PORT;
+#ifdef LIRC_TIMER
+unsigned int timer;
+unsigned int default_timer = LIRC_TIMER;
+#endif
+
+#define WBUF_SIZE (256)
+#define RBUF_SIZE (256) /* this must be a power of 2 larger than 1 */
+
+static int wbuf[WBUF_SIZE];
+static int rbuf[RBUF_SIZE];
+
+DECLARE_WAIT_QUEUE_HEAD(lirc_wait);
+
+unsigned int rptr;
+unsigned int wptr;
+unsigned int lost_irqs;
+int is_open;
+
+struct parport *pport;
+struct pardevice *ppdevice;
+int is_claimed;
+
+unsigned int tx_mask = 1;
+
+/***********************************************************************
+ *************************   Interne Funktionen  ***********************
+ ***********************************************************************/
+
+static inline unsigned int in(int offset)
+{
+	switch (offset) {
+	case LIRC_LP_BASE:
+		return parport_read_data(pport);
+	case LIRC_LP_STATUS:
+		return parport_read_status(pport);
+	case LIRC_LP_CONTROL:
+		return parport_read_control(pport);
+	}
+	return 0; /* make compiler happy */
+}
+
+static inline void out(int offset, int value)
+{
+	switch (offset) {
+	case LIRC_LP_BASE:
+		parport_write_data(pport, value);
+		break;
+	case LIRC_LP_CONTROL:
+		parport_write_control(pport, value);
+		break;
+	case LIRC_LP_STATUS:
+		printk(KERN_INFO "%s: attempt to write to status register\n",
+		       LIRC_DRIVER_NAME);
+		break;
+	}
+}
+
+static inline unsigned int lirc_get_timer(void)
+{
+	return in(LIRC_PORT_TIMER) & LIRC_PORT_TIMER_BIT;
+}
+
+static inline  unsigned int lirc_get_signal(void)
+{
+	return in(LIRC_PORT_SIGNAL) & LIRC_PORT_SIGNAL_BIT;
+}
+
+static inline void lirc_on(void)
+{
+	out(LIRC_PORT_DATA, tx_mask);
+}
+
+static inline void lirc_off(void)
+{
+	out(LIRC_PORT_DATA, 0);
+}
+
+static unsigned int init_lirc_timer(void)
+{
+	struct timeval tv, now;
+	unsigned int level, newlevel, timeelapsed, newtimer;
+	int count = 0;
+
+	do_gettimeofday(&tv);
+	tv.tv_sec++;                     /* wait max. 1 sec. */
+	level = lirc_get_timer();
+	do {
+		newlevel = lirc_get_timer();
+		if (level == 0 && newlevel != 0)
+			count++;
+		level = newlevel;
+		do_gettimeofday(&now);
+	} while (count < 1000 && (now.tv_sec < tv.tv_sec
+			     || (now.tv_sec == tv.tv_sec
+				 && now.tv_usec < tv.tv_usec)));
+
+	timeelapsed = ((now.tv_sec + 1 - tv.tv_sec)*1000000
+		     + (now.tv_usec - tv.tv_usec));
+	if (count >= 1000 && timeelapsed > 0) {
+		if (default_timer == 0) {
+			/* autodetect timer */
+			newtimer = (1000000*count)/timeelapsed;
+			printk(KERN_INFO "%s: %u Hz timer detected\n",
+			       LIRC_DRIVER_NAME, newtimer);
+			return newtimer;
+		}  else {
+			newtimer = (1000000*count)/timeelapsed;
+			if (abs(newtimer - default_timer) > default_timer/10) {
+				/* bad timer */
+				printk(KERN_NOTICE "%s: bad timer: %u Hz\n",
+				       LIRC_DRIVER_NAME, newtimer);
+				printk(KERN_NOTICE "%s: using default timer: "
+				       "%u Hz\n",
+				       LIRC_DRIVER_NAME, default_timer);
+				return default_timer;
+			} else {
+				printk(KERN_INFO "%s: %u Hz timer detected\n",
+				       LIRC_DRIVER_NAME, newtimer);
+				return newtimer; /* use detected value */
+			}
+		}
+	} else {
+		printk(KERN_NOTICE "%s: no timer detected\n", LIRC_DRIVER_NAME);
+		return 0;
+	}
+}
+
+static int lirc_claim(void)
+{
+	if (parport_claim(ppdevice) != 0) {
+		printk(KERN_WARNING "%s: could not claim port\n",
+		       LIRC_DRIVER_NAME);
+		printk(KERN_WARNING "%s: waiting for port becoming available"
+		       "\n", LIRC_DRIVER_NAME);
+		if (parport_claim_or_block(ppdevice) < 0) {
+			printk(KERN_NOTICE "%s: could not claim port, giving"
+			       " up\n", LIRC_DRIVER_NAME);
+			return 0;
+		}
+	}
+	out(LIRC_LP_CONTROL, LP_PSELECP|LP_PINITP);
+	is_claimed = 1;
+	return 1;
+}
+
+/***********************************************************************
+ *************************   interrupt handler  ************************
+ ***********************************************************************/
+
+static inline void rbuf_write(int signal)
+{
+	unsigned int nwptr;
+
+	nwptr = (wptr + 1) & (RBUF_SIZE - 1);
+	if (nwptr == rptr) {
+		/* no new signals will be accepted */
+		lost_irqs++;
+		printk(KERN_NOTICE "%s: buffer overrun\n", LIRC_DRIVER_NAME);
+		return;
+	}
+	rbuf[wptr] = signal;
+	wptr = nwptr;
+}
+
+static void irq_handler(void *blah)
+{
+	struct timeval tv;
+	static struct timeval lasttv;
+	static int init;
+	long signal;
+	int data;
+	unsigned int level, newlevel;
+	unsigned int timeout;
+
+	if (!module_refcount(THIS_MODULE))
+		return;
+
+	if (!is_claimed)
+		return;
+
+	/* disable interrupt */
+	/*
+	  disable_irq(irq);
+	  out(LIRC_PORT_IRQ, in(LIRC_PORT_IRQ) & (~LP_PINTEN));
+	*/
+	if (check_pselecd && (in(1) & LP_PSELECD))
+		return;
+
+#ifdef LIRC_TIMER
+	if (init) {
+		do_gettimeofday(&tv);
+
+		signal = tv.tv_sec - lasttv.tv_sec;
+		if (signal > 15)
+			/* really long time */
+			data = PULSE_MASK;
+		else
+			data = (int) (signal*1000000 +
+					 tv.tv_usec - lasttv.tv_usec +
+					 LIRC_SFH506_DELAY);
+
+		rbuf_write(data); /* space */
+	} else {
+		if (timer == 0) {
+			/* wake up; we'll lose this signal
+			 * but it will be garbage if the device
+			 * is turned on anyway */
+			timer = init_lirc_timer();
+			/* enable_irq(irq); */
+			return;
+		}
+		init = 1;
+	}
+
+	timeout = timer/10;	/* timeout after 1/10 sec. */
+	signal = 1;
+	level = lirc_get_timer();
+	do {
+		newlevel = lirc_get_timer();
+		if (level == 0 && newlevel != 0)
+			signal++;
+		level = newlevel;
+
+		/* giving up */
+		if (signal > timeout
+		    || (check_pselecd && (in(1) & LP_PSELECD))) {
+			signal = 0;
+			printk(KERN_NOTICE "%s: timeout\n", LIRC_DRIVER_NAME);
+			break;
+		}
+	} while (lirc_get_signal());
+
+	if (signal != 0) {
+		/* ajust value to usecs */
+		unsigned long long helper;
+
+		helper = ((unsigned long long) signal)*1000000;
+		do_div(helper, timer);
+		signal = (long) helper;
+
+		if (signal > LIRC_SFH506_DELAY)
+			data = signal - LIRC_SFH506_DELAY;
+		else
+			data = 1;
+		rbuf_write(PULSE_BIT|data); /* pulse */
+	}
+	do_gettimeofday(&lasttv);
+#else
+	/* add your code here */
+#endif
+
+	wake_up_interruptible(&lirc_wait);
+
+	/* enable interrupt */
+	/*
+	  enable_irq(irq);
+	  out(LIRC_PORT_IRQ, in(LIRC_PORT_IRQ)|LP_PINTEN);
+	*/
+}
+
+/***********************************************************************
+ **************************   file_operations   ************************
+ ***********************************************************************/
+
+static loff_t lirc_lseek(struct file *filep, loff_t offset, int orig)
+{
+	return -ESPIPE;
+}
+
+static ssize_t lirc_read(struct file *filep, char *buf, size_t n, loff_t *ppos)
+{
+	int result = 0;
+	int count = 0;
+	DECLARE_WAITQUEUE(wait, current);
+
+	if (n % sizeof(int))
+		return -EINVAL;
+
+	add_wait_queue(&lirc_wait, &wait);
+	set_current_state(TASK_INTERRUPTIBLE);
+	while (count < n) {
+		if (rptr != wptr) {
+			if (copy_to_user(buf+count, (char *) &rbuf[rptr],
+					 sizeof(int))) {
+				result = -EFAULT;
+				break;
+			}
+			rptr = (rptr + 1) & (RBUF_SIZE - 1);
+			count += sizeof(int);
+		} else {
+			if (filep->f_flags & O_NONBLOCK) {
+				result = -EAGAIN;
+				break;
+			}
+			if (signal_pending(current)) {
+				result = -ERESTARTSYS;
+				break;
+			}
+			schedule();
+			set_current_state(TASK_INTERRUPTIBLE);
+		}
+	}
+	remove_wait_queue(&lirc_wait, &wait);
+	set_current_state(TASK_RUNNING);
+	return count ? count : result;
+}
+
+static ssize_t lirc_write(struct file *filep, const char *buf, size_t n,
+			  loff_t *ppos)
+{
+	int count;
+	unsigned int i;
+	unsigned int level, newlevel;
+	unsigned long flags;
+	int counttimer;
+
+	if (!is_claimed)
+		return -EBUSY;
+
+	if (n % sizeof(int))
+		return -EINVAL;
+
+	count = n / sizeof(int);
+
+	if (count > WBUF_SIZE || count % 2 == 0)
+		return -EINVAL;
+
+	if (copy_from_user(wbuf, buf, n))
+		return -EFAULT;
+
+#ifdef LIRC_TIMER
+	if (timer == 0) {
+		/* try again if device is ready */
+		timer = init_lirc_timer();
+		if (timer == 0)
+			return -EIO;
+	}
+
+	/* ajust values from usecs */
+	for (i = 0; i < count; i++) {
+		unsigned long long helper;
+
+		helper = ((unsigned long long) wbuf[i])*timer;
+		do_div(helper, 1000000);
+		wbuf[i] = (int) helper;
+	}
+
+	local_irq_save(flags);
+	i = 0;
+	while (i < count) {
+		level = lirc_get_timer();
+		counttimer = 0;
+		lirc_on();
+		do {
+			newlevel = lirc_get_timer();
+			if (level == 0 && newlevel != 0)
+				counttimer++;
+			level = newlevel;
+			if (check_pselecd && (in(1) & LP_PSELECD)) {
+				lirc_off();
+				local_irq_restore(flags);
+				return -EIO;
+			}
+		} while (counttimer < wbuf[i]);
+		i++;
+
+		lirc_off();
+		if (i == count)
+			break;
+		counttimer = 0;
+		do {
+			newlevel = lirc_get_timer();
+			if (level == 0 && newlevel != 0)
+				counttimer++;
+			level = newlevel;
+			if (check_pselecd && (in(1) & LP_PSELECD)) {
+				local_irq_restore(flags);
+				return -EIO;
+			}
+		} while (counttimer < wbuf[i]);
+		i++;
+	}
+	local_irq_restore(flags);
+#else
+	/* place code that handles write
+	 * without external timer here */
+#endif
+	return n;
+}
+
+static unsigned int lirc_poll(struct file *file, poll_table *wait)
+{
+	poll_wait(file, &lirc_wait, wait);
+	if (rptr != wptr)
+		return POLLIN | POLLRDNORM;
+	return 0;
+}
+
+static int lirc_ioctl(struct inode *node, struct file *filep, unsigned int cmd,
+		      unsigned long arg)
+{
+	int result;
+	unsigned long features = LIRC_CAN_SET_TRANSMITTER_MASK |
+				 LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2;
+	unsigned long mode;
+	unsigned int ivalue;
+
+	switch (cmd) {
+	case LIRC_GET_FEATURES:
+		result = put_user(features, (unsigned long *) arg);
+		if (result)
+			return result;
+		break;
+	case LIRC_GET_SEND_MODE:
+		result = put_user(LIRC_MODE_PULSE, (unsigned long *) arg);
+		if (result)
+			return result;
+		break;
+	case LIRC_GET_REC_MODE:
+		result = put_user(LIRC_MODE_MODE2, (unsigned long *) arg);
+		if (result)
+			return result;
+		break;
+	case LIRC_SET_SEND_MODE:
+		result = get_user(mode, (unsigned long *) arg);
+		if (result)
+			return result;
+		if (mode != LIRC_MODE_PULSE)
+			return -EINVAL;
+		break;
+	case LIRC_SET_REC_MODE:
+		result = get_user(mode, (unsigned long *) arg);
+		if (result)
+			return result;
+		if (mode != LIRC_MODE_MODE2)
+			return -ENOSYS;
+		break;
+	case LIRC_SET_TRANSMITTER_MASK:
+		result = get_user(ivalue, (unsigned int *) arg);
+		if (result)
+			return result;
+		if ((ivalue & LIRC_PARALLEL_TRANSMITTER_MASK) != ivalue)
+			return LIRC_PARALLEL_MAX_TRANSMITTERS;
+		tx_mask = ivalue;
+		break;
+	default:
+		return -ENOIOCTLCMD;
+	}
+	return 0;
+}
+
+static int lirc_open(struct inode *node, struct file *filep)
+{
+	if (module_refcount(THIS_MODULE) || !lirc_claim())
+		return -EBUSY;
+
+	parport_enable_irq(pport);
+
+	/* init read ptr */
+	rptr = 0;
+	wptr = 0;
+	lost_irqs = 0;
+
+	is_open = 1;
+	return 0;
+}
+
+static int lirc_close(struct inode *node, struct file *filep)
+{
+	if (is_claimed) {
+		is_claimed = 0;
+		parport_release(ppdevice);
+	}
+	is_open = 0;
+	return 0;
+}
+
+static struct file_operations lirc_fops = {
+	.llseek		= lirc_lseek,
+	.read		= lirc_read,
+	.write		= lirc_write,
+	.poll		= lirc_poll,
+	.ioctl		= lirc_ioctl,
+	.open		= lirc_open,
+	.release	= lirc_close
+};
+
+static int set_use_inc(void *data)
+{
+	return 0;
+}
+
+static void set_use_dec(void *data)
+{
+}
+
+static struct lirc_plugin plugin = {
+       .name		= LIRC_DRIVER_NAME,
+       .minor		= -1,
+       .code_length	= 1,
+       .sample_rate	= 0,
+       .data		= NULL,
+       .add_to_buf	= NULL,
+       .get_queue	= NULL,
+       .set_use_inc	= set_use_inc,
+       .set_use_dec	= set_use_dec,
+       .fops		= &lirc_fops,
+       .dev		= NULL,
+       .owner		= THIS_MODULE,
+};
+
+#ifdef MODULE
+
+static int pf(void *handle);
+static void kf(void *handle);
+
+static struct timer_list poll_timer;
+static void poll_state(unsigned long ignored);
+
+static void poll_state(unsigned long ignored)
+{
+	printk(KERN_NOTICE "%s: time\n",
+	       LIRC_DRIVER_NAME);
+	del_timer(&poll_timer);
+	if (is_claimed)
+		return;
+	kf(NULL);
+	if (!is_claimed) {
+		printk(KERN_NOTICE "%s: could not claim port, giving up\n",
+		       LIRC_DRIVER_NAME);
+		init_timer(&poll_timer);
+		poll_timer.expires = jiffies + HZ;
+		poll_timer.data = (unsigned long)current;
+		poll_timer.function = poll_state;
+		add_timer(&poll_timer);
+	}
+}
+
+static int pf(void *handle)
+{
+	parport_disable_irq(pport);
+	is_claimed = 0;
+	return 0;
+}
+
+static void kf(void *handle)
+{
+	if (!is_open)
+		return;
+	if (!lirc_claim())
+		return;
+	parport_enable_irq(pport);
+	lirc_off();
+	/* this is a bit annoying when you actually print...*/
+	/*
+	printk(KERN_INFO "%s: reclaimed port\n", LIRC_DRIVER_NAME);
+	*/
+}
+
+/***********************************************************************
+ ******************   init_module()/cleanup_module()  ******************
+ ***********************************************************************/
+
+int init_module(void)
+{
+	pport = parport_find_base(io);
+	if (pport == NULL) {
+		printk(KERN_NOTICE "%s: no port at %x found\n",
+		       LIRC_DRIVER_NAME, io);
+		return -ENXIO;
+	}
+	ppdevice = parport_register_device(pport, LIRC_DRIVER_NAME,
+					   pf, kf, irq_handler, 0, NULL);
+	parport_put_port(pport);
+	if (ppdevice == NULL) {
+		printk(KERN_NOTICE "%s: parport_register_device() failed\n",
+		       LIRC_DRIVER_NAME);
+		return -ENXIO;
+	}
+	if (parport_claim(ppdevice) != 0)
+		goto skip_init;
+	is_claimed = 1;
+	out(LIRC_LP_CONTROL, LP_PSELECP|LP_PINITP);
+
+#ifdef LIRC_TIMER
+	if (debug)
+		out(LIRC_PORT_DATA, tx_mask);
+
+	timer = init_lirc_timer();
+
+#if 0	/* continue even if device is offline */
+	if (timer == 0) {
+		is_claimed = 0;
+		parport_release(pport);
+		parport_unregister_device(ppdevice);
+		return -EIO;
+	}
+
+#endif
+	if (debug)
+		out(LIRC_PORT_DATA, 0);
+#endif
+
+	is_claimed = 0;
+	parport_release(ppdevice);
+ skip_init:
+	plugin.minor = lirc_register_plugin(&plugin);
+	if (plugin.minor < 0) {
+		printk(KERN_NOTICE "%s: register_chrdev() failed\n",
+		       LIRC_DRIVER_NAME);
+		parport_unregister_device(ppdevice);
+		return -EIO;
+	}
+	printk(KERN_INFO "%s: installed using port 0x%04x irq %d\n",
+	       LIRC_DRIVER_NAME, io, irq);
+	return 0;
+}
+
+void cleanup_module(void)
+{
+	parport_unregister_device(ppdevice);
+	lirc_unregister_plugin(plugin.minor);
+}
+
+MODULE_DESCRIPTION("Infrared receiver driver for parallel ports.");
+MODULE_AUTHOR("Christoph Bartelmus");
+MODULE_LICENSE("GPL");
+
+module_param(io, int, 0444);
+MODULE_PARM_DESC(io, "I/O address base (0x3bc, 0x378 or 0x278)");
+
+module_param(irq, int, 0444);
+MODULE_PARM_DESC(irq, "Interrupt (7 or 5)");
+
+module_param(tx_mask, int, 0444);
+MODULE_PARM_DESC(tx_maxk, "Transmitter mask (default: 0x01)");
+
+module_param(debug, bool, 0644);
+MODULE_PARM_DESC(debug, "Enable debugging messages");
+
+module_param(check_pselecd, bool, 0644);
+MODULE_PARM_DESC(debug, "Check for printer (default: 0)");
+
+#endif /* MODULE */
diff --git a/drivers/input/lirc/lirc_parallel.h b/drivers/input/lirc/lirc_parallel.h
new file mode 100644
index 0000000..4bed6af
--- /dev/null
+++ b/drivers/input/lirc/lirc_parallel.h
@@ -0,0 +1,26 @@
+/* lirc_parallel.h */
+
+#ifndef _LIRC_PARALLEL_H
+#define _LIRC_PARALLEL_H
+
+#include <linux/lp.h>
+
+#define LIRC_PORT_LEN 3
+
+#define LIRC_LP_BASE    0
+#define LIRC_LP_STATUS  1
+#define LIRC_LP_CONTROL 2
+
+#define LIRC_PORT_DATA           LIRC_LP_BASE    /* base */
+#define LIRC_PORT_TIMER        LIRC_LP_STATUS    /* status port */
+#define LIRC_PORT_TIMER_BIT          LP_PBUSY    /* busy signal */
+#define LIRC_PORT_SIGNAL       LIRC_LP_STATUS    /* status port */
+#define LIRC_PORT_SIGNAL_BIT          LP_PACK    /* ack signal */
+#define LIRC_PORT_IRQ         LIRC_LP_CONTROL    /* control port */
+
+#define LIRC_SFH506_DELAY 0             /* delay t_phl in usecs */
+
+#define LIRC_PARALLEL_MAX_TRANSMITTERS 8
+#define LIRC_PARALLEL_TRANSMITTER_MASK ((1<<LIRC_PARALLEL_MAX_TRANSMITTERS) - 1)
+
+#endif
diff --git a/drivers/input/lirc/lirc_sasem.c b/drivers/input/lirc/lirc_sasem.c
new file mode 100644
index 0000000..003f492
--- /dev/null
+++ b/drivers/input/lirc/lirc_sasem.c
@@ -0,0 +1,969 @@
+/* lirc_sasem.c - USB remote support for LIRC
+ * Version 0.5
+ *
+ * Copyright (C) 2004-2005 Oliver Stabel <oliver.stabel@gmx.de>
+ *			 Tim Davies <tim@opensystems.net.au>
+ *
+ * This driver was derived from:
+ *   Venky Raju <dev@venky.ws>
+ *      "lirc_imon - "LIRC plugin/VFD driver for Ahanix/Soundgraph IMON IR/VFD"
+ *   Paul Miller <pmiller9@users.sourceforge.net>'s 2003-2004
+ *      "lirc_atiusb - USB remote support for LIRC"
+ *   Culver Consulting Services <henry@culcon.com>'s 2003
+ *      "Sasem OnAir VFD/IR USB driver"
+ *
+ *
+ * 2004/06/13   -   0.1
+ *		  initial version
+ *
+ * 2004/06/28   -   0.2
+ *		  added file system support to write data to VFD device (used
+ *		  in conjunction with LCDProc)
+ *
+ * 2004/11/22   -   0.3
+ *		  Ported to 2.6 kernel
+ *			- Tim Davies <tim@opensystems.net.au>
+ *
+ * 2005/03/29   -   0.4
+ *		  A few tidyups and keypress timings
+ *			- Tim Davies <tim@opensystems.net.au>
+ *
+ * 2005/06/23   -   0.5
+ *		  A complete rewrite (shamelessly) based on lirc_imon.c
+ *		  Tim Davies <tim@opensystems.net.au>
+ *
+ * NOTE - The LCDproc iMon driver should work with this module.  More info at
+ *	http://www.frogstorm.info/sasem
+ */
+
+/*
+ *  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>
+
+
+#include <linux/autoconf.h>
+
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/usb.h>
+
+#include "lirc.h"
+#include "lirc_dev.h"
+
+
+#define MOD_AUTHOR	"Oliver Stabel <oliver.stabel@gmx.de>, " \
+			"Tim Davies <tim@opensystems.net.au>"
+#define MOD_DESC	"USB Driver for Sasem Remote Controller V1.1"
+#define MOD_NAME	"lirc_sasem"
+#define MOD_VERSION	"0.5"
+
+#define VFD_MINOR_BASE	144	/* Same as LCD */
+#define DEVICE_NAME	"lcd%d"
+
+#define BUF_CHUNK_SIZE	8
+#define BUF_SIZE	128
+
+#define SUCCESS		0
+#define	TRUE		1
+#define FALSE		0
+
+#define IOCTL_LCD_CONTRAST 1
+
+/* ------------------------------------------------------------
+ *		     P R O T O T Y P E S
+ * ------------------------------------------------------------
+ */
+
+/* USB Callback prototypes */
+static int sasem_probe(struct usb_interface *interface,
+			const struct usb_device_id *id);
+static void sasem_disconnect(struct usb_interface *interface);
+static void usb_rx_callback(struct urb *urb);
+static void usb_tx_callback(struct urb *urb);
+
+/* VFD file_operations function prototypes */
+static int vfd_open(struct inode *inode, struct file *file);
+static int vfd_ioctl(struct inode *inode, struct file *file,
+				unsigned cmd, unsigned long arg);
+static int vfd_close(struct inode *inode, struct file *file);
+static ssize_t vfd_write(struct file *file, const char *buf,
+				size_t n_bytes, loff_t *pos);
+
+/* LIRC plugin function prototypes */
+static int ir_open(void *data);
+static void ir_close(void *data);
+
+/* Driver init/exit prototypes */
+static int __init sasem_init(void);
+static void __exit sasem_exit(void);
+
+/* ------------------------------------------------------------
+ *		     G L O B A L S
+ * ------------------------------------------------------------
+ */
+
+struct sasem_context {
+
+	struct usb_device *dev;
+	int vfd_isopen;			/* VFD port has been opened       */
+	unsigned int vfd_contrast;	/* VFD contrast		   */
+	int ir_isopen;			/* IR port has been opened	*/
+	int dev_present;		/* USB device presence	    */
+	struct mutex lock;		/* to lock this object	    */
+	wait_queue_head_t remove_ok;	/* For unexpected USB disconnects */
+
+	struct lirc_plugin *plugin;
+	struct usb_endpoint_descriptor *rx_endpoint;
+	struct usb_endpoint_descriptor *tx_endpoint;
+	struct urb *rx_urb;
+	struct urb *tx_urb;
+	unsigned char usb_rx_buf[8];
+	unsigned char usb_tx_buf[8];
+
+	struct tx_t {
+		unsigned char data_buf[32];  /* user data buffer	  */
+		struct completion finished;  /* wait for write to finish  */
+		atomic_t busy;		     /* write in progress	 */
+		int status;		     /* status of tx completion   */
+	} tx;
+
+	/* for dealing with repeat codes (wish there was a toggle bit!) */
+	struct timeval presstime;
+	char lastcode[8];
+	int codesaved;
+};
+
+#define LOCK_CONTEXT	mutex_lock(&context->lock)
+#define UNLOCK_CONTEXT	mutex_unlock(&context->lock)
+
+/* VFD file operations */
+static struct file_operations vfd_fops = {
+
+	.owner		= THIS_MODULE,
+	.open		= &vfd_open,
+	.write		= &vfd_write,
+	.ioctl		= &vfd_ioctl,
+	.release	= &vfd_close
+};
+
+/* USB Device ID for Sasem USB Control Board */
+static struct usb_device_id sasem_usb_id_table[] = {
+	/* Sasem USB Control Board */
+	{ USB_DEVICE(0x11ba, 0x0101) },
+	/* Terminiating entry */
+	{}
+};
+
+/* USB Device data */
+static struct usb_driver sasem_driver = {
+	.name		= MOD_NAME,
+	.probe		= sasem_probe,
+	.disconnect	= sasem_disconnect,
+	.id_table	= sasem_usb_id_table,
+};
+
+static struct usb_class_driver sasem_class = {
+	.name		= DEVICE_NAME,
+	.fops		= &vfd_fops,
+	.minor_base	= VFD_MINOR_BASE,
+};
+
+/* to prevent races between open() and disconnect() */
+static DECLARE_MUTEX(disconnect_sem);
+
+static int debug;
+
+
+/* ------------------------------------------------------------
+ *		     M O D U L E   C O D E
+ * ------------------------------------------------------------
+ */
+
+MODULE_AUTHOR(MOD_AUTHOR);
+MODULE_DESCRIPTION(MOD_DESC);
+MODULE_LICENSE("GPL");
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "Debug messages: 0=no, 1=yes (default: no)");
+
+static inline void delete_context(struct sasem_context *context)
+{
+	usb_free_urb(context->tx_urb);  /* VFD */
+	usb_free_urb(context->rx_urb);  /* IR */
+	lirc_buffer_free(context->plugin->rbuf);
+	kfree(context->plugin->rbuf);
+	kfree(context->plugin);
+	kfree(context);
+
+	if (debug)
+		info("%s: context deleted", __func__);
+}
+
+static inline void deregister_from_lirc(struct sasem_context *context)
+{
+	int retval;
+	int minor = context->plugin->minor;
+
+	retval = lirc_unregister_plugin(minor);
+	if (retval)
+		err("%s: unable to deregister from lirc (%d)",
+			__func__, retval);
+	else
+		info("Deregistered Sasem plugin (minor:%d)", minor);
+
+}
+
+/**
+ * Called when the VFD device (e.g. /dev/usb/lcd)
+ * is opened by the application.
+ */
+static int vfd_open(struct inode *inode, struct file *file)
+{
+	struct usb_interface *interface;
+	struct sasem_context *context = NULL;
+	int subminor;
+	int retval = SUCCESS;
+
+	/* prevent races with disconnect */
+	down(&disconnect_sem);
+
+	subminor = iminor(inode);
+	interface = usb_find_interface(&sasem_driver, subminor);
+	if (!interface) {
+		err("%s: could not find interface for minor %d",
+		    __func__, subminor);
+		retval = -ENODEV;
+		goto exit;
+	}
+	context = usb_get_intfdata(interface);
+
+	if (!context) {
+		err("%s: no context found for minor %d",
+					__func__, subminor);
+		retval = -ENODEV;
+		goto exit;
+	}
+
+	LOCK_CONTEXT;
+
+	if (context->vfd_isopen) {
+		err("%s: VFD port is already open", __func__);
+		retval = -EBUSY;
+	} else {
+		context->vfd_isopen = TRUE;
+		file->private_data = context;
+		info("VFD port opened");
+	}
+
+	UNLOCK_CONTEXT;
+
+exit:
+	up(&disconnect_sem);
+	return retval;
+}
+
+/**
+ * Called when the VFD device (e.g. /dev/usb/lcd)
+ * is closed by the application.
+ */
+static int vfd_ioctl(struct inode *inode, struct file *file,
+		     unsigned cmd, unsigned long arg)
+{
+	struct sasem_context *context = NULL;
+
+	context = (struct sasem_context *) file->private_data;
+
+	if (!context) {
+		err("%s: no context for device", __func__);
+		return -ENODEV;
+	}
+
+	LOCK_CONTEXT;
+
+	switch (cmd) {
+	case IOCTL_LCD_CONTRAST:
+		if (arg > 1000)
+			arg = 1000;
+		if (arg < 0)
+			arg = 0;
+		context->vfd_contrast = (unsigned int)arg;
+		break;
+	default:
+		info("Unknown IOCTL command");
+		UNLOCK_CONTEXT;
+		return -ENOIOCTLCMD;  /* not supported */
+	}
+
+	UNLOCK_CONTEXT;
+	return 0;
+}
+
+/**
+ * Called when the VFD device (e.g. /dev/usb/lcd)
+ * is closed by the application.
+ */
+static int vfd_close(struct inode *inode, struct file *file)
+{
+	struct sasem_context *context = NULL;
+	int retval = SUCCESS;
+
+	context = (struct sasem_context *) file->private_data;
+
+	if (!context) {
+		err("%s: no context for device", __func__);
+		return -ENODEV;
+	}
+
+	LOCK_CONTEXT;
+
+	if (!context->vfd_isopen) {
+		err("%s: VFD is not open", __func__);
+		retval = -EIO;
+	} else {
+		context->vfd_isopen = FALSE;
+		info("VFD port closed");
+		if (!context->dev_present && !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. */
+			UNLOCK_CONTEXT;
+			delete_context(context);
+			return retval;
+		}
+	}
+
+	UNLOCK_CONTEXT;
+	return retval;
+}
+
+/**
+ * Sends a packet to the VFD.
+ */
+static inline int send_packet(struct sasem_context *context)
+{
+	unsigned int pipe;
+	int interval = 0;
+	int retval = SUCCESS;
+
+	pipe = usb_sndintpipe(context->dev,
+			context->tx_endpoint->bEndpointAddress);
+	interval = context->tx_endpoint->bInterval;
+
+	usb_fill_int_urb(context->tx_urb, context->dev, pipe,
+		context->usb_tx_buf, sizeof(context->usb_tx_buf),
+		usb_tx_callback, context, interval);
+
+	context->tx_urb->actual_length = 0;
+
+	init_completion(&context->tx.finished);
+	atomic_set(&(context->tx.busy), 1);
+
+	retval =  usb_submit_urb(context->tx_urb, GFP_KERNEL);
+	if (retval != SUCCESS) {
+		atomic_set(&(context->tx.busy), 0);
+		err("%s: error submitting urb (%d)", __func__, retval);
+	} else {
+		/* Wait for tranmission to complete (or abort) */
+		UNLOCK_CONTEXT;
+		wait_for_completion(&context->tx.finished);
+		LOCK_CONTEXT;
+
+		retval = context->tx.status;
+		if (retval != SUCCESS)
+			err("%s: packet tx failed (%d)", __func__, retval);
+	}
+
+	return retval;
+}
+
+/**
+ * Writes data to the VFD.  The Sasem VFD is 2x16 characters
+ * and requires data in 9 consecutive USB interrupt packets,
+ * each packet carrying 8 bytes.
+ */
+static ssize_t vfd_write(struct file *file, const char *buf,
+				size_t n_bytes, loff_t *pos)
+{
+	int i;
+	int retval = SUCCESS;
+	struct sasem_context *context;
+
+	context = (struct sasem_context *) file->private_data;
+	if (!context) {
+		err("%s: no context for device", __func__);
+		return -ENODEV;
+	}
+
+	LOCK_CONTEXT;
+
+	if (!context->dev_present) {
+		err("%s: no Sasem device present", __func__);
+		retval = -ENODEV;
+		goto exit;
+	}
+
+	if (n_bytes <= 0 || n_bytes > 32) {
+		err("%s: invalid payload size", __func__);
+		retval = -EINVAL;
+		goto exit;
+	}
+
+	retval = copy_from_user(context->tx.data_buf, buf, n_bytes);
+	if (retval < 0)
+		goto exit;
+
+	/* Pad with spaces */
+	for (i = n_bytes; i < 32; ++i)
+		context->tx.data_buf[i] = ' ';
+
+	/* Nine 8 byte packets to be sent */
+	/* NOTE: "\x07\x01\0\0\0\0\0\0" or "\x0c\0\0\0\0\0\0\0"
+	 *       will clear the VFD */
+	for (i = 0; i < 9; i++) {
+		switch (i) {
+		case 0:
+			memcpy(context->usb_tx_buf, "\x07\0\0\0\0\0\0\0", 8);
+			context->usb_tx_buf[1] = (context->vfd_contrast) ?
+				(0x2B - (context->vfd_contrast - 1) / 250)
+				: 0x2B;
+			break;
+		case 1:
+			memcpy(context->usb_tx_buf, "\x09\x01\0\0\0\0\0\0", 8);
+			break;
+		case 2:
+			memcpy(context->usb_tx_buf, "\x0b\x01\0\0\0\0\0\0", 8);
+			break;
+		case 3:
+			memcpy(context->usb_tx_buf, context->tx.data_buf, 8);
+			break;
+		case 4:
+			memcpy(context->usb_tx_buf,
+			       context->tx.data_buf + 8, 8);
+			break;
+		case 5:
+			memcpy(context->usb_tx_buf, "\x09\x01\0\0\0\0\0\0", 8);
+			break;
+		case 6:
+			memcpy(context->usb_tx_buf, "\x0b\x02\0\0\0\0\0\0", 8);
+			break;
+		case 7:
+			memcpy(context->usb_tx_buf,
+			       context->tx.data_buf + 16, 8);
+			break;
+		case 8:
+			memcpy(context->usb_tx_buf,
+			       context->tx.data_buf + 24, 8);
+			break;
+		}
+		retval = send_packet(context);
+		if (retval != SUCCESS) {
+
+			err("%s: send packet failed for packet #%d",
+					__func__, i);
+			goto exit;
+		}
+	}
+exit:
+
+	UNLOCK_CONTEXT;
+
+	return (retval == SUCCESS) ? n_bytes : retval;
+}
+
+/**
+ * Callback function for USB core API: transmit data
+ */
+static void usb_tx_callback(struct urb *urb)
+{
+	struct sasem_context *context;
+
+	if (!urb)
+		return;
+	context = (struct sasem_context *) urb->context;
+	if (!context)
+		return;
+
+	context->tx.status = urb->status;
+
+	/* notify waiters that write has finished */
+	atomic_set(&context->tx.busy, 0);
+	complete(&context->tx.finished);
+
+	return;
+}
+
+/**
+ * Called by lirc_dev when the application opens /dev/lirc
+ */
+static int ir_open(void *data)
+{
+	int retval = SUCCESS;
+	struct sasem_context *context;
+
+	/* prevent races with disconnect */
+	down(&disconnect_sem);
+
+	context = (struct sasem_context *) data;
+
+	LOCK_CONTEXT;
+
+	if (context->ir_isopen) {
+		err("%s: IR port is already open", __func__);
+		retval = -EBUSY;
+		goto exit;
+	}
+
+	usb_fill_int_urb(context->rx_urb, context->dev,
+		usb_rcvintpipe(context->dev,
+				context->rx_endpoint->bEndpointAddress),
+		context->usb_rx_buf, sizeof(context->usb_rx_buf),
+		usb_rx_callback, context, context->rx_endpoint->bInterval);
+
+	retval = usb_submit_urb(context->rx_urb, GFP_KERNEL);
+
+	if (retval)
+		err("%s: usb_submit_urb failed for ir_open (%d)",
+		    __func__, retval);
+	else {
+		context->ir_isopen = TRUE;
+		info("IR port opened");
+	}
+
+exit:
+	UNLOCK_CONTEXT;
+
+	up(&disconnect_sem);
+	return SUCCESS;
+}
+
+/**
+ * Called by lirc_dev when the application closes /dev/lirc
+ */
+static void ir_close(void *data)
+{
+	struct sasem_context *context;
+
+	context = (struct sasem_context *)data;
+	if (!context) {
+		err("%s: no context for device", __func__);
+		return;
+	}
+
+	LOCK_CONTEXT;
+
+	usb_kill_urb(context->rx_urb);
+	context->ir_isopen = FALSE;
+	info("IR port closed");
+
+	if (!context->dev_present) {
+
+		/*
+		 * Device disconnected while IR port was
+		 * still open. Plugin was not deregistered
+		 * at disconnect time, so do it now.
+		 */
+		deregister_from_lirc(context);
+
+		if (!context->vfd_isopen) {
+
+			UNLOCK_CONTEXT;
+			delete_context(context);
+			return;
+		}
+		/* If VFD port is open, context will be deleted by vfd_close */
+	}
+
+	UNLOCK_CONTEXT;
+	return;
+}
+
+/**
+ * Process the incoming packet
+ */
+static inline void incoming_packet(struct sasem_context *context,
+				   struct urb *urb)
+{
+	int len = urb->actual_length;
+	unsigned char *buf = urb->transfer_buffer;
+	long ms;
+	struct timeval tv;
+
+	if (len != 8) {
+		warn("%s: invalid incoming packet size (%d)",
+		     __func__, len);
+		return;
+	}
+
+#ifdef DEBUG
+	int i;
+	for (i = 0; i < 8; ++i)
+		printk(KERN_INFO "%02x ", buf[i]);
+	printk(KERN_INFO "\n");
+#endif
+
+	/* Lirc could deal with the repeat code, but we really need to block it
+	 * if it arrives too late.  Otherwise we could repeat the wrong code. */
+
+	/* get the time since the last button press */
+	do_gettimeofday(&tv);
+	ms = (tv.tv_sec - context->presstime.tv_sec) * 1000 +
+	     (tv.tv_usec - context->presstime.tv_usec) / 1000;
+
+	if (memcmp(buf, "\x08\0\0\0\0\0\0\0", 8) == 0) {
+		/* the repeat code is being sent, so we copy
+		 * the old code to LIRC */
+
+		/* NOTE: Only if the last code was less than 250ms ago
+		 * - no one should be able to push another (undetected) button
+		 *   in that time and then get a false repeat of the previous
+		 *   press but it is long enough for a genuine repeat */
+		if ((ms < 250) && (context->codesaved != 0)) {
+			memcpy(buf, &context->lastcode, 8);
+			context->presstime.tv_sec = tv.tv_sec;
+			context->presstime.tv_usec = tv.tv_usec;
+		}
+	} else {
+		/* save the current valid code for repeats */
+		memcpy(&context->lastcode, buf, 8);
+		/* set flag to signal a valid code was save;
+		 * just for safety reasons */
+		context->codesaved = 1;
+		context->presstime.tv_sec = tv.tv_sec;
+		context->presstime.tv_usec = tv.tv_usec;
+	}
+
+	lirc_buffer_write_1(context->plugin->rbuf, buf);
+	wake_up(&context->plugin->rbuf->wait_poll);
+}
+
+/**
+ * Callback function for USB core API: receive data
+ */
+static void usb_rx_callback(struct urb *urb)
+{
+	struct sasem_context *context;
+
+	if (!urb)
+		return;
+	context = (struct sasem_context *) urb->context;
+	if (!context)
+		return;
+
+	switch (urb->status) {
+
+	case -ENOENT: 		/* usbcore unlink successful! */
+		return;
+
+	case SUCCESS:
+		if (context->ir_isopen)
+			incoming_packet(context, urb);
+		break;
+
+	default:
+		warn("%s: status (%d): ignored",
+			 __func__, urb->status);
+		break;
+	}
+
+	usb_submit_urb(context->rx_urb, GFP_ATOMIC);
+	return;
+}
+
+
+
+/**
+ * Callback function for USB core API: Probe
+ */
+static int sasem_probe(struct usb_interface *interface,
+			const struct usb_device_id *id)
+{
+	struct usb_device *dev = NULL;
+	struct usb_host_interface *iface_desc = NULL;
+	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_plugin *plugin = NULL;
+	struct lirc_buffer *rbuf = NULL;
+	int lirc_minor = 0;
+	int num_endpoints;
+	int retval = SUCCESS;
+	int vfd_ep_found;
+	int ir_ep_found;
+	int alloc_status;
+	struct sasem_context *context = NULL;
+	int i;
+
+	info("%s: found Sasem device", __func__);
+
+
+	dev = usb_get_dev(interface_to_usbdev(interface));
+	iface_desc = interface->cur_altsetting;
+	num_endpoints = iface_desc->desc.bNumEndpoints;
+
+	/*
+	 * Scan the endpoint list and set:
+	 * 	first input endpoint = IR endpoint
+	 * 	first output endpoint = VFD endpoint
+	 */
+
+	ir_ep_found = FALSE;
+	vfd_ep_found = FALSE;
+
+	for (i = 0; i < num_endpoints && !(ir_ep_found && vfd_ep_found); ++i) {
+
+		struct usb_endpoint_descriptor *ep;
+		int ep_dir;
+		int ep_type;
+		ep = &iface_desc->endpoint [i].desc;
+		ep_dir = ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK;
+		ep_type = ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
+
+		if (!ir_ep_found &&
+			ep_dir == USB_DIR_IN &&
+			ep_type == USB_ENDPOINT_XFER_INT) {
+
+			rx_endpoint = ep;
+			ir_ep_found = TRUE;
+			if (debug)
+				info("%s: found IR endpoint", __func__);
+
+		} else if (!vfd_ep_found &&
+			ep_dir == USB_DIR_OUT &&
+			ep_type == USB_ENDPOINT_XFER_INT) {
+
+			tx_endpoint = ep;
+			vfd_ep_found = TRUE;
+			if (debug)
+				info("%s: found VFD endpoint", __func__);
+		}
+	}
+
+	/* Input endpoint is mandatory */
+	if (!ir_ep_found) {
+
+		err("%s: no valid input (IR) endpoint found.", __func__);
+		retval = -ENODEV;
+		goto exit;
+	}
+
+	/* Warning if no VFD endpoint */
+	if (!vfd_ep_found)
+		info("%s: no valid output (VFD) endpoint found.", __func__);
+
+
+	/* Allocate memory */
+	alloc_status = SUCCESS;
+
+	context = kmalloc(sizeof(struct sasem_context), GFP_KERNEL);
+	if (!context) {
+		err("%s: kmalloc failed for context", __func__);
+		alloc_status = 1;
+		goto alloc_status_switch;
+	}
+	plugin = kmalloc(sizeof(struct lirc_plugin), GFP_KERNEL);
+	if (!plugin) {
+		err("%s: kmalloc failed for lirc_plugin", __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;
+	}
+	if (vfd_ep_found) {
+		tx_urb = usb_alloc_urb(0, GFP_KERNEL);
+		if (!tx_urb) {
+			err("%s: usb_alloc_urb failed for VFD urb",
+			    __func__);
+			alloc_status = 6;
+			goto alloc_status_switch;
+		}
+	}
+
+	/* clear all members of sasem_context and lirc_plugin */
+	memset(context, 0, sizeof(struct sasem_context));
+	mutex_init(&context->lock);
+
+	memset(plugin, 0, sizeof(struct lirc_plugin));
+
+	strcpy(plugin->name, MOD_NAME);
+	plugin->minor = -1;
+	plugin->code_length = 64;
+	plugin->sample_rate = 0;
+	plugin->features = LIRC_CAN_REC_LIRCCODE;
+	plugin->data = context;
+	plugin->rbuf = rbuf;
+	plugin->set_use_inc = ir_open;
+	plugin->set_use_dec = ir_close;
+	plugin->dev   = &dev->dev;
+	plugin->owner = THIS_MODULE;
+
+	LOCK_CONTEXT;
+
+	lirc_minor = lirc_register_plugin(plugin);
+	if (lirc_minor < 0) {
+		err("%s: lirc_register_plugin failed", __func__);
+		alloc_status = 7;
+		UNLOCK_CONTEXT;
+	} else
+		info("%s: Registered Sasem plugin (minor:%d)",
+			__func__, lirc_minor);
+
+alloc_status_switch:
+
+	switch (alloc_status) {
+
+	case 7:
+		if (vfd_ep_found)
+			usb_free_urb(tx_urb);
+	case 6:
+		usb_free_urb(rx_urb);
+	case 5:
+		lirc_buffer_free(rbuf);
+	case 4:
+		kfree(rbuf);
+	case 3:
+		kfree(plugin);
+	case 2:
+		kfree(context);
+		context = NULL;
+	case 1:
+		retval = -ENOMEM;
+		goto exit;
+	}
+
+	/* Needed while unregistering! */
+	plugin->minor = lirc_minor;
+
+	context->dev = dev;
+	context->dev_present = TRUE;
+	context->rx_endpoint = rx_endpoint;
+	context->rx_urb = rx_urb;
+	if (vfd_ep_found) {
+		context->tx_endpoint = tx_endpoint;
+		context->tx_urb = tx_urb;
+		context->vfd_contrast = 1000;   /* range 0 - 1000 */
+	}
+	context->plugin = plugin;
+
+	usb_set_intfdata(interface, context);
+
+	if (vfd_ep_found) {
+
+		if (debug)
+			info("Registering VFD with sysfs");
+		if (usb_register_dev(interface, &sasem_class))
+			/* Not a fatal error, so ignore */
+			info("%s: could not get a minor number for VFD",
+				__func__);
+	}
+
+	info("%s: Sasem device on usb<%d:%d> initialized",
+			__func__, dev->bus->busnum, dev->devnum);
+
+	UNLOCK_CONTEXT;
+exit:
+	return retval;
+}
+
+/**
+ * Callback function for USB core API: disonnect
+ */
+static void sasem_disconnect(struct usb_interface *interface)
+{
+	struct sasem_context *context;
+
+	/* prevent races with ir_open()/vfd_open() */
+	down(&disconnect_sem);
+
+	context = usb_get_intfdata(interface);
+	LOCK_CONTEXT;
+
+	info("%s: Sasem device disconnected", __func__);
+
+	usb_set_intfdata(interface, NULL);
+	context->dev_present = FALSE;
+
+	/* Stop reception */
+	usb_kill_urb(context->rx_urb);
+
+	/* Abort ongoing write */
+	if (atomic_read(&context->tx.busy)) {
+
+		usb_kill_urb(context->tx_urb);
+		wait_for_completion(&context->tx.finished);
+	}
+
+	/* De-register from lirc_dev if IR port is not open */
+	if (!context->ir_isopen)
+		deregister_from_lirc(context);
+
+	usb_deregister_dev(interface, &sasem_class);
+
+	UNLOCK_CONTEXT;
+
+	if (!context->ir_isopen && !context->vfd_isopen)
+		delete_context(context);
+
+	up(&disconnect_sem);
+}
+
+#ifdef MODULE
+static int __init sasem_init(void)
+{
+	int rc;
+
+	info(MOD_DESC ", v" MOD_VERSION);
+	info(MOD_AUTHOR);
+
+	rc = usb_register(&sasem_driver);
+	if (rc < 0) {
+		err("%s: usb register failed (%d)", __func__, rc);
+		return -ENODEV;
+	}
+	return SUCCESS;
+}
+
+static void __exit sasem_exit(void)
+{
+	usb_deregister(&sasem_driver);
+	info("module removed. Goodbye!");
+}
+
+
+module_init(sasem_init);
+module_exit(sasem_exit);
+
+#endif /* MODULE */
diff --git a/drivers/input/lirc/lirc_serial.c b/drivers/input/lirc/lirc_serial.c
new file mode 100644
index 0000000..465edd9
--- /dev/null
+++ b/drivers/input/lirc/lirc_serial.c
@@ -0,0 +1,1312 @@
+/****************************************************************************
+ ** lirc_serial.c ***********************************************************
+ ****************************************************************************
+ *
+ * lirc_serial - Device driver that records pulse- and pause-lengths
+ *	       (space-lengths) between DDCD event on a serial port.
+ *
+ * Copyright (C) 1996,97 Ralph Metzler <rjkm@thp.uni-koeln.de>
+ * Copyright (C) 1998 Trent Piepho <xyzzy@u.washington.edu>
+ * Copyright (C) 1998 Ben Pfaff <blp@gnu.org>
+ * Copyright (C) 1999 Christoph Bartelmus <lirc@bartelmus.de>
+ * Copyright (C) 2007 Andrei Tanas <andrei@tanas.ca> (suspend/resume support)
+ *  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
+ *
+ */
+
+/* Steve's changes to improve transmission fidelity:
+     - for systems with the rdtsc instruction and the clock counter, a
+       send_pule that times the pulses directly using the counter.
+       This means that the LIRC_SERIAL_TRANSMITTER_LATENCY fudge is
+       not needed. Measurement shows very stable waveform, even where
+       PCI activity slows the access to the UART, which trips up other
+       versions.
+     - For other system, non-integer-microsecond pulse/space lengths,
+       done using fixed point binary. So, much more accurate carrier
+       frequency.
+     - fine tuned transmitter latency, taking advantage of fractional
+       microseconds in previous change
+     - Fixed bug in the way transmitter latency was accounted for by
+       tuning the pulse lengths down - the send_pulse routine ignored
+       this overhead as it timed the overall pulse length - so the
+       pulse frequency was right but overall pulse length was too
+       long. Fixed by accounting for latency on each pulse/space
+       iteration.
+
+   Steve Davies <steve@daviesfam.org>  July 2001
+*/
+
+#include <linux/version.h>
+
+#include <linux/autoconf.h>
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/serial_reg.h>
+#include <linux/time.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+#include <linux/mm.h>
+#include <linux/delay.h>
+#include <linux/poll.h>
+#include <linux/platform_device.h>
+
+#include <asm/system.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/fcntl.h>
+
+#if defined(LIRC_SERIAL_NSLU2)
+#include <asm/hardware.h>
+/* From Intel IXP42X Developer's Manual (#252480-005): */
+/* ftp://download.intel.com/design/network/manuals/25248005.pdf */
+#define UART_IE_IXP42X_UUE   0x40 /* IXP42X UART Unit enable */
+#define UART_IE_IXP42X_RTOIE 0x10 /* IXP42X Receiver Data Timeout int.enable */
+#ifndef NSLU2_LED_GRN_GPIO
+/* added in 2.6.22 */
+#define NSLU2_LED_GRN_GPIO NSLU2_LED_GRN
+#endif
+#endif
+
+#include "lirc.h"
+#include "lirc_dev.h"
+
+#define LIRC_DRIVER_NAME "lirc_serial"
+
+struct lirc_serial {
+	int signal_pin;
+	int signal_pin_change;
+	int on;
+	int off;
+	long (*send_pulse)(unsigned long length);
+	void (*send_space)(long length);
+	int features;
+};
+
+#define LIRC_HOMEBREW	0
+#define LIRC_IRDEO	   1
+#define LIRC_IRDEO_REMOTE    2
+#define LIRC_ANIMAX	  3
+#define LIRC_IGOR	    4
+#define LIRC_NSLU2	   5
+
+#ifdef LIRC_SERIAL_IRDEO
+static int type = LIRC_IRDEO;
+#elif defined(LIRC_SERIAL_IRDEO_REMOTE)
+static int type = LIRC_IRDEO_REMOTE;
+#elif defined(LIRC_SERIAL_ANIMAX)
+static int type = LIRC_ANIMAX;
+#elif defined(LIRC_SERIAL_IGOR)
+static int type = LIRC_IGOR;
+#elif defined(LIRC_SERIAL_NSLU2)
+static int type = LIRC_NSLU2;
+#else
+static int type = LIRC_HOMEBREW;
+#endif
+
+/* Set defaults for NSLU2 */
+#if defined(LIRC_SERIAL_NSLU2)
+#ifndef LIRC_IRQ
+#define LIRC_IRQ IRQ_IXP4XX_UART2
+#endif
+#ifndef LIRC_PORT
+#define LIRC_PORT (IXP4XX_UART2_BASE_VIRT + REG_OFFSET)
+#endif
+#ifndef LIRC_IOMMAP
+#define LIRC_IOMMAP IXP4XX_UART2_BASE_PHYS
+#endif
+#ifndef LIRC_IOSHIFT
+#define LIRC_IOSHIFT 2
+#endif
+#ifndef LIRC_ALLOW_MMAPPED_IO
+#define LIRC_ALLOW_MMAPPED_IO
+#endif
+#endif
+
+#if defined(LIRC_ALLOW_MMAPPED_IO)
+#ifndef LIRC_IOMMAP
+#define LIRC_IOMMAP 0
+#endif
+#ifndef LIRC_IOSHIFT
+#define LIRC_IOSHIFT 0
+#endif
+static int iommap = LIRC_IOMMAP;
+static int ioshift = LIRC_IOSHIFT;
+#endif
+
+#ifdef LIRC_SERIAL_SOFTCARRIER
+static int softcarrier = 1;
+#else
+static int softcarrier;
+#endif
+
+static int share_irq;
+static int debug;
+
+#define dprintk(fmt, args...)					\
+	do {							\
+		if (debug)					\
+			printk(KERN_DEBUG LIRC_DRIVER_NAME ": "	\
+			       fmt, ## args);			\
+	} while (0)
+
+/* forward declarations */
+static long send_pulse_irdeo(unsigned long length);
+static long send_pulse_homebrew(unsigned long length);
+static void send_space_irdeo(long length);
+static void send_space_homebrew(long length);
+
+static struct lirc_serial hardware[] = {
+	/* home-brew receiver/transmitter */
+	{
+		UART_MSR_DCD,
+		UART_MSR_DDCD,
+		UART_MCR_RTS|UART_MCR_OUT2|UART_MCR_DTR,
+		UART_MCR_RTS|UART_MCR_OUT2,
+		send_pulse_homebrew,
+		send_space_homebrew,
+		(
+#ifdef LIRC_SERIAL_TRANSMITTER
+		 LIRC_CAN_SET_SEND_DUTY_CYCLE|
+		 LIRC_CAN_SET_SEND_CARRIER|
+		 LIRC_CAN_SEND_PULSE|
+#endif
+		 LIRC_CAN_REC_MODE2)
+	},
+
+	/* IRdeo classic */
+	{
+		UART_MSR_DSR,
+		UART_MSR_DDSR,
+		UART_MCR_OUT2,
+		UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2,
+		send_pulse_irdeo,
+		send_space_irdeo,
+		(LIRC_CAN_SET_SEND_DUTY_CYCLE|
+		 LIRC_CAN_SEND_PULSE|
+		 LIRC_CAN_REC_MODE2)
+	},
+
+	/* IRdeo remote */
+	{
+		UART_MSR_DSR,
+		UART_MSR_DDSR,
+		UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2,
+		UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2,
+		send_pulse_irdeo,
+		send_space_irdeo,
+		(LIRC_CAN_SET_SEND_DUTY_CYCLE|
+		 LIRC_CAN_SEND_PULSE|
+		 LIRC_CAN_REC_MODE2)
+	},
+
+	/* AnimaX */
+	{
+		UART_MSR_DCD,
+		UART_MSR_DDCD,
+		0,
+		UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2,
+		NULL,
+		NULL,
+		LIRC_CAN_REC_MODE2
+	},
+
+	/* home-brew receiver/transmitter (Igor Cesko's variation) */
+	{
+		UART_MSR_DSR,
+		UART_MSR_DDSR,
+		UART_MCR_RTS|UART_MCR_OUT2|UART_MCR_DTR,
+		UART_MCR_RTS|UART_MCR_OUT2,
+		send_pulse_homebrew,
+		send_space_homebrew,
+		(
+#ifdef LIRC_SERIAL_TRANSMITTER
+		 LIRC_CAN_SET_SEND_DUTY_CYCLE|
+		 LIRC_CAN_SET_SEND_CARRIER|
+		 LIRC_CAN_SEND_PULSE|
+#endif
+		 LIRC_CAN_REC_MODE2)
+	},
+
+#if defined(LIRC_SERIAL_NSLU2)
+	/* Modified Linksys Network Storage Link USB 2.0 (NSLU2):
+	   We receive on CTS of the 2nd serial port (R142,LHS), we
+	   transmit with a IR diode between GPIO[1] (green status LED),
+	   and ground (Matthias Goebl <matthias.goebl@goebl.net>).
+	   See also http://www.nslu2-linux.org for this device */
+	{
+		UART_MSR_CTS,
+		UART_MSR_DCTS,
+		UART_MCR_RTS|UART_MCR_OUT2|UART_MCR_DTR,
+		UART_MCR_RTS|UART_MCR_OUT2,
+		send_pulse_homebrew,
+		send_space_homebrew,
+		(
+#ifdef LIRC_SERIAL_TRANSMITTER
+		 LIRC_CAN_SET_SEND_DUTY_CYCLE|
+		 LIRC_CAN_SET_SEND_CARRIER|
+		 LIRC_CAN_SEND_PULSE|
+#endif
+		 LIRC_CAN_REC_MODE2)
+	},
+#endif
+
+};
+
+#define RS_ISR_PASS_LIMIT 256
+
+/* A long pulse code from a remote might take upto 300 bytes.  The
+   daemon should read the bytes as soon as they are generated, so take
+   the number of keys you think you can push before the daemon runs
+   and multiply by 300.  The driver will warn you if you overrun this
+   buffer.  If you have a slow computer or non-busmastering IDE disks,
+   maybe you will need to increase this.  */
+
+/* This MUST be a power of two!  It has to be larger than 1 as well. */
+
+#define RBUF_LEN 256
+#define WBUF_LEN 256
+
+static int sense = -1;	/* -1 = auto, 0 = active high, 1 = active low */
+static int txsense;     /* 0 = active high, 1 = active low */
+
+#ifndef LIRC_IRQ
+#define LIRC_IRQ 4
+#endif
+#ifndef LIRC_PORT
+#define LIRC_PORT 0x3f8
+#endif
+
+static int io = LIRC_PORT;
+static int irq = LIRC_IRQ;
+
+static struct timeval lasttv = {0, 0};
+
+static struct lirc_buffer rbuf;
+
+static int wbuf[WBUF_LEN];
+
+static unsigned int freq = 38000;
+static unsigned int duty_cycle = 50;
+
+/* Initialized in init_timing_params() */
+static unsigned long period;
+static unsigned long pulse_width;
+static unsigned long space_width;
+
+#if defined(__i386__)
+/*
+  From:
+  Linux I/O port programming mini-HOWTO
+  Author: Riku Saikkonen <Riku.Saikkonen@hut.fi>
+  v, 28 December 1997
+
+  [...]
+  Actually, a port I/O instruction on most ports in the 0-0x3ff range
+  takes almost exactly 1 microsecond, so if you're, for example, using
+  the parallel port directly, just do additional inb()s from that port
+  to delay.
+  [...]
+*/
+/* transmitter latency 1.5625us 0x1.90 - this figure arrived at from
+ * comment above plus trimming to match actual measured frequency.
+ * This will be sensitive to cpu speed, though hopefully most of the 1.5us
+ * is spent in the uart access.  Still - for reference test machine was a
+ * 1.13GHz Athlon system - Steve
+ */
+
+/* changed from 400 to 450 as this works better on slower machines;
+   faster machines will use the rdtsc code anyway */
+
+#define LIRC_SERIAL_TRANSMITTER_LATENCY 450
+
+#else
+
+/* does anybody have information on other platforms ? */
+/* 256 = 1<<8 */
+#define LIRC_SERIAL_TRANSMITTER_LATENCY 256
+
+#endif  /* __i386__ */
+
+static inline unsigned int sinp(int offset)
+{
+#if defined(LIRC_ALLOW_MMAPPED_IO)
+	if (iommap != 0) {
+		/* the register is memory-mapped */
+		offset <<= ioshift;
+		return readb(io + offset);
+	}
+#endif
+	return inb(io + offset);
+}
+
+static inline void soutp(int offset, int value)
+{
+#if defined(LIRC_ALLOW_MMAPPED_IO)
+	if (iommap != 0) {
+		/* the register is memory-mapped */
+		offset <<= ioshift;
+		writeb(value, io + offset);
+	}
+#endif
+	outb(value, io + offset);
+}
+
+static inline void on(void)
+{
+#if defined(LIRC_SERIAL_NSLU2)
+	/* On NSLU2, we put the transmit diode between the output of the green
+	   status LED and ground */
+	if (type == LIRC_NSLU2) {
+		gpio_line_set(NSLU2_LED_GRN_GPIO, IXP4XX_GPIO_LOW);
+		return;
+	}
+#endif
+	if (txsense)
+		soutp(UART_MCR, hardware[type].off);
+	else
+		soutp(UART_MCR, hardware[type].on);
+}
+
+static inline void off(void)
+{
+#if defined(LIRC_SERIAL_NSLU2)
+	if (type == LIRC_NSLU2) {
+		gpio_line_set(NSLU2_LED_GRN_GPIO, IXP4XX_GPIO_HIGH);
+		return;
+	}
+#endif
+	if (txsense)
+		soutp(UART_MCR, hardware[type].on);
+	else
+		soutp(UART_MCR, hardware[type].off);
+}
+
+#ifndef MAX_UDELAY_MS
+#define MAX_UDELAY_US 5000
+#else
+#define MAX_UDELAY_US (MAX_UDELAY_MS*1000)
+#endif
+
+static inline void safe_udelay(unsigned long usecs)
+{
+	while (usecs > MAX_UDELAY_US) {
+		udelay(MAX_UDELAY_US);
+		usecs -= MAX_UDELAY_US;
+	}
+	udelay(usecs);
+}
+
+#ifdef USE_RDTSC
+/* This is an overflow/precision juggle, complicated in that we can't
+   do long long divide in the kernel */
+
+/* When we use the rdtsc instruction to measure clocks, we keep the
+ * pulse and space widths as clock cycles.  As this is CPU speed
+ * dependent, the widths must be calculated in init_port and ioctl
+ * time
+ */
+
+/* So send_pulse can quickly convert microseconds to clocks */
+static unsigned long conv_us_to_clocks;
+
+static inline int init_timing_params(unsigned int new_duty_cycle,
+		unsigned int new_freq)
+{
+	unsigned long long loops_per_sec, work;
+
+	duty_cycle = new_duty_cycle;
+	freq = new_freq;
+
+	loops_per_sec = current_cpu_data.loops_per_jiffy;
+	loops_per_sec *= HZ;
+
+	/* How many clocks in a microsecond?, avoiding long long divide */
+	work = loops_per_sec;
+	work *= 4295;  /* 4295 = 2^32 / 1e6 */
+	conv_us_to_clocks = (work>>32);
+
+	/* Carrier period in clocks, approach good up to 32GHz clock,
+	   gets carrier frequency within 8Hz */
+	period = loops_per_sec>>3;
+	period /= (freq>>3);
+
+	/* Derive pulse and space from the period */
+
+	pulse_width = period*duty_cycle/100;
+	space_width = period - pulse_width;
+	dprintk("in init_timing_params, freq=%d, duty_cycle=%d, "
+		"clk/jiffy=%ld, pulse=%ld, space=%ld, "
+		"conv_us_to_clocks=%ld\n",
+		freq, duty_cycle, current_cpu_data.loops_per_jiffy,
+		pulse_width, space_width, conv_us_to_clocks);
+	return 0;
+}
+#else /* ! USE_RDTSC */
+static inline int init_timing_params(unsigned int new_duty_cycle,
+		unsigned int new_freq)
+{
+/* period, pulse/space width are kept with 8 binary places -
+ * IE multiplied by 256. */
+	if (256*1000000L/new_freq*new_duty_cycle/100 <=
+	    LIRC_SERIAL_TRANSMITTER_LATENCY)
+		return -EINVAL;
+	if (256*1000000L/new_freq*(100-new_duty_cycle)/100 <=
+	    LIRC_SERIAL_TRANSMITTER_LATENCY)
+		return -EINVAL;
+	duty_cycle = new_duty_cycle;
+	freq = new_freq;
+	period = 256*1000000L/freq;
+	pulse_width = period*duty_cycle/100;
+	space_width = period-pulse_width;
+	dprintk("in init_timing_params, freq=%d pulse=%ld, "
+		"space=%ld\n", freq, pulse_width, space_width);
+	return 0;
+}
+#endif /* USE_RDTSC */
+
+
+/* return value: space length delta */
+
+static long send_pulse_irdeo(unsigned long length)
+{
+	long rawbits;
+	int i;
+	unsigned char output;
+	unsigned char chunk, shifted;
+
+	/* how many bits have to be sent ? */
+	rawbits = length*1152/10000;
+	if (duty_cycle > 50)
+		chunk = 3;
+	else
+		chunk = 1;
+	for (i = 0, output = 0x7f; rawbits > 0; rawbits -= 3) {
+		shifted = chunk<<(i*3);
+		shifted >>= 1;
+		output &= (~shifted);
+		i++;
+		if (i == 3) {
+			soutp(UART_TX, output);
+			while (!(sinp(UART_LSR) & UART_LSR_THRE))
+				;
+			output = 0x7f;
+			i = 0;
+		}
+	}
+	if (i != 0) {
+		soutp(UART_TX, output);
+		while (!(sinp(UART_LSR) & UART_LSR_TEMT))
+			;
+	}
+
+	if (i == 0)
+		return (-rawbits)*10000/1152;
+	else
+		return (3-i)*3*10000/1152 + (-rawbits)*10000/1152;
+}
+
+#ifdef USE_RDTSC
+/* Version that uses Pentium rdtsc instruction to measure clocks */
+
+/* This version does sub-microsecond timing using rdtsc instruction,
+ * and does away with the fudged LIRC_SERIAL_TRANSMITTER_LATENCY
+ * Implicitly i586 architecture...  - Steve
+ */
+
+static inline long send_pulse_homebrew_softcarrier(unsigned long length)
+{
+	int flag;
+	unsigned long target, start, now;
+
+	/* Get going quick as we can */
+	rdtscl(start); on();
+	/* Convert length from microseconds to clocks */
+	length *= conv_us_to_clocks;
+	/* And loop till time is up - flipping at right intervals */
+	now = start;
+	target = pulse_width;
+	flag = 1;
+	while ((now-start) < length) {
+		/* Delay till flip time */
+		do {
+			rdtscl(now);
+		} while ((now-start) < target);
+
+		/* flip */
+		if (flag) {
+			rdtscl(now); off();
+			target += space_width;
+		} else {
+			rdtscl(now); on();
+			target += pulse_width;
+		}
+		flag = !flag;
+	}
+	rdtscl(now);
+	return ((now-start)-length) / conv_us_to_clocks;
+}
+#else /* ! USE_RDTSC */
+/* Version using udelay() */
+
+/* here we use fixed point arithmetic, with 8
+   fractional bits.  that gets us within 0.1% or so of the right average
+   frequency, albeit with some jitter in pulse length - Steve */
+
+/* To match 8 fractional bits used for pulse/space length */
+
+static inline long send_pulse_homebrew_softcarrier(unsigned long length)
+{
+	int flag;
+	unsigned long actual, target, d;
+	length <<= 8;
+
+	actual = 0; target = 0; flag = 0;
+	while (actual < length) {
+		if (flag) {
+			off();
+			target += space_width;
+		} else {
+			on();
+			target += pulse_width;
+		}
+		d = (target-actual-LIRC_SERIAL_TRANSMITTER_LATENCY+128)>>8;
+		/* Note - we've checked in ioctl that the pulse/space
+		   widths are big enough so that d is > 0 */
+		udelay(d);
+		actual += (d<<8)+LIRC_SERIAL_TRANSMITTER_LATENCY;
+		flag = !flag;
+	}
+	return (actual-length)>>8;
+}
+#endif /* USE_RDTSC */
+
+static long send_pulse_homebrew(unsigned long length)
+{
+	if (length <= 0)
+		return 0;
+
+	if (softcarrier)
+		return send_pulse_homebrew_softcarrier(length);
+	else {
+		on();
+		safe_udelay(length);
+		return 0;
+	}
+}
+
+static void send_space_irdeo(long length)
+{
+	if (length <= 0)
+		return;
+
+	safe_udelay(length);
+}
+
+static void send_space_homebrew(long length)
+{
+	off();
+	if (length <= 0)
+		return;
+	safe_udelay(length);
+}
+
+static inline void rbwrite(int l)
+{
+	if (lirc_buffer_full(&rbuf)) {
+		/* no new signals will be accepted */
+		dprintk("Buffer overrun\n");
+		return;
+	}
+	_lirc_buffer_write_1(&rbuf, (void *)&l);
+}
+
+static inline void frbwrite(int l)
+{
+	/* simple noise filter */
+	static int pulse, space;
+	static unsigned int ptr;
+
+	if (ptr > 0 && (l & PULSE_BIT)) {
+		pulse += l & PULSE_MASK;
+		if (pulse > 250) {
+			rbwrite(space);
+			rbwrite(pulse | PULSE_BIT);
+			ptr = 0;
+			pulse = 0;
+		}
+		return;
+	}
+	if (!(l & PULSE_BIT)) {
+		if (ptr == 0) {
+			if (l > 20000) {
+				space = l;
+				ptr++;
+				return;
+			}
+		} else {
+			if (l > 20000) {
+				space += pulse;
+				if (space > PULSE_MASK)
+					space = PULSE_MASK;
+				space += l;
+				if (space > PULSE_MASK)
+					space = PULSE_MASK;
+				pulse = 0;
+				return;
+			}
+			rbwrite(space);
+			rbwrite(pulse | PULSE_BIT);
+			ptr = 0;
+			pulse = 0;
+		}
+	}
+	rbwrite(l);
+}
+
+static irqreturn_t irq_handler(int i, void *blah)
+{
+	struct timeval tv;
+	int status, counter, dcd;
+	long deltv;
+	int data;
+	static int last_dcd = -1;
+
+	if ((sinp(UART_IIR) & UART_IIR_NO_INT)) {
+		/* not our interrupt */
+		return IRQ_RETVAL(IRQ_NONE);
+	}
+
+	counter = 0;
+	do {
+		counter++;
+		status = sinp(UART_MSR);
+		if (counter > RS_ISR_PASS_LIMIT) {
+			printk(KERN_WARNING LIRC_DRIVER_NAME ": AIEEEE: "
+			       "We're caught!\n");
+			break;
+		}
+		if ((status&hardware[type].signal_pin_change) && sense != -1) {
+			/* get current time */
+			do_gettimeofday(&tv);
+
+			/* New mode, written by Trent Piepho
+			   <xyzzy@u.washington.edu>. */
+
+			/* The old format was not very portable.
+			   We now use an int to pass pulses
+			   and spaces to user space.
+
+			   If PULSE_BIT is set a pulse has been
+			   received, otherwise a space has been
+			   received.  The driver needs to know if your
+			   receiver is active high or active low, or
+			   the space/pulse sense could be
+			   inverted. The bits denoted by PULSE_MASK are
+			   the length in microseconds. Lengths greater
+			   than or equal to 16 seconds are clamped to
+			   PULSE_MASK.  All other bits are unused.
+			   This is a much simpler interface for user
+			   programs, as well as eliminating "out of
+			   phase" errors with space/pulse
+			   autodetection. */
+
+			/* calculate time since last interrupt in
+			   microseconds */
+			dcd = (status & hardware[type].signal_pin) ? 1 : 0;
+
+			if (dcd == last_dcd) {
+				printk(KERN_WARNING LIRC_DRIVER_NAME
+				": ignoring spike: %d %d %lx %lx %lx %lx\n",
+				dcd, sense,
+				tv.tv_sec, lasttv.tv_sec,
+				tv.tv_usec, lasttv.tv_usec);
+				continue;
+			}
+
+			deltv = tv.tv_sec-lasttv.tv_sec;
+			if (tv.tv_sec < lasttv.tv_sec ||
+			    (tv.tv_sec == lasttv.tv_sec &&
+			     tv.tv_usec < lasttv.tv_usec)) {
+				printk(KERN_WARNING LIRC_DRIVER_NAME
+				       ": AIEEEE: your clock just jumped "
+				       "backwards\n");
+				printk(KERN_WARNING LIRC_DRIVER_NAME
+				       ": %d %d %lx %lx %lx %lx\n",
+				       dcd, sense,
+				       tv.tv_sec, lasttv.tv_sec,
+				       tv.tv_usec, lasttv.tv_usec);
+				data = PULSE_MASK;
+			} else if (deltv > 15) {
+				data = PULSE_MASK; /* really long time */
+				if (!(dcd^sense)) {
+					/* sanity check */
+					printk(KERN_WARNING LIRC_DRIVER_NAME
+					       ": AIEEEE: "
+					       "%d %d %lx %lx %lx %lx\n",
+					       dcd, sense,
+					       tv.tv_sec, lasttv.tv_sec,
+					       tv.tv_usec, lasttv.tv_usec);
+					/* detecting pulse while this
+					   MUST be a space! */
+					sense = sense ? 0 : 1;
+				}
+			} else
+				data = (int) (deltv*1000000 +
+					       tv.tv_usec -
+					       lasttv.tv_usec);
+			frbwrite(dcd^sense ? data : (data|PULSE_BIT));
+			lasttv = tv;
+			last_dcd = dcd;
+			wake_up_interruptible(&rbuf.wait_poll);
+		}
+	} while (!(sinp(UART_IIR) & UART_IIR_NO_INT)); /* still pending ? */
+	return IRQ_RETVAL(IRQ_HANDLED);
+}
+
+static void hardware_init_port(void)
+{
+	unsigned long flags;
+	local_irq_save(flags);
+
+	/* Set DLAB 0. */
+	soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
+
+	/* First of all, disable all interrupts */
+	soutp(UART_IER, sinp(UART_IER) &
+	      (~(UART_IER_MSI|UART_IER_RLSI|UART_IER_THRI|UART_IER_RDI)));
+
+	/* Clear registers. */
+	sinp(UART_LSR);
+	sinp(UART_RX);
+	sinp(UART_IIR);
+	sinp(UART_MSR);
+
+#if defined(LIRC_SERIAL_NSLU2)
+	if (type == LIRC_NSLU2) {
+		/* Setup NSLU2 UART */
+
+		/* Enable UART */
+		soutp(UART_IER, sinp(UART_IER) | UART_IE_IXP42X_UUE);
+		/* Disable Receiver data Time out interrupt */
+		soutp(UART_IER, sinp(UART_IER) & ~UART_IE_IXP42X_RTOIE);
+		/* set out2 = interupt unmask; off() doesn't set MCR
+		   on NSLU2 */
+		soutp(UART_MCR, UART_MCR_RTS|UART_MCR_OUT2);
+	}
+#endif
+
+	/* Set line for power source */
+	off();
+
+	/* Clear registers again to be sure. */
+	sinp(UART_LSR);
+	sinp(UART_RX);
+	sinp(UART_IIR);
+	sinp(UART_MSR);
+
+	switch (type) {
+	case LIRC_IRDEO:
+	case LIRC_IRDEO_REMOTE:
+		/* setup port to 7N1 @ 115200 Baud */
+		/* 7N1+start = 9 bits at 115200 ~ 3 bits at 38kHz */
+
+		/* Set DLAB 1. */
+		soutp(UART_LCR, sinp(UART_LCR) | UART_LCR_DLAB);
+		/* Set divisor to 1 => 115200 Baud */
+		soutp(UART_DLM, 0);
+		soutp(UART_DLL, 1);
+		/* Set DLAB 0 +  7N1 */
+		soutp(UART_LCR, UART_LCR_WLEN7);
+		/* THR interrupt already disabled at this point */
+		break;
+	default:
+		break;
+	}
+
+	local_irq_restore(flags);
+}
+
+static int init_port(void)
+{
+	int i, nlow, nhigh;
+
+	/* Reserve io region. */
+#if defined(LIRC_ALLOW_MMAPPED_IO)
+	/* Future MMAP-Developers: Attention!
+	   For memory mapped I/O you *might* need to use ioremap() first,
+	   for the NSLU2 it's done in boot code. */
+	if (((iommap != 0)
+	     && (request_mem_region(iommap, 8<<ioshift,
+				    LIRC_DRIVER_NAME) == NULL))
+	   || ((iommap == 0)
+	       && (request_region(io, 8, LIRC_DRIVER_NAME) == NULL))) {
+#else
+	if (request_region(io, 8, LIRC_DRIVER_NAME) == NULL) {
+#endif
+		printk(KERN_ERR  LIRC_DRIVER_NAME
+		       ": port %04x already in use\n", io);
+		printk(KERN_WARNING LIRC_DRIVER_NAME
+		       ": use 'setserial /dev/ttySX uart none'\n");
+		printk(KERN_WARNING LIRC_DRIVER_NAME
+		       ": or compile the serial port driver as module and\n");
+		printk(KERN_WARNING LIRC_DRIVER_NAME
+		       ": make sure this module is loaded first\n");
+		return -EBUSY;
+	}
+
+	hardware_init_port();
+
+	/* Initialize pulse/space widths */
+	init_timing_params(duty_cycle, freq);
+
+	/* If pin is high, then this must be an active low receiver. */
+	if (sense == -1) {
+		/* wait 1/2 sec for the power supply */
+
+		set_current_state(TASK_INTERRUPTIBLE);
+		schedule_timeout(HZ/2);
+
+		/* probe 9 times every 0.04s, collect "votes" for
+		   active high/low */
+		nlow = 0;
+		nhigh = 0;
+		for (i = 0; i < 9; i++) {
+			if (sinp(UART_MSR) & hardware[type].signal_pin)
+				nlow++;
+			else
+				nhigh++;
+			schedule_timeout(HZ/25);
+		}
+		sense = (nlow >= nhigh ? 1 : 0);
+		printk(KERN_INFO  LIRC_DRIVER_NAME  ": auto-detected active "
+		       "%s receiver\n", sense ? "low" : "high");
+	} else
+		printk(KERN_INFO  LIRC_DRIVER_NAME  ": Manually using active "
+		       "%s receiver\n", sense ? "low" : "high");
+
+	return 0;
+}
+
+static int set_use_inc(void *data)
+{
+	int result;
+	unsigned long flags;
+
+	/* Init read buffer. */
+	if (lirc_buffer_init(&rbuf, sizeof(int), RBUF_LEN) < 0)
+		return -ENOMEM;
+
+	/* initialize timestamp */
+	do_gettimeofday(&lasttv);
+
+	result = request_irq(irq, irq_handler,
+			   IRQF_DISABLED | (share_irq ? IRQF_SHARED : 0),
+			   LIRC_DRIVER_NAME, (void *)&hardware);
+
+	switch (result) {
+	case -EBUSY:
+		printk(KERN_ERR LIRC_DRIVER_NAME ": IRQ %d busy\n", irq);
+		lirc_buffer_free(&rbuf);
+		return -EBUSY;
+	case -EINVAL:
+		printk(KERN_ERR LIRC_DRIVER_NAME
+		       ": Bad irq number or handler\n");
+		lirc_buffer_free(&rbuf);
+		return -EINVAL;
+	default:
+		dprintk("Interrupt %d, port %04x obtained\n", irq, io);
+		break;
+	};
+
+	local_irq_save(flags);
+
+	/* Set DLAB 0. */
+	soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
+
+	soutp(UART_IER, sinp(UART_IER)|UART_IER_MSI);
+
+	local_irq_restore(flags);
+
+	return 0;
+}
+
+static void set_use_dec(void *data)
+{	unsigned long flags;
+
+	local_irq_save(flags);
+
+	/* Set DLAB 0. */
+	soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
+
+	/* First of all, disable all interrupts */
+	soutp(UART_IER, sinp(UART_IER) &
+	      (~(UART_IER_MSI|UART_IER_RLSI|UART_IER_THRI|UART_IER_RDI)));
+	local_irq_restore(flags);
+
+	free_irq(irq, (void *)&hardware);
+
+	dprintk("freed IRQ %d\n", irq);
+	lirc_buffer_free(&rbuf);
+}
+
+static ssize_t lirc_write(struct file *file, const char *buf,
+			 size_t n, loff_t *ppos)
+{
+	int i, count;
+	unsigned long flags;
+	long delta = 0;
+
+	if (!(hardware[type].features&LIRC_CAN_SEND_PULSE))
+		return -EBADF;
+
+	if (n % sizeof(int))
+		return -EINVAL;
+	count = n / sizeof(int);
+	if (count > WBUF_LEN || count % 2 == 0)
+		return -EINVAL;
+	if (copy_from_user(wbuf, buf, n))
+		return -EFAULT;
+	local_irq_save(flags);
+	if (type == LIRC_IRDEO) {
+		/* DTR, RTS down */
+		on();
+	}
+	for (i = 0; i < count; i++) {
+		if (i%2)
+			hardware[type].send_space(wbuf[i]-delta);
+		else
+			delta = hardware[type].send_pulse(wbuf[i]);
+	}
+	off();
+	local_irq_restore(flags);
+	return n;
+}
+
+static int lirc_ioctl(struct inode *node, struct file *filep, unsigned int cmd,
+		      unsigned long arg)
+{
+	int result;
+	unsigned long value;
+	unsigned int ivalue;
+
+	switch (cmd) {
+	case LIRC_GET_SEND_MODE:
+		if (!(hardware[type].features&LIRC_CAN_SEND_MASK))
+			return -ENOIOCTLCMD;
+
+		result = put_user(LIRC_SEND2MODE
+				  (hardware[type].features&LIRC_CAN_SEND_MASK),
+				  (unsigned long *) arg);
+		if (result)
+			return result;
+		break;
+
+	case LIRC_SET_SEND_MODE:
+		if (!(hardware[type].features&LIRC_CAN_SEND_MASK))
+			return -ENOIOCTLCMD;
+
+		result = get_user(value, (unsigned long *) arg);
+		if (result)
+			return result;
+		/* only LIRC_MODE_PULSE supported */
+		if (value != LIRC_MODE_PULSE)
+			return -ENOSYS;
+		break;
+
+	case LIRC_GET_LENGTH:
+		return -ENOSYS;
+		break;
+
+	case LIRC_SET_SEND_DUTY_CYCLE:
+		dprintk("SET_SEND_DUTY_CYCLE\n");
+		if (!(hardware[type].features&LIRC_CAN_SET_SEND_DUTY_CYCLE))
+			return -ENOIOCTLCMD;
+
+		result = get_user(ivalue, (unsigned int *) arg);
+		if (result)
+			return result;
+		if (ivalue <= 0 || ivalue > 100)
+			return -EINVAL;
+		return init_timing_params(ivalue, freq);
+		break;
+
+	case LIRC_SET_SEND_CARRIER:
+		dprintk("SET_SEND_CARRIER\n");
+		if (!(hardware[type].features&LIRC_CAN_SET_SEND_CARRIER))
+			return -ENOIOCTLCMD;
+
+		result = get_user(ivalue, (unsigned int *) arg);
+		if (result)
+			return result;
+		if (ivalue > 500000 || ivalue < 20000)
+			return -EINVAL;
+		return init_timing_params(duty_cycle, ivalue);
+		break;
+
+	default:
+		return -ENOIOCTLCMD;
+	}
+	return 0;
+}
+
+static struct file_operations lirc_fops = {
+	.write	= lirc_write,
+};
+
+static struct lirc_plugin plugin = {
+	.name		= LIRC_DRIVER_NAME,
+	.minor		= -1,
+	.code_length	= 1,
+	.sample_rate	= 0,
+	.data		= NULL,
+	.add_to_buf	= NULL,
+	.get_queue	= NULL,
+	.rbuf		= &rbuf,
+	.set_use_inc	= set_use_inc,
+	.set_use_dec	= set_use_dec,
+	.ioctl		= lirc_ioctl,
+	.fops		= &lirc_fops,
+	.dev		= NULL,
+	.owner		= THIS_MODULE,
+};
+
+#ifdef MODULE
+
+static struct platform_device *lirc_serial_dev;
+
+static int __devinit lirc_serial_probe(struct platform_device *dev)
+{
+	return 0;
+}
+
+static int __devexit lirc_serial_remove(struct platform_device *dev)
+{
+	return 0;
+}
+
+static int lirc_serial_suspend(struct platform_device *dev,
+			       pm_message_t state)
+{
+	/* Set DLAB 0. */
+	soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
+
+	/* Disable all interrupts */
+	soutp(UART_IER, sinp(UART_IER) &
+	      (~(UART_IER_MSI|UART_IER_RLSI|UART_IER_THRI|UART_IER_RDI)));
+
+	/* Clear registers. */
+	sinp(UART_LSR);
+	sinp(UART_RX);
+	sinp(UART_IIR);
+	sinp(UART_MSR);
+
+	return 0;
+}
+
+static int lirc_serial_resume(struct platform_device *dev)
+{
+	unsigned long flags;
+
+	hardware_init_port();
+
+	local_irq_save(flags);
+	/* Enable Interrupt */
+	do_gettimeofday(&lasttv);
+	soutp(UART_IER, sinp(UART_IER)|UART_IER_MSI);
+	off();
+
+	lirc_buffer_clear(&rbuf);
+
+	local_irq_restore(flags);
+
+	return 0;
+}
+
+static struct platform_driver lirc_serial_driver = {
+	.probe		= lirc_serial_probe,
+	.remove		= __devexit_p(lirc_serial_remove),
+	.suspend	= lirc_serial_suspend,
+	.resume		= lirc_serial_resume,
+	.driver		= {
+		.name	= "lirc_serial",
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init lirc_serial_init(void)
+{
+	int result;
+
+	result = platform_driver_register(&lirc_serial_driver);
+	if (result) {
+		printk("lirc register returned %d\n", result);
+		return result;
+	}
+
+	lirc_serial_dev = platform_device_alloc("lirc_serial", 0);
+	if (!lirc_serial_dev) {
+		result = -ENOMEM;
+		goto exit_driver_unregister;
+	}
+
+	result = platform_device_add(lirc_serial_dev);
+	if (result)
+		goto exit_device_put;
+
+	return 0;
+
+exit_device_put:
+	platform_device_put(lirc_serial_dev);
+exit_driver_unregister:
+	platform_driver_unregister(&lirc_serial_driver);
+	return result;
+}
+
+static void __exit lirc_serial_exit(void)
+{
+	platform_device_unregister(lirc_serial_dev);
+	platform_driver_unregister(&lirc_serial_driver);
+}
+
+int __init init_module(void)
+{
+	int result;
+
+	result = lirc_serial_init();
+	if (result)
+		return result;
+	switch (type) {
+	case LIRC_HOMEBREW:
+	case LIRC_IRDEO:
+	case LIRC_IRDEO_REMOTE:
+	case LIRC_ANIMAX:
+	case LIRC_IGOR:
+#if defined(LIRC_SERIAL_NSLU2)
+	case LIRC_NSLU2:
+#endif
+		break;
+	default:
+		result = -EINVAL;
+		goto exit_serial_exit;
+	}
+	if (!softcarrier) {
+		switch (type) {
+		case LIRC_HOMEBREW:
+		case LIRC_IGOR:
+		case LIRC_NSLU2:
+			hardware[type].features &=
+				~(LIRC_CAN_SET_SEND_DUTY_CYCLE|
+				  LIRC_CAN_SET_SEND_CARRIER);
+			break;
+		}
+	}
+	result = init_port();
+	if (result < 0)
+		goto exit_serial_exit;
+	plugin.features = hardware[type].features;
+	plugin.minor = lirc_register_plugin(&plugin);
+	if (plugin.minor < 0) {
+		printk(KERN_ERR  LIRC_DRIVER_NAME
+		       ": register_chrdev failed!\n");
+		result = -EIO;
+		goto exit_release;
+	}
+	return 0;
+exit_release:
+	release_region(io, 8);
+exit_serial_exit:
+	lirc_serial_exit();
+	return result;
+}
+
+void __exit cleanup_module(void)
+{
+	lirc_serial_exit();
+#if defined(LIRC_ALLOW_MMAPPED_IO)
+	if (iommap != 0)
+		release_mem_region(iommap, 8<<ioshift);
+	else
+		release_region(io, 8);
+#else
+	release_region(io, 8);
+#endif
+	lirc_unregister_plugin(plugin.minor);
+	dprintk("cleaned up module\n");
+}
+
+MODULE_DESCRIPTION("Infra-red receiver driver for serial ports.");
+MODULE_AUTHOR("Ralph Metzler, Trent Piepho, Ben Pfaff, "
+	      "Christoph Bartelmus, Andrei Tanas");
+MODULE_LICENSE("GPL");
+
+module_param(type, int, 0444);
+#if defined(LIRC_SERIAL_NSLU2)
+MODULE_PARM_DESC(type, "Hardware type (0 = home-brew, 1 = IRdeo,"
+		 " 2 = IRdeo Remote, 3 = AnimaX, 4 = IgorPlug,"
+		 " 5 = NSLU2 RX:CTS2/TX:GreenLED)");
+#else
+MODULE_PARM_DESC(type, "Hardware type (0 = home-brew, 1 = IRdeo,"
+		 " 2 = IRdeo Remote, 3 = AnimaX, 4 = IgorPlug)");
+#endif
+
+module_param(io, int, 0444);
+MODULE_PARM_DESC(io, "I/O address base (0x3f8 or 0x2f8)");
+
+#if defined(LIRC_ALLOW_MMAPPED_IO)
+/* some architectures (e.g. intel xscale) have memory mapped registers */
+module_param(iommap, bool, 0444);
+MODULE_PARM_DESC(iommap, "physical base for memory mapped I/O"
+		" (0 = no memory mapped io)");
+
+/* some architectures (e.g. intel xscale) align the 8bit serial registers
+   on 32bit word boundaries.
+   See linux-kernel/serial/8250.c serial_in()/out() */
+module_param(ioshift, int, 0444);
+MODULE_PARM_DESC(ioshift, "shift I/O register offset (0 = no shift)");
+#endif
+
+module_param(irq, int, 0444);
+MODULE_PARM_DESC(irq, "Interrupt (4 or 3)");
+
+module_param(share_irq, bool, 0444);
+MODULE_PARM_DESC(share_irq, "Share interrupts (0 = off, 1 = on)");
+
+module_param(sense, bool, 0444);
+MODULE_PARM_DESC(sense, "Override autodetection of IR receiver circuit"
+		 " (0 = active high, 1 = active low )");
+
+#ifdef LIRC_SERIAL_TRANSMITTER
+module_param(txsense, bool, 0444);
+MODULE_PARM_DESC(txsense, "Sense of transmitter circuit"
+		 " (0 = active high, 1 = active low )");
+#endif
+
+module_param(softcarrier, bool, 0444);
+MODULE_PARM_DESC(softcarrier, "Software carrier (0 = off, 1 = on)");
+
+module_param(debug, bool, 0644);
+MODULE_PARM_DESC(debug, "Enable debugging messages");
+
+#endif /* MODULE */
diff --git a/drivers/input/lirc/lirc_sir.c b/drivers/input/lirc/lirc_sir.c
new file mode 100644
index 0000000..ea192b2
--- /dev/null
+++ b/drivers/input/lirc/lirc_sir.c
@@ -0,0 +1,1302 @@
+/*
+ * LIRC SIR driver, (C) 2000 Milan Pikula <www@fornax.sk>
+ *
+ * lirc_sir - Device driver for use with SIR (serial infra red)
+ * mode of IrDA on many notebooks.
+ *
+ *  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
+ *
+ *
+ * 2000/09/16 Frank Przybylski <mail@frankprzybylski.de> :
+ *  added timeout and relaxed pulse detection, removed gap bug
+ *
+ * 2000/12/15 Christoph Bartelmus <lirc@bartelmus.de> :
+ *   added support for Tekram Irmate 210 (sending does not work yet,
+ *   kind of disappointing that nobody was able to implement that
+ *   before),
+ *   major clean-up
+ *
+ * 2001/02/27 Christoph Bartelmus <lirc@bartelmus.de> :
+ *   added support for StrongARM SA1100 embedded microprocessor
+ *   parts cut'n'pasted from sa1100_ir.c (C) 2000 Russell King
+ */
+
+
+#include <linux/version.h>
+#include <linux/module.h>
+
+#include <linux/autoconf.h>
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/serial_reg.h>
+#include <linux/time.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+#include <linux/mm.h>
+#include <linux/delay.h>
+#include <linux/poll.h>
+#include <asm/system.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+#include <asm/irq.h>
+#include <linux/fcntl.h>
+#ifdef LIRC_ON_SA1100
+#include <asm/hardware.h>
+#ifdef CONFIG_SA1100_COLLIE
+#include <asm/arch/tc35143.h>
+#include <asm/ucb1200.h>
+#endif
+#endif
+
+#include <linux/timer.h>
+
+#include "lirc.h"
+#include "lirc_dev.h"
+
+/* SECTION: Definitions */
+
+/**************************** Tekram dongle ***************************/
+#ifdef LIRC_SIR_TEKRAM
+/* stolen from kernel source */
+/* definitions for Tekram dongle */
+#define TEKRAM_115200 0x00
+#define TEKRAM_57600  0x01
+#define TEKRAM_38400  0x02
+#define TEKRAM_19200  0x03
+#define TEKRAM_9600   0x04
+#define TEKRAM_2400   0x08
+
+#define TEKRAM_PW 0x10 /* Pulse select bit */
+
+/* 10bit * 1s/115200bit in miliseconds = 87ms*/
+#define TIME_CONST (10000000ul/115200ul)
+
+#endif
+
+#ifdef LIRC_SIR_ACTISYS_ACT200L
+static void init_act200(void);
+#elif defined(LIRC_SIR_ACTISYS_ACT220L)
+static void init_act220(void);
+#endif
+
+/******************************* SA1100 ********************************/
+#ifdef LIRC_ON_SA1100
+struct sa1100_ser2_registers {
+	/* HSSP control register */
+	unsigned char hscr0;
+	/* UART registers */
+	unsigned char utcr0;
+	unsigned char utcr1;
+	unsigned char utcr2;
+	unsigned char utcr3;
+	unsigned char utcr4;
+	unsigned char utdr;
+	unsigned char utsr0;
+	unsigned char utsr1;
+} sr;
+
+static int irq = IRQ_Ser2ICP;
+
+#define LIRC_ON_SA1100_TRANSMITTER_LATENCY 0
+
+/* pulse/space ratio of 50/50 */
+static unsigned long pulse_width = (13-LIRC_ON_SA1100_TRANSMITTER_LATENCY);
+/* 1000000/freq-pulse_width */
+static unsigned long space_width = (13-LIRC_ON_SA1100_TRANSMITTER_LATENCY);
+static unsigned int freq = 38000;      /* modulation frequency */
+static unsigned int duty_cycle = 50;   /* duty cycle of 50% */
+
+#endif
+
+#define RBUF_LEN 1024
+#define WBUF_LEN 1024
+
+#define LIRC_DRIVER_NAME "lirc_sir"
+
+#define PULSE '['
+
+#ifndef LIRC_SIR_TEKRAM
+/* 9bit * 1s/115200bit in milli seconds = 78.125ms*/
+#define TIME_CONST (9000000ul/115200ul)
+#endif
+
+
+/* timeout for sequences in jiffies (=5/100s) */
+/* must be longer than TIME_CONST */
+#define SIR_TIMEOUT	(HZ*5/100)
+
+#ifndef LIRC_ON_SA1100
+#ifndef LIRC_IRQ
+#define LIRC_IRQ 4
+#endif
+#ifndef LIRC_PORT
+#define LIRC_PORT 0x3e8
+#endif
+
+static int io = LIRC_PORT;
+static int irq = LIRC_IRQ;
+static int threshold = 3;
+#endif
+
+static DEFINE_SPINLOCK(timer_lock);
+static struct timer_list timerlist;
+/* time of last signal change detected */
+static struct timeval last_tv = {0, 0};
+/* time of last UART data ready interrupt */
+static struct timeval last_intr_tv = {0, 0};
+static int last_value;
+
+static DECLARE_WAIT_QUEUE_HEAD(lirc_read_queue);
+
+static DEFINE_SPINLOCK(hardware_lock);
+static DEFINE_SPINLOCK(dev_lock);
+
+static int rx_buf[RBUF_LEN];
+static unsigned int rx_tail, rx_head;
+static int tx_buf[WBUF_LEN];
+
+static int debug;
+#define dprintk(fmt, args...)						\
+	do {								\
+		if (debug)						\
+			printk(KERN_DEBUG LIRC_DRIVER_NAME ": "		\
+				fmt, ## args);				\
+	} while (0)
+
+/* SECTION: Prototypes */
+
+/* Communication with user-space */
+static int lirc_open(struct inode *inode, struct file *file);
+static int lirc_close(struct inode *inode, struct file *file);
+static unsigned int lirc_poll(struct file *file, poll_table *wait);
+static ssize_t lirc_read(struct file *file, char *buf, size_t count,
+		loff_t *ppos);
+static ssize_t lirc_write(struct file *file, const char *buf, size_t n,
+		loff_t *pos);
+static int lirc_ioctl(struct inode *node, struct file *filep, unsigned int cmd,
+		unsigned long arg);
+static void add_read_queue(int flag, unsigned long val);
+#ifdef MODULE
+static int init_chrdev(void);
+static void drop_chrdev(void);
+#endif
+	/* Hardware */
+static irqreturn_t sir_interrupt(int irq, void *dev_id);
+static void send_space(unsigned long len);
+static void send_pulse(unsigned long len);
+static int init_hardware(void);
+static void drop_hardware(void);
+	/* Initialisation */
+static int init_port(void);
+static void drop_port(void);
+
+#ifdef LIRC_ON_SA1100
+static inline void on(void)
+{
+	PPSR |= PPC_TXD2;
+}
+
+static inline void off(void)
+{
+	PPSR &= ~PPC_TXD2;
+}
+#else
+static inline unsigned int sinp(int offset)
+{
+	return inb(io + offset);
+}
+
+static inline void soutp(int offset, int value)
+{
+	outb(value, io + offset);
+}
+#endif
+
+#ifndef MAX_UDELAY_MS
+#define MAX_UDELAY_US 5000
+#else
+#define MAX_UDELAY_US (MAX_UDELAY_MS*1000)
+#endif
+
+static inline void safe_udelay(unsigned long usecs)
+{
+	while (usecs > MAX_UDELAY_US) {
+		udelay(MAX_UDELAY_US);
+		usecs -= MAX_UDELAY_US;
+	}
+	udelay(usecs);
+}
+
+/* SECTION: Communication with user-space */
+
+static int lirc_open(struct inode *inode, struct file *file)
+{
+	spin_lock(&dev_lock);
+	if (module_refcount(THIS_MODULE)) {
+		spin_unlock(&dev_lock);
+		return -EBUSY;
+	}
+	spin_unlock(&dev_lock);
+	return 0;
+}
+
+static int lirc_close(struct inode *inode, struct file *file)
+{
+	return 0;
+}
+
+static unsigned int lirc_poll(struct file *file, poll_table *wait)
+{
+	poll_wait(file, &lirc_read_queue, wait);
+	if (rx_head != rx_tail)
+		return POLLIN | POLLRDNORM;
+	return 0;
+}
+
+static ssize_t lirc_read(struct file *file, char *buf, size_t count,
+		loff_t *ppos)
+{
+	int n = 0;
+	int retval = 0;
+	DECLARE_WAITQUEUE(wait, current);
+
+	if (n % sizeof(int))
+		return -EINVAL;
+
+	add_wait_queue(&lirc_read_queue, &wait);
+	set_current_state(TASK_INTERRUPTIBLE);
+	while (n < count) {
+		if (rx_head != rx_tail) {
+			if (copy_to_user((void *) buf + n,
+					(void *) (rx_buf + rx_head),
+					sizeof(int))) {
+				retval = -EFAULT;
+				break;
+			}
+			rx_head = (rx_head + 1) & (RBUF_LEN - 1);
+			n += sizeof(int);
+		} else {
+			if (file->f_flags & O_NONBLOCK) {
+				retval = -EAGAIN;
+				break;
+			}
+			if (signal_pending(current)) {
+				retval = -ERESTARTSYS;
+				break;
+			}
+			schedule();
+			set_current_state(TASK_INTERRUPTIBLE);
+		}
+	}
+	remove_wait_queue(&lirc_read_queue, &wait);
+	set_current_state(TASK_RUNNING);
+	return n ? n : retval;
+}
+static ssize_t lirc_write(struct file *file, const char *buf, size_t n,
+				loff_t *pos)
+{
+	unsigned long flags;
+	int i;
+
+	if (n % sizeof(int) || (n / sizeof(int)) > WBUF_LEN)
+		return -EINVAL;
+	if (copy_from_user(tx_buf, buf, n))
+		return -EFAULT;
+	i = 0;
+	n /= sizeof(int);
+#ifdef LIRC_ON_SA1100
+	/* disable receiver */
+	Ser2UTCR3 = 0;
+#endif
+	local_irq_save(flags);
+	while (1) {
+		if (i >= n)
+			break;
+		if (tx_buf[i])
+			send_pulse(tx_buf[i]);
+		i++;
+		if (i >= n)
+			break;
+		if (tx_buf[i])
+			send_space(tx_buf[i]);
+		i++;
+	}
+	local_irq_restore(flags);
+#ifdef LIRC_ON_SA1100
+	off();
+	udelay(1000); /* wait 1ms for IR diode to recover */
+	Ser2UTCR3 = 0;
+	/* clear status register to prevent unwanted interrupts */
+	Ser2UTSR0 &= (UTSR0_RID | UTSR0_RBB | UTSR0_REB);
+	/* enable receiver */
+	Ser2UTCR3 = UTCR3_RXE|UTCR3_RIE;
+#endif
+	return n;
+}
+
+static int lirc_ioctl(struct inode *node, struct file *filep, unsigned int cmd,
+		unsigned long arg)
+{
+	int retval = 0;
+	unsigned long value = 0;
+#ifdef LIRC_ON_SA1100
+	unsigned int ivalue;
+
+	if (cmd == LIRC_GET_FEATURES)
+		value = LIRC_CAN_SEND_PULSE |
+			LIRC_CAN_SET_SEND_DUTY_CYCLE |
+			LIRC_CAN_SET_SEND_CARRIER |
+			LIRC_CAN_REC_MODE2;
+	else if (cmd == LIRC_GET_SEND_MODE)
+		value = LIRC_MODE_PULSE;
+	else if (cmd == LIRC_GET_REC_MODE)
+		value = LIRC_MODE_MODE2;
+#else
+	if (cmd == LIRC_GET_FEATURES)
+		value = LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2;
+	else if (cmd == LIRC_GET_SEND_MODE)
+		value = LIRC_MODE_PULSE;
+	else if (cmd == LIRC_GET_REC_MODE)
+		value = LIRC_MODE_MODE2;
+#endif
+
+	switch (cmd) {
+	case LIRC_GET_FEATURES:
+	case LIRC_GET_SEND_MODE:
+	case LIRC_GET_REC_MODE:
+		retval = put_user(value, (unsigned long *) arg);
+		break;
+
+	case LIRC_SET_SEND_MODE:
+	case LIRC_SET_REC_MODE:
+		retval = get_user(value, (unsigned long *) arg);
+		break;
+#ifdef LIRC_ON_SA1100
+	case LIRC_SET_SEND_DUTY_CYCLE:
+		retval = get_user(ivalue, (unsigned int *) arg);
+		if (retval)
+			return reetval;
+		if (ivalue <= 0 || ivalue > 100)
+			return -EINVAL;
+		/* (ivalue/100)*(1000000/freq) */
+		duty_cycle = ivalue;
+		pulse_width = (unsigned long) duty_cycle*10000/freq;
+		space_width = (unsigned long) 1000000L/freq-pulse_width;
+		if (pulse_width >= LIRC_ON_SA1100_TRANSMITTER_LATENCY)
+			pulse_width -= LIRC_ON_SA1100_TRANSMITTER_LATENCY;
+		if (space_width >= LIRC_ON_SA1100_TRANSMITTER_LATENCY)
+			space_width -= LIRC_ON_SA1100_TRANSMITTER_LATENCY;
+		break;
+	case LIRC_SET_SEND_CARRIER:
+		retval = get_user(ivalue, (unsigned int *) arg);
+		if (retval)
+			return retval;
+		if (ivalue > 500000 || ivalue < 20000)
+			return -EINVAL;
+		freq = ivalue;
+		pulse_width = (unsigned long) duty_cycle*10000/freq;
+		space_width = (unsigned long) 1000000L/freq-pulse_width;
+		if (pulse_width >= LIRC_ON_SA1100_TRANSMITTER_LATENCY)
+			pulse_width -= LIRC_ON_SA1100_TRANSMITTER_LATENCY;
+		if (space_width >= LIRC_ON_SA1100_TRANSMITTER_LATENCY)
+			space_width -= LIRC_ON_SA1100_TRANSMITTER_LATENCY;
+		break;
+#endif
+	default:
+		retval = -ENOIOCTLCMD;
+
+	}
+
+	if (retval)
+		return retval;
+	if (cmd == LIRC_SET_REC_MODE) {
+		if (value != LIRC_MODE_MODE2)
+			retval = -ENOSYS;
+	} else if (cmd == LIRC_SET_SEND_MODE) {
+		if (value != LIRC_MODE_PULSE)
+			retval = -ENOSYS;
+	}
+
+	return retval;
+}
+
+static void add_read_queue(int flag, unsigned long val)
+{
+	unsigned int new_rx_tail;
+	int newval;
+
+	dprintk("add flag %d with val %lu\n", flag, val);
+
+	newval = val & PULSE_MASK;
+
+	/* statistically pulses are ~TIME_CONST/2 too long: we could
+	   maybe make this more exactly but this is good enough */
+	if (flag) {
+		/* pulse */
+		if (newval > TIME_CONST/2)
+			newval -= TIME_CONST/2;
+		else /* should not ever happen */
+			newval = 1;
+		newval |= PULSE_BIT;
+	} else {
+		newval += TIME_CONST/2;
+	}
+	new_rx_tail = (rx_tail + 1) & (RBUF_LEN - 1);
+	if (new_rx_tail == rx_head) {
+		dprintk("Buffer overrun.\n");
+		return;
+	}
+	rx_buf[rx_tail] = newval;
+	rx_tail = new_rx_tail;
+	wake_up_interruptible(&lirc_read_queue);
+}
+
+static struct file_operations lirc_fops = {
+	.read		= lirc_read,
+	.write		= lirc_write,
+	.poll		= lirc_poll,
+	.ioctl		= lirc_ioctl,
+	.open		= lirc_open,
+	.release	= lirc_close,
+};
+
+static int set_use_inc(void *data)
+{
+       return 0;
+}
+
+static void set_use_dec(void *data)
+{
+}
+
+static struct lirc_plugin plugin = {
+       .name		= LIRC_DRIVER_NAME,
+       .minor		= -1,
+       .code_length	= 1,
+       .sample_rate	= 0,
+       .data		= NULL,
+       .add_to_buf	= NULL,
+       .get_queue	= NULL,
+       .set_use_inc	= set_use_inc,
+       .set_use_dec	= set_use_dec,
+       .fops		= &lirc_fops,
+       .dev		= NULL,
+       .owner		= THIS_MODULE,
+};
+
+
+#ifdef MODULE
+static int init_chrdev(void)
+{
+	plugin.minor = lirc_register_plugin(&plugin);
+	if (plugin.minor < 0) {
+		printk(KERN_ERR LIRC_DRIVER_NAME ": init_chrdev() failed.\n");
+		return -EIO;
+	}
+	return 0;
+}
+
+static void drop_chrdev(void)
+{
+	lirc_unregister_plugin(plugin.minor);
+}
+#endif
+
+/* SECTION: Hardware */
+static long delta(struct timeval *tv1, struct timeval *tv2)
+{
+	unsigned long deltv;
+
+	deltv = tv2->tv_sec - tv1->tv_sec;
+	if (deltv > 15)
+		deltv = 0xFFFFFF;
+	else
+		deltv = deltv*1000000 +
+			tv2->tv_usec -
+			tv1->tv_usec;
+	return deltv;
+}
+
+static void sir_timeout(unsigned long data)
+{
+	/* if last received signal was a pulse, but receiving stopped
+	   within the 9 bit frame, we need to finish this pulse and
+	   simulate a signal change to from pulse to space. Otherwise
+	   upper layers will receive two sequences next time. */
+
+	unsigned long flags;
+	unsigned long pulse_end;
+
+	/* avoid interference with interrupt */
+	spin_lock_irqsave(&timer_lock, flags);
+	if (last_value) {
+#ifndef LIRC_ON_SA1100
+		/* clear unread bits in UART and restart */
+		outb(UART_FCR_CLEAR_RCVR, io + UART_FCR);
+#endif
+		/* determine 'virtual' pulse end: */
+		pulse_end = delta(&last_tv, &last_intr_tv);
+		dprintk("timeout add %d for %lu usec\n", last_value, pulse_end);
+		add_read_queue(last_value, pulse_end);
+		last_value = 0;
+		last_tv = last_intr_tv;
+	}
+	spin_unlock_irqrestore(&timer_lock, flags);
+}
+
+static irqreturn_t sir_interrupt(int irq, void *dev_id)
+{
+	unsigned char data;
+	struct timeval curr_tv;
+	static unsigned long deltv;
+#ifdef LIRC_ON_SA1100
+	int status;
+	static int n;
+
+	status = Ser2UTSR0;
+	/*
+	 * Deal with any receive errors first.  The bytes in error may be
+	 * the only bytes in the receive FIFO, so we do this first.
+	 */
+	while (status & UTSR0_EIF) {
+		int bstat;
+
+		if (debug) {
+			dprintk("EIF\n");
+			bstat = Ser2UTSR1;
+
+			if (bstat & UTSR1_FRE)
+				dprintk("frame error\n");
+			if (bstat & UTSR1_ROR)
+				dprintk("receive fifo overrun\n");
+			if (bstat & UTSR1_PRE)
+				dprintk("parity error\n");
+		}
+
+		bstat = Ser2UTDR;
+		n++;
+		status = Ser2UTSR0;
+	}
+
+	if (status & (UTSR0_RFS | UTSR0_RID)) {
+		do_gettimeofday(&curr_tv);
+		deltv = delta(&last_tv, &curr_tv);
+		do {
+			data = Ser2UTDR;
+			dprintk("%d data: %u\n", n, (unsigned int) data);
+			n++;
+		} while (status & UTSR0_RID && /* do not empty fifo in
+						order to get UTSR0_RID in
+						any case */
+		      Ser2UTSR1 & UTSR1_RNE); /* data ready */
+
+		if (status&UTSR0_RID) {
+			add_read_queue(0 , deltv - n * TIME_CONST); /*space*/
+			add_read_queue(1, n * TIME_CONST); /*pulse*/
+			n = 0;
+			last_tv = curr_tv;
+		}
+	}
+
+	if (status & UTSR0_TFS)
+		printk(KERN_ERR "transmit fifo not full, shouldn't happen\n");
+
+	/*
+	 * We must clear certain bits.
+	 */
+	status &= (UTSR0_RID | UTSR0_RBB | UTSR0_REB);
+	if (status)
+		Ser2UTSR0 = status;
+#else
+	unsigned long deltintrtv;
+	unsigned long flags;
+	int iir, lsr;
+
+	while ((iir = inb(io + UART_IIR) & UART_IIR_ID)) {
+		switch (iir&UART_IIR_ID) { /* FIXME toto treba preriedit */
+		case UART_IIR_MSI:
+			(void) inb(io + UART_MSR);
+			break;
+		case UART_IIR_RLSI:
+			(void) inb(io + UART_LSR);
+			break;
+		case UART_IIR_THRI:
+#if 0
+			if (lsr & UART_LSR_THRE) /* FIFO is empty */
+				outb(data, io + UART_TX)
+#endif
+			break;
+		case UART_IIR_RDI:
+			/* avoid interference with timer */
+			spin_lock_irqsave(&timer_lock, flags);
+			do {
+				del_timer(&timerlist);
+				data = inb(io + UART_RX);
+				do_gettimeofday(&curr_tv);
+				deltv = delta(&last_tv, &curr_tv);
+				deltintrtv = delta(&last_intr_tv, &curr_tv);
+				dprintk("t %lu, d %d\n", deltintrtv, (int)data);
+				/* if nothing came in last X cycles,
+				   it was gap */
+				if (deltintrtv > TIME_CONST * threshold) {
+					if (last_value) {
+						dprintk("GAP\n");
+						/* simulate signal change */
+						add_read_queue(last_value,
+							       deltv -
+							       deltintrtv);
+						last_value = 0;
+						last_tv.tv_sec =
+							last_intr_tv.tv_sec;
+						last_tv.tv_usec =
+							last_intr_tv.tv_usec;
+						deltv = deltintrtv;
+					}
+				}
+				data = 1;
+				if (data ^ last_value) {
+					/* deltintrtv > 2*TIME_CONST,
+						remember ? */
+					/* the other case is timeout */
+					add_read_queue(last_value,
+						       deltv-TIME_CONST);
+					last_value = data;
+					last_tv = curr_tv;
+					if (last_tv.tv_usec >= TIME_CONST) {
+						last_tv.tv_usec -= TIME_CONST;
+					} else {
+						last_tv.tv_sec--;
+						last_tv.tv_usec += 1000000 -
+							TIME_CONST;
+					}
+				}
+				last_intr_tv = curr_tv;
+				if (data) {
+					/* start timer for end of
+					 * sequence detection */
+					timerlist.expires = jiffies +
+								SIR_TIMEOUT;
+					add_timer(&timerlist);
+				}
+
+				lsr = inb(io + UART_LSR);
+			} while (lsr & UART_LSR_DR); /* data ready */
+			spin_unlock_irqrestore(&timer_lock, flags);
+			break;
+		default:
+			break;
+		}
+	}
+#endif
+	return IRQ_RETVAL(IRQ_HANDLED);
+}
+
+#ifdef LIRC_ON_SA1100
+static void send_pulse(unsigned long length)
+{
+	unsigned long k, delay;
+	int flag;
+
+	if (length == 0)
+		return;
+	/* this won't give us the carrier frequency we really want
+	   due to integer arithmetic, but we can accept this inaccuracy */
+
+	for (k = flag = 0; k < length; k += delay, flag = !flag) {
+		if (flag) {
+			off();
+			delay = space_width;
+		} else {
+			on();
+			delay = pulse_width;
+		}
+		safe_udelay(delay);
+	}
+	off();
+}
+
+static void send_space(unsigned long length)
+{
+	if (length == 0)
+		return;
+	off();
+	safe_udelay(length);
+}
+#else
+static void send_space(unsigned long len)
+{
+	safe_udelay(len);
+}
+
+static void send_pulse(unsigned long len)
+{
+	long bytes_out = len / TIME_CONST;
+	long time_left;
+
+	time_left = (long)len - (long)bytes_out * (long)TIME_CONST;
+	if (bytes_out == 0) {
+		bytes_out++;
+		time_left = 0;
+	}
+	while (bytes_out--) {
+		outb(PULSE, io + UART_TX);
+		/* FIXME treba seriozne cakanie z char/serial.c */
+		while (!(inb(io + UART_LSR) & UART_LSR_THRE))
+			;
+	}
+#if 0
+	if (time_left > 0)
+		safe_udelay(time_left);
+#endif
+}
+#endif
+
+#ifdef CONFIG_SA1100_COLLIE
+static inline int sa1100_irda_set_power_collie(int state)
+{
+	if (state) {
+		/*
+		 *  0 - off
+		 *  1 - short range, lowest power
+		 *  2 - medium range, medium power
+		 *  3 - maximum range, high power
+		 */
+		ucb1200_set_io_direction(TC35143_GPIO_IR_ON,
+					 TC35143_IODIR_OUTPUT);
+		ucb1200_set_io(TC35143_GPIO_IR_ON, TC35143_IODAT_LOW);
+		udelay(100);
+	} else {
+		/* OFF */
+		ucb1200_set_io_direction(TC35143_GPIO_IR_ON,
+					 TC35143_IODIR_OUTPUT);
+		ucb1200_set_io(TC35143_GPIO_IR_ON, TC35143_IODAT_HIGH);
+	}
+	return 0;
+}
+#endif
+
+static int init_hardware(void)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&hardware_lock, flags);
+	/* reset UART */
+#ifdef LIRC_ON_SA1100
+#ifdef CONFIG_SA1100_BITSY
+	if (machine_is_bitsy()) {
+		printk(KERN_INFO "Power on IR module\n");
+		set_bitsy_egpio(EGPIO_BITSY_IR_ON);
+	}
+#endif
+#ifdef CONFIG_SA1100_COLLIE
+	sa1100_irda_set_power_collie(3);	/* power on */
+#endif
+	sr.hscr0 = Ser2HSCR0;
+
+	sr.utcr0 = Ser2UTCR0;
+	sr.utcr1 = Ser2UTCR1;
+	sr.utcr2 = Ser2UTCR2;
+	sr.utcr3 = Ser2UTCR3;
+	sr.utcr4 = Ser2UTCR4;
+
+	sr.utdr = Ser2UTDR;
+	sr.utsr0 = Ser2UTSR0;
+	sr.utsr1 = Ser2UTSR1;
+
+	/* configure GPIO */
+	/* output */
+	PPDR |= PPC_TXD2;
+	PSDR |= PPC_TXD2;
+	/* set output to 0 */
+	off();
+
+	/*
+	 * Enable HP-SIR modulation, and ensure that the port is disabled.
+	 */
+	Ser2UTCR3 = 0;
+	Ser2HSCR0 = sr.hscr0 & (~HSCR0_HSSP);
+
+	/* clear status register to prevent unwanted interrupts */
+	Ser2UTSR0 &= (UTSR0_RID | UTSR0_RBB | UTSR0_REB);
+
+	/* 7N1 */
+	Ser2UTCR0 = UTCR0_1StpBit|UTCR0_7BitData;
+	/* 115200 */
+	Ser2UTCR1 = 0;
+	Ser2UTCR2 = 1;
+	/* use HPSIR, 1.6 usec pulses */
+	Ser2UTCR4 = UTCR4_HPSIR|UTCR4_Z1_6us;
+
+	/* enable receiver, receive fifo interrupt */
+	Ser2UTCR3 = UTCR3_RXE|UTCR3_RIE;
+
+	/* clear status register to prevent unwanted interrupts */
+	Ser2UTSR0 &= (UTSR0_RID | UTSR0_RBB | UTSR0_REB);
+
+#elif defined(LIRC_SIR_TEKRAM)
+	/* disable FIFO */
+	soutp(UART_FCR,
+	      UART_FCR_CLEAR_RCVR|
+	      UART_FCR_CLEAR_XMIT|
+	      UART_FCR_TRIGGER_1);
+
+	/* Set DLAB 0. */
+	soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
+
+	/* First of all, disable all interrupts */
+	soutp(UART_IER, sinp(UART_IER) &
+	      (~(UART_IER_MSI|UART_IER_RLSI|UART_IER_THRI|UART_IER_RDI)));
+
+	/* Set DLAB 1. */
+	soutp(UART_LCR, sinp(UART_LCR) | UART_LCR_DLAB);
+
+	/* Set divisor to 12 => 9600 Baud */
+	soutp(UART_DLM, 0);
+	soutp(UART_DLL, 12);
+
+	/* Set DLAB 0. */
+	soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
+
+	/* power supply */
+	soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2);
+	safe_udelay(50*1000);
+
+	/* -DTR low -> reset PIC */
+	soutp(UART_MCR, UART_MCR_RTS|UART_MCR_OUT2);
+	udelay(1*1000);
+
+	soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2);
+	udelay(100);
+
+
+	/* -RTS low -> send control byte */
+	soutp(UART_MCR, UART_MCR_DTR|UART_MCR_OUT2);
+	udelay(7);
+	soutp(UART_TX, TEKRAM_115200|TEKRAM_PW);
+
+	/* one byte takes ~1042 usec to transmit at 9600,8N1 */
+	udelay(1500);
+
+	/* back to normal operation */
+	soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2);
+	udelay(50);
+
+	udelay(1500);
+
+	/* read previous control byte */
+	printk(KERN_INFO LIRC_DRIVER_NAME
+	       ": 0x%02x\n", sinp(UART_RX));
+
+	/* Set DLAB 1. */
+	soutp(UART_LCR, sinp(UART_LCR) | UART_LCR_DLAB);
+
+	/* Set divisor to 1 => 115200 Baud */
+	soutp(UART_DLM, 0);
+	soutp(UART_DLL, 1);
+
+	/* Set DLAB 0, 8 Bit */
+	soutp(UART_LCR, UART_LCR_WLEN8);
+	/* enable interrupts */
+	soutp(UART_IER, sinp(UART_IER)|UART_IER_RDI);
+#else
+	outb(0, io + UART_MCR);
+	outb(0, io + UART_IER);
+	/* init UART */
+		/* set DLAB, speed = 115200 */
+	outb(UART_LCR_DLAB | UART_LCR_WLEN7, io + UART_LCR);
+	outb(1, io + UART_DLL); outb(0, io + UART_DLM);
+		/* 7N1+start = 9 bits at 115200 ~ 3 bits at 44000 */
+	outb(UART_LCR_WLEN7, io + UART_LCR);
+		/* FIFO operation */
+	outb(UART_FCR_ENABLE_FIFO, io + UART_FCR);
+		/* interrupts */
+	/* outb(UART_IER_RLSI|UART_IER_RDI|UART_IER_THRI, io + UART_IER); */
+	outb(UART_IER_RDI, io + UART_IER);
+	/* turn on UART */
+	outb(UART_MCR_DTR|UART_MCR_RTS|UART_MCR_OUT2, io + UART_MCR);
+#ifdef LIRC_SIR_ACTISYS_ACT200L
+	init_act200();
+#elif defined(LIRC_SIR_ACTISYS_ACT220L)
+	init_act220();
+#endif
+#endif
+	spin_unlock_irqrestore(&hardware_lock, flags);
+	return 0;
+}
+
+static void drop_hardware(void)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&hardware_lock, flags);
+
+#ifdef LIRC_ON_SA1100
+	Ser2UTCR3 = 0;
+
+	Ser2UTCR0 = sr.utcr0;
+	Ser2UTCR1 = sr.utcr1;
+	Ser2UTCR2 = sr.utcr2;
+	Ser2UTCR4 = sr.utcr4;
+	Ser2UTCR3 = sr.utcr3;
+
+	Ser2HSCR0 = sr.hscr0;
+#ifdef CONFIG_SA1100_BITSY
+	if (machine_is_bitsy())
+		clr_bitsy_egpio(EGPIO_BITSY_IR_ON);
+#endif
+#ifdef CONFIG_SA1100_COLLIE
+	sa1100_irda_set_power_collie(0);	/* power off */
+#endif
+#else
+	/* turn off interrupts */
+	outb(0, io + UART_IER);
+#endif
+	spin_unlock_irqrestore(&hardware_lock, flags);
+}
+
+/* SECTION: Initialisation */
+
+static int init_port(void)
+{
+	int retval;
+
+	/* get I/O port access and IRQ line */
+#ifndef LIRC_ON_SA1100
+	if (request_region(io, 8, LIRC_DRIVER_NAME) == NULL) {
+		printk(KERN_ERR LIRC_DRIVER_NAME
+		       ": i/o port 0x%.4x already in use.\n", io);
+		return -EBUSY;
+	}
+#endif
+	retval = request_irq(irq, sir_interrupt, IRQF_DISABLED,
+			     LIRC_DRIVER_NAME, NULL);
+	if (retval < 0) {
+#               ifndef LIRC_ON_SA1100
+		release_region(io, 8);
+#               endif
+		printk(KERN_ERR LIRC_DRIVER_NAME
+			": IRQ %d already in use.\n",
+			irq);
+		return retval;
+	}
+#ifndef LIRC_ON_SA1100
+	printk(KERN_INFO LIRC_DRIVER_NAME
+		": I/O port 0x%.4x, IRQ %d.\n",
+		io, irq);
+#endif
+
+	init_timer(&timerlist);
+	timerlist.function = sir_timeout;
+	timerlist.data = 0xabadcafe;
+
+	return 0;
+}
+
+static void drop_port(void)
+{
+	free_irq(irq, NULL);
+	del_timer_sync(&timerlist);
+#ifndef LIRC_ON_SA1100
+	release_region(io, 8);
+#endif
+}
+
+#ifdef LIRC_SIR_ACTISYS_ACT200L
+/******************************************************/
+/* Crystal/Cirrus CS8130 IR transceiver, used in Actisys Act200L dongle */
+/* some code borrowed from Linux IRDA driver */
+
+/* Regsiter 0: Control register #1 */
+#define ACT200L_REG0    0x00
+#define ACT200L_TXEN    0x01 /* Enable transmitter */
+#define ACT200L_RXEN    0x02 /* Enable receiver */
+#define ACT200L_ECHO    0x08 /* Echo control chars */
+
+/* Register 1: Control register #2 */
+#define ACT200L_REG1    0x10
+#define ACT200L_LODB    0x01 /* Load new baud rate count value */
+#define ACT200L_WIDE    0x04 /* Expand the maximum allowable pulse */
+
+/* Register 3: Transmit mode register #2 */
+#define ACT200L_REG3    0x30
+#define ACT200L_B0      0x01 /* DataBits, 0=6, 1=7, 2=8, 3=9(8P)  */
+#define ACT200L_B1      0x02 /* DataBits, 0=6, 1=7, 2=8, 3=9(8P)  */
+#define ACT200L_CHSY    0x04 /* StartBit Synced 0=bittime, 1=startbit */
+
+/* Register 4: Output Power register */
+#define ACT200L_REG4    0x40
+#define ACT200L_OP0     0x01 /* Enable LED1C output */
+#define ACT200L_OP1     0x02 /* Enable LED2C output */
+#define ACT200L_BLKR    0x04
+
+/* Register 5: Receive Mode register */
+#define ACT200L_REG5    0x50
+#define ACT200L_RWIDL   0x01 /* fixed 1.6us pulse mode */
+    /*.. other various IRDA bit modes, and TV remote modes..*/
+
+/* Register 6: Receive Sensitivity register #1 */
+#define ACT200L_REG6    0x60
+#define ACT200L_RS0     0x01 /* receive threshold bit 0 */
+#define ACT200L_RS1     0x02 /* receive threshold bit 1 */
+
+/* Register 7: Receive Sensitivity register #2 */
+#define ACT200L_REG7    0x70
+#define ACT200L_ENPOS   0x04 /* Ignore the falling edge */
+
+/* Register 8,9: Baud Rate Dvider register #1,#2 */
+#define ACT200L_REG8    0x80
+#define ACT200L_REG9    0x90
+
+#define ACT200L_2400    0x5f
+#define ACT200L_9600    0x17
+#define ACT200L_19200   0x0b
+#define ACT200L_38400   0x05
+#define ACT200L_57600   0x03
+#define ACT200L_115200  0x01
+
+/* Register 13: Control register #3 */
+#define ACT200L_REG13   0xd0
+#define ACT200L_SHDW    0x01 /* Enable access to shadow registers */
+
+/* Register 15: Status register */
+#define ACT200L_REG15   0xf0
+
+/* Register 21: Control register #4 */
+#define ACT200L_REG21   0x50
+#define ACT200L_EXCK    0x02 /* Disable clock output driver */
+#define ACT200L_OSCL    0x04 /* oscillator in low power, medium accuracy mode */
+
+static void init_act200(void)
+{
+	int i;
+	__u8 control[] = {
+		ACT200L_REG15,
+		ACT200L_REG13 | ACT200L_SHDW,
+		ACT200L_REG21 | ACT200L_EXCK | ACT200L_OSCL,
+		ACT200L_REG13,
+		ACT200L_REG7  | ACT200L_ENPOS,
+		ACT200L_REG6  | ACT200L_RS0  | ACT200L_RS1,
+		ACT200L_REG5  | ACT200L_RWIDL,
+		ACT200L_REG4  | ACT200L_OP0  | ACT200L_OP1 | ACT200L_BLKR,
+		ACT200L_REG3  | ACT200L_B0,
+		ACT200L_REG0  | ACT200L_TXEN | ACT200L_RXEN,
+		ACT200L_REG8 |  (ACT200L_115200       & 0x0f),
+		ACT200L_REG9 | ((ACT200L_115200 >> 4) & 0x0f),
+		ACT200L_REG1 | ACT200L_LODB | ACT200L_WIDE
+	};
+
+	/* Set DLAB 1. */
+	soutp(UART_LCR, UART_LCR_DLAB | UART_LCR_WLEN8);
+
+	/* Set divisor to 12 => 9600 Baud */
+	soutp(UART_DLM, 0);
+	soutp(UART_DLL, 12);
+
+	/* Set DLAB 0. */
+	soutp(UART_LCR, UART_LCR_WLEN8);
+	/* Set divisor to 12 => 9600 Baud */
+
+	/* power supply */
+	soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2);
+	for (i = 0; i < 50; i++)
+		safe_udelay(1000);
+
+		/* Reset the dongle : set RTS low for 25 ms */
+	soutp(UART_MCR, UART_MCR_DTR|UART_MCR_OUT2);
+	for (i = 0; i < 25; i++)
+		udelay(1000);
+
+	soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2);
+	udelay(100);
+
+	/* Clear DTR and set RTS to enter command mode */
+	soutp(UART_MCR, UART_MCR_RTS|UART_MCR_OUT2);
+	udelay(7);
+
+/* send out the control register settings for 115K 7N1 SIR operation */
+	for (i = 0; i < sizeof(control); i++) {
+		soutp(UART_TX, control[i]);
+		/* one byte takes ~1042 usec to transmit at 9600,8N1 */
+		udelay(1500);
+	}
+
+	/* back to normal operation */
+	soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2);
+	udelay(50);
+
+	udelay(1500);
+	soutp(UART_LCR, sinp(UART_LCR) | UART_LCR_DLAB);
+
+	/* Set DLAB 1. */
+	soutp(UART_LCR, UART_LCR_DLAB | UART_LCR_WLEN7);
+
+	/* Set divisor to 1 => 115200 Baud */
+	soutp(UART_DLM, 0);
+	soutp(UART_DLL, 1);
+
+	/* Set DLAB 0. */
+	soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
+
+	/* Set DLAB 0, 7 Bit */
+	soutp(UART_LCR, UART_LCR_WLEN7);
+
+	/* enable interrupts */
+	soutp(UART_IER, sinp(UART_IER)|UART_IER_RDI);
+}
+#endif
+
+#ifdef LIRC_SIR_ACTISYS_ACT220L
+/* Derived from linux IrDA driver (net/irda/actisys.c)
+ * Drop me a mail for any kind of comment: maxx@spaceboyz.net */
+
+void init_act220(void)
+{
+	int i;
+
+	/* DLAB 1 */
+	soutp(UART_LCR, UART_LCR_DLAB|UART_LCR_WLEN7);
+
+	/* 9600 baud */
+	soutp(UART_DLM, 0);
+	soutp(UART_DLL, 12);
+
+	/* DLAB 0 */
+	soutp(UART_LCR, UART_LCR_WLEN7);
+
+	/* reset the dongle, set DTR low for 10us */
+	soutp(UART_MCR, UART_MCR_RTS|UART_MCR_OUT2);
+	udelay(10);
+
+	/* back to normal (still 9600) */
+	soutp(UART_MCR, UART_MCR_DTR|UART_MCR_RTS|UART_MCR_OUT2);
+
+	/* send RTS pulses until we reach 115200
+	 * i hope this is really the same for act220l/act220l+ */
+	for (i = 0; i < 3; i++) {
+		udelay(10);
+		/* set RTS low for 10 us */
+		soutp(UART_MCR, UART_MCR_DTR|UART_MCR_OUT2);
+		udelay(10);
+		/* set RTS high for 10 us */
+		soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2);
+	}
+
+	/* back to normal operation */
+	udelay(1500); /* better safe than sorry ;) */
+
+	/* Set DLAB 1. */
+	soutp(UART_LCR, UART_LCR_DLAB | UART_LCR_WLEN7);
+
+	/* Set divisor to 1 => 115200 Baud */
+	soutp(UART_DLM, 0);
+	soutp(UART_DLL, 1);
+
+	/* Set DLAB 0, 7 Bit */
+	/* The dongle doesn't seem to have any problems with operation
+	   at 7N1 */
+	soutp(UART_LCR, UART_LCR_WLEN7);
+
+	/* enable interrupts */
+	soutp(UART_IER, UART_IER_RDI);
+}
+#endif
+
+static int init_lirc_sir(void)
+{
+	int retval;
+
+	init_waitqueue_head(&lirc_read_queue);
+	retval = init_port();
+	if (retval < 0)
+		return retval;
+	init_hardware();
+	printk(KERN_INFO LIRC_DRIVER_NAME
+		": Installed.\n");
+	return 0;
+}
+
+#ifdef MODULE
+
+static int __init lirc_sir_init(void)
+{
+	int retval;
+
+	retval = init_chrdev();
+	if (retval < 0)
+		return retval;
+	retval = init_lirc_sir();
+	if (retval) {
+		drop_chrdev();
+		return retval;
+	}
+	return 0;
+}
+
+static void __exit lirc_sir_exit(void)
+{
+	drop_hardware();
+	drop_chrdev();
+	drop_port();
+	printk(KERN_INFO LIRC_DRIVER_NAME ": Uninstalled.\n");
+}
+
+module_init(lirc_sir_init);
+module_exit(lirc_sir_exit);
+
+#ifdef LIRC_SIR_TEKRAM
+MODULE_DESCRIPTION("Infrared receiver driver for Tekram Irmate 210");
+MODULE_AUTHOR("Christoph Bartelmus");
+#elif defined(LIRC_ON_SA1100)
+MODULE_DESCRIPTION("LIRC driver for StrongARM SA1100 embedded microprocessor");
+MODULE_AUTHOR("Christoph Bartelmus");
+#elif defined(LIRC_SIR_ACTISYS_ACT200L)
+MODULE_DESCRIPTION("LIRC driver for Actisys Act200L");
+MODULE_AUTHOR("Karl Bongers");
+#elif defined(LIRC_SIR_ACTISYS_ACT220L)
+MODULE_DESCRIPTION("LIRC driver for Actisys Act220L(+)");
+MODULE_AUTHOR("Jan Roemisch");
+#else
+MODULE_DESCRIPTION("Infrared receiver driver for SIR type serial ports");
+MODULE_AUTHOR("Milan Pikula");
+#endif
+MODULE_LICENSE("GPL");
+
+#ifdef LIRC_ON_SA1100
+module_param(irq, int, 0444);
+MODULE_PARM_DESC(irq, "Interrupt (16)");
+#else
+module_param(io, int, 0444);
+MODULE_PARM_DESC(io, "I/O address base (0x3f8 or 0x2f8)");
+
+module_param(irq, int, 0444);
+MODULE_PARM_DESC(irq, "Interrupt (4 or 3)");
+
+module_param(threshold, int, 0444);
+MODULE_PARM_DESC(threshold, "space detection threshold (3)");
+#endif
+
+module_param(debug, bool, 0644);
+MODULE_PARM_DESC(debug, "Enable debugging messages");
+
+#endif /* MODULE */
diff --git a/drivers/input/lirc/lirc_streamzap.c b/drivers/input/lirc/lirc_streamzap.c
new file mode 100644
index 0000000..69865cb
--- /dev/null
+++ b/drivers/input/lirc/lirc_streamzap.c
@@ -0,0 +1,795 @@
+/*
+ * Streamzap Remote Control driver
+ *
+ * Copyright (c) 2005 Christoph Bartelmus <lirc@bartelmus.de>
+ *
+ * This driver was based on the work of Greg Wickham and Adrian
+ * Dewhurst. It was substantially rewritten to support correct signal
+ * gaps and now maintains a delay buffer, which is used to present
+ * consistent timing behaviour to user space applications. Without the
+ * delay buffer an ugly hack would be required in lircd, which can
+ * cause sluggish signal decoding in certain situations.
+ *
+ * This driver is based on the USB skeleton driver packaged with the
+ * kernel; copyright (C) 2001-2003 Greg Kroah-Hartman (greg@kroah.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/version.h>
+
+#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/smp_lock.h>
+#include <linux/completion.h>
+#include <linux/uaccess.h>
+#include <linux/usb.h>
+
+#include "lirc.h"
+#include "lirc_dev.h"
+
+#define DRIVER_VERSION	"1.28"
+#define DRIVER_NAME	"lirc_streamzap"
+#define DRIVER_DESC	"Streamzap Remote Control driver"
+
+/* ------------------------------------------------------------------ */
+
+static int debug;
+
+#define USB_STREAMZAP_VENDOR_ID		0x0e9c
+#define USB_STREAMZAP_PRODUCT_ID	0x0000
+
+/* Use our own dbg macro */
+#define dprintk(fmt, args...)					\
+	do {							\
+		if (debug)					\
+			printk(KERN_DEBUG DRIVER_NAME "[%d]: "	\
+			       fmt "\n", ## args);		\
+	} while (0)
+
+/*
+ * table of devices that work with this driver
+ */
+static struct usb_device_id streamzap_table[] = {
+	/* Streamzap Remote Control */
+	{ USB_DEVICE(USB_STREAMZAP_VENDOR_ID, USB_STREAMZAP_PRODUCT_ID) },
+	/* Terminating entry */
+	{ }
+};
+
+MODULE_DEVICE_TABLE(usb, streamzap_table);
+
+#define STREAMZAP_PULSE_MASK 0xf0
+#define STREAMZAP_SPACE_MASK 0x0f
+#define STREAMZAP_RESOLUTION 256
+
+/* number of samples buffered */
+#define STREAMZAP_BUFFER_SIZE 128
+
+enum StreamzapDecoderState {
+	PulseSpace,
+	FullPulse,
+	FullSpace,
+	IgnorePulse
+};
+
+/* Structure to hold all of our device specific stuff */
+/* some remarks regarding locking:
+   theoretically this struct can be accessed from three threads:
+
+   - from lirc_dev through set_use_inc/set_use_dec
+
+   - from the USB layer throuh probe/disconnect/irq
+
+     Careful placement of lirc_register_plugin/lirc_unregister_plugin
+     calls will prevent conflicts. lirc_dev makes sure that
+     set_use_inc/set_use_dec are not being executed and will not be
+     called after lirc_unregister_plugin returns.
+
+   - by the timer callback
+
+     The timer is only running when the device is connected and the
+     LIRC device is open. Making sure the timer is deleted by
+     set_use_dec will make conflicts impossible.
+*/
+struct usb_streamzap {
+
+	/* usb */
+	/* save off the usb device pointer */
+	struct usb_device	*udev;
+	/* the interface for this device */
+	struct usb_interface	*interface;
+
+	/* buffer & dma */
+	unsigned char		*buf_in;
+	dma_addr_t		dma_in;
+	unsigned int		buf_in_len;
+
+	struct usb_endpoint_descriptor *endpoint;
+
+	/* IRQ */
+	struct urb		*urb_in;
+
+	/* lirc */
+	struct lirc_plugin	plugin;
+	struct lirc_buffer	delay_buf;
+	struct lirc_buffer	lirc_buf;
+
+	/* timer used to support delay buffering */
+	struct timer_list	delay_timer;
+	int			timer_running;
+	spinlock_t		timer_lock;
+
+	/* tracks whether we are currently receiving some signal */
+	int			idle;
+	/* sum of signal lengths received since signal start */
+	unsigned long		sum;
+	/* start time of signal; necessary for gap tracking */
+	struct timeval		signal_last;
+	struct timeval		signal_start;
+	enum StreamzapDecoderState decoder_state;
+	struct timer_list	flush_timer;
+	int			flush;
+	int			in_use;
+};
+
+
+/* local function prototypes */
+static int streamzap_probe(struct usb_interface *interface,
+			   const struct usb_device_id *id);
+static void streamzap_disconnect(struct usb_interface *interface);
+static void usb_streamzap_irq(struct urb *urb);
+static int streamzap_use_inc(void *data);
+static void streamzap_use_dec(void *data);
+static int streamzap_ioctl(struct inode *node, struct file *filep,
+			   unsigned int cmd, unsigned long arg);
+static int streamzap_suspend(struct usb_interface *intf, pm_message_t message);
+static int streamzap_resume(struct usb_interface *intf);
+
+/* usb specific object needed to register this driver with the usb subsystem */
+
+static struct usb_driver streamzap_driver = {
+	.name =		DRIVER_NAME,
+	.probe =	streamzap_probe,
+	.disconnect =	streamzap_disconnect,
+	.suspend =	streamzap_suspend,
+	.resume =	streamzap_resume,
+	.id_table =	streamzap_table,
+};
+
+static void stop_timer(struct usb_streamzap *sz)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&sz->timer_lock, flags);
+	if (sz->timer_running) {
+		sz->timer_running = 0;
+		del_timer_sync(&sz->delay_timer);
+	}
+	spin_unlock_irqrestore(&sz->timer_lock, flags);
+}
+
+static void flush_timeout(unsigned long arg)
+{
+	struct usb_streamzap *sz = (struct usb_streamzap *) arg;
+
+	/* finally start accepting data */
+	sz->flush = 0;
+}
+static void delay_timeout(unsigned long arg)
+{
+	unsigned long flags;
+	/* deliver data every 10 ms */
+	static unsigned long timer_inc =
+		(10000/(1000000/HZ)) == 0 ? 1 : (10000/(1000000/HZ));
+	struct usb_streamzap *sz = (struct usb_streamzap *) arg;
+	int data;
+
+	spin_lock_irqsave(&sz->timer_lock, flags);
+
+	if (!lirc_buffer_empty(&sz->delay_buf) &&
+	    !lirc_buffer_full(&sz->lirc_buf)) {
+		lirc_buffer_read_1(&sz->delay_buf, (unsigned char *) &data);
+		lirc_buffer_write_1(&sz->lirc_buf, (unsigned char *) &data);
+	}
+	if (!lirc_buffer_empty(&sz->delay_buf)) {
+		while (lirc_buffer_available(&sz->delay_buf) <
+		      STREAMZAP_BUFFER_SIZE/2 &&
+		      !lirc_buffer_full(&sz->lirc_buf)) {
+			lirc_buffer_read_1(&sz->delay_buf,
+					   (unsigned char *) &data);
+			lirc_buffer_write_1(&sz->lirc_buf,
+					    (unsigned char *) &data);
+		}
+		if (sz->timer_running) {
+			sz->delay_timer.expires += timer_inc;
+			add_timer(&sz->delay_timer);
+		}
+	} else {
+		sz->timer_running = 0;
+	}
+
+	if (!lirc_buffer_empty(&sz->lirc_buf))
+		wake_up(&sz->lirc_buf.wait_poll);
+
+	spin_unlock_irqrestore(&sz->timer_lock, flags);
+}
+
+static inline void flush_delay_buffer(struct usb_streamzap *sz)
+{
+	int data;
+	int empty = 1;
+
+	while (!lirc_buffer_empty(&sz->delay_buf)) {
+		empty = 0;
+		lirc_buffer_read_1(&sz->delay_buf, (unsigned char *) &data);
+		if (!lirc_buffer_full(&sz->lirc_buf)) {
+			lirc_buffer_write_1(&sz->lirc_buf,
+					    (unsigned char *) &data);
+		} else {
+			dprintk("buffer overflow\n", sz->plugin.minor);
+		}
+	}
+	if (!empty)
+		wake_up(&sz->lirc_buf.wait_poll);
+}
+
+static inline void push(struct usb_streamzap *sz, unsigned char *data)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&sz->timer_lock, flags);
+	if (lirc_buffer_full(&sz->delay_buf)) {
+		int data;
+
+		lirc_buffer_read_1(&sz->delay_buf, (unsigned char *) &data);
+		if (!lirc_buffer_full(&sz->lirc_buf)) {
+			lirc_buffer_write_1(&sz->lirc_buf,
+					    (unsigned char *) &data);
+		} else {
+			dprintk("buffer overflow", sz->plugin.minor);
+		}
+	}
+
+	lirc_buffer_write_1(&sz->delay_buf, data);
+
+	if (!sz->timer_running) {
+		sz->delay_timer.expires = jiffies + HZ/10;
+		add_timer(&sz->delay_timer);
+		sz->timer_running = 1;
+	}
+
+	spin_unlock_irqrestore(&sz->timer_lock, flags);
+}
+
+static inline void push_full_pulse(struct usb_streamzap *sz,
+				   unsigned char value)
+{
+	int pulse;
+
+	if (sz->idle) {
+		long deltv;
+		int tmp;
+
+		sz->signal_last = sz->signal_start;
+		do_gettimeofday(&sz->signal_start);
+
+		deltv = sz->signal_start.tv_sec-sz->signal_last.tv_sec;
+		if (deltv > 15) {
+			tmp = PULSE_MASK; /* really long time */
+		} else {
+			tmp = (int) (deltv*1000000+
+					sz->signal_start.tv_usec -
+					sz->signal_last.tv_usec);
+			tmp -= sz->sum;
+		}
+		dprintk("ls %u", sz->plugin.minor, tmp);
+		push(sz, (char *)&tmp);
+
+		sz->idle = 0;
+		sz->sum = 0;
+	}
+
+	pulse = ((int) value)*STREAMZAP_RESOLUTION;
+	pulse += STREAMZAP_RESOLUTION/2;
+	sz->sum += pulse;
+	pulse |= PULSE_BIT;
+
+	dprintk("p %u", sz->plugin.minor, pulse&PULSE_MASK);
+	push(sz, (char *)&pulse);
+}
+
+static inline void push_half_pulse(struct usb_streamzap *sz,
+				   unsigned char value)
+{
+	push_full_pulse(sz, (value & STREAMZAP_PULSE_MASK)>>4);
+}
+
+static inline void push_full_space(struct usb_streamzap *sz,
+				   unsigned char value)
+{
+	int space;
+
+	space = ((int) value)*STREAMZAP_RESOLUTION;
+	space += STREAMZAP_RESOLUTION/2;
+	sz->sum += space;
+	dprintk("s %u", sz->plugin.minor, space);
+	push(sz, (char *)&space);
+}
+
+static inline void push_half_space(struct usb_streamzap *sz,
+				   unsigned char value)
+{
+	push_full_space(sz, value & STREAMZAP_SPACE_MASK);
+}
+
+/*
+ * usb_streamzap_irq - IRQ handler
+ *
+ * This procedure is invoked on reception of data from
+ * the usb remote.
+ */
+static void usb_streamzap_irq(struct urb *urb)
+{
+	struct usb_streamzap *sz;
+	int		len;
+	unsigned int	i = 0;
+
+	if (!urb)
+		return;
+
+	sz = urb->context;
+	len = urb->actual_length;
+
+	switch (urb->status) {
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* this urb is terminated, clean up */
+		/* sz might already be invalid at this point */
+		dprintk("urb status: %d", -1, urb->status);
+		return;
+	default:
+		break;
+	}
+
+	dprintk("received %d", sz->plugin.minor, urb->actual_length);
+	if (!sz->flush) {
+		for (i = 0; i < urb->actual_length; i++) {
+			dprintk("%d: %x", sz->plugin.minor,
+				i, (unsigned char) sz->buf_in[i]);
+			switch (sz->decoder_state) {
+			case PulseSpace:
+				if ((sz->buf_in[i]&STREAMZAP_PULSE_MASK) ==
+				    STREAMZAP_PULSE_MASK) {
+					sz->decoder_state = FullPulse;
+					continue;
+				} else if ((sz->buf_in[i]&STREAMZAP_SPACE_MASK)
+					   == STREAMZAP_SPACE_MASK) {
+					push_half_pulse(sz, sz->buf_in[i]);
+					sz->decoder_state = FullSpace;
+					continue;
+				} else {
+					push_half_pulse(sz, sz->buf_in[i]);
+					push_half_space(sz, sz->buf_in[i]);
+				}
+				break;
+			case FullPulse:
+				push_full_pulse(sz, sz->buf_in[i]);
+				sz->decoder_state = IgnorePulse;
+				break;
+			case FullSpace:
+				if (sz->buf_in[i] == 0xff) {
+					sz->idle = 1;
+					stop_timer(sz);
+					flush_delay_buffer(sz);
+				} else
+					push_full_space(sz, sz->buf_in[i]);
+				sz->decoder_state = PulseSpace;
+				break;
+			case IgnorePulse:
+				if ((sz->buf_in[i]&STREAMZAP_SPACE_MASK) ==
+				    STREAMZAP_SPACE_MASK) {
+					sz->decoder_state = FullSpace;
+					continue;
+				}
+				push_half_space(sz, sz->buf_in[i]);
+				sz->decoder_state = PulseSpace;
+				break;
+			}
+		}
+	}
+
+	/* resubmit only for 2.6 */
+	usb_submit_urb(urb, GFP_ATOMIC);
+
+	return;
+}
+
+/**
+ *	streamzap_probe
+ *
+ *	Called by usb-core to associated with a candidate device
+ *	On any failure the return value is the ERROR
+ *	On success return 0
+ */
+static int streamzap_probe(struct usb_interface *interface,
+			   const struct usb_device_id *id)
+{
+	struct usb_device *udev = interface_to_usbdev(interface);
+	struct usb_host_interface *iface_host;
+	int retval = -ENOMEM;
+	struct usb_streamzap *sz = NULL;
+	char buf[63], name[128] = "";
+
+	/***************************************************
+	 * Allocate space for device driver specific data
+	 */
+	sz = kmalloc(sizeof(struct usb_streamzap), GFP_KERNEL);
+	if (sz == NULL)
+		goto error;
+
+	memset(sz, 0, sizeof(*sz));
+	sz->udev = udev;
+	sz->interface = interface;
+
+	/***************************************************
+	 * Check to ensure endpoint information matches requirements
+	 */
+	iface_host = interface->cur_altsetting;
+
+	if (iface_host->desc.bNumEndpoints != 1) {
+		err("%s: Unexpected desc.bNumEndpoints (%d)", __func__,
+		    iface_host->desc.bNumEndpoints);
+		retval = -ENODEV;
+		goto error;
+	}
+
+	sz->endpoint = &(iface_host->endpoint[0].desc);
+	if ((sz->endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
+	    != USB_DIR_IN) {
+		err("%s: endpoint doesn't match input device 02%02x",
+		    __func__, sz->endpoint->bEndpointAddress);
+		retval = -ENODEV;
+		goto error;
+	}
+
+	if ((sz->endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+	    != USB_ENDPOINT_XFER_INT) {
+		err("%s: endpoint attributes don't match xfer 02%02x",
+		    __func__, sz->endpoint->bmAttributes);
+		retval = -ENODEV;
+		goto error;
+	}
+
+	if (sz->endpoint->wMaxPacketSize == 0) {
+		err("%s: endpoint message size==0? ", __func__);
+		retval = -ENODEV;
+		goto error;
+	}
+
+	/***************************************************
+	 * Allocate the USB buffer and IRQ URB
+	 */
+
+	sz->buf_in_len = sz->endpoint->wMaxPacketSize;
+	sz->buf_in = usb_buffer_alloc(sz->udev, sz->buf_in_len,
+				      GFP_ATOMIC, &sz->dma_in);
+	if (sz->buf_in == NULL)
+		goto error;
+
+	sz->urb_in = usb_alloc_urb(0, GFP_KERNEL);
+	if (sz->urb_in == NULL)
+		goto error;
+
+	/***************************************************
+	 * Connect this device to the LIRC sub-system
+	 */
+
+	if (lirc_buffer_init(&sz->lirc_buf, sizeof(int),
+			     STREAMZAP_BUFFER_SIZE))
+		goto error;
+
+	if (lirc_buffer_init(&sz->delay_buf, sizeof(int),
+			     STREAMZAP_BUFFER_SIZE)) {
+		lirc_buffer_free(&sz->lirc_buf);
+		goto error;
+	}
+
+	/***************************************************
+	 * As required memory is allocated now populate the plugin structure
+	 */
+
+	memset(&sz->plugin, 0, sizeof(sz->plugin));
+
+	strcpy(sz->plugin.name, DRIVER_NAME);
+	sz->plugin.minor = -1;
+	sz->plugin.sample_rate = 0;
+	sz->plugin.code_length = sizeof(int) * 8;
+	sz->plugin.features = LIRC_CAN_REC_MODE2 | LIRC_CAN_GET_REC_RESOLUTION;
+	sz->plugin.data = sz;
+	sz->plugin.rbuf = &sz->lirc_buf;
+	sz->plugin.set_use_inc = &streamzap_use_inc;
+	sz->plugin.set_use_dec = &streamzap_use_dec;
+	sz->plugin.ioctl = streamzap_ioctl;
+	sz->plugin.dev = &udev->dev;
+	sz->plugin.owner = THIS_MODULE;
+
+	sz->idle = 1;
+	sz->decoder_state = PulseSpace;
+	init_timer(&sz->delay_timer);
+	sz->delay_timer.function = delay_timeout;
+	sz->delay_timer.data = (unsigned long) sz;
+	sz->timer_running = 0;
+	spin_lock_init(&sz->timer_lock);
+
+	init_timer(&sz->flush_timer);
+	sz->flush_timer.function = flush_timeout;
+	sz->flush_timer.data = (unsigned long) sz;
+	/***************************************************
+	 * Complete final initialisations
+	 */
+
+	usb_fill_int_urb(sz->urb_in, udev,
+		usb_rcvintpipe(udev, sz->endpoint->bEndpointAddress),
+		sz->buf_in, sz->buf_in_len, usb_streamzap_irq, sz,
+		sz->endpoint->bInterval);
+
+	if (udev->descriptor.iManufacturer
+	    && usb_string(udev, udev->descriptor.iManufacturer, buf, 63) > 0)
+		strncpy(name, buf, 128);
+
+	if (udev->descriptor.iProduct
+	    && usb_string(udev,  udev->descriptor.iProduct, buf, 63) > 0)
+		snprintf(name, 128, "%s %s", name, buf);
+
+	printk(KERN_INFO DRIVER_NAME "[%d]: %s on usb%d:%d attached\n",
+	       sz->plugin.minor, name,
+	       udev->bus->busnum, sz->udev->devnum);
+
+	usb_set_intfdata(interface, sz);
+
+	if (lirc_register_plugin(&sz->plugin) < 0) {
+		lirc_buffer_free(&sz->delay_buf);
+		lirc_buffer_free(&sz->lirc_buf);
+		goto error;
+	}
+
+	return 0;
+
+error:
+
+	/***************************************************
+	 * Premise is that a 'goto error' can be invoked from inside the
+	 * probe function and all necessary cleanup actions will be taken
+	 * including freeing any necessary memory blocks
+	 */
+
+	if (retval == -ENOMEM)
+		err("Out of memory");
+
+	if (sz) {
+		usb_free_urb(sz->urb_in);
+		usb_buffer_free(udev, sz->buf_in_len, sz->buf_in, sz->dma_in);
+		kfree(sz);
+	}
+
+	return retval;
+}
+
+static int streamzap_use_inc(void *data)
+{
+	struct usb_streamzap *sz = data;
+
+	if (!sz) {
+		dprintk("%s called with no context", -1, __func__);
+		return -EINVAL;
+	}
+	dprintk("set use inc", sz->plugin.minor);
+
+	while (!lirc_buffer_empty(&sz->lirc_buf))
+		lirc_buffer_remove_1(&sz->lirc_buf);
+	while (!lirc_buffer_empty(&sz->delay_buf))
+		lirc_buffer_remove_1(&sz->delay_buf);
+
+	sz->flush_timer.expires = jiffies + HZ;
+	sz->flush = 1;
+	add_timer(&sz->flush_timer);
+
+	sz->urb_in->dev = sz->udev;
+	if (usb_submit_urb(sz->urb_in, GFP_ATOMIC)) {
+		dprintk("open result = -EIO error submitting urb",
+			sz->plugin.minor);
+		return -EIO;
+	}
+	sz->in_use++;
+
+	return 0;
+}
+
+static void streamzap_use_dec(void *data)
+{
+	struct usb_streamzap *sz = data;
+
+	if (!sz) {
+		dprintk("%s called with no context", -1, __func__);
+		return;
+	}
+	dprintk("set use dec", sz->plugin.minor);
+
+	if (sz->flush) {
+		sz->flush = 0;
+		del_timer_sync(&sz->flush_timer);
+	}
+
+	stop_timer(sz);
+
+	usb_kill_urb(sz->urb_in);
+
+	sz->in_use--;
+}
+
+static int streamzap_ioctl(struct inode *node, struct file *filep,
+			   unsigned int cmd, unsigned long arg)
+{
+	int result;
+
+	switch (cmd) {
+	case LIRC_GET_REC_RESOLUTION:
+		result = put_user(STREAMZAP_RESOLUTION, (unsigned long *) arg);
+		if (result)
+			return result;
+		break;
+	default:
+		return -ENOIOCTLCMD;
+	}
+	return 0;
+}
+
+/**
+ *	streamzap_disconnect
+ *
+ *	Called by the usb core when the device is removed from the system.
+ *
+ *	This routine guarantees that the driver will not submit any more urbs
+ *	by clearing dev->udev.  It is also supposed to terminate any currently
+ *	active urbs.  Unfortunately, usb_bulk_msg(), used in streamzap_read(),
+ *	does not provide any way to do this.
+ */
+static void streamzap_disconnect(struct usb_interface *interface)
+{
+	struct usb_streamzap *sz;
+	int errnum;
+	int minor;
+
+	sz = usb_get_intfdata(interface);
+
+	/*
+	 * unregister from the LIRC sub-system
+	 */
+
+	errnum = lirc_unregister_plugin(sz->plugin.minor);
+	if (errnum != 0)
+		dprintk("error in lirc_unregister: (returned %d)",
+			sz->plugin.minor, errnum);
+
+	lirc_buffer_free(&sz->delay_buf);
+	lirc_buffer_free(&sz->lirc_buf);
+
+	/*
+	 * unregister from the USB sub-system
+	 */
+
+	usb_free_urb(sz->urb_in);
+
+	usb_buffer_free(sz->udev, sz->buf_in_len, sz->buf_in, sz->dma_in);
+
+	minor = sz->plugin.minor;
+	kfree(sz);
+
+	printk(KERN_INFO DRIVER_NAME "[%d]: disconnected\n", minor);
+}
+
+static int streamzap_suspend(struct usb_interface *intf, pm_message_t message)
+{
+	struct usb_streamzap *sz = usb_get_intfdata(intf);
+
+	printk(DRIVER_NAME "[%d]: suspend\n", sz->plugin.minor);
+	if (sz->in_use) {
+		if (sz->flush) {
+			sz->flush = 0;
+			del_timer_sync(&sz->flush_timer);
+		}
+
+		stop_timer(sz);
+
+		usb_kill_urb(sz->urb_in);
+	}
+	return 0;
+}
+
+static int streamzap_resume(struct usb_interface *intf)
+{
+	struct usb_streamzap *sz = usb_get_intfdata(intf);
+
+	while (!lirc_buffer_empty(&sz->lirc_buf))
+		lirc_buffer_remove_1(&sz->lirc_buf);
+	while (!lirc_buffer_empty(&sz->delay_buf))
+		lirc_buffer_remove_1(&sz->delay_buf);
+
+	if (sz->in_use) {
+		sz->flush_timer.expires = jiffies + HZ;
+		sz->flush = 1;
+		add_timer(&sz->flush_timer);
+
+		sz->urb_in->dev = sz->udev;
+		if (usb_submit_urb(sz->urb_in, GFP_ATOMIC)) {
+			dprintk("open result = -EIO error submitting urb",
+				sz->plugin.minor);
+			return -EIO;
+		}
+	}
+	return 0;
+}
+
+#ifdef MODULE
+
+/**
+ *	usb_streamzap_init
+ */
+static int __init usb_streamzap_init(void)
+{
+	int result;
+
+	/* register this driver with the USB subsystem */
+
+	result = usb_register(&streamzap_driver);
+
+	if (result) {
+		err("usb_register failed. Error number %d",
+		    result);
+		return result;
+	}
+
+	printk(KERN_INFO DRIVER_NAME " " DRIVER_VERSION " registered\n");
+	return 0;
+}
+
+/**
+ *	usb_streamzap_exit
+ */
+static void __exit usb_streamzap_exit(void)
+{
+	/* deregister this driver with the USB subsystem */
+	usb_deregister(&streamzap_driver);
+}
+
+
+module_init(usb_streamzap_init);
+module_exit(usb_streamzap_exit);
+
+MODULE_AUTHOR("Christoph Bartelmus, Greg Wickham, Adrian Dewhurst");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+module_param(debug, bool, 0644);
+MODULE_PARM_DESC(debug, "Enable debugging messages");
+
+#endif /* MODULE */
diff --git a/drivers/input/lirc/lirc_ttusbir.c b/drivers/input/lirc/lirc_ttusbir.c
new file mode 100644
index 0000000..9ed9c7b
--- /dev/null
+++ b/drivers/input/lirc/lirc_ttusbir.c
@@ -0,0 +1,400 @@
+/****************************************************************************
+ ** lirc_ttusbir.c ***********************************************************
+ ****************************************************************************
+ *
+ * lirc_ttusbir - LIRC device driver for the TechnoTrend USB IR Receiver
+ *
+ * Copyright (C) 2007 Stefan Macher <st_maker-lirc@yahoo.de>
+ *
+ *  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
+ *
+ */
+
+/* This LIRC driver provides access to the TechnoTrend USB IR Receiver.
+ * The receiver delivers the IR signal as raw sampled true/false data in
+ * isochronous USB packets each of size 128 byte.
+ * Currently the driver reduces the sampling rate by factor of 8 as this
+ * is still more than enough to decode RC-5 - others should be analyzed.
+ * But the driver does not rely on RC-5 it should be able to decode every
+ * IR signal that is not too fast.
+ */
+
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+
+#include "lirc.h"
+#include "lirc_dev.h"
+
+MODULE_DESCRIPTION("TechnoTrend USB IR device driver for LIRC");
+MODULE_AUTHOR("Stefan Macher (st_maker-lirc@yahoo.de)");
+MODULE_LICENSE("GPL");
+
+/* #define DEBUG */
+#ifdef DEBUG
+#define DPRINTK printk
+#else
+#define DPRINTK(_x_, a...)
+#endif
+
+/* function declarations */
+static int probe(struct usb_interface *intf, const struct usb_device_id *id);
+static void disconnect(struct usb_interface *intf);
+static void urb_complete(struct urb *urb);
+static int set_use_inc(void *data);
+static void set_use_dec(void *data);
+
+static int num_urbs = 2;
+module_param(num_urbs, int, 0444);
+MODULE_PARM_DESC(num_urbs,
+		 "Number of URBs in queue. Try to increase to 4 in case "
+		 "of problems (default: 2; minimum: 2)");
+
+/* table of devices that work with this driver */
+static struct usb_device_id device_id_table[] = {
+	/* TechnoTrend USB IR Receiver */
+	{ USB_DEVICE(0x0B48, 0x2003) },
+	/* Terminating entry */
+	{ }
+};
+MODULE_DEVICE_TABLE(usb, device_id_table);
+
+/* USB driver definition */
+static struct usb_driver driver = {
+	.name = "TTUSBIR",
+	.id_table = &(device_id_table[0]),
+	.probe = probe,
+	.disconnect = disconnect,
+};
+
+/* USB device definition */
+struct ttusbir_device {
+	struct usb_driver *driver;
+	struct usb_device *udev;
+	struct usb_interface *interf;
+	struct usb_class_driver class_driver;
+	unsigned int ifnum; /* Interface number to use */
+	unsigned int alt_setting; /* alternate setting to use */
+	unsigned int endpoint; /* Endpoint to use */
+	struct urb **urb; /* num_urb URB pointers*/
+	char **buffer; /* 128 byte buffer for each URB */
+	struct lirc_buffer rbuf; /* Buffer towards LIRC */
+	struct lirc_plugin plugin;
+	int minor;
+	int last_pulse; /* remembers if last received byte was pulse or space */
+	int last_num; /* remembers how many last bytes appeared */
+	int opened;
+};
+
+/*************************************
+ * LIRC specific functions
+ */
+static int set_use_inc(void *data)
+{
+	int i;
+	struct ttusbir_device *ttusbir = data;
+
+	DPRINTK("Sending first URBs\n");
+	/* @TODO Do I need to check if I am already opened */
+	ttusbir->opened = 1;
+
+	for (i = 0; i < num_urbs; i++)
+		usb_submit_urb(ttusbir->urb[i], GFP_KERNEL);
+
+	return 0;
+}
+
+static void set_use_dec(void *data)
+{
+	struct ttusbir_device *ttusbir = data;
+
+	DPRINTK("Device closed\n");
+
+	ttusbir->opened = 0;
+}
+
+/*************************************
+ * USB specific functions
+ */
+
+/* This mapping table is used to do a very simple filtering of the
+ * input signal.
+ * For a value with at least 4 bits set it returns 0xFF otherwise
+ * 0x00.  For faster IR signals this can not be used. But for RC-5 we
+ * still have about 14 samples per pulse/space, i.e. we sample with 14
+ * times higher frequency than the signal frequency */
+const unsigned char map_table[] =
+{
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
+	0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
+	0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
+	0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
+	0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
+	0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
+	0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
+	0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
+	0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
+	0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
+	0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
+	0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
+	0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
+	0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+static void urb_complete(struct urb *urb)
+{
+	struct ttusbir_device *ttusbir;
+	unsigned char *buf;
+	int i;
+	int l;
+
+	ttusbir = urb->context;
+
+	if (!ttusbir->opened)
+		return;
+
+	buf = (unsigned char *)urb->transfer_buffer;
+
+	for (i = 0; i < 128; i++) {
+		/* Here we do the filtering and some kind of down sampling */
+		buf[i] = ~map_table[buf[i]];
+		if (ttusbir->last_pulse == buf[i]) {
+			if (ttusbir->last_num < PULSE_MASK/63)
+				ttusbir->last_num++;
+		/* else we are in a idle period and do not need to
+		 * increment any longer */
+		} else {
+			l = ttusbir->last_num * 62; /* about 62 = us/byte */
+			if (ttusbir->last_pulse) /* pulse or space? */
+				l |= PULSE_BIT;
+			if (!lirc_buffer_full(&ttusbir->rbuf)) {
+				lirc_buffer_write_1(&ttusbir->rbuf, (void *)&l);
+				wake_up_interruptible(&ttusbir->rbuf.wait_poll);
+			}
+			ttusbir->last_num = 0;
+			ttusbir->last_pulse = buf[i];
+		}
+	}
+	usb_submit_urb(urb, GFP_ATOMIC); /* keep data rolling :-) */
+}
+
+/* Called whenever the USB subsystem thinks we could be the right driver
+   to handle this device
+*/
+static int probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+	int alt_set, endp;
+	int found = 0;
+	int i, j;
+	int struct_size;
+	struct usb_host_interface *host_interf;
+	struct usb_interface_descriptor *interf_desc;
+	struct usb_host_endpoint *host_endpoint;
+	struct ttusbir_device *ttusbir;
+
+	DPRINTK("Module ttusbir probe\n");
+
+	/* To reduce memory fragmentation we use only one allocation */
+	struct_size =  sizeof(struct ttusbir_device) +
+		(sizeof(struct urb *) * num_urbs) +
+		(sizeof(char *) * num_urbs) +
+		(num_urbs * 128);
+	ttusbir = kmalloc(struct_size, GFP_KERNEL);
+	if (!ttusbir)
+		return -ENOMEM;
+	memset(ttusbir, 0, struct_size);
+
+	ttusbir->urb = (struct urb **)((char *)ttusbir +
+				      sizeof(struct ttusbir_device));
+	ttusbir->buffer = (char **)((char *)ttusbir->urb +
+				   (sizeof(struct urb *) * num_urbs));
+	for (i = 0; i < num_urbs; i++)
+		ttusbir->buffer[i] = (char *)ttusbir->buffer +
+			(sizeof(char *)*num_urbs) + (i * 128);
+
+	ttusbir->driver = &driver;
+	ttusbir->alt_setting = -1;
+	/* @TODO check if error can be returned */
+	ttusbir->udev = usb_get_dev(interface_to_usbdev(intf));
+	ttusbir->interf = intf;
+	ttusbir->last_pulse = 0x00;
+	ttusbir->last_num = 0;
+
+	/* Now look for interface setting we can handle
+	   We are searching for the alt setting where end point
+	   0x82 has max packet size 16
+	*/
+	for (alt_set = 0; alt_set < intf->num_altsetting && !found; alt_set++) {
+		host_interf = &intf->altsetting[alt_set];
+		interf_desc = &host_interf->desc;
+		for (endp = 0; endp < interf_desc->bNumEndpoints; endp++) {
+			host_endpoint = &host_interf->endpoint[endp];
+			if ((host_endpoint->desc.bEndpointAddress == 0x82) &&
+			    (host_endpoint->desc.wMaxPacketSize == 0x10)) {
+				ttusbir->alt_setting = alt_set;
+				ttusbir->endpoint = endp;
+				found = 1;
+				break;
+			}
+		}
+	}
+	if (ttusbir->alt_setting != -1)
+		DPRINTK("alt setting: %d\n", ttusbir->alt_setting);
+	else {
+		err("Could not find alternate setting\n");
+		kfree(ttusbir);
+		return -EINVAL;
+	}
+
+	/* OK lets setup this interface setting */
+	usb_set_interface(ttusbir->udev, 0, ttusbir->alt_setting);
+
+	/* Store device info in interface structure */
+	usb_set_intfdata(intf, ttusbir);
+
+	/* Register as a LIRC plugin */
+	if (lirc_buffer_init(&ttusbir->rbuf, sizeof(int), 256) < 0) {
+		err("Could not get memory for LIRC data buffer\n");
+		usb_set_intfdata(intf, NULL);
+		kfree(ttusbir);
+		return -ENOMEM;
+	}
+	strcpy(ttusbir->plugin.name, "TTUSBIR");
+	ttusbir->plugin.minor = -1;
+	ttusbir->plugin.code_length = 1;
+	ttusbir->plugin.sample_rate = 0;
+	ttusbir->plugin.data = ttusbir;
+	ttusbir->plugin.add_to_buf = NULL;
+	ttusbir->plugin.get_queue = NULL;
+	ttusbir->plugin.rbuf = &ttusbir->rbuf;
+	ttusbir->plugin.set_use_inc = set_use_inc;
+	ttusbir->plugin.set_use_dec = set_use_dec;
+	ttusbir->plugin.ioctl = NULL;
+	ttusbir->plugin.fops = NULL;
+	ttusbir->plugin.owner = THIS_MODULE;
+	ttusbir->plugin.features = LIRC_CAN_REC_MODE2;
+	ttusbir->minor = lirc_register_plugin(&ttusbir->plugin);
+	if (ttusbir->minor < 0) {
+		err("Error registering as LIRC plugin\n");
+		usb_set_intfdata(intf, NULL);
+		lirc_buffer_free(&ttusbir->rbuf);
+		kfree(ttusbir);
+		return -EIO;
+	}
+
+	/* Allocate and setup the URB that we will use to talk to the device */
+	for (i = 0; i < num_urbs; i++) {
+		ttusbir->urb[i] = usb_alloc_urb(8, GFP_KERNEL);
+		if (!ttusbir->urb[i]) {
+			err("Could not allocate memory for the URB\n");
+			for (j = i - 1; j >= 0; j--)
+				kfree(ttusbir->urb[j]);
+			lirc_buffer_free(&ttusbir->rbuf);
+			lirc_unregister_plugin(ttusbir->minor);
+			kfree(ttusbir);
+			usb_set_intfdata(intf, NULL);
+			return -ENOMEM;
+		}
+		ttusbir->urb[i]->dev = ttusbir->udev;
+		ttusbir->urb[i]->context = ttusbir;
+		ttusbir->urb[i]->pipe = usb_rcvisocpipe(ttusbir->udev,
+							ttusbir->endpoint);
+		ttusbir->urb[i]->interval = 1;
+		ttusbir->urb[i]->transfer_flags = URB_ISO_ASAP;
+		ttusbir->urb[i]->transfer_buffer = &ttusbir->buffer[i][0];
+		ttusbir->urb[i]->complete = urb_complete;
+		ttusbir->urb[i]->number_of_packets = 8;
+		ttusbir->urb[i]->transfer_buffer_length = 128;
+		for (j = 0; j < 8; j++) {
+			ttusbir->urb[i]->iso_frame_desc[j].offset = j*16;
+			ttusbir->urb[i]->iso_frame_desc[j].length = 16;
+		}
+	}
+	return 0;
+}
+
+/* Called when the driver is unloaded or the device is unplugged
+ */
+static void disconnect(struct usb_interface *intf)
+{
+	int i;
+	struct ttusbir_device *ttusbir;
+
+	DPRINTK("Module ttusbir disconnect\n");
+
+	ttusbir = (struct ttusbir_device *) usb_get_intfdata(intf);
+	usb_set_intfdata(intf, NULL);
+	lirc_unregister_plugin(ttusbir->minor);
+	DPRINTK("unregistered\n");
+
+	for (i = 0; i < num_urbs; i++) {
+		usb_kill_urb(ttusbir->urb[i]);
+		usb_free_urb(ttusbir->urb[i]);
+	}
+	DPRINTK("URBs killed\n");
+	lirc_buffer_free(&ttusbir->rbuf);
+	kfree(ttusbir);
+}
+
+static int ttusbir_init_module(void)
+{
+	int result;
+
+	DPRINTK(KERN_DEBUG "Module ttusbir init\n");
+
+	/* register this driver with the USB subsystem */
+	result = usb_register(&driver);
+	if (result)
+		err("usb_register failed. Error number %d", result);
+	return result;
+}
+
+static void ttusbir_exit_module(void)
+{
+	printk(KERN_DEBUG "Module ttusbir exit\n");
+	/* deregister this driver with the USB subsystem */
+	usb_deregister(&driver);
+}
+
+#ifdef MODULE
+module_init(ttusbir_init_module);
+module_exit(ttusbir_exit_module);
+
+#else
+subsys_initcall(ttusbir_init_module);
+
+#endif /* MODULE */
diff --git a/drivers/input/lirc/lirc_zilog.c b/drivers/input/lirc/lirc_zilog.c
new file mode 100644
index 0000000..20e6b27
--- /dev/null
+++ b/drivers/input/lirc/lirc_zilog.c
@@ -0,0 +1,1395 @@
+/*
+ * i2c IR lirc plugin for devices with zilog IR processors
+ *
+ * Copyright (c) 2000 Gerd Knorr <kraxel@goldbach.in-berlin.de>
+ * modified for PixelView (BT878P+W/FM) by
+ *      Michal Kochanowicz <mkochano@pld.org.pl>
+ *      Christoph Bartelmus <lirc@bartelmus.de>
+ * modified for KNC ONE TV Station/Anubis Typhoon TView Tuner by
+ *      Ulrich Mueller <ulrich.mueller42@web.de>
+ * modified for Asus TV-Box and Creative/VisionTek BreakOut-Box by
+ *      Stefan Jahn <stefan@lkcc.org>
+ * modified for inclusion into kernel sources by
+ *      Jerome Brock <jbrock@users.sourceforge.net>
+ * modified for Leadtek Winfast PVR2000 by
+ *      Thomas Reitmayr (treitmayr@yahoo.com)
+ * modified for Hauppauge PVR-150 IR TX device by
+ *      Mark Weaver <mark@npsl.co.uk>
+ * changed name from lirc_pvr150 to lirc_zilog, works on more than pvr-150
+ *	Jarod Wilson <jarod@redhat.com>
+ *
+ * parts are cut&pasted from the lirc_i2c.c driver
+ *
+ *  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>
+#include <linux/module.h>
+#include <linux/kmod.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/poll.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/delay.h>
+#include <linux/completion.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include <linux/firmware.h>
+#include <linux/vmalloc.h>
+
+#include <linux/mutex.h>
+#include <linux/kthread.h>
+
+#include "lirc_dev.h"
+#include "lirc.h"
+
+struct IR {
+	struct lirc_plugin l;
+
+	/* Device info */
+	struct mutex       lock;
+	int		   open;
+
+	/* RX device */
+	struct i2c_client  c_rx;
+
+	/* RX device buffer & lock */
+	struct lirc_buffer buf;
+	struct mutex       buf_lock;
+
+	/* RX polling thread data */
+	struct completion  *t_notify;
+	struct completion  *t_notify2;
+	int		   shutdown;
+	struct task_struct *task;
+
+	/* RX read data */
+	unsigned char      b[3];
+
+	/* TX device */
+	struct i2c_client  c_tx;
+	int		   need_boot;
+
+	/* # devices, for shutdown */
+	int		   devs;
+};
+
+/* Minor -> data mapping */
+static struct IR *ir_devices[MAX_IRCTL_DEVICES];
+
+/* Block size for IR transmitter */
+#define TX_BLOCK_SIZE	99
+
+/* Hauppuage IR transmitter data */
+struct tx_data_struct {
+	/* Boot block */
+	unsigned char *boot_data;
+
+	/* Start of binary data block */
+	unsigned char *datap;
+
+	/* End of binary data block */
+	unsigned char *endp;
+
+	/* Number of installed codesets */
+	unsigned int num_code_sets;
+
+	/* Pointers to codesets */
+	unsigned char **code_sets;
+
+	/* Global fixed data template */
+	int fixed[TX_BLOCK_SIZE];
+};
+
+static struct tx_data_struct *tx_data;
+struct mutex tx_data_lock;
+
+/* ----------------------------------------------------------------------- */
+
+#define DEVICE_NAME "lirc_zilog"
+#define zilog_notify(s, args...) printk(KERN_NOTICE KBUILD_MODNAME ": " s, \
+					## args)
+#define zilog_error(s, args...) printk(KERN_ERR KBUILD_MODNAME ": " s, ## args)
+
+/* ----------------------------------------------------------------------- */
+/* insmod parameters						       */
+
+static int debug;	/* debug output */
+static int disable_rx;	/* disable RX device */
+static int disable_tx;	/* disable TX device */
+static int minor = -1;	/* minor number */
+
+#define dprintk(fmt, args...)						\
+	do {								\
+		if (debug)						\
+			printk(KERN_DEBUG DEVICE_NAME ": " fmt,		\
+				 ## args);				\
+	} while (0)
+
+/* ----------------------------------------------------------------------- */
+
+static int add_to_buf(struct IR *ir)
+{
+	__u16 code;
+	unsigned char codes[2];
+	unsigned char keybuf[6];
+	int got_data = 0;
+	int ret;
+	int failures = 0;
+	unsigned char sendbuf[1] = { 0 };
+
+	if (lirc_buffer_full(&ir->buf)) {
+		dprintk("buffer overflow\n");
+		return -EOVERFLOW;
+	}
+
+	/* service the device as long as it is returning
+	 * data and we have space
+	 */
+	do {
+		/* Lock i2c bus for the duration.  RX/TX chips interfere so
+		   this is worth it
+		 */
+		mutex_lock(&ir->lock);
+
+		/* Send random "poll command" (?)  Windows driver does this
+		   and it  is a good point to detect chip failure.
+		 */
+		ret = i2c_master_send(&ir->c_rx, sendbuf, 1);
+		if (ret != 1) {
+			zilog_error("i2c_master_send failed with %d\n",	ret);
+			if (failures >= 3) {
+				mutex_unlock(&ir->lock);
+				zilog_error("unable to read from the IR chip "
+					    "after 3 resets, giving up\n");
+				return ret;
+			}
+
+			/* Looks like the chip crashed, reset it */
+			zilog_error("polling the IR receiver chip failed, "
+				    "trying reset\n");
+
+			set_current_state(TASK_UNINTERRUPTIBLE);
+			schedule_timeout((100 * HZ + 999) / 1000);
+			ir->need_boot = 1;
+
+			++failures;
+			mutex_unlock(&ir->lock);
+			continue;
+		}
+
+		ret = i2c_master_recv(&ir->c_rx, keybuf, sizeof(keybuf));
+		mutex_unlock(&ir->lock);
+		if (ret != sizeof(keybuf)) {
+			zilog_error("i2c_master_recv failed with %d -- "
+				    "keeping last read buffer\n", ret);
+		} else {
+			ir->b[0] = keybuf[3];
+			ir->b[1] = keybuf[4];
+			ir->b[2] = keybuf[5];
+			dprintk("key (0x%02x/0x%02x)\n", ir->b[0], ir->b[1]);
+		}
+
+		/* key pressed ? */
+		if ((ir->b[0] & 0x80) == 0)
+			return got_data ? 0 : -ENODATA;
+
+		/* look what we have */
+		code = (((__u16)ir->b[0]&0x7f)<<6) | (ir->b[1]>>2);
+
+		codes[0] = (code >> 8) & 0xff;
+		codes[1] = code & 0xff;
+
+		/* return it */
+		lirc_buffer_write_1(&ir->buf, codes);
+		++got_data;
+	} while (!lirc_buffer_full(&ir->buf));
+	return 0;
+}
+
+/* Main function of the polling thread -- from lirc_dev.
+ * We don't fit the LIRC model at all anymore.  This is horrible, but
+ * basically we have a single RX/TX device with a nasty failure mode
+ * that needs to be accounted for across the pair.  lirc lets us provide
+ * fops, but prevents us from using the internal polling, etc. if we do
+ * so.  Hence the replication.  Might be neater to extend the LIRC model
+ * to account for this but I'd think it's a very special case of seriously
+ * messed up hardware.
+ */
+static int lirc_thread(void *arg)
+{
+	struct IR *ir = arg;
+
+	if (ir->t_notify != NULL)
+		complete(ir->t_notify);
+
+	dprintk("poll thread started\n");
+
+	do {
+		if (ir->open) {
+			set_current_state(TASK_INTERRUPTIBLE);
+
+			/* This is ~113*2 + 24 + jitter (2*repeat gap +
+			   code length).  We use this interval as the chip
+			   resets every time you poll it (bad!).  This is
+			   therefore just sufficient to catch all of the
+			   button presses.  It makes the remote much more
+			   responsive.  You can see the difference by
+			   running irw and holding down a button.  With
+			   100ms, the old polling interval, you'll notice
+			   breaks in the repeat sequence corresponding to
+			   lost keypresses.
+			*/
+			schedule_timeout((260 * HZ) / 1000);
+			if (ir->shutdown)
+				break;
+			if (!add_to_buf(ir))
+				wake_up_interruptible(&ir->buf.wait_poll);
+		} else {
+			/* if device not opened so we can sleep half a second */
+			set_current_state(TASK_INTERRUPTIBLE);
+			schedule_timeout(HZ/2);
+		}
+	} while (!ir->shutdown);
+
+	if (ir->t_notify2 != NULL)
+		wait_for_completion(ir->t_notify2);
+
+	ir->task = NULL;
+	if (ir->t_notify != NULL)
+		complete(ir->t_notify);
+
+	dprintk("poll thread ended\n");
+	return 0;
+}
+
+static int set_use_inc(void *data)
+{
+	struct IR *ir = data;
+
+	if (ir->l.owner == NULL || try_module_get(ir->l.owner) == 0)
+		return -ENODEV;
+
+	/* lock bttv in memory while /dev/lirc is in use  */
+	/* this is completely broken code. lirc_unregister_plugin()
+	   must be possible even when the device is open */
+	if (ir->c_rx.addr)
+		i2c_use_client(&ir->c_rx);
+	if (ir->c_tx.addr)
+		i2c_use_client(&ir->c_tx);
+
+	return 0;
+}
+
+static void set_use_dec(void *data)
+{
+	struct IR *ir = data;
+
+	if (ir->c_rx.addr)
+		i2c_release_client(&ir->c_rx);
+	if (ir->c_tx.addr)
+		i2c_release_client(&ir->c_tx);
+	if (ir->l.owner != NULL)
+		module_put(ir->l.owner);
+}
+
+/* safe read of a uint32 (always network byte order) */
+static inline int read_uint32(unsigned char **data,
+				     unsigned char *endp, unsigned int *val)
+{
+	if (*data + 4 > endp)
+		return 0;
+	*val = ((*data)[0] << 24) | ((*data)[1] << 16) |
+	       ((*data)[2] << 8) | (*data)[3];
+	*data += 4;
+	return 1;
+}
+
+/* safe read of a uint8 */
+static inline int read_uint8(unsigned char **data,
+				    unsigned char *endp, unsigned char *val)
+{
+	if (*data + 1 > endp)
+		return 0;
+	*val = *((*data)++);
+	return 1;
+}
+
+/* safe skipping of N bytes */
+static inline int skip(unsigned char **data,
+			      unsigned char *endp, unsigned int distance)
+{
+	if (*data + distance > endp)
+		return 0;
+	*data += distance;
+	return 1;
+}
+
+/* decompress key data into the given buffer */
+static int get_key_data(unsigned char *buf,
+			     unsigned int codeset, unsigned int key)
+{
+	unsigned char *data, *endp, *diffs, *key_block;
+	unsigned char keys, ndiffs, id;
+	unsigned int base, lim, pos, i;
+
+	/* Binary search for the codeset */
+	for (base = 0, lim = tx_data->num_code_sets; lim; lim >>= 1) {
+		pos = base + (lim >> 1);
+		data = tx_data->code_sets[pos];
+
+		if (!read_uint32(&data, tx_data->endp, &i))
+			goto corrupt;
+
+		if (i == codeset)
+			break;
+		else if (codeset > i) {
+			base = pos + 1;
+			--lim;
+		}
+	}
+	/* Not found? */
+	if (!lim)
+		return -EPROTO;
+
+	/* Set end of data block */
+	endp = pos < tx_data->num_code_sets - 1 ?
+		tx_data->code_sets[pos + 1] : tx_data->endp;
+
+	/* Read the block header */
+	if (!read_uint8(&data, endp, &keys) ||
+	    !read_uint8(&data, endp, &ndiffs) ||
+	    ndiffs > TX_BLOCK_SIZE || keys == 0)
+		goto corrupt;
+
+	/* Save diffs & skip */
+	diffs = data;
+	if (!skip(&data, endp, ndiffs))
+		goto corrupt;
+
+	/* Read the id of the first key */
+	if (!read_uint8(&data, endp, &id))
+		goto corrupt;
+
+	/* Unpack the first key's data */
+	for (i = 0; i < TX_BLOCK_SIZE; ++i) {
+		if (tx_data->fixed[i] == -1) {
+			if (!read_uint8(&data, endp, &buf[i]))
+				goto corrupt;
+		} else {
+			buf[i] = (unsigned char)tx_data->fixed[i];
+		}
+	}
+
+	/* Early out key found/not found */
+	if (key == id)
+		return 0;
+	if (keys == 1)
+		return -EPROTO;
+
+	/* Sanity check */
+	key_block = data;
+	if (!skip(&data, endp, (keys - 1) * (ndiffs + 1)))
+		goto corrupt;
+
+	/* Binary search for the key */
+	for (base = 0, lim = keys - 1; lim; lim >>= 1) {
+		/* Seek to block */
+		unsigned char *key_data;
+		pos = base + (lim >> 1);
+		key_data = key_block + (ndiffs + 1) * pos;
+
+		if (*key_data == key) {
+			/* skip key id */
+			++key_data;
+
+			/* found, so unpack the diffs */
+			for (i = 0; i < ndiffs; ++i) {
+				unsigned char val;
+				if (!read_uint8(&key_data, endp, &val) ||
+				    diffs[i] >= TX_BLOCK_SIZE)
+					goto corrupt;
+				buf[diffs[i]] = val;
+			}
+
+			return 0;
+		} else if (key > *key_data) {
+			base = pos + 1;
+			--lim;
+		}
+	}
+	/* Key not found */
+	return -EPROTO;
+
+corrupt:
+	zilog_error("firmware is corrupt\n");
+	return -EFAULT;
+}
+
+/* send a block of data to the IR TX device */
+static int send_data_block(struct IR *ir, unsigned char *data_block)
+{
+	int i, j, ret;
+	unsigned char buf[5];
+
+	for (i = 0; i < TX_BLOCK_SIZE;) {
+		int tosend = TX_BLOCK_SIZE - i;
+		if (tosend > 4)
+			tosend = 4;
+		buf[0] = (unsigned char)(i + 1);
+		for (j = 0; j < tosend; ++j)
+			buf[1 + j] = data_block[i + j];
+		dprintk("%02x %02x %02x %02x %02x",
+			buf[0], buf[1], buf[2], buf[3], buf[4]);
+		ret = i2c_master_send(&ir->c_tx, buf, tosend + 1);
+		if (ret != tosend + 1) {
+			zilog_error("i2c_master_send failed with %d\n", ret);
+			return ret < 0 ? ret : -EFAULT;
+		}
+		i += tosend;
+	}
+	return 0;
+}
+
+/* send boot data to the IR TX device */
+static int send_boot_data(struct IR *ir)
+{
+	int ret;
+	unsigned char buf[4];
+
+	/* send the boot block */
+	ret = send_data_block(ir, tx_data->boot_data);
+	if (ret != 0)
+		return ret;
+
+	/* kick it off? */
+	buf[0] = 0x00;
+	buf[1] = 0x20;
+	ret = i2c_master_send(&ir->c_tx, buf, 2);
+	if (ret != 2) {
+		zilog_error("i2c_master_send failed with %d\n", ret);
+		return ret < 0 ? ret : -EFAULT;
+	}
+	ret = i2c_master_send(&ir->c_tx, buf, 1);
+	if (ret != 1) {
+		zilog_error("i2c_master_send failed with %d\n", ret);
+		return ret < 0 ? ret : -EFAULT;
+	}
+
+	/* Here comes the firmware version... (hopefully) */
+	ret = i2c_master_recv(&ir->c_tx, buf, 4);
+	if (ret != 4) {
+		zilog_error("i2c_master_recv failed with %d\n", ret);
+		return 0;
+	}
+	if (buf[0] != 0x80) {
+		zilog_error("unexpected IR TX response: %02x\n", buf[0]);
+		return 0;
+	}
+	zilog_notify("Zilog/Hauppauge IR blaster: firmware version "
+		     "%d.%d.%d\n", buf[1], buf[2], buf[3]);
+
+	return 0;
+}
+
+/* unload "firmware", lock held */
+static void fw_unload_locked(void)
+{
+	if (tx_data) {
+		if (tx_data->code_sets)
+			vfree(tx_data->code_sets);
+
+		if (tx_data->datap)
+			vfree(tx_data->datap);
+
+		vfree(tx_data);
+		tx_data = NULL;
+		dprintk("successfully unloaded IR blaster firmware\n");
+	}
+}
+
+/* unload "firmware" for the IR TX device */
+static void fw_unload(void)
+{
+	mutex_lock(&tx_data_lock);
+	fw_unload_locked();
+	mutex_unlock(&tx_data_lock);
+}
+
+/* load "firmware" for the IR TX device */
+static int fw_load(struct IR *ir)
+{
+	int ret;
+	unsigned int i;
+	unsigned char *data, version, num_global_fixed;
+	const struct firmware *fw_entry = NULL;
+
+	/* Already loaded? */
+	mutex_lock(&tx_data_lock);
+	if (tx_data) {
+		ret = 0;
+		goto out;
+	}
+
+	/* Request codeset data file */
+	ret = request_firmware(&fw_entry, "haup-ir-blaster.bin", &ir->c_tx.dev);
+	if (ret != 0) {
+		zilog_error("firmware haup-ir-blaster.bin not available "
+			    "(%d)\n", ret);
+		ret = ret < 0 ? ret : -EFAULT;
+		goto out;
+	}
+	zilog_notify("firmware of size %zu loaded\n", fw_entry->size);
+
+	/* Parse the file */
+	tx_data = vmalloc(sizeof(*tx_data));
+	if (tx_data == NULL) {
+		zilog_error("out of memory\n");
+		release_firmware(fw_entry);
+		ret = -ENOMEM;
+		goto out;
+	}
+	tx_data->code_sets = NULL;
+
+	/* Copy the data so hotplug doesn't get confused and timeout */
+	tx_data->datap = vmalloc(fw_entry->size);
+	if (tx_data->datap == NULL) {
+		zilog_error("out of memory\n");
+		release_firmware(fw_entry);
+		vfree(tx_data);
+		ret = -ENOMEM;
+		goto out;
+	}
+	memcpy(tx_data->datap, fw_entry->data, fw_entry->size);
+	tx_data->endp = tx_data->datap + fw_entry->size;
+	release_firmware(fw_entry); fw_entry = NULL;
+
+	/* Check version */
+	data = tx_data->datap;
+	if (!read_uint8(&data, tx_data->endp, &version))
+		goto corrupt;
+	if (version != 1) {
+		zilog_error("unsupported code set file version (%u, expected"
+			    "1) -- please upgrade to a newer driver",
+			    version);
+		fw_unload_locked();
+		ret = -EFAULT;
+		goto out;
+	}
+
+	/* Save boot block for later */
+	tx_data->boot_data = data;
+	if (!skip(&data, tx_data->endp, TX_BLOCK_SIZE))
+		goto corrupt;
+
+	if (!read_uint32(&data, tx_data->endp,
+			      &tx_data->num_code_sets))
+		goto corrupt;
+
+	zilog_notify("%u codesets loaded\n", tx_data->num_code_sets);
+
+	tx_data->code_sets = vmalloc(
+		tx_data->num_code_sets * sizeof(char *));
+	if (tx_data->code_sets == NULL) {
+		fw_unload_locked();
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	for (i = 0; i < TX_BLOCK_SIZE; ++i)
+		tx_data->fixed[i] = -1;
+
+	/* Read global fixed data template */
+	if (!read_uint8(&data, tx_data->endp, &num_global_fixed) ||
+	    num_global_fixed > TX_BLOCK_SIZE)
+		goto corrupt;
+	for (i = 0; i < num_global_fixed; ++i) {
+		unsigned char pos, val;
+		if (!read_uint8(&data, tx_data->endp, &pos) ||
+		    !read_uint8(&data, tx_data->endp, &val) ||
+		    pos >= TX_BLOCK_SIZE)
+			goto corrupt;
+		tx_data->fixed[pos] = (int)val;
+	}
+
+	/* Filch out the position of each code set */
+	for (i = 0; i < tx_data->num_code_sets; ++i) {
+		unsigned int id;
+		unsigned char keys;
+		unsigned char ndiffs;
+
+		/* Save the codeset position */
+		tx_data->code_sets[i] = data;
+
+		/* Read header */
+		if (!read_uint32(&data, tx_data->endp, &id) ||
+		    !read_uint8(&data, tx_data->endp, &keys) ||
+		    !read_uint8(&data, tx_data->endp, &ndiffs) ||
+		    ndiffs > TX_BLOCK_SIZE || keys == 0)
+			goto corrupt;
+
+		/* skip diff positions */
+		if (!skip(&data, tx_data->endp, ndiffs))
+			goto corrupt;
+
+		/* After the diffs we have the first key id + data -
+		   global fixed */
+		if (!skip(&data, tx_data->endp,
+			       1 + TX_BLOCK_SIZE - num_global_fixed))
+			goto corrupt;
+
+		/* Then we have keys-1 blocks of key id+diffs */
+		if (!skip(&data, tx_data->endp,
+			       (ndiffs + 1) * (keys - 1)))
+			goto corrupt;
+	}
+	ret = 0;
+	goto out;
+
+corrupt:
+	zilog_error("firmware is corrupt\n");
+	fw_unload_locked();
+	ret = -EFAULT;
+
+out:
+	mutex_unlock(&tx_data_lock);
+	return ret;
+}
+
+/* initialise the IR TX device */
+static int tx_init(struct IR *ir)
+{
+	int ret;
+
+	/* Load 'firmware' */
+	ret = fw_load(ir);
+	if (ret != 0)
+		return ret;
+
+	/* Send boot block */
+	ret = send_boot_data(ir);
+	if (ret != 0)
+		return ret;
+	ir->need_boot = 0;
+
+	/* Looks good */
+	return 0;
+}
+
+/* do nothing stub to make LIRC happy */
+static loff_t lseek(struct file *filep, loff_t offset, int orig)
+{
+	return -ESPIPE;
+}
+
+/* copied from lirc_dev */
+static ssize_t read(struct file *filep, char *outbuf, size_t n, loff_t *ppos)
+{
+	struct IR *ir = (struct IR *)filep->private_data;
+	unsigned char buf[ir->buf.chunk_size];
+	int ret = 0, written = 0;
+	DECLARE_WAITQUEUE(wait, current);
+
+	dprintk("read called\n");
+	if (ir->c_rx.addr == 0)
+		return -ENODEV;
+
+	if (mutex_lock_interruptible(&ir->buf_lock))
+		return -ERESTARTSYS;
+
+	if (n % ir->buf.chunk_size) {
+		dprintk("read result = -EINVAL\n");
+		mutex_unlock(&ir->buf_lock);
+		return -EINVAL;
+	}
+
+	/* we add ourselves to the task queue before buffer check
+	 * to avoid losing scan code (in case when queue is awaken somewhere
+	 * beetwen while condition checking and scheduling)
+	 */
+	add_wait_queue(&ir->buf.wait_poll, &wait);
+	set_current_state(TASK_INTERRUPTIBLE);
+
+	/* while we did't provide 'length' bytes, device is opened in blocking
+	 * mode and 'copy_to_user' is happy, wait for data.
+	 */
+	while (written < n && ret == 0) {
+		if (lirc_buffer_empty(&ir->buf)) {
+			/* According to the read(2) man page, 'written' can be
+			 * returned as less than 'n', instead of blocking
+			 * again, returning -EWOULDBLOCK, or returning
+			 * -ERESTARTSYS */
+			if (written)
+				break;
+			if (filep->f_flags & O_NONBLOCK) {
+				ret = -EWOULDBLOCK;
+				break;
+			}
+			if (signal_pending(current)) {
+				ret = -ERESTARTSYS;
+				break;
+			}
+			schedule();
+			set_current_state(TASK_INTERRUPTIBLE);
+		} else {
+			lirc_buffer_read_1(&ir->buf, buf);
+			ret = copy_to_user((void *)outbuf+written, buf,
+					   ir->buf.chunk_size);
+			written += ir->buf.chunk_size;
+		}
+	}
+
+	remove_wait_queue(&ir->buf.wait_poll, &wait);
+	set_current_state(TASK_RUNNING);
+	mutex_unlock(&ir->buf_lock);
+
+	dprintk("read result = %s (%d)\n",
+		ret ? "-EFAULT" : "OK", ret);
+
+	return ret ? ret : written;
+}
+
+/* send a keypress to the IR TX device */
+static int send_code(struct IR *ir, unsigned int code, unsigned int key)
+{
+	unsigned char data_block[TX_BLOCK_SIZE];
+	unsigned char buf[2];
+	int i, ret;
+
+	/* Get data for the codeset/key */
+	ret = get_key_data(data_block, code, key);
+
+	if (ret == -EPROTO) {
+		zilog_error("failed to get data for code %u, key %u -- check "
+			    "lircd.conf entries\n", code, key);
+		return ret;
+	} else if (ret != 0)
+		return ret;
+
+	/* Send the data block */
+	ret = send_data_block(ir, data_block);
+	if (ret != 0)
+		return ret;
+
+	/* Send data block length? */
+	buf[0] = 0x00;
+	buf[1] = 0x40;
+	ret = i2c_master_send(&ir->c_tx, buf, 2);
+	if (ret != 2) {
+		zilog_error("i2c_master_send failed with %d\n", ret);
+		return ret < 0 ? ret : -EFAULT;
+	}
+	ret = i2c_master_send(&ir->c_tx, buf, 1);
+	if (ret != 1) {
+		zilog_error("i2c_master_send failed with %d\n", ret);
+		return ret < 0 ? ret : -EFAULT;
+	}
+
+	/* Send finished download? */
+	ret = i2c_master_recv(&ir->c_tx, buf, 1);
+	if (ret != 1) {
+		zilog_error("i2c_master_recv failed with %d\n", ret);
+		return ret < 0 ? ret : -EFAULT;
+	}
+	if (buf[0] != 0xA0) {
+		zilog_error("unexpected IR TX response #1: %02x\n",
+			buf[0]);
+		return ret < 0 ? ret : -EFAULT;
+	}
+
+	/* Send prepare command? */
+	buf[0] = 0x00;
+	buf[1] = 0x80;
+	ret = i2c_master_send(&ir->c_tx, buf, 2);
+	if (ret != 2) {
+		zilog_error("i2c_master_send failed with %d\n", ret);
+		return ret < 0 ? ret : -EFAULT;
+	}
+
+	/* This bit NAKs until the device is ready, so we retry it
+	   sleeping a bit each time.  This seems to be what the windows
+	   driver does, approximately.
+	   Try for up to 1s.
+	*/
+	for (i = 0; i < 20; ++i) {
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout((50 * HZ + 999) / 1000);
+		ret = i2c_master_send(&ir->c_tx, buf, 1);
+		if (ret == 1)
+			break;
+		dprintk("NAK expected: i2c_master_send "
+			"failed with %d (try %d)\n", ret, i+1);
+	}
+	if (ret != 1) {
+		zilog_error("IR TX chip never got ready: last i2c_master_send "
+			    "failed with %d\n", ret);
+		return ret < 0 ? ret : -EFAULT;
+	}
+
+	/* Seems to be an 'ok' response */
+	i = i2c_master_recv(&ir->c_tx, buf, 1);
+	if (i != 1) {
+		zilog_error("i2c_master_recv failed with %d\n", ret);
+		return ret < 0 ? ret : -EFAULT;
+	}
+	if (buf[0] != 0x80) {
+		zilog_error("unexpected IR TX response #2: %02x\n", buf[0]);
+		return -EFAULT;
+	}
+
+	/* Oh good, it worked */
+	dprintk("sent code %u, key %u\n", code, key);
+	return 0;
+}
+
+/*
+ * Write a code to the device.  We take in a 32-bit number (an int) and then
+ * decode this to a codeset/key index.  The key data is then decompressed and
+ * sent to the device.  We have a spin lock as per i2c documentation to prevent
+ * multiple concurrent sends which would probably cause the device to explode.
+ */
+static ssize_t write(struct file *filep, const char *buf, size_t n,
+			  loff_t *ppos)
+{
+	struct IR *ir = (struct IR *)filep->private_data;
+	size_t i;
+	int failures = 0;
+
+	if (ir->c_tx.addr == 0)
+		return -ENODEV;
+
+	/* Validate user parameters */
+	if (n % sizeof(int))
+		return -EINVAL;
+
+	/* Lock i2c bus for the duration */
+	mutex_lock(&ir->lock);
+
+	/* Send each keypress */
+	for (i = 0; i < n;) {
+		int ret = 0;
+		int command;
+
+		if (copy_from_user(&command, buf + i, sizeof(command))) {
+			mutex_unlock(&ir->lock);
+			return -EFAULT;
+		}
+
+		/* Send boot data first if required */
+		if (ir->need_boot == 1) {
+			ret = send_boot_data(ir);
+			if (ret == 0)
+				ir->need_boot = 0;
+		}
+
+		/* Send the code */
+		if (ret == 0) {
+			ret = send_code(ir, (unsigned)command >> 16,
+					    (unsigned)command & 0xFFFF);
+			if (ret == -EPROTO) {
+				mutex_unlock(&ir->lock);
+				return ret;
+			}
+		}
+
+		/* Hmm, a failure.  If we've had a few then give up, otherwise
+		   try a reset
+		 */
+		if (ret != 0) {
+			/* Looks like the chip crashed, reset it */
+			zilog_error("sending to the IR transmitter chip "
+				    "failed, trying reset\n");
+
+			if (failures >= 3) {
+				zilog_error("unable to send to the IR chip "
+					    "after 3 resets, giving up\n");
+				mutex_unlock(&ir->lock);
+				return ret;
+			}
+			set_current_state(TASK_UNINTERRUPTIBLE);
+			schedule_timeout((100 * HZ + 999) / 1000);
+			ir->need_boot = 1;
+			++failures;
+		} else
+			i += sizeof(int);
+	}
+
+	/* Release i2c bus */
+	mutex_unlock(&ir->lock);
+
+	/* All looks good */
+	return n;
+}
+
+/* copied from lirc_dev */
+static unsigned int poll(struct file *filep, poll_table *wait)
+{
+	struct IR *ir = (struct IR *)filep->private_data;
+	unsigned int ret;
+
+	dprintk("poll called\n");
+	if (ir->c_rx.addr == 0)
+		return -ENODEV;
+
+	mutex_lock(&ir->buf_lock);
+
+	poll_wait(filep, &ir->buf.wait_poll, wait);
+
+	dprintk("poll result = %s\n",
+		lirc_buffer_empty(&ir->buf) ? "0" : "POLLIN|POLLRDNORM");
+
+	ret = lirc_buffer_empty(&ir->buf) ? 0 : (POLLIN|POLLRDNORM);
+
+	mutex_unlock(&ir->buf_lock);
+	return ret;
+}
+
+static int ioctl(struct inode *node, struct file *filep, unsigned int cmd,
+		      unsigned long arg)
+{
+	struct IR *ir = (struct IR *)filep->private_data;
+	int result;
+	unsigned long mode, features = 0;
+
+	if (ir->c_rx.addr != 0)
+		features |= LIRC_CAN_REC_LIRCCODE;
+	if (ir->c_tx.addr != 0)
+		features |= LIRC_CAN_SEND_PULSE;
+
+	switch (cmd) {
+	case LIRC_GET_LENGTH:
+		result = put_user((unsigned long)13,
+				  (unsigned long *)arg);
+		break;
+	case LIRC_GET_FEATURES:
+		result = put_user(features, (unsigned long *) arg);
+		if (result)
+			return result;
+		break;
+	case LIRC_GET_REC_MODE:
+		if (!(features&LIRC_CAN_REC_MASK))
+			return -ENOSYS;
+
+		result = put_user(LIRC_REC2MODE
+				  (features&LIRC_CAN_REC_MASK),
+				  (unsigned long *)arg);
+		break;
+	case LIRC_SET_REC_MODE:
+		if (!(features&LIRC_CAN_REC_MASK))
+			return -ENOSYS;
+
+		result = get_user(mode, (unsigned long *)arg);
+		if (!result && !(LIRC_MODE2REC(mode) & features))
+			result = -EINVAL;
+		break;
+	case LIRC_GET_SEND_MODE:
+		if (!(features&LIRC_CAN_SEND_MASK))
+			return -ENOSYS;
+
+		result = put_user(LIRC_MODE_PULSE, (unsigned long *) arg);
+		if (result)
+			return result;
+		break;
+	case LIRC_SET_SEND_MODE:
+		if (!(features&LIRC_CAN_SEND_MASK))
+			return -ENOSYS;
+
+		result = get_user(mode, (unsigned long *) arg);
+		if (result)
+			return result;
+		if (mode != LIRC_MODE_PULSE)
+			return -EINVAL;
+		break;
+	default:
+		return -ENOIOCTLCMD;
+	}
+	return 0;
+}
+
+/* Open the IR device.  Get hold of our IR structure and
+   stash it in private_data for the file */
+static int open(struct inode *node, struct file *filep)
+{
+	struct IR *ir;
+	int ret;
+
+	/* find our IR struct */
+	unsigned minor = MINOR(node->i_rdev);
+	if (minor >= MAX_IRCTL_DEVICES) {
+		dprintk("minor %d: open result = -ENODEV\n",
+			minor);
+		return -ENODEV;
+	}
+	ir = ir_devices[minor];
+
+	/* increment in use count */
+	mutex_lock(&ir->lock);
+	++ir->open;
+	ret = set_use_inc(ir);
+	if (ret != 0) {
+		--ir->open;
+		mutex_unlock(&ir->lock);
+		return ret;
+	}
+	mutex_unlock(&ir->lock);
+
+	/* stash our IR struct */
+	filep->private_data = ir;
+
+	return 0;
+}
+
+/* Close the IR device */
+static int close(struct inode *node, struct file *filep)
+{
+	/* find our IR struct */
+	struct IR *ir = (struct IR *)filep->private_data;
+	if (ir == NULL) {
+		zilog_error("close: no private_data attached to the file!\n");
+		return -ENODEV;
+	}
+
+	/* decrement in use count */
+	mutex_lock(&ir->lock);
+	--ir->open;
+	set_use_dec(ir);
+	mutex_unlock(&ir->lock);
+
+	return 0;
+}
+
+static struct lirc_plugin lirc_template = {
+	.name		= "lirc_zilog",
+	.set_use_inc	= set_use_inc,
+	.set_use_dec	= set_use_dec,
+	.owner		= THIS_MODULE
+};
+
+/* ----------------------------------------------------------------------- */
+
+static int ir_attach(struct i2c_adapter *adap, int have_rx, int have_tx);
+static int ir_detach(struct i2c_client *client);
+static int ir_probe(struct i2c_adapter *adap);
+static int ir_command(struct i2c_client *client, unsigned int cmd, void *arg);
+
+static struct i2c_driver driver = {
+	.driver = {
+		.owner	= THIS_MODULE,
+		.name	= "i2c ir driver",
+	},
+	.attach_adapter	= ir_probe,
+	.detach_client	= ir_detach,
+	.command	= ir_command,
+};
+
+static struct i2c_client client_template = {
+	.name	= "unset",
+	.driver	= &driver
+};
+
+static struct file_operations lirc_fops = {
+	.llseek		= lseek,
+	.read		= read,
+	.write		= write,
+	.poll		= poll,
+	.ioctl		= ioctl,
+	.open		= open,
+	.release	= close
+};
+
+static int i2c_attach(struct i2c_client *client, struct IR *ir)
+{
+	int ret;
+
+	i2c_set_clientdata(client, ir);
+
+	ret = i2c_attach_client(client);
+	if (ret != 0) {
+		client->addr = 0;
+		return ret;
+	}
+	if (!i2c_use_client(client)) {
+		i2c_detach_client(client);
+		client->addr = 0;
+		return -EFAULT;
+	}
+	++ir->devs;
+	return 0;
+}
+
+static int ir_attach(struct i2c_adapter *adap, int have_rx, int have_tx)
+{
+	struct IR *ir;
+	int ret, i;
+
+	printk("lirc_zilog: chip found with %s\n",
+		have_rx && have_tx ? "RX and TX" :
+			have_rx ? "RX only" : "TX only");
+
+	ir = kmalloc(sizeof(struct IR), GFP_KERNEL);
+	if (ir == NULL)
+		return -ENOMEM;
+	if (lirc_buffer_init(&ir->buf, 2, BUFLEN/2) != 0) {
+		kfree(ir);
+		return -ENOMEM;
+	}
+	mutex_init(&ir->lock);
+	mutex_init(&ir->buf_lock);
+	ir->open = 0;
+	ir->devs = 0;
+	ir->task = NULL;
+	ir->need_boot = 1;
+	ir->shutdown = 0;
+	ir->t_notify = ir->t_notify2 = NULL;
+	for (i = 0; i < sizeof(ir->b); ++i)
+		ir->b[0] = 0;
+
+	memcpy(&ir->l, &lirc_template, sizeof(struct lirc_plugin));
+	ir->l.minor = -1;
+
+	/* initialise RX device */
+	client_template.adapter = adap;
+	memcpy(&ir->c_rx, &client_template, sizeof(struct i2c_client));
+	if (have_rx) {
+		DECLARE_COMPLETION(tn);
+
+		/* I2C attach to device */
+		ir->c_rx.addr = 0x71;
+		strncpy(ir->c_rx.name, "Zilog/Hauppauge RX", I2C_NAME_SIZE);
+		ret = i2c_attach(&ir->c_rx, ir);
+		if (ret != 0)
+			goto err;
+
+		/* try to fire up polling thread */
+		ir->t_notify = &tn;
+		ir->task = kthread_run(lirc_thread, ir, "lirc_zilog");
+		ret = PTR_ERR(ir->task);
+		if (ret <= 0) {
+			zilog_error("lirc_register_plugin: cannot run "
+				    "poll thread\n");
+			goto err;
+		}
+		wait_for_completion(&tn);
+		ir->t_notify = NULL;
+	}
+
+	/* initialise TX device */
+	memcpy(&ir->c_tx, &client_template, sizeof(struct i2c_client));
+	if (have_tx) {
+		/* I2C attach to device */
+		ir->c_tx.addr = 0x70;
+		strncpy(ir->c_tx.name, "Zilog/Hauppauge TX", I2C_NAME_SIZE);
+		ret = i2c_attach(&ir->c_tx, ir);
+		if (ret != 0)
+			goto err;
+	}
+
+	/* set lirc_dev stuff */
+	ir->l.code_length = 13;
+	ir->l.rbuf	  = &ir->buf;
+	ir->l.fops	= &lirc_fops;
+	ir->l.data	= ir;
+	ir->l.minor       = minor;
+	ir->l.sample_rate = 0;
+
+	/* register with lirc */
+	ir->l.minor = lirc_register_plugin(&ir->l);
+	if (ir->l.minor < 0 || ir->l.minor >= MAX_IRCTL_DEVICES) {
+		zilog_error("ir_attach: \"minor\" must be between 0 and %d "
+			    "(%d)!\n", MAX_IRCTL_DEVICES-1, ir->l.minor);
+		ret = -EBADRQC;
+		goto err;
+	}
+
+	/* store this for getting back in open() later on */
+	ir_devices[ir->l.minor] = ir;
+
+	/* if we have the tx device, load the 'firmware'.  We do this
+	   after registering with lirc as otherwise hotplug seems to take
+	   10s to create the lirc device.
+	 */
+	if (have_tx) {
+		/* Special TX init */
+		ret = tx_init(ir);
+		if (ret != 0)
+			goto err;
+	}
+	return 0;
+
+err:
+	/* undo everything, hopefully... */
+	if (ir->c_rx.addr)
+		ir_detach(&ir->c_rx);
+	if (ir->c_tx.addr)
+		ir_detach(&ir->c_tx);
+	return ret;
+}
+
+static int ir_detach(struct i2c_client *client)
+{
+	struct IR *ir = i2c_get_clientdata(client);
+	mutex_lock(&ir->lock);
+
+	if (client == &ir->c_rx) {
+		DECLARE_COMPLETION(tn);
+		DECLARE_COMPLETION(tn2);
+
+		/* end up polling thread */
+		if (ir->task && !IS_ERR(ir->task)) {
+			ir->t_notify = &tn;
+			ir->t_notify2 = &tn2;
+			ir->shutdown = 1;
+			wake_up_process(ir->task);
+			complete(&tn2);
+			wait_for_completion(&tn);
+			ir->t_notify = NULL;
+			ir->t_notify2 = NULL;
+		}
+
+		/* unregister device */
+		i2c_detach_client(&ir->c_rx);
+	} else if (client == &ir->c_tx) {
+		i2c_detach_client(&ir->c_tx);
+	} else {
+		mutex_unlock(&ir->lock);
+		zilog_error("ir_detach: detached from something we didn't "
+			    "attach to\n");
+		return -ENODEV;
+	}
+
+	--ir->devs;
+	if (ir->devs < 0) {
+		mutex_unlock(&ir->lock);
+		zilog_error("ir_detach: invalid device count\n");
+		return -ENODEV;
+	} else if (ir->devs == 0) {
+		/* unregister lirc plugin */
+		if (ir->l.minor >= 0 && ir->l.minor < MAX_IRCTL_DEVICES) {
+			lirc_unregister_plugin(ir->l.minor);
+			ir_devices[ir->l.minor] = NULL;
+		}
+
+		/* free memory */
+		lirc_buffer_free(&ir->buf);
+		mutex_unlock(&ir->lock);
+		kfree(ir);
+		return 0;
+	}
+	mutex_unlock(&ir->lock);
+	return 0;
+}
+
+static int ir_probe(struct i2c_adapter *adap)
+{
+	struct i2c_client c;
+	char buf;
+
+	if (adap->id == I2C_HW_B_BT848 ||
+	    adap->id == I2C_HW_B_CX2341X) {
+		int have_rx = 0, have_tx = 0;
+
+		/*
+		 * The external IR receiver is at i2c address 0x71.
+		 * The IR transmitter is at 0x70.
+		 */
+		memset(&c, 0, sizeof(c));
+		c.adapter = adap;
+		c.addr = 0x70;
+
+		if (!disable_rx) {
+			if (i2c_master_recv(&c, &buf, 1) == 1)
+				have_rx = 1;
+			dprintk("probe 0x70 @ %s: %s\n",
+				adap->name,
+				have_rx ? "yes" : "no");
+		}
+
+		if (!disable_tx) {
+			c.addr = 0x71;
+			if (i2c_master_recv(&c, &buf, 1) == 1)
+				have_tx = 1;
+			dprintk("probe 0x71 @ %s: %s\n",
+				adap->name,
+				have_tx ? "yes" : "no");
+		}
+
+		if (have_rx || have_tx)
+			return ir_attach(adap, have_rx, have_tx);
+		else
+			zilog_error("%s: no devices found\n", adap->name);
+	}
+
+	return 0;
+}
+
+static int ir_command(struct i2c_client *client, unsigned int cmd, void *arg)
+{
+	/* nothing */
+	return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+#ifdef MODULE
+
+int init_module(void)
+{
+	mutex_init(&tx_data_lock);
+	request_module("ivtv");
+	request_module("firmware_class");
+	i2c_add_driver(&driver);
+	return 0;
+}
+
+void cleanup_module(void)
+{
+	i2c_del_driver(&driver);
+	/* if loaded */
+	fw_unload();
+}
+
+MODULE_DESCRIPTION("Zilog/Hauppauge infrared transmitter driver (i2c stack)");
+MODULE_AUTHOR("Gerd Knorr, Michal Kochanowicz, Christoph Bartelmus, "
+	      "Ulrich Mueller, Stefan Jahn, Jerome Brock, Mark Weaver");
+MODULE_LICENSE("GPL");
+/* for compat with old name, which isn't all that accurate anymore */
+MODULE_ALIAS("lirc_pvr150");
+
+module_param(minor, int, 0444);
+MODULE_PARM_DESC(minor, "Preferred minor device number");
+
+module_param(debug, bool, 0644);
+MODULE_PARM_DESC(debug, "Enable debugging messages");
+
+module_param(disable_rx, bool, 0644);
+MODULE_PARM_DESC(disable_rx, "Disable the IR receiver device");
+
+module_param(disable_tx, bool, 0644);
+MODULE_PARM_DESC(disable_tx, "Disable the IR transmitter device");
+
+#endif /* MODULE */
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */

^ permalink raw reply related	[flat|nested] 111+ messages in thread

* Re: [PATCH 05/18] lirc driver for i2c-based IR receivers
  2008-09-09  4:05         ` [PATCH 05/18] lirc driver for i2c-based IR receivers Jarod Wilson
  2008-09-09  4:05           ` [PATCH 06/18] lirc driver for the ATI USB RF remote receiver Jarod Wilson
@ 2008-09-09  4:13           ` Jarod Wilson
  2008-09-10 15:42           ` Jonathan Corbet
  2 siblings, 0 replies; 111+ messages in thread
From: Jarod Wilson @ 2008-09-09  4:13 UTC (permalink / raw)
  To: linux-kernel, Janne Grunau; +Cc: Christoph Bartelmus

On Tuesday 09 September 2008 00:05:50 Jarod Wilson wrote:
> Successfully tested with the IR interface on a Hauppauge PVR-250 and
> the flimsly little grey remote that shipped with it.
>
> Signed-off-by: Jarod Wilson <jarod@redhat.com>
> Signed-off-by: Janne Grunau <j@jannua.net>

Knew I had to screw up at least one thing. That should be j@jannau.net, not 
jannua.net.

-- 
Jarod Wilson
jarod@redhat.com


^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 0/18] linux infrared remote control drivers
  2008-09-09  4:05 [PATCH 0/18] linux infrared remote control drivers Jarod Wilson
  2008-09-09  4:05 ` [PATCH 01/18] lirc core device driver infrastructure Jarod Wilson
@ 2008-09-09  4:36 ` Chris Wedgwood
  2008-09-09  7:06 ` Alexey Dobriyan
  2008-09-09 12:46 ` Christoph Hellwig
  3 siblings, 0 replies; 111+ messages in thread
From: Chris Wedgwood @ 2008-09-09  4:36 UTC (permalink / raw)
  To: Jarod Wilson
  Cc: linux-kernel, Jarod Wilson, Janne Grunau, Christoph Bartelmus,
	Eric Sandeen, Mario Limonciello

On Tue, Sep 09, 2008 at 12:05:45AM -0400, Jarod Wilson wrote:

> The following patch series adds 17 new drivers for assorted infrared
> and/or RF remote control receivers and/or transmitters.

Why does most of this have to be in the kernel?  Can a lot of this not
be done from userspace?

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 0/18] linux infrared remote control drivers
  2008-09-09  4:05 [PATCH 0/18] linux infrared remote control drivers Jarod Wilson
  2008-09-09  4:05 ` [PATCH 01/18] lirc core device driver infrastructure Jarod Wilson
  2008-09-09  4:36 ` [PATCH 0/18] linux infrared remote control drivers Chris Wedgwood
@ 2008-09-09  7:06 ` Alexey Dobriyan
  2008-09-09  8:32   ` Janne Grunau
  2008-09-09 12:46 ` Christoph Hellwig
  3 siblings, 1 reply; 111+ messages in thread
From: Alexey Dobriyan @ 2008-09-09  7:06 UTC (permalink / raw)
  To: Jarod Wilson
  Cc: linux-kernel, Jarod Wilson, Janne Grunau, Christoph Bartelmus,
	Eric Sandeen, Mario Limonciello

On Tue, Sep 09, 2008 at 12:05:45AM -0400, Jarod Wilson wrote:
> The following patch series adds 17 new drivers for assorted infrared and/or RF
> remote control receivers and/or transmitters. These drivers have long lived
> out-of-tree at http://www.lirc.org/, packaged as 3rd-party modules by many
> distributions, and more recently, patched into the kernels of at least Fedora
> and Ubuntu.

Woo-hoo! One Vista-compatible (oh how it's called) remote control was
waiting for this!

All version.h and autoconf.h inclusions aren't needed.

#ifdef MODULE is gross and very old. Don't need that. Just use

	static int __init x_init(void)
	{
		...
	}

	static void __exit x_exit(void)
	{
		...
	}
	module_init(x_init);
	module_exit(x_exit);
	MODULE_LICENSE
		...

It will work correctly in modular and built-in case.

Static number of devices is simple but how about adding them dynamically.

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 01/18] lirc core device driver infrastructure
  2008-09-09  4:05 ` [PATCH 01/18] lirc core device driver infrastructure Jarod Wilson
  2008-09-09  4:05   ` [PATCH 02/18] lirc serial port receiver/transmitter device driver Jarod Wilson
@ 2008-09-09  7:40   ` Sebastian Siewior
  2008-09-09  9:53     ` Janne Grunau
                       ` (3 more replies)
  2008-09-09  9:46   ` Andi Kleen
                     ` (3 subsequent siblings)
  5 siblings, 4 replies; 111+ messages in thread
From: Sebastian Siewior @ 2008-09-09  7:40 UTC (permalink / raw)
  To: Jarod Wilson
  Cc: linux-kernel, Jarod Wilson, Janne Grunau, Christoph Bartelmus,
	Mario Limonciello

>diff --git a/drivers/input/lirc/Makefile b/drivers/input/lirc/Makefile
>new file mode 100644
>index 0000000..cdb4c45
>--- /dev/null
>+++ b/drivers/input/lirc/Makefile
>@@ -0,0 +1,8 @@
>+# Makefile for the lirc drivers.
>+#
>+
>+# Each configuration option enables a list of files.
>+
>+EXTRA_CFLAGS =-DIRCTL_DEV_MAJOR=61 -DLIRC_SERIAL_TRANSMITTER -I$(src)
Do you rely on this specific major? Since your daemon opens /dev/lirc0
you don't need a fixed major do you?
LIRC_SERIAL_TRANSMITTER is used in patch 2 and just to enable a module
options. Since it is always the case, please remove it.
I haven't found the source of $src. It is probably a relic.

>+obj-$(CONFIG_LIRC_DEV)		+= lirc_dev.o
>diff --git a/drivers/input/lirc/lirc_dev.c b/drivers/input/lirc/lirc_dev.c
>new file mode 100644
>index 0000000..c8f325c
>--- /dev/null
>+++ b/drivers/input/lirc/lirc_dev.c
>@@ -0,0 +1,809 @@
>+/*
>+ * LIRC base driver
>+ *
>+ * (L) by Artur Lipowski <alipowski@interia.pl>
Is that L here on purpose?

>+ *
>+ *  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>
You souldn't need this one

>+
>+#include <linux/autoconf.h>
this as well.

>+#include <linux/module.h>
>+#include <linux/kernel.h>
>+#include <linux/sched.h>
>+#include <linux/errno.h>
>+#include <linux/ioctl.h>
>+#include <linux/fs.h>
>+#include <linux/poll.h>
>+#include <linux/smp_lock.h>
if you need this than you use the BKL back. As far as I remember
the ioctl() handler in kernel core no longer takes the BKL and I don't
see any locking in irctl_ioctl(). 

>+#include <linux/completion.h>
>+#include <linux/uaccess.h>
>+#include <linux/errno.h>
>+#include <linux/semaphore.h>
haven't found any call of down(). Do you really need this?

>+#define __KERNEL_SYSCALLS__
this define shouldn't be here, should it?

>+#include <linux/unistd.h>
>+#include <linux/kthread.h>
>+
>+/* SysFS header */
>+#include <linux/device.h>
>+
>+#include "lirc.h"
>+#include "lirc_dev.h"
>+
>+static int debug;
>+#define dprintk(fmt, args...)					\
>+	do {							\
>+		if (debug)					\
>+			printk(KERN_DEBUG fmt, ## args);	\
>+	} while (0)
>+
>+#define IRCTL_DEV_NAME    "BaseRemoteCtl"
>+#define SUCCESS           0
>+#define NOPLUG            -1
>+#define LOGHEAD           "lirc_dev (%s[%d]): "
>+
>+struct irctl {
>+	struct lirc_plugin p;
>+	int attached;
>+	int open;
>+
>+	struct mutex buffer_lock;
>+	struct lirc_buffer *buf;
>+
>+	struct task_struct *task;
>+	long jiffies_to_wait;
>+
>+};
>+
>+static DEFINE_MUTEX(plugin_lock);
>+
>+static struct irctl irctls[MAX_IRCTL_DEVICES];
>+static struct file_operations fops;
>+
>+/* Only used for sysfs but defined to void otherwise */
>+static struct class *lirc_class;
>+
>+/*  helper function
>+ *  initializes the irctl structure
>+ */
For all comments above functions:
- Please use the default comment style. 
- don't comment obvious things
- please use kernel doc if
- please comment the prototypes but the actual function.

>+static inline void init_irctl(struct irctl *ir)
this inline can go in my opinion. The others here as well. It is not
*that* performance critical.

>+{
>+	memset(&ir->p, 0, sizeof(struct lirc_plugin));
>+	mutex_init(&ir->buffer_lock);
>+	ir->p.minor = NOPLUG;
>+
>+	ir->task = NULL;
>+	ir->jiffies_to_wait = 0;
>+
>+	ir->open = 0;
>+	ir->attached = 0;
>+}
>+
>+static void cleanup(struct irctl *ir)
>+{
>+	dprintk(LOGHEAD "cleaning up\n", ir->p.name, ir->p.minor);
>+
>+	device_destroy(lirc_class, MKDEV(IRCTL_DEV_MAJOR, ir->p.minor));
>+
>+	if (ir->buf != ir->p.rbuf) {
>+		lirc_buffer_free(ir->buf);
>+		kfree(ir->buf);
>+	}
>+	ir->buf = NULL;
>+
>+	init_irctl(ir);
>+}
>+
>+/*  helper function
>+ *  reads key codes from plugin and puts them into buffer
>+ *  buffer free space is checked and locking performed
>+ *  returns 0 on success
>+ */
>+static inline int add_to_buf(struct irctl *ir)
>+{
>+	if (lirc_buffer_full(ir->buf)) {
>+		dprintk(LOGHEAD "buffer overflow\n",
>+			ir->p.name, ir->p.minor);
>+		return -EOVERFLOW;
>+	}
>+
>+	if (ir->p.add_to_buf) {
>+		int res = -ENODATA;
>+		int got_data = 0;
>+
>+		/* service the device as long as it is returning
>+		 * data and we have space
>+		 */
>+		while (!lirc_buffer_full(ir->buf)) {
>+			res = ir->p.add_to_buf(ir->p.data, ir->buf);
>+			if (res == SUCCESS)
>+				got_data++;
>+			else
>+				break;
>+		}
>+
>+		if (res == -ENODEV)
>+			kthread_stop(ir->task);
>+
>+		return got_data ? SUCCESS : res;
>+	}
>+
>+	return SUCCESS;
>+}
>+
>+/* main function of the polling thread
>+ */
>+static int lirc_thread(void *irctl)
>+{
>+	struct irctl *ir = irctl;
>+
>+	/* This thread doesn't need any user-level access,
>+	 * so get rid of all our resources
>+	 */
>+
>+	dprintk(LOGHEAD "poll thread started\n", ir->p.name, ir->p.minor);
>+
>+	do {
>+		if (ir->open) {
>+			if (ir->jiffies_to_wait) {
>+				set_current_state(TASK_INTERRUPTIBLE);
>+				schedule_timeout(ir->jiffies_to_wait);
>+			} else {
>+				interruptible_sleep_on(
>+					ir->p.get_queue(ir->p.data));
>+			}
>+			if (kthread_should_stop())
>+				break;
>+			if (!add_to_buf(ir))
>+				wake_up_interruptible(&ir->buf->wait_poll);
>+		} else {
>+			/* if device not opened so we can sleep half a second */
>+			set_current_state(TASK_INTERRUPTIBLE);
>+			schedule_timeout(HZ/2);
>+		}
>+	} while (!kthread_should_stop());
>+
>+	dprintk(LOGHEAD "poll thread ended\n", ir->p.name, ir->p.minor);
>+
>+	return 0;
>+}
>+
>+int lirc_register_plugin(struct lirc_plugin *p)
>+{
>+	struct irctl *ir;
>+	int minor;
>+	int bytes_in_key;
>+	int err;
>+	DECLARE_COMPLETION(tn);
>+
>+	if (!p) {
>+		printk(KERN_ERR "lirc_dev: lirc_register_plugin: "
>+		       "plugin pointer must be not NULL!\n");
>+		err = -EBADRQC;
>+		goto out;
>+	}
>+
>+	if (MAX_IRCTL_DEVICES <= p->minor) {
>+		printk(KERN_ERR "lirc_dev: lirc_register_plugin: "
>+		       "\"minor\" must be between 0 and %d (%d)!\n",
>+		       MAX_IRCTL_DEVICES-1, p->minor);
>+		err = -EBADRQC;
>+		goto out;
>+	}
>+
>+	if (1 > p->code_length || (BUFLEN * 8) < p->code_length) {
>+		printk(KERN_ERR "lirc_dev: lirc_register_plugin: "
>+		       "code length in bits for minor (%d) "
>+		       "must be less than %d!\n",
>+		       p->minor, BUFLEN * 8);
>+		err = -EBADRQC;
>+		goto out;
>+	}
>+
>+	printk(KERN_INFO "lirc_dev: lirc_register_plugin: sample_rate: %d\n",
>+		p->sample_rate);
>+	if (p->sample_rate) {
>+		if (2 > p->sample_rate || HZ < p->sample_rate) {
>+			printk(KERN_ERR "lirc_dev: lirc_register_plugin: "
>+			       "sample_rate must be between 2 and %d!\n", HZ);
>+			err = -EBADRQC;
>+			goto out;
>+		}
>+		if (!p->add_to_buf) {
>+			printk(KERN_ERR "lirc_dev: lirc_register_plugin: "
>+			       "add_to_buf cannot be NULL when "
>+			       "sample_rate is set\n");
>+			err = -EBADRQC;
>+			goto out;
>+		}
>+	} else if (!(p->fops && p->fops->read)
>+		   && !p->get_queue && !p->rbuf) {
>+		printk(KERN_ERR "lirc_dev: lirc_register_plugin: "
>+		       "fops->read, get_queue and rbuf "
>+		       "cannot all be NULL!\n");
>+		err = -EBADRQC;
>+		goto out;
>+	} else if (!p->get_queue && !p->rbuf) {
>+		if (!(p->fops && p->fops->read && p->fops->poll)
>+		    || (!p->fops->ioctl && !p->ioctl)) {
>+			printk(KERN_ERR "lirc_dev: lirc_register_plugin: "
>+			       "neither read, poll nor ioctl can be NULL!\n");
>+			err = -EBADRQC;
>+			goto out;
>+		}
>+	}
>+
>+	if (p->owner == NULL) {
>+		printk(KERN_ERR "lirc_dev: lirc_register_plugin: "
>+				    "no module owner registered\n");
>+		err = -EBADRQC;
>+		goto out;
>+	}
>+
>+	mutex_lock(&plugin_lock);
>+
>+	minor = p->minor;
>+
>+	if (0 > minor) {
>+		/* find first free slot for plugin */
>+		for (minor = 0; minor < MAX_IRCTL_DEVICES; minor++)
>+			if (irctls[minor].p.minor == NOPLUG)
>+				break;
>+		if (MAX_IRCTL_DEVICES == minor) {
>+			printk(KERN_ERR "lirc_dev: lirc_register_plugin: "
>+			       "no free slots for plugins!\n");
>+			err = -ENOMEM;
>+			goto out_lock;
>+		}
>+	} else if (irctls[minor].p.minor != NOPLUG) {
>+		printk(KERN_ERR "lirc_dev: lirc_register_plugin: "
>+		       "minor (%d) just registered!\n", minor);
>+		err = -EBUSY;
>+		goto out_lock;
>+	}
>+
>+	ir = &irctls[minor];
>+
>+	if (p->sample_rate) {
>+		ir->jiffies_to_wait = HZ / p->sample_rate;
>+	} else {
>+		/* it means - wait for external event in task queue */
>+		ir->jiffies_to_wait = 0;
>+	}
>+
>+	/* some safety check 8-) */
>+	p->name[sizeof(p->name)-1] = '\0';
>+
>+	bytes_in_key = p->code_length/8 + (p->code_length%8 ? 1 : 0);
did you actually pass checkpatch.pl ?

>+
>+	if (p->rbuf) {
>+		ir->buf = p->rbuf;
>+	} else {
>+		ir->buf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
>+		if (!ir->buf) {
>+			err = -ENOMEM;
>+			goto out_lock;
>+		}
>+		if (lirc_buffer_init(ir->buf, bytes_in_key,
>+				     BUFLEN/bytes_in_key) != 0) {
>+			kfree(ir->buf);
>+			err = -ENOMEM;
>+			goto out_lock;
>+		}
>+	}
>+
>+	if (p->features == 0)
>+		p->features = (p->code_length > 8) ?
>+			LIRC_CAN_REC_LIRCCODE : LIRC_CAN_REC_CODE;
>+
>+	ir->p = *p;
>+	ir->p.minor = minor;
>+
>+	device_create(lirc_class, ir->p.dev,
>+		      MKDEV(IRCTL_DEV_MAJOR, ir->p.minor), NULL,
>+		      "lirc%u", ir->p.minor);
>+
>+	if (p->sample_rate || p->get_queue) {
>+		/* try to fire up polling thread */
>+		ir->task = kthread_run(lirc_thread, (void *)ir, "lirc_dev");
>+		if (IS_ERR(ir->task)) {
>+			printk(KERN_ERR "lirc_dev: lirc_register_plugin: "
>+			       "cannot run poll thread for minor = %d\n",
>+			       p->minor);
>+			err = -ECHILD;
>+			goto out_sysfs;
>+		}
>+	}
>+	ir->attached = 1;
>+	mutex_unlock(&plugin_lock);
>+
>+/*
>+ * Recent kernels should handle this autmatically by increasing/decreasing
>+ * use count when a dependant module is loaded/unloaded.
>+ */
>+	dprintk("lirc_dev: plugin %s registered at minor number = %d\n",
>+		ir->p.name, ir->p.minor);
>+	p->minor = minor;
>+	return minor;
>+
>+out_sysfs:
>+	device_destroy(lirc_class, MKDEV(IRCTL_DEV_MAJOR, ir->p.minor));
>+out_lock:
>+	mutex_unlock(&plugin_lock);
>+out:
>+	return err;
>+}
>+EXPORT_SYMBOL(lirc_register_plugin);
Is EXPORT_SYMBOL_GPL() possible?

>+
>+int lirc_unregister_plugin(int minor)
>+{
>+	struct irctl *ir;
>+	DECLARE_COMPLETION(tn);
>+	DECLARE_COMPLETION(tn2);
>+
>+	if (minor < 0 || minor >= MAX_IRCTL_DEVICES) {
>+		printk(KERN_ERR "lirc_dev: lirc_unregister_plugin: "
>+		       "\"minor\" must be between 0 and %d!\n",
>+		       MAX_IRCTL_DEVICES-1);
>+		return -EBADRQC;
>+	}
>+
>+	ir = &irctls[minor];
>+
>+	mutex_lock(&plugin_lock);
>+
>+	if (ir->p.minor != minor) {
>+		printk(KERN_ERR "lirc_dev: lirc_unregister_plugin: "
>+		       "minor (%d) device not registered!", minor);
>+		mutex_unlock(&plugin_lock);
>+		return -ENOENT;
>+	}
>+
>+	/* end up polling thread */
>+	if (ir->task) {
>+		wake_up_process(ir->task);
>+		kthread_stop(ir->task);
>+	}
>+
>+	dprintk("lirc_dev: plugin %s unregistered from minor number = %d\n",
>+		ir->p.name, ir->p.minor);
>+
>+	ir->attached = 0;
>+	if (ir->open) {
>+		dprintk(LOGHEAD "releasing opened plugin\n",
>+			ir->p.name, ir->p.minor);
>+		wake_up_interruptible(&ir->buf->wait_poll);
>+		mutex_lock(&ir->buffer_lock);
>+		ir->p.set_use_dec(ir->p.data);
>+		module_put(ir->p.owner);
>+		mutex_unlock(&ir->buffer_lock);
>+	} else
>+		cleanup(ir);
>+	mutex_unlock(&plugin_lock);
>+
>+/*
>+ * Recent kernels should handle this autmatically by increasing/decreasing
>+ * use count when a dependant module is loaded/unloaded.
>+ */
>+
>+	return SUCCESS;
>+}
>+EXPORT_SYMBOL(lirc_unregister_plugin);
>+
>+/*
>+ *
>+ */
>+static int irctl_open(struct inode *inode, struct file *file)
>+{
>+	struct irctl *ir;
>+	int retval;
>+
>+	if (MINOR(inode->i_rdev) >= MAX_IRCTL_DEVICES) {
>+		dprintk("lirc_dev [%d]: open result = -ENODEV\n",
>+			MINOR(inode->i_rdev));
>+		return -ENODEV;
>+	}
>+
>+	ir = &irctls[MINOR(inode->i_rdev)];
>+
>+	dprintk(LOGHEAD "open called\n", ir->p.name, ir->p.minor);
>+
>+	/* if the plugin has an open function use it instead */
>+	if (ir->p.fops && ir->p.fops->open)
>+		return ir->p.fops->open(inode, file);
>+
>+	if (mutex_lock_interruptible(&plugin_lock))
>+		return -ERESTARTSYS;
>+
>+	if (ir->p.minor == NOPLUG) {
>+		mutex_unlock(&plugin_lock);
>+		dprintk(LOGHEAD "open result = -ENODEV\n",
>+			ir->p.name, ir->p.minor);
>+		return -ENODEV;
>+	}
>+
>+	if (ir->open) {
>+		mutex_unlock(&plugin_lock);
>+		dprintk(LOGHEAD "open result = -EBUSY\n",
>+			ir->p.name, ir->p.minor);
>+		return -EBUSY;
>+	}
>+
>+	/* there is no need for locking here because ir->open is 0
>+	 * and lirc_thread isn't using buffer
>+	 * plugins which use irq's should allocate them on set_use_inc,
>+	 * so there should be no problem with those either.
>+	 */
>+	ir->buf->head = ir->buf->tail;
>+	ir->buf->fill = 0;
>+
>+	if (ir->p.owner != NULL && try_module_get(ir->p.owner)) {
>+		++ir->open;
>+		retval = ir->p.set_use_inc(ir->p.data);
>+
>+		if (retval != SUCCESS) {
>+			module_put(ir->p.owner);
>+			--ir->open;
>+		}
>+	} else {
>+		if (ir->p.owner == NULL)
>+			dprintk(LOGHEAD "no module owner!!!\n",
>+				ir->p.name, ir->p.minor);
>+
>+		retval = -ENODEV;
>+	}
>+
>+	dprintk(LOGHEAD "open result = %d\n", ir->p.name, ir->p.minor, retval);
>+	mutex_unlock(&plugin_lock);
>+
>+	return retval;
>+}
>+
>+/*
>+ *
>+ */
>+static int irctl_close(struct inode *inode, struct file *file)
>+{
>+	struct irctl *ir = &irctls[MINOR(inode->i_rdev)];
>+
>+	dprintk(LOGHEAD "close called\n", ir->p.name, ir->p.minor);
>+
>+	/* if the plugin has a close function use it instead */
>+	if (ir->p.fops && ir->p.fops->release)
>+		return ir->p.fops->release(inode, file);
>+
>+	if (mutex_lock_interruptible(&plugin_lock))
>+		return -ERESTARTSYS;
>+
>+	--ir->open;
>+	if (ir->attached) {
>+		ir->p.set_use_dec(ir->p.data);
>+		module_put(ir->p.owner);
>+	} else {
>+		cleanup(ir);
>+	}
>+
>+	mutex_unlock(&plugin_lock);
>+
>+	return SUCCESS;
>+}
>+
>+/*
>+ *
>+ */
>+static unsigned int irctl_poll(struct file *file, poll_table *wait)
>+{
>+	struct irctl *ir = &irctls[MINOR(file->f_dentry->d_inode->i_rdev)];
>+	unsigned int ret;
>+
>+	dprintk(LOGHEAD "poll called\n", ir->p.name, ir->p.minor);
>+
>+	/* if the plugin has a poll function use it instead */
>+	if (ir->p.fops && ir->p.fops->poll)
>+		return ir->p.fops->poll(file, wait);
>+
>+	mutex_lock(&ir->buffer_lock);
>+	if (!ir->attached) {
>+		mutex_unlock(&ir->buffer_lock);
>+		return POLLERR;
>+	}
>+
>+	poll_wait(file, &ir->buf->wait_poll, wait);
>+
>+	dprintk(LOGHEAD "poll result = %s\n",
>+		ir->p.name, ir->p.minor,
>+		lirc_buffer_empty(ir->buf) ? "0" : "POLLIN|POLLRDNORM");
>+
>+	ret = lirc_buffer_empty(ir->buf) ? 0 : (POLLIN|POLLRDNORM);
>+
>+	mutex_unlock(&ir->buffer_lock);
>+	return ret;
>+}
>+
>+/*
>+ *
>+ */
>+static int irctl_ioctl(struct inode *inode, struct file *file,
>+		       unsigned int cmd, unsigned long arg)
>+{
>+	unsigned long mode;
>+	int result;
>+	struct irctl *ir = &irctls[MINOR(inode->i_rdev)];
>+
>+	dprintk(LOGHEAD "ioctl called (0x%x)\n",
>+		ir->p.name, ir->p.minor, cmd);
>+
>+	/* if the plugin has a ioctl function use it instead */
>+	if (ir->p.fops && ir->p.fops->ioctl)
>+		return ir->p.fops->ioctl(inode, file, cmd, arg);
>+
>+	if (ir->p.minor == NOPLUG || !ir->attached) {
>+		dprintk(LOGHEAD "ioctl result = -ENODEV\n",
>+			ir->p.name, ir->p.minor);
>+		return -ENODEV;
>+	}
>+
>+	/* Give the plugin a chance to handle the ioctl */
>+	if (ir->p.ioctl) {
>+		result = ir->p.ioctl(inode, file, cmd, arg);
>+		if (result != -ENOIOCTLCMD)
>+			return result;
>+	}
>+	/* The plugin can't handle cmd */
>+	result = SUCCESS;
>+
>+	switch (cmd) {
>+	case LIRC_GET_FEATURES:
>+		result = put_user(ir->p.features, (unsigned long *)arg);
>+		break;
>+	case LIRC_GET_REC_MODE:
>+		if (!(ir->p.features&LIRC_CAN_REC_MASK))
>+			return -ENOSYS;
>+
>+		result = put_user(LIRC_REC2MODE
>+				  (ir->p.features&LIRC_CAN_REC_MASK),
>+				  (unsigned long *)arg);
>+		break;
>+	case LIRC_SET_REC_MODE:
>+		if (!(ir->p.features&LIRC_CAN_REC_MASK))
>+			return -ENOSYS;
>+
>+		result = get_user(mode, (unsigned long *)arg);
>+		if (!result && !(LIRC_MODE2REC(mode) & ir->p.features))
>+			result = -EINVAL;
>+		/*
>+		 * FIXME: We should actually set the mode somehow but
>+		 * for now, lirc_serial doesn't support mode changing either
>+		 */
>+		break;
>+	case LIRC_GET_LENGTH:
>+		result = put_user((unsigned long)ir->p.code_length,
>+				  (unsigned long *)arg);
>+		break;
>+	default:
>+		result = -ENOIOCTLCMD;
>+	}
>+
>+	dprintk(LOGHEAD "ioctl result = %d\n",
>+		ir->p.name, ir->p.minor, result);
>+
>+	return result;
>+}
>+
>+/*
>+ *
>+ */
>+static ssize_t irctl_read(struct file *file,
>+			  char *buffer,
>+			  size_t length,
>+			  loff_t *ppos)
>+{
>+	struct irctl *ir = &irctls[MINOR(file->f_dentry->d_inode->i_rdev)];
>+	unsigned char buf[ir->buf->chunk_size];
>+	int ret = 0, written = 0;
>+	DECLARE_WAITQUEUE(wait, current);
>+
>+	dprintk(LOGHEAD "read called\n", ir->p.name, ir->p.minor);
>+
>+	/* if the plugin has a specific read function use it instead */
>+	if (ir->p.fops && ir->p.fops->read)
>+		return ir->p.fops->read(file, buffer, length, ppos);
>+
>+	if (mutex_lock_interruptible(&ir->buffer_lock))
>+		return -ERESTARTSYS;
>+	if (!ir->attached) {
>+		mutex_unlock(&ir->buffer_lock);
>+		return -ENODEV;
>+	}
>+
>+	if (length % ir->buf->chunk_size) {
>+		dprintk(LOGHEAD "read result = -EINVAL\n",
>+			ir->p.name, ir->p.minor);
>+		mutex_unlock(&ir->buffer_lock);
>+		return -EINVAL;
>+	}
>+
>+	/*
>+	 * we add ourselves to the task queue before buffer check
>+	 * to avoid losing scan code (in case when queue is awaken somewhere
>+	 * beetwen while condition checking and scheduling)
>+	 */
>+	add_wait_queue(&ir->buf->wait_poll, &wait);
>+	set_current_state(TASK_INTERRUPTIBLE);
>+
>+	/*
>+	 * while we did't provide 'length' bytes, device is opened in blocking
>+	 * mode and 'copy_to_user' is happy, wait for data.
>+	 */
>+	while (written < length && ret == 0) {
>+		if (lirc_buffer_empty(ir->buf)) {
>+			/* According to the read(2) man page, 'written' can be
>+			 * returned as less than 'length', instead of blocking
>+			 * again, returning -EWOULDBLOCK, or returning
>+			 * -ERESTARTSYS */
>+			if (written)
>+				break;
>+			if (file->f_flags & O_NONBLOCK) {
>+				ret = -EWOULDBLOCK;
>+				break;
>+			}
>+			if (signal_pending(current)) {
>+				ret = -ERESTARTSYS;
>+				break;
>+			}
>+			schedule();
>+			set_current_state(TASK_INTERRUPTIBLE);
>+			if (!ir->attached) {
>+				ret = -ENODEV;
>+				break;
>+			}
>+		} else {
>+			lirc_buffer_read_1(ir->buf, buf);
>+			ret = copy_to_user((void *)buffer+written, buf,
>+					   ir->buf->chunk_size);
>+			written += ir->buf->chunk_size;
>+		}
>+	}
>+
>+	remove_wait_queue(&ir->buf->wait_poll, &wait);
>+	set_current_state(TASK_RUNNING);
>+	mutex_unlock(&ir->buffer_lock);
>+
>+	dprintk(LOGHEAD "read result = %s (%d)\n",
>+		ir->p.name, ir->p.minor, ret ? "-EFAULT" : "OK", ret);
>+
>+	return ret ? ret : written;
>+}
>+
>+
>+void *lirc_get_pdata(struct file *file)
>+{
>+	void *data = NULL;
>+
>+	if (file && file->f_dentry && file->f_dentry->d_inode &&
>+	    file->f_dentry->d_inode->i_rdev) {
>+		struct irctl *ir;
>+		ir = &irctls[MINOR(file->f_dentry->d_inode->i_rdev)];
>+		data = ir->p.data;
>+	}
>+
>+	return data;
>+}
>+EXPORT_SYMBOL(lirc_get_pdata);
>+
>+
>+static ssize_t irctl_write(struct file *file, const char *buffer,
>+			   size_t length, loff_t *ppos)
>+{
>+	struct irctl *ir = &irctls[MINOR(file->f_dentry->d_inode->i_rdev)];
>+
>+	dprintk(LOGHEAD "write called\n", ir->p.name, ir->p.minor);
>+
>+	/* if the plugin has a specific read function use it instead */
>+	if (ir->p.fops && ir->p.fops->write)
>+		return ir->p.fops->write(file, buffer, length, ppos);
>+
>+	if (!ir->attached)
>+		return -ENODEV;
>+
>+	return -EINVAL;
>+}
>+
>+
>+static struct file_operations fops = {
>+	.read		= irctl_read,
>+	.write		= irctl_write,
>+	.poll		= irctl_poll,
>+	.ioctl		= irctl_ioctl,
>+	.open		= irctl_open,
>+	.release	= irctl_close
>+};
>+
>+
>+static int lirc_dev_init(void)
>+{
>+	int i;
>+
>+	for (i = 0; i < MAX_IRCTL_DEVICES; ++i)
>+		init_irctl(&irctls[i]);
>+
>+	if (register_chrdev(IRCTL_DEV_MAJOR, IRCTL_DEV_NAME, &fops)) {
>+		printk(KERN_ERR "lirc_dev: register_chrdev failed\n");
>+		goto out;
>+	}
>+
>+	lirc_class = class_create(THIS_MODULE, "lirc");
>+	if (IS_ERR(lirc_class)) {
>+		printk(KERN_ERR "lirc_dev: class_create failed\n");
>+		goto out_unregister;
>+	}
>+
>+	printk(KERN_INFO "lirc_dev: IR Remote Control driver registered, "
>+	       "major %d \n", IRCTL_DEV_MAJOR);
>+
>+	return SUCCESS;
>+
>+out_unregister:
>+	/* unregister_chrdev returns void now */
>+	unregister_chrdev(IRCTL_DEV_MAJOR, IRCTL_DEV_NAME);
>+out:
>+	return -1;
Ehm, the error code from register_chrdev() ?

>+}
>+
>+/* ---------------------------------------------------------------------- */
I hate those

>+
>+#ifdef MODULE
You don't need that one. subsys_initcall() does the right thing for you
allready.

>+
>+/*
>+ *
>+ */
>+int init_module(void)
>+{
>+	return lirc_dev_init();
>+}
>+
>+/*
>+ *
>+ */
>+void cleanup_module(void)
>+{
>+	/* unregister_chrdev returns void now */
>+	unregister_chrdev(IRCTL_DEV_MAJOR, IRCTL_DEV_NAME);
>+	class_destroy(lirc_class);
>+	dprintk("lirc_dev: module unloaded\n");
>+}
>+
>+MODULE_DESCRIPTION("LIRC base driver module");
>+MODULE_AUTHOR("Artur Lipowski");
>+MODULE_LICENSE("GPL");
>+MODULE_ALIAS_CHARDEV_MAJOR(IRCTL_DEV_MAJOR);
>+
>+module_param(debug, bool, 0644);
>+MODULE_PARM_DESC(debug, "Enable debugging messages");
>+
>+#else /* not a MODULE */
>+subsys_initcall(lirc_dev_init);
>+
>+#endif /* MODULE */
>+
>+/*
>+ * Overrides for Emacs so that we follow Linus's tabbing style.
>+ * ---------------------------------------------------------------------------
>+ * Local variables:
>+ * c-basic-offset: 8
>+ * End:
>+ */
Please don't enforce formating that way.

Regards,
Sebastian

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 0/18] linux infrared remote control drivers
  2008-09-09  7:06 ` Alexey Dobriyan
@ 2008-09-09  8:32   ` Janne Grunau
  0 siblings, 0 replies; 111+ messages in thread
From: Janne Grunau @ 2008-09-09  8:32 UTC (permalink / raw)
  To: Alexey Dobriyan
  Cc: Jarod Wilson, linux-kernel, Jarod Wilson, Christoph Bartelmus,
	Eric Sandeen, Mario Limonciello

On Tuesday 09 September 2008 09:06:03 Alexey Dobriyan wrote:
>
> All version.h and autoconf.h inclusions aren't needed.

I'll remove them.

> #ifdef MODULE is gross and very old. Don't need that. Just use
>
> 	static int __init x_init(void)
> 	{
> 		...
> 	}
>
> 	static void __exit x_exit(void)
> 	{
> 		...
> 	}
> 	module_init(x_init);
> 	module_exit(x_exit);
> 	MODULE_LICENSE
> 		...
>
> It will work correctly in modular and built-in case.

Will do so.

> Static number of devices is simple but how about adding them
> dynamically.

Will have a look

Janne

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 01/18] lirc core device driver infrastructure
  2008-09-09  4:05 ` [PATCH 01/18] lirc core device driver infrastructure Jarod Wilson
  2008-09-09  4:05   ` [PATCH 02/18] lirc serial port receiver/transmitter device driver Jarod Wilson
  2008-09-09  7:40   ` [PATCH 01/18] lirc core device driver infrastructure Sebastian Siewior
@ 2008-09-09  9:46   ` Andi Kleen
  2008-09-09 11:35     ` Janne Grunau
  2008-09-09 13:01   ` Christoph Hellwig
                     ` (2 subsequent siblings)
  5 siblings, 1 reply; 111+ messages in thread
From: Andi Kleen @ 2008-09-09  9:46 UTC (permalink / raw)
  To: Jarod Wilson
  Cc: linux-kernel, Jarod Wilson, Janne Grunau, Christoph Bartelmus,
	Mario Limonciello

Jarod Wilson <jwilson@redhat.com> writes:

Just some quick comments, no complete review

> +EXTRA_CFLAGS =-DIRCTL_DEV_MAJOR=61 -DLIRC_SERIAL_TRANSMITTER -I$(src)

Put the Ds into some include instead?

> +
> +static int debug;
> +#define dprintk(fmt, args...)					\
> +	do {							\
> +		if (debug)					\
> +			printk(KERN_DEBUG fmt, ## args);	\
> +	} while (0)

There's some effort to replace that all with pr_printk

> +	dprintk(LOGHEAD "poll thread started\n", ir->p.name, ir->p.minor);
> +
> +	do {
> +		if (ir->open) {
> +			if (ir->jiffies_to_wait) {
> +				set_current_state(TASK_INTERRUPTIBLE);
> +				schedule_timeout(ir->jiffies_to_wait);
> +			} else {
> +				interruptible_sleep_on(
> +					ir->p.get_queue(ir->p.data));

sleep_on is really discouraged, consider replacing it with wait_event()

> +			}
> +			if (kthread_should_stop())
> +				break;
> +			if (!add_to_buf(ir))
> +				wake_up_interruptible(&ir->buf->wait_poll);
> +		} else {
> +			/* if device not opened so we can sleep half a second */
> +			set_current_state(TASK_INTERRUPTIBLE);
> +			schedule_timeout(HZ/2);

This means you always wake up every half a second? That will not make 
the powertop users happy. When there's nothing to do it should just sleep.


The checks in plugin_register seem a bit extreme.
At least make that all WARN_ONs

> +
> +	/* end up polling thread */
> +	if (ir->task) {
> +		wake_up_process(ir->task);
> +		kthread_stop(ir->task);
> +	}
> +
> +	dprintk("lirc_dev: plugin %s unregistered from minor number = %d\n",
> +		ir->p.name, ir->p.minor);
> +
> +	ir->attached = 0;
> +	if (ir->open) {
> +		dprintk(LOGHEAD "releasing opened plugin\n",
> +			ir->p.name, ir->p.minor);
> +		wake_up_interruptible(&ir->buf->wait_poll);

This seems racy with some users who are lockless

> +		mutex_lock(&ir->buffer_lock);
> +		ir->p.set_use_dec(ir->p.data);
> +		module_put(ir->p.owner);
> +		mutex_unlock(&ir->buffer_lock);
> +	} else
> +		cleanup(ir);
> +	mutex_unlock(&plugin_lock);
> +
> +/*
> + * Recent kernels should handle this autmatically by increasing/decreasing
> + * use count when a dependant module is loaded/unloaded.
> + */

All kernels are recent now.

> +#ifdef MODULE
> +
> +/*
> + *
> + */
> +int init_module(void)
> +{
> +	return lirc_dev_init();
> +}
> +
> +/*
> + *
> + */
> +void cleanup_module(void)
> +{
> +	/* unregister_chrdev returns void now */
> +	unregister_chrdev(IRCTL_DEV_MAJOR, IRCTL_DEV_NAME);
> +	class_destroy(lirc_class);
> +	dprintk("lirc_dev: module unloaded\n");
> +}
> +
> +MODULE_DESCRIPTION("LIRC base driver module");
> +MODULE_AUTHOR("Artur Lipowski");
> +MODULE_LICENSE("GPL");
> +MODULE_ALIAS_CHARDEV_MAJOR(IRCTL_DEV_MAJOR);
> +
> +module_param(debug, bool, 0644);
> +MODULE_PARM_DESC(debug, "Enable debugging messages");
> +
> +#else /* not a MODULE */
> +subsys_initcall(lirc_dev_init);
> +
> +#endif /* MODULE */

Always use the #ifdef MODULE path, it does DTRT when compiled in too.
Remove the init_module wrapper, replace with module_init() 

... not read further ...
-Andi

-- 
ak@linux.intel.com

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 01/18] lirc core device driver infrastructure
  2008-09-09  7:40   ` [PATCH 01/18] lirc core device driver infrastructure Sebastian Siewior
@ 2008-09-09  9:53     ` Janne Grunau
  2008-09-09 12:33       ` Sebastian Siewior
  2008-09-11 16:41       ` Christoph Bartelmus
  2008-09-09 11:13     ` Alan Cox
                       ` (2 subsequent siblings)
  3 siblings, 2 replies; 111+ messages in thread
From: Janne Grunau @ 2008-09-09  9:53 UTC (permalink / raw)
  To: Sebastian Siewior
  Cc: Jarod Wilson, linux-kernel, Jarod Wilson, Christoph Bartelmus,
	Mario Limonciello, Janne Grunau

On Tuesday 09 September 2008 09:40:18 Sebastian Siewior wrote:
> >diff --git a/drivers/input/lirc/Makefile
> > b/drivers/input/lirc/Makefile new file mode 100644
> >index 0000000..cdb4c45
> >--- /dev/null
> >+++ b/drivers/input/lirc/Makefile
> >@@ -0,0 +1,8 @@
> >+# Makefile for the lirc drivers.
> >+#
> >+
> >+# Each configuration option enables a list of files.
> >+
> >+EXTRA_CFLAGS =-DIRCTL_DEV_MAJOR=61 -DLIRC_SERIAL_TRANSMITTER
> > -I$(src)

> LIRC_SERIAL_TRANSMITTER is used in patch 2 and just to enable a
> module options. Since it is always the case, please remove it.

Added CONFIG_LIRC_SERIAL_TRANSMITTER to Kconfig and removed this from 
the Makefile

> I haven't found the source of $src. It is probably a relic.

removed

> >+obj-$(CONFIG_LIRC_DEV)		+= lirc_dev.o
> >diff --git a/drivers/input/lirc/lirc_dev.c
> > b/drivers/input/lirc/lirc_dev.c new file mode 100644
> >index 0000000..c8f325c
> >--- /dev/null
> >+++ b/drivers/input/lirc/lirc_dev.c
> >@@ -0,0 +1,809 @@
...
> >+
> >+#include <linux/version.h>
>
> You souldn't need this one
>
> >+
> >+#include <linux/autoconf.h>
>
> this as well.

fixed, in all other files as well.

> >+#include <linux/semaphore.h>
>
> haven't found any call of down(). Do you really need this?

A relict. Lirc used many single holder semaphore and I forgot to remove 
the include after converting them to mutexes. I'll check the other 
files too.

> >+#define __KERNEL_SYSCALLS__
>
> this define shouldn't be here, should it?

Probably not, but it doesn't make a difference for the current kernel.
The only other hit for this symbol is arch/xtensa/kernel/syscall.c
38:#undef  __KERNEL_SYSCALLS__

...

> >+static inline void init_irctl(struct irctl *ir)
>
> this inline can go in my opinion. The others here as well. It is not
> *that* performance critical.

There is only one other inline. I doubt it makes a difference there but 
I won't change it without test. It does for the object size since the 
function is only called from one place.

...

> >+
> >+	bytes_in_key = p->code_length/8 + (p->code_length%8 ? 1 : 0);
>
> did you actually pass checkpatch.pl ?

yes, and while this is ugly I see no obvious CodingStyle violation.

is following better?

bytes_in_key = p->code_length/8;
if (p->code_length%8)
	bytes_in_key++;

> >+EXPORT_SYMBOL(lirc_register_plugin);
>
> Is EXPORT_SYMBOL_GPL() possible?

Afaik yes, all drivers using this symbol are in this patchset and under 
GPL. If it is desireable to to use EXPORT_SYMBOL_GPL() has someone else 
to decide. Christoph?


> >+/*
> >+ * Overrides for Emacs so that we follow Linus's tabbing style.
> >+ *
> > -------------------------------------------------------------------
> >-------- + * Local variables:
> >+ * c-basic-offset: 8
> >+ * End:
> >+ */
>
> Please don't enforce formating that way.

Removed, from all other files as well.

Thanks for the review. We will address the misssing comments later

Janne

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 01/18] lirc core device driver infrastructure
  2008-09-09  7:40   ` [PATCH 01/18] lirc core device driver infrastructure Sebastian Siewior
  2008-09-09  9:53     ` Janne Grunau
@ 2008-09-09 11:13     ` Alan Cox
  2008-09-09 13:27     ` Stefan Richter
  2008-09-09 17:03     ` Jarod Wilson
  3 siblings, 0 replies; 111+ messages in thread
From: Alan Cox @ 2008-09-09 11:13 UTC (permalink / raw)
  To: Sebastian Siewior
  Cc: Jarod Wilson, linux-kernel, Jarod Wilson, Janne Grunau,
	Christoph Bartelmus, Mario Limonciello

> Do you rely on this specific major? Since your daemon opens /dev/lirc0
> you don't need a fixed major do you?

Needs to change anyway 60 is already assigned for local and experimental
use.

Alan

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 01/18] lirc core device driver infrastructure
  2008-09-09  9:46   ` Andi Kleen
@ 2008-09-09 11:35     ` Janne Grunau
  2008-09-09 13:03       ` Andi Kleen
  0 siblings, 1 reply; 111+ messages in thread
From: Janne Grunau @ 2008-09-09 11:35 UTC (permalink / raw)
  To: Andi Kleen
  Cc: Jarod Wilson, linux-kernel, Jarod Wilson, Christoph Bartelmus,
	Mario Limonciello, Janne Grunau

On Tuesday 09 September 2008 11:46:48 Andi Kleen wrote:
> Jarod Wilson <jwilson@redhat.com> writes:
>
> Just some quick comments, no complete review
>
> > +EXTRA_CFLAGS =-DIRCTL_DEV_MAJOR=61 -DLIRC_SERIAL_TRANSMITTER
> > -I$(src)
>
> Put the Ds into some include instead?

LIRC_SERIAL_TRANSMITTER is already gone, not sure that should happen to 
IRCTL_DEV_MAJOR but it could go into lirc.h. moved

> > +
> > +static int debug;
> > +#define dprintk(fmt, args...)					\
> > +	do {							\
> > +		if (debug)					\
> > +			printk(KERN_DEBUG fmt, ## args);	\
> > +	} while (0)
>
> There's some effort to replace that all with pr_printk

pr_printk is not found in include/ nor by google. please explain

> > +	dprintk(LOGHEAD "poll thread started\n", ir->p.name,
> > ir->p.minor); +
> > +	do {
> > +		if (ir->open) {
> > +			if (ir->jiffies_to_wait) {
> > +				set_current_state(TASK_INTERRUPTIBLE);
> > +				schedule_timeout(ir->jiffies_to_wait);
> > +			} else {
> > +				interruptible_sleep_on(
> > +					ir->p.get_queue(ir->p.data));
>
> sleep_on is really discouraged, consider replacing it with
> wait_event()

done

> > +			}
> > +			if (kthread_should_stop())
> > +				break;
> > +			if (!add_to_buf(ir))
> > +				wake_up_interruptible(&ir->buf->wait_poll);
> > +		} else {
> > +			/* if device not opened so we can sleep half a second */
> > +			set_current_state(TASK_INTERRUPTIBLE);
> > +			schedule_timeout(HZ/2);
>
> This means you always wake up every half a second? That will not make
> the powertop users happy. When there's nothing to do it should just
> sleep.

done

> The checks in plugin_register seem a bit extreme.
> At least make that all WARN_ONs
>
> > +
> > +	/* end up polling thread */
> > +	if (ir->task) {
> > +		wake_up_process(ir->task);
> > +		kthread_stop(ir->task);
> > +	}
> > +
> > +	dprintk("lirc_dev: plugin %s unregistered from minor number =
> > %d\n", +		ir->p.name, ir->p.minor);
> > +
> > +	ir->attached = 0;
> > +	if (ir->open) {
> > +		dprintk(LOGHEAD "releasing opened plugin\n",
> > +			ir->p.name, ir->p.minor);
> > +		wake_up_interruptible(&ir->buf->wait_poll);
>
> This seems racy with some users who are lockless
>
> > +		mutex_lock(&ir->buffer_lock);
> > +		ir->p.set_use_dec(ir->p.data);
> > +		module_put(ir->p.owner);
> > +		mutex_unlock(&ir->buffer_lock);
> > +	} else
> > +		cleanup(ir);
> > +	mutex_unlock(&plugin_lock);
> > +
> > +/*
> > + * Recent kernels should handle this autmatically by
> > increasing/decreasing + * use count when a dependant module is
> > loaded/unloaded.
> > + */
>
> All kernels are recent now.

removed

> > +#ifdef MODULE
> > +
> > +/*
> > + *
> > + */
> > +int init_module(void)
> > +{
> > +	return lirc_dev_init();
> > +}
> > +
> > +/*
> > + *
> > + */
> > +void cleanup_module(void)
> > +{
> > +	/* unregister_chrdev returns void now */
> > +	unregister_chrdev(IRCTL_DEV_MAJOR, IRCTL_DEV_NAME);
> > +	class_destroy(lirc_class);
> > +	dprintk("lirc_dev: module unloaded\n");
> > +}
> > +
> > +MODULE_DESCRIPTION("LIRC base driver module");
> > +MODULE_AUTHOR("Artur Lipowski");
> > +MODULE_LICENSE("GPL");
> > +MODULE_ALIAS_CHARDEV_MAJOR(IRCTL_DEV_MAJOR);
> > +
> > +module_param(debug, bool, 0644);
> > +MODULE_PARM_DESC(debug, "Enable debugging messages");
> > +
> > +#else /* not a MODULE */
> > +subsys_initcall(lirc_dev_init);
> > +
> > +#endif /* MODULE */
>
> Always use the #ifdef MODULE path, it does DTRT when compiled in too.
> Remove the init_module wrapper, replace with module_init()

done

thanks for the review. My changes are until Jarod pulls them in 
following tree git://git.jannau.net/linux-2.6-lirc.git

Janne

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 01/18] lirc core device driver infrastructure
  2008-09-09  9:53     ` Janne Grunau
@ 2008-09-09 12:33       ` Sebastian Siewior
  2008-09-09 13:10         ` Janne Grunau
  2008-09-11 16:41       ` Christoph Bartelmus
  1 sibling, 1 reply; 111+ messages in thread
From: Sebastian Siewior @ 2008-09-09 12:33 UTC (permalink / raw)
  To: Janne Grunau
  Cc: Jarod Wilson, linux-kernel, Jarod Wilson, Christoph Bartelmus,
	Mario Limonciello

* Janne Grunau | 2008-09-09 11:53:48 [+0200]:

>> >+
>> >+	bytes_in_key = p->code_length/8 + (p->code_length%8 ? 1 : 0);
>>
>> did you actually pass checkpatch.pl ?
>
>yes, and while this is ugly I see no obvious CodingStyle violation.
>
>is following better?
>
>bytes_in_key = p->code_length/8;
>if (p->code_length%8)
>	bytes_in_key++;
>
bytes_in_key = p->code_length / 8;
if (p->code_length % 8)
	bytes_in_key++;

I meant the missing spaces around / and %. Looking now at it, it seems
that it behaves like DIV_ROUND_UP(). The magic of spaces :)

>
>Janne

Sebastian

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 0/18] linux infrared remote control drivers
  2008-09-09  4:05 [PATCH 0/18] linux infrared remote control drivers Jarod Wilson
                   ` (2 preceding siblings ...)
  2008-09-09  7:06 ` Alexey Dobriyan
@ 2008-09-09 12:46 ` Christoph Hellwig
  2008-09-09 15:23   ` Jarod Wilson
  2008-09-09 15:34   ` Jon Smirl
  3 siblings, 2 replies; 111+ messages in thread
From: Christoph Hellwig @ 2008-09-09 12:46 UTC (permalink / raw)
  To: Jarod Wilson
  Cc: linux-kernel, Jarod Wilson, Janne Grunau, Christoph Bartelmus,
	Eric Sandeen, Mario Limonciello

On Tue, Sep 09, 2008 at 12:05:45AM -0400, Jarod Wilson wrote:
> The following patch series adds 17 new drivers for assorted infrared and/or RF
> remote control receivers and/or transmitters. These drivers have long lived
> out-of-tree at http://www.lirc.org/, packaged as 3rd-party modules by many
> distributions, and more recently, patched into the kernels of at least Fedora
> and Ubuntu. The primary maintainer of lirc, Christoph Bartelmus simply hasn't
> had the time to send these bits upstream, and a few months back, gave me the
> go-ahead to take on the task.

Any reason this is a separate subsystem instead of just a bunch new
drivers for the input subsystem?

> Not all drivers have been tested with this codebase and certainly not all
> devices they support, but its a solid place to start with these in-kernel.
> Patches are against 2.6.27-rc5-git9 or so, and have been tested running
> the same, at least in the cases where hardware was available. These patches
> are also in the latest nightly Fedora rawhide kernel, for those who want
> instant gratification[3]. Note that you also need the lirc userspace to
> really do any meaningful testing...

Can you first only send the actually tested drivers?  That way review
an cleanup can focus on the core and those few and we might get them
ready for 2.6.28.  Other drivers can than be added when testing and
review is done.


^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 01/18] lirc core device driver infrastructure
  2008-09-09  4:05 ` [PATCH 01/18] lirc core device driver infrastructure Jarod Wilson
                     ` (2 preceding siblings ...)
  2008-09-09  9:46   ` Andi Kleen
@ 2008-09-09 13:01   ` Christoph Hellwig
  2008-09-10 12:24     ` Janne Grunau
  2008-09-12  0:16     ` Janne Grunau
  2008-09-09 15:33   ` Jonathan Corbet
  2008-09-10 13:08   ` Dmitry Torokhov
  5 siblings, 2 replies; 111+ messages in thread
From: Christoph Hellwig @ 2008-09-09 13:01 UTC (permalink / raw)
  To: Jarod Wilson
  Cc: linux-kernel, Jarod Wilson, Janne Grunau, Christoph Bartelmus,
	Mario Limonciello

> +menuconfig INPUT_LIRC
> +	bool "Linux Infrared Remote Control IR receiver/transmitter drivers"
> +	default n

n is the default, no needed add a "default n" line for it.

Anyone configuring the kernel should know he's on Linux, so strike the
Linux away :)

> +if INPUT_LIRC
> +
> +config LIRC_DEV
> +	tristate "LIRC device loadable module support"
> +	default n
> +	help
> +	  LIRC device loadable module support, required for most LIRC drivers

Obviously this can be built in, so it should be named better.  Also I
don't think LIRC means anything to a user, so use Infrared Remote
Control support or something similar instead.

I don't quite understand why this needs a different option from
INPUT_LIRC.

> +#define __KERNEL_SYSCALLS__

No need for this in any semi-recent kernel.

> +#include <linux/unistd.h>
> +#include <linux/kthread.h>
> +
> +/* SysFS header */
> +#include <linux/device.h>

That comment is not quite correct, just remove it.

> +/*  helper function
> + *  initializes the irctl structure
> + */
> +static inline void init_irctl(struct irctl *ir)
> +{
> +	memset(&ir->p, 0, sizeof(struct lirc_plugin));
> +	mutex_init(&ir->buffer_lock);
> +	ir->p.minor = NOPLUG;
> +
> +	ir->task = NULL;
> +	ir->jiffies_to_wait = 0;
> +
> +	ir->open = 0;
> +	ir->attached = 0;
> +}

Please don't mark funtion inline unless there's a very good reason for
it.

> +static void cleanup(struct irctl *ir)
> +{
> +	dprintk(LOGHEAD "cleaning up\n", ir->p.name, ir->p.minor);
> +
> +	device_destroy(lirc_class, MKDEV(IRCTL_DEV_MAJOR, ir->p.minor));
> +
> +	if (ir->buf != ir->p.rbuf) {
> +		lirc_buffer_free(ir->buf);
> +		kfree(ir->buf);
> +	}
> +	ir->buf = NULL;
> +
> +	init_irctl(ir);
> +}

What's the init doing in a cleanup routine?  Oh, you initialize it again
becaus of the static array.  I think the right approach is to
dynamically allocate struct irctl.

> +static inline int add_to_buf(struct irctl *ir)

The inline thing also applies here and in various other placez.

> +{
> +	if (lirc_buffer_full(ir->buf)) {
> +		dprintk(LOGHEAD "buffer overflow\n",
> +			ir->p.name, ir->p.minor);
> +		return -EOVERFLOW;
> +	}
> +
> +	if (ir->p.add_to_buf) {
> +		int res = -ENODATA;
> +		int got_data = 0;
> +
> +		/* service the device as long as it is returning
> +		 * data and we have space
> +		 */
> +		while (!lirc_buffer_full(ir->buf)) {
> +			res = ir->p.add_to_buf(ir->p.data, ir->buf);
> +			if (res == SUCCESS)
> +				got_data++;
> +			else
> +				break;
> +		}
> +
> +		if (res == -ENODEV)
> +			kthread_stop(ir->task);
> +
> +		return got_data ? SUCCESS : res;
> +	}
> +
> +	return SUCCESS;

I guess success is a #define for 0?  Just user 0 directly.  Also the
kthread_stop here looks odd.  The normal way to use kthreads is to
start them when bringing up an interface of some sorts, and call
kthread_stop when the interface is brought down.  Doing it in a routine
like this screams "unclear lifetime rules".

> +	do {
> +		if (ir->open) {
> +			if (ir->jiffies_to_wait) {
> +				set_current_state(TASK_INTERRUPTIBLE);
> +				schedule_timeout(ir->jiffies_to_wait);
> +			} else {
> +				interruptible_sleep_on(
> +					ir->p.get_queue(ir->p.data));
> +			}

No sleep on, please.  But this one should be trivial to fix anyway, by
just changing this to a

	set_current_state(TASK_INTERRUPTIBLE);
	schedule();

and using wake_up_process in the wakeup path.

> +		} else {
> +			/* if device not opened so we can sleep half a second */
> +			set_current_state(TASK_INTERRUPTIBLE);
> +			schedule_timeout(HZ/2);
> +		}

Yikes.  This should use some form of more fine-grained wakeus.

> +int lirc_register_plugin(struct lirc_plugin *p)
> +{

Please add a kerneldoc comments for exported functions like this,
instead of the comments in the header.

> +	struct irctl *ir;
> +	int minor;
> +	int bytes_in_key;
> +	int err;
> +	DECLARE_COMPLETION(tn);
> +
> +	if (!p) {
> +		printk(KERN_ERR "lirc_dev: lirc_register_plugin: "
> +		       "plugin pointer must be not NULL!\n");
> +		err = -EBADRQC;
> +		goto out;
> +	}

No need for this, a null pointer derference should be a clear enough
warning for the writer of the broken pluging..

> +int lirc_unregister_plugin(int minor)

Why doesn't this one take a struct lirc_plugin pointer?

> +{
> +	struct irctl *ir;
> +	DECLARE_COMPLETION(tn);
> +	DECLARE_COMPLETION(tn2);

both completion seems unused.

> +	/* end up polling thread */
> +	if (ir->task) {
> +		wake_up_process(ir->task);
> +		kthread_stop(ir->task);
> +	}

kthread_stop already wakes the thread up. 

> +/*
> + * Recent kernels should handle this autmatically by increasing/decreasing
> + * use count when a dependant module is loaded/unloaded.
> + */
> +
> +	return SUCCESS;

The comment above looks superflous.

> +static int irctl_open(struct inode *inode, struct file *file)
> +{
> +	struct irctl *ir;
> +	int retval;
> +
> +	if (MINOR(inode->i_rdev) >= MAX_IRCTL_DEVICES) {

iminor.

> +	/* if the plugin has an open function use it instead */
> +	if (ir->p.fops && ir->p.fops->open)
> +		return ir->p.fops->open(inode, file);

in which case this 'plugin' should just install it's own fops.  Thanks
to cdev_add we can install fops at minor number granularity.

> +static unsigned int irctl_poll(struct file *file, poll_table *wait)
> +{
> +	struct irctl *ir = &irctls[MINOR(file->f_dentry->d_inode->i_rdev)];
> +	unsigned int ret;
> +
> +	dprintk(LOGHEAD "poll called\n", ir->p.name, ir->p.minor);
> +
> +	/* if the plugin has a poll function use it instead */
> +	if (ir->p.fops && ir->p.fops->poll)
> +		return ir->p.fops->poll(file, wait);
> +
> +	mutex_lock(&ir->buffer_lock);

->poll ust not sleep.

> +/*
> + *
> + */

???

> +
> +void *lirc_get_pdata(struct file *file)
> +{
> +	void *data = NULL;
> +
> +	if (file && file->f_dentry && file->f_dentry->d_inode &&
> +	    file->f_dentry->d_inode->i_rdev) {
> +		struct irctl *ir;
> +		ir = &irctls[MINOR(file->f_dentry->d_inode->i_rdev)];
> +		data = ir->p.data;
> +	}
> +
> +	return data;
> +}
> +EXPORT_SYMBOL(lirc_get_pdata);

Yikes. All these should never really be null.  But once you have a
struct file you should just use file->private_data.


> +/* #define LIRC_BUFF_POWER_OF_2 */
> +#ifdef LIRC_BUFF_POWER_OF_2
> +#define mod(n, div) ((n) & ((div) - 1))
> +#else
> +#define mod(n, div) ((n) % (div))
> +#endif

??

> +static inline void _lirc_buffer_clear(struct lirc_buffer *buf)
> +{
> +	buf->head = 0;
> +	buf->tail = 0;
> +	buf->fill = 0;
> +}
> +static inline int lirc_buffer_init(struct lirc_buffer *buf,
> +				    unsigned int chunk_size,
> +				    unsigned int size)
> +{
> +	/* Adjusting size to the next power of 2 would allow for
> +	 * inconditional LIRC_BUFF_POWER_OF_2 optimization */
> +	init_waitqueue_head(&buf->wait_poll);
> +	spin_lock_init(&buf->lock);
> +	_lirc_buffer_clear(buf);
> +	buf->chunk_size = chunk_size;
> +	buf->size = size;
> +	buf->data = kmalloc(size*chunk_size, GFP_KERNEL);
> +	if (buf->data == NULL)
> +		return -1;
> +	memset(buf->data, 0, size*chunk_size);
> +	return 0;
> +}
> +static inline void lirc_buffer_free(struct lirc_buffer *buf)
> +{
> +	kfree(buf->data);
> +	buf->data = NULL;
> +	buf->head = 0;
> +	buf->tail = 0;
> +	buf->fill = 0;
> +	buf->chunk_size = 0;
> +	buf->size = 0;
> +}

Please move these out of line.  And please document all the functions.
Or switch to a kfifo or the existing tty buffering helpers.

> +static inline void lirc_buffer_lock(struct lirc_buffer *buf,
> +				    unsigned long *flags)
> +{
> +	spin_lock_irqsave(&buf->lock, *flags);
> +}
> +static inline void lirc_buffer_unlock(struct lirc_buffer *buf,
> +				      unsigned long *flags)
> +{
> +	spin_unlock_irqrestore(&buf->lock, *flags);
> +}

Please don't do you own spinlock wrappers.


^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 01/18] lirc core device driver infrastructure
  2008-09-09 11:35     ` Janne Grunau
@ 2008-09-09 13:03       ` Andi Kleen
  2008-09-09 13:20         ` Janne Grunau
  0 siblings, 1 reply; 111+ messages in thread
From: Andi Kleen @ 2008-09-09 13:03 UTC (permalink / raw)
  To: Janne Grunau
  Cc: Andi Kleen, Jarod Wilson, linux-kernel, Jarod Wilson,
	Christoph Bartelmus, Mario Limonciello

> pr_printk is not found in include/ nor by google. please explain

pr_debug() sorry.

-Andi

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 01/18] lirc core device driver infrastructure
  2008-09-09 12:33       ` Sebastian Siewior
@ 2008-09-09 13:10         ` Janne Grunau
  0 siblings, 0 replies; 111+ messages in thread
From: Janne Grunau @ 2008-09-09 13:10 UTC (permalink / raw)
  To: Sebastian Siewior
  Cc: Jarod Wilson, linux-kernel, Jarod Wilson, Christoph Bartelmus,
	Mario Limonciello

On Tuesday 09 September 2008 14:33:35 Sebastian Siewior wrote:
> * Janne Grunau | 2008-09-09 11:53:48 [+0200]:
> >> >+
> >> >+	bytes_in_key = p->code_length/8 + (p->code_length%8 ? 1 : 0);
> >>
> >> did you actually pass checkpatch.pl ?
> >
> >yes, and while this is ugly I see no obvious CodingStyle violation.
> >
> >is following better?
> >
> >bytes_in_key = p->code_length/8;
> >if (p->code_length%8)
> >	bytes_in_key++;
>
> bytes_in_key = p->code_length / 8;
> if (p->code_length % 8)
> 	bytes_in_key++;
>
> I meant the missing spaces around / and %. Looking now at it, it
> seems that it behaves like DIV_ROUND_UP(). The magic of spaces :)

There is also BITS_TO_LONGS(x) which does the intended. I found it only 
since it uses DIV_ROUND_UP. Will use that. Thanks.

Janne

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 01/18] lirc core device driver infrastructure
  2008-09-09 13:03       ` Andi Kleen
@ 2008-09-09 13:20         ` Janne Grunau
  2008-09-12 16:46           ` Greg KH
  0 siblings, 1 reply; 111+ messages in thread
From: Janne Grunau @ 2008-09-09 13:20 UTC (permalink / raw)
  To: Andi Kleen
  Cc: Jarod Wilson, linux-kernel, Jarod Wilson, Christoph Bartelmus,
	Mario Limonciello

On Tuesday 09 September 2008 15:03:11 Andi Kleen wrote:
> > pr_printk is not found in include/ nor by google. please explain
>
> pr_debug() sorry.

pr_debug() does something else. that dprintk macro prints depending on a 
module parameter

Janne

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 01/18] lirc core device driver infrastructure
  2008-09-09  7:40   ` [PATCH 01/18] lirc core device driver infrastructure Sebastian Siewior
  2008-09-09  9:53     ` Janne Grunau
  2008-09-09 11:13     ` Alan Cox
@ 2008-09-09 13:27     ` Stefan Richter
  2008-09-09 17:03     ` Jarod Wilson
  3 siblings, 0 replies; 111+ messages in thread
From: Stefan Richter @ 2008-09-09 13:27 UTC (permalink / raw)
  To: Sebastian Siewior
  Cc: Jarod Wilson, linux-kernel, Jarod Wilson, Janne Grunau,
	Christoph Bartelmus, Mario Limonciello, Jonathan Corbet

Sebastian Siewior wrote:
>> --- /dev/null
>> +++ b/drivers/input/lirc/lirc_dev.c
...
>> +#include <linux/ioctl.h>
>> +#include <linux/fs.h>
>> +#include <linux/poll.h>
>> +#include <linux/smp_lock.h>
> if you need this than you use the BKL back. As far as I remember
> the ioctl() handler in kernel core no longer takes the BKL and I don't
> see any locking in irctl_ioctl(). 

Really?  .open() has been changed to be called without the BKL held, but 
.ioctl() is still called with BKL protection.  Currently, many .ioctl() 
implementations are replaced by .unlocked_ioctl() which take the BKL 
themselves if necessary or if it is not yet clear whether they would 
work without BKL protection.

...
>> +static struct file_operations fops = {
>> +	.read		= irctl_read,
>> +	.write		= irctl_write,
>> +	.poll		= irctl_poll,
>> +	.ioctl		= irctl_ioctl,
>> +	.open		= irctl_open,
>> +	.release	= irctl_close
>> +};

This should be audited for the following aspects:

   - Could there be a race condition between irctl_open() and
     lirc_dev_init()?  If yes, try to rework them to eliminate the race,
     or as a last resort take the BKL in irctl_open().

   - Does irctl_ioctl() require BKL protection, i.e. does it have to be
     serialized against itself and against irctl_open()?  If not, replace
     it by .unlocked_ioctl.  If yes, preferably convert it to
     .unlocked_ioctl too and add a local mutex for the necessary
     serialization.

(Added Cc: Jonathan Corbet to correct me if I'm wrong.)
-- 
Stefan Richter
-=====-==--- =--= -=--=
http://arcgraph.de/sr/

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 0/18] linux infrared remote control drivers
  2008-09-09 12:46 ` Christoph Hellwig
@ 2008-09-09 15:23   ` Jarod Wilson
  2008-09-09 18:27     ` Lennart Sorensen
  2008-09-09 15:34   ` Jon Smirl
  1 sibling, 1 reply; 111+ messages in thread
From: Jarod Wilson @ 2008-09-09 15:23 UTC (permalink / raw)
  To: Christoph Hellwig, Janne Grunau
  Cc: linux-kernel, Christoph Bartelmus, Eric Sandeen, Mario Limonciello

On Tuesday 09 September 2008 08:46:09 Christoph Hellwig wrote:
> On Tue, Sep 09, 2008 at 12:05:45AM -0400, Jarod Wilson wrote:
> > The following patch series adds 17 new drivers for assorted infrared
> > and/or RF remote control receivers and/or transmitters. These drivers
> > have long lived out-of-tree at http://www.lirc.org/, packaged as
> > 3rd-party modules by many distributions, and more recently, patched into
> > the kernels of at least Fedora and Ubuntu. The primary maintainer of
> > lirc, Christoph Bartelmus simply hasn't had the time to send these bits
> > upstream, and a few months back, gave me the go-ahead to take on the
> > task.
>
> Any reason this is a separate subsystem instead of just a bunch new
> drivers for the input subsystem?

IR device handling is often a bit, shall we say, "special", for a number of 
devices... Some receivers decode the waveform and give us something sensible 
the input layer could use (and some IR receivers do work that way), while 
other receivers pass along raw IR codes, which we require the use of the lirc 
daemon to decode and convert into something sensible. To complicate the matter 
further, a number of devices are either output-only devices, or both input and 
output devices.

There's been talk about converting lirc drivers over to using evdev in the 
long run, but evdev would need a decent amount of extending to be able to 
support everything lircd does right now.

> > Not all drivers have been tested with this codebase and certainly not all
> > devices they support, but its a solid place to start with these
> > in-kernel. Patches are against 2.6.27-rc5-git9 or so, and have been
> > tested running the same, at least in the cases where hardware was
> > available. These patches are also in the latest nightly Fedora rawhide
> > kernel, for those who want instant gratification[3]. Note that you also
> > need the lirc userspace to really do any meaningful testing...
>
> Can you first only send the actually tested drivers?  That way review
> an cleanup can focus on the core and those few and we might get them
> ready for 2.6.28.  Other drivers can than be added when testing and
> review is done.

I meant to include that the ordering of the patches was more or less from 
most-used/most-tested to least-used/least-tested, and a few of the patches do 
include Tested-by: lines in them. Basically, the first four or five drivers 
probably cover the majority of users, so getting them in first would provide 
the most bang-for-your-buck. But I can certainly re-send with only the tested 
drivers after we get through the first round of comments, if that's preferred 
over my approach of putting the higher-prio ones to the front of the ordering.

-- 
Jarod Wilson
jarod@redhat.com


^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 01/18] lirc core device driver infrastructure
  2008-09-09  4:05 ` [PATCH 01/18] lirc core device driver infrastructure Jarod Wilson
                     ` (3 preceding siblings ...)
  2008-09-09 13:01   ` Christoph Hellwig
@ 2008-09-09 15:33   ` Jonathan Corbet
  2008-09-12  0:12     ` Janne Grunau
  2008-09-10 13:08   ` Dmitry Torokhov
  5 siblings, 1 reply; 111+ messages in thread
From: Jonathan Corbet @ 2008-09-09 15:33 UTC (permalink / raw)
  To: Jarod Wilson
  Cc: linux-kernel, Jarod Wilson, Jarod Wilson, Janne Grunau,
	Christoph Bartelmus, Mario Limonciello

I think it's most cool that this code is finally making its way toward
the mainline.  What I have is mostly nits...

> +int lirc_unregister_plugin(int minor)
> +{
 ... 
> +	/* end up polling thread */
> +	if (ir->task) {
> +		wake_up_process(ir->task);
> +		kthread_stop(ir->task);
> +	}

kthread_stop() will wake up the process, so there is no need to do it
separately here.  In fact, it almost looks like the separate
wake_up_process() call could create an (unlikely) race where the thread
would miss the fact that it's supposed to stop and sleep again.  Fixing the
sleep_on() call there to use something like wait_event() would help in that
regard.


> +static int irctl_open(struct inode *inode, struct file *file)
> +{
> +	struct irctl *ir;
> +	int retval;
> +
> +	if (MINOR(inode->i_rdev) >= MAX_IRCTL_DEVICES) {

All of these might be better as iminor(inode)

> +		dprintk("lirc_dev [%d]: open result = -ENODEV\n",
> +			MINOR(inode->i_rdev));
> +		return -ENODEV;
> +	}
> +
> +	ir = &irctls[MINOR(inode->i_rdev)];
> +
> +	dprintk(LOGHEAD "open called\n", ir->p.name, ir->p.minor);
> +
> +	/* if the plugin has an open function use it instead */
> +	if (ir->p.fops && ir->p.fops->open)
> +		return ir->p.fops->open(inode, file);
> +
> +	if (mutex_lock_interruptible(&plugin_lock))
> +		return -ERESTARTSYS;

So the plugin open() function is called outside of any lock.  Note that
open() no longer has BKL protection as of 2.6.27.  This might all be OK,
but I hope you're convinced of it.

...
> +	if (ir->p.owner != NULL && try_module_get(ir->p.owner)) {
> +		++ir->open;
> +		retval = ir->p.set_use_inc(ir->p.data);

Why is there a set_use_inc() function separate from open()?

> +		if (retval != SUCCESS) {
> +			module_put(ir->p.owner);
> +			--ir->open;
> +		}
> +	} else {
> +		if (ir->p.owner == NULL)
> +			dprintk(LOGHEAD "no module owner!!!\n",
> +				ir->p.name, ir->p.minor);
> +
> +		retval = -ENODEV;
> +	}

If "no owner" is a fatal condition, it seems better to check it when the
plugin is registered.  (Also, BTW, your variant of dprintk() is confusing
to read - I was wondering where all the %'s were.  I still wonder,
actually.  dev_printk() would be better.)

> +static int irctl_close(struct inode *inode, struct file *file)
> +{
> +	struct irctl *ir = &irctls[MINOR(inode->i_rdev)];
> +
> +	dprintk(LOGHEAD "close called\n", ir->p.name, ir->p.minor);
> +
> +	/* if the plugin has a close function use it instead */
> +	if (ir->p.fops && ir->p.fops->release)
> +		return ir->p.fops->release(inode, file);
> +
> +	if (mutex_lock_interruptible(&plugin_lock))
> +		return -ERESTARTSYS;

Should this be interruptible?  You probably want the close call to get its
job done.  Maybe mutex_lock_killable() - and still do the cleanup on a
signal? 

> +static int irctl_ioctl(struct inode *inode, struct file *file,
> +		       unsigned int cmd, unsigned long arg)
> +{
> +	unsigned long mode;
> +	int result;
> +	struct irctl *ir = &irctls[MINOR(inode->i_rdev)];
> +
> +	dprintk(LOGHEAD "ioctl called (0x%x)\n",
> +		ir->p.name, ir->p.minor, cmd);
> +
> +	/* if the plugin has a ioctl function use it instead */
> +	if (ir->p.fops && ir->p.fops->ioctl)
> +		return ir->p.fops->ioctl(inode, file, cmd, arg);
> +
> +	if (ir->p.minor == NOPLUG || !ir->attached) {
> +		dprintk(LOGHEAD "ioctl result = -ENODEV\n",
> +			ir->p.name, ir->p.minor);
> +		return -ENODEV;
> +	}
> +
> +	/* Give the plugin a chance to handle the ioctl */
> +	if (ir->p.ioctl) {
> +		result = ir->p.ioctl(inode, file, cmd, arg);
> +		if (result != -ENOIOCTLCMD)
> +			return result;
> +	}

Why two ioctl() handlers?  It seems better to just have one way for plugins
to handle this call.

...
> +	default:
> +		result = -ENOIOCTLCMD;

Hmm, I note with interest that unlocked_ioctl() remaps -ENOIOCTLCMD to
-EINVAL, while regular, locked ioctl() (which this is) does not.  Not sure
what to make of that.

> +static ssize_t irctl_read(struct file *file,
> +			  char *buffer,
> +			  size_t length,
> +			  loff_t *ppos)
> +{
 ...
> +			schedule();
> +			set_current_state(TASK_INTERRUPTIBLE);
> +			if (!ir->attached) {
> +				ret = -ENODEV;
> +				break;
> +			}

How can ir->attached go to zero?  You checked it earlier and have been
holding the mutex ever since.

> +static ssize_t irctl_write(struct file *file, const char *buffer,
> +			   size_t length, loff_t *ppos)
> +{
> +	struct irctl *ir =
> &irctls[MINOR(file->f_dentry->d_inode->i_rdev)]; 
> +	dprintk(LOGHEAD "write called\n", ir->p.name, ir->p.minor);
> +
> +	/* if the plugin has a specific read function use it instead
> */
> +	if (ir->p.fops && ir->p.fops->write)
> +		return ir->p.fops->write(file, buffer, length, ppos);

Looks like you're using the "specific write function" instead :)

> +static struct file_operations fops = {
> +	.read		= irctl_read,
> +	.write		= irctl_write,
> +	.poll		= irctl_poll,
> +	.ioctl		= irctl_ioctl,
> +	.open		= irctl_open,
> +	.release	= irctl_close
> +};

You should probably set .owner too.

> +static int lirc_dev_init(void)
> +{
> +	int i;
> +
> +	for (i = 0; i < MAX_IRCTL_DEVICES; ++i)
> +		init_irctl(&irctls[i]);
> +
> +	if (register_chrdev(IRCTL_DEV_MAJOR, IRCTL_DEV_NAME, &fops))
> {
> +		printk(KERN_ERR "lirc_dev: register_chrdev
> failed\n");
> +		goto out;
> +	}
> +
> +	lirc_class = class_create(THIS_MODULE, "lirc");
> +	if (IS_ERR(lirc_class)) {
> +		printk(KERN_ERR "lirc_dev: class_create failed\n");
> +		goto out_unregister;
> +	}
> +
> +	printk(KERN_INFO "lirc_dev: IR Remote Control driver
> registered, "
> +	       "major %d \n", IRCTL_DEV_MAJOR);
> +
> +	return SUCCESS;
> +
> +out_unregister:
> +	/* unregister_chrdev returns void now */
> +	unregister_chrdev(IRCTL_DEV_MAJOR, IRCTL_DEV_NAME);
> +out:
> +	return -1;
> +}

Do you want to fail completely if class_create() fails?  What if somebody
already opened one of your devices?

> +static inline int lirc_buffer_init(struct lirc_buffer *buf,
> +				    unsigned int chunk_size,
> +				    unsigned int size)
> +{
> +	/* Adjusting size to the next power of 2 would allow for
> +	 * inconditional LIRC_BUFF_POWER_OF_2 optimization */
> +	init_waitqueue_head(&buf->wait_poll);
> +	spin_lock_init(&buf->lock);
> +	_lirc_buffer_clear(buf);
> +	buf->chunk_size = chunk_size;
> +	buf->size = size;
> +	buf->data = kmalloc(size*chunk_size, GFP_KERNEL);
> +	if (buf->data == NULL)
> +		return -1;
> +	memset(buf->data, 0, size*chunk_size);
> +	return 0;
> +}

All of these inlines probably shouldn't be.

jon

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 0/18] linux infrared remote control drivers
  2008-09-09 12:46 ` Christoph Hellwig
  2008-09-09 15:23   ` Jarod Wilson
@ 2008-09-09 15:34   ` Jon Smirl
  1 sibling, 0 replies; 111+ messages in thread
From: Jon Smirl @ 2008-09-09 15:34 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Jarod Wilson, linux-kernel, Jarod Wilson, Janne Grunau,
	Christoph Bartelmus, Eric Sandeen, Mario Limonciello

On 9/9/08, Christoph Hellwig <hch@infradead.org> wrote:
> On Tue, Sep 09, 2008 at 12:05:45AM -0400, Jarod Wilson wrote:
>
> > The following patch series adds 17 new drivers for assorted infrared and/or RF
>  > remote control receivers and/or transmitters. These drivers have long lived
>  > out-of-tree at http://www.lirc.org/, packaged as 3rd-party modules by many
>  > distributions, and more recently, patched into the kernels of at least Fedora
>  > and Ubuntu. The primary maintainer of lirc, Christoph Bartelmus simply hasn't
>  > had the time to send these bits upstream, and a few months back, gave me the
>  > go-ahead to take on the task.
>
>
> Any reason this is a separate subsystem instead of just a bunch new
>  drivers for the input subsystem?

I've been asking this one too. Can someone familiar with the guts of
input give an assessment of integrating LIRC in with the rest of
input? Don't we want these events handled via evdev so that ordering
between IR, mouse, keyboard will be maintained?

-- 
Jon Smirl
jonsmirl@gmail.com

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 02/18] lirc serial port receiver/transmitter device driver
  2008-09-09  4:05   ` [PATCH 02/18] lirc serial port receiver/transmitter device driver Jarod Wilson
  2008-09-09  4:05     ` [PATCH 03/18] lirc driver for 1st-gen Media Center Ed. USB IR transceivers Jarod Wilson
@ 2008-09-09 16:14     ` Jonathan Corbet
  2008-09-09 19:51       ` Stefan Lippers-Hollmann
  2008-09-10 17:40       ` Jarod Wilson
  1 sibling, 2 replies; 111+ messages in thread
From: Jonathan Corbet @ 2008-09-09 16:14 UTC (permalink / raw)
  To: Jarod Wilson
  Cc: linux-kernel, Jarod Wilson, Jarod Wilson, Janne Grunau,
	Christoph Bartelmus

> +config LIRC_SERIAL
> +	tristate "Homebrew Serial Port Receiver"
> +	default n
> +	depends on LIRC_DEV
> +	help
> +	  Driver for Homebrew Serial Port Receivers

What receivers might these be?  Do any actually exist?

> +#ifdef LIRC_SERIAL_IRDEO
> +static int type = LIRC_IRDEO;
> +#elif defined(LIRC_SERIAL_IRDEO_REMOTE)
> +static int type = LIRC_IRDEO_REMOTE;
> +#elif defined(LIRC_SERIAL_ANIMAX)
> +static int type = LIRC_ANIMAX;
> +#elif defined(LIRC_SERIAL_IGOR)
> +static int type = LIRC_IGOR;
> +#elif defined(LIRC_SERIAL_NSLU2)
> +static int type = LIRC_NSLU2;
> +#else
> +static int type = LIRC_HOMEBREW;
> +#endif

So where do all these LIRC_SERIAL_* macros come from?  I can't really tell
what this bunch of ifdeffery is doing or how one might influence it.

> +
> +static struct lirc_serial hardware[] = {
> +	/* home-brew receiver/transmitter */
> +	{
> +		UART_MSR_DCD,
> +		UART_MSR_DDCD,
> +		UART_MCR_RTS|UART_MCR_OUT2|UART_MCR_DTR,
> +		UART_MCR_RTS|UART_MCR_OUT2,
> +		send_pulse_homebrew,
> +		send_space_homebrew,
> +		(
> +#ifdef LIRC_SERIAL_TRANSMITTER
> +		 LIRC_CAN_SET_SEND_DUTY_CYCLE|
> +		 LIRC_CAN_SET_SEND_CARRIER|
> +		 LIRC_CAN_SEND_PULSE|
> +#endif
> +		 LIRC_CAN_REC_MODE2)
> +	},

It would be really nice to use the .field=value structure initialization
conventions here.

> +#if defined(__i386__)
> +/*
> +  From:
> +  Linux I/O port programming mini-HOWTO
> +  Author: Riku Saikkonen <Riku.Saikkonen@hut.fi>
> +  v, 28 December 1997
> +
> +  [...]
> +  Actually, a port I/O instruction on most ports in the 0-0x3ff range
> +  takes almost exactly 1 microsecond, so if you're, for example,using
> +  the parallel port directly, just do additional inb()s from that port
> +  to delay.
> +  [...]
> +*/
> +/* transmitter latency 1.5625us 0x1.90 - this figure arrived at from
> + * comment above plus trimming to match actual measured frequency.
> + * This will be sensitive to cpu speed, though hopefully most of the 1.5us
> + * is spent in the uart access.  Still - for reference test machine was a
> + * 1.13GHz Athlon system - Steve
> + */
> +
> +/* changed from 400 to 450 as this works better on slower machines;
> +   faster machines will use the rdtsc code anyway */
> +
> +#define LIRC_SERIAL_TRANSMITTER_LATENCY 450
> +
> +#else
> +
> +/* does anybody have information on other platforms ? */
> +/* 256 = 1<<8 */
> +#define LIRC_SERIAL_TRANSMITTER_LATENCY 256
> +
> +#endif  /* __i386__ */

This is a little scary.  Maybe hrtimers would be a better way of handling
your timing issues?

> +static inline unsigned int sinp(int offset)
> +{
> +#if defined(LIRC_ALLOW_MMAPPED_IO)
> +	if (iommap != 0) {
> +		/* the register is memory-mapped */
> +		offset <<= ioshift;
> +		return readb(io + offset);
> +	}
> +#endif
> +	return inb(io + offset);
> +}

This all looks like a reimplementation of ioport_map() and the associated
ioread*() and iowrite*() functions...?

> +#ifdef USE_RDTSC
> +/* Version that uses Pentium rdtsc instruction to measure clocks */
> +
> +/* This version does sub-microsecond timing using rdtsc instruction,
> + * and does away with the fudged LIRC_SERIAL_TRANSMITTER_LATENCY
> + * Implicitly i586 architecture...  - Steve
> + */
> +
> +static inline long send_pulse_homebrew_softcarrier(unsigned long length) 
> +{
> +	int flag;
> +	unsigned long target, start, now;
> +
> +	/* Get going quick as we can */
> +	rdtscl(start); on();
> +	/* Convert length from microseconds to clocks */
> +	length *= conv_us_to_clocks;
> +	/* And loop till time is up - flipping at right intervals */
> +	now = start;
> +	target = pulse_width;
> +	flag = 1;
> +	while ((now-start) < length) {
> +		/* Delay till flip time */
> +		do {
> +			rdtscl(now);
> +		} while ((now-start) < target);

This looks like a hard busy wait, without even an occasional, polite,
cpu_relax() call.  There's got to be a better way?

The i2c code has the result of a lot of bit-banging work, I wonder if
there's something there which could be helpful here.

> +static irqreturn_t irq_handler(int i, void *blah)
> +{
> +	struct timeval tv;
> +	int status, counter, dcd;
> +	long deltv;
> +	int data;
> +	static int last_dcd = -1;
> +
> +	if ((sinp(UART_IIR) & UART_IIR_NO_INT)) {
> +		/* not our interrupt */
> +		return IRQ_RETVAL(IRQ_NONE);
> +	}

That should just be IRQ_NONE, no?

> +static void hardware_init_port(void)
> +{
> +	unsigned long flags;
> +	local_irq_save(flags);

That won't help you if an interrupt is handled by another processor.  This
needs proper locking, methinks.

Nothing in this function does anything to assure itself that the port
actually exists and is the right kind of hardware.  Maybe that can't really
be done with this kind of device?

> +static int init_port(void)
> +{
 ...
> +	if (sense == -1) {
> +		/* wait 1/2 sec for the power supply */
> +
> +		set_current_state(TASK_INTERRUPTIBLE);
> +		schedule_timeout(HZ/2);

msleep(), maybe?

> +static int set_use_inc(void *data)
> +{
> +	int result;
> +	unsigned long flags;
> +
> +	/* Init read buffer. */
> +	if (lirc_buffer_init(&rbuf, sizeof(int), RBUF_LEN) < 0)
> +		return -ENOMEM;
> +
> +	/* initialize timestamp */
> +	do_gettimeofday(&lasttv);
> +
> +	result = request_irq(irq, irq_handler,
> +			   IRQF_DISABLED | (share_irq ? IRQF_SHARED : 0),
> +			   LIRC_DRIVER_NAME, (void *)&hardware);
> +
> +	switch (result) {
> +	case -EBUSY:
> +		printk(KERN_ERR LIRC_DRIVER_NAME ": IRQ %d busy\n", irq);
> +		lirc_buffer_free(&rbuf);
> +		return -EBUSY;
> +	case -EINVAL:
> +		printk(KERN_ERR LIRC_DRIVER_NAME
> +		       ": Bad irq number or handler\n");
> +		lirc_buffer_free(&rbuf);
> +		return -EINVAL;
> +	default:
> +		dprintk("Interrupt %d, port %04x obtained\n", irq,
> io);
> +		break;
> +	};
> +
> +	local_irq_save(flags);
> +
> +	/* Set DLAB 0. */
> +	soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
> +
> +	soutp(UART_IER, sinp(UART_IER)|UART_IER_MSI);
> +
> +	local_irq_restore(flags);
> +
> +	return 0;
> +}

OK, so set_use_inc() really is just an open() function.  It still seems
like a strange duplication.

Again, local_irq_save() seems insufficient here.  You need a lock to
serialize access to the hardware.

jon

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 01/18] lirc core device driver infrastructure
  2008-09-09  7:40   ` [PATCH 01/18] lirc core device driver infrastructure Sebastian Siewior
                       ` (2 preceding siblings ...)
  2008-09-09 13:27     ` Stefan Richter
@ 2008-09-09 17:03     ` Jarod Wilson
  2008-09-11 18:30       ` Christoph Bartelmus
  3 siblings, 1 reply; 111+ messages in thread
From: Jarod Wilson @ 2008-09-09 17:03 UTC (permalink / raw)
  To: Sebastian Siewior, Janne Grunau
  Cc: linux-kernel, Christoph Bartelmus, Mario Limonciello

On Tuesday 09 September 2008 03:40:18 Sebastian Siewior wrote:
> >diff --git a/drivers/input/lirc/Makefile b/drivers/input/lirc/Makefile
> >new file mode 100644
> >index 0000000..cdb4c45
> >--- /dev/null
> >+++ b/drivers/input/lirc/Makefile
> >@@ -0,0 +1,8 @@
> >+# Makefile for the lirc drivers.
> >+#
> >+
> >+# Each configuration option enables a list of files.
> >+
> >+EXTRA_CFLAGS =-DIRCTL_DEV_MAJOR=61 -DLIRC_SERIAL_TRANSMITTER -I$(src)
>
> Do you rely on this specific major? Since your daemon opens /dev/lirc0
> you don't need a fixed major do you?

Good question. Quite honestly, I'm not sure. Christoph?

> LIRC_SERIAL_TRANSMITTER is used in patch 2 and just to enable a module
> options. Since it is always the case, please remove it.
> I haven't found the source of $src. It is probably a relic.

Janne took care of these. Heck, he's already replied, I'll just leave out the 
parts he already replied to...

> >+++ b/drivers/input/lirc/lirc_dev.c
> >@@ -0,0 +1,809 @@
> >+/*
> >+ * LIRC base driver
> >+ *
> >+ * (L) by Artur Lipowski <alipowski@interia.pl>
>
> Is that L here on purpose?

Historical. I think it is supposed to signify that this was originally 
licensed and written by Artur, but simply removing "(L) " is fine (here and in 
lirc_dev.h).

> >+#include <linux/smp_lock.h>
>
> if you need this than you use the BKL back. As far as I remember
> the ioctl() handler in kernel core no longer takes the BKL and I don't
> see any locking in irctl_ioctl().


> >+/*  helper function
> >+ *  initializes the irctl structure
> >+ */
>
> For all comments above functions:
> - Please use the default comment style.

Apologies, I started on that, didn't finish, then forgot about it. Will do.

> - don't comment obvious things
> - please use kernel doc if
> - please comment the prototypes but the actual function.

Working on this too.

> >+	bytes_in_key = p->code_length/8 + (p->code_length%8 ? 1 : 0);
>
> did you actually pass checkpatch.pl ?

Yeah, but now that you point that out, I'm not sure how... :)

Taken care of by simply using BITS_TO_LONG() instead.

> >+EXPORT_SYMBOL(lirc_register_plugin);
>
> Is EXPORT_SYMBOL_GPL() possible?

Should be, I'm definitely not aware of any non-GPL users.

> >+EXPORT_SYMBOL(lirc_unregister_plugin);

Ditto.

> >+/* ----------------------------------------------------------------------
> > */
>
> I hate those

Me too. Gone.

-- 
Jarod Wilson
jarod@redhat.com


^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 0/18] linux infrared remote control drivers
  2008-09-09 15:23   ` Jarod Wilson
@ 2008-09-09 18:27     ` Lennart Sorensen
  2008-09-09 18:34       ` Jarod Wilson
  0 siblings, 1 reply; 111+ messages in thread
From: Lennart Sorensen @ 2008-09-09 18:27 UTC (permalink / raw)
  To: Jarod Wilson
  Cc: Christoph Hellwig, Janne Grunau, linux-kernel,
	Christoph Bartelmus, Eric Sandeen, Mario Limonciello

On Tue, Sep 09, 2008 at 11:23:56AM -0400, Jarod Wilson wrote:
> I meant to include that the ordering of the patches was more or less from 
> most-used/most-tested to least-used/least-tested, and a few of the patches do 
> include Tested-by: lines in them. Basically, the first four or five drivers 
> probably cover the majority of users, so getting them in first would provide 
> the most bang-for-your-buck. But I can certainly re-send with only the tested 
> drivers after we get through the first round of comments, if that's preferred 
> over my approach of putting the higher-prio ones to the front of the ordering.

The soundgraph imon driver is number 8, but it works great for me, and lots
of cases include that one (at least silverstone cases).  I think more
than the first 4 or 5 are in very active use.

-- 
Len Sorensen

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 0/18] linux infrared remote control drivers
  2008-09-09 18:27     ` Lennart Sorensen
@ 2008-09-09 18:34       ` Jarod Wilson
  0 siblings, 0 replies; 111+ messages in thread
From: Jarod Wilson @ 2008-09-09 18:34 UTC (permalink / raw)
  To: Lennart Sorensen
  Cc: Christoph Hellwig, Janne Grunau, linux-kernel,
	Christoph Bartelmus, Eric Sandeen, Mario Limonciello

On Tuesday 09 September 2008 14:27:10 Lennart Sorensen wrote:
> On Tue, Sep 09, 2008 at 11:23:56AM -0400, Jarod Wilson wrote:
> > I meant to include that the ordering of the patches was more or less from
> > most-used/most-tested to least-used/least-tested, and a few of the
> > patches do include Tested-by: lines in them. Basically, the first four or
> > five drivers probably cover the majority of users, so getting them in
> > first would provide the most bang-for-your-buck. But I can certainly
> > re-send with only the tested drivers after we get through the first round
> > of comments, if that's preferred over my approach of putting the
> > higher-prio ones to the front of the ordering.
>
> The soundgraph imon driver is number 8, but it works great for me, and lots
> of cases include that one (at least silverstone cases).  I think more
> than the first 4 or 5 are in very active use.

Yeah, I realized imon was much lower in the sort order after my reply there, 
which was a bad initial move by me, since its actually one of the ones that 
has definite positive testing feedback already. I believe quite a number of 
vendors of HTPC cases use imon lcd (or vfd) and ir combo devices.

-- 
Jarod Wilson
jarod@redhat.com


^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 03/18] lirc driver for 1st-gen Media Center Ed. USB IR transceivers
  2008-09-09  4:05     ` [PATCH 03/18] lirc driver for 1st-gen Media Center Ed. USB IR transceivers Jarod Wilson
  2008-09-09  4:05       ` [PATCH 04/18] lirc driver for 2nd-gen and later " Jarod Wilson
@ 2008-09-09 19:21       ` Jonathan Corbet
  2008-09-09 23:59         ` Janne Grunau
  2008-09-10  0:04         ` Janne Grunau
  1 sibling, 2 replies; 111+ messages in thread
From: Jonathan Corbet @ 2008-09-09 19:21 UTC (permalink / raw)
  To: Jarod Wilson; +Cc: linux-kernel, Janne Grunau, Christoph Bartelmus

> +config LIRC_MCEUSB
> +	tristate "Microsoft Media Center Ed. Receiver, v1"
> +	default n
> +	depends on LIRC_DEV
> +	help
> +	  Driver for the Microsoft Media Center Ed. Receiver, v1

A little more verbosity in these help texts might be nice.  

> + * 2003_11_11 - Restructured to minimalize code interpretation in the
> + *	      driver. The normal use case will be with lirc.
> + *
> + * 2004_01_01 - Removed all code interpretation. Generate mode2 data
> + *	      for passing off to lirc. Cleanup
> + *
> + * 2004_01_04 - Removed devfs handle. Put in a temporary workaround
> + *	      for a known issue where repeats generate two
> + *	      sequential spaces (last_was_repeat_gap)
> + *
> + * 2004_02_17 - Changed top level api to no longer use fops, and
> + *	      instead use new interface for polling via
> + *	      lirc_thread. Restructure data read/mode2 generation to
> + *	      a single pass, reducing number of buffers. Rev to .2
> + *
> + * 2004_02_27 - Last of fixups to plugin->add_to_buf API. Properly
> + *	      handle broken fragments from the receiver. Up the
> + *	      sample rate and remove any pacing from
> + *	      fetch_more_data. Fixes all known issues.

General convention is that this sort of changelog information belongs in
the SCM, not in the code.  That's doubly true for the USB skeleton driver
info.

> +#include <linux/smp_lock.h>

It doesn't look like this include is needed.

> +/* Structure to hold all of our device specific stuff */
> +struct usb_skel {

Perhaps renaming this structure to something more directly descriptive
would make sense?

> +	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 */

Minor numbers don't necessarily fit in an unsigned char.

> +	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 */
> +
> +	atomic_t write_busy;		/* true iff write urb is busy */
> +	struct completion write_finished; /* wait for the write to finish */
> +
> +	wait_queue_head_t wait_q; /* for timeouts */
> +	int open_count;		/* number of times this port has been opened */
> +	struct mutex sem;	/* locks this structure */

"sem" is not a semaphore; it should probably have a different name.

> +static void mceusb_setup(struct usb_device *udev)
> +{
> +	char data[8];
> +	int res;
> +
> +	memset(data, 0, 8);
> +
> +	/* Get Status */
> +	res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
> +			      USB_REQ_GET_STATUS, USB_DIR_IN,
> +			      0, 0, data, 2, HZ * 3);

res is set many times in this function, but it is never checked.  It seems
to me like the addition of some error handling would be a good idea.

...

> +	/* These two are sent by the windows driver, but stall for
> +	 * me. I dont have an analyzer on the linux side so i can't
> +	 * see what is actually different and why the device takes
> +	 * issue with them
> +	 */

Hmm...how was that information obtained?  If this driver was
reverse-engineered, it would be good to know just what process was
followed. 

> +static int msir_fetch_more_data(struct usb_skel *dev, int dont_block)
> +{
...
> +			/* 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;

As others have noted, I think, getting new sleep_on() calls into the kernel
is kind of a hard sell.

> +/**
> + *	mceusb_probe
> + *
> + *	Called by the usb core when a new device is connected that it
> + *	thinks this driver might be interested in.
> + */
> +static int mceusb_probe(struct usb_interface *interface,
> +			const struct usb_device_id *id)
> +{

...
> +
> +	/* select a "subminor" number (part of a minor number) */
> +	down(&minor_table_mutex);

So...this driver has a mutex called "sem" and a semaphore called
"minor_table_mutex".  I suspect that minor_table_mutex should, in fact, be
a mutex.

...
> +error:
> +	mceusb_delete(dev);
> +	dev = NULL;
> +	dprintk("%s: retval = %x", __func__, retval);
> +	up(&minor_table_mutex);
> +	return retval;
> +}

This will leak the memory allocated for dev.  It also leaves the entry in
minor_table pointing to a nonfunctional device.

jon

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 02/18] lirc serial port receiver/transmitter device driver
  2008-09-09 16:14     ` [PATCH 02/18] lirc serial port receiver/transmitter device driver Jonathan Corbet
@ 2008-09-09 19:51       ` Stefan Lippers-Hollmann
  2008-09-09 19:56         ` Jarod Wilson
  2008-09-10 17:40       ` Jarod Wilson
  1 sibling, 1 reply; 111+ messages in thread
From: Stefan Lippers-Hollmann @ 2008-09-09 19:51 UTC (permalink / raw)
  To: Jonathan Corbet
  Cc: Jarod Wilson, linux-kernel, Jarod Wilson, Janne Grunau,
	Christoph Bartelmus

Hi

On Dienstag, 9. September 2008, Jonathan Corbet wrote:
> > +config LIRC_SERIAL
> > +	tristate "Homebrew Serial Port Receiver"
> > +	default n
> > +	depends on LIRC_DEV
> > +	help
> > +	  Driver for Homebrew Serial Port Receivers
> 
> What receivers might these be?  Do any actually exist?
[...]

This might actually be the most common setup, as it is very easy to 
build[1] with minimal soldering skills and has also been used by various 
vendors (older models of the TechniSat SkyStar2 DVB-S budget card, to name 
just one example; it is only slowly being replaced by alternative means, 
as serial ports are becoming rare on newer systems.

Regards
	Stefan Lippers-Hollmann

[1]	http://www.lirc.org/receivers.html

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 02/18] lirc serial port receiver/transmitter device driver
  2008-09-09 19:51       ` Stefan Lippers-Hollmann
@ 2008-09-09 19:56         ` Jarod Wilson
  0 siblings, 0 replies; 111+ messages in thread
From: Jarod Wilson @ 2008-09-09 19:56 UTC (permalink / raw)
  To: Stefan Lippers-Hollmann, Janne Grunau
  Cc: Jonathan Corbet, linux-kernel, Christoph Bartelmus

On Tuesday 09 September 2008 15:51:12 Stefan Lippers-Hollmann wrote:
> Hi
>
> On Dienstag, 9. September 2008, Jonathan Corbet wrote:
> > > +config LIRC_SERIAL
> > > +	tristate "Homebrew Serial Port Receiver"
> > > +	default n
> > > +	depends on LIRC_DEV
> > > +	help
> > > +	  Driver for Homebrew Serial Port Receivers
> >
> > What receivers might these be?  Do any actually exist?
>
> [...]
>
> This might actually be the most common setup, as it is very easy to
> build[1] with minimal soldering skills and has also been used by various
> vendors (older models of the TechniSat SkyStar2 DVB-S budget card, to name
> just one example; it is only slowly being replaced by alternative means,
> as serial ports are becoming rare on newer systems.
>
> Regards
> 	Stefan Lippers-Hollmann
>
> [1]	http://www.lirc.org/receivers.html

One frequently-used source of pre-built serial receivers (and transmitters) 
here in the US is http://irblaster.info/.

Hey, that actually just reminded me... I think I *do* have a serial IR 
receiver laying about somewhere that came w/one of my TechniSat SkyStar 
HD-5000 cards... Thank you, Stefan! ;)

-- 
Jarod Wilson
jarod@redhat.com


^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 04/18] lirc driver for 2nd-gen and later Media Center Ed. USB IR transceivers
  2008-09-09  4:05       ` [PATCH 04/18] lirc driver for 2nd-gen and later " Jarod Wilson
  2008-09-09  4:05         ` [PATCH 05/18] lirc driver for i2c-based IR receivers Jarod Wilson
@ 2008-09-09 23:30         ` Jonathan Corbet
  2008-09-10  0:36           ` Janne Grunau
  2008-09-11  9:21           ` Adrian Bunk
  1 sibling, 2 replies; 111+ messages in thread
From: Jonathan Corbet @ 2008-09-09 23:30 UTC (permalink / raw)
  To: Jarod Wilson; +Cc: linux-kernel, Janne Grunau, Christoph Bartelmus

On Tue,  9 Sep 2008 00:05:49 -0400
Jarod Wilson <jwilson@redhat.com> wrote:

> +/*
> + * LIRC driver for Philips eHome USB Infrared Transceiver
> + * and the Microsoft MCE 2005 Remote Control
> + *
> + * (C) by Martin A. Blatter <martin_a_blatter@yahoo.com>
> + *
> + * Transmitter support and reception code cleanup.
> + * (C) by Daniel Melander <lirc@rajidae.se>

As I understand it, proper assertions of copyright need the word
"copyright" and a year; the "(C)" trigraph doesn't have any legal meaning.

> + * 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.

My guess is that this will not be a major impediment to mainline inclusion;
this comment can probably go.

> +/* lock irctl structure */
> +#define IRLOCK		mutex_lock(&ir->lock)
> +#define IRUNLOCK	mutex_unlock(&ir->lock)

Might I request that these go away?  They can only obfuscate the code.

> +/* init strings */
> +static char init1[] = {0x00, 0xff, 0xaa, 0xff, 0x0b};
> +static char init2[] = {0xff, 0x18};
> +
> +static char pin_init1[] = { 0x9f, 0x07};
> +static char pin_init2[] = { 0x9f, 0x13};
> +static char pin_init3[] = { 0x9f, 0x0d};

Documentation here would be nice; what do these magic numbers do?

> +/* request incoming or send outgoing usb packet - used to initialize remote */
> +static void request_packet_async(struct irctl *ir,
> +				 struct usb_endpoint_descriptor *ep,
> +				 unsigned char *data, int size, int urb_type)

What is the locking regime for this function?  As far as I can tell, it's
called with no locking at all, even though it's manipulating the irctl
structure. 

> +{
...
> +				if (urb_type == PHILUSB_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);

This kind of formatting suggests that, just maybe, the control structures
in this function have been nested too deeply.

> +static int unregister_from_lirc(struct irctl *ir)
> +{
> +	struct lirc_plugin *p = ir->p;
> +	int devnum;
> +	int rtn;
> +
> +	devnum = ir->devnum;
> +	dprintk(DRIVER_NAME "[%d]: unregister from lirc called\n", devnum);
> +
> +	rtn = lirc_unregister_plugin(p->minor);
> +	if (rtn > 0) {
> +		printk(DRIVER_NAME "[%d]: error in lirc_unregister minor: %d\n"
> +			"Trying again...\n", devnum, p->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_plugin(p->minor);
> +		if (rtn > 0)
> +			printk(DRIVER_NAME "[%d]: lirc_unregister failed\n",
> +			devnum);
> +	}

Looking at lirc_unregister_plugin(), I don't see any cause of failure that
could be expected to go away one second later.  But this code has the look
of something somebody actually needed once upon a time.  Am I missing
something?  It seems there should be a better way.

> +static int set_use_inc(void *data)
> +{
> +	struct irctl *ir = data;
> +
> +	if (!ir) {
> +		printk(DRIVER_NAME "[?]: set_use_inc called with no context\n");
> +		return -EIO;
> +	}
> +	dprintk(DRIVER_NAME "[%d]: set use inc\n", ir->devnum);
> +
> +	if (!ir->flags.connected) {
> +		if (!ir->usbdev)
> +			return -ENOENT;
> +		ir->flags.connected = 1;
> +	}
> +
> +	return SUCCESS;
> +}
> +
> +static void set_use_dec(void *data)
> +{
> +	struct irctl *ir = data;
> +
> +	if (!ir) {
> +		printk(DRIVER_NAME "[?]: set_use_dec called with no context\n");
> +		return;
> +	}
> +	dprintk(DRIVER_NAME "[%d]: set use dec\n", ir->devnum);
> +
> +	if (ir->flags.connected) {
> +		IRLOCK;
> +		ir->flags.connected = 0;
> +		IRUNLOCK;
> +	}
> +}

One function sets ir->flags.connected under lock, the other does not.  Is
that what was intended?

Also, set_use_inc() is returning a normal zero-or-negative-error status to
an outside caller.  It shouldn't return a locally-defined symbol like
SUCCESS.  I'd just use "return 0;". 

> +static ssize_t lirc_write(struct file *file, const char *buf,
> +			  size_t n, loff_t *ppos)
> +{
> +	int i, count = 0, cmdcount = 0;
> +	struct irctl *ir = NULL;
> +	int 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;

That looks like roughly 1400 bytes on the stack - on a 32-bit system.

[...]

> +	/* delay with the closest number of ticks */
> +	set_current_state(TASK_INTERRUPTIBLE);
> +	schedule_timeout(usecs_to_jiffies(signal_duration));

Why do you need to delay here?  And wouldn't something like msleep() be a
better way to do it?

lirc_write() seems short on locking.

> +static void set_transmitter_mask(struct irctl *ir, unsigned int mask)
> +{
> +	if (ir->flags.transmitter_mask_inverted)
> +		ir->transmitter_mask = (mask != 0x03 ? mask ^ 0x03 : mask) << 1;
> +	else
> +		ir->transmitter_mask = mask;
> +}

What this short function is doing is not entirely obvious to me.

> +/* Sets the send carrier frequency */
> +static int set_send_carrier(struct irctl *ir, int carrier)
> +{
> +	int clk = 10000000;
> +	int prescaler = 0, divisor = 0;
> +	unsigned char cmdbuf[] = { 0x9F, 0x06, 0x01, 0x80 };

And this sequence of magic numbers does...?

> +static int lirc_ioctl(struct inode *node, struct file *filep,
> +		      unsigned int cmd, unsigned long arg)
> +{
> +	int result;
> +	unsigned int ivalue;
> +	unsigned long lvalue;
> +	struct irctl *ir = NULL;
> +
> +	/* Retrieve lirc_plugin 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;
> +
> +		default: /* Unsupported transmitter mask */
> +			return MCE_MAX_CHANNELS;
> +		}
> +
> +		dprintk(DRIVER_NAME ": SET_TRANSMITTERS mask=%d\n", ivalue);
> +		break;
> +
> +	case LIRC_GET_SEND_MODE:
> +
> +		result = put_user(LIRC_SEND2MODE(LIRC_CAN_SEND_PULSE &
> +						 LIRC_CAN_SEND_MASK),
> +				  (unsigned long *) arg);
> +
> +		if (result)
> +			return result;
> +		break;
> +
> +	case LIRC_SET_SEND_MODE:
> +
> +		result = get_user(lvalue, (unsigned long *) arg);
> +
> +		if (result)
> +			return result;
> +		if (lvalue != (LIRC_MODE_PULSE&LIRC_CAN_SEND_MASK))
> +			return -EINVAL;
> +		break;
> +
> +	case LIRC_SET_SEND_CARRIER:
> +
> +		result = get_user(ivalue, (unsigned int *) arg);
> +		if (result)
> +			return result;
> +
> +		set_send_carrier(ir, ivalue);
> +		break;
> +
> +	default:
> +		return -ENOIOCTLCMD;
> +	}
> +
> +	return 0;
> +}
> +
> +static struct file_operations lirc_fops = {
> +	.write	= lirc_write,
> +};

My first reaction here was that .ioctl=lirc_ioctl is missing.  But, of
course, it's the plugin ioctl() function instead.  I still think that needs
some cleaning up.

> +static int usb_remote_probe(struct usb_interface *intf,
> +				const struct usb_device_id *id)
> +{

[...]

> +mem_failure_switch:
> +
> +	/* free allocated memory incase of failure */
> +	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(plugin);
> +	case 2:
> +		kfree(ir);
> +	case 1:
> +		printk(DRIVER_NAME "[%d]: out of memory (code=%d)\n",
> +			devnum, mem_failure);
> +		return -ENOMEM;
> +	}

Usually this kind of error recovery is handled at the end of the function
with a series of cascading goto labels.  To find it wedged into the middle
of this (very long) function as a switch is a bit disorienting.  Normal
execution will just route around the whole thing via the (missing) default
branch.  I think it would be better to put it at the end and use the normal
conventions that everybody knows how to read.

> +	plugin->minor = minor;
> +	ir->p = plugin;
> +	ir->devnum = devnum;
> +	ir->usbdev = dev;
> +	ir->len_in = maxp;
> +	ir->flags.connected = 0;
> +	ir->flags.pinnacle = is_pinnacle;
> +	ir->flags.transmitter_mask_inverted =
> +		usb_match_id(intf, transmitter_mask_list) ? 0 : 1;

At this point, the plugin is already registered, but you're still
initializing it.  Are you sure there's no potential for a race here?

> +#ifdef MODULE
> +static int __init usb_remote_init(void)
> +{

I'm curious: you don't need to initialize if not compiled as a module?

> +	int i;
> +
> +	printk(KERN_INFO "\n");
> +	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");
> +
> +	request_module("lirc_dev");

You're calling functions from lirc_dev.c, so modprobe will not load this
module until lirc_dev is already there.  There's no point in trying to load
it explicitly with request_module().

> +	i = usb_register(&usb_remote_driver);
> +	if (i < 0) {
> +		printk(DRIVER_NAME ": usb register failed, result = %d\n", i);
> +		return -ENODEV;
> +	}
> +
> +	return SUCCESS;
> +}

"return 0;" please.

jon

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 03/18] lirc driver for 1st-gen Media Center Ed. USB IR transceivers
  2008-09-09 19:21       ` [PATCH 03/18] lirc driver for 1st-gen " Jonathan Corbet
@ 2008-09-09 23:59         ` Janne Grunau
  2008-09-10  1:39           ` Jarod Wilson
  2008-09-10  0:04         ` Janne Grunau
  1 sibling, 1 reply; 111+ messages in thread
From: Janne Grunau @ 2008-09-09 23:59 UTC (permalink / raw)
  To: Jonathan Corbet; +Cc: Jarod Wilson, linux-kernel, Christoph Bartelmus, dconti

On Tuesday 09 September 2008 21:21:40 Jonathan Corbet wrote:
>
> > + * 2003_11_11 - Restructured to minimalize code interpretation in
> > the + *	      driver. The normal use case will be with lirc.
> > + *
> > + * 2004_01_01 - Removed all code interpretation. Generate mode2
> > data + *	      for passing off to lirc. Cleanup
> > + *
> > + * 2004_01_04 - Removed devfs handle. Put in a temporary
> > workaround + *	      for a known issue where repeats generate two
> > + *	      sequential spaces (last_was_repeat_gap)
> > + *
> > + * 2004_02_17 - Changed top level api to no longer use fops, and
> > + *	      instead use new interface for polling via
> > + *	      lirc_thread. Restructure data read/mode2 generation to
> > + *	      a single pass, reducing number of buffers. Rev to .2
> > + *
> > + * 2004_02_27 - Last of fixups to plugin->add_to_buf API. Properly
> > + *	      handle broken fragments from the receiver. Up the
> > + *	      sample rate and remove any pacing from
> > + *	      fetch_more_data. Fixes all known issues.
>
> General convention is that this sort of changelog information belongs
> in the SCM, not in the code.  That's doubly true for the USB skeleton
> driver info.

I don't really care and I agree that the SCM is the preferred place for 
the information. The only problem is that the info is in a different 
SCM (lirc cvs repository).

I've removed the changelogs from all files as long as no names wre 
mentioned.

> > +#include <linux/smp_lock.h>
>
> It doesn't look like this include is needed.

removed

> > +/* Structure to hold all of our device specific stuff */
> > +struct usb_skel {
>
> Perhaps renaming this structure to something more directly
> descriptive would make sense?

renamed to 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 */
>
> Minor numbers don't necessarily fit in an unsigned char.
>
> > +	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
> > */ +
> > +	atomic_t write_busy;		/* true iff write urb is busy */
> > +	struct completion write_finished; /* wait for the write to finish
> > */ +
> > +	wait_queue_head_t wait_q; /* for timeouts */
> > +	int open_count;		/* number of times this port has been opened */
> > +	struct mutex sem;	/* locks this structure */
>
> "sem" is not a semaphore; it should probably have a different name.

renamed to lock

> > +static void mceusb_setup(struct usb_device *udev)
> > +{
> > +	char data[8];
> > +	int res;
> > +
> > +	memset(data, 0, 8);
> > +
> > +	/* Get Status */
> > +	res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
> > +			      USB_REQ_GET_STATUS, USB_DIR_IN,
> > +			      0, 0, data, 2, HZ * 3);
>
> res is set many times in this function, but it is never checked.  It
> seems to me like the addition of some error handling would be a good
> idea.

sigh, It would be a good idea, not sure if I'm motivated enough to add 
that to a driver I can't test and I know almost nothing about.


> > +	/* These two are sent by the windows driver, but stall for
> > +	 * me. I dont have an analyzer on the linux side so i can't
> > +	 * see what is actually different and why the device takes
> > +	 * issue with them
> > +	 */
>
> Hmm...how was that information obtained?  If this driver was
> reverse-engineered, it would be good to know just what process was
> followed.

That's probably a question for the original author, Dan. CC added. I'm 
just cleaning them up for kernel inclusion.

> > +static int msir_fetch_more_data(struct usb_skel *dev, int
> > dont_block) +{
>
> ...
>
> > +			/* 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;
>
> As others have noted, I think, getting new sleep_on() calls into the
> kernel is kind of a hard sell.

there are more, I'll fix them all before the next patchset get posted. 

> ...
>
> > +
> > +	/* select a "subminor" number (part of a minor number) */
> > +	down(&minor_table_mutex);
>
> So...this driver has a mutex called "sem" and a semaphore called
> "minor_table_mutex".  I suspect that minor_table_mutex should, in
> fact, be a mutex.

yes, the declaration looks like this:
static DECLARE_MUTEX(...);

which gives us a single holder semaphore. the static in front of 
DECLARE_MUTEX() fools checkpatch. changed to to DEFINE_MUTEX, also in 
two other drivers.

>
> ...
>
> > +error:
> > +	mceusb_delete(dev);
> > +	dev = NULL;
> > +	dprintk("%s: retval = %x", __func__, retval);
> > +	up(&minor_table_mutex);
> > +	return retval;
> > +}
>
> This will leak the memory allocated for dev.  It also leaves the
> entry in minor_table pointing to a nonfunctional device.

fixed.

Thanks for the review.

Janne


^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 03/18] lirc driver for 1st-gen Media Center Ed. USB IR transceivers
  2008-09-09 19:21       ` [PATCH 03/18] lirc driver for 1st-gen " Jonathan Corbet
  2008-09-09 23:59         ` Janne Grunau
@ 2008-09-10  0:04         ` Janne Grunau
  1 sibling, 0 replies; 111+ messages in thread
From: Janne Grunau @ 2008-09-10  0:04 UTC (permalink / raw)
  To: Jonathan Corbet
  Cc: Jarod Wilson, linux-kernel, Christoph Bartelmus, Janne Grunau

On Tuesday 09 September 2008 21:21:40 Jonathan Corbet wrote:
> > +error:
> > +     mceusb_delete(dev);
> > +     dev = NULL;
> > +     dprintk("%s: retval = %x", __func__, retval);
> > +     up(&minor_table_mutex);
> > +     return retval;
> > +}
>
> This will leak the memory allocated for dev.  It also leaves the
> entry in minor_table pointing to a nonfunctional device.

Argh, I replied too quickly.

kfree(dev) and minor_table cleanup are in mceusb_delete()

Janne

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 04/18] lirc driver for 2nd-gen and later Media Center Ed. USB IR transceivers
  2008-09-09 23:30         ` [PATCH 04/18] lirc driver for 2nd-gen and later Media Center Ed. USB IR transceivers Jonathan Corbet
@ 2008-09-10  0:36           ` Janne Grunau
  2008-09-11  9:21           ` Adrian Bunk
  1 sibling, 0 replies; 111+ messages in thread
From: Janne Grunau @ 2008-09-10  0:36 UTC (permalink / raw)
  To: Jonathan Corbet
  Cc: Jarod Wilson, linux-kernel, Christoph Bartelmus, Janne Grunau

On Wednesday 10 September 2008 01:30:57 Jonathan Corbet wrote:
> On Tue,  9 Sep 2008 00:05:49 -0400
>
> Jarod Wilson <jwilson@redhat.com> wrote:
> > +/*
> > + * LIRC driver for Philips eHome USB Infrared Transceiver
> > + * and the Microsoft MCE 2005 Remote Control
> > + *
> > + * (C) by Martin A. Blatter <martin_a_blatter@yahoo.com>
> > + *
> > + * Transmitter support and reception code cleanup.
> > + * (C) by Daniel Melander <lirc@rajidae.se>
>
> As I understand it, proper assertions of copyright need the word
> "copyright" and a year; the "(C)" trigraph doesn't have any legal
> meaning.

Depends on the country. I won't touch copyright notices or attributions.

> > + * 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.
>
> My guess is that this will not be a major impediment to mainline
> inclusion; this comment can probably go.

Already removed.

> > +/* lock irctl structure */
> > +#define IRLOCK		mutex_lock(&ir->lock)
> > +#define IRUNLOCK	mutex_unlock(&ir->lock)
>
> Might I request that these go away?  They can only obfuscate the
> code.

yes, I will manually expand similar macros in the other drivers too.

> Also, set_use_inc() is returning a normal zero-or-negative-error
> status to an outside caller.  It shouldn't return a locally-defined
> symbol like SUCCESS.  I'd just use "return 0;".

fixed

> > +#ifdef MODULE
> > +static int __init usb_remote_init(void)
> > +{
>
> I'm curious: you don't need to initialize if not compiled as a
> module?

Already fixed in all drivers, relict from out of tree lirc.

> > +	int i;
> > +
> > +	printk(KERN_INFO "\n");
> > +	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");
> > +
> > +	request_module("lirc_dev");
>
> You're calling functions from lirc_dev.c, so modprobe will not load
> this module until lirc_dev is already there.  There's no point in
> trying to load it explicitly with request_module().

True, request_module("lirc_dev"); removed from all drivers.

> > +	i = usb_register(&usb_remote_driver);
> > +	if (i < 0) {
> > +		printk(DRIVER_NAME ": usb register failed, result = %d\n", i);
> > +		return -ENODEV;
> > +	}
> > +
> > +	return SUCCESS;
> > +}
>
> "return 0;" please.

fixed for all drivers

Janne

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 03/18] lirc driver for 1st-gen Media Center Ed. USB IR transceivers
  2008-09-09 23:59         ` Janne Grunau
@ 2008-09-10  1:39           ` Jarod Wilson
  0 siblings, 0 replies; 111+ messages in thread
From: Jarod Wilson @ 2008-09-10  1:39 UTC (permalink / raw)
  To: Janne Grunau; +Cc: Jonathan Corbet, linux-kernel, Christoph Bartelmus, dconti

On Tuesday 09 September 2008 19:59:32 Janne Grunau wrote:
> > > +static void mceusb_setup(struct usb_device *udev)
> > > +{
> > > +   char data[8];
> > > +   int res;
> > > +
> > > +   memset(data, 0, 8);
> > > +
> > > +   /* Get Status */
> > > +   res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
> > > +                         USB_REQ_GET_STATUS, USB_DIR_IN,
> > > +                         0, 0, data, 2, HZ * 3);
> >
> > res is set many times in this function, but it is never checked.  It
> > seems to me like the addition of some error handling would be a good
> > idea.
>
> sigh, It would be a good idea, not sure if I'm motivated enough to add
> that to a driver I can't test and I know almost nothing about.

I'll take a look, I've at least got the hardware to test with.

-- 
Jarod Wilson
jarod@redhat.com


^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 06/18] lirc driver for the ATI USB RF remote receiver
  2008-09-09  4:05           ` [PATCH 06/18] lirc driver for the ATI USB RF remote receiver Jarod Wilson
  2008-09-09  4:05             ` [PATCH 07/18] lirc driver for the CommandIR USB Transceiver Jarod Wilson
@ 2008-09-10  9:58             ` Ville Syrjälä
  2008-09-10 13:05               ` Jarod Wilson
  1 sibling, 1 reply; 111+ messages in thread
From: Ville Syrjälä @ 2008-09-10  9:58 UTC (permalink / raw)
  To: Jarod Wilson
  Cc: linux-kernel, Jarod Wilson, Janne Grunau, Christoph Bartelmus

On Tue, Sep 09, 2008 at 12:05:51AM -0400, Jarod Wilson wrote:
> +config LIRC_ATIUSB
> +	tristate "ATI RF USB Receiver support"
> +	default n
> +	depends on LIRC_DEV
> +	help
> +	  Driver for the ATI USB RF remote receiver
> +

We already have proper input drivers for these devices (ati_remote and
ati_remote2).

-- 
Ville Syrjälä
syrjala@sci.fi
http://www.sci.fi/~syrjala/

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 01/18] lirc core device driver infrastructure
  2008-09-09 13:01   ` Christoph Hellwig
@ 2008-09-10 12:24     ` Janne Grunau
  2008-09-10 12:29       ` Christoph Hellwig
  2008-09-11 18:03       ` Christoph Bartelmus
  2008-09-12  0:16     ` Janne Grunau
  1 sibling, 2 replies; 111+ messages in thread
From: Janne Grunau @ 2008-09-10 12:24 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Jarod Wilson, linux-kernel, Jarod Wilson, Christoph Bartelmus,
	Mario Limonciello, Janne Grunau

On Tuesday 09 September 2008 15:01:02 Christoph Hellwig wrote:

> > +	do {
> > +		if (ir->open) {
> > +			if (ir->jiffies_to_wait) {
> > +				set_current_state(TASK_INTERRUPTIBLE);
> > +				schedule_timeout(ir->jiffies_to_wait);
> > +			} else {
> > +				interruptible_sleep_on(
> > +					ir->p.get_queue(ir->p.data));
> > +			}
>
> No sleep on, please.  But this one should be trivial to fix anyway,
> by just changing this to a
>
> 	set_current_state(TASK_INTERRUPTIBLE);
> 	schedule();
>
> and using wake_up_process in the wakeup path.

No driver uses the get_queue callback. So I'm inclined to just remove 
it. Christoph, any objections?

Janne

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 01/18] lirc core device driver infrastructure
  2008-09-10 12:24     ` Janne Grunau
@ 2008-09-10 12:29       ` Christoph Hellwig
  2008-09-10 12:45         ` Janne Grunau
  2008-09-11 18:03       ` Christoph Bartelmus
  1 sibling, 1 reply; 111+ messages in thread
From: Christoph Hellwig @ 2008-09-10 12:29 UTC (permalink / raw)
  To: Janne Grunau
  Cc: Christoph Hellwig, Jarod Wilson, linux-kernel, Jarod Wilson,
	Christoph Bartelmus, Mario Limonciello

On Wed, Sep 10, 2008 at 02:24:36PM +0200, Janne Grunau wrote:
> On Tuesday 09 September 2008 15:01:02 Christoph Hellwig wrote:
> 
> > > +	do {
> > > +		if (ir->open) {
> > > +			if (ir->jiffies_to_wait) {
> > > +				set_current_state(TASK_INTERRUPTIBLE);
> > > +				schedule_timeout(ir->jiffies_to_wait);
> > > +			} else {
> > > +				interruptible_sleep_on(
> > > +					ir->p.get_queue(ir->p.data));
> > > +			}
> >
> > No sleep on, please.  But this one should be trivial to fix anyway,
> > by just changing this to a
> >
> > 	set_current_state(TASK_INTERRUPTIBLE);
> > 	schedule();
> >
> > and using wake_up_process in the wakeup path.
> 
> No driver uses the get_queue callback. So I'm inclined to just remove 
> it. Christoph, any objections?

Sounds good.

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 01/18] lirc core device driver infrastructure
  2008-09-10 12:29       ` Christoph Hellwig
@ 2008-09-10 12:45         ` Janne Grunau
  0 siblings, 0 replies; 111+ messages in thread
From: Janne Grunau @ 2008-09-10 12:45 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Jarod Wilson, linux-kernel, Jarod Wilson, Christoph Bartelmus,
	Mario Limonciello

On Wednesday 10 September 2008 14:29:43 Christoph Hellwig wrote:
> On Wed, Sep 10, 2008 at 02:24:36PM +0200, Janne Grunau wrote:
> > On Tuesday 09 September 2008 15:01:02 Christoph Hellwig wrote:
> > > > +	do {
> > > > +		if (ir->open) {
> > > > +			if (ir->jiffies_to_wait) {
> > > > +				set_current_state(TASK_INTERRUPTIBLE);
> > > > +				schedule_timeout(ir->jiffies_to_wait);
> > > > +			} else {
> > > > +				interruptible_sleep_on(
> > > > +					ir->p.get_queue(ir->p.data));
> > > > +			}
> > >
> > > No sleep on, please.  But this one should be trivial to fix
> > > anyway, by just changing this to a
> > >
> > > 	set_current_state(TASK_INTERRUPTIBLE);
> > > 	schedule();
> > >
> > > and using wake_up_process in the wakeup path.
> >
> > No driver uses the get_queue callback. So I'm inclined to just
> > remove it. Christoph, any objections?
>
> Sounds good.

Oh, just realized that there are two Christophs in TO an CC, I meant 
Christoph Bartelmus, the LIRC maintainer, sorry.

Janne

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 06/18] lirc driver for the ATI USB RF remote receiver
  2008-09-10  9:58             ` [PATCH 06/18] lirc driver for the ATI USB RF remote receiver Ville Syrjälä
@ 2008-09-10 13:05               ` Jarod Wilson
  2008-09-10 13:14                 ` Christoph Hellwig
  2008-09-10 14:08                 ` Ville Syrjälä
  0 siblings, 2 replies; 111+ messages in thread
From: Jarod Wilson @ 2008-09-10 13:05 UTC (permalink / raw)
  To: Ville Syrjälä, Janne Grunau; +Cc: linux-kernel, Christoph Bartelmus

On Wednesday 10 September 2008 05:58:32 Ville Syrjälä wrote:
> On Tue, Sep 09, 2008 at 12:05:51AM -0400, Jarod Wilson wrote:
> > +config LIRC_ATIUSB
> > +	tristate "ATI RF USB Receiver support"
> > +	default n
> > +	depends on LIRC_DEV
> > +	help
> > +	  Driver for the ATI USB RF remote receiver
> > +
>
> We already have proper input drivers for these devices (ati_remote and
> ati_remote2).

True, though I think some users still prefer using them with the lirc drivers 
for assorted reasons. Configuring something like mythtv to work with the 
ati_remote{,2} driver appears to be a bit more complex (or at least non-
standard vs. several other popular remotes) and not as functional vs. 
configuring mythtv w/lirc_atiusb.

http://evuraan.blogspot.com/2008/01/how-to-use-ati-remote-wonder-ii-
remote.html

-- 
Jarod Wilson
jarod@redhat.com


^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 01/18] lirc core device driver infrastructure
  2008-09-09  4:05 ` [PATCH 01/18] lirc core device driver infrastructure Jarod Wilson
                     ` (4 preceding siblings ...)
  2008-09-09 15:33   ` Jonathan Corbet
@ 2008-09-10 13:08   ` Dmitry Torokhov
  2008-09-11  8:47     ` Gerd Hoffmann
  5 siblings, 1 reply; 111+ messages in thread
From: Dmitry Torokhov @ 2008-09-10 13:08 UTC (permalink / raw)
  To: Jarod Wilson
  Cc: linux-kernel, Jarod Wilson, Janne Grunau, Christoph Bartelmus,
	Mario Limonciello

Hi Jarod,

On Tue, Sep 09, 2008 at 12:05:46AM -0400, Jarod Wilson wrote:
> -Add Kconfig and Makefile bits
> -Add device driver interface and headers
> 
> The initial Kconfig and Makefile bits were done by Mario Limonciello for
> the Ubuntu kernel, but have been tweaked a bit since then. Any errors are
> probably my doing.
> 

Couple of questions - what is missing in the current input core to
properly support IR devices? How can we enhance it to not need new
subsystem to handle such devices? We already have in-kernel support for
some of the devices you are adding (ATI remotes, xbox, etc) so the
situation is not ideal. Could we try to work it in ininput core and
maybe provide LIRC device access through an input handler
implementation so that applications do not have to implement 2 types of
interfaces?

-- 
Dmitry

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 06/18] lirc driver for the ATI USB RF remote receiver
  2008-09-10 13:05               ` Jarod Wilson
@ 2008-09-10 13:14                 ` Christoph Hellwig
  2008-09-10 13:37                   ` Jon Smirl
  2008-09-10 13:44                   ` Janne Grunau
  2008-09-10 14:08                 ` Ville Syrjälä
  1 sibling, 2 replies; 111+ messages in thread
From: Christoph Hellwig @ 2008-09-10 13:14 UTC (permalink / raw)
  To: Jarod Wilson
  Cc: Ville Syrj?l?, Janne Grunau, linux-kernel, Christoph Bartelmus

On Wed, Sep 10, 2008 at 09:05:58AM -0400, Jarod Wilson wrote:
> True, though I think some users still prefer using them with the lirc drivers 
> for assorted reasons. Configuring something like mythtv to work with the 
> ati_remote{,2} driver appears to be a bit more complex (or at least non-
> standard vs. several other popular remotes) and not as functional vs. 
> configuring mythtv w/lirc_atiusb.

Bad idea to have two drivers for the same piece of hardware.  And this
gets straight back into the why should lirc be different from the input
layer point raised earlies.  I think we really shouldn't keep lirc as
a separate subsystem, but make sure all the drivers are written to the
input layer.  To make the migration easier for exiting users we could
add a /dev/lirc driver ontop of the input layer, similar to how the ps2
mouse and joystick drivers sit ontop of the input core.


^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 06/18] lirc driver for the ATI USB RF remote receiver
  2008-09-10 13:14                 ` Christoph Hellwig
@ 2008-09-10 13:37                   ` Jon Smirl
  2008-09-10 14:30                     ` Dmitry Torokhov
  2008-09-10 13:44                   ` Janne Grunau
  1 sibling, 1 reply; 111+ messages in thread
From: Jon Smirl @ 2008-09-10 13:37 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Jarod Wilson, Ville Syrj?l?,
	Janne Grunau, linux-kernel, Christoph Bartelmus, lirc-list

On 9/10/08, Christoph Hellwig <hch@infradead.org> wrote:
> On Wed, Sep 10, 2008 at 09:05:58AM -0400, Jarod Wilson wrote:
>  > True, though I think some users still prefer using them with the lirc drivers
>  > for assorted reasons. Configuring something like mythtv to work with the
>  > ati_remote{,2} driver appears to be a bit more complex (or at least non-
>  > standard vs. several other popular remotes) and not as functional vs.
>  > configuring mythtv w/lirc_atiusb.
>
>
> Bad idea to have two drivers for the same piece of hardware.  And this
>  gets straight back into the why should lirc be different from the input
>  layer point raised earlies.  I think we really shouldn't keep lirc as
>  a separate subsystem, but make sure all the drivers are written to the
>  input layer.  To make the migration easier for exiting users we could
>  add a /dev/lirc driver ontop of the input layer, similar to how the ps2
>  mouse and joystick drivers sit ontop of the input core.

There have been concerns on the lirc list that integrating with the
input core would mean that the kernel has to translate from the lirc
timing data to key strokes. Keyboards do their translation in user
space, lirc can do the same, right? User space app receives lirc event
and then calls a translation library function.

A second issue is that some IR drivers are completely in user space.
Is there a mechanism for these user space drivers to send events down
into the input layer? These events would then get queued with the
other kernel generated events and be delivered to consuming apps in
order.


>
>
>  --
>  To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
>  the body of a message to majordomo@vger.kernel.org
>  More majordomo info at  http://vger.kernel.org/majordomo-info.html
>  Please read the FAQ at  http://www.tux.org/lkml/
>


-- 
Jon Smirl
jonsmirl@gmail.com

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 06/18] lirc driver for the ATI USB RF remote receiver
  2008-09-10 13:14                 ` Christoph Hellwig
  2008-09-10 13:37                   ` Jon Smirl
@ 2008-09-10 13:44                   ` Janne Grunau
  2008-09-10 14:13                     ` Jarod Wilson
  2008-09-10 14:19                     ` Christoph Hellwig
  1 sibling, 2 replies; 111+ messages in thread
From: Janne Grunau @ 2008-09-10 13:44 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Jarod Wilson, Ville Syrjälä, linux-kernel, Christoph Bartelmus

On Wednesday 10 September 2008 15:14:32 Christoph Hellwig wrote:
> On Wed, Sep 10, 2008 at 09:05:58AM -0400, Jarod Wilson wrote:
> > True, though I think some users still prefer using them with the
> > lirc drivers for assorted reasons. Configuring something like
> > mythtv to work with the ati_remote{,2} driver appears to be a bit
> > more complex (or at least non- standard vs. several other popular
> > remotes) and not as functional vs. configuring mythtv
> > w/lirc_atiusb.
>
> Bad idea to have two drivers for the same piece of hardware.  And
> this gets straight back into the why should lirc be different from
> the input layer point raised earlies.  I think we really shouldn't
> keep lirc as a separate subsystem, but make sure all the drivers are
> written to the input layer.

Some drivers don't report events but deliver the signal to the lirc 
daemon. The daemon decodes the signal. Decoding of several IR protocols 
and mapping of each possible remote to events are things much easier 
done in userspace.
Aslo LIRC is not only about IR input but also output.

See following mail from Christoph Bertelmus for more details.
http://sourceforge.net/mailarchive/message.php?msg_id=AAryKgq9UOB%40bartelmus.de

> To make the migration easier for exiting 
> users we could add a /dev/lirc driver ontop of the input layer,

This shouldn't be necessary since the lirc daemon can already read from 
input devices.

Janne

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 06/18] lirc driver for the ATI USB RF remote receiver
  2008-09-10 13:05               ` Jarod Wilson
  2008-09-10 13:14                 ` Christoph Hellwig
@ 2008-09-10 14:08                 ` Ville Syrjälä
  2008-09-10 14:37                   ` Dmitry Torokhov
  1 sibling, 1 reply; 111+ messages in thread
From: Ville Syrjälä @ 2008-09-10 14:08 UTC (permalink / raw)
  To: Jarod Wilson; +Cc: Janne Grunau, linux-kernel, Christoph Bartelmus

On Wed, Sep 10, 2008 at 09:05:58AM -0400, Jarod Wilson wrote:
> On Wednesday 10 September 2008 05:58:32 Ville Syrjälä wrote:
> > On Tue, Sep 09, 2008 at 12:05:51AM -0400, Jarod Wilson wrote:
> > > +config LIRC_ATIUSB
> > > +	tristate "ATI RF USB Receiver support"
> > > +	default n
> > > +	depends on LIRC_DEV
> > > +	help
> > > +	  Driver for the ATI USB RF remote receiver
> > > +
> >
> > We already have proper input drivers for these devices (ati_remote and
> > ati_remote2).
> 
> True, though I think some users still prefer using them with the lirc drivers 
> for assorted reasons. Configuring something like mythtv to work with the 
> ati_remote{,2} driver appears to be a bit more complex (or at least non-
> standard vs. several other popular remotes) and not as functional vs. 
> configuring mythtv w/lirc_atiusb.
> 
> http://evuraan.blogspot.com/2008/01/how-to-use-ati-remote-wonder-ii-
> remote.html

The X11 limitations are unfortunate but they can be worked around with
keymap support in the kernel driver. ati_remote2 keymap patch is somewhere
on it's way upstream as we speak. Not sure about ati_remote though.

Furthermore if lirc can already handle input from event devices then the
lirc_atiusb drivers are simply not needed.

-- 
Ville Syrjälä
syrjala@sci.fi
http://www.sci.fi/~syrjala/

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 06/18] lirc driver for the ATI USB RF remote receiver
  2008-09-10 13:44                   ` Janne Grunau
@ 2008-09-10 14:13                     ` Jarod Wilson
  2008-09-10 14:19                     ` Christoph Hellwig
  1 sibling, 0 replies; 111+ messages in thread
From: Jarod Wilson @ 2008-09-10 14:13 UTC (permalink / raw)
  To: Janne Grunau
  Cc: Christoph Hellwig, Ville Syrjälä,
	linux-kernel, Christoph Bartelmus

On Wednesday 10 September 2008 09:44:17 Janne Grunau wrote:
> On Wednesday 10 September 2008 15:14:32 Christoph Hellwig wrote:
> > On Wed, Sep 10, 2008 at 09:05:58AM -0400, Jarod Wilson wrote:
> > > True, though I think some users still prefer using them with the
> > > lirc drivers for assorted reasons. Configuring something like
> > > mythtv to work with the ati_remote{,2} driver appears to be a bit
> > > more complex (or at least non- standard vs. several other popular
> > > remotes) and not as functional vs. configuring mythtv
> > > w/lirc_atiusb.
> >
> > Bad idea to have two drivers for the same piece of hardware.  And
> > this gets straight back into the why should lirc be different from
> > the input layer point raised earlies.  I think we really shouldn't
> > keep lirc as a separate subsystem, but make sure all the drivers are
> > written to the input layer.
>
> Some drivers don't report events but deliver the signal to the lirc
> daemon. The daemon decodes the signal. Decoding of several IR protocols
> and mapping of each possible remote to events are things much easier
> done in userspace.
> Aslo LIRC is not only about IR input but also output.
>
> See following mail from Christoph Bertelmus for more details.
> http://sourceforge.net/mailarchive/message.php?msg_id=AAryKgq9UOB%40bartelm
>us.de

Yeah, I think that pretty well covers the issues.

> > To make the migration easier for exiting
> > users we could add a /dev/lirc driver ontop of the input layer,
>
> This shouldn't be necessary since the lirc daemon can already read from
> input devices.

Okay, so what I'm hearing and thinking is that we should at drop lirc_atiusb 
from consideration for inclusion, and instead extend ati_remote{,2} as/if 
necessary to be able to let the lirc daemon use it via the provided input 
device, so people can get the best of both worlds (it behaves like a keyboard 
if so configured, talks to lircd if so configured).

-- 
Jarod Wilson
jarod@redhat.com


^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 06/18] lirc driver for the ATI USB RF remote receiver
  2008-09-10 13:44                   ` Janne Grunau
  2008-09-10 14:13                     ` Jarod Wilson
@ 2008-09-10 14:19                     ` Christoph Hellwig
  1 sibling, 0 replies; 111+ messages in thread
From: Christoph Hellwig @ 2008-09-10 14:19 UTC (permalink / raw)
  To: Janne Grunau
  Cc: Christoph Hellwig, Jarod Wilson, Ville Syrj?l?,
	linux-kernel, Christoph Bartelmus

On Wed, Sep 10, 2008 at 03:44:17PM +0200, Janne Grunau wrote:
> On Wednesday 10 September 2008 15:14:32 Christoph Hellwig wrote:
> > On Wed, Sep 10, 2008 at 09:05:58AM -0400, Jarod Wilson wrote:
> > > True, though I think some users still prefer using them with the
> > > lirc drivers for assorted reasons. Configuring something like
> > > mythtv to work with the ati_remote{,2} driver appears to be a bit
> > > more complex (or at least non- standard vs. several other popular
> > > remotes) and not as functional vs. configuring mythtv
> > > w/lirc_atiusb.
> >
> > Bad idea to have two drivers for the same piece of hardware.  And
> > this gets straight back into the why should lirc be different from
> > the input layer point raised earlies.  I think we really shouldn't
> > keep lirc as a separate subsystem, but make sure all the drivers are
> > written to the input layer.
> 
> Some drivers don't report events but deliver the signal to the lirc 
> daemon. The daemon decodes the signal. Decoding of several IR protocols 
> and mapping of each possible remote to events are things much easier 
> done in userspace.

Mixing these two in the same interface seems like a really bad idea.
The input layer is the right interface for devices that directly produce
events.  If you want to decode them in userspace you need a different
interface for the daemon.  Not sure why you really want to do this in
userspace, though.

> Aslo LIRC is not only about IR input but also output.

The input layer also does some outputs, like the pc speaker :)




^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 06/18] lirc driver for the ATI USB RF remote receiver
  2008-09-10 13:37                   ` Jon Smirl
@ 2008-09-10 14:30                     ` Dmitry Torokhov
  0 siblings, 0 replies; 111+ messages in thread
From: Dmitry Torokhov @ 2008-09-10 14:30 UTC (permalink / raw)
  To: Jon Smirl
  Cc: Christoph Hellwig, Jarod Wilson, Ville Syrj?l?,
	Janne Grunau, linux-kernel, Christoph Bartelmus, lirc-list

On Wed, Sep 10, 2008 at 09:37:19AM -0400, Jon Smirl wrote:
> A second issue is that some IR drivers are completely in user space.
> Is there a mechanism for these user space drivers to send events down
> into the input layer? These events would then get queued with the
> other kernel generated events and be delivered to consuming apps in
> order.
> 

Uinput driver (/dev/uinput, drivers/input/misc/uinput.c) can be used to
inject events into kernel for subsequent distribution through normal
interfaces (console, evdev, etc).

-- 
Dmitry

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 06/18] lirc driver for the ATI USB RF remote receiver
  2008-09-10 14:08                 ` Ville Syrjälä
@ 2008-09-10 14:37                   ` Dmitry Torokhov
  0 siblings, 0 replies; 111+ messages in thread
From: Dmitry Torokhov @ 2008-09-10 14:37 UTC (permalink / raw)
  To: Ville Syrjälä,
	Jarod Wilson, Janne Grunau, linux-kernel, Christoph Bartelmus

On Wed, Sep 10, 2008 at 05:08:31PM +0300, Ville Syrjälä wrote:
> On Wed, Sep 10, 2008 at 09:05:58AM -0400, Jarod Wilson wrote:
> > On Wednesday 10 September 2008 05:58:32 Ville Syrjälä wrote:
> > > On Tue, Sep 09, 2008 at 12:05:51AM -0400, Jarod Wilson wrote:
> > > > +config LIRC_ATIUSB
> > > > +	tristate "ATI RF USB Receiver support"
> > > > +	default n
> > > > +	depends on LIRC_DEV
> > > > +	help
> > > > +	  Driver for the ATI USB RF remote receiver
> > > > +
> > >
> > > We already have proper input drivers for these devices (ati_remote and
> > > ati_remote2).
> > 
> > True, though I think some users still prefer using them with the lirc drivers 
> > for assorted reasons. Configuring something like mythtv to work with the 
> > ati_remote{,2} driver appears to be a bit more complex (or at least non-
> > standard vs. several other popular remotes) and not as functional vs. 
> > configuring mythtv w/lirc_atiusb.
> > 
> > http://evuraan.blogspot.com/2008/01/how-to-use-ati-remote-wonder-ii-
> > remote.html
> 
> The X11 limitations are unfortunate but they can be worked around with
> keymap support in the kernel driver.

X's evdev support is also maturing and hopefully hotplug support will be
implemented soon.

> ati_remote2 keymap patch is somewhere
> on it's way upstream as we speak. Not sure about ati_remote though.
> 

Yeah, its in my "next" branch, waiting for .28 to open.

> Furthermore if lirc can already handle input from event devices then the
> lirc_atiusb drivers are simply not needed.
> 
> -- 
> Ville Syrjälä
> syrjala@sci.fi
> http://www.sci.fi/~syrjala/
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/

-- 
Dmitry

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 05/18] lirc driver for i2c-based IR receivers
  2008-09-09  4:05         ` [PATCH 05/18] lirc driver for i2c-based IR receivers Jarod Wilson
  2008-09-09  4:05           ` [PATCH 06/18] lirc driver for the ATI USB RF remote receiver Jarod Wilson
  2008-09-09  4:13           ` [PATCH 05/18] lirc driver for i2c-based IR receivers Jarod Wilson
@ 2008-09-10 15:42           ` Jonathan Corbet
  2 siblings, 0 replies; 111+ messages in thread
From: Jonathan Corbet @ 2008-09-10 15:42 UTC (permalink / raw)
  To: Jarod Wilson; +Cc: linux-kernel, Janne Grunau, Christoph Bartelmus

> +config LIRC_I2C
> +	tristate "I2C Based IR Receivers"
> +	default n
> +	depends on LIRC_DEV
> +	help
> +	  Driver for I2C-based IR receivers, such as those commonly
> +	  found onboard Hauppauge PVR-150/250/350 video capture cards

This driver offers relatively few things to gripe about.  But I'm not one
to shrink from a challenge...

> +#include <linux/semaphore.h>

It looks like this driver is semaphore-free?  Actually, it looks kind of
lock-free in general.  That's probably my biggest concern; what is
protecting against concurrent operations on the device?

> +static int add_to_buf_pcf8574(void *data, struct lirc_buffer *buf)
> +{
> +	struct IR *ir = data;
> +	int rc;
> +	unsigned char all, mask;
> +	unsigned char key;
> +
> +	/* compute all valid bits (key code + pressed/release flag) */
> +	all = ir->bits | ir->flag;
> +
> +	/* save IR writable mask bits */
> +	mask = i2c_smbus_read_byte(&ir->c) & ~all;
> +
> +	/* send bit mask */
> +	rc = i2c_smbus_write_byte(&ir->c, (0xff & all) | mask);
> +
> +	/* receive scan code */
> +	rc = i2c_smbus_read_byte(&ir->c);
> +
> +	if (rc == -1) {
> +		dprintk("%s read error\n", ir->c.name);
> +		return -EIO;
> +	}

This ignores the return value from the first i2c_smbus_read_byte() and
i2c_smbus_write_byte() calls.  Beyond that, i2c_smbus_read_byte() can
return -EANYTHING, so I think the check should be "rc < 0", no?  Tests
against -1 are common in this driver, I'll not point them all out.

> +static int ir_attach(struct i2c_adapter *adap, int addr,
> +		     unsigned short flags, int kind)
> +{

...

> +	ir->l.minor = lirc_register_plugin(&ir->l);
> +
> +	return 0;
> +}

The lirc_register_plugin() call can fail, the result should be checked.

> +#ifdef MODULE
> +
> +int init_module(void)
> +{
> +	request_module("bttv");
> +	request_module("rivatv");
> +	request_module("ivtv");
> +	request_module("cx8800");
> +	i2c_add_driver(&driver);
> +	return 0;
> +}

Why are all these modules being loaded here?  That would seem like a job
for udev.

jon

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 07/18] lirc driver for the CommandIR USB Transceiver
  2008-09-09  4:05             ` [PATCH 07/18] lirc driver for the CommandIR USB Transceiver Jarod Wilson
  2008-09-09  4:05               ` [PATCH 08/18] lirc driver for the Soundgraph IMON IR Receivers Jarod Wilson
@ 2008-09-10 17:09               ` Jonathan Corbet
  2008-09-11 18:24                 ` Christoph Bartelmus
  2008-09-25 15:21                 ` Jarod Wilson
  1 sibling, 2 replies; 111+ messages in thread
From: Jonathan Corbet @ 2008-09-10 17:09 UTC (permalink / raw)
  To: Jarod Wilson; +Cc: linux-kernel, Janne Grunau, Christoph Bartelmus

This driver has real locking problems.  Parts of it depend on the BKL, but
I would have a hard time arguing for the addition of new BKL users in this
century.  The assumption that ->open() is called under the BKL no longer
holds.  It's really time to put a proper lock here.

This driver also needs a lot of "static" keywords thrown in.

> +/* circular packet queue */
> +unsigned char ourbuffers[QUEUELENGTH][64];
> +int waitusecs[QUEUELENGTH];
> +int ourbufferlengths[QUEUELENGTH];
> +int nexttosend;
> +int nexttofill;

So this driver only handles a single device?

> +/* Structure to hold all of our device specific stuff */
> +struct usb_skel {
> +	struct usb_device *udev; /* the usb device for this device */
> +	struct usb_interface *interface; /* the interface for this device */
> +	unsigned char *bulk_in_buffer; /* the buffer to receive data */
> +	size_t bulk_in_size; /* the size of the receive buffer */
> +	__u8 bulk_in_endpointAddr; /* the address of the bulk in endpoint */
> +	__u8 bulk_out_endpointAddr; /* the address of the bulk out endpoint */
> +	struct kref kref;
> +};

As with a previous driver, a more descriptive name than "usb_skel" would be
nice.

As far as I can tell, the kref here is just extra overhead.  Why not just
free the structure in cmdir_release()?

> +#define CMDIR_VAR_LEN 68
> +static char cmdir_var[] =
> +"COMMANDIRx:\n TX Enabled: 1, 2, 3, 4\n RX: commandirx\n LCD: commandirx";

This struck me as a little weird, so I went and looked at where it is used:

> +static void update_cmdir_string(int device_num)
> +{
> +	int next_comma = 0;
> +	int next_pos = 25;
> +	unsigned int multiplier;
> +	int i;
> +
> +	/* cmdir_var[] = "COMMANDIRx:\n"
> +	 * 		 " TX Enabled: 1, 2, 3, 4\n"
> +	 * 		 " RX: commandirx\n"
> +	 * 		 " LCD: commandirx\n" */
> +
> +	cmdir_var[9] = ASCII0+device_num;
> +	cmdir_var[50] = ASCII0+rx_device;
> +	cmdir_var[67] = ASCII0+lcd_device;
> +
> +	for (i = 25; i < 35; i++)
> +		cmdir_var[i] = ' ';

[...]

I don't get it.  Something is wrong with sprintf()?

> +static int cmdir_probe(struct usb_interface *interface,
> +		       const struct usb_device_id *id)
> +{

[...] 

> +	/* check whether minor already includes base */
> +	minor = interface->minor;
> +	if (minor >= USB_CMDIR_MINOR_BASE)
> +		minor = minor-USB_CMDIR_MINOR_BASE;

This kind of stuff looks like a recipe for confusion.  Why not just deal
with the minor number as it is?

> +static void cmdir_disconnect(struct usb_interface *interface)
> +{
> +	struct usb_skel *dev;
> +	int minor = interface->minor;
> +
> +	/* prevent cmdir_open() from racing cmdir_disconnect() */
> +	lock_kernel();

As noted before, that won't work.

> +static int cmdir_check(int device_num)
> +{
> +	struct usb_interface *interface;
> +
> +	interface = usb_find_interface(&cmdir_driver,
> +				       USB_CMDIR_MINOR_BASE+device_num);
> +	if (!interface) {
> +		/* also check without adding base, for devfs */
> +		interface = usb_find_interface(&cmdir_driver, rx_device);
> +		if (!interface)
> +			return -ENODEV;
> +	}
> +	return 0;
> +}

For devfs??  Again, this messing around with the minor number looks like
trouble.

> +static void init_cmdir_var(int device_num)
> +{
> +	int i;
> +	unsigned int multiplier = 1;
> +
> +	for (i = 0; i < device_num; i++)
> +		multiplier = multiplier*0x10;

I bet that could be implemented without a loop.

> +	transmitters |= multiplier * 0x0F;
> +	next_transmitters = transmitters;
> +	info("commandir%d reset", device_num);
> +	return;
> +}

> +static ssize_t cmdir_file_read(struct file *file, char *buffer,
> +			       size_t count, loff_t *ppos)
> +{
> +	int retval = 0;
> +	int minor = 0;
> +	struct usb_skel *dev;
> +
> +	dev = (struct usb_skel *)file->private_data;
> +	minor = dev->interface->minor;
> +	if (minor >= USB_CMDIR_MINOR_BASE)
> +		minor = minor - USB_CMDIR_MINOR_BASE;
> +
> +	if (((int)*ppos) == 0) {
> +		update_cmdir_string(minor);
> +		if (copy_to_user(buffer, cmdir_var, CMDIR_VAR_LEN))

So it ignores the count the user asked for and stuffs the full length down
there anyway?

> +			retval = -EFAULT;
> +		else
> +			retval = CMDIR_VAR_LEN;
> +		return retval;
> +	} else
> +		return 0;
> +}
> +
> +/*  Read data from CommandIR  */
> +ssize_t cmdir_read(unsigned char *buffer, size_t count)
> +{
> +	struct usb_skel *dev;
> +	int length, retval = 0;
> +
> +	struct usb_interface *interface;
> +	interface = usb_find_interface(&cmdir_driver,
> +			USB_CMDIR_MINOR_BASE+rx_device);
> +	if (!interface) {
> +		/* also check without adding base, for devfs */
> +		interface = usb_find_interface(&cmdir_driver, rx_device);
> +		if (!interface)
> +			return -ENODEV;
> +	}

Again, devfs compatibility is probably not needed anymore.

> +	dev = usb_get_intfdata(interface);
> +	if (!dev)
> +		return -ENODEV;
> +	retval = usb_bulk_msg(dev->udev, usb_rcvbulkpipe(dev->udev,
> +						dev->bulk_in_endpointAddr),
> +		 dev->bulk_in_buffer, min(dev->bulk_in_size, count),
> +		 &length, HZ*10);
> +	if (!retval) {
> +		if (!memcpy(buffer, dev->bulk_in_buffer, length))
> +			retval = -EFAULT;
> +		else {
> +			/* current status of the TX buffer */
> +			curTXFill = buffer[2];
> +			retval = length;
> +		}
> +	}
> +	/* suppress errors */
> +	/*
> +	else {
> +		err("Read from device failed, error %d",retval);
> +	}
> +	*/
> +	/* printk(KERN_INFO "CommandIR Reporting TX buffer at %d bytes. \n",
> +	 * 	  curTXFill); */
> +	return retval;
> +}
> +EXPORT_SYMBOL(cmdir_read);

Who is the consumer of these exports?  (OK, I found it later).

> +static ssize_t cmdir_file_write(struct file *file, const char *buffer,
> +				size_t count, loff_t *ppos)
> +{
> +	int retval;
> +	int i;
> +	int equalsign = 0;
> +	int changeType = 0;
> +	unsigned char ctrl_buffer[MCU_CTRL_SIZE];
> +	char *local_buffer;
> +	int minor;
> +
> +	/* set as default - if non-specific error,
> +	 * won't keep calling this function */
> +	retval = count;
> +	local_buffer = kmalloc(count, GFP_KERNEL);
> +
> +	/* verify that we actually have some data to write */
> +	if (count == 0) {
> +		err("Write request of 0 bytes");
> +		goto exit;
> +	}
> +	if (count > 64) {
> +		err("Input too long");
> +		goto exit;
> +	}
> +
> +	/* copy the data from userspace into our local buffer */
> +	if (copy_from_user(local_buffer, buffer, count)) {
> +		retval = -EFAULT;
> +		goto exit;
> +	}
> +
> +	/* parse code */
> +	changeType = cNothing;
> +	equalsign = 0;
> +	for (i = 0; i < MCU_CTRL_SIZE; i++)
> +		ctrl_buffer[i] = 'j';
> +
> +	for (i = 0; i < count; i++) {
> +		switch (local_buffer[i]) {
> +		case 'X':
> +		case 'x':
> +			if ((i > 0) && ((local_buffer[i - 1] == 'R')
> +			    || (local_buffer[i - 1] == 'r')))
> +				changeType = cRX;
> +			break;

So we have a command parser built into this driver.  It looks like somebody
wanted to avoid ioctl() at all costs.  But ioctl() exists for a reason, and
this looks like a good use for it to me.  Of course, now this is a
user-space API which will be hard to change, but I think consideration
should be given to doing just that.

> +		case 'S':
> +		case 's':
> +			if ((i > 1) && ((local_buffer[i - 1] == 'E')
> +			    || (local_buffer[i - 1] == 'e'))) {
> +				if ((local_buffer[i-2] == 'R')
> +				    || (local_buffer[i-2] == 'r'))
> +					changeType = cRESET;
> +			}
> +			break;
> +		case 'L':
> +		case 'l':
> +			if ((i > 0) && ((local_buffer[i - 1] == 'F')
> +			    || (local_buffer[i - 1] == 'f')))
> +				changeType = cFLASH;
> +			break;
> +		case 'C':
> +		case 'c':
> +			if ((i > 0) && ((local_buffer[i - 1] == 'L')
> +			    || (local_buffer[i - 1] == 'l')))
> +				changeType = cLCD;
> +			break;
> +		case '=':
> +			if (changeType != cNothing)
> +				equalsign = i;
> +			break;
> +		case '0':
> +		case '1':
> +		case '2':
> +		case '3':
> +		case '4':
> +		case '5':
> +		case '6':
> +		case '7':
> +			if (equalsign > 0) {
> +				minor = local_buffer[i] - ASCII0;

This code never checks minor for validity, it just assumes that the device
exists - and that the current process has the right to mess with it.

[...]

> +		default:
> +			if ((equalsign > 0) && (local_buffer[i] > 32)) {
> +				err("Non-numerical argument");
> +				goto exit;
> +			}
> +			break;

And here it ignores junk if an "=" has not been seen yet.

> +int cmdir_write(unsigned char *buffer, int count,
> +		void *callback_fct, int usecdelay)
> +{
> +	/* Always add to queue, then send queue number
> +	 * no locks
> +	 * mbodkin, Sept 8, 2005 */

Why no locks?  This code is messing with a global buffer.

> +int wait_to_tx(int usecs)
> +{
> +	/* don't return until last_time + usecs has been reached
> +	 * for non-zero last_tx's. */

I will confess that I have no clue what this function is really trying to
do.  I also note, though, that the only call to it is commented out.  So it
should maybe just go away?

> +int write_core(unsigned char *buffer, int count,
> +	       void *callback_fct, int device_num)
> +{
> +	struct usb_skel *dev;
> +	int retval = count;
> +
> +	struct usb_interface *interface;
> +	struct urb *urb = NULL;
> +	char *buf = NULL;
> +	interface = usb_find_interface(&cmdir_driver,
> +			USB_CMDIR_MINOR_BASE + device_num);
> +	if (!interface) {
> +		/* also check without adding base, for devfs */
> +		interface = usb_find_interface(&cmdir_driver, device_num);
> +		if (!interface)
> +			return -ENODEV;
> +	}
> +	dev = usb_get_intfdata(interface);
> +	if (!dev)
> +		return -ENODEV;
> +	/* create a urb, and a buffer for it, and copy the data to the urb */
> +	urb = usb_alloc_urb(0, GFP_ATOMIC);	/* Now -=Atomic=- */

Why GFP_ATOMIC?  Somehow the comment fails to shed much light.


> diff --git a/drivers/input/lirc/commandir.h b/drivers/input/lirc/commandir.h
> new file mode 100644
> index 0000000..bed703d
> --- /dev/null
> +++ b/drivers/input/lirc/commandir.h
> @@ -0,0 +1,68 @@
> +/*
> + *  commandir.h
> + */
> +
> +#define ASCII0      48

'0' was too clear?


> diff --git a/drivers/input/lirc/lirc_cmdir.c b/drivers/input/lirc/lirc_cmdir.c
> new file mode 100644
> index 0000000..1faa8e3
> --- /dev/null
> +++ b/drivers/input/lirc/lirc_cmdir.c
> @@ -0,0 +1,596 @@

> +#ifndef MAX_UDELAY_MS
> +#define MAX_UDELAY_US 5000
> +#else
> +#define MAX_UDELAY_US (MAX_UDELAY_MS*1000)
> +#endif
> +
> +static inline void safe_udelay(unsigned long usecs)
> +{
> +	while (usecs > MAX_UDELAY_US) {
> +		udelay(MAX_UDELAY_US);
> +		usecs -= MAX_UDELAY_US;
> +	}
> +	udelay(usecs);
> +}

This looks like mdelay() in disguise?  Perhaps something that sleeps would
be even better (haven't looked at the calling context yet)?

> +static int write_to_usb(unsigned char *buffer, int count, int time_elapsed)
> +{
> +	int write_return;
> +
> +	write_return = cmdir_write(buffer, count, NULL, time_elapsed);

Ah, this is why those functions are exported?  Is there any reason not to
just link the whole mess together into a single module?

> +static int cmdir_convert_RX(unsigned char *orig_rxbuffer)
> +{
> +	unsigned char tmp_char_buffer[80];
> +	unsigned int tmp_int_buffer[20];
> +	unsigned int final_data_buffer[20];
> +	unsigned int num_data_values = 0;
> +	unsigned char num_data_bytes = 0;
> +	unsigned int orig_index = 0;
> +	int i;
> +
> +	for (i = 0; i < 80; i++)
> +		tmp_char_buffer[i] = 0;
> +	for (i = 0; i < 20; i++)
> +		tmp_int_buffer[i] = 0;

Looks like memset() to me.

> +	/*
> +	 * get number of data bytes that follow the control bytes
> +	 * (NOT including them)
> +	 */
> +	num_data_bytes = orig_rxbuffer[1];
> +
> +	/* check if num_bytes is multiple of 3; if not, error */
> +	if (num_data_bytes % 3 > 0)
> +		return -1;
> +	if (num_data_bytes > 60)
> +		return -3;
> +	if (num_data_bytes < 3)
> +		return -2;
> +
> +	/*
> +	 * get number of ints to be returned; num_data_bytes does
> +	 * NOT include control bytes
> +	 */
> +	num_data_values = num_data_bytes/3;
> +
> +	for (i = 0; i < num_data_values; i++) {
> +		tmp_char_buffer[i*4] = orig_rxbuffer[(i+1)*3];
> +		tmp_char_buffer[i*4+1] = orig_rxbuffer[(i+1)*3+1];
> +		tmp_char_buffer[i*4+2] = 0;
> +		tmp_char_buffer[i*4+3] = 0;
> +	}

Hmm...  num_data_bytes can be 60, so num_data values can be 20, so i*4+3
can be 83, you've just overflowed the buffer.

> +	/* convert to int array */
> +	memcpy((unsigned char *)tmp_int_buffer, tmp_char_buffer,
> +						(num_data_values*4));

Hmm...I think there ought to be a better way.  Even before considering
endian problems.

> +static ssize_t lirc_write(struct file *file, const char *buf,
> +			 size_t n, loff_t *ppos)
> +{
> +	int i, count;
> +	unsigned int mod_signal_length = 0;
> +	unsigned int time_elapse = 0;
> +	unsigned int total_time_elapsed = 0;
> +	unsigned int num_bytes_already_sent = 0;
> +	unsigned int hibyte = 0;
> +	unsigned int lobyte = 0;
> +	int cmdir_cnt = 0;
> +	unsigned int wait_this = 0;
> +	struct timeval start_time;
> +	struct timeval end_time;
> +	unsigned int real_time_elapsed = 0;
> +
> +	/* save the time we started the write: */
> +	do_gettimeofday(&start_time);
> +
> +	if (n % sizeof(int))
> +		return -EINVAL;
> +
> +	count = n / sizeof(int);
> +	if (count > WBUF_LEN || count % 2 == 0)
> +		return -EINVAL;
> +	if (copy_from_user(wbuf, buf, n))
> +		return -EFAULT;

That's a global buffer - what's protecting it from concurrent access?

[...]

> +	 * we need to _manually delay ourselves_ to remain backwards
> +	 * compatible with LIRC and prevent our queue buffer from overflowing.
> +	 * Queuing in this driver is about instant, and send_start for example
> +	 * will fill it up quickly and prevent send_stop from taking immediate
> +	 * effect.
> +	 */
> +	dprintk("Total elapsed time is: %d. \n", total_time_elapsed);
> +	do_gettimeofday(&end_time);
> +	/*
> +	 * udelay for the difference between endtime and
> +	 * start + total_time_elapsed
> +	 */
> +	if (start_time.tv_usec < end_time.tv_usec)
> +		real_time_elapsed = (end_time.tv_usec - start_time.tv_usec);
> +	else
> +		real_time_elapsed = ((end_time.tv_usec +  1000000) -
> +							start_time.tv_usec);
> +	dprintk("Real time elapsed was %u.\n", real_time_elapsed);
> +	if (real_time_elapsed < (total_time_elapsed - 1000))
> +		wait_this = total_time_elapsed - real_time_elapsed - 1000;
> +
> +#if 0 /* enable this for backwards compatibility */
> +	safe_udelay(wait_this);
> +#endif

I assume that compatibility is not needed now?  In that case, this code
(and safe_udelay()) can go away.

jon

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 02/18] lirc serial port receiver/transmitter device driver
  2008-09-09 16:14     ` [PATCH 02/18] lirc serial port receiver/transmitter device driver Jonathan Corbet
  2008-09-09 19:51       ` Stefan Lippers-Hollmann
@ 2008-09-10 17:40       ` Jarod Wilson
  1 sibling, 0 replies; 111+ messages in thread
From: Jarod Wilson @ 2008-09-10 17:40 UTC (permalink / raw)
  To: Jonathan Corbet
  Cc: linux-kernel, Jarod Wilson, Janne Grunau, Christoph Bartelmus

On Tuesday 09 September 2008 12:14:22 Jonathan Corbet wrote:
> > +#ifdef LIRC_SERIAL_IRDEO
> > +static int type = LIRC_IRDEO;
> > +#elif defined(LIRC_SERIAL_IRDEO_REMOTE)
> > +static int type = LIRC_IRDEO_REMOTE;
> > +#elif defined(LIRC_SERIAL_ANIMAX)
> > +static int type = LIRC_ANIMAX;
> > +#elif defined(LIRC_SERIAL_IGOR)
> > +static int type = LIRC_IGOR;
> > +#elif defined(LIRC_SERIAL_NSLU2)
> > +static int type = LIRC_NSLU2;
> > +#else
> > +static int type = LIRC_HOMEBREW;
> > +#endif
>
> So where do all these LIRC_SERIAL_* macros come from?  I can't really tell
> what this bunch of ifdeffery is doing or how one might influence it.

Bleah. I believe these get passed in when building lirc userspace and drivers 
together, when manually selected the specific type of serial receiver you have 
in lirc's menu-driven configuration tool thingy...

In other words, they do us absolutely no good here. We just build for the 
default type (LIRC_HOMEBREW), and users can override the type as necessary via 
the 'type' module parameter. I'll nuke that chunk.

> > +
> > +static struct lirc_serial hardware[] = {
> > +	/* home-brew receiver/transmitter */
> > +	{
> > +		UART_MSR_DCD,
> > +		UART_MSR_DDCD,
> > +		UART_MCR_RTS|UART_MCR_OUT2|UART_MCR_DTR,
> > +		UART_MCR_RTS|UART_MCR_OUT2,
> > +		send_pulse_homebrew,
> > +		send_space_homebrew,
> > +		(
> > +#ifdef LIRC_SERIAL_TRANSMITTER
> > +		 LIRC_CAN_SET_SEND_DUTY_CYCLE|
> > +		 LIRC_CAN_SET_SEND_CARRIER|
> > +		 LIRC_CAN_SEND_PULSE|
> > +#endif
> > +		 LIRC_CAN_REC_MODE2)
> > +	},
>
> It would be really nice to use the .field=value structure initialization
> conventions here.

Indeed. Done.

> > +#if defined(__i386__)
> > +/*
> > +  From:
> > +  Linux I/O port programming mini-HOWTO
> > +  Author: Riku Saikkonen <Riku.Saikkonen@hut.fi>
> > +  v, 28 December 1997
> > +
> > +  [...]
> > +  Actually, a port I/O instruction on most ports in the 0-0x3ff range
> > +  takes almost exactly 1 microsecond, so if you're, for example,using
> > +  the parallel port directly, just do additional inb()s from that port
> > +  to delay.
> > +  [...]
> > +*/
> > +/* transmitter latency 1.5625us 0x1.90 - this figure arrived at from
> > + * comment above plus trimming to match actual measured frequency.
> > + * This will be sensitive to cpu speed, though hopefully most of the
> > 1.5us + * is spent in the uart access.  Still - for reference test
> > machine was a + * 1.13GHz Athlon system - Steve
> > + */
> > +
> > +/* changed from 400 to 450 as this works better on slower machines;
> > +   faster machines will use the rdtsc code anyway */
> > +
> > +#define LIRC_SERIAL_TRANSMITTER_LATENCY 450
> > +
> > +#else
> > +
> > +/* does anybody have information on other platforms ? */
> > +/* 256 = 1<<8 */
> > +#define LIRC_SERIAL_TRANSMITTER_LATENCY 256
> > +
> > +#endif  /* __i386__ */
>
> This is a little scary.  Maybe hrtimers would be a better way of handling
> your timing issues?

Sounds plausible, will look into it. Of course, this partially hinges on the 
USE_RDTSC bits, more comments just a little ways down...

> > +static inline unsigned int sinp(int offset)
> > +{
> > +#if defined(LIRC_ALLOW_MMAPPED_IO)
> > +	if (iommap != 0) {
> > +		/* the register is memory-mapped */
> > +		offset <<= ioshift;
> > +		return readb(io + offset);
> > +	}
> > +#endif
> > +	return inb(io + offset);
> > +}
>
> This all looks like a reimplementation of ioport_map() and the associated
> ioread*() and iowrite*() functions...?

Probably. Will see about using those instead.

> > +#ifdef USE_RDTSC
> > +/* Version that uses Pentium rdtsc instruction to measure clocks */
> > +
> > +/* This version does sub-microsecond timing using rdtsc instruction,
> > + * and does away with the fudged LIRC_SERIAL_TRANSMITTER_LATENCY
> > + * Implicitly i586 architecture...  - Steve
> > + */
> > +
> > +static inline long send_pulse_homebrew_softcarrier(unsigned long length)
> > +{
> > +	int flag;
> > +	unsigned long target, start, now;
> > +
> > +	/* Get going quick as we can */
> > +	rdtscl(start); on();
> > +	/* Convert length from microseconds to clocks */
> > +	length *= conv_us_to_clocks;
> > +	/* And loop till time is up - flipping at right intervals */
> > +	now = start;
> > +	target = pulse_width;
> > +	flag = 1;
> > +	while ((now-start) < length) {
> > +		/* Delay till flip time */
> > +		do {
> > +			rdtscl(now);
> > +		} while ((now-start) < target);
>
> This looks like a hard busy wait, without even an occasional, polite,
> cpu_relax() call.  There's got to be a better way?
>
> The i2c code has the result of a lot of bit-banging work, I wonder if
> there's something there which could be helpful here.

Hrm... So at some point in the past, there was an "#if defined(rdtscl)" in the 
lirc_serial.c code that triggered USE_RDTSC being defined. At the moment, 
there's nothing defining it (I probably overzealously nuked it during clean-
up), so we're not even touching the above code. However, this is supposed to 
be the "better" code path wrt producing a reliable waveform, at least on 
platforms with rdtscl... Will have to do some investigating... This actually 
affects whether or not we bother with hrtimers as suggested above too, as 
LIRC_SERIAL_TRANSMITTER_LATENCY is not used at all in the USE_RDTSC case.

> > +static irqreturn_t irq_handler(int i, void *blah)
> > +{
> > +	struct timeval tv;
> > +	int status, counter, dcd;
> > +	long deltv;
> > +	int data;
> > +	static int last_dcd = -1;
> > +
> > +	if ((sinp(UART_IIR) & UART_IIR_NO_INT)) {
> > +		/* not our interrupt */
> > +		return IRQ_RETVAL(IRQ_NONE);
> > +	}
>
> That should just be IRQ_NONE, no?

Yeah, looks like it. Done.

> > +static void hardware_init_port(void)
> > +{
> > +	unsigned long flags;
> > +	local_irq_save(flags);
>
> That won't help you if an interrupt is handled by another processor.  This
> needs proper locking, methinks.

Yeah, working on implementing locking right now.

> Nothing in this function does anything to assure itself that the port
> actually exists and is the right kind of hardware.  Maybe that can't really
> be done with this kind of device?

We should probably try to make sure the port actually exists, but I don't 
think there's a whole lot (if anything) we can do as far as verifying the 
device itself.

> > +static int init_port(void)
> > +{
>
>  ...
>
> > +	if (sense == -1) {
> > +		/* wait 1/2 sec for the power supply */
> > +
> > +		set_current_state(TASK_INTERRUPTIBLE);
> > +		schedule_timeout(HZ/2);
>
> msleep(), maybe?

Yeah, looks like it.

> > +static int set_use_inc(void *data)
> > +{
> > +	int result;
> > +	unsigned long flags;
> > +
> > +	/* Init read buffer. */
> > +	if (lirc_buffer_init(&rbuf, sizeof(int), RBUF_LEN) < 0)
> > +		return -ENOMEM;
> > +
> > +	/* initialize timestamp */
> > +	do_gettimeofday(&lasttv);
> > +
> > +	result = request_irq(irq, irq_handler,
> > +			   IRQF_DISABLED | (share_irq ? IRQF_SHARED : 0),
> > +			   LIRC_DRIVER_NAME, (void *)&hardware);
> > +
> > +	switch (result) {
> > +	case -EBUSY:
> > +		printk(KERN_ERR LIRC_DRIVER_NAME ": IRQ %d busy\n", irq);
> > +		lirc_buffer_free(&rbuf);
> > +		return -EBUSY;
> > +	case -EINVAL:
> > +		printk(KERN_ERR LIRC_DRIVER_NAME
> > +		       ": Bad irq number or handler\n");
> > +		lirc_buffer_free(&rbuf);
> > +		return -EINVAL;
> > +	default:
> > +		dprintk("Interrupt %d, port %04x obtained\n", irq,
> > io);
> > +		break;
> > +	};
> > +
> > +	local_irq_save(flags);
> > +
> > +	/* Set DLAB 0. */
> > +	soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
> > +
> > +	soutp(UART_IER, sinp(UART_IER)|UART_IER_MSI);
> > +
> > +	local_irq_restore(flags);
> > +
> > +	return 0;
> > +}
>
> OK, so set_use_inc() really is just an open() function.  It still seems
> like a strange duplication.

Going to let the duplication be for the moment, since I don't know the history 
behind why there's duplication, and there are enough other mountains to climb 
first... :)

> Again, local_irq_save() seems insufficient here.  You need a lock to
> serialize access to the hardware.

Will do.

-- 
Jarod Wilson
jarod@redhat.com


^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 08/18] lirc driver for the Soundgraph IMON IR Receivers
  2008-09-09  4:05               ` [PATCH 08/18] lirc driver for the Soundgraph IMON IR Receivers Jarod Wilson
  2008-09-09  4:05                 ` [PATCH 09/18] lirc driver for the Streamzap PC Receiver Jarod Wilson
@ 2008-09-10 21:02                 ` Jonathan Corbet
  2008-09-10 21:23                   ` Janne Grunau
  2008-09-22 21:47                   ` Jarod Wilson
  1 sibling, 2 replies; 111+ messages in thread
From: Jonathan Corbet @ 2008-09-10 21:02 UTC (permalink / raw)
  Cc: linux-kernel, Jarod Wilson, Janne Grunau, Christoph Bartelmus

> +#define SUCCESS		0
> +#define	TRUE		1
> +#define FALSE		0

(See my grumble in previous reviews...:)

> +#define LOCK_CONTEXT	mutex_lock(&context->lock)
> +#define UNLOCK_CONTEXT	mutex_unlock(&context->lock)

Here too.

> +/* to prevent races between open() and disconnect() */
> +static DECLARE_MUTEX(disconnect_sem);

This should be a real mutex, I think.

> +/* lcd or vfd? */
> +static int is_lcd;

The driver can only do one or the other?  You can't have both in the
system?  And somehow the user is supposed to configure it at load time to
do the right thing?

> +/**
> + * Sends a packet to the VFD.
> + */
> +static inline int send_packet(struct imon_context *context)
> +{
> +	unsigned int pipe;
> +	int interval = 0;
> +	int retval = SUCCESS;
> +	struct usb_ctrlrequest *control_req = NULL;
> +
> +	/* Check if we need to use control or interrupt urb */
> +	if (!context->tx_control) {
> +		pipe = usb_sndintpipe(context->dev,
> +				      context->tx_endpoint->bEndpointAddress);
> +		interval = context->tx_endpoint->bInterval;
> +
> +		usb_fill_int_urb(context->tx_urb, context->dev, pipe,
> +				 context->usb_tx_buf,
> +				 sizeof(context->usb_tx_buf),
> +				 usb_tx_callback, context, interval);
> +
> +		context->tx_urb->actual_length = 0;
> +	} else {
> +		/* fill request into kmalloc'ed space: */
> +		control_req = kmalloc(sizeof(struct usb_ctrlrequest), GFP_NOIO);

Why GFP_NOIO?

> +		if (control_req == NULL)
> +			return -ENOMEM;
> +
> +		/* setup packet is '21 09 0200 0001 0008' */
> +		control_req->bRequestType = 0x21;
> +		control_req->bRequest = 0x09;
> +		control_req->wValue = cpu_to_le16(0x0002);
> +		control_req->wIndex = cpu_to_le16(0x0100);
> +		control_req->wLength = cpu_to_le16(0x0800);
> +
> +		/* control pipe is endpoint 0x00 */
> +		pipe = usb_sndctrlpipe(context->dev, 0);
> +
> +		/* build the control urb */
> +		usb_fill_control_urb(context->tx_urb, context->dev, pipe,
> +				     (unsigned char *)control_req,
> +				     context->usb_tx_buf,
> +				     sizeof(context->usb_tx_buf),
> +				     usb_tx_callback, context);
> +		context->tx_urb->actual_length = 0;
> +	}
> +
> +	init_completion(&context->tx.finished);
> +	atomic_set(&(context->tx.busy), 1);
> +
> +	retval =  usb_submit_urb(context->tx_urb, GFP_KERNEL);
> +	if (retval != SUCCESS) {
> +		atomic_set(&(context->tx.busy), 0);
> +		err("%s: error submitting urb(%d)", __func__, retval);
> +	} else {
> +		/* Wait for tranmission to complete(or abort) */
> +		UNLOCK_CONTEXT;
> +		wait_for_completion(&context->tx.finished);
> +		LOCK_CONTEXT;

Should that be an interruptible (or killable) wait?

> +static ssize_t show_associate_remote(struct device *d,
> +				     struct device_attribute *attr,
> +				     char *buf)
> +{
> +	struct imon_context *context = dev_get_drvdata(d);
> +
> +	if (!context)
> +		return -ENODEV;
> +
> +	if (context->ir_isassociating) {
> +		strcpy(buf, "The device it associating press some button "
> +			    "on the remote.\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");
> +	} else {
> +		strcpy(buf, "Device is closed, you need to open it to "
> +			    "associate the remote(you can use irw).\n");
> +	}
> +	return strlen(buf);
> +}

I *guess* that's one value per file, but it's still not quite the sysfs
norm.  How about one-word status codes which can be made more verbose by
user space?  (That's an API change, of course...)

> +static ssize_t store_associate_remote(struct device *d,
> +				      struct device_attribute *attr,
> +				      const char *buf, size_t count)
> +{
> +	struct imon_context *context;
> +
> +	context = dev_get_drvdata(d);
> +
> +	if (!context)
> +		return -ENODEV;
> +
> +	if (!context->ir_isopen)
> +		return -EINVAL;
> +
> +	if (context->ir_isopen) {
> +		context->ir_isassociating = TRUE;
> +		send_associate_24g(context);
> +	}
> +
> +	return count;
> +}

No need to take the mutex here?

> +/**
> + * Called by lirc_dev when the application opens /dev/lirc
> + */
> +static int ir_open(void *data)
> +{
> +	int retval = SUCCESS;
> +	struct imon_context *context;
> +
> +	/* prevent races with disconnect */
> +	down(&disconnect_sem);
> +
> +	context = (struct imon_context *) data;
> +
> +	LOCK_CONTEXT;
> +
> +	if (context->ir_isopen) {
> +		err("%s: IR port is already open", __func__);
> +		retval = -EBUSY;
> +		goto exit;
> +	}

I wonder if the single-open semantics are really doing what the author
intended?  It is unsufficient to prevent concurrent calls elsewhere.

> +	/* 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->dev,
> +		usb_rcvintpipe(context->dev,
> +				context->rx_endpoint->bEndpointAddress),
> +		context->usb_rx_buf, sizeof(context->usb_rx_buf),
> +		usb_rx_callback, context, context->rx_endpoint->bInterval);
> +
> +	retval = usb_submit_urb(context->rx_urb, GFP_KERNEL);
> +
> +	if (retval)
> +		err("%s: usb_submit_urb failed for ir_open(%d)",
> +		    __func__, retval);
> +	else {
> +		context->ir_isopen = TRUE;
> +		info("IR port opened");
> +	}
> +
> +exit:
> +	UNLOCK_CONTEXT;
> +
> +	up(&disconnect_sem);
> +	return SUCCESS;
> +}

This discards "retval", which could hold an error status - the function
returns "SUCCESS" even if ->ir_isopen is not set.

> +/**
> + * Callback function for USB core API: disonnect
> + */
> +static void imon_disconnect(struct usb_interface *interface)
> +{
> +	struct imon_context *context;
> +
> +	/* prevent races with ir_open()/vfd_open() */
> +	down(&disconnect_sem);
> +
> +	context = usb_get_intfdata(interface);
> +	LOCK_CONTEXT;
> +
> +	info("%s: iMON device disconnected", __func__);
> +
> +	/* 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);
> +	context->dev_present = FALSE;
> +
> +	/* Stop reception */
> +	usb_kill_urb(context->rx_urb);
> +
> +	/* Abort ongoing write */
> +	if (atomic_read(&context->tx.busy)) {
> +		usb_kill_urb(context->tx_urb);
> +		wait_for_completion(&context->tx.finished);
> +	}

What if this races with another thread waiting for the completion?  It
seems like it should be completed with complete_all(), but that wasn't the
case. 


^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 08/18] lirc driver for the Soundgraph IMON IR Receivers
  2008-09-10 21:02                 ` [PATCH 08/18] lirc driver for the Soundgraph IMON IR Receivers Jonathan Corbet
@ 2008-09-10 21:23                   ` Janne Grunau
  2008-09-11  3:22                     ` Jarod Wilson
  2008-09-22 21:47                   ` Jarod Wilson
  1 sibling, 1 reply; 111+ messages in thread
From: Janne Grunau @ 2008-09-10 21:23 UTC (permalink / raw)
  To: Jonathan Corbet; +Cc: linux-kernel, Jarod Wilson, Christoph Bartelmus

On Wednesday 10 September 2008 23:02:29 Jonathan Corbet wrote:
> > +#define SUCCESS		0
> > +#define	TRUE		1
> > +#define FALSE		0
>
> (See my grumble in previous reviews...:)
>
> > +#define LOCK_CONTEXT	mutex_lock(&context->lock)
> > +#define UNLOCK_CONTEXT	mutex_unlock(&context->lock)
>
> Here too.
>
> > +/* to prevent races between open() and disconnect() */
> > +static DECLARE_MUTEX(disconnect_sem);
>
> This should be a real mutex, I think.

All three already resolved. I think it would wise to stop the review of 
this patch set until we repost or review based on Jarod's git 
repository (http://git.wilsonet.com/linux-2.6-lirc.git/).

Thanks for the reviews so far.

Janne

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 08/18] lirc driver for the Soundgraph IMON IR Receivers
  2008-09-10 21:23                   ` Janne Grunau
@ 2008-09-11  3:22                     ` Jarod Wilson
  0 siblings, 0 replies; 111+ messages in thread
From: Jarod Wilson @ 2008-09-11  3:22 UTC (permalink / raw)
  To: Janne Grunau; +Cc: Jonathan Corbet, linux-kernel, Christoph Bartelmus

On Wednesday 10 September 2008 17:23:51 Janne Grunau wrote:
> On Wednesday 10 September 2008 23:02:29 Jonathan Corbet wrote:
> > > +#define SUCCESS		0
> > > +#define	TRUE		1
> > > +#define FALSE		0
> >
> > (See my grumble in previous reviews...:)
> >
> > > +#define LOCK_CONTEXT	mutex_lock(&context->lock)
> > > +#define UNLOCK_CONTEXT	mutex_unlock(&context->lock)
> >
> > Here too.
> >
> > > +/* to prevent races between open() and disconnect() */
> > > +static DECLARE_MUTEX(disconnect_sem);
> >
> > This should be a real mutex, I think.
>
> All three already resolved. I think it would wise to stop the review of
> this patch set until we repost or review based on Jarod's git
> repository (http://git.wilsonet.com/linux-2.6-lirc.git/).

Yeah, I'd say for any driver that hasn't yet been reviewed, if anyone wants to 
continue reviewing, it might be best to poke the version in that git tree, as 
a number of issues common across multiple drivers have already (hopefully) 
been whacked. We'll definitely put together an updated patch set in the near 
future, but at the moment, we're still working through all the bits that have 
received feedback so far.

> Thanks for the reviews so far.

Indeed, very much appreciated!

-- 
Jarod Wilson
jarod@redhat.com


^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 01/18] lirc core device driver infrastructure
  2008-09-10 13:08   ` Dmitry Torokhov
@ 2008-09-11  8:47     ` Gerd Hoffmann
  2008-09-11 21:28       ` Maxim Levitsky
  2008-09-12  4:44       ` Dmitry Torokhov
  0 siblings, 2 replies; 111+ messages in thread
From: Gerd Hoffmann @ 2008-09-11  8:47 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Jarod Wilson, linux-kernel, Jarod Wilson, Janne Grunau,
	Christoph Bartelmus, Mario Limonciello

Dmitry Torokhov wrote:
> Couple of questions - what is missing in the current input core to
> properly support IR devices?

One issue is *sending* IR codes.
Another one is access to the raw IR signal (more on this below).

Probably 90% of the users are happy without that.

> Could we try to work it in ininput core and
> maybe provide LIRC device access through an input handler
> implementation so that applications do not have to implement 2 types of
> interfaces?

Not needed.  Applications don't talk to the lirc device directly, they
talk to the lircd daemon.  The lircd daemon can handle linux input layer
devices just fine.  So moving drivers from lirc to the input layer can
be done transparently to the applications.

Quite some time ago, back the days when I maintained video4linux, I've
switched the tv card IR drivers from lirc to the input layer.  Main
reason for that was that having a in-kernel driver using an out-of-tree
subsystem was a PITA.  I think these days most (all?) TV card drivers
use the input layer for the IR remotes frequently shipped with those cards.


Some more background, using the Hauppauge cards as example, which
usually use rc5 coding with the remotes.

The older, bt878 based ones do decoding in hardware (i2c chip).  You'll
get the decoded rc5 keycode.

The newer, cx88 based ones just sample the raw IR signal and give you
that.  The decoding has to be done in software.  The driver can program
the sample rate and has to do the biphase decoding in software to get
the rc5 keycodes.  The driver gets that done just fine, and the remote
shipped with the TV card works without problems.

The part which doesn't work is supporting *any* remote.  The (newer)
hardware gives you the raw IR signal, so it is possible to decode any IR
signal in software.  The missing bit is some way to send the raw IR
samples to userspace (i.e. the lircd daemon) for decoding.  lirc drivers
do just that.

HTH,
  Gerd

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 04/18] lirc driver for 2nd-gen and later Media Center Ed. USB IR transceivers
  2008-09-09 23:30         ` [PATCH 04/18] lirc driver for 2nd-gen and later Media Center Ed. USB IR transceivers Jonathan Corbet
  2008-09-10  0:36           ` Janne Grunau
@ 2008-09-11  9:21           ` Adrian Bunk
  1 sibling, 0 replies; 111+ messages in thread
From: Adrian Bunk @ 2008-09-11  9:21 UTC (permalink / raw)
  To: Jonathan Corbet
  Cc: Jarod Wilson, linux-kernel, Janne Grunau, Christoph Bartelmus

On Tue, Sep 09, 2008 at 05:30:57PM -0600, Jonathan Corbet wrote:
> On Tue,  9 Sep 2008 00:05:49 -0400
> Jarod Wilson <jwilson@redhat.com> wrote:
> 
> > +/*
> > + * LIRC driver for Philips eHome USB Infrared Transceiver
> > + * and the Microsoft MCE 2005 Remote Control
> > + *
> > + * (C) by Martin A. Blatter <martin_a_blatter@yahoo.com>
> > + *
> > + * Transmitter support and reception code cleanup.
> > + * (C) by Daniel Melander <lirc@rajidae.se>
> 
> As I understand it, proper assertions of copyright need the word
> "copyright" and a year; the "(C)" trigraph doesn't have any legal meaning.
>...

As far as I understand it your statement is only true if you are in
the USA and travel back in time by at least 20 years, and otherwise
no proper assertion is required at all.

Anything I miss?

> jon

cu
Adrian

-- 

       "Is there not promise of rain?" Ling Tan asked suddenly out
        of the darkness. There had been need of rain for many days.
       "Only a promise," Lao Er said.
                                       Pearl S. Buck - Dragon Seed


^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 09/18] lirc driver for the Streamzap PC Receiver
  2008-09-09  4:05                 ` [PATCH 09/18] lirc driver for the Streamzap PC Receiver Jarod Wilson
  2008-09-09  4:05                   ` [PATCH 10/18] lirc driver for Igor Cesko's USB IR receiver Jarod Wilson
@ 2008-09-11 15:22                   ` Jonathan Corbet
  1 sibling, 0 replies; 111+ messages in thread
From: Jonathan Corbet @ 2008-09-11 15:22 UTC (permalink / raw)
  Cc: linux-kernel, Jarod Wilson, Janne Grunau, Christoph Bartelmus

[Yes, you said I shouldn't do more reviews of the posted code, but the
following was already pretty much written.  I really only found one thing
in this driver, and I suspect it may not have been addressed yet.  

This was set to be the last anyway, I'm pretty much out of time for now.]

jon

I'm curious about the following function:

> +static void stop_timer(struct usb_streamzap *sz)
> +{
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&sz->timer_lock, flags);
> +	if (sz->timer_running) {

What happens if the timer fires right here?  

> +		sz->timer_running = 0;
> +		del_timer_sync(&sz->delay_timer);
> +	}
> +	spin_unlock_irqrestore(&sz->timer_lock, flags);
> +}

The timer acquires the lock too, so this looks a bit like a deadlock to
me.  Probably del_timer_sync() should be called without the lock?

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 01/18] lirc core device driver infrastructure
  2008-09-09  9:53     ` Janne Grunau
  2008-09-09 12:33       ` Sebastian Siewior
@ 2008-09-11 16:41       ` Christoph Bartelmus
  1 sibling, 0 replies; 111+ messages in thread
From: Christoph Bartelmus @ 2008-09-11 16:41 UTC (permalink / raw)
  To: j; +Cc: jarod, jwilson, linux-kernel, lkml, superm1

Hi Janne,

on 09 Sep 08 at 11:53, Janne Grunau wrote:
> On Tuesday 09 September 2008 09:40:18 Sebastian Siewior wrote:
[...]
>>>+EXPORT_SYMBOL(lirc_register_plugin);
>>
>> Is EXPORT_SYMBOL_GPL() possible?

> Afaik yes, all drivers using this symbol are in this patchset and under
> GPL. If it is desireable to to use EXPORT_SYMBOL_GPL() has someone else
> to decide. Christoph?

Please keep EXPORT_SYMBOL().

Christoph

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 01/18] lirc core device driver infrastructure
  2008-09-10 12:24     ` Janne Grunau
  2008-09-10 12:29       ` Christoph Hellwig
@ 2008-09-11 18:03       ` Christoph Bartelmus
  2008-09-11 19:18         ` Janne Grunau
  1 sibling, 1 reply; 111+ messages in thread
From: Christoph Bartelmus @ 2008-09-11 18:03 UTC (permalink / raw)
  To: j; +Cc: hch, jarod, jwilson, linux-kernel, superm1

Hi Janne,

on 10 Sep 08 at 14:24, Janne Grunau wrote:
> On Tuesday 09 September 2008 15:01:02 Christoph Hellwig wrote:

>>> +	do {
>>> +		if (ir->open) {
>>> +			if (ir->jiffies_to_wait) {
>>> +				set_current_state(TASK_INTERRUPTIBLE);
>>> +				schedule_timeout(ir->jiffies_to_wait);
>>> +			} else {
>>> +				interruptible_sleep_on(
>>> +					ir->p.get_queue(ir->p.data));
>>> +			}
>>
>> No sleep on, please.  But this one should be trivial to fix anyway,
>> by just changing this to a
>>
>> 	set_current_state(TASK_INTERRUPTIBLE);
>> 	schedule();
>>
>> and using wake_up_process in the wakeup path.

> No driver uses the get_queue callback. So I'm inclined to just remove
> it. Christoph, any objections?

lirc_gpio is using it.
I guess there will be no need to use lirc_gpio in future, but I'd like to  
keep this in CVS for people who still use it.

Christoph

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 07/18] lirc driver for the CommandIR USB Transceiver
  2008-09-10 17:09               ` [PATCH 07/18] lirc driver for the CommandIR USB Transceiver Jonathan Corbet
@ 2008-09-11 18:24                 ` Christoph Bartelmus
       [not found]                   ` <1221159005.13683.34.camel@minimatt>
  2008-09-25 15:21                 ` Jarod Wilson
  1 sibling, 1 reply; 111+ messages in thread
From: Christoph Bartelmus @ 2008-09-11 18:24 UTC (permalink / raw)
  To: corbet; +Cc: j, jwilson, linux-kernel, matthew.bodkin

Hi,

there is a user-space driver that is going to replace the kernel driver  
for the CommandIR driver. I don't think there is a reason anymore to get  
this driver into the kernel.
Added, Matthew.

Christoph

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 01/18] lirc core device driver infrastructure
  2008-09-09 17:03     ` Jarod Wilson
@ 2008-09-11 18:30       ` Christoph Bartelmus
  2008-09-11 19:09         ` Jarod Wilson
  0 siblings, 1 reply; 111+ messages in thread
From: Christoph Bartelmus @ 2008-09-11 18:30 UTC (permalink / raw)
  To: jwilson; +Cc: j, linux-kernel, lkml, superm1

Hi,

on 09 Sep 08 at 13:03, Jarod Wilson wrote:
> On Tuesday 09 September 2008 03:40:18 Sebastian Siewior wrote:
[...]
>>>+EXTRA_CFLAGS =-DIRCTL_DEV_MAJOR=61 -DLIRC_SERIAL_TRANSMITTER -I$(src)
>>
>> Do you rely on this specific major? Since your daemon opens /dev/lirc0
>> you don't need a fixed major do you?

> Good question. Quite honestly, I'm not sure. Christoph?

LIRC does not rely on a specific major. But to be honest, the last time I  
looked into this issue, was when devfs was bleeding-edge...

So, we need some advice here how to proceed. Should we try to register an  
official major number for LIRC? Should we try to have a minor number  
mapping, e.g.
/dev/lirc/serial/0 LIRC device on 1st UART serial port
...
/dev/lirc/serial/n LIRC device on n-th UART serial port
/dev/lirc/parallel/0 LIRC device on 1st parallel port
...
/dev/lirc/parallel/n LIRC device on n-th parallel port
/dev/lirc/usb/0 1st LIRC USB device
...

Christoph

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 07/18] lirc driver for the CommandIR USB Transceiver
       [not found]                   ` <1221159005.13683.34.camel@minimatt>
@ 2008-09-11 19:03                     ` Jarod Wilson
  2008-09-11 19:14                     ` Janne Grunau
  1 sibling, 0 replies; 111+ messages in thread
From: Jarod Wilson @ 2008-09-11 19:03 UTC (permalink / raw)
  To: Matthew Bodkin; +Cc: Christoph Bartelmus, corbet, j, linux-kernel

On Thursday 11 September 2008 14:50:05 Matthew Bodkin wrote:
> Hi Christoph, All -
>
> True, I added a 'commandir' userspace driver a couple months ago that
> replaces the kernel 'lirc_cmdir' driver.
>
> However the userspace driver remains unreleased in CVS so until there is
> a new LIRC release, I think the include the old kernel driver will have
> to be included.

I thought I remembered something about a userspace driver... Don't worry about 
whether or not its released in an lirc tarball though. The code headed to the 
kernel is cvs head based already.

I take it the userspace driver is the commandir.{c,h} bits vs. the old 
lirc_cmdir.{c,h} bits being the old driver. D'oh. Its been sitting right under 
my nose... So yeah, we'll drop the lirc_cmdir bits, and roll with just the 
commandir bits.

Side bar: wish I'd had more time to read over *all* the code, rather than 
focusing so much on just the checkpatch cleanups, but I think it was better to 
get this out here and get some momentum built up anyhow...

> Demo hardware is available for testing if anyone wants.

That would be most excellent. I'd definitely love to get some, will drop you a 
line off-list.


> On Thu, 2008-09-11 at 20:24 +0200, Christoph Bartelmus wrote:
> > Hi,
> >
> > there is a user-space driver that is going to replace the kernel driver
> > for the CommandIR driver. I don't think there is a reason anymore to get
> > this driver into the kernel.
> > Added, Matthew.
> >
> > Christoph

-- 
Jarod Wilson
jarod@redhat.com


^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 01/18] lirc core device driver infrastructure
  2008-09-11 18:30       ` Christoph Bartelmus
@ 2008-09-11 19:09         ` Jarod Wilson
  2008-09-13  7:21           ` Christoph Bartelmus
  0 siblings, 1 reply; 111+ messages in thread
From: Jarod Wilson @ 2008-09-11 19:09 UTC (permalink / raw)
  To: Christoph Bartelmus; +Cc: j, linux-kernel, lkml, superm1

On Thursday 11 September 2008 14:30:00 Christoph Bartelmus wrote:
> Hi,
>
> on 09 Sep 08 at 13:03, Jarod Wilson wrote:
> > On Tuesday 09 September 2008 03:40:18 Sebastian Siewior wrote:
>
> [...]
>
> >>>+EXTRA_CFLAGS =-DIRCTL_DEV_MAJOR=61 -DLIRC_SERIAL_TRANSMITTER -I$(src)
> >>
> >> Do you rely on this specific major? Since your daemon opens /dev/lirc0
> >> you don't need a fixed major do you?
> >
> > Good question. Quite honestly, I'm not sure. Christoph?
>
> LIRC does not rely on a specific major. But to be honest, the last time I
> looked into this issue, was when devfs was bleeding-edge...
>
> So, we need some advice here how to proceed. Should we try to register an
> official major number for LIRC? Should we try to have a minor number
> mapping, e.g.
> /dev/lirc/serial/0 LIRC device on 1st UART serial port
> ...
> /dev/lirc/serial/n LIRC device on n-th UART serial port
> /dev/lirc/parallel/0 LIRC device on 1st parallel port
> ...
> /dev/lirc/parallel/n LIRC device on n-th parallel port
> /dev/lirc/usb/0 1st LIRC USB device
> ...

Janne took a crack at dynamic device allocation earlier today, committed into 
my git tree:

http://git.wilsonet.com/linux-2.6-
lirc.git/?a=commitdiff;h=ea74897bf7b03e2ed2401df3815637caa0702eab

Haven't yet tested it out, that's on tap for tonight...

-- 
Jarod Wilson
jarod@redhat.com


^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 07/18] lirc driver for the CommandIR USB Transceiver
       [not found]                   ` <1221159005.13683.34.camel@minimatt>
  2008-09-11 19:03                     ` Jarod Wilson
@ 2008-09-11 19:14                     ` Janne Grunau
  1 sibling, 0 replies; 111+ messages in thread
From: Janne Grunau @ 2008-09-11 19:14 UTC (permalink / raw)
  To: Matthew Bodkin; +Cc: Christoph Bartelmus, corbet, jwilson, linux-kernel

Hi,

On Thursday 11 September 2008 20:50:05 Matthew Bodkin wrote:
>
> True, I added a 'commandir' userspace driver a couple months ago that
> replaces the kernel 'lirc_cmdir' driver.
>
> However the userspace driver remains unreleased in CVS so until there
> is a new LIRC release, I think the include the old kernel driver will
> have to be included.

It doesn't make sense to spend considerible effort cleaning the driver 
up for kernel inclusion if a user space driver exist in licr cvs.

Janne

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 01/18] lirc core device driver infrastructure
  2008-09-11 18:03       ` Christoph Bartelmus
@ 2008-09-11 19:18         ` Janne Grunau
  0 siblings, 0 replies; 111+ messages in thread
From: Janne Grunau @ 2008-09-11 19:18 UTC (permalink / raw)
  To: Christoph Bartelmus; +Cc: hch, jarod, jwilson, linux-kernel, superm1

On Thursday 11 September 2008 20:03:00 Christoph Bartelmus wrote:
> Hi Janne,
>
> on 10 Sep 08 at 14:24, Janne Grunau wrote:
> > On Tuesday 09 September 2008 15:01:02 Christoph Hellwig wrote:
> >>> +	do {
> >>> +		if (ir->open) {
> >>> +			if (ir->jiffies_to_wait) {
> >>> +				set_current_state(TASK_INTERRUPTIBLE);
> >>> +				schedule_timeout(ir->jiffies_to_wait);
> >>> +			} else {
> >>> +				interruptible_sleep_on(
> >>> +					ir->p.get_queue(ir->p.data));
> >>> +			}
> >>
> >> No sleep on, please.  But this one should be trivial to fix
> >> anyway, by just changing this to a
> >>
> >> 	set_current_state(TASK_INTERRUPTIBLE);
> >> 	schedule();
> >>
> >> and using wake_up_process in the wakeup path.
> >
> > No driver uses the get_queue callback. So I'm inclined to just
> > remove it. Christoph, any objections?
>
> lirc_gpio is using it.
> I guess there will be no need to use lirc_gpio in future, but I'd
> like to keep this in CVS for people who still use it.

We don't have lirc_gpio in the kernel tree? And I guess there is no need 
to integrate into the kernel. I've removed it already.

Janne

> Christoph



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 01/18] lirc core device driver infrastructure
  2008-09-11  8:47     ` Gerd Hoffmann
@ 2008-09-11 21:28       ` Maxim Levitsky
  2008-09-13  7:20         ` Christoph Bartelmus
  2008-09-12  4:44       ` Dmitry Torokhov
  1 sibling, 1 reply; 111+ messages in thread
From: Maxim Levitsky @ 2008-09-11 21:28 UTC (permalink / raw)
  To: Gerd Hoffmann
  Cc: Dmitry Torokhov, Jarod Wilson, linux-kernel, Jarod Wilson,
	Janne Grunau, Christoph Bartelmus, Mario Limonciello

Gerd Hoffmann wrote:
> Dmitry Torokhov wrote:
>> Couple of questions - what is missing in the current input core to
>> properly support IR devices?
> 
> One issue is *sending* IR codes.
> Another one is access to the raw IR signal (more on this below).
> 
> Probably 90% of the users are happy without that.
> 
>> Could we try to work it in ininput core and
>> maybe provide LIRC device access through an input handler
>> implementation so that applications do not have to implement 2 types of
>> interfaces?
> 
> Not needed.  Applications don't talk to the lirc device directly, they
> talk to the lircd daemon.  The lircd daemon can handle linux input layer
> devices just fine.  So moving drivers from lirc to the input layer can
> be done transparently to the applications.
> 
> Quite some time ago, back the days when I maintained video4linux, I've
> switched the tv card IR drivers from lirc to the input layer.  Main
> reason for that was that having a in-kernel driver using an out-of-tree
> subsystem was a PITA.  I think these days most (all?) TV card drivers
> use the input layer for the IR remotes frequently shipped with those cards.
> 
> 
> Some more background, using the Hauppauge cards as example, which
> usually use rc5 coding with the remotes.
> 
> The older, bt878 based ones do decoding in hardware (i2c chip).  You'll
> get the decoded rc5 keycode.
> 
> The newer, cx88 based ones just sample the raw IR signal and give you
> that.  The decoding has to be done in software.  The driver can program
> the sample rate and has to do the biphase decoding in software to get
> the rc5 keycodes.  The driver gets that done just fine, and the remote
> shipped with the TV card works without problems.
> 
> The part which doesn't work is supporting *any* remote.  The (newer)
> hardware gives you the raw IR signal, so it is possible to decode any IR
> signal in software.  The missing bit is some way to send the raw IR
> samples to userspace (i.e. the lircd daemon) for decoding.  lirc drivers
> do just that.
Hi,

Just my .02 cents,

I personally don't like the lircd daemon (*), I would like to have all input keycodes in
input layer.
What do you, lirc developers think about making lircd a daemon that injects input events via uinput?


(*) the reason I don't like lircd is that not all applications support it.


Best regards,
	Maxim Levitsky

> 
> HTH,
>   Gerd
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/


^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 01/18] lirc core device driver infrastructure
  2008-09-09 15:33   ` Jonathan Corbet
@ 2008-09-12  0:12     ` Janne Grunau
  0 siblings, 0 replies; 111+ messages in thread
From: Janne Grunau @ 2008-09-12  0:12 UTC (permalink / raw)
  To: Jonathan Corbet
  Cc: Jarod Wilson, linux-kernel, Jarod Wilson, Christoph Bartelmus,
	Mario Limonciello

On Tuesday 09 September 2008 17:33:08 Jonathan Corbet wrote:
> I think it's most cool that this code is finally making its way
> toward the mainline.  What I have is mostly nits...

ignoring bits already handled in Christoph Hellwig's review

> > +		dprintk("lirc_dev [%d]: open result = -ENODEV\n",
> > +			MINOR(inode->i_rdev));
> > +		return -ENODEV;
> > +	}
> > +
> > +	ir = &irctls[MINOR(inode->i_rdev)];
> > +
> > +	dprintk(LOGHEAD "open called\n", ir->p.name, ir->p.minor);
> > +
> > +	/* if the plugin has an open function use it instead */
> > +	if (ir->p.fops && ir->p.fops->open)
> > +		return ir->p.fops->open(inode, file);
> > +
> > +	if (mutex_lock_interruptible(&plugin_lock))
> > +		return -ERESTARTSYS;
>
> So the plugin open() function is called outside of any lock.  Note
> that open() no longer has BKL protection as of 2.6.27.  This might
> all be OK, but I hope you're convinced of it.

plugins fops are called directly now

> > +	if (ir->p.owner != NULL && try_module_get(ir->p.owner)) {
> > +		++ir->open;
> > +		retval = ir->p.set_use_inc(ir->p.data);
>
> Why is there a set_use_inc() function separate from open()?

since not all plugins set p.fops? some of those functions in the drivers 
are even empty. I'll look at it.

> > +		if (retval != SUCCESS) {
> > +			module_put(ir->p.owner);
> > +			--ir->open;
> > +		}
> > +	} else {
> > +		if (ir->p.owner == NULL)
> > +			dprintk(LOGHEAD "no module owner!!!\n",
> > +				ir->p.name, ir->p.minor);
> > +
> > +		retval = -ENODEV;
> > +	}
>
> If "no owner" is a fatal condition, it seems better to check it when
> the plugin is registered.

maybe because it's not needed if the plugin has its own fops.open?

> (Also, BTW, your variant of dprintk() is 
> confusing to read - I was wondering where all the %'s were.  I still
> wonder, actually.  dev_printk() would be better.)

the LOGHEAD define is confusing, I'll look into make more clear

> > +static int irctl_close(struct inode *inode, struct file *file)
> > +{
> > +	struct irctl *ir = &irctls[MINOR(inode->i_rdev)];
> > +
> > +	dprintk(LOGHEAD "close called\n", ir->p.name, ir->p.minor);
> > +
> > +	/* if the plugin has a close function use it instead */
> > +	if (ir->p.fops && ir->p.fops->release)
> > +		return ir->p.fops->release(inode, file);
> > +
> > +	if (mutex_lock_interruptible(&plugin_lock))
> > +		return -ERESTARTSYS;
>
> Should this be interruptible?  You probably want the close call to
> get its job done.  Maybe mutex_lock_killable() - and still do the
> cleanup on a signal?

That's better than the current state.

> > +static int irctl_ioctl(struct inode *inode, struct file *file,
> > +		       unsigned int cmd, unsigned long arg)
> > +{
> > +	unsigned long mode;
> > +	int result;
> > +	struct irctl *ir = &irctls[MINOR(inode->i_rdev)];
> > +
> > +	dprintk(LOGHEAD "ioctl called (0x%x)\n",
> > +		ir->p.name, ir->p.minor, cmd);
> > +
> > +	/* if the plugin has a ioctl function use it instead */
> > +	if (ir->p.fops && ir->p.fops->ioctl)
> > +		return ir->p.fops->ioctl(inode, file, cmd, arg);
> > +
> > +	if (ir->p.minor == NOPLUG || !ir->attached) {
> > +		dprintk(LOGHEAD "ioctl result = -ENODEV\n",
> > +			ir->p.name, ir->p.minor);
> > +		return -ENODEV;
> > +	}
> > +
> > +	/* Give the plugin a chance to handle the ioctl */
> > +	if (ir->p.ioctl) {
> > +		result = ir->p.ioctl(inode, file, cmd, arg);
> > +		if (result != -ENOIOCTLCMD)
> > +			return result;
> > +	}
>
> Why two ioctl() handlers?  It seems better to just have one way for
> plugins to handle this call.

after a cursory look this is used if the plugin doesn't reimplement or 
want to overwrite the commands handled in irctl. I'll look if this can 
be handled better.

> > +	default:
> > +		result = -ENOIOCTLCMD;
>
> Hmm, I note with interest that unlocked_ioctl() remaps -ENOIOCTLCMD
> to -EINVAL, while regular, locked ioctl() (which this is) does not. 
> Not sure what to make of that.

changed this return value to -EINVAL

> > +static ssize_t irctl_read(struct file *file,
> > +			  char *buffer,
> > +			  size_t length,
> > +			  loff_t *ppos)
> > +{
>
>  ...
>
> > +			schedule();
> > +			set_current_state(TASK_INTERRUPTIBLE);
> > +			if (!ir->attached) {
> > +				ret = -ENODEV;
> > +				break;
> > +			}
>
> How can ir->attached go to zero?  You checked it earlier and have
> been holding the mutex ever since.

I don't think the lock has to be held while sleeping

> > +static ssize_t irctl_write(struct file *file, const char *buffer,
> > +			   size_t length, loff_t *ppos)
> > +{
> > +	struct irctl *ir =
> > &irctls[MINOR(file->f_dentry->d_inode->i_rdev)];
> > +	dprintk(LOGHEAD "write called\n", ir->p.name, ir->p.minor);
> > +
> > +	/* if the plugin has a specific read function use it instead
> > */
> > +	if (ir->p.fops && ir->p.fops->write)
> > +		return ir->p.fops->write(file, buffer, length, ppos);
>
> Looks like you're using the "specific write function" instead :)

plugins fops will be used directly now, no need for this comment and 
code anymore ;)

> > +static struct file_operations fops = {
> > +	.read		= irctl_read,
> > +	.write		= irctl_write,
> > +	.poll		= irctl_poll,
> > +	.ioctl		= irctl_ioctl,
> > +	.open		= irctl_open,
> > +	.release	= irctl_close
> > +};
>
> You should probably set .owner too.

done

> > +static int lirc_dev_init(void)
> > +{
> > +	int i;
> > +
> > +	for (i = 0; i < MAX_IRCTL_DEVICES; ++i)
> > +		init_irctl(&irctls[i]);
> > +
> > +	if (register_chrdev(IRCTL_DEV_MAJOR, IRCTL_DEV_NAME, &fops))
> > {
> > +		printk(KERN_ERR "lirc_dev: register_chrdev
> > failed\n");
> > +		goto out;
> > +	}
> > +
> > +	lirc_class = class_create(THIS_MODULE, "lirc");
> > +	if (IS_ERR(lirc_class)) {
> > +		printk(KERN_ERR "lirc_dev: class_create failed\n");
> > +		goto out_unregister;
> > +	}
> > +
> > +	printk(KERN_INFO "lirc_dev: IR Remote Control driver
> > registered, "
> > +	       "major %d \n", IRCTL_DEV_MAJOR);
> > +
> > +	return SUCCESS;
> > +
> > +out_unregister:
> > +	/* unregister_chrdev returns void now */
> > +	unregister_chrdev(IRCTL_DEV_MAJOR, IRCTL_DEV_NAME);
> > +out:
> > +	return -1;
> > +}
>
> Do you want to fail completely if class_create() fails?  What if
> somebody already opened one of your devices?

order reversed and using alloc_chrdev_region so there is no device which 
could be opened. Also returning correct error values now.

Thanks for the review.

Janne

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 01/18] lirc core device driver infrastructure
  2008-09-09 13:01   ` Christoph Hellwig
  2008-09-10 12:24     ` Janne Grunau
@ 2008-09-12  0:16     ` Janne Grunau
  2008-09-12  8:33       ` Christoph Hellwig
  1 sibling, 1 reply; 111+ messages in thread
From: Janne Grunau @ 2008-09-12  0:16 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Jarod Wilson, linux-kernel, Jarod Wilson, Christoph Bartelmus,
	Mario Limonciello, Janne Grunau

On Tuesday 09 September 2008 15:01:02 Christoph Hellwig wrote:
> > +menuconfig INPUT_LIRC
> > +	bool "Linux Infrared Remote Control IR receiver/transmitter
> > drivers" +	default n
>
> n is the default, no needed add a "default n" line for it.

removed

> > +if INPUT_LIRC
> > +
> > +config LIRC_DEV
> > +	tristate "LIRC device loadable module support"
> > +	default n
> > +	help
> > +	  LIRC device loadable module support, required for most LIRC
> > drivers
>
> Obviously this can be built in, so it should be named better.  Also I
> don't think LIRC means anything to a user, so use Infrared Remote
> Control support or something similar instead.

I'm pretty sure LIRC will be recognized by its current users, it is 
probably enough if it is in the help text though.

> > +#define __KERNEL_SYSCALLS__
>
> No need for this in any semi-recent kernel.

already gone

> > +#include <linux/unistd.h>
> > +#include <linux/kthread.h>
> > +
> > +/* SysFS header */
> > +#include <linux/device.h>
>
> That comment is not quite correct, just remove it.

removed

> > +/*  helper function
> > + *  initializes the irctl structure
> > + */
> > +static inline void init_irctl(struct irctl *ir)
> > +{
> > +	memset(&ir->p, 0, sizeof(struct lirc_plugin));
> > +	mutex_init(&ir->buffer_lock);
> > +	ir->p.minor = NOPLUG;
> > +
> > +	ir->task = NULL;
> > +	ir->jiffies_to_wait = 0;
> > +
> > +	ir->open = 0;
> > +	ir->attached = 0;
> > +}
>
> Please don't mark funtion inline unless there's a very good reason
> for it.

all inlines (65 in drivers/inpu/lirc) removed

> > +static void cleanup(struct irctl *ir)
> > +{
> > +	dprintk(LOGHEAD "cleaning up\n", ir->p.name, ir->p.minor);
> > +
> > +	device_destroy(lirc_class, MKDEV(IRCTL_DEV_MAJOR, ir->p.minor));
> > +
> > +	if (ir->buf != ir->p.rbuf) {
> > +		lirc_buffer_free(ir->buf);
> > +		kfree(ir->buf);
> > +	}
> > +	ir->buf = NULL;
> > +
> > +	init_irctl(ir);
> > +}
>
> What's the init doing in a cleanup routine?  Oh, you initialize it
> again becaus of the static array.  I think the right approach is to
> dynamically allocate struct irctl.

agree, it's done dynamically now

> > +{
> > +	if (lirc_buffer_full(ir->buf)) {
> > +		dprintk(LOGHEAD "buffer overflow\n",
> > +			ir->p.name, ir->p.minor);
> > +		return -EOVERFLOW;
> > +	}
> > +
> > +	if (ir->p.add_to_buf) {
> > +		int res = -ENODATA;
> > +		int got_data = 0;
> > +
> > +		/* service the device as long as it is returning
> > +		 * data and we have space
> > +		 */
> > +		while (!lirc_buffer_full(ir->buf)) {
> > +			res = ir->p.add_to_buf(ir->p.data, ir->buf);
> > +			if (res == SUCCESS)
> > +				got_data++;
> > +			else
> > +				break;
> > +		}
> > +
> > +		if (res == -ENODEV)
> > +			kthread_stop(ir->task);
> > +
> > +		return got_data ? SUCCESS : res;
> > +	}
> > +
> > +	return SUCCESS;
>
> I guess success is a #define for 0?  Just user 0 directly.

yes, already removed. also removed custom TRUE/FALSE defines

> Also the 
> kthread_stop here looks odd.  The normal way to use kthreads is to
> start them when bringing up an interface of some sorts, and call
> kthread_stop when the interface is brought down.  Doing it in a
> routine like this screams "unclear lifetime rules".
>
> > +		} else {
> > +			/* if device not opened so we can sleep half a second */
> > +			set_current_state(TASK_INTERRUPTIBLE);
> > +			schedule_timeout(HZ/2);
> > +		}
>
> Yikes.  This should use some form of more fine-grained wakeus.

added a waitqueue and wait_event

> > +	struct irctl *ir;
> > +	int minor;
> > +	int bytes_in_key;
> > +	int err;
> > +	DECLARE_COMPLETION(tn);
> > +
> > +	if (!p) {
> > +		printk(KERN_ERR "lirc_dev: lirc_register_plugin: "
> > +		       "plugin pointer must be not NULL!\n");
> > +		err = -EBADRQC;
> > +		goto out;
> > +	}
>
> No need for this, a null pointer derference should be a clear enough
> warning for the writer of the broken pluging..

ok

> > +int lirc_unregister_plugin(int minor)
>
> Why doesn't this one take a struct lirc_plugin pointer?

I don't know, It doesn't really help though since the struct lirc_plugin 
is copied by value to irctl.p in lirc_register_plugin.

> > +{
> > +	struct irctl *ir;
> > +	DECLARE_COMPLETION(tn);
> > +	DECLARE_COMPLETION(tn2);
>
> both completion seems unused.

the one in lirc_register_plugin too, removed

> > +	/* end up polling thread */
> > +	if (ir->task) {
> > +		wake_up_process(ir->task);
> > +		kthread_stop(ir->task);
> > +	}
>
> kthread_stop already wakes the thread up.

removed

> > +/*
> > + * Recent kernels should handle this autmatically by
> > increasing/decreasing + * use count when a dependant module is
> > loaded/unloaded.
> > + */
> > +
> > +	return SUCCESS;
>
> The comment above looks superflous.

already removed.

> > +static int irctl_open(struct inode *inode, struct file *file)
> > +{
> > +	struct irctl *ir;
> > +	int retval;
> > +
> > +	if (MINOR(inode->i_rdev) >= MAX_IRCTL_DEVICES) {
>
> iminor.

replaced in all occurances

> > +	/* if the plugin has an open function use it instead */
> > +	if (ir->p.fops && ir->p.fops->open)
> > +		return ir->p.fops->open(inode, file);
>
> in which case this 'plugin' should just install it's own fops. 
> Thanks to cdev_add we can install fops at minor number granularity.

done

> > +static unsigned int irctl_poll(struct file *file, poll_table
> > *wait) +{
> > +	struct irctl *ir =
> > &irctls[MINOR(file->f_dentry->d_inode->i_rdev)]; +	unsigned int
> > ret;
> > +
> > +	dprintk(LOGHEAD "poll called\n", ir->p.name, ir->p.minor);
> > +
> > +	/* if the plugin has a poll function use it instead */
> > +	if (ir->p.fops && ir->p.fops->poll)
> > +		return ir->p.fops->poll(file, wait);
> > +
> > +	mutex_lock(&ir->buffer_lock);
>
> ->poll ust not sleep.

I don't see why poll has to hold the lock, removed

> > +/* #define LIRC_BUFF_POWER_OF_2 */
> > +#ifdef LIRC_BUFF_POWER_OF_2
> > +#define mod(n, div) ((n) & ((div) - 1))
> > +#else
> > +#define mod(n, div) ((n) % (div))
> > +#endif

removed

> > +static inline void _lirc_buffer_clear(struct lirc_buffer *buf)
> > +{
> > +	buf->head = 0;
> > +	buf->tail = 0;
> > +	buf->fill = 0;
> > +}
> > +static inline int lirc_buffer_init(struct lirc_buffer *buf,
> > +				    unsigned int chunk_size,
> > +				    unsigned int size)
> > +{
> > +	/* Adjusting size to the next power of 2 would allow for
> > +	 * inconditional LIRC_BUFF_POWER_OF_2 optimization */
> > +	init_waitqueue_head(&buf->wait_poll);
> > +	spin_lock_init(&buf->lock);
> > +	_lirc_buffer_clear(buf);
> > +	buf->chunk_size = chunk_size;
> > +	buf->size = size;
> > +	buf->data = kmalloc(size*chunk_size, GFP_KERNEL);
> > +	if (buf->data == NULL)
> > +		return -1;
> > +	memset(buf->data, 0, size*chunk_size);
> > +	return 0;
> > +}
> > +static inline void lirc_buffer_free(struct lirc_buffer *buf)
> > +{
> > +	kfree(buf->data);
> > +	buf->data = NULL;
> > +	buf->head = 0;
> > +	buf->tail = 0;
> > +	buf->fill = 0;
> > +	buf->chunk_size = 0;
> > +	buf->size = 0;
> > +}
>
> Please move these out of line.  And please document all the
> functions. Or switch to a kfifo or the existing tty buffering
> helpers.

I'll look at it.

> > +static inline void lirc_buffer_lock(struct lirc_buffer *buf,
> > +				    unsigned long *flags)
> > +{
> > +	spin_lock_irqsave(&buf->lock, *flags);
> > +}
> > +static inline void lirc_buffer_unlock(struct lirc_buffer *buf,
> > +				      unsigned long *flags)
> > +{
> > +	spin_unlock_irqrestore(&buf->lock, *flags);
> > +}
>
> Please don't do you own spinlock wrappers.

removed

Thanks for the review.

Janne

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 01/18] lirc core device driver infrastructure
  2008-09-11  8:47     ` Gerd Hoffmann
  2008-09-11 21:28       ` Maxim Levitsky
@ 2008-09-12  4:44       ` Dmitry Torokhov
  1 sibling, 0 replies; 111+ messages in thread
From: Dmitry Torokhov @ 2008-09-12  4:44 UTC (permalink / raw)
  To: Gerd Hoffmann
  Cc: Jarod Wilson, linux-kernel, Jarod Wilson, Janne Grunau,
	Christoph Bartelmus, Mario Limonciello

Hi Gerd,

On Thu, Sep 11, 2008 at 10:47:10AM +0200, Gerd Hoffmann wrote:
> Dmitry Torokhov wrote:
> > Couple of questions - what is missing in the current input core to
> > properly support IR devices?
> 
> One issue is *sending* IR codes.
> Another one is access to the raw IR signal (more on this below).
> 
> Probably 90% of the users are happy without that.
> 
> > Could we try to work it in ininput core and
> > maybe provide LIRC device access through an input handler
> > implementation so that applications do not have to implement 2 types of
> > interfaces?
> 
> Not needed.  Applications don't talk to the lirc device directly, they
> talk to the lircd daemon.  The lircd daemon can handle linux input layer
> devices just fine.  So moving drivers from lirc to the input layer can
> be done transparently to the applications.
> 
> Quite some time ago, back the days when I maintained video4linux, I've
> switched the tv card IR drivers from lirc to the input layer.  Main
> reason for that was that having a in-kernel driver using an out-of-tree
> subsystem was a PITA.  I think these days most (all?) TV card drivers
> use the input layer for the IR remotes frequently shipped with those cards.
> 
> 
> Some more background, using the Hauppauge cards as example, which
> usually use rc5 coding with the remotes.
> 
> The older, bt878 based ones do decoding in hardware (i2c chip).  You'll
> get the decoded rc5 keycode.
> 
> The newer, cx88 based ones just sample the raw IR signal and give you
> that.  The decoding has to be done in software.  The driver can program
> the sample rate and has to do the biphase decoding in software to get
> the rc5 keycodes.  The driver gets that done just fine, and the remote
> shipped with the TV card works without problems.
> 
> The part which doesn't work is supporting *any* remote.  The (newer)
> hardware gives you the raw IR signal, so it is possible to decode any IR
> signal in software.  The missing bit is some way to send the raw IR
> samples to userspace (i.e. the lircd daemon) for decoding.  lirc drivers
> do just that.
> 

I see. I guess what I would like to see it first to have "smart"
devices that do decoding in hardware to be converted to input subsystem
since lirc can use it and other programs can do that too. We should
probably start using KEY_NUMERIC_* there to help userspace
distinguishing RC keys from normal numeric/keypad keys.

For protocols that require decoding in userspace the best solution IMHO
is to route them back via uinput so that applications would not need to
keep implementing 2 interfaces, one for standard input devices and
another for lirc.

I am not sure if input devices are the best for transmittign raw IR
signal. It is pretty easy to add EV_MSC/IR_RAW event type to the input
core... It would give you a timestamp and a 32 bit value... I guess I
need to start looking at extending number of event devices in the
system.

-- 
Dmitry

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 01/18] lirc core device driver infrastructure
  2008-09-12  0:16     ` Janne Grunau
@ 2008-09-12  8:33       ` Christoph Hellwig
  2008-09-12 14:51         ` Jarod Wilson
  0 siblings, 1 reply; 111+ messages in thread
From: Christoph Hellwig @ 2008-09-12  8:33 UTC (permalink / raw)
  To: Janne Grunau
  Cc: Christoph Hellwig, Jarod Wilson, linux-kernel, Jarod Wilson,
	Christoph Bartelmus, Mario Limonciello

On Fri, Sep 12, 2008 at 02:16:30AM +0200, Janne Grunau wrote:
> I'm pretty sure LIRC will be recognized by its current users, it is 
> probably enough if it is in the help text though.

But we don't care just for your current users :)  Kernel features /
drivers do need good descriptions.

> > > +		} else {
> > > +			/* if device not opened so we can sleep half a second */
> > > +			set_current_state(TASK_INTERRUPTIBLE);
> > > +			schedule_timeout(HZ/2);
> > > +		}
> >
> > Yikes.  This should use some form of more fine-grained wakeus.
> 
> added a waitqueue and wait_event

I don't think you'll need a wait queue - you can just call
wake_up_process on ir->task while the thread just sleeps in schedule().

> > > +int lirc_unregister_plugin(int minor)
> >
> > Why doesn't this one take a struct lirc_plugin pointer?
> 
> I don't know, It doesn't really help though since the struct lirc_plugin 
> is copied by value to irctl.p in lirc_register_plugin.

Well, the lifetime rules for these structures seems a litte odd.  I
would except this to work like:

struct lirc_plugin statically allocated by the driver, then passed into
lirc_register_plugin, which dynamically allocates an irdev.  The irdev
would point to the plugin, not copy it.

And btw, any chance for an s/plugin/driver/g - that's the terminology
we use everywhere else in the kernel.


^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 01/18] lirc core device driver infrastructure
  2008-09-12  8:33       ` Christoph Hellwig
@ 2008-09-12 14:51         ` Jarod Wilson
  0 siblings, 0 replies; 111+ messages in thread
From: Jarod Wilson @ 2008-09-12 14:51 UTC (permalink / raw)
  To: Christoph Hellwig, Christoph Bartelmus
  Cc: Janne Grunau, linux-kernel, Mario Limonciello

On Friday 12 September 2008 04:33:32 Christoph Hellwig wrote:
> On Fri, Sep 12, 2008 at 02:16:30AM +0200, Janne Grunau wrote:
> > I'm pretty sure LIRC will be recognized by its current users, it is
> > probably enough if it is in the help text though.
>
> But we don't care just for your current users :)  Kernel features /
> drivers do need good descriptions.

I'll work on that, as well as some less terse help texts.

[...wait queue and lifetime bits snipped, Janne's handling 'em...]

> And btw, any chance for an s/plugin/driver/g - that's the terminology
> we use everywhere else in the kernel.

Done. (will push after doing a quick sanity build-test to make sure I didn't 
miss anything)

-- 
Jarod Wilson
jarod@redhat.com


^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 01/18] lirc core device driver infrastructure
  2008-09-09 13:20         ` Janne Grunau
@ 2008-09-12 16:46           ` Greg KH
  0 siblings, 0 replies; 111+ messages in thread
From: Greg KH @ 2008-09-12 16:46 UTC (permalink / raw)
  To: Janne Grunau
  Cc: Andi Kleen, Jarod Wilson, linux-kernel, Jarod Wilson,
	Christoph Bartelmus, Mario Limonciello

On Tue, Sep 09, 2008 at 03:20:35PM +0200, Janne Grunau wrote:
> On Tuesday 09 September 2008 15:03:11 Andi Kleen wrote:
> > > pr_printk is not found in include/ nor by google. please explain
> >
> > pr_debug() sorry.
> 
> pr_debug() does something else. that dprintk macro prints depending on a 
> module parameter

pr_debug() is what you want, 2.6.28 will turn it into a dynamic option
to turn on and off at a per-module level.

Actually you should be using dev_dbg() instead, that's much better and
will also work the same way on a per-module level.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 01/18] lirc core device driver infrastructure
  2008-09-11 21:28       ` Maxim Levitsky
@ 2008-09-13  7:20         ` Christoph Bartelmus
  0 siblings, 0 replies; 111+ messages in thread
From: Christoph Bartelmus @ 2008-09-13  7:20 UTC (permalink / raw)
  To: maximlevitsky
  Cc: dmitry.torokhov, j, jarod, jwilson, kraxel, linux-kernel, superm1

Hi Maxim,

on 12 Sep 08 at 00:28, Maxim Levitsky wrote:
[...]
> What do you, lirc developers think about making lircd a daemon that injects
> input events via uinput?

I have already agreed to implement this in lircd.
The reason why I didn't until now is that there never was any reguest for
such a feature from real users of LIRC. This is often suggested by people
from an architectural viewpoint, because it's nice to have a unified
interface through the input layer. But if you start using LIRC you usually
realise quite fast that simple key events are not enough to build a remote
controlled home entertainment system for example.
But if enough people want this feature, it will be in the next release.

Christoph

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 01/18] lirc core device driver infrastructure
  2008-09-11 19:09         ` Jarod Wilson
@ 2008-09-13  7:21           ` Christoph Bartelmus
  0 siblings, 0 replies; 111+ messages in thread
From: Christoph Bartelmus @ 2008-09-13  7:21 UTC (permalink / raw)
  To: jwilson; +Cc: j, linux-kernel, lkml, superm1

Hi Jarod,

on 11 Sep 08 at 15:09, Jarod Wilson wrote:
> On Thursday 11 September 2008 14:30:00 Christoph Bartelmus wrote:
>> on 09 Sep 08 at 13:03, Jarod Wilson wrote:
>>> On Tuesday 09 September 2008 03:40:18 Sebastian Siewior wrote:
>>>>>+EXTRA_CFLAGS =-DIRCTL_DEV_MAJOR=61 -DLIRC_SERIAL_TRANSMITTER -I$(src)
>>>>
>>>> Do you rely on this specific major? Since your daemon opens /dev/lirc0
>>>> you don't need a fixed major do you?
>>>
>>> Good question. Quite honestly, I'm not sure. Christoph?
>>
>> LIRC does not rely on a specific major. But to be honest, the last time I
>> looked into this issue, was when devfs was bleeding-edge...
>>
>> So, we need some advice here how to proceed. Should we try to register an
>> official major number for LIRC? Should we try to have a minor number
>> mapping, e.g.
>> /dev/lirc/serial/0 LIRC device on 1st UART serial port
>> ...
>> /dev/lirc/serial/n LIRC device on n-th UART serial port
>> /dev/lirc/parallel/0 LIRC device on 1st parallel port
>> ...
>> /dev/lirc/parallel/n LIRC device on n-th parallel port
>> /dev/lirc/usb/0 1st LIRC USB device
>> ...

> Janne took a crack at dynamic device allocation earlier today, committed
> into my git tree:

The problem is this:
http://www.lirc.org/html/configure.html#multiple

Quoting the very last paragraph:
"The only situation where the described procedure will not work is when  
you have two devices that both use a kernel driver that can only handle  
one device at once like e.g. lirc_serial, lirc_sir or lirc_parallel. You  
can still make it work with a trick by compiling the affected driver  
multiple times using different names and different major numbers. You will  
find detailed instructions how to achieve this by searching the mailing  
list. Lifting this limitation is one of the todo items for future  
releases."

The limitation that lirc_serial can only handle one device at a time still  
exists and I think we should have a concept how to solve it before the  
drivers are merged into the kernel.

Christoph

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 08/18] lirc driver for the Soundgraph IMON IR Receivers
  2008-09-10 21:02                 ` [PATCH 08/18] lirc driver for the Soundgraph IMON IR Receivers Jonathan Corbet
  2008-09-10 21:23                   ` Janne Grunau
@ 2008-09-22 21:47                   ` Jarod Wilson
  2008-09-24 20:21                     ` Jarod Wilson
  1 sibling, 1 reply; 111+ messages in thread
From: Jarod Wilson @ 2008-09-22 21:47 UTC (permalink / raw)
  To: Jonathan Corbet; +Cc: linux-kernel, Janne Grunau, Christoph Bartelmus

On Wednesday 10 September 2008 17:02:29 Jonathan Corbet wrote:
> > +#define SUCCESS		0
> > +#define	TRUE		1
> > +#define FALSE		0
>
> (See my grumble in previous reviews...:)
>
> > +#define LOCK_CONTEXT	mutex_lock(&context->lock)
> > +#define UNLOCK_CONTEXT	mutex_unlock(&context->lock)
>
> Here too.

All gone.

> > +/* to prevent races between open() and disconnect() */
> > +static DECLARE_MUTEX(disconnect_sem);
>
> This should be a real mutex, I think.

Done.

> > +/* lcd or vfd? */
> > +static int is_lcd;
>
> The driver can only do one or the other?  You can't have both in the
> system?  And somehow the user is supposed to configure it at load time to
> do the right thing?

Should be autodetected by default now, based on usb device ID, but can still 
be overridden by a module para 'display_type'. Not sure exactly what would 
happen if the user had more than one, but I don't know why they would in the 
first place...

> > +static inline int send_packet(struct imon_context *context)
[...]
> > +		/* fill request into kmalloc'ed space: */
> > +		control_req = kmalloc(sizeof(struct usb_ctrlrequest), GFP_NOIO);
>
> Why GFP_NOIO?

Original author probably thought it was necessary. Certainly doesn't look like 
it is to me, I'll make that just GFP_KERNEL.

> > +	} else {
> > +		/* Wait for tranmission to complete(or abort) */
> > +		UNLOCK_CONTEXT;
> > +		wait_for_completion(&context->tx.finished);
> > +		LOCK_CONTEXT;
>
> Should that be an interruptible (or killable) wait?

Don't see why it couldn't be. Done.

> > +static ssize_t show_associate_remote(struct device *d,
> > +				     struct device_attribute *attr,
> > +				     char *buf)
> > +{
> > +	struct imon_context *context = dev_get_drvdata(d);
> > +
> > +	if (!context)
> > +		return -ENODEV;
> > +
> > +	if (context->ir_isassociating) {
> > +		strcpy(buf, "The device it associating press some button "
> > +			    "on the remote.\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");
> > +	} else {
> > +		strcpy(buf, "Device is closed, you need to open it to "
> > +			    "associate the remote(you can use irw).\n");
> > +	}
> > +	return strlen(buf);
> > +}
>
> I *guess* that's one value per file, but it's still not quite the sysfs
> norm.  How about one-word status codes which can be made more verbose by
> user space?  (That's an API change, of course...)

Yuk. There really is no user space. But chances of someone stumbling upon 
these directions in sysfs are pretty slim. Reducing to single-word, and 
logging a url pointer to the lirc.org page on setting these up. Sound sane 
enough?

> > +static ssize_t store_associate_remote(struct device *d,
> > +				      struct device_attribute *attr,
> > +				      const char *buf, size_t count)
> > +{
> > +	struct imon_context *context;
> > +
> > +	context = dev_get_drvdata(d);
> > +
> > +	if (!context)
> > +		return -ENODEV;
> > +
> > +	if (!context->ir_isopen)
> > +		return -EINVAL;
> > +
> > +	if (context->ir_isopen) {
> > +		context->ir_isassociating = TRUE;
> > +		send_associate_24g(context);
> > +	}
> > +
> > +	return count;
> > +}
>
> No need to take the mutex here?

Added mutex here and in show_associate_remote() for good measure.

> > +/**
> > + * Called by lirc_dev when the application opens /dev/lirc
> > + */
> > +static int ir_open(void *data)
> > +{
> > +	int retval = SUCCESS;
> > +	struct imon_context *context;
> > +
> > +	/* prevent races with disconnect */
> > +	down(&disconnect_sem);
> > +
> > +	context = (struct imon_context *) data;
> > +
> > +	LOCK_CONTEXT;
> > +
> > +	if (context->ir_isopen) {
> > +		err("%s: IR port is already open", __func__);
> > +		retval = -EBUSY;
> > +		goto exit;
> > +	}
>
> I wonder if the single-open semantics are really doing what the author
> intended?  It is unsufficient to prevent concurrent calls elsewhere.

I think we're good to go with disconnect_sem being converted to an actual 
mutex, no?

> > +exit:
> > +	UNLOCK_CONTEXT;
> > +
> > +	up(&disconnect_sem);
> > +	return SUCCESS;
> > +}
>
> This discards "retval", which could hold an error status - the function
> returns "SUCCESS" even if ->ir_isopen is not set.

Gah. That's fugly. Fixed.

> > +static void imon_disconnect(struct usb_interface *interface)
[...]
> > +	/* Abort ongoing write */
> > +	if (atomic_read(&context->tx.busy)) {
> > +		usb_kill_urb(context->tx_urb);
> > +		wait_for_completion(&context->tx.finished);
> > +	}
>
> What if this races with another thread waiting for the completion?  It
> seems like it should be completed with complete_all(), but that wasn't the
> case.

Agreed. At this point, we're disconnecting, no point in waiting on anyone 
else.

I've attempted to remedy everything, compile-tested and briefly run-time 
tested the changes, and have pushed 'em all into my git tree. My shiny new 
iMon Knob receiver and remote are reasonably happy still (less some lockdep 
spew that I have to figure out what to do with still), but I have no vfd or 
lcd to test with, unfortunately.

-- 
Jarod Wilson
jarod@redhat.com


^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 08/18] lirc driver for the Soundgraph IMON IR Receivers
  2008-09-22 21:47                   ` Jarod Wilson
@ 2008-09-24 20:21                     ` Jarod Wilson
  0 siblings, 0 replies; 111+ messages in thread
From: Jarod Wilson @ 2008-09-24 20:21 UTC (permalink / raw)
  To: Jonathan Corbet; +Cc: linux-kernel, Janne Grunau, Christoph Bartelmus

On Monday 22 September 2008 17:47:56 Jarod Wilson wrote:
> > > +/**
> > > + * Called by lirc_dev when the application opens /dev/lirc
> > > + */
> > > +static int ir_open(void *data)
> > > +{
> > > +   int retval = SUCCESS;
> > > +   struct imon_context *context;
> > > +
> > > +   /* prevent races with disconnect */
> > > +   down(&disconnect_sem);
> > > +
> > > +   context = (struct imon_context *) data;
> > > +
> > > +   LOCK_CONTEXT;
> > > +
> > > +   if (context->ir_isopen) {
> > > +           err("%s: IR port is already open", __func__);
> > > +           retval = -EBUSY;
> > > +           goto exit;
> > > +   }
> >
> > I wonder if the single-open semantics are really doing what the author
> > intended?  It is unsufficient to prevent concurrent calls elsewhere.
>
> I think we're good to go with disconnect_sem being converted to an actual
> mutex, no?

No. The lock here is worthless, as is the check for context->ir_isopen, as 
this function *only* gets called with the driver_lock held in lirc_dev, and 
lirc_dev won't call this if the device is already open. I've simply dropped 
the context lock, as its not needed at all (and in fact, triggered lockdep 
spew).

-- 
Jarod Wilson
jarod@redhat.com


^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 07/18] lirc driver for the CommandIR USB Transceiver
  2008-09-10 17:09               ` [PATCH 07/18] lirc driver for the CommandIR USB Transceiver Jonathan Corbet
  2008-09-11 18:24                 ` Christoph Bartelmus
@ 2008-09-25 15:21                 ` Jarod Wilson
  1 sibling, 0 replies; 111+ messages in thread
From: Jarod Wilson @ 2008-09-25 15:21 UTC (permalink / raw)
  To: Jonathan Corbet
  Cc: linux-kernel, Janne Grunau, Christoph Bartelmus, Matthew Bodkin

Jonathan Corbet wrote:
> This driver has real locking problems.  Parts of it depend on the BKL, but
> I would have a hard time arguing for the addition of new BKL users in this
> century.  The assumption that ->open() is called under the BKL no longer
> holds.  It's really time to put a proper lock here.

The kernel-space drivers for CommandIR devices are being dropped entirely, in 
favor of the new userspace driver for it, which is included in the lirc 
0.8.4pre1 userspace.

-- 
Jarod Wilson
jarod@redhat.com


^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 02/18] lirc serial port receiver/transmitter device driver
  2008-09-20  0:10                   ` Janne Grunau
@ 2008-09-26 19:42                     ` Stefan Bauer
  0 siblings, 0 replies; 111+ messages in thread
From: Stefan Bauer @ 2008-09-26 19:42 UTC (permalink / raw)
  To: Janne Grunau; +Cc: Jarod Wilson, linux-kernel, Christoph Bartelmus

Hi,

On Saturday 20 September 2008 02:10, Janne Grunau wrote:
> but lirc serial still doesn't work after
> bc2039245c6390bd414a61c9395604c4155a58c6. I'll look at it tomorrow.

just to tell you that I'm still there... :) Todays git 
(a842c8095aad7b71ffcd1e426185e53661d758c7) is also giving me this at first 
access to the device by lircd:

------------[ cut here ]------------
WARNING: at drivers/input/lirc/lirc_dev.h:37
lirc_buffer_clear+0x2c/0x30 [lirc_dev]()
calling lirc_buffer_clear on an uninitialized lirc_buffer
Modules linked in: lirc_serial lirc_dev ves1820 arc4 ecb crypto_blkcipher 
cryptomgr crypto_algapi rt61pci rt2x00pci rt2x00lib led_class dvb_ttpci 
saa7146_vv saa7146 videobuf_dma_sg mac80211 videobuf_core videodev ehci_hcd 
uhci_hcd ttpci_eeprom cfg80211 eeprom_93cx6 [last unloaded: lirc_dev]
Pid: 5144, comm: lircd Not tainted 2.6.27-rc5 #7
 [<c01151ce>] warn_slowpath+0x61/0x84
 [<c015971f>] __d_lookup+0x8b/0xbf
 [<c0151f26>] do_lookup+0x40/0x92
 [<c0158f6d>] dput+0x15/0x8d
 [<c0153533>] __link_path_walk+0x7ad/0x8b4
 [<c0153533>] __link_path_walk+0x7ad/0x8b4
 [<c01de0c1>] kobject_get+0xf/0x13
 [<c014dfbb>] cdev_get+0x1b/0x2d
 [<c014dfd4>] exact_lock+0x7/0xd
 [<c0242154>] kobj_lookup+0xda/0x104
 [<ca9fa15a>] lirc_buffer_clear+0x2c/0x30 [lirc_dev]
 [<ca9fa4f4>] lirc_dev_fop_open+0x9e/0x135 [lirc_dev]
 [<c014e15c>] chrdev_open+0xc1/0xf6
 [<c014e09b>] chrdev_open+0x0/0xf6
 [<c014b052>] __dentry_open+0xf2/0x1da
 [<c014b156>] nameidata_to_filp+0x1c/0x2c
 [<c01544ae>] do_filp_open+0x28b/0x545
 [<c015bc37>] alloc_fd+0x46/0xad
 [<c014ae8d>] do_sys_open+0x42/0xb6
 [<c014af45>] sys_open+0x1e/0x23
 [<c010297d>] sysenter_do_call+0x12/0x25
 [<c02f0000>] ioctl_standard_iw_point+0x12d/0x200
 =======================
---[ end trace 6c8eb5cbb580deaf ]---

Regards,
Stefan

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 02/18] lirc serial port receiver/transmitter device driver
  2008-09-19 18:53                 ` Stefan Bauer
  2008-09-19 19:24                   ` Janne Grunau
@ 2008-09-20  0:10                   ` Janne Grunau
  2008-09-26 19:42                     ` Stefan Bauer
  1 sibling, 1 reply; 111+ messages in thread
From: Janne Grunau @ 2008-09-20  0:10 UTC (permalink / raw)
  To: Stefan Bauer; +Cc: Jarod Wilson, linux-kernel, Christoph Bartelmus

On Friday 19 September 2008 20:53:36 Stefan Bauer wrote:
> On Friday 19 September 2008 20:26, Janne Grunau wrote:
> >
> > not necessary, it is
> > commit bc2039245c6390bd414a61c9395604c4155a58c6
> > Author: Janne Grunau <j@jannau.net>
> > Date:   Thu Sep 18 20:49:19 2008 +0200
> >
> >     reimplement lirc_buffer as kfifo wrapper
>
> Thank you for pointing me, but
> bc2039245c6390bd414a61c9395604c4155a58c6~1 (lirc_streamzap can use
> lirc_buffer_clear) also crashes lircd when irexec is started.

yes, a different bug. both fixed in 
git://git.jannau.net/linux-2.6-lirc.git

but lirc serial still doesn't work after 
bc2039245c6390bd414a61c9395604c4155a58c6. I'll look at it tomorrow.

I have also good news: irrecord + lirc_serial is working with bc203924^. 
Not sure if something changed or if it was PEBCAK.

Janne

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 02/18] lirc serial port receiver/transmitter device driver
  2008-09-19 18:53                 ` Stefan Bauer
@ 2008-09-19 19:24                   ` Janne Grunau
  2008-09-20  0:10                   ` Janne Grunau
  1 sibling, 0 replies; 111+ messages in thread
From: Janne Grunau @ 2008-09-19 19:24 UTC (permalink / raw)
  To: Stefan Bauer; +Cc: Jarod Wilson, linux-kernel, Christoph Bartelmus

On Friday 19 September 2008 20:53:36 Stefan Bauer wrote:
> On Friday 19 September 2008 20:26, Janne Grunau wrote:
> > On Friday 19 September 2008 20:05:11 Stefan Bauer wrote:
> > > Thank you for keeping me up to date, here are my test results
> > > with todays (36e60bd457588b2a2fd37e0c1d0652a9fe214d85) git:
> > >
> > > - But lircd crashes immediately every time irexec/irw tries to
> > > connect:
> > >
> > > BUG: unable to handle kernel NULL pointer dereference at 0000000c
> >
> > not necessary, it is
> > commit bc2039245c6390bd414a61c9395604c4155a58c6
> > Author: Janne Grunau <j@jannau.net>
> > Date:   Thu Sep 18 20:49:19 2008 +0200
> >
> >     reimplement lirc_buffer as kfifo wrapper
>
> Thank you for pointing me, but
> bc2039245c6390bd414a61c9395604c4155a58c6~1 (lirc_streamzap can use
> lirc_buffer_clear) also crashes lircd when irexec is started.

ok, then it is not the obvious thing, I'll test in a couple of minutes

Janne

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 02/18] lirc serial port receiver/transmitter device driver
  2008-09-19 18:54                 ` Jarod Wilson
@ 2008-09-19 19:12                   ` Stefan Bauer
  0 siblings, 0 replies; 111+ messages in thread
From: Stefan Bauer @ 2008-09-19 19:12 UTC (permalink / raw)
  To: Jarod Wilson; +Cc: Janne Grunau, linux-kernel, Christoph Bartelmus

On Friday 19 September 2008 20:54, Jarod Wilson wrote:
> On Friday 19 September 2008 14:26:20 Janne Grunau wrote:
> > On Friday 19 September 2008 20:05:11 Stefan Bauer wrote:
> > > Thank you for keeping me up to date, here are my test results with
> > > todays (36e60bd457588b2a2fd37e0c1d0652a9fe214d85) git:
> > >
> > > - But lircd crashes immediately every time irexec/irw tries to
> > > connect:
> > >
> > > BUG: unable to handle kernel NULL pointer dereference at 0000000c
> >
> > not necessary, it is
> > commit bc2039245c6390bd414a61c9395604c4155a58c6
> > Author: Janne Grunau <j@jannau.net>
> > Date:   Thu Sep 18 20:49:19 2008 +0200
> >
> >     reimplement lirc_buffer as kfifo wrapper
>
> Hm... Two things... First, I neglected to pull some of Janne's later
> changes in the same area until just a bit ago, so perhaps this has already
> been remedied, because second, lirc_i2c is working as expected w/current
> head (98242cbdca...)

98242cbdca behaves exactly as 36e60bd4575.

-Stefan

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 02/18] lirc serial port receiver/transmitter device driver
  2008-09-19 18:26               ` Janne Grunau
  2008-09-19 18:53                 ` Stefan Bauer
@ 2008-09-19 18:54                 ` Jarod Wilson
  2008-09-19 19:12                   ` Stefan Bauer
  1 sibling, 1 reply; 111+ messages in thread
From: Jarod Wilson @ 2008-09-19 18:54 UTC (permalink / raw)
  To: Janne Grunau; +Cc: Stefan Bauer, linux-kernel, Christoph Bartelmus

On Friday 19 September 2008 14:26:20 Janne Grunau wrote:
> On Friday 19 September 2008 20:05:11 Stefan Bauer wrote:
> > Thank you for keeping me up to date, here are my test results with
> > todays (36e60bd457588b2a2fd37e0c1d0652a9fe214d85) git:
> >
> > - But lircd crashes immediately every time irexec/irw tries to
> > connect:
> >
> > BUG: unable to handle kernel NULL pointer dereference at 0000000c
>
> not necessary, it is
> commit bc2039245c6390bd414a61c9395604c4155a58c6
> Author: Janne Grunau <j@jannau.net>
> Date:   Thu Sep 18 20:49:19 2008 +0200
>
>     reimplement lirc_buffer as kfifo wrapper

Hm... Two things... First, I neglected to pull some of Janne's later changes 
in the same area until just a bit ago, so perhaps this has already been 
remedied, because second, lirc_i2c is working as expected w/current head 
(98242cbdca...)


-- 
Jarod Wilson
jarod@redhat.com


^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 02/18] lirc serial port receiver/transmitter device driver
  2008-09-19 18:26               ` Janne Grunau
@ 2008-09-19 18:53                 ` Stefan Bauer
  2008-09-19 19:24                   ` Janne Grunau
  2008-09-20  0:10                   ` Janne Grunau
  2008-09-19 18:54                 ` Jarod Wilson
  1 sibling, 2 replies; 111+ messages in thread
From: Stefan Bauer @ 2008-09-19 18:53 UTC (permalink / raw)
  To: Janne Grunau; +Cc: Jarod Wilson, linux-kernel, Christoph Bartelmus

On Friday 19 September 2008 20:26, Janne Grunau wrote:
> On Friday 19 September 2008 20:05:11 Stefan Bauer wrote:
> > Thank you for keeping me up to date, here are my test results with
> > todays (36e60bd457588b2a2fd37e0c1d0652a9fe214d85) git:
> >
> > - But lircd crashes immediately every time irexec/irw tries to
> > connect:
> >
> > BUG: unable to handle kernel NULL pointer dereference at 0000000c
>
> not necessary, it is
> commit bc2039245c6390bd414a61c9395604c4155a58c6
> Author: Janne Grunau <j@jannau.net>
> Date:   Thu Sep 18 20:49:19 2008 +0200
>
>     reimplement lirc_buffer as kfifo wrapper

Thank you for pointing me, but bc2039245c6390bd414a61c9395604c4155a58c6~1 
(lirc_streamzap can use lirc_buffer_clear) also crashes lircd when irexec is 
started.

BUG: unable to handle kernel NULL pointer dereference at 00000000
IP: [<c01118ce>] try_to_wake_up+0x16/0x7c
*pde = 00000000 
Oops: 0000 [#1] 
Modules linked in: lirc_serial lirc_dev ves1820 arc4 ecb crypto_blkcipher 
cryptomgr crypto_algapi rt61pci rt2x00pci rt2x00lib led_class dvb_ttpci 
saa7146_vv saa7146 mac80211 videobuf_dma_sg ehci_hcd uhci_hcd videobuf_core 
videodev v4l1_compat ttpci_eeprom cfg80211 eeprom_93cx6

Pid: 2939, comm: lircd Not tainted (2.6.27-rc5 #7)
EIP: 0060:[<c01118ce>] EFLAGS: 00010046 CPU: 0
EIP is at try_to_wake_up+0x16/0x7c
EAX: c04091c0 EBX: 0000000f ECX: 00000000 EDX: c3e85e98
ESI: c04091c0 EDI: 00000000 EBP: 00000000 ESP: c3e85e98
 DS: 007b ES: 007b FS: 0000 GS: 0033 SS: 0068
Process lircd (pid: 2939, ti=c3e84000 task=c3e866c0 task.ti=c3e84000)
Stack: 00000246 c6a16000 00000000 c691905c c6bed400 cadc05b3 00000000 c6a1607c 
       c014e15c 00000000 c6bed400 c691905c 00000000 c014e09b c014b052 c78c2a00 
       c774c180 c3e85f14 c6bed400 c3e85f14 00000003 c014b156 c6bed400 00000000 
Call Trace:
 [<cadc05b3>] irctl_open+0xdd/0x13c [lirc_dev]
 [<c014e15c>] chrdev_open+0xc1/0xf6
 [<c014e09b>] chrdev_open+0x0/0xf6
 [<c014b052>] __dentry_open+0xf2/0x1da
 [<c014b156>] nameidata_to_filp+0x1c/0x2c
 [<c01544ae>] do_filp_open+0x28b/0x545
 [<c015bc37>] alloc_fd+0x46/0xad
 [<c014ae8d>] do_sys_open+0x42/0xb6
 [<c014af45>] sys_open+0x1e/0x23
 [<c010297d>] sysenter_do_call+0x12/0x25
 [<c02f0000>] ioctl_standard_iw_point+0x12d/0x200
 =======================
Code: 00 00 00 00 5b 5e 5f c3 53 9c 5b fa e8 bb ff ff ff 53 9d 5b c3 55 57 89 
c7 56 53 89 d3 83 ec 04 89 e2 31 ed e8 3a fd ff ff 89 c6 <8b> 07 85 d8 74 41 
83 7f 48 00 75 25 31 c0 66 bd 01 00 e8 ba 40 
EIP: [<c01118ce>] try_to_wake_up+0x16/0x7c SS:ESP 0068:c3e85e98
---[ end trace 344ec27be5d90ccf ]---


Regards,
Stefan

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 02/18] lirc serial port receiver/transmitter device driver
  2008-09-19 18:05             ` Stefan Bauer
@ 2008-09-19 18:26               ` Janne Grunau
  2008-09-19 18:53                 ` Stefan Bauer
  2008-09-19 18:54                 ` Jarod Wilson
  0 siblings, 2 replies; 111+ messages in thread
From: Janne Grunau @ 2008-09-19 18:26 UTC (permalink / raw)
  To: Stefan Bauer; +Cc: Jarod Wilson, linux-kernel, Christoph Bartelmus

On Friday 19 September 2008 20:05:11 Stefan Bauer wrote:
>
> Thank you for keeping me up to date, here are my test results with
> todays (36e60bd457588b2a2fd37e0c1d0652a9fe214d85) git:

> - But lircd crashes immediately every time irexec/irw tries to
> connect:
>
> BUG: unable to handle kernel NULL pointer dereference at 0000000c

not necessary, it is
commit bc2039245c6390bd414a61c9395604c4155a58c6
Author: Janne Grunau <j@jannau.net>
Date:   Thu Sep 18 20:49:19 2008 +0200

    reimplement lirc_buffer as kfifo wrapper

Janne

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 02/18] lirc serial port receiver/transmitter device driver
  2008-09-16  4:08           ` Jarod Wilson
  2008-09-18 14:00             ` Jarod Wilson
@ 2008-09-19 18:05             ` Stefan Bauer
  2008-09-19 18:26               ` Janne Grunau
  1 sibling, 1 reply; 111+ messages in thread
From: Stefan Bauer @ 2008-09-19 18:05 UTC (permalink / raw)
  To: Jarod Wilson; +Cc: linux-kernel, Janne Grunau, Christoph Bartelmus

On Tuesday 16 September 2008 06:08, Jarod Wilson wrote:
> Current git tree works for receiving IR signals via lirc_serial (tested by
> Janne) and via lirc_mceusb2 (tested by me). Something is still slightly
> haywire with lirc_i2c though. It registers fine, but IR signals simply
> aren't making it to lircd. Probably unrelated to the dynamic device num
> changes, looking into it.

Thank you for keeping me up to date, here are my test results with todays 
(36e60bd457588b2a2fd37e0c1d0652a9fe214d85) git:

- Compiles fine, except some warnings:

drivers/input/lirc/lirc_dev.h:60:
  warning: 'lirc_buffer_available' defined but not used
drivers/input/lirc/lirc_dev.h:72:
  warning: 'lirc_buffer_write' defined but not used
drivers/input/lirc/lirc_dev.h:56:
  warning: 'lirc_buffer_empty' defined but not used
drivers/input/lirc/lirc_dev.h:60:
  warning: 'lirc_buffer_available' defined but not used
drivers/input/lirc/lirc_dev.h:66:
  warning: 'lirc_buffer_read' defined but not used

- Module loads fine, with this messages:

lirc_dev: IR Remote Control driver registered, major 252 
lirc_serial: auto-detected active low receiver
lirc_dev: lirc_register_driver: sample_rate: 0

- But lircd crashes immediately every time irexec/irw tries to connect:

BUG: unable to handle kernel NULL pointer dereference at 0000000c
IP: [<cabc9006>] :lirc_dev:lirc_buffer_clear+0x6/0x17
*pde = 00000000 
Oops: 0002 [#1] 
Modules linked in: lirc_serial lirc_dev ves1820 arc4 ecb crypto_blkcipher 
cryptomgr crypto_algapi rt61pci rt2x00pci rt2x00lib led_class dvb_ttpci 
saa7146_vv saa7146 videobuf_dma_sg videobuf_core mac80211 videodev 
v4l1_compat ttpci_eeprom ehci_hcd uhci_hcd cfg80211 eeprom_93cx6

Pid: 2501, comm: lircd Not tainted (2.6.27-rc5 #7)
EIP: 0060:[<cabc9006>] EFLAGS: 00010046 CPU: 0
EIP is at lirc_buffer_clear+0x6/0x17 [lirc_dev]
EAX: 00000000 EBX: c7825cc0 ECX: cabc94f1 EDX: 00000246
ESI: fffffff0 EDI: c726537c EBP: c6bdf000 ESP: c6b73eac
 DS: 007b ES: 007b FS: 0000 GS: 0033 SS: 0068
Process lircd (pid: 2501, ti=c6b72000 task=c72ef7a0 task.ti=c6b72000)
Stack: cabc958f 00000000 c7825d3c c014e15c 00000000 c6bdf000 c726537c 00000000 
       c014e09b c014b052 c78c2900 c759fd80 c6b73f14 c6bdf000 c6b73f14 00000003 
       c014b156 c6bdf000 00000000 00000000 00000000 c01544ae 00000002 00000026 
Call Trace:
 [<cabc958f>] irctl_open+0x9e/0x131 [lirc_dev]
 [<c014e15c>] chrdev_open+0xc1/0xf6
 [<c014e09b>] chrdev_open+0x0/0xf6
 [<c014b052>] __dentry_open+0xf2/0x1da
 [<c014b156>] nameidata_to_filp+0x1c/0x2c
 [<c01544ae>] do_filp_open+0x28b/0x545
 [<c015bc37>] alloc_fd+0x46/0xad
 [<c014ae8d>] do_sys_open+0x42/0xb6
 [<c014af45>] sys_open+0x1e/0x23
 [<c010297d>] sysenter_do_call+0x12/0x25
 [<c02f0000>] ioctl_standard_iw_point+0x12d/0x200
 =======================
Code: <c7> 40 0c 00 00 00 00 c7 40 08 00 00 00 00 52 9d c3 53 89 c3 8b 40 
EIP: [<cabc9006>] lirc_buffer_clear+0x6/0x17 [lirc_dev] SS:ESP 0068:c6b73eac
---[ end trace 782e5196b9791a10 ]---


If wanted I could start a bisect on this.


Regards,
Stefan

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 02/18] lirc serial port receiver/transmitter device driver
  2008-09-16  4:08           ` Jarod Wilson
@ 2008-09-18 14:00             ` Jarod Wilson
  2008-09-19 18:05             ` Stefan Bauer
  1 sibling, 0 replies; 111+ messages in thread
From: Jarod Wilson @ 2008-09-18 14:00 UTC (permalink / raw)
  To: linux-kernel
  Cc: Janne Grunau, Stefan Bauer, Christoph Bartelmus, Erik Hovland

On Tuesday 16 September 2008 00:08:43 Jarod Wilson wrote:
> Current git tree works for receiving IR signals via lirc_serial (tested by
> Janne) and via lirc_mceusb2 (tested by me). Something is still slightly
> haywire with lirc_i2c though. It registers fine, but IR signals simply
> aren't making it to lircd. Probably unrelated to the dynamic device num
> changes, looking into it.

Despite compiling fine w/o #include'ing linux/i2c-algo-bit.h and coverity 
apparently thinking its not needed, its really quite necessary for things to 
function. Restored it, and lirc_i2c now works again.

-- 
Jarod Wilson
jarod@redhat.com


^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 02/18] lirc serial port receiver/transmitter device driver
  2008-09-15 18:20         ` Jarod Wilson
@ 2008-09-16  4:08           ` Jarod Wilson
  2008-09-18 14:00             ` Jarod Wilson
  2008-09-19 18:05             ` Stefan Bauer
  0 siblings, 2 replies; 111+ messages in thread
From: Jarod Wilson @ 2008-09-16  4:08 UTC (permalink / raw)
  To: linux-kernel; +Cc: Janne Grunau, Stefan Bauer, Christoph Bartelmus

On Monday 15 September 2008 14:20:31 Jarod Wilson wrote:
> On Sunday 14 September 2008 23:55:38 Jarod Wilson wrote:
> > On Saturday 13 September 2008 04:41:33 Stefan Bauer wrote:
> > > On Saturday 13 September 2008 02:26, Janne Grunau wrote:
> > > > I hope I haven't broken anything with my lirc_dev changes. I doubt
> > > > I'll have a change to test it before monday.
> > >
> > > Unfortunately, you did. Commit ea74897 (port lirc to dynamic device
> > > numbers) broke things.
[...]
> So I've done a bit of work to fix up a few drivers so they properly check
> to see that lirc_register_driver() actually succeeded, and then
> accidentally stumbled onto the fix for the failure when merging some
> coverity-inspired patches from Erik Hovland. lirc_dev.c's lirc_cdev_add()
> function had an inverted kmalloc failure check. With that fixed, lirc_i2c
> appears to be behaving for me now, although I can't actually check to be
> 100% certain until I get home tonight.
[...]
> Fixes are in the git tree now for anyone else who wants to or is willing to
> test. Will verify myself tonight w/lirc_i2c and lirc_mceusb2 at a minimum.

Current git tree works for receiving IR signals via lirc_serial (tested by 
Janne) and via lirc_mceusb2 (tested by me). Something is still slightly 
haywire with lirc_i2c though. It registers fine, but IR signals simply aren't 
making it to lircd. Probably unrelated to the dynamic device num changes, 
looking into it.

-- 
Jarod Wilson
jarod@redhat.com


^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 02/18] lirc serial port receiver/transmitter device driver
  2008-09-15  3:55       ` Jarod Wilson
@ 2008-09-15 18:20         ` Jarod Wilson
  2008-09-16  4:08           ` Jarod Wilson
  0 siblings, 1 reply; 111+ messages in thread
From: Jarod Wilson @ 2008-09-15 18:20 UTC (permalink / raw)
  To: Stefan Bauer; +Cc: Janne Grunau, linux-kernel, Christoph Bartelmus

On Sunday 14 September 2008 23:55:38 Jarod Wilson wrote:
> On Saturday 13 September 2008 04:41:33 Stefan Bauer wrote:
> > On Saturday 13 September 2008 02:26, Janne Grunau wrote:
> > > On Friday 12 September 2008 18:24:33 Jarod Wilson wrote:
> > > > > I just want to thank you very much for your work and give you my
> > > > > Tested-By. Todays git (b2e9c18a32423310a309f94ea5a659c4bb264378)
> > > > > works well here with lirc-0.8.3 userspace on a Pentium
> > > > > 3/i815-system.
> > > >
> > > > Oh good! I haven't broken anything further w/my changes up to
> > > > b2e9c18... ;) I've got another slew of updates to lirc_serial still
> > > > pending though
> > >
> > > I hope I haven't broken anything with my lirc_dev changes. I doubt I'll
> > > have a change to test it before monday.
> >
> > Unfortunately, you did. Commit ea74897 (port lirc to dynamic device
> > numbers) broke things.
> > This is what ea74897 and further (latest tested was dd13cc7) are telling
> > me:
> >
> > $ insmod drivers/input/lirc/lirc_dev.ko
> > $ insmod drivers/input/lirc/lirc_serial.ko
> > insmod: error inserting 'drivers/input/lirc/lirc_serial.ko': -1
> > Input/output error
> > $ dmesg | tail
> > lirc_dev: IR Remote Control driver registered, major 253
> > lirc_serial: auto-detected active low receiver
> > lirc_dev: lirc_register_driver: sample_rate: 0
> > lirc_serial: register_chrdev failed!
> >
> >
> > There has also been a compile issue in the meanwhile, introduced with
> > 95efa30 (inb/outb and readb/writeb deal in u8 types), but this is gone
> > with todays git pull :)
> >
> >
> > I'll keep on testing (unfortunately only at weekends),
> > regards, Stefan
>
> I'd hope to get around to some testing myself much earlier in the weekend,
> but alas... Did just mix in a quick peed at lirc_i2c though:
>
> ...
> lirc_dev: IR Remote Control driver registered, major 247
> bttv: driver version 0.9.17 loaded
> bttv: using 8 buffers with 2080k (520 pages) each for capture
> cx88/0: cx2388x v4l2 driver version 0.0.6 loaded
> lirc_i2c: chip 0x10020 found @ 0x18 (Hauppauge IR)
> lirc_dev: lirc_register_driver: sample_rate: 10
>
> No register_chrdev failure reported, everything looks the same as prior to
> the dynamic dev num change (save the dev num, of course), and I've got a
> /dev/lirc0, but I'm unable to see any IR signals (start up lircd, run irw,
> press buttons on remote).
>
> ...and I just took a quick look at lirc_i2c... The result from
> lirc_register_driver() is never checked, whereas it is in the lirc_serial
> case (which is where the register_chrdev error msg came from). Narf. So its
> likely the same failure, just not noticed (will fix lirc_i2c in a sec).

So I've done a bit of work to fix up a few drivers so they properly check to 
see that lirc_register_driver() actually succeeded, and then accidentally 
stumbled onto the fix for the failure when merging some coverity-inspired 
patches from Erik Hovland. lirc_dev.c's lirc_cdev_add() function had an 
inverted kmalloc failure check. With that fixed, lirc_i2c appears to be 
behaving for me now, although I can't actually check to be 100% certain until 
I get home tonight.

...
lirc_dev: IR Remote Control driver registered, major 240 
lirc_i2c: probe 0x1a @ ivtv i2c driver #0: no
lirc_i2c: probe 0x18 @ ivtv i2c driver #0: yes
lirc_i2c: chip 0x10020 found @ 0x18 (Hauppauge IR)
lirc_dev: lirc_register_driver: sample_rate: 10
lirc_dev: driver lirc_i2c registered at minor number = 0
lirc_dev (lirc_i2c[0]): poll thread started
lirc_i2c: ir_attach: success (minor 0)
...

Fixes are in the git tree now for anyone else who wants to or is willing to 
test. Will verify myself tonight w/lirc_i2c and lirc_mceusb2 at a minimum.

-- 
Jarod Wilson
jarod@redhat.com


^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 02/18] lirc serial port receiver/transmitter device driver
  2008-09-13  8:41     ` Stefan Bauer
@ 2008-09-15  3:55       ` Jarod Wilson
  2008-09-15 18:20         ` Jarod Wilson
  0 siblings, 1 reply; 111+ messages in thread
From: Jarod Wilson @ 2008-09-15  3:55 UTC (permalink / raw)
  To: Stefan Bauer; +Cc: Janne Grunau, linux-kernel, Christoph Bartelmus

On Saturday 13 September 2008 04:41:33 Stefan Bauer wrote:
> On Saturday 13 September 2008 02:26, Janne Grunau wrote:
> > On Friday 12 September 2008 18:24:33 Jarod Wilson wrote:
> > > > I just want to thank you very much for your work and give you my
> > > > Tested-By. Todays git (b2e9c18a32423310a309f94ea5a659c4bb264378)
> > > > works well here with lirc-0.8.3 userspace on a Pentium
> > > > 3/i815-system.
> > >
> > > Oh good! I haven't broken anything further w/my changes up to
> > > b2e9c18... ;) I've got another slew of updates to lirc_serial still
> > > pending though
> >
> > I hope I haven't broken anything with my lirc_dev changes. I doubt I'll
> > have a change to test it before monday.
>
> Unfortunately, you did. Commit ea74897 (port lirc to dynamic device
> numbers) broke things.
> This is what ea74897 and further (latest tested was dd13cc7) are telling
> me:
>
> $ insmod drivers/input/lirc/lirc_dev.ko
> $ insmod drivers/input/lirc/lirc_serial.ko
> insmod: error inserting 'drivers/input/lirc/lirc_serial.ko': -1
> Input/output error
> $ dmesg | tail
> lirc_dev: IR Remote Control driver registered, major 253
> lirc_serial: auto-detected active low receiver
> lirc_dev: lirc_register_driver: sample_rate: 0
> lirc_serial: register_chrdev failed!
>
>
> There has also been a compile issue in the meanwhile, introduced with
> 95efa30 (inb/outb and readb/writeb deal in u8 types), but this is gone with
> todays git pull :)
>
>
> I'll keep on testing (unfortunately only at weekends),
> regards, Stefan

I'd hope to get around to some testing myself much earlier in the weekend, but 
alas... Did just mix in a quick peed at lirc_i2c though:

...
lirc_dev: IR Remote Control driver registered, major 247 
bttv: driver version 0.9.17 loaded
bttv: using 8 buffers with 2080k (520 pages) each for capture
cx88/0: cx2388x v4l2 driver version 0.0.6 loaded
lirc_i2c: chip 0x10020 found @ 0x18 (Hauppauge IR)
lirc_dev: lirc_register_driver: sample_rate: 10

No register_chrdev failure reported, everything looks the same as prior to the 
dynamic dev num change (save the dev num, of course), and I've got a 
/dev/lirc0, but I'm unable to see any IR signals (start up lircd, run irw, 
press buttons on remote).

...and I just took a quick look at lirc_i2c... The result from 
lirc_register_driver() is never checked, whereas it is in the lirc_serial case 
(which is where the register_chrdev error msg came from). Narf. So its likely 
the same failure, just not noticed (will fix lirc_i2c in a sec).

-- 
Jarod Wilson
jarod@redhat.com


^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 02/18] lirc serial port receiver/transmitter device driver
  2008-09-13  0:26   ` Janne Grunau
@ 2008-09-13  8:41     ` Stefan Bauer
  2008-09-15  3:55       ` Jarod Wilson
  0 siblings, 1 reply; 111+ messages in thread
From: Stefan Bauer @ 2008-09-13  8:41 UTC (permalink / raw)
  To: Janne Grunau; +Cc: Jarod Wilson, linux-kernel, Christoph Bartelmus

On Saturday 13 September 2008 02:26, Janne Grunau wrote:
> On Friday 12 September 2008 18:24:33 Jarod Wilson wrote:
> > > I just want to thank you very much for your work and give you my
> > > Tested-By. Todays git (b2e9c18a32423310a309f94ea5a659c4bb264378)
> > > works well here with lirc-0.8.3 userspace on a Pentium
> > > 3/i815-system.
> >
> > Oh good! I haven't broken anything further w/my changes up to
> > b2e9c18... ;) I've got another slew of updates to lirc_serial still
> > pending though
>
> I hope I haven't broken anything with my lirc_dev changes. I doubt I'll
> have a change to test it before monday.

Unfortunately, you did. Commit ea74897 (port lirc to dynamic device numbers) 
broke things.
This is what ea74897 and further (latest tested was dd13cc7) are telling me:

$ insmod drivers/input/lirc/lirc_dev.ko
$ insmod drivers/input/lirc/lirc_serial.ko
insmod: error inserting 'drivers/input/lirc/lirc_serial.ko': -1 Input/output 
error
$ dmesg | tail
lirc_dev: IR Remote Control driver registered, major 253 
lirc_serial: auto-detected active low receiver
lirc_dev: lirc_register_driver: sample_rate: 0
lirc_serial: register_chrdev failed!


There has also been a compile issue in the meanwhile, introduced with 95efa30 
(inb/outb and readb/writeb deal in u8 types), but this is gone with todays 
git pull :)


I'll keep on testing (unfortunately only at weekends),
regards, Stefan

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 02/18] lirc serial port receiver/transmitter device driver
  2008-09-12 16:24 ` Jarod Wilson
  2008-09-13  0:26   ` Janne Grunau
@ 2008-09-13  7:09   ` Christoph Bartelmus
  1 sibling, 0 replies; 111+ messages in thread
From: Christoph Bartelmus @ 2008-09-13  7:09 UTC (permalink / raw)
  To: jwilson; +Cc: j, linux-kernel, stefan.bauer

Hi Jarod,

on 12 Sep 08 at 12:24, Jarod Wilson wrote:
> On Thursday 11 September 2008 15:49:25 Stefan Bauer wrote:
>> Jarod Wilson wrote:
>>>> Nothing in this function does anything to assure itself that the port
>>>> actually exists and is the right kind of hardware.  Maybe that can't
>>>> really be done with this kind of device?
>>>
>>> We should probably try to make sure the port actually exists, but I don't
>>> think there's a whole lot (if anything) we can do as far as verifying the
>>> device itself.

> I've borrowed the simple existence test from
> drivers/serial/8250.c::autoconfig(), which tries a few reads and writes from
> UART_IER. I think this is probably sufficient for verifying the port is
> legit.
>
> Christoph B., you're definitely much more familiar with serial IR devices
> than I am... Is there any sort of test you can think of that we could use to
> try to verify the existence of an IR device hooked to the port? Or should we
> be happy we at least know there's a port and just assume the IR device is
> there?

No, I can't think of anything else. OTOH there have never been real  
problems with this for the past 10 years...

Christoph

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 02/18] lirc serial port receiver/transmitter device driver
  2008-09-12 16:24 ` Jarod Wilson
@ 2008-09-13  0:26   ` Janne Grunau
  2008-09-13  8:41     ` Stefan Bauer
  2008-09-13  7:09   ` Christoph Bartelmus
  1 sibling, 1 reply; 111+ messages in thread
From: Janne Grunau @ 2008-09-13  0:26 UTC (permalink / raw)
  To: Jarod Wilson; +Cc: Stefan Bauer, linux-kernel, Christoph Bartelmus

On Friday 12 September 2008 18:24:33 Jarod Wilson wrote:
> > I just want to thank you very much for your work and give you my
> > Tested-By. Todays git (b2e9c18a32423310a309f94ea5a659c4bb264378)
> > works well here with lirc-0.8.3 userspace on a Pentium
> > 3/i815-system.
>
> Oh good! I haven't broken anything further w/my changes up to
> b2e9c18... ;) I've got another slew of updates to lirc_serial still
> pending though

I hope I haven't broken anything with my lirc_dev changes. I doubt I'll 
have a change to test it before monday.

Janne

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 02/18] lirc serial port receiver/transmitter device driver
  2008-09-11 19:49 [PATCH 02/18] lirc serial port receiver/transmitter device driver Stefan Bauer
@ 2008-09-12 16:24 ` Jarod Wilson
  2008-09-13  0:26   ` Janne Grunau
  2008-09-13  7:09   ` Christoph Bartelmus
  0 siblings, 2 replies; 111+ messages in thread
From: Jarod Wilson @ 2008-09-12 16:24 UTC (permalink / raw)
  To: Stefan Bauer, Janne Grunau; +Cc: linux-kernel, Christoph Bartelmus

On Thursday 11 September 2008 15:49:25 Stefan Bauer wrote:
> Jarod Wilson wrote:
[...]
> >> > +static inline unsigned int sinp(int offset)
> >> > +{
> >> > +#if defined(LIRC_ALLOW_MMAPPED_IO)
> >> > +	if (iommap != 0) {
> >> > +		/* the register is memory-mapped */
> >> > +		offset <<= ioshift;
> >> > +		return readb(io + offset);
> >> > +	}
> >> > +#endif
> >> > +	return inb(io + offset);
> >> > +}
> >>
> >> This all looks like a reimplementation of ioport_map() and the
> >> associated ioread*() and iowrite*() functions...?
> >
> > Probably. Will see about using those instead.

>From what I've been able to ascertain, reducing the above to...

static u8 sinp(int offset)
{
        if (iommap != 0)
                /* the register is memory-mapped */
                offset <<= ioshift;

        return inb(io + offset);
}

...should be sane. inb() either calls ioport_map() and ioread8() as needed, or 
defines inb() as readb() (or __raw_readb()) on platforms where its 
appropriate. (and similar for lirc_serial.c::soutp()).

> >> Nothing in this function does anything to assure itself that the port
> >> actually exists and is the right kind of hardware.  Maybe that can't
> >> really be done with this kind of device?
> >
> > We should probably try to make sure the port actually exists, but I don't
> > think there's a whole lot (if anything) we can do as far as verifying the
> > device itself.

I've borrowed the simple existence test from 
drivers/serial/8250.c::autoconfig(), which tries a few reads and writes from 
UART_IER. I think this is probably sufficient for verifying the port is legit.

Christoph B., you're definitely much more familiar with serial IR devices than 
I am... Is there any sort of test you can think of that we could use to try to 
verify the existence of an IR device hooked to the port? Or should we be happy 
we at least know there's a port and just assume the IR device is there?


> I just want to thank you very much for your work and give you my Tested-By.
> Todays git (b2e9c18a32423310a309f94ea5a659c4bb264378) works well here with
> lirc-0.8.3 userspace on a Pentium 3/i815-system.

Oh good! I haven't broken anything further w/my changes up to b2e9c18... ;) 
I've got another slew of updates to lirc_serial still pending though -- a few 
that I'm about to push, and at least one more chunk I still need to figure out 
(the hrtimers/rdtscl bits).

Continued testing would be much appreciated, since I still have no serial IR 
hardware myself (couldn't find the thingy that shipped with my Technisat card, 
but I do now have hardware on the way).

> But I've had a section mismatch in the lirc code, don't know if this is
> serious.
>
> WARNING: drivers/input/lirc/lirc_serial.o(.init.text+0x11e): Section
> mismatch in reference from the function init_module() to the
> function .exit.text:lirc_serial_exit()
> The function __init init_module() references
> a function __exit lirc_serial_exit().
> This is often seen when error handling in the init function
> uses functionality in the exit path.
> The fix is often to remove the __exit annotation of
> lirc_serial_exit() so it may be used outside an exit section.

Ah, I believe you're absolutely correct. Done.

-- 
Jarod Wilson
jarod@redhat.com


^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH 02/18] lirc serial port receiver/transmitter device driver
@ 2008-09-11 19:49 Stefan Bauer
  2008-09-12 16:24 ` Jarod Wilson
  0 siblings, 1 reply; 111+ messages in thread
From: Stefan Bauer @ 2008-09-11 19:49 UTC (permalink / raw)
  To: Jarod Wilson
  Cc: linux-kernel, Jarod Wilson, Janne Grunau, Christoph Bartelmus

Jarod Wilson wrote:

> On Tuesday 09 September 2008 12:14:22 Jonathan Corbet wrote:
>> > +#ifdef LIRC_SERIAL_IRDEO
>> > +static int type = LIRC_IRDEO;
>> > +#elif defined(LIRC_SERIAL_IRDEO_REMOTE)
>> > +static int type = LIRC_IRDEO_REMOTE;
>> > +#elif defined(LIRC_SERIAL_ANIMAX)
>> > +static int type = LIRC_ANIMAX;
>> > +#elif defined(LIRC_SERIAL_IGOR)
>> > +static int type = LIRC_IGOR;
>> > +#elif defined(LIRC_SERIAL_NSLU2)
>> > +static int type = LIRC_NSLU2;
>> > +#else
>> > +static int type = LIRC_HOMEBREW;
>> > +#endif
>>
>> So where do all these LIRC_SERIAL_* macros come from?  I can't really
>> tell what this bunch of ifdeffery is doing or how one might influence it.
> 
> Bleah. I believe these get passed in when building lirc userspace and
> drivers together, when manually selected the specific type of serial
> receiver you have in lirc's menu-driven configuration tool thingy...
> 
> In other words, they do us absolutely no good here. We just build for the
> default type (LIRC_HOMEBREW), and users can override the type as necessary
> via the 'type' module parameter. I'll nuke that chunk.
> 
>> > +
>> > +static struct lirc_serial hardware[] = {
>> > +	/* home-brew receiver/transmitter */
>> > +	{
>> > +		UART_MSR_DCD,
>> > +		UART_MSR_DDCD,
>> > +		UART_MCR_RTS|UART_MCR_OUT2|UART_MCR_DTR,
>> > +		UART_MCR_RTS|UART_MCR_OUT2,
>> > +		send_pulse_homebrew,
>> > +		send_space_homebrew,
>> > +		(
>> > +#ifdef LIRC_SERIAL_TRANSMITTER
>> > +		 LIRC_CAN_SET_SEND_DUTY_CYCLE|
>> > +		 LIRC_CAN_SET_SEND_CARRIER|
>> > +		 LIRC_CAN_SEND_PULSE|
>> > +#endif
>> > +		 LIRC_CAN_REC_MODE2)
>> > +	},
>>
>> It would be really nice to use the .field=value structure initialization
>> conventions here.
> 
> Indeed. Done.
> 
>> > +#if defined(__i386__)
>> > +/*
>> > +  From:
>> > +  Linux I/O port programming mini-HOWTO
>> > +  Author: Riku Saikkonen <Riku.Saikkonen@hut.fi>
>> > +  v, 28 December 1997
>> > +
>> > +  [...]
>> > +  Actually, a port I/O instruction on most ports in the 0-0x3ff range
>> > +  takes almost exactly 1 microsecond, so if you're, for example,using
>> > +  the parallel port directly, just do additional inb()s from that port
>> > +  to delay.
>> > +  [...]
>> > +*/
>> > +/* transmitter latency 1.5625us 0x1.90 - this figure arrived at from
>> > + * comment above plus trimming to match actual measured frequency.
>> > + * This will be sensitive to cpu speed, though hopefully most of the
>> > 1.5us + * is spent in the uart access.  Still - for reference test
>> > machine was a + * 1.13GHz Athlon system - Steve
>> > + */
>> > +
>> > +/* changed from 400 to 450 as this works better on slower machines;
>> > +   faster machines will use the rdtsc code anyway */
>> > +
>> > +#define LIRC_SERIAL_TRANSMITTER_LATENCY 450
>> > +
>> > +#else
>> > +
>> > +/* does anybody have information on other platforms ? */
>> > +/* 256 = 1<<8 */
>> > +#define LIRC_SERIAL_TRANSMITTER_LATENCY 256
>> > +
>> > +#endif  /* __i386__ */
>>
>> This is a little scary.  Maybe hrtimers would be a better way of handling
>> your timing issues?
> 
> Sounds plausible, will look into it. Of course, this partially hinges on
> the USE_RDTSC bits, more comments just a little ways down...
> 
>> > +static inline unsigned int sinp(int offset)
>> > +{
>> > +#if defined(LIRC_ALLOW_MMAPPED_IO)
>> > +	if (iommap != 0) {
>> > +		/* the register is memory-mapped */
>> > +		offset <<= ioshift;
>> > +		return readb(io + offset);
>> > +	}
>> > +#endif
>> > +	return inb(io + offset);
>> > +}
>>
>> This all looks like a reimplementation of ioport_map() and the associated
>> ioread*() and iowrite*() functions...?
> 
> Probably. Will see about using those instead.
> 
>> > +#ifdef USE_RDTSC
>> > +/* Version that uses Pentium rdtsc instruction to measure clocks */
>> > +
>> > +/* This version does sub-microsecond timing using rdtsc instruction,
>> > + * and does away with the fudged LIRC_SERIAL_TRANSMITTER_LATENCY
>> > + * Implicitly i586 architecture...  - Steve
>> > + */
>> > +
>> > +static inline long send_pulse_homebrew_softcarrier(unsigned long
>> > length) +{
>> > +	int flag;
>> > +	unsigned long target, start, now;
>> > +
>> > +	/* Get going quick as we can */
>> > +	rdtscl(start); on();
>> > +	/* Convert length from microseconds to clocks */
>> > +	length *= conv_us_to_clocks;
>> > +	/* And loop till time is up - flipping at right intervals */
>> > +	now = start;
>> > +	target = pulse_width;
>> > +	flag = 1;
>> > +	while ((now-start) < length) {
>> > +		/* Delay till flip time */
>> > +		do {
>> > +			rdtscl(now);
>> > +		} while ((now-start) < target);
>>
>> This looks like a hard busy wait, without even an occasional, polite,
>> cpu_relax() call.  There's got to be a better way?
>>
>> The i2c code has the result of a lot of bit-banging work, I wonder if
>> there's something there which could be helpful here.
> 
> Hrm... So at some point in the past, there was an "#if defined(rdtscl)" in
> the lirc_serial.c code that triggered USE_RDTSC being defined. At the
> moment, there's nothing defining it (I probably overzealously nuked it
> during clean- up), so we're not even touching the above code. However,
> this is supposed to be the "better" code path wrt producing a reliable
> waveform, at least on platforms with rdtscl... Will have to do some
> investigating... This actually affects whether or not we bother with
> hrtimers as suggested above too, as LIRC_SERIAL_TRANSMITTER_LATENCY is not
> used at all in the USE_RDTSC case.
> 
>> > +static irqreturn_t irq_handler(int i, void *blah)
>> > +{
>> > +	struct timeval tv;
>> > +	int status, counter, dcd;
>> > +	long deltv;
>> > +	int data;
>> > +	static int last_dcd = -1;
>> > +
>> > +	if ((sinp(UART_IIR) & UART_IIR_NO_INT)) {
>> > +		/* not our interrupt */
>> > +		return IRQ_RETVAL(IRQ_NONE);
>> > +	}
>>
>> That should just be IRQ_NONE, no?
> 
> Yeah, looks like it. Done.
> 
>> > +static void hardware_init_port(void)
>> > +{
>> > +	unsigned long flags;
>> > +	local_irq_save(flags);
>>
>> That won't help you if an interrupt is handled by another processor. 
>> This needs proper locking, methinks.
> 
> Yeah, working on implementing locking right now.
> 
>> Nothing in this function does anything to assure itself that the port
>> actually exists and is the right kind of hardware.  Maybe that can't
>> really be done with this kind of device?
> 
> We should probably try to make sure the port actually exists, but I don't
> think there's a whole lot (if anything) we can do as far as verifying the
> device itself.
> 
>> > +static int init_port(void)
>> > +{
>>
>>  ...
>>
>> > +	if (sense == -1) {
>> > +		/* wait 1/2 sec for the power supply */
>> > +
>> > +		set_current_state(TASK_INTERRUPTIBLE);
>> > +		schedule_timeout(HZ/2);
>>
>> msleep(), maybe?
> 
> Yeah, looks like it.
> 
>> > +static int set_use_inc(void *data)
>> > +{
>> > +	int result;
>> > +	unsigned long flags;
>> > +
>> > +	/* Init read buffer. */
>> > +	if (lirc_buffer_init(&rbuf, sizeof(int), RBUF_LEN) < 0)
>> > +		return -ENOMEM;
>> > +
>> > +	/* initialize timestamp */
>> > +	do_gettimeofday(&lasttv);
>> > +
>> > +	result = request_irq(irq, irq_handler,
>> > +			   IRQF_DISABLED | (share_irq ? IRQF_SHARED : 0),
>> > +			   LIRC_DRIVER_NAME, (void *)&hardware);
>> > +
>> > +	switch (result) {
>> > +	case -EBUSY:
>> > +		printk(KERN_ERR LIRC_DRIVER_NAME ": IRQ %d busy\n", irq);
>> > +		lirc_buffer_free(&rbuf);
>> > +		return -EBUSY;
>> > +	case -EINVAL:
>> > +		printk(KERN_ERR LIRC_DRIVER_NAME
>> > +		       ": Bad irq number or handler\n");
>> > +		lirc_buffer_free(&rbuf);
>> > +		return -EINVAL;
>> > +	default:
>> > +		dprintk("Interrupt %d, port %04x obtained\n", irq,
>> > io);
>> > +		break;
>> > +	};
>> > +
>> > +	local_irq_save(flags);
>> > +
>> > +	/* Set DLAB 0. */
>> > +	soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
>> > +
>> > +	soutp(UART_IER, sinp(UART_IER)|UART_IER_MSI);
>> > +
>> > +	local_irq_restore(flags);
>> > +
>> > +	return 0;
>> > +}
>>
>> OK, so set_use_inc() really is just an open() function.  It still seems
>> like a strange duplication.
> 
> Going to let the duplication be for the moment, since I don't know the
> history behind why there's duplication, and there are enough other
> mountains to climb first... :)
> 
>> Again, local_irq_save() seems insufficient here.  You need a lock to
>> serialize access to the hardware.
> 
> Will do.

I just want to thank you very much for your work and give you my Tested-By. 
Todays git (b2e9c18a32423310a309f94ea5a659c4bb264378) works well here with 
lirc-0.8.3 userspace on a Pentium 3/i815-system.

But I've had a section mismatch in the lirc code, don't know if this is 
serious.

WARNING: drivers/input/lirc/lirc_serial.o(.init.text+0x11e): Section mismatch 
in reference from the function init_module() to the 
function .exit.text:lirc_serial_exit()
The function __init init_module() references
a function __exit lirc_serial_exit().
This is often seen when error handling in the init function
uses functionality in the exit path.
The fix is often to remove the __exit annotation of
lirc_serial_exit() so it may be used outside an exit section.


Regards,
Stefan

^ permalink raw reply	[flat|nested] 111+ messages in thread

end of thread, other threads:[~2008-09-26 19:42 UTC | newest]

Thread overview: 111+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2008-09-09  4:05 [PATCH 0/18] linux infrared remote control drivers Jarod Wilson
2008-09-09  4:05 ` [PATCH 01/18] lirc core device driver infrastructure Jarod Wilson
2008-09-09  4:05   ` [PATCH 02/18] lirc serial port receiver/transmitter device driver Jarod Wilson
2008-09-09  4:05     ` [PATCH 03/18] lirc driver for 1st-gen Media Center Ed. USB IR transceivers Jarod Wilson
2008-09-09  4:05       ` [PATCH 04/18] lirc driver for 2nd-gen and later " Jarod Wilson
2008-09-09  4:05         ` [PATCH 05/18] lirc driver for i2c-based IR receivers Jarod Wilson
2008-09-09  4:05           ` [PATCH 06/18] lirc driver for the ATI USB RF remote receiver Jarod Wilson
2008-09-09  4:05             ` [PATCH 07/18] lirc driver for the CommandIR USB Transceiver Jarod Wilson
2008-09-09  4:05               ` [PATCH 08/18] lirc driver for the Soundgraph IMON IR Receivers Jarod Wilson
2008-09-09  4:05                 ` [PATCH 09/18] lirc driver for the Streamzap PC Receiver Jarod Wilson
2008-09-09  4:05                   ` [PATCH 10/18] lirc driver for Igor Cesko's USB IR receiver Jarod Wilson
2008-09-09  4:05                     ` [PATCH 11/18] lirc driver for the Technotrend " Jarod Wilson
2008-09-09  4:05                       ` [PATCH 12/18] lirc driver for the Sasem OnAir and Dign HV5 receivers Jarod Wilson
2008-09-09  4:05                         ` [PATCH 13/18] lirc driver for ITE8709 CIR port receiver Jarod Wilson
2008-09-09  4:05                           ` [PATCH 14/18] lirc driver for the ITE IT87xx CIR Port receivers Jarod Wilson
2008-09-09  4:06                             ` [PATCH 15/18] lirc driver for the SIR IrDA port Jarod Wilson
2008-09-09  4:06                               ` [PATCH 16/18] lirc driver for the IR interface on BT829-based hardware Jarod Wilson
2008-09-09  4:06                                 ` [PATCH 17/18] lirc driver for homebrew parallel port receivers Jarod Wilson
2008-09-09  4:06                                   ` [PATCH 18/18] lirc driver for the zilog/haupauge IR transceiver Jarod Wilson
2008-09-09  4:06                                     ` Jarod Wilson
2008-09-11 15:22                   ` [PATCH 09/18] lirc driver for the Streamzap PC Receiver Jonathan Corbet
2008-09-10 21:02                 ` [PATCH 08/18] lirc driver for the Soundgraph IMON IR Receivers Jonathan Corbet
2008-09-10 21:23                   ` Janne Grunau
2008-09-11  3:22                     ` Jarod Wilson
2008-09-22 21:47                   ` Jarod Wilson
2008-09-24 20:21                     ` Jarod Wilson
2008-09-10 17:09               ` [PATCH 07/18] lirc driver for the CommandIR USB Transceiver Jonathan Corbet
2008-09-11 18:24                 ` Christoph Bartelmus
     [not found]                   ` <1221159005.13683.34.camel@minimatt>
2008-09-11 19:03                     ` Jarod Wilson
2008-09-11 19:14                     ` Janne Grunau
2008-09-25 15:21                 ` Jarod Wilson
2008-09-10  9:58             ` [PATCH 06/18] lirc driver for the ATI USB RF remote receiver Ville Syrjälä
2008-09-10 13:05               ` Jarod Wilson
2008-09-10 13:14                 ` Christoph Hellwig
2008-09-10 13:37                   ` Jon Smirl
2008-09-10 14:30                     ` Dmitry Torokhov
2008-09-10 13:44                   ` Janne Grunau
2008-09-10 14:13                     ` Jarod Wilson
2008-09-10 14:19                     ` Christoph Hellwig
2008-09-10 14:08                 ` Ville Syrjälä
2008-09-10 14:37                   ` Dmitry Torokhov
2008-09-09  4:13           ` [PATCH 05/18] lirc driver for i2c-based IR receivers Jarod Wilson
2008-09-10 15:42           ` Jonathan Corbet
2008-09-09 23:30         ` [PATCH 04/18] lirc driver for 2nd-gen and later Media Center Ed. USB IR transceivers Jonathan Corbet
2008-09-10  0:36           ` Janne Grunau
2008-09-11  9:21           ` Adrian Bunk
2008-09-09 19:21       ` [PATCH 03/18] lirc driver for 1st-gen " Jonathan Corbet
2008-09-09 23:59         ` Janne Grunau
2008-09-10  1:39           ` Jarod Wilson
2008-09-10  0:04         ` Janne Grunau
2008-09-09 16:14     ` [PATCH 02/18] lirc serial port receiver/transmitter device driver Jonathan Corbet
2008-09-09 19:51       ` Stefan Lippers-Hollmann
2008-09-09 19:56         ` Jarod Wilson
2008-09-10 17:40       ` Jarod Wilson
2008-09-09  7:40   ` [PATCH 01/18] lirc core device driver infrastructure Sebastian Siewior
2008-09-09  9:53     ` Janne Grunau
2008-09-09 12:33       ` Sebastian Siewior
2008-09-09 13:10         ` Janne Grunau
2008-09-11 16:41       ` Christoph Bartelmus
2008-09-09 11:13     ` Alan Cox
2008-09-09 13:27     ` Stefan Richter
2008-09-09 17:03     ` Jarod Wilson
2008-09-11 18:30       ` Christoph Bartelmus
2008-09-11 19:09         ` Jarod Wilson
2008-09-13  7:21           ` Christoph Bartelmus
2008-09-09  9:46   ` Andi Kleen
2008-09-09 11:35     ` Janne Grunau
2008-09-09 13:03       ` Andi Kleen
2008-09-09 13:20         ` Janne Grunau
2008-09-12 16:46           ` Greg KH
2008-09-09 13:01   ` Christoph Hellwig
2008-09-10 12:24     ` Janne Grunau
2008-09-10 12:29       ` Christoph Hellwig
2008-09-10 12:45         ` Janne Grunau
2008-09-11 18:03       ` Christoph Bartelmus
2008-09-11 19:18         ` Janne Grunau
2008-09-12  0:16     ` Janne Grunau
2008-09-12  8:33       ` Christoph Hellwig
2008-09-12 14:51         ` Jarod Wilson
2008-09-09 15:33   ` Jonathan Corbet
2008-09-12  0:12     ` Janne Grunau
2008-09-10 13:08   ` Dmitry Torokhov
2008-09-11  8:47     ` Gerd Hoffmann
2008-09-11 21:28       ` Maxim Levitsky
2008-09-13  7:20         ` Christoph Bartelmus
2008-09-12  4:44       ` Dmitry Torokhov
2008-09-09  4:36 ` [PATCH 0/18] linux infrared remote control drivers Chris Wedgwood
2008-09-09  7:06 ` Alexey Dobriyan
2008-09-09  8:32   ` Janne Grunau
2008-09-09 12:46 ` Christoph Hellwig
2008-09-09 15:23   ` Jarod Wilson
2008-09-09 18:27     ` Lennart Sorensen
2008-09-09 18:34       ` Jarod Wilson
2008-09-09 15:34   ` Jon Smirl
2008-09-11 19:49 [PATCH 02/18] lirc serial port receiver/transmitter device driver Stefan Bauer
2008-09-12 16:24 ` Jarod Wilson
2008-09-13  0:26   ` Janne Grunau
2008-09-13  8:41     ` Stefan Bauer
2008-09-15  3:55       ` Jarod Wilson
2008-09-15 18:20         ` Jarod Wilson
2008-09-16  4:08           ` Jarod Wilson
2008-09-18 14:00             ` Jarod Wilson
2008-09-19 18:05             ` Stefan Bauer
2008-09-19 18:26               ` Janne Grunau
2008-09-19 18:53                 ` Stefan Bauer
2008-09-19 19:24                   ` Janne Grunau
2008-09-20  0:10                   ` Janne Grunau
2008-09-26 19:42                     ` Stefan Bauer
2008-09-19 18:54                 ` Jarod Wilson
2008-09-19 19:12                   ` Stefan Bauer
2008-09-13  7:09   ` Christoph Bartelmus

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).