diff -urN --exclude='*.orig' --exclude='*.rej' linux-2.6.11-mm4/drivers/char/lirc/lirc_atiusb.c linux-2.6.11-no1/drivers/char/lirc/lirc_atiusb.c
--- linux-2.6.11-mm4/drivers/char/lirc/lirc_atiusb.c	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.11-no1/drivers/char/lirc/lirc_atiusb.c	2005-03-30 00:12:39.516813241 -0500
@@ -0,0 +1,1267 @@
+/* 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
+ *
+ * $Id: lirc_atiusb.c,v 1.49 2005/03/12 11:32:14 lirc Exp $
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/version.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 0)
+#error "*******************************************************"
+#error "Sorry, this driver needs kernel version 2.4.0 or higher"
+#error "*******************************************************"
+#endif
+
+#include <linux/config.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 <asm/uaccess.h>
+#include <linux/usb.h>
+#include <linux/poll.h>
+#include <linux/wait.h>
+#include <linux/list.h>
+
+#include "linux/lirc.h"
+#include "linux/kcompat.h"
+#include "lirc_dev.h"
+
+#define DRIVER_VERSION		"0.4"
+#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		5
+#define CODE_LENGTH_ATI2	3
+#define CODE_MIN_LENGTH		3
+
+#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 = 0;
+#endif
+#define dprintk(fmt, args...)                                 \
+	do{                                                   \
+		if(debug) printk(KERN_DEBUG fmt, ## args);    \
+	}while(0)
+
+static int mask = 0xFFFF;	// channel acceptance bit mask
+static int unique = 0;		// enable channel-specific codes
+static int repeat = 10;		// repeat time in 1/100 sec
+static int emit_updown = 0;	// send seperate press/release codes (rw2)
+static int emit_modekeys = 0;	// send keycodes for aux1-aux4, pc, and mouse (rw2)
+static unsigned long repeat_jiffies; // repeat timeout
+static int mdeadzone = 0;	// 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			down_interruptible(&ir->lock)
+#define IRUNLOCK		up(&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
+
+static struct usb_device_id usb_remote_table [] = {
+	{ USB_DEVICE(VENDOR_ATI1, 0x0002) },	/* X10 USB Firecracker Interface */
+	{ USB_DEVICE(VENDOR_ATI1, 0x0003) },	/* X10 VGA Video Sender */
+	{ USB_DEVICE(VENDOR_ATI1, 0x0004) },	/* ATI Wireless Remote Receiver */
+	{ USB_DEVICE(VENDOR_ATI1, 0x0005) },	/* NVIDIA Wireless Remote Receiver */
+	{ USB_DEVICE(VENDOR_ATI1, 0x0006) },	/* ATI Wireless Remote Receiver */
+	{ USB_DEVICE(VENDOR_ATI1, 0x0007) },	/* X10 USB Wireless Transceiver */
+	{ USB_DEVICE(VENDOR_ATI1, 0x0008) },	/* X10 USB Wireless Transceiver */
+	{ USB_DEVICE(VENDOR_ATI1, 0x0009) },	/* X10 USB Wireless Transceiver */
+	{ USB_DEVICE(VENDOR_ATI1, 0x000A) },	/* X10 USB Wireless Transceiver */
+	{ USB_DEVICE(VENDOR_ATI1, 0x000B) },	/* X10 USB Transceiver */
+	{ USB_DEVICE(VENDOR_ATI1, 0x000C) },	/* X10 USB Transceiver */
+	{ USB_DEVICE(VENDOR_ATI1, 0x000D) },	/* X10 USB Transceiver */
+	{ USB_DEVICE(VENDOR_ATI1, 0x000E) },	/* X10 USB Transceiver */
+	{ USB_DEVICE(VENDOR_ATI1, 0x000F) },	/* X10 USB Transceiver */
+
+	{ USB_DEVICE(VENDOR_ATI2, 0x0602) },	/* ATI Remote Wonder 2: Input Device */
+	{ USB_DEVICE(VENDOR_ATI2, 0x0603) },	/* ATI Remote Wonder 2: Controller (???) */
+
+	{ }					/* 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;
+#ifdef KERNEL_2_5
+	dma_addr_t dma;
+#endif
+
+	/* handle repeats */
+	unsigned char old[CODE_LENGTH];
+	unsigned long old_jiffies;
+};
+
+struct out_endpt {
+	struct irctl *ir;
+	struct urb *urb;
+	struct usb_endpoint_descriptor *ep;
+
+	/* buffers and dma */
+	unsigned char *buf;
+#ifdef KERNEL_2_5
+	dma_addr_t dma;
+#endif
+
+	/* 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
+	} remote_type;
+
+	/* rw2 current mode (mirror's remote's state) */
+	int mode;
+
+	/* lirc */
+	struct lirc_plugin *p;
+	int connected;
+
+	/* locking */
+	struct semaphore 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);
+
+#ifdef KERNEL_2_5
+	if (usb_submit_urb(oep->urb, SLAB_ATOMIC)) {
+#else
+	if (usb_submit_urb(oep->urb)) {
+#endif
+		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);
+#ifdef KERNEL_2_5
+	oep->urb->transfer_flags |= URB_ASYNC_UNLINK;
+#endif
+	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;
+
+	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);
+
+	MOD_INC_USE_COUNT;
+
+	if (!ir->connected) {
+		if (!ir->usbdev)
+			return -ENOENT;
+
+		IRLOCK;
+		/* 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;
+#ifdef KERNEL_2_5
+			if (usb_submit_urb(iep->urb, SLAB_ATOMIC)) {
+#else
+			if (usb_submit_urb(iep->urb)) {
+#endif
+				printk(DRIVER_NAME "[%d]: open result = -EIO error "
+					"submitting urb\n", ir->devnum);
+				IRUNLOCK;
+				MOD_DEC_USE_COUNT;
+				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);
+
+	if (ir->connected) {
+		IRLOCK;
+		/* Free inbound usb urbs */
+		list_for_each_safe(pos, n, &ir->iep_listhead) {
+			iep = get_iep_from_link(pos);
+#ifdef KERNEL_2_5
+			iep->urb->transfer_flags |= URB_ASYNC_UNLINK;
+#endif
+			usb_unlink_urb(iep->urb);
+		}
+		ir->connected = 0;
+		IRUNLOCK;
+	}
+	MOD_DEC_USE_COUNT;
+}
+
+static void print_data(struct in_endpt *iep, char *buf, int len)
+{
+	char codes[CODE_LENGTH*3 + 1];
+	int i;
+
+	if (len <= 0)
+		return;
+
+	for (i = 0; i < len && i < CODE_LENGTH; 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) {
+		/* check for repeats */
+		if (memcmp(iep->old, iep->buf, len) == 0) {
+			if (iep->old_jiffies + repeat_jiffies > jiffies) {
+				return -1;
+			}
+		} else {
+			for (i = len; i < CODE_LENGTH; i++) iep->buf[i] = 0;
+			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_ATI2) {
+		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;
+}
+
+
+#ifdef KERNEL_2_5
+static void usb_remote_recv(struct urb *urb, struct pt_regs *regs)
+#else
+static void usb_remote_recv(struct urb *urb)
+#endif
+{
+	struct in_endpt *iep;
+	int len, result;
+
+	if (!urb)
+		return;
+	if (!(iep = urb->context)) {
+#ifdef KERNEL_2_5
+		urb->transfer_flags |= URB_ASYNC_UNLINK;
+#endif
+		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 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:
+#ifdef KERNEL_2_5
+		urb->transfer_flags |= URB_ASYNC_UNLINK;
+#endif
+		usb_unlink_urb(urb);
+		return;
+
+	case -EPIPE:
+	default:
+		break;
+	}
+
+	/* resubmit urb */
+#ifdef KERNEL_2_5
+	usb_submit_urb(urb, SLAB_ATOMIC);
+#endif
+}
+
+#ifdef KERNEL_2_5
+static void usb_remote_send(struct urb *urb, struct pt_regs *regs)
+#else
+static void usb_remote_send(struct urb *urb)
+#endif
+{
+	struct out_endpt *oep;
+
+	if (!urb)
+		return;
+	if (!(oep = urb->context)) {
+#ifdef KERNEL_2_5
+		urb->transfer_flags |= URB_ASYNC_UNLINK;
+#endif
+		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) {
+#ifdef KERNEL_2_5
+			iep->urb->transfer_flags |= URB_ASYNC_UNLINK;
+#endif
+			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:
+#ifdef KERNEL_2_5
+		usb_buffer_free(iep->ir->usbdev, iep->len, iep->buf, iep->dma);
+#else
+		kfree(iep->buf);
+#endif
+		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;
+	if ( !(iep = kmalloc(sizeof(*iep), GFP_KERNEL)) ) {
+		mem_failure = 1;
+	} else {
+		memset(iep, 0, sizeof(*iep));
+		iep->ir = ir;
+		iep->ep = ep;
+		iep->len = len;
+
+#ifdef KERNEL_2_5
+		if ( !(iep->buf = usb_buffer_alloc(dev, len, SLAB_ATOMIC, &iep->dma)) ) {
+			mem_failure = 2;
+		} else if ( !(iep->urb = usb_alloc_urb(0, GFP_KERNEL)) ) {
+			mem_failure = 3;
+		}
+#else
+		if ( !(iep->buf = kmalloc(len, GFP_KERNEL)) ) {
+			mem_failure = 2;
+		} else if ( !(iep->urb = usb_alloc_urb(0)) ) {
+			mem_failure = 3;
+		}
+#endif
+	}
+	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) {
+#ifdef KERNEL_2_5
+			oep->urb->transfer_flags |= URB_ASYNC_UNLINK;
+#endif
+			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:
+#ifdef KERNEL_2_5
+		usb_buffer_free(oep->ir->usbdev, USB_OUTLEN, oep->buf, oep->dma);
+#else
+		kfree(oep->buf);
+#endif
+		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;
+	if ( !(oep = kmalloc(sizeof(*oep), GFP_KERNEL)) ) {
+		mem_failure = 1;
+	} else {
+		memset(oep, 0, sizeof(*oep));
+		oep->ir = ir;
+		oep->ep = ep;
+		init_waitqueue_head(&oep->wait);
+
+#ifdef KERNEL_2_5
+		if ( !(oep->buf = usb_buffer_alloc(dev, USB_OUTLEN, SLAB_ATOMIC, &oep->dma)) ) {
+			mem_failure = 2;
+		} else if ( !(oep->urb = usb_alloc_urb(0, GFP_KERNEL)) ) {
+			mem_failure = 3;
+		}
+#else
+		if ( !(oep->buf = kmalloc(USB_OUTLEN, GFP_KERNEL)) ) {
+			mem_failure = 2;
+		} else if ( !(oep->urb = usb_alloc_urb(0)) ) {
+			mem_failure = 3;
+		}
+#endif
+	}
+	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;
+	struct lirc_buffer *rbuf;
+	int type, devnum;
+	int mem_failure;
+
+	devnum = dev->devnum;
+
+	/* determine remote type */
+	switch (dev->descriptor.idVendor) {
+	case VENDOR_ATI1:
+		type = ATI1_COMPATIBLE;
+		break;
+	case VENDOR_ATI2:
+		type = ATI2_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;
+	if ( !(ir = kmalloc(sizeof(*ir), GFP_KERNEL)) ) {
+		mem_failure = 1;
+	} else {
+		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;
+
+		if (!(plugin = kmalloc(sizeof(*plugin), GFP_KERNEL))) {
+			mem_failure = 2;
+		} else if (!(rbuf = kmalloc(sizeof(*rbuf), GFP_KERNEL))) {
+			mem_failure = 3;
+		} else if (lirc_buffer_init(rbuf, CODE_LENGTH, 1)) {
+			mem_failure = 4;
+		} else {
+			memset(plugin, 0, sizeof(*plugin));
+			strcpy(plugin->name, DRIVER_NAME " ");
+			plugin->minor = -1;
+			plugin->code_length = CODE_LENGTH*8;
+			plugin->features = LIRC_CAN_REC_LIRCCODE;
+			plugin->data = ir;
+			plugin->rbuf = rbuf;
+			plugin->set_use_inc = &set_use_inc;
+			plugin->set_use_dec = &set_use_dec;
+			plugin->owner = THIS_MODULE;
+			ir->usbdev = dev;
+			ir->p = plugin;
+			ir->remote_type = type;
+			ir->devnum = devnum;
+			ir->mode = RW2_NULL_MODE;
+
+			init_MUTEX(&ir->lock);
+			INIT_LIST_HEAD(&ir->iep_listhead);
+		}
+	}
+	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);
+}
+
+
+#ifdef KERNEL_2_5
+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;
+#else
+static void *usb_remote_probe(struct usb_device *dev, unsigned int ifnum,
+				const struct usb_device_id *id)
+{
+	struct usb_interface *intf = &dev->actconfig->interface[ifnum];
+	struct usb_interface_descriptor *idesc;
+#endif
+	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);
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,4)
+	idesc = intf->cur_altsetting;
+#else
+	idesc = &intf->altsetting[intf->act_altsetting];
+#endif
+
+	/* Check if a usb remote has already been registered for this device */
+	ir = get_prior_reg_ir(dev);
+
+	if ( !ir && !(ir = new_irctl(dev)) ) {
+#ifdef KERNEL_2_5
+		return -ENOMEM;
+#else
+		return NULL;
+#endif
+	}
+	type = ir->remote_type;
+
+	// step through the endpoints to find first in and first out endpoint
+	// of type interrupt transfer
+#ifdef KERNEL_2_5
+	for (i = 0; i < idesc->desc.bNumEndpoints; ++i) {
+		ep = &idesc->endpoint[i].desc;
+#else
+	for (i = 0; i < idesc->bNumEndpoints; ++i) {
+		ep = &idesc->endpoint[i];
+#endif
+		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)) {
+
+			if ((iep = new_in_endpt(ir,ep))) {
+				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);
+#ifdef KERNEL_2_5
+		return -ENODEV;
+#else
+		return NULL;
+#endif
+	}
+	if (ir->dev_refcount == 1) {
+		if ((ir->p->minor = lirc_register_plugin(ir->p)) < 0) {
+			free_irctl(ir, FREE_ALL);
+#ifdef KERNEL_2_5
+			return -ENODEV;
+#else
+			return NULL;
+#endif
+		}
+
+		/* Note new driver registration in kernel logs */
+		log_usb_dev_info(dev);
+
+		/* outbound data (initialization) */
+		send_outbound_init(ir);
+	}
+
+#ifdef KERNEL_2_5
+	usb_set_intfdata(intf, ir);
+	return SUCCESS;
+#else
+	return ir;
+#endif
+}
+
+#ifdef KERNEL_2_5
+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);
+#else
+static void usb_remote_disconnect(struct usb_device *dev, void *ptr)
+{
+	struct irctl *ir = ptr;
+#endif
+
+	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 = {
+	.owner =	THIS_MODULE,
+	.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("\n" DRIVER_NAME ": " DRIVER_DESC " v" DRIVER_VERSION "\n");
+	printk(DRIVER_NAME ": " DRIVER_AUTHOR "\n");
+	dprintk(DRIVER_NAME ": debug mode enabled: $Id: lirc_atiusb.c,v 1.49 2005/03/12 11:32:14 lirc Exp $\n");
+
+	request_module("lirc_dev");
+
+	repeat_jiffies = repeat*HZ/100;
+
+	if ((i = usb_register(&usb_remote_driver)) < 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 (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)");
+
+EXPORT_NO_SYMBOLS;
diff -urN --exclude='*.orig' --exclude='*.rej' linux-2.6.11-mm4/drivers/char/lirc/lirc_bt829.c linux-2.6.11-no1/drivers/char/lirc/lirc_bt829.c
--- linux-2.6.11-mm4/drivers/char/lirc/lirc_bt829.c	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.11-no1/drivers/char/lirc/lirc_bt829.c	2005-03-30 00:12:39.517813102 -0500
@@ -0,0 +1,395 @@
+/*
+ * 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>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 0)
+#error "This driver needs kernel version 2.4.0 or higher"
+#endif
+
+#include <linux/config.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 "linux/kcompat.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 = 0;
+#define dprintk(fmt, args...)                                 \
+	do{                                                   \
+		if(debug) printk(KERN_DEBUG fmt, ## args);    \
+	}while(0)
+
+static int atir_minor;
+static unsigned long pci_addr_phys, pci_addr_lin;
+
+static struct lirc_plugin atir_plugin;
+
+static int do_pci_probe(void)
+{
+	struct pci_dev *my_dev;
+#ifndef KERNEL_2_5
+	/* unnecessary with recent kernels */
+	if ( !pci_present() ) {
+		printk(KERN_ERR "ATIR: no pci in this kernel\n");
+	}
+#endif
+	my_dev = (struct pci_dev *)pci_find_device(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_264VT,NULL);
+	if ( my_dev ) {
+		printk(KERN_ERR "ATIR: Using device: %s\n",
+		       pci_pretty_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 0;
+		}
+	} else {
+		printk(KERN_ERR "ATIR: pci_prob failed\n");
+		return 0;
+	}
+	return 1;
+}
+
+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 )
+	{
+	//    printk(KERN_INFO "ATIR reading key %02X\n",*key);
+		lirc_buffer_write_1( buf, &key );
+		return 0;
+	}
+	return -ENODATA;
+}
+
+static int atir_set_use_inc(void* data)
+{
+	MOD_INC_USE_COUNT;
+	dprintk("ATIR driver is opened\n");
+	return 0;
+}
+
+static void atir_set_use_dec(void* data)
+{
+	MOD_DEC_USE_COUNT;
+	dprintk("ATIR driver is closed\n");
+}
+
+int init_module(void)
+{
+	if ( !do_pci_probe() ) {
+		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.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 = (unsigned long)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);
+
+		if ( (bits_2 = do_get_bits()) & 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 int addr, 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 int 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");
+
+EXPORT_NO_SYMBOLS;
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff -urN --exclude='*.orig' --exclude='*.rej' linux-2.6.11-mm4/drivers/char/lirc/lirc_dev.c linux-2.6.11-no1/drivers/char/lirc/lirc_dev.c
--- linux-2.6.11-mm4/drivers/char/lirc/lirc_dev.c	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.11-no1/drivers/char/lirc/lirc_dev.c	2005-03-30 00:11:50.271640351 -0500
@@ -0,0 +1,911 @@
+/*
+ * 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
+ *
+ * $Id: lirc_dev.c,v 1.41 2005/03/12 11:15:34 lirc Exp $
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+ 
+#include <linux/version.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 2, 18)
+#error "**********************************************************"
+#error " Sorry, this driver needs kernel version 2.2.18 or higher "
+#error "**********************************************************"
+#endif
+
+#include <linux/config.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 <asm/uaccess.h>
+#include <asm/semaphore.h>
+#include <asm/errno.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+#include <linux/wrapper.h>
+#endif
+#define __KERNEL_SYSCALLS__
+#include <linux/unistd.h>
+/* DevFS header */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+#include <linux/devfs_fs_kernel.h>
+#endif
+/* SysFS header */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+#include <linux/device.h>
+#endif
+
+#include "linux/kcompat.h"
+#include "linux/lirc.h"
+#include "lirc_dev.h"
+
+static int debug = 0;
+#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 semaphore buffer_sem;
+	struct lirc_buffer *buf;
+	unsigned int chunk_size;
+
+	int tpid;
+	struct semaphore *t_notify;
+	struct semaphore *t_notify2;
+	int shutdown;
+	long jiffies_to_wait;
+
+#ifdef LIRC_HAVE_DEVFS_24
+	devfs_handle_t devfs_handle;
+#endif
+};
+
+DECLARE_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_simple *lirc_class;
+
+/*  helper function
+ *  initializes the irctl structure
+ */
+static inline void init_irctl(struct irctl *ir)
+{
+	memset(&ir->p, 0, sizeof(struct lirc_plugin));
+	sema_init(&ir->buffer_sem, 1);
+	ir->p.minor = NOPLUG;
+
+	ir->tpid = -1;
+	ir->t_notify = NULL;
+	ir->t_notify2 = NULL;
+	ir->shutdown = 0;
+	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);
+
+#ifdef LIRC_HAVE_DEVFS_24
+	devfs_unregister(ir->devfs_handle);
+#endif
+#ifdef LIRC_HAVE_DEVFS_26
+	devfs_remove(DEV_LIRC "/%u", ir->p.minor);
+#endif
+	class_simple_device_remove(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 )
+		{
+			ir->shutdown = 1;
+		}
+		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
+	 */
+	daemonize("lirc_dev");
+	
+	if (ir->t_notify != NULL) {
+		up(ir->t_notify);
+	}
+	
+	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 (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) {
+		down(ir->t_notify2);
+	}
+
+	ir->tpid = -1;
+	if (ir->t_notify != NULL) {
+		up(ir->t_notify);
+	}
+	
+	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;
+#ifdef LIRC_HAVE_DEVFS_24
+	char name[16];
+#endif
+	DECLARE_MUTEX_LOCKED(tn);
+
+	if (!p) {
+		printk("lirc_dev: lirc_register_plugin: "
+		       "plugin pointer must be not NULL!\n");
+		err = -EBADRQC;
+		goto out;
+	}
+
+	if (MAX_IRCTL_DEVICES <= p->minor) {
+		printk("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("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("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("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("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("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("lirc_dev: lirc_register_plugin: "
+			       "neither read, poll nor ioctl can be NULL!\n");
+			err = -EBADRQC;
+			goto out;
+		}
+	}
+
+	down(&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("lirc_dev: lirc_register_plugin: "
+			       "no free slots for plugins!\n");
+			err = -ENOMEM;
+			goto out_lock;
+		}
+	} else if (irctls[minor].p.minor != NOPLUG) {
+		printk("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 externeal 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;
+		}
+	}
+	ir->chunk_size = ir->buf->chunk_size;
+
+	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;
+
+#if defined(LIRC_HAVE_DEVFS_24)
+	sprintf (name, DEV_LIRC "/%d", ir->p.minor);
+	ir->devfs_handle = devfs_register(NULL, name, DEVFS_FL_DEFAULT,
+					  IRCTL_DEV_MAJOR, ir->p.minor,
+					  S_IFCHR | S_IRUSR | S_IWUSR,
+					  &fops, NULL);
+#elif defined(LIRC_HAVE_DEVFS_26)
+	devfs_mk_cdev(MKDEV(IRCTL_DEV_MAJOR, ir->p.minor),
+			S_IFCHR|S_IRUSR|S_IWUSR,
+			DEV_LIRC "/%u", ir->p.minor);
+#endif
+	(void) class_simple_device_add(lirc_class, 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->t_notify = &tn;
+		ir->tpid = kernel_thread(lirc_thread, (void*)ir, 0);
+		if (ir->tpid < 0) {
+			printk("lirc_dev: lirc_register_plugin: "
+			       "cannot run poll thread for minor = %d\n",
+			       p->minor);
+			err = -ECHILD;
+			goto out_sysfs;
+		}
+		down(&tn);
+		ir->t_notify = NULL;
+	}
+	ir->attached = 1;
+	up(&plugin_lock);
+
+/*
+ * Recent kernels should handle this autmatically by increasing/decreasing
+ * use count when a dependant module is loaded/unloaded.
+ */
+#ifndef KERNEL_2_5
+	MOD_INC_USE_COUNT;
+#endif
+	dprintk("lirc_dev: plugin %s registered at minor number = %d\n",
+		ir->p.name, ir->p.minor);
+	p->minor = minor;
+	return minor;
+	
+out_sysfs:
+	class_simple_device_remove(MKDEV(IRCTL_DEV_MAJOR, ir->p.minor));
+#ifdef LIRC_HAVE_DEVFS_24
+	devfs_unregister(ir->devfs_handle);
+#endif
+#ifdef LIRC_HAVE_DEVFS_26
+	devfs_remove(DEV_LIRC "/%i", ir->p.minor);
+#endif
+out_lock:
+	up(&plugin_lock);
+out:
+	return err;
+}
+
+/*
+ *
+ */
+int lirc_unregister_plugin(int minor)
+{
+	struct irctl *ir;
+	DECLARE_MUTEX_LOCKED(tn);
+	DECLARE_MUTEX_LOCKED(tn2);
+
+	if (minor < 0 || minor >= MAX_IRCTL_DEVICES) {
+		printk("lirc_dev: lirc_unregister_plugin: "
+		       "\"minor\" must be between 0 and %d!\n",
+		       MAX_IRCTL_DEVICES-1);
+		return -EBADRQC;
+	}
+
+	ir = &irctls[minor];
+
+	down(&plugin_lock);
+
+	if (ir->p.minor != minor) {
+		printk("lirc_dev: lirc_unregister_plugin: "
+		       "minor (%d) device not registered!", minor);
+		up(&plugin_lock);
+		return -ENOENT;
+	}
+
+	/* end up polling thread */
+	if (ir->tpid >= 0) {
+		ir->t_notify = &tn;
+		ir->t_notify2 = &tn2;
+		ir->shutdown = 1;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+		{
+			struct task_struct *p;
+			
+			p = find_task_by_pid(ir->tpid);
+			wake_up_process(p);
+		}
+#else
+		/* 2.2.x does not export wake_up_process() */
+		wake_up_interruptible(ir->p.get_queue(ir->p.data));
+#endif
+		up(&tn2);
+		down(&tn);
+		ir->t_notify = NULL;
+		ir->t_notify2 = NULL;
+	}
+
+	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);
+		down(&ir->buffer_sem);
+		ir->p.set_use_dec(ir->p.data);
+		module_put(ir->p.owner);
+		up(&ir->buffer_sem);
+	}
+	else
+	{
+		cleanup(ir);
+	}
+	up(&plugin_lock);
+
+/*
+ * Recent kernels should handle this autmatically by increasing/decreasing
+ * use count when a dependant module is loaded/unloaded.
+ */
+#ifndef KERNEL_2_5
+	MOD_DEC_USE_COUNT;
+#endif
+
+	return SUCCESS;
+}
+
+/*
+ *
+ */
+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);
+
+	down_interruptible(&plugin_lock);
+
+	if (ir->p.minor == NOPLUG) {
+		up(&plugin_lock);
+		dprintk(LOGHEAD "open result = -ENODEV\n",
+			ir->p.name, ir->p.minor);
+		return -ENODEV;
+	}
+
+	if (ir->open) {
+		up(&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);
+		
+		up(&plugin_lock);
+		
+		if (retval != SUCCESS) {
+			module_put(ir->p.owner);
+			--ir->open;
+			return retval;
+		}
+	}
+	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, SUCCESS);
+
+	return SUCCESS;
+}
+
+/*
+ *
+ */
+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);
+
+	down_interruptible(&plugin_lock);
+
+	--ir->open;
+	if(ir->attached)
+	{
+		ir->p.set_use_dec(ir->p.data);
+		module_put(ir->p.owner);
+	}
+	else
+	{
+		cleanup(ir);
+	}
+
+	up(&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);
+
+	down(&ir->buffer_sem);
+	if(!ir->attached)
+	{
+		up(&ir->buffer_sem);
+		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);
+	
+	up(&ir->buffer_sem);
+	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 changin
+		 * eighter */
+		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->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(down_interruptible(&ir->buffer_sem))
+	{
+		return -ERESTARTSYS;
+	}
+	if(!ir->attached)
+	{
+		up(&ir->buffer_sem);
+		return -ENODEV;
+	}
+
+	if (length % ir->buf->chunk_size) {
+		dprintk(LOGHEAD "read result = -EINVAL\n",
+			ir->p.name, ir->p.minor);
+		up(&ir->buffer_sem);
+		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);
+	up(&ir->buffer_sem);
+	
+	dprintk(LOGHEAD "read result = %s (%d)\n",
+		ir->p.name, ir->p.minor, ret ? "-EFAULT" : "OK", ret);
+
+	return ret ? ret : written;
+}
+
+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 "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->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
+};
+
+
+
+EXPORT_SYMBOL(lirc_register_plugin);
+EXPORT_SYMBOL(lirc_unregister_plugin);
+
+/*
+ *
+ */
+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_simple_create(THIS_MODULE, "lirc");
+	if(IS_ERR(lirc_class)) {
+		printk(KERN_ERR "lirc_dev: class_simple_create failed\n");
+		goto out_unregister;
+	}
+
+	printk("lirc_dev: IR Remote Control driver registered, at major %d \n", 
+	       IRCTL_DEV_MAJOR);
+
+	return SUCCESS;
+
+out_unregister:
+	if(unregister_chrdev(IRCTL_DEV_MAJOR, IRCTL_DEV_NAME))
+		printk(KERN_ERR "lirc_dev: unregister_chrdev failed!\n");
+out:
+	return -1;
+}
+
+/* ---------------------------------------------------------------------- */
+
+/* For now dont try to use it as a static version !  */
+
+#ifdef MODULE
+
+/*
+ *
+ */
+int init_module(void)
+{
+	return lirc_dev_init();
+}
+
+/*
+ *
+ */
+void cleanup_module(void)
+{
+	int ret;
+
+	ret = unregister_chrdev(IRCTL_DEV_MAJOR, IRCTL_DEV_NAME);
+	class_simple_destroy(lirc_class);
+
+	if(ret)
+		printk("lirc_dev: error in module_unregister_chrdev: %d\n", ret);
+	else
+		dprintk("lirc_dev: module successfully 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");
+
+#endif /* MODULE */
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff -urN --exclude='*.orig' --exclude='*.rej' linux-2.6.11-mm4/drivers/char/lirc/lirc_dev.h linux-2.6.11-no1/drivers/char/lirc/lirc_dev.h
--- linux-2.6.11-mm4/drivers/char/lirc/lirc_dev.h	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.11-no1/drivers/char/lirc/lirc_dev.h	2005-03-30 00:11:50.274639935 -0500
@@ -0,0 +1,243 @@
+/*
+ * LIRC base driver
+ * 
+ * (L) by Artur Lipowski <alipowski@interia.pl>
+ *        This code is licensed under GNU GPL
+ *
+ * $Id: lirc_dev.h,v 1.16 2005/02/19 15:30:20 lirc Exp $
+ *
+ */
+
+#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 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);
+	buf->head = buf->tail = buf->fill = 0;
+	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 = buf->tail = 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_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 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);
+
+
+#endif
diff -urN --exclude='*.orig' --exclude='*.rej' linux-2.6.11-mm4/drivers/char/lirc/lirc_gpio.c linux-2.6.11-no1/drivers/char/lirc/lirc_gpio.c
--- linux-2.6.11-mm4/drivers/char/lirc/lirc_gpio.c	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.11-no1/drivers/char/lirc/lirc_gpio.c	2005-03-30 00:12:39.519812825 -0500
@@ -0,0 +1,571 @@
+/*
+ * Remote control driver for the TV-card
+ * key codes are obtained from GPIO port
+ * 
+ * (L) by Artur Lipowski <alipowski@interia.pl>
+ *     patch for the AverMedia by Santiago Garcia Mantinan <manty@i.am>
+ *                            and Christoph Bartelmus <lirc@bartelmus.de>
+ *     patch for the BestBuy by Miguel Angel Alvarez <maacruz@navegalia.com>
+ *     patch for the Winfast TV2000 by Juan Toledo
+ *     <toledo@users.sourceforge.net>
+ *     patch for the I-O Data GV-BCTV5/PCI by Jens C. Rasmussen
+ *     <jens.rasmussen@ieee.org>
+ *
+ *  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
+ *
+ * $Id: lirc_gpio.c,v 1.42 2005/03/12 11:32:15 lirc Exp $
+ *
+ */
+
+#include <linux/version.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 2, 4)
+#error "*******************************************************"
+#error "Sorry, this driver needs kernel version 2.2.4 or higher"
+#error "*******************************************************"
+#endif
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/kmod.h>
+#include <linux/sched.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+#include <linux/wrapper.h>
+#endif
+#include <linux/errno.h>
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
+#include "../drivers/char/bttv.h"
+#include "../drivers/char/bttvp.h"
+#else
+#include "../drivers/media/video/bttv.h"
+#include "../drivers/media/video/bttvp.h"
+#endif
+
+#if BTTV_VERSION_CODE < KERNEL_VERSION(0,7,45)
+#error "*******************************************************"
+#error " Sorry, this driver needs bttv version 0.7.45 or       "
+#error " higher. If you are using the bttv package, copy it to "
+#error " the kernel                                            "
+#error "*******************************************************"
+#endif
+
+#include "linux/kcompat.h"
+#include "lirc_dev.h"
+
+/* insmod parameters */
+static int debug = 0;
+static int card = 0;
+static int minor = -1;
+static int bttv_id = BTTV_UNKNOWN;
+static unsigned long gpio_mask = 0;
+static unsigned long gpio_enable = 0;
+static unsigned long gpio_lock_mask = 0;
+static unsigned long gpio_xor_mask = 0;
+static int soft_gap = 0;
+static int sample_rate = 10;
+
+#define dprintk(fmt, args...)                                 \
+	do{                                                   \
+		if(debug) printk(KERN_DEBUG fmt, ## args);    \
+	}while(0)
+
+struct rcv_info {
+	int bttv_id;
+	int card_id;
+	unsigned long gpio_mask;
+	unsigned long gpio_enable;
+	unsigned long gpio_lock_mask;
+	unsigned long gpio_xor_mask;
+	int soft_gap;
+	int sample_rate;
+	unsigned char code_length;
+};
+
+static struct rcv_info rcv_infos[] = {
+	{BTTV_UNKNOWN,                0,          0,          0,         0,          0,   0,  1,  0},
+	{BTTV_PXELVWPLTVPAK,          0, 0x00003e00,          0, 0x0010000,          0,   0, 15, 32},
+	{BTTV_PXELVWPLTVPRO,          0, 0x00001f00,          0, 0x0008000,          0, 500, 12, 32},
+	{BTTV_PV_BT878P_9B,           0, 0x00001f00,          0, 0x0008000,          0, 500, 12, 32},
+	{BTTV_PV_BT878P_PLUS,         0, 0x00001f00,          0, 0x0008000,          0, 500, 12, 32},
+	{BTTV_AVERMEDIA,              0, 0x00f88000,          0, 0x0010000, 0x00010000,   0, 10, 32},
+	{BTTV_AVPHONE98,     0x00011461, 0x003b8000, 0x00004000, 0x0800000, 0x00800000,   0, 10,  0}, /*mapped to Capture98*/
+	{BTTV_AVERMEDIA98,   0x00021461, 0x003b8000, 0x00004000, 0x0800000, 0x00800000,   0, 10,  0}, /*mapped to Capture98*/
+	{BTTV_AVPHONE98,     0x00031461, 0x00f88000,          0, 0x0010000, 0x00010000,   0, 10, 32}, /*mapped to Phone98*/
+	/* is this one correct? */
+	{BTTV_AVERMEDIA98,   0x00041461, 0x00f88000,          0, 0x0010000, 0x00010000,   0, 10, 32}, /*mapped to Phone98*/
+	/* work-around for VDOMATE */
+	{BTTV_AVERMEDIA98,   0x03001461, 0x00f88000,          0, 0x0010000, 0x00010000,   0, 10, 32}, /*mapped to Phone98*/
+	/* reported by Danijel Korzinek, AVerTV GOw/FM */
+	{BTTV_AVERMEDIA98,   0x00000000, 0x00f88000,          0, 0x0010000, 0x00010000,   0, 10, 32}, /*mapped to Phone98*/
+	{BTTV_CHRONOS_VS2,            0, 0x000000f8,          0, 0x0000100,          0,   0, 20,  0},
+	/* CPH031 and CPH033 cards (?) */
+	/* MIRO was just a work-around */
+	{BTTV_MIRO,                   0, 0x00001f00,          0, 0x0004000,          0,   0, 10, 32},
+	{BTTV_DYNALINK,               0, 0x00001f00,          0, 0x0004000,          0,   0, 10, 32},
+	{BTTV_WINVIEW_601,            0, 0x00001f00,          0, 0x0004000,          0,   0,  0, 32},
+#ifdef BTTV_KWORLD
+	{BTTV_KWORLD,                 0, 0x00007f00,          0, 0x0004000,          0,   0, 12, 32},
+#endif
+	/* just a guess */
+	{BTTV_MAGICTVIEW061,          0, 0x0028e000,          0, 0x0020000,          0,   0, 20, 32},
+ 	{BTTV_MAGICTVIEW063,          0, 0x0028e000,          0, 0x0020000,          0,   0, 20, 32},
+ 	{BTTV_PHOEBE_TVMAS,           0, 0x0028e000,          0, 0x0020000,          0,   0, 20, 32},
+#ifdef BTTV_BESTBUY_EASYTV2
+        {BTTV_BESTBUY_EASYTV,         0, 0x00007F00,          0, 0x0004000,          0,   0, 10,  8},
+        {BTTV_BESTBUY_EASYTV2,        0, 0x00007F00,          0, 0x0008000,          0,   0, 10,  8},
+#endif
+	/* lock_mask probably also 0x100, or maybe it is 0x0 for all others !?! */
+	{BTTV_FLYVIDEO,               0, 0x000000f8,          0,         0,          0,   0,  0, 42},
+ 	{BTTV_FLYVIDEO_98,            0, 0x000000f8,          0, 0x0000100,          0,   0,  0, 42},
+ 	{BTTV_TYPHOON_TVIEW,          0, 0x000000f8,          0, 0x0000100,          0,   0,  0, 42},
+#ifdef BTTV_FLYVIDEO_98FM
+	/* smorar@alfonzo.smuts.uct.ac.za */
+	{BTTV_FLYVIDEO_98FM,          0, 0x000000f8,          0, 0x0000100,          0,   0,  0, 42},
+#endif
+	/* The Leadtek WinFast TV 2000 XP card (id 0x6606107d) uses an
+	 * extra gpio bit compared to the original TV 2000 card (id
+	 * 0x217d6606); as the bttv-0.7.100 driver does not
+	 * distinguish between the two cards, we enable the extra bit
+	 * based on the card id: */
+	{BTTV_WINFAST2000,   0x6606107d, 0x000008f8,          0, 0x0000100,          0,   0,  0, 32},
+	/* default: */
+	{BTTV_WINFAST2000,            0, 0x000000f8,          0, 0x0000100,          0,   0,  0, 32},
+#ifdef BTTV_GVBCTV5PCI
+	{BTTV_GVBCTV5PCI,             0, 0x00f0b000,          0,         0,          0,   0, 20,  8},
+#endif
+};
+
+static unsigned char code_length = 0;
+static unsigned char code_bytes = 1;
+
+#define MAX_BYTES 8
+
+#define SUCCESS 0
+#define LOGHEAD "lirc_gpio (%d): "
+
+/* how many bits GPIO value can be shifted right before processing
+ * it is computed from the value of gpio_mask_parameter
+ */
+static unsigned char gpio_pre_shift = 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 build_key(unsigned long gpio_val, unsigned char codes[MAX_BYTES])
+{
+	unsigned long mask = gpio_mask;
+	unsigned char shift = 0;
+
+	dprintk(LOGHEAD "gpio_val is %lx\n",card,(unsigned long) gpio_val);
+	
+	gpio_val ^= gpio_xor_mask;
+	
+	if (gpio_lock_mask && (gpio_val & gpio_lock_mask)) {
+		return -EBUSY;
+	}
+	
+	switch (bttv_id)
+	{
+	case BTTV_AVERMEDIA98:
+		if (bttv_write_gpio(card, gpio_enable, gpio_enable)) {
+			dprintk(LOGHEAD "cannot write to GPIO\n", card);
+			return -EIO;
+		}
+		if (bttv_read_gpio(card, &gpio_val)) {
+			dprintk(LOGHEAD "cannot read GPIO\n", card);
+			return -EIO;
+		}
+		if (bttv_write_gpio(card, gpio_enable, 0)) {
+			dprintk(LOGHEAD "cannot write to GPIO\n", card);
+			return -EIO;
+		}
+		break;
+	default:
+		break;
+	}
+	
+	/* extract bits from "raw" GPIO value using gpio_mask */
+	codes[0] = 0;
+	gpio_val >>= gpio_pre_shift;
+	while (mask) {
+		if (mask & 1u) {
+			codes[0] |= (gpio_val & 1u) << shift++;
+		}
+		mask >>= 1;
+		gpio_val >>= 1;
+	}
+	
+	dprintk(LOGHEAD "code is %lx\n",card,(unsigned long) codes[0]);
+	switch (bttv_id)
+	{
+	case BTTV_AVERMEDIA:
+		codes[2] = (codes[0]<<2)&0xff;
+		codes[3] = (~codes[2])&0xff;
+		codes[0] = 0x02;
+		codes[1] = 0xFD;
+		break;
+	case BTTV_AVPHONE98:
+		codes[2] = ((codes[0]&(~0x1))<<2)&0xff;
+		codes[3] = (~codes[2])&0xff;
+		if (codes[0]&0x1) {
+			codes[0] = 0xc0;
+			codes[1] = 0x3f;
+		} else {
+			codes[0] = 0x40;
+			codes[1] = 0xbf;
+		}
+		break;
+	case BTTV_AVERMEDIA98:
+		break;
+	case BTTV_FLYVIDEO:
+	case BTTV_FLYVIDEO_98:
+	case BTTV_TYPHOON_TVIEW:
+#ifdef BTTV_FLYVIDEO_98FM
+	case BTTV_FLYVIDEO_98FM:
+#endif
+		codes[4]=codes[0]<<3;
+		codes[5]=((~codes[4])&0xff);
+		
+		codes[0]=0x00;
+		codes[1]=0x1A;
+		codes[2]=0x1F;
+		codes[3]=0x2F;
+		break;
+        case BTTV_MAGICTVIEW061:
+        case BTTV_MAGICTVIEW063:
+	case BTTV_PHOEBE_TVMAS:
+		codes[0] = (codes[0]&0x01)
+			|((codes[0]&0x02)<<1)
+			|((codes[0]&0x04)<<2)
+			|((codes[0]&0x08)>>2)
+			|((codes[0]&0x10)>>1);
+		/* FALLTHROUGH */
+	case BTTV_MIRO:
+	case BTTV_DYNALINK:
+	case BTTV_PXELVWPLTVPAK:
+	case BTTV_PXELVWPLTVPRO:
+	case BTTV_PV_BT878P_9B:
+	case BTTV_PV_BT878P_PLUS:
+#ifdef BTTV_KWORLD
+	case BTTV_KWORLD:
+#endif
+		codes[2] = reverse(codes[0],8);
+		codes[3] = (~codes[2])&0xff;
+		codes[0] = 0x61;
+		codes[1] = 0xD6;
+		break;
+#if 0
+		/* derived from e-tech config file */
+		/* 26 + 16 bits */
+		/* won't apply it until it's confirmed with a fly98 */
+ 	case BTTV_FLYVIDEO_98:
+	case BTTV_FLYVIDEO_98FM:
+		codes[4]=codes[0]<<3;
+		codes[5]=(~codes[4])&0xff;
+		
+		codes[0]=0x00;
+		codes[1]=0x1A;
+		codes[2]=0x1F;
+		codes[3]=0x2F;
+		break;
+#endif
+	case BTTV_WINFAST2000:
+		/* shift extra bit */
+		codes[0] = (codes[0]&0x1f) | ((codes[0]&0x20) << 1);
+	case BTTV_WINVIEW_601:
+		codes[2] = reverse(codes[0],8);
+		codes[3] = (~codes[2])&0xff;
+		codes[0] = 0xC0;
+		codes[1] = 0x3F;
+		break;
+	default:
+		break;
+	}
+
+	return SUCCESS;
+}
+
+/* add_to_buf - copy a code to the buffer */
+static int add_to_buf(void* data, struct lirc_buffer* buf)
+{
+	static unsigned long next_time = 0;
+	static unsigned char prev_codes[MAX_BYTES];
+	unsigned long code = 0;
+	unsigned char cur_codes[MAX_BYTES];
+    
+	if (bttv_read_gpio(card, &code)) {
+		dprintk(LOGHEAD "cannot read GPIO\n", card);
+		return -EIO;
+	}
+	
+	if (build_key(code, cur_codes)) {
+		return -EFAULT;
+	}
+	
+	if (soft_gap) {
+		if (!memcmp(prev_codes, cur_codes, code_bytes) &&
+			jiffies < next_time) {
+			return -EAGAIN;
+		}
+		next_time = jiffies + soft_gap;
+	}
+	memcpy( prev_codes, cur_codes, code_bytes );
+		
+	lirc_buffer_write_1( buf, cur_codes );
+		
+	return SUCCESS;
+}
+
+static int set_use_inc(void* data)
+{
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static void set_use_dec(void* data)
+{
+	MOD_DEC_USE_COUNT;
+}
+
+static wait_queue_head_t* get_queue(void* data)
+{
+	return bttv_get_gpio_queue(card);
+}
+
+static struct lirc_plugin plugin = {
+	.name		= "lirc_gpio  ",
+	.add_to_buf	= add_to_buf,
+	.get_queue	= get_queue,
+	.set_use_inc	= set_use_inc,
+	.set_use_dec	= set_use_dec,
+	.owner          = THIS_MODULE,
+};
+
+/*
+ *
+ */
+static int gpio_remote_init(void)
+{  	
+	int ret;
+	unsigned int mask;
+
+	/* "normalize" gpio_mask
+	 * this means shift it right until first bit is set
+	 */
+	while (!(gpio_mask & 1u)) {
+		gpio_pre_shift++;
+		gpio_mask >>= 1;
+	}
+
+	if (code_length) {
+		plugin.code_length = code_length;
+	} else {
+		/* calculate scan code length in bits if needed */
+		plugin.code_length = 1;
+		mask = gpio_mask >> 1;
+		while (mask) {
+			if (mask & 1u) {
+				plugin.code_length++;
+			}
+			mask >>= 1;
+		}
+	}
+
+	code_bytes = (plugin.code_length/8) + (plugin.code_length%8 ? 1 : 0);
+	if (MAX_BYTES < code_bytes) {
+		printk (LOGHEAD "scan code too long (%d bytes)\n",
+			minor, code_bytes);
+		return -EBADRQC;
+	}
+
+	if (gpio_enable) {
+		if(bttv_gpio_enable(card, gpio_enable, gpio_enable)) {
+			printk(LOGHEAD "gpio_enable failure\n", minor);
+			return -EIO;
+		}
+	}
+
+
+	/* translate ms to jiffies */
+	soft_gap = (soft_gap*HZ) / 1000;
+
+	plugin.minor = minor;
+	plugin.sample_rate = sample_rate;
+
+	ret = lirc_register_plugin(&plugin);
+	
+	if (0 > ret) {
+		printk (LOGHEAD "device registration failed with %d\n",
+			minor, ret);
+		return ret;
+	}
+	
+	minor = ret;
+	printk(LOGHEAD "driver registered\n", minor);
+
+	return SUCCESS;
+}
+
+#ifdef MODULE
+/*
+ *
+ */
+int init_module(void)
+{
+	int type,cardid,card_type;
+
+	if (MAX_IRCTL_DEVICES < minor) {
+		printk("lirc_gpio: parameter minor (%d) must be less than %d!\n",
+		       minor, MAX_IRCTL_DEVICES-1);
+		return -EBADRQC;
+	}
+	
+	request_module("bttv");
+
+	/* if gpio_mask not zero then use module parameters 
+	 * instead of autodetecting TV card
+	 */
+	if (gpio_mask) {
+		if (sample_rate!=0 &&
+		    (2 > sample_rate || HZ < sample_rate)) {
+			printk(LOGHEAD "parameter sample_rate "
+			       "must be between 2 and %d!\n", minor, HZ);
+			return -EBADRQC;
+		}
+
+		if (sample_rate!=0 && soft_gap && 
+		    ((2000/sample_rate) > soft_gap || 1000 < soft_gap)) {
+			printk(LOGHEAD "parameter soft_gap "
+			       "must be between %d and 1000!\n",
+			       minor, 2000/sample_rate);
+			return -EBADRQC;
+		}
+	} else {
+		if(bttv_get_cardinfo(card,&type,&cardid)==-1) {
+			printk(LOGHEAD "could not get card type\n", minor);
+			return -EBADRQC;
+		}
+		printk(LOGHEAD "card type 0x%x, id 0x%x\n",minor,
+		       type,cardid);
+
+		if (type == BTTV_UNKNOWN) {
+			printk(LOGHEAD "cannot detect TV card nr %d!\n",
+			       minor, card);
+			return -EBADRQC;
+		}
+		for (card_type = 1;
+		     card_type < sizeof(rcv_infos)/sizeof(struct rcv_info); 
+		     card_type++) {
+			if (rcv_infos[card_type].bttv_id == type &&
+			    (rcv_infos[card_type].card_id == 0 ||
+			     rcv_infos[card_type].card_id == cardid)) {
+				bttv_id = rcv_infos[card_type].bttv_id;
+				gpio_mask = rcv_infos[card_type].gpio_mask;
+				gpio_enable = rcv_infos[card_type].gpio_enable;
+				gpio_lock_mask = rcv_infos[card_type].gpio_lock_mask;
+				gpio_xor_mask = rcv_infos[card_type].gpio_xor_mask;
+				soft_gap = rcv_infos[card_type].soft_gap;
+				sample_rate = rcv_infos[card_type].sample_rate;
+				code_length = rcv_infos[card_type].code_length;
+				break;
+			}
+		}
+		if (type==BTTV_AVPHONE98 && cardid==0x00011461)	{
+			bttv_id = BTTV_AVERMEDIA98;
+		}
+		if (type==BTTV_AVERMEDIA98 && cardid==0x00041461) {
+			bttv_id = BTTV_AVPHONE98;
+		}
+		if (type==BTTV_AVERMEDIA98 && cardid==0x03001461) {
+			bttv_id = BTTV_AVPHONE98;
+		}
+		if (type==BTTV_AVERMEDIA98 && cardid==0x00000000) {
+			bttv_id = BTTV_AVPHONE98;
+		}
+		if (card_type == sizeof(rcv_infos)/sizeof(struct rcv_info)) {
+			printk(LOGHEAD "TV card type 0x%x not supported!\n",
+			       minor, type);
+			return -EBADRQC;
+		}
+	}
+
+	request_module("lirc_dev");
+
+	return gpio_remote_init();
+}
+
+/*
+ *
+ */
+void cleanup_module(void)
+{
+	lirc_unregister_plugin(minor);
+
+	dprintk(LOGHEAD "module successfully unloaded\n", minor);
+}
+
+/* Dont try to use it as a static version !  */
+MODULE_DESCRIPTION("Driver module for remote control (data from bt848 GPIO port)");
+MODULE_AUTHOR("Artur Lipowski");
+MODULE_LICENSE("GPL");
+
+module_param(minor, int, 0444);
+MODULE_PARM_DESC(minor, "Preferred minor device number");
+
+module_param(card, int, 0444);
+MODULE_PARM_DESC(card, "TV card number to attach to");
+
+module_param(gpio_mask, long, 0444);
+MODULE_PARM_DESC(gpio_mask, "gpio_mask");
+
+module_param(gpio_lock_mask, long, 0444);
+MODULE_PARM_DESC(gpio_lock_mask, "gpio_lock_mask");
+
+module_param(gpio_xor_mask, long, 0444);
+MODULE_PARM_DESC(gpio_xor_mask, "gpio_xor_mask");
+
+module_param(soft_gap, int, 0444);
+MODULE_PARM_DESC(soft_gap, "Time between keypresses (in ms)");
+
+module_param(sample_rate, int, 0444);
+MODULE_PARM_DESC(sample_rate, "Sample rate (between 2 and HZ)");
+
+module_param(bttv_id, int, 0444);
+MODULE_PARM_DESC(bttv_id, "BTTV card type");
+
+module_param(debug, bool, 0644);
+MODULE_PARM_DESC(debug, "Enable debugging messages");
+
+EXPORT_NO_SYMBOLS;
+
+#endif /* MODULE */
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff -urN --exclude='*.orig' --exclude='*.rej' linux-2.6.11-mm4/drivers/char/lirc/lirc_i2c.c linux-2.6.11-no1/drivers/char/lirc/lirc_i2c.c
--- linux-2.6.11-mm4/drivers/char/lirc/lirc_i2c.c	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.11-no1/drivers/char/lirc/lirc_i2c.c	2005-03-30 00:12:39.520812686 -0500
@@ -0,0 +1,634 @@
+/*      $Id: lirc_i2c.c,v 1.34 2005/03/28 09:25:36 lirc Exp $      */
+
+/*
+ * 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)
+ *
+ * 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
+ *
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#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 <asm/semaphore.h>
+
+#include "linux/kcompat.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   = 0;    /* 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 */
+	if (-1 == (flags = i2c_smbus_read_byte_data(&ir->c,0x10))) {
+		dprintk("read error\n");
+		return -ENODATA;
+	}
+	/* key pressed ? */
+	if (0 == (flags & 0x80))
+		return -ENODATA;
+
+	/* read actual key code */
+	if (-1 == (code = i2c_smbus_read_byte_data(&ir->c,0x00))) {
+		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  */
+	/* this is completely broken code. lirc_unregister_plugin()
+	   must be possible even when the device is open */
+	i2c_use_client(&ir->c);
+
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static void set_use_dec(void* data)
+{
+	struct IR *ir = data;
+
+	i2c_release_client(&ir->c);
+	MOD_DEC_USE_COUNT;
+}
+
+static struct lirc_plugin lirc_template = {
+	name:        "lirc_i2c",
+	set_use_inc: set_use_inc,
+	set_use_dec: set_use_dec,
+	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 = {
+        name:           "i2c ir driver",
+        id:             I2C_DRIVERID_EXP3, /* FIXME */
+        flags:          I2C_DF_NOTIFY,
+        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;
+	
+        client_template.adapter = adap;
+        client_template.addr = addr;
+	
+        if (NULL == (ir = kmalloc(sizeof(struct IR),GFP_KERNEL)))
+                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:
+		strcpy(ir->c.name,"Pixelview IR");
+		ir->l.code_length = 8;
+		ir->l.add_to_buf=add_to_buf_pixelview;
+		break;
+	case 0x4b:
+		strcpy(ir->c.name,"PV951 IR");
+		ir->l.code_length = 32;
+		ir->l.add_to_buf=add_to_buf_pv951;
+		break;
+	case 0x71:
+		/* 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.
+		 */
+		strcpy(ir->c.name,"Hauppauge IR (PVR150)");
+		ir->l.code_length = 13;
+		ir->l.add_to_buf=add_to_buf_haup_pvr150;
+		break;
+	case 0x6b:
+		strcpy(ir->c.name,"Adaptec IR");
+		ir->l.code_length = 32;
+		ir->l.add_to_buf=add_to_buf_adap;
+		break;
+	case 0x18:
+	case 0x1a:
+		if (adap->id == (I2C_ALGO_BIT | I2C_HW_B_BT848)) {
+			strcpy(ir->c.name,"Hauppauge IR");
+			ir->l.code_length = 13;
+			ir->l.add_to_buf=add_to_buf_haup;
+		}
+		else {
+			strcpy(ir->c.name,"Leadtek IR");
+			ir->l.code_length = 8;
+			ir->l.add_to_buf=add_to_buf_pvr2000;
+		}
+		break;
+	case 0x30:
+		strcpy(ir->c.name,"KNC ONE IR");
+		ir->l.code_length = 8;
+		ir->l.add_to_buf=add_to_buf_knc1;
+		break;
+	case 0x21:
+	case 0x23:
+		strcpy(ir->c.name,"TV-Box IR");
+		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("lirc_i2c: chip found @ 0x%02x (%s)\n",addr,ir->c.name);
+	
+	/* register device */
+	i2c_attach_client(&ir->c);
+	ir->l.minor = lirc_register_plugin(&ir->l);
+	i2c_use_client(&ir->c);
+	
+	return 0;
+}
+
+static int ir_detach(struct i2c_client *client)
+{
+	struct IR *ir = i2c_get_clientdata(client);
+	
+	/* unregister device */
+	i2c_release_client(&ir->c);
+	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};
+	struct i2c_client c; char buf; int i,rc;
+
+	if (adap->id == (I2C_ALGO_BIT | I2C_HW_B_BT848)) {
+		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);
+			}
+		}
+	}
+
+#ifdef I2C_HW_B_CX2388x
+	/* Leadtek Winfast PVR2000 */
+	else if (adap->id == (I2C_ALGO_BIT | I2C_HW_B_CX2388x)) {
+		memset(&c,0,sizeof(c));
+		c.adapter = adap;
+		c.addr    = 0x18;
+		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);
+		}
+	}
+#endif
+
+	/* Asus TV-Box and Creative/VisionTek BreakOut-Box (PCF8574) */
+	else if (adap->id == (I2C_ALGO_BIT | I2C_HW_B_RIVA)) {
+		/* addresses to probe;
+		   leave 0x24 and 0x25 because SAA7113H possibly uses it 
+		   0x21 and 0x22 possibly used by SAA7108E 
+		   Asus:      0x21 is a correct address (channel 1 of PCF8574)
+		   Creative:  0x23 is a correct address (channel 3 of PCF8574)
+		   VisionTek: 0x23 is a correct address (channel 3 of PCF8574)
+		*/
+		static const int pcf_probe[] = { 0x20, 0x21, 0x22, 0x23,
+						 0x24, 0x25, 0x26, 0x27, -1 };
+		int ret1, ret2, ret3, ret4;
+		unsigned char bits = 0, flag = 0;
+
+		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");
+
+EXPORT_NO_SYMBOLS;
+
+#endif /* MODULE */
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff -urN --exclude='*.orig' --exclude='*.rej' linux-2.6.11-mm4/drivers/char/lirc/lirc_igorplugusb.c linux-2.6.11-no1/drivers/char/lirc/lirc_igorplugusb.c
--- linux-2.6.11-mm4/drivers/char/lirc/lirc_igorplugusb.c	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.11-no1/drivers/char/lirc/lirc_igorplugusb.c	2005-03-30 00:12:39.522812409 -0500
@@ -0,0 +1,669 @@
+/* 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>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 0)
+#error "*******************************************************"
+#error "Sorry, this driver needs kernel version 2.4.0 or higher"
+#error "*******************************************************"
+#endif
+
+#include <linux/config.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 "linux/kcompat.h"
+#include "linux/lirc.h"
+#include "lirc_dev.h"
+
+#if !defined(KERNEL_2_5)
+#        define USB_CTRL_GET_TIMEOUT    5
+#endif
+
+/* lock irctl structure */
+#define IRLOCK			down_interruptible(&ir->lock)
+#define IRUNLOCK		up(&ir->lock)
+
+/* 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 = 0;
+#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(lirc_t)
+
+/* 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             10
+
+
+/**** 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;
+
+#if defined(KERNEL_2_5)
+	dma_addr_t dma_in;
+#endif
+
+	/* lirc */
+	struct lirc_plugin *p;
+
+	/* handle sending (init strings) */
+	int send_flags;
+	wait_queue_head_t wait_out;
+
+	struct semaphore lock;
+};
+
+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);
+
+	MOD_INC_USE_COUNT;
+
+	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);
+
+	MOD_DEC_USE_COUNT;
+}
+
+
+/** 
+ * 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);
+  
+	if((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)) > 0)
+	{
+		int i = DEVICE_HEADERLEN;
+		lirc_t 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);
+			i = DEVICE_HEADERLEN + ir->buf_in[2];  /* start at earliest byte */
+			/* 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;
+		}
+
+		if((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)) < 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;
+}
+
+
+
+#if defined(KERNEL_2_5)
+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;
+#else
+static void *usb_remote_probe(struct usb_device *dev, unsigned int ifnum,
+				const struct usb_device_id *id)
+{
+	struct usb_interface *intf;
+	struct usb_interface_descriptor *idesc;
+	struct usb_endpoint_descriptor *ep_ctl2;
+#endif
+	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");
+
+#if defined(KERNEL_2_5)
+	dev = interface_to_usbdev(intf);
+
+#  if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 5)
+	idesc = &intf->altsetting[intf->act_altsetting];  /* in 2.6.4 */
+#  else
+	idesc = intf->cur_altsetting;  /* in 2.6.6 */
+#  endif
+
+	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);
+#else
+	intf = &dev->actconfig->interface[ifnum];
+	idesc = &intf->altsetting[intf->act_altsetting];
+	if (idesc->bNumEndpoints != 1)
+		return NULL;
+	ep_ctl2 = idesc->endpoint;
+	if (((ep_ctl2->bEndpointAddress & USB_ENDPOINT_DIR_MASK) != USB_DIR_IN)
+		|| (ep_ctl2->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+		!= USB_ENDPOINT_XFER_CONTROL)
+		return NULL;
+	pipe = usb_rcvctrlpipe(dev, ep_ctl2->bEndpointAddress);
+#endif
+	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;
+	if (!(ir = kmalloc(sizeof(struct irctl), GFP_KERNEL))) {
+		mem_failure = 1;
+	} else {
+		memset(ir, 0, sizeof(struct irctl));
+
+		if (!(plugin = kmalloc(sizeof(struct lirc_plugin), GFP_KERNEL))) {
+			mem_failure = 2;
+		} else if (!(rbuf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL))) {
+			mem_failure = 3;
+		} else if (lirc_buffer_init(rbuf, bytes_in_key, 
+                		DEVICE_BUFLEN+ADDITIONAL_LIRC_BYTES)) {
+			mem_failure = 4;
+#if defined(KERNEL_2_5)
+		} else if (!(ir->buf_in = usb_buffer_alloc(dev, 
+				DEVICE_BUFLEN+DEVICE_HEADERLEN, 
+                                SLAB_ATOMIC, &ir->dma_in))) {
+			mem_failure = 5;
+#else
+		} else if (!(ir->buf_in = kmalloc(
+				DEVICE_BUFLEN+DEVICE_HEADERLEN, GFP_KERNEL))) {
+			mem_failure = 5;
+#endif
+		} else {
+
+			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->owner = THIS_MODULE;
+
+			init_MUTEX(&ir->lock);
+			init_waitqueue_head(&ir->wait_out);
+
+			if ((minor = lirc_register_plugin(plugin)) < 0) {
+				mem_failure = 9;
+			}
+		}
+	}
+
+	/* free allocated memory in case of failure */
+	switch (mem_failure) {
+	case 9:
+#if defined(KERNEL_2_5)
+		usb_buffer_free(dev, DEVICE_BUFLEN+DEVICE_HEADERLEN, 
+                	ir->buf_in, ir->dma_in);
+#else
+		kfree(ir->buf_in);
+#endif
+	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);
+#if defined(KERNEL_2_5)
+		return -ENOMEM;
+#else
+		return NULL;
+#endif
+	}
+
+	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 */
+	if ((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)) < 0)
+	{
+		printk(DRIVER_NAME "[%d]: SET_INFRABUFFER_EMPTY: error %d\n", 
+			devnum, ret);
+	}
+
+#if defined(KERNEL_2_5)
+	usb_set_intfdata(intf, ir);
+	return SUCCESS;
+#else
+	return ir;
+#endif
+}
+
+
+#if defined(KERNEL_2_5)
+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);
+#else
+static void usb_remote_disconnect(struct usb_device *dev, void *ptr)
+{
+	struct irctl *ir = ptr;
+#endif
+
+	if (!ir || !ir->p)
+		return;
+
+	ir->usbdev = NULL;
+	wake_up_all(&ir->wait_out);
+
+	IRLOCK;
+#if defined(KERNEL_2_5)
+	usb_buffer_free(dev, ir->len_in, ir->buf_in, ir->dma_in);
+#else
+	kfree(ir->buf_in);
+#endif
+	IRUNLOCK;
+
+	unregister_from_lirc(ir);
+}
+
+static struct usb_device_id usb_remote_id_table [] = {
+	{ USB_DEVICE(0x03eb, 0x0002) },	/* Igor Plug USB (Atmel's Manufact. ID) */
+	{ }				/* Terminating entry */
+};
+
+static struct usb_driver usb_remote_driver = {
+	.owner =	THIS_MODULE,
+	.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("\n" DRIVER_NAME ": " DRIVER_DESC " v" DRIVER_VERSION "\n");
+	printk(DRIVER_NAME ": " DRIVER_AUTHOR "\n");
+	dprintk(DRIVER_NAME ": debug mode enabled\n");
+
+	request_module("lirc_dev");
+
+	if ((i = usb_register(&usb_remote_driver)) < 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);
+
+#if defined(KERNEL_2_5)
+#include <linux/vermagic.h>
+MODULE_INFO(vermagic, VERMAGIC_STRING);
+#endif
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(usb, usb_remote_id_table);
+
+EXPORT_NO_SYMBOLS;
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff -urN --exclude='*.orig' --exclude='*.rej' linux-2.6.11-mm4/drivers/char/lirc/lirc_imon.c linux-2.6.11-no1/drivers/char/lirc/lirc_imon.c
--- linux-2.6.11-mm4/drivers/char/lirc/lirc_imon.c	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.11-no1/drivers/char/lirc/lirc_imon.c	2005-03-30 00:12:39.523812270 -0500
@@ -0,0 +1,1129 @@
+/*
+ *   lirc_imon.c:  LIRC plugin/VFD driver for Ahanix/Soundgraph IMON IR/VFD
+ *
+ *   $Id: lirc_imon.c,v 1.4 2005/02/19 15:13:00 lirc Exp $
+ *
+ *   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>
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,22)
+#error "*** Sorry, this driver requires kernel version 2.4.22 or higher"
+#endif
+
+#include <linux/config.h>
+
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <asm/uaccess.h>
+#include <linux/usb.h>
+#include <linux/devfs_fs_kernel.h>
+
+#include "linux/lirc.h"
+#include "linux/kcompat.h"
+#include "lirc_dev.h"
+
+
+#define MOD_AUTHOR	"Venky Raju <dev@venky.ws>"
+#define MOD_DESC	"Driver for Soundgraph iMON MultiMedian IR/VFD"
+#define MOD_NAME	"lirc_imon"
+#define MOD_VERSION	"0.2b2"
+
+#define VFD_MINOR_BASE	144	/* Same as LCD */
+#define DEVFS_MODE	S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH
+#define DEVFS_NAME	"usb/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 */
+#ifdef KERNEL_2_5
+static int imon_probe (struct usb_interface *interface,
+			const struct usb_device_id *id);
+static void imon_disconnect (struct usb_interface *interface);
+static void usb_rx_callback (struct urb *urb, struct pt_regs *regs);
+static void usb_tx_callback (struct urb *urb, struct pt_regs *regs);
+#else
+static void * imon_probe (struct usb_device * dev, unsigned int intf,
+				const struct usb_device_id *id);
+static void imon_disconnect (struct usb_device *dev, void *data);
+static void usb_rx_callback (struct urb *urb);
+static void usb_tx_callback (struct urb *urb);
+#endif
+
+/* 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);
+
+/* 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       */
+#if !defined (KERNEL_2_5)
+	int subminor;			/* index into minor_table         */
+	devfs_handle_t devfs;
+#endif
+	int ir_isopen;			/* IR port has been opened        */
+	int dev_present;		/* USB device presence            */
+	struct semaphore sem;		/* 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 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	down (&context ->sem)
+#define UNLOCK_CONTEXT	up (&context ->sem)
+
+/* 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 [] = {
+	{ USB_DEVICE(0x0aa8, 0xffda) },		/* IR & VFD    */
+	{ USB_DEVICE(0x0aa8, 0x8001) },		/* IR only     */
+	{ USB_DEVICE(0x15c2, 0xffda) },		/* IR & VFD    */
+	{ USB_DEVICE(0x04e8, 0xff30) },		/* ext IR only */
+	{}
+};
+
+/* Some iMON VFD models requires a 6th packet */
+static int vfd_proto_6p = FALSE;
+static unsigned short vfd_proto_6p_vendor_list [] = {
+			/* terminate this list with a 0 */
+			0x15c2,
+			0 };
+static unsigned char vfd_packet6 [] = {
+		0x01, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF };
+
+/* USB Device data */
+static struct usb_driver imon_driver = {
+	.owner 		= THIS_MODULE,
+	.name 		= MOD_NAME,
+	.probe 		= imon_probe,
+	.disconnect 	= imon_disconnect,
+	.id_table 	= imon_usb_id_table,
+#if !defined(KERNEL_2_5)
+	.fops		= &vfd_fops,
+	.minor		= VFD_MINOR_BASE,
+#endif
+};
+
+#ifdef KERNEL_2_5
+static struct usb_class_driver imon_class = {
+	.name 		= DEVFS_NAME,
+	.fops		= &vfd_fops,
+	.mode		= DEVFS_MODE,
+	.minor_base	= VFD_MINOR_BASE,
+};
+#endif
+
+/* to prevent races between open() and disconnect() */
+static DECLARE_MUTEX (disconnect_sem);
+
+static int debug = 0;
+
+#if !defined(KERNEL_2_5)
+
+#define MAX_DEVICES	4	/* In case there's more than one iMON device */
+static struct imon_context * minor_table [MAX_DEVICES];
+
+/*
+static DECLARE_MUTEX (minor_table_sem);
+#define LOCK_MINOR_TABLE	down (&minor_table_sem)
+#define UNLOCK_MINOR_TABLE	up (&minor_table_sem)
+*/
+
+/* the global usb devfs handle */
+extern devfs_handle_t usb_devfs_handle;
+
+#endif
+
+/* ------------------------------------------------------------
+ *                     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_param (vfd_proto_6p, int, 0);
+MODULE_PARM_DESC (debug, "Debug messages: 0=no, 1=yes (default: no)");
+MODULE_PARM_DESC (vfd_proto_6p, "Force the 6 packet VFD protocol: 0=no 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", __FUNCTION__);
+}
+
+static inline void deregister_from_lirc (struct imon_context *context) {
+
+	int retval;
+	int minor = context ->plugin ->minor;
+
+	if ((retval = lirc_unregister_plugin (minor))) {
+
+		err ("%s: unable to deregister from lirc (%d)", 
+			__FUNCTION__, 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)
+{
+#ifdef KERNEL_2_5
+	struct usb_interface *interface;
+#endif
+	struct imon_context *context = NULL;
+	int subminor;
+	int retval = SUCCESS;
+
+	/* prevent races with disconnect */
+	down (&disconnect_sem);
+	
+#ifdef KERNEL_2_5
+	subminor = iminor (inode);
+	interface = usb_find_interface (&imon_driver, subminor);
+	if (!interface) {
+		err ("%s: could not find interface for minor %d", __FUNCTION__, subminor);
+		retval = -ENODEV;
+		goto exit;
+	}
+	context = usb_get_intfdata (interface);
+#else
+	subminor = MINOR (inode ->i_rdev) - VFD_MINOR_BASE;
+	if (subminor < 0 || subminor >= MAX_DEVICES) {
+		err ("%s: no record of minor %d", __FUNCTION__, subminor);
+		retval = -ENODEV;
+		goto exit;
+	}
+	context = minor_table [subminor];
+#endif
+
+	if (!context) {
+		err ("%s: no context found for minor %d", 
+					__FUNCTION__, subminor);
+		retval = -ENODEV;
+		goto exit;
+	}
+
+	LOCK_CONTEXT;
+
+	if (!context ->vfd_supported) {
+		err ("%s: VFD not supported by device", __FUNCTION__);
+		retval = -ENODEV;
+	}
+	else if (context ->vfd_isopen) {
+		
+		err ("%s: VFD port is already open", __FUNCTION__);
+		retval = -EBUSY;
+	}
+	else {
+		MOD_INC_USE_COUNT;
+		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", __FUNCTION__);
+		return -ENODEV;
+	}
+
+	LOCK_CONTEXT;
+
+	if (!context ->vfd_supported) {
+		err ("%s: VFD not supported by device", __FUNCTION__);
+		retval = -ENODEV;
+	}
+	else if (!context ->vfd_isopen) {
+		err ("%s: VFD is not open", __FUNCTION__);
+		retval = -EIO;
+	}
+	else {
+		context ->vfd_isopen = FALSE;
+		MOD_DEC_USE_COUNT;
+		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;
+
+	pipe = usb_sndintpipe (context ->dev,
+			context-> tx_endpoint ->bEndpointAddress);
+#ifdef KERNEL_2_5
+	interval = context ->tx_endpoint ->bInterval;
+#endif	/* Use 0 for 2.4 kernels */
+
+	usb_fill_int_urb (context ->tx_urb, context ->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);
+
+#ifdef KERNEL_2_5
+	retval =  usb_submit_urb (context ->tx_urb, GFP_KERNEL);
+#else
+	retval =  usb_submit_urb (context ->tx_urb);
+#endif
+	if (retval != SUCCESS) {
+		atomic_set (&(context ->tx.busy), 0);
+		err ("%s: error submitting urb (%d)", __FUNCTION__, 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)", __FUNCTION__, retval);
+	}
+
+	return retval;
+}
+
+/**
+ * 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", __FUNCTION__);
+		return -ENODEV;
+	}
+
+	LOCK_CONTEXT;
+
+	if (!context ->dev_present) {
+		err ("%s: no iMON device present", __FUNCTION__);
+		retval = -ENODEV;
+		goto exit;
+	}
+
+	if (n_bytes <= 0 || n_bytes > 32) {
+		err ("%s: invalid payload size", __FUNCTION__);
+		retval = -EINVAL;
+		goto exit;
+	}
+
+	copy_from_user (context ->tx.data_buf, buf, n_bytes);
+
+	/* 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 = seq = 0;
+
+	do {
+		memcpy (context ->usb_tx_buf, context ->tx.data_buf + offset, 7);
+		context ->usb_tx_buf [7] = (unsigned char) seq;
+
+		if ((retval = send_packet (context)) != SUCCESS) {
+
+			err ("%s: send packet failed for packet #%d", 
+					__FUNCTION__, seq/2);
+			goto exit;
+		}
+		else {
+			seq += 2;
+			offset += 7;
+		}
+
+	} while (offset < 35);
+
+	if (vfd_proto_6p) {
+
+		/* Send packet #6 */
+		memcpy (context ->usb_tx_buf, vfd_packet6, 7);
+		context ->usb_tx_buf [7] = (unsigned char) seq;
+		if ((retval = send_packet (context)) != SUCCESS)
+			err ("%s: send packet failed for packet #%d",
+					__FUNCTION__, seq/2);
+	}
+
+exit:
+	UNLOCK_CONTEXT;
+
+	return (retval == SUCCESS) ? n_bytes : retval;
+}
+
+/**
+ * Callback function for USB core API: transmit data
+ */
+#ifdef KERNEL_2_5
+static void usb_tx_callback (struct urb *urb, struct pt_regs *regs)
+#else
+static void usb_tx_callback (struct urb *urb)
+#endif
+{
+	struct imon_context *context;
+
+	if (!urb || !(context = (struct imon_context *) urb->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", __FUNCTION__);
+		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);
+
+#ifdef KERNEL_2_5
+	retval = usb_submit_urb (context ->rx_urb, GFP_KERNEL);
+#else
+	retval = usb_submit_urb (context ->rx_urb);
+#endif
+
+	if (retval) {
+		err ("%s: usb_submit_urb failed for ir_open (%d)", __FUNCTION__, retval);
+	}
+	else {
+		MOD_INC_USE_COUNT;
+		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", __FUNCTION__);
+		return;
+	}
+
+	LOCK_CONTEXT;
+
+	usb_unlink_urb (context ->rx_urb);
+	context ->ir_isopen = FALSE;
+	MOD_DEC_USE_COUNT;
+	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");
+	
+	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;
+
+
+	if (len != 8) {
+		warn ("%s: invalid incoming packet size (%d)", __FUNCTION__, len);
+		return;
+	}
+
+	chunk_num = buf [7];
+
+	if (chunk_num == 0xFF)
+		return;		/* filler frame, no data here */
+
+	/*
+	 * 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
+ */
+#ifdef KERNEL_2_5
+static void usb_rx_callback (struct urb *urb, struct pt_regs *regs)
+#else
+static void usb_rx_callback (struct urb *urb)
+#endif
+{
+	struct imon_context *context;
+
+	if (!urb || !(context = (struct imon_context *) urb->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",
+				 __FUNCTION__, urb ->status);
+			break;
+	}
+
+#ifdef KERNEL_2_5
+	usb_submit_urb (context ->rx_urb, GFP_KERNEL);
+#endif
+	return;
+}
+
+
+
+/**
+ * Callback function for USB core API: Probe
+ */
+#ifdef KERNEL_2_5
+static int imon_probe (struct usb_interface *interface,
+			const struct usb_device_id *id)
+#else
+static void * imon_probe (struct usb_device * dev, unsigned int intf,
+			const struct usb_device_id *id)
+#endif
+{
+#ifdef KERNEL_2_5
+	struct usb_device *dev = NULL;
+	struct usb_host_interface *iface_desc = NULL;
+#else
+	struct usb_interface *interface = NULL;
+	struct usb_interface_descriptor *iface_desc = NULL;
+	char name [10];
+	int subminor = 0;
+#endif
+	struct usb_endpoint_descriptor *rx_endpoint = NULL;
+	struct usb_endpoint_descriptor *tx_endpoint = NULL;
+	struct urb *rx_urb = NULL;
+	struct urb *tx_urb = NULL;
+	struct lirc_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 imon_context *context = NULL;
+	int i;
+
+	info ("%s: found IMON device", __FUNCTION__);
+
+#if !defined(KERNEL_2_5)
+	for (subminor = 0; subminor < MAX_DEVICES; ++subminor) {
+		if (minor_table [subminor] == NULL)
+			break;
+	}
+	if (subminor == MAX_DEVICES) {
+	
+		err ("%s: allowed number of devices already present", __FUNCTION__);
+		retval = -ENOMEM;
+		goto exit;
+	}
+#endif
+
+#ifdef KERNEL_2_5
+	dev = usb_get_dev (interface_to_usbdev (interface));
+	iface_desc = interface ->cur_altsetting;
+	num_endpoints = iface_desc ->desc.bNumEndpoints;
+#else
+	interface = &dev ->actconfig ->interface [intf];
+	iface_desc = &interface ->altsetting [interface ->act_altsetting];
+	num_endpoints = iface_desc ->bNumEndpoints;
+#endif
+
+	/*
+	 * Scan the endpoint list and set:
+	 * 	first input endpoint = IR endpoint
+	 * 	first output endpoint = VFD endpoint
+	 */
+
+	ir_ep_found = 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;
+#ifdef KERNEL_2_5
+		ep = &iface_desc ->endpoint [i].desc;
+#else
+		ep = &iface_desc ->endpoint [i];
+#endif
+		ep_dir = ep ->bEndpointAddress & USB_ENDPOINT_DIR_MASK;
+		ep_type = ep ->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
+
+		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", __FUNCTION__);
+
+		}
+		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", __FUNCTION__);
+		}
+		else
+			;
+
+	}
+
+	/* Input endpoint is mandatory */
+	if (!ir_ep_found) {
+
+		err ("%s: no valid input (IR) endpoint found.", __FUNCTION__);
+		retval = -ENODEV;
+		goto exit;
+	}
+
+	/* Determine if VFD requires 6 packets */
+	if (vfd_ep_found) {
+
+		unsigned short vendor_id;
+		unsigned short *id_list_item;
+
+		vendor_id = dev ->descriptor.idVendor;
+		id_list_item = vfd_proto_6p_vendor_list;
+		while (*id_list_item) {
+			if (*id_list_item++ == vendor_id) {
+				vfd_proto_6p = TRUE;
+				break;
+			}
+		}
+
+		if (debug) info ("vfd_proto_6p: %d", vfd_proto_6p);
+	}
+
+
+	/* Allocate memory */
+
+	alloc_status = SUCCESS;
+
+	if (!(context = kmalloc (sizeof(struct imon_context), GFP_KERNEL))) {
+		err ("%s: kmalloc failed for context", __FUNCTION__);
+		alloc_status = 1;
+	}
+	else if (!(plugin = kmalloc (sizeof(struct lirc_plugin), GFP_KERNEL))) {
+		err ("%s: kmalloc failed for lirc_plugin", __FUNCTION__);
+		alloc_status = 2;
+	}
+	else if (!(rbuf = kmalloc (sizeof(struct lirc_buffer), GFP_KERNEL))) {
+		err ("%s: kmalloc failed for lirc_buffer", __FUNCTION__);
+		alloc_status = 3;
+	}
+	else if (lirc_buffer_init (rbuf, BUF_CHUNK_SIZE, BUF_SIZE)) {
+		err ("%s: lirc_buffer_init failed", __FUNCTION__);
+		alloc_status = 4;
+	}
+#ifdef KERNEL_2_5
+	else if (!(rx_urb = usb_alloc_urb (0, GFP_KERNEL))) {
+#else
+	else if (!(rx_urb = usb_alloc_urb (0))) {
+#endif
+		err ("%s: usb_alloc_urb failed for IR urb", __FUNCTION__);
+		alloc_status = 5;
+	}
+#ifdef KERNEL_2_5
+	else if (vfd_ep_found && !(tx_urb = usb_alloc_urb (0, GFP_KERNEL))) {
+#else
+	else if (vfd_ep_found && !(tx_urb = usb_alloc_urb (0))) {
+#endif
+		err ("%s: usb_alloc_urb failed for VFD urb", __FUNCTION__);
+		alloc_status = 6;
+	}
+	else {
+
+		/* clear all members of imon_context and lirc_plugin */
+		memset (context, 0, sizeof (struct imon_context));
+		init_MUTEX (&context ->sem);
+
+		memset (plugin, 0, sizeof (struct lirc_plugin));
+
+		strcpy (plugin ->name, MOD_NAME);
+		plugin ->minor = -1;
+		plugin ->code_length = sizeof (lirc_t) * 8;
+		plugin ->sample_rate = 0;
+		plugin ->features = LIRC_CAN_REC_MODE2;
+		plugin ->data = context;
+		plugin ->rbuf = rbuf;
+		plugin ->set_use_inc = ir_open;
+		plugin ->set_use_dec = ir_close;
+		plugin->owner = THIS_MODULE;
+
+		LOCK_CONTEXT;
+
+		if ((lirc_minor = lirc_register_plugin (plugin)) < 0) {
+			err ("%s: lirc_register_plugin failed", __FUNCTION__);
+			alloc_status = 7;
+			UNLOCK_CONTEXT;
+		}
+		else
+			info ("%s: Registered iMON plugin (minor:%d)", 
+				__FUNCTION__, lirc_minor);
+	}
+
+	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 ->vfd_supported = TRUE;
+		context ->tx_endpoint = tx_endpoint;
+		context ->tx_urb = tx_urb;
+	}
+	context ->plugin = plugin;
+
+#ifdef KERNEL_2_5
+	usb_set_intfdata (interface, context);
+#else
+	minor_table [subminor] = context;
+	context ->subminor = subminor;
+#endif
+
+	if (vfd_ep_found) {
+
+		if (debug) info ("Registering VFD with devfs");
+#ifdef KERNEL_2_5
+		if (usb_register_dev (interface, &imon_class)) {
+
+			// Not a fatal error, so ignore
+			info ("%s: could not get a minor number for VFD", 
+				__FUNCTION__);
+		}
+#else
+		sprintf (name, DEVFS_NAME, subminor);
+		if (!(context ->devfs = devfs_register (usb_devfs_handle, name, 
+					DEVFS_FL_DEFAULT,
+					USB_MAJOR, VFD_MINOR_BASE + subminor,
+					DEVFS_MODE, &vfd_fops, NULL))) {
+
+			// not a fatal error so ignore
+			info ("%s: devfs register failed for VFD",
+					__FUNCTION__);
+		}
+#endif
+	}
+
+	info ("%s: iMON device on usb<%d:%d> initialized",
+			__FUNCTION__, dev ->bus ->busnum, dev ->devnum);
+
+	UNLOCK_CONTEXT;
+exit:
+#ifdef KERNEL_2_5
+	return retval;
+#else
+	return (retval == SUCCESS) ? context : NULL;
+#endif
+}
+
+/**
+ * Callback function for USB core API: disonnect
+ */
+#ifdef KERNEL_2_5
+static void imon_disconnect (struct usb_interface *interface)
+#else
+static void imon_disconnect (struct usb_device *dev, void *data)
+#endif
+{
+	struct imon_context *context;
+
+	/* prevent races with ir_open()/vfd_open() */
+	down (&disconnect_sem);
+
+#ifdef KERNEL_2_5
+	context = usb_get_intfdata (interface);
+#else
+	context = (struct imon_context *)data;
+#endif
+	LOCK_CONTEXT;
+
+	info ("%s: iMON device disconnected", __FUNCTION__);
+
+#ifdef KERNEL_2_5
+	usb_set_intfdata (interface, NULL);
+#else
+	minor_table [context ->subminor] = NULL;
+#endif
+	context ->dev_present = FALSE;
+
+	/* Stop reception */
+	usb_unlink_urb (context ->rx_urb);
+
+	/* Abort ongoing write */
+	if (atomic_read (&context ->tx.busy)) {
+
+		usb_unlink_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) {
+#ifdef KERNEL_2_5
+		usb_deregister_dev (interface, &imon_class);
+#else
+		if (context ->devfs)
+			devfs_unregister (context ->devfs);
+#endif
+	}
+
+	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);
+
+	if ((rc = usb_register (&imon_driver)) < 0) {
+		err ("%s: usb register failed (%d)", __FUNCTION__, rc);
+		return -ENODEV;
+	}
+	return SUCCESS;
+}
+
+static void __exit imon_exit (void)
+{
+	usb_deregister (&imon_driver);
+	info ("module removed. Goodbye!");
+}
+
+
+module_init (imon_init);
+module_exit (imon_exit);
+
+#if !defined(KERNEL_2_5)
+EXPORT_NO_SYMBOLS;
+#endif
diff -urN --exclude='*.orig' --exclude='*.rej' linux-2.6.11-mm4/drivers/char/lirc/lirc_it87.c linux-2.6.11-no1/drivers/char/lirc/lirc_it87.c
--- linux-2.6.11-mm4/drivers/char/lirc/lirc_it87.c	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.11-no1/drivers/char/lirc/lirc_it87.c	2005-03-30 00:12:39.525811993 -0500
@@ -0,0 +1,947 @@
+/*
+ * LIRC driver for ITE IT8712/IT8705 CIR port
+ *
+ * Copyright (C) 2001 Hans-Günter Lütke Uphues <hg_lu@web.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * 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
+ */
+
+
+#include <linux/version.h>
+#include <linux/module.h>
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+ 
+#include <linux/config.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 <asm/segment.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/fcntl.h>
+
+#include <linux/timer.h>
+
+#include "linux/lirc.h"
+#include "lirc_dev.h"
+#include "linux/kcompat.h"
+
+#include "lirc_it87.h"
+
+static unsigned long it87_bits_in_byte_out = 0;
+static unsigned long it87_send_counter = 0;
+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 = 0;
+#define dprintk(fmt, args...)                                     \
+	do{                                                       \
+		if(debug) printk(KERN_DEBUG LIRC_DRIVER_NAME ": " \
+                                 fmt, ## args);                   \
+	}while(0)
+
+static int io = IT87_CIR_DEFAULT_IOBASE;
+static int irq = IT87_CIR_DEFAULT_IRQ;
+static unsigned char it87_freq = 38; /* kHz */
+/* receiver demodulator default: off */
+static int it87_enable_demodulator = 0;
+
+static spinlock_t timer_lock = SPIN_LOCK_UNLOCKED;
+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 = 0;
+
+static DECLARE_WAIT_QUEUE_HEAD(lirc_read_queue);
+
+static spinlock_t hardware_lock = SPIN_LOCK_UNLOCKED;
+static spinlock_t dev_lock = SPIN_LOCK_UNLOCKED;
+
+static lirc_t rx_buf[RBUF_LEN]; unsigned int rx_tail = 0, rx_head = 0;
+static lirc_t 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,
+				 struct pt_regs * regs);
+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);
+int init_module(void);
+void cleanup_module(void);
+
+
+/* SECTION: Communication with user-space */
+
+static int lirc_open(struct inode * inode,
+		     struct file * file)
+{
+	spin_lock(&dev_lock);
+	if (MOD_IN_USE) {
+		spin_unlock(&dev_lock);
+		return -EBUSY;
+	}
+	MOD_INC_USE_COUNT;
+	spin_unlock(&dev_lock);
+	return 0;
+}
+
+
+static int lirc_close(struct inode * inode,
+		      struct file *file)
+{
+	MOD_DEC_USE_COUNT;
+	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;
+		}
+		
+		retval=verify_area(VERIFY_WRITE,(void *) buf+n,
+				   sizeof(lirc_t));
+		if (retval)
+		{
+			return retval;
+		}
+		copy_to_user((void *) buf+n,(void *) (rx_buf+rx_head),
+			     sizeof(lirc_t));
+		rx_head=(rx_head+1)&(RBUF_LEN-1);
+		n+=sizeof(lirc_t);
+	}
+	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;
+	int retval;
+
+        if(n%sizeof(lirc_t) || (n/sizeof(lirc_t)) > WBUF_LEN)
+		return(-EINVAL);
+	retval = verify_area(VERIFY_READ, buf, n);
+	if (retval)
+		return retval;
+	copy_from_user(tx_buf, buf, n);
+	i = 0;
+	n/=sizeof(lirc_t);
+	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;
+
+	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;
+		{
+			unsigned long hw_flags;
+
+			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;
+	lirc_t 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)
+{
+#if WE_DONT_USE_LOCAL_OPEN_CLOSE
+	MOD_INC_USE_COUNT;
+#endif
+       return 0;
+}
+
+static void set_use_dec(void* data)
+{
+#if WE_DONT_USE_LOCAL_OPEN_CLOSE
+	MOD_DEC_USE_COUNT;
+#endif
+}
+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,
+       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) 
+{
+	/* 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) {
+		/* 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,
+				 struct pt_regs * regs)
+{
+	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;
+
+	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);
+		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 = 0;
+
+	dprintk("%s: len=%ld, sb=%d\n", __FUNCTION__, 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);
+			{
+				unsigned long hw_flags;
+
+				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 */
+	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);
+	/* 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)
+{
+	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 (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;
+	
+	{
+		unsigned long hw_flags;
+
+		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 /*SA_INTERRUPT*/,
+			     LIRC_DRIVER_NAME, NULL);
+	if (retval < 0) {
+		printk(KERN_ERR LIRC_DRIVER_NAME
+		       ": IRQ %d already in use.\n",
+		       irq);
+		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
+
+int init_module(void)
+{
+	int retval;
+	
+	retval=init_chrdev();
+	if(retval < 0)
+		return retval;
+	retval = init_lirc_it87();
+	if (retval) {
+		drop_chrdev();
+		return retval;
+	}
+	return 0;
+}
+
+
+void cleanup_module(void)
+{
+	drop_hardware();
+	drop_chrdev();
+	drop_port();
+	printk(KERN_INFO LIRC_DRIVER_NAME ": Uninstalled.\n");
+}
+
+MODULE_DESCRIPTION("LIRC driver for ITE IT8712/IT8705 CIR port");
+MODULE_AUTHOR("Hans-Günter Lütke Uphues");
+MODULE_LICENSE("GPL");
+
+module_param(io, int, 0444);
+MODULE_PARM_DESC(io, "I/O base address (default: 0x310)");
+
+module_param(irq, int, 0444);
+MODULE_PARM_DESC(irq, "Interrupt (1,3-12) (default: 7)");
+
+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");
+
+EXPORT_NO_SYMBOLS;
+
+#endif /* MODULE */
+
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff -urN --exclude='*.orig' --exclude='*.rej' linux-2.6.11-mm4/drivers/char/lirc/lirc_it87.h linux-2.6.11-no1/drivers/char/lirc/lirc_it87.h
--- linux-2.6.11-mm4/drivers/char/lirc/lirc_it87.h	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.11-no1/drivers/char/lirc/lirc_it87.h	2005-03-30 00:11:50.291637579 -0500
@@ -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 -urN --exclude='*.orig' --exclude='*.rej' linux-2.6.11-mm4/drivers/char/lirc/lirc_mceusb.c linux-2.6.11-no1/drivers/char/lirc/lirc_mceusb.c
--- linux-2.6.11-mm4/drivers/char/lirc/lirc_mceusb.c	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.11-no1/drivers/char/lirc/lirc_mceusb.c	2005-03-30 00:12:39.527811716 -0500
@@ -0,0 +1,1020 @@
+/*
+ * 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/config.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>
+#ifdef KERNEL_2_5
+#include <linux/completion.h>
+#include <asm/uaccess.h>
+#else
+#include <linux/spinlock.h>
+#include <linux/list.h>
+#include <linux/fcntl.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#endif
+
+#ifdef CONFIG_USB_DEBUG
+	static int debug = 1;
+#else
+	static int debug = 0;
+#endif
+
+#include "linux/kcompat.h"
+#include "linux/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 we have */
+	char            num_bulk_in;			/* number of bulk in endpoints we have */
+	char            num_bulk_out;			/* number of bulk out endpoints we have */
+
+	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 the 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 the 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 semaphore   sem;				/* locks this structure */
+	
+	int			present;		/* if the device is not disconnected */		
+
+	struct lirc_plugin* plugin;
+	
+	lirc_t lircdata[256];          			/* place to store values until lirc processes them */
+	int    lircidx;                			/* current index */
+	int    lirccnt;                			/* remaining values */
+	
+	int    usb_valid_bytes_in_bulk_buffer;		/* leftover data from a previous read */
+	int    mce_bytes_left_in_packet;		/* for packets split across multiple reads */
+	
+	/* Value to hold the last received space; 0 if last value
+	 * received was a pulse
+	 */
+	int    last_space;
+	
+#ifdef KERNEL_2_5
+	dma_addr_t dma_in;
+	dma_addr_t dma_out;
+#endif
+};
+
+#define MCE_TIME_UNIT 50
+
+/* driver api */
+#ifdef KERNEL_2_5
+static int mceusb_probe		(struct usb_interface *interface, const struct usb_device_id *id);
+static void mceusb_disconnect	(struct usb_interface *interface);
+static void mceusb_write_bulk_callback	(struct urb *urb, struct pt_regs *regs);
+#else
+static void * mceusb_probe	(struct usb_device *dev, unsigned int ifnum, const struct usb_device_id *id);
+static void mceusb_disconnect	(struct usb_device *dev, void *ptr);
+static void mceusb_write_bulk_callback	(struct urb *urb);
+#endif
+
+/* 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 = {
+	.owner =	THIS_MODULE,
+	.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;
+#ifdef KERNEL_2_5
+	usb_buffer_free(dev->udev, dev->bulk_in_size, dev->bulk_in_buffer, dev->dma_in);
+	usb_buffer_free(dev->udev, dev->bulk_out_size, dev->bulk_out_buffer, dev->dma_out);
+#else
+	if (dev->bulk_in_buffer != NULL)
+		kfree (dev->bulk_in_buffer);
+	if (dev->bulk_out_buffer != NULL)
+		kfree (dev->bulk_out_buffer);
+#endif
+	if (dev->write_urb != NULL)
+		usb_free_urb (dev->write_urb);
+	kfree (dev);
+}
+
+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)
+{
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static void set_use_dec(void* data)
+{
+	MOD_DEC_USE_COUNT;
+}
+
+/*
+ * msir_fetch_more_data
+ *
+ * The goal here is to read in more remote codes from the remote. In
+ * the event that the remote isn't sending us anything, the caller
+ * will block until a key is pressed (i.e. this performs phys read,
+ * filtering, and queueing of data) unless dont_block is set to 1; in
+ * this situation, it will perform a few reads and will exit out if it
+ * does not see any appropriate data
+ *
+ * dev->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(lirc_t)) - dev->lirccnt;
+	int partial, this_read = 0;
+	int bulkidx = 0;
+	int bytes_left_in_packet = 0;
+	signed char* signedp = (signed char*)dev->bulk_in_buffer;
+	
+	if( 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 = partial = 
+				dev->usb_valid_bytes_in_bulk_buffer;
+			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 the lirc_t 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;
+
+	down( &dev->sem );
+
+	/* verify device still present */
+	if( dev->udev == NULL )
+	{
+		up( &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 ) {
+			up( &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;
+        
+		up( &dev->sem );
+		return 0;
+	}
+
+	up( &dev->sem );
+	return -ENODATA;
+}
+
+/**
+ *	mceusb_write_bulk_callback
+ */
+#ifdef KERNEL_2_5 
+static void mceusb_write_bulk_callback (struct urb *urb, struct pt_regs *regs)
+#else
+static void mceusb_write_bulk_callback (struct urb *urb)
+#endif
+{
+	struct 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.
+ */
+#ifdef KERNEL_2_5 
+static int mceusb_probe(struct usb_interface *interface, const struct usb_device_id *id)
+{
+	struct usb_device *udev = interface_to_usbdev(interface);
+	struct usb_host_interface *iface_desc;
+#else
+static void * mceusb_probe(struct usb_device *udev, unsigned int ifnum,
+			   const struct usb_device_id *id)
+{
+	struct usb_interface *interface = &udev->actconfig->interface[ifnum];
+	struct usb_interface_descriptor *iface_desc;	
+#endif
+	struct 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;
+	
+	/* See if the device offered us matches what we can accept */
+	if ((udev->descriptor.idVendor != USB_MCEUSB_VENDOR_ID) ||
+	    (udev->descriptor.idProduct != USB_MCEUSB_PRODUCT_ID)) {
+	    	dprintk("Wrong Vendor/Product IDs");
+#ifdef KERNEL_2_5
+		return -ENODEV;
+#else
+		return NULL;
+#endif
+	}
+
+	/* 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");
+#ifdef KERNEL_2_5
+		retval = -ENOMEM;
+#endif
+		goto error;
+	}
+	minor_table[minor] = dev;
+	
+	memset (dev, 0x00, sizeof (*dev));
+	init_MUTEX (&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 */
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,4)
+	iface_desc = interface->cur_altsetting;
+#else
+	iface_desc = &interface->altsetting[0];
+#endif
+
+#ifdef KERNEL_2_5
+	for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+		endpoint = &iface_desc->endpoint[i].desc;
+#else
+	for (i = 0; i < iface_desc->bNumEndpoints; ++i) {
+		endpoint = &iface_desc->endpoint[i];
+#endif
+		if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) &&
+		    ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
+		     USB_ENDPOINT_XFER_BULK)) {
+			dprintk("we found a bulk in endpoint");
+			buffer_size = endpoint->wMaxPacketSize;
+			dev->bulk_in_size = buffer_size;
+			dev->bulk_in_endpointAddr = endpoint->bEndpointAddress;
+#ifdef KERNEL_2_5
+			dev->bulk_in_buffer = usb_buffer_alloc
+				(udev, buffer_size, SLAB_ATOMIC, &dev->dma_in);
+#else
+			dev->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL);
+#endif
+			if (!dev->bulk_in_buffer) {
+				err("Couldn't allocate bulk_in_buffer");
+				goto error;
+			}
+		}
+		
+		if (((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == 0x00) &&
+		    ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
+		     USB_ENDPOINT_XFER_BULK)) {
+			dprintk("we found a bulk out endpoint");
+#ifdef KERNEL_2_5
+			dev->write_urb = usb_alloc_urb(0, GFP_KERNEL);
+#else
+			dev->write_urb = usb_alloc_urb(0);
+#endif
+			if (!dev->write_urb) {
+				err("No free urbs available");
+				goto error;
+			}
+			buffer_size = endpoint->wMaxPacketSize;
+			dev->bulk_out_size = buffer_size;
+			dev->bulk_out_endpointAddr = endpoint->bEndpointAddress;
+#ifdef KERNEL_2_5
+			dev->bulk_out_buffer = usb_buffer_alloc(udev, buffer_size, SLAB_ATOMIC, &dev->dma_out);
+#else
+			dev->bulk_out_buffer = kmalloc (buffer_size, GFP_KERNEL);
+#endif
+			if (!dev->bulk_out_buffer) {
+				err("Couldn't allocate bulk_out_buffer");
+				goto error;
+			}
+#ifdef KERNEL_2_5
+			usb_fill_bulk_urb(dev->write_urb, udev, 
+				      usb_sndbulkpipe
+				      (udev, endpoint->bEndpointAddress),
+				      dev->bulk_out_buffer, buffer_size,
+				      mceusb_write_bulk_callback, dev);
+#else
+			FILL_BULK_URB(dev->write_urb, udev,
+				      usb_sndbulkpipe
+				      (udev, endpoint->bEndpointAddress),
+				      dev->bulk_out_buffer, buffer_size,
+				      mceusb_write_bulk_callback, dev);
+#endif 
+		}
+	}
+
+	if (!(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 */
+	if(!(plugin = kmalloc(sizeof(struct lirc_plugin), GFP_KERNEL))) {
+		err("out of memory");
+		goto error;
+	}
+	memset( plugin, 0, sizeof(struct lirc_plugin) );
+
+	if(!(rbuf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL))) {
+		err("out of memory");
+		kfree( plugin );
+		goto error;
+	}
+    
+	/* the lirc_atiusb module doesn't memset rbuf here ... ? */
+	if( lirc_buffer_init( rbuf, sizeof(lirc_t), 128)) {
+		err("out of memory");
+		kfree( plugin );
+		kfree( rbuf );
+		goto error;
+	}
+
+	strcpy(plugin->name, DRIVER_NAME " ");
+	plugin->minor       = minor;
+	plugin->code_length = sizeof(lirc_t) * 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->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
+	 */
+	{
+		char junk[64];
+		int partial = 0, retval, i;
+		for( i = 0; i < 40; i++ )
+		{
+			retval = usb_bulk_msg
+				(udev, usb_rcvbulkpipe
+				 (udev, dev->bulk_in_endpointAddr),
+				 junk, 64,
+				 &partial, HZ*10);
+		}
+	}
+    
+	msir_cleanup( dev );
+	mceusb_setup( udev );
+	
+#ifdef KERNEL_2_5
+	/* we can register the device now, as it is ready */
+	usb_set_intfdata (interface, dev);
+#endif	
+	/* 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);
+#ifdef KERNEL_2_5
+	return 0;
+#else
+	return dev;
+#endif
+ error:
+	mceusb_delete (dev);
+	dev = NULL;
+	dprintk("%s: retval = %x", __func__, retval);
+	up (&minor_table_mutex);
+#ifdef KERNEL_2_5
+	return retval;
+#else
+	return NULL;
+#endif
+}
+
+/**
+ *	mceusb_disconnect
+ *
+ *	Called by the usb core when the device is removed from the system.
+ *
+ *	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.
+ */
+#ifdef KERNEL_2_5 
+static void mceusb_disconnect(struct usb_interface *interface)
+#else
+static void mceusb_disconnect(struct usb_device *udev, void *ptr)
+#endif
+{
+	struct usb_skel *dev;
+	int minor;
+#ifdef KERNEL_2_5
+	dev = usb_get_intfdata (interface);
+	usb_set_intfdata (interface, NULL);
+#else
+	dev = (struct usb_skel *)ptr;
+#endif
+	
+	down (&minor_table_mutex);
+	down (&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 );
+#ifdef KERNEL_2_5
+	/* terminate an ongoing write */
+	if (atomic_read (&dev->write_busy)) {
+		usb_unlink_urb (dev->write_urb);
+		wait_for_completion (&dev->write_finished);
+	}
+
+	/* prevent device read, write and ioctl */
+	dev->present = 0;
+#endif
+	
+	mceusb_delete (dev);
+	
+	info("Microsoft IR Transceiver #%d now disconnected", minor);
+	up (&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);
+#ifdef KERNEL_2_5	
+	if ( result ) {
+#else
+	if ( result < 0 ) {
+#endif
+		err("usb_register failed for the " DRIVER_NAME " driver. error number %d",result);
+#ifdef KERNEL_2_5
+		return result;
+#else
+		return -1;
+#endif
+	}
+
+	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);
+}
+
+ 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");
+
+EXPORT_NO_SYMBOLS;
diff -urN --exclude='*.orig' --exclude='*.rej' linux-2.6.11-mm4/drivers/char/lirc/lirc_parallel.c linux-2.6.11-no1/drivers/char/lirc/lirc_parallel.c
--- linux-2.6.11-mm4/drivers/char/lirc/lirc_parallel.c	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.11-no1/drivers/char/lirc/lirc_parallel.c	2005-03-30 00:12:39.528811577 -0500
@@ -0,0 +1,783 @@
+/*      $Id: lirc_parallel.c,v 5.30 2005/03/12 11:15:36 lirc Exp $      */
+
+/****************************************************************************
+ ** 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        ***********************
+ ***********************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <linux/version.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 2, 18)
+#error "**********************************************************"
+#error " Sorry, this driver needs kernel version 2.2.18 or higher "
+#error "**********************************************************"
+#endif
+
+#include <linux/config.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/config.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 <asm/io.h>
+#include <asm/signal.h>
+#include <asm/irq.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 0)
+#include <asm/div64.h>
+#endif
+
+#include <asm/uaccess.h>
+#include <linux/poll.h>
+#include <linux/parport.h>
+
+#include "linux/lirc.h"
+#include "linux/kcompat.h"
+#include "lirc_dev.h"
+
+#include "lirc_parallel.h"
+
+#define LIRC_DRIVER_NAME "lirc_parallel"
+
+/***********************************************************************
+ *************************   Globale Variablen   ***********************
+ ***********************************************************************/
+
+static int debug = 0;
+unsigned int irq = LIRC_IRQ;
+unsigned int io = LIRC_PORT;
+#ifdef LIRC_TIMER
+unsigned int timer = 0;
+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 lirc_t wbuf[WBUF_SIZE];
+static lirc_t rbuf[RBUF_SIZE];
+
+DECLARE_WAIT_QUEUE_HEAD(lirc_wait);
+
+unsigned int rptr=0,wptr=0;
+unsigned int lost_irqs=0;
+int is_open=0;
+
+struct parport *pport;
+struct pardevice *ppdevice;
+int is_claimed=0;
+
+/***********************************************************************
+ *************************   Interne Funktionen  ***********************
+ ***********************************************************************/
+
+static unsigned int __inline__ 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 void __inline__ 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 unsigned int __inline__ lirc_get_timer(void)
+{
+	return(in(LIRC_PORT_TIMER)&LIRC_PORT_TIMER_BIT);
+}
+
+static unsigned int __inline__  lirc_get_signal(void)
+{
+	return(in(LIRC_PORT_SIGNAL)&LIRC_PORT_SIGNAL_BIT);
+}
+
+static void __inline__ lirc_on(void)
+{
+	out(LIRC_PORT_DATA,LIRC_PORT_DATA_BIT);
+}
+
+static void __inline__ 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(lirc_t 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(int i,void *blah,struct pt_regs * regs)
+{
+	struct timeval tv;
+	static struct timeval lasttv;
+	static int init=0;
+	long signal;
+	lirc_t data;
+	unsigned int level,newlevel;
+	unsigned int timeout;
+
+	if(!MOD_IN_USE)
+		return;
+
+	if(!is_claimed)
+	{
+		return;
+	}
+
+	/* disable interrupt */
+	/*
+	  disable_irq(irq);
+	  out(LIRC_PORT_IRQ,in(LIRC_PORT_IRQ)&(~LP_PINTEN));
+	*/
+	if(in(1)&LP_PSELECD)
+	{
+		return;
+	}
+
+#ifdef LIRC_TIMER
+	if(init)
+	{
+		do_gettimeofday(&tv);
+		
+	        signal=tv.tv_sec-lasttv.tv_sec;
+		if(signal>15)
+		{
+			data=PULSE_MASK;  /* really long time */
+		}
+		else
+		{
+			data=(lirc_t) (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 || (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 */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 0)
+		unsigned long long helper;
+		
+		helper = ((unsigned long long) signal)*1000000;
+		do_div(helper, timer);
+		signal = (long) helper;
+#else
+		signal=(long) ((((double) signal)*1000000)/timer);
+#endif
+
+		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;
+	int count=0;
+	DECLARE_WAITQUEUE(wait, current);
+	
+	if(n%sizeof(lirc_t)) return(-EINVAL);
+	
+	result=verify_area(VERIFY_WRITE,buf,n);
+	if(result) return(result);
+	
+	add_wait_queue(&lirc_wait,&wait);
+	set_current_state(TASK_INTERRUPTIBLE);
+	while(count<n)
+	{
+		if(rptr!=wptr)
+		{
+			copy_to_user(buf+count,(char *) &rbuf[rptr],
+				     sizeof(lirc_t));
+			rptr=(rptr+1)&(RBUF_SIZE-1);
+			count+=sizeof(lirc_t);
+		}
+		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 result,count;
+	unsigned int i;
+	unsigned int level,newlevel;
+	unsigned long flags;
+	lirc_t counttimer;
+	
+	if(!is_claimed)
+	{
+		return(-EBUSY);
+	}
+	if(n%sizeof(lirc_t)) return(-EINVAL);
+	result=verify_area(VERIFY_READ,buf,n);
+	if(result) return(result);
+	
+	count=n/sizeof(lirc_t);
+	
+	if(count>WBUF_SIZE || count%2==0) return(-EINVAL);
+	
+	copy_from_user(wbuf,buf,n);
+	
+#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++)
+	{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 0)
+		unsigned long long helper;
+		
+		helper = ((unsigned long long) wbuf[i])*timer;
+		do_div(helper, 1000000);
+		wbuf[i] = (lirc_t) helper;
+#else
+		wbuf[i]=(lirc_t) (((double) wbuf[i])*timer/1000000);
+#endif
+	}
+	
+	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(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(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 extarnal 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_SEND_PULSE|LIRC_CAN_REC_MODE2,mode;
+	
+	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;
+	default:
+		return(-ENOIOCTLCMD);
+	}
+	return(0);
+}
+
+static int lirc_open(struct inode* node,struct file* filep)
+{
+	if(MOD_IN_USE)
+	{
+		return(-EBUSY);
+	}
+	if(!lirc_claim())
+	{
+		return(-EBUSY);
+	}
+	parport_enable_irq(pport);
+
+	/* init read ptr */
+	rptr=wptr=0;
+	lost_irqs=0;
+
+	MOD_INC_USE_COUNT;
+	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;
+	MOD_DEC_USE_COUNT;
+	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)
+{
+#if WE_DONT_USE_LOCAL_OPEN_CLOSE
+	MOD_INC_USE_COUNT;
+#endif
+	return 0;
+}
+
+static void set_use_dec(void* data)
+{
+#if WE_DONT_USE_LOCAL_OPEN_CLOSE
+	MOD_DEC_USE_COUNT;
+#endif
+}
+
+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,
+       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);
+	/* 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)
+{
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 3)
+	pport=parport_find_base(io);
+#else
+	pport=parport_enumerate();
+	while(pport!=NULL)
+	{
+		if(pport->base==io)
+		{
+			break;
+		}
+		pport=pport->next;
+	}
+#endif
+	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);
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 3)
+	parport_put_port(pport);
+#endif
+	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,LIRC_PORT_DATA_BIT);
+	}
+	
+	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:
+	if ((plugin.minor = lirc_register_plugin(&plugin)) < 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(debug, bool, 0644);
+MODULE_PARM_DESC(debug, "Enable debugging messages");
+
+EXPORT_NO_SYMBOLS;
+
+#endif /* MODULE */
diff -urN --exclude='*.orig' --exclude='*.rej' linux-2.6.11-mm4/drivers/char/lirc/lirc_parallel.h linux-2.6.11-no1/drivers/char/lirc/lirc_parallel.h
--- linux-2.6.11-mm4/drivers/char/lirc/lirc_parallel.h	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.11-no1/drivers/char/lirc/lirc_parallel.h	2005-03-30 00:11:50.299636470 -0500
@@ -0,0 +1,24 @@
+/*      $Id: lirc_parallel.h,v 5.1 1999/07/21 18:23:37 columbus Exp $      */
+
+#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_DATA_BIT               0x01    /* 1st bit */
+#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 */
+
+#endif
diff -urN --exclude='*.orig' --exclude='*.rej' linux-2.6.11-mm4/drivers/char/lirc/lirc_sasem.c linux-2.6.11-no1/drivers/char/lirc/lirc_sasem.c
--- linux-2.6.11-mm4/drivers/char/lirc/lirc_sasem.c	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.11-no1/drivers/char/lirc/lirc_sasem.c	2005-03-30 00:12:39.555807834 -0500
@@ -0,0 +1,831 @@
+/*      $Id: lirc_sasem.c,v 1.7 2005/03/29 17:51:45 lirc Exp $      */
+
+/* lirc_sasem.c - USB remote support for LIRC
+ * Version 0.3  [beta status]
+ *
+ * Copyright (C) 2004 Oliver Stabel <oliver.stabel@gmx.de>
+ *
+ * This driver was derived from:
+ *   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>
+ *
+ * TODO
+ *	- check USB Minor allocation
+ *	- param to enable/disable LIRC communication (??)
+ *
+ */
+
+/*
+ *  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/module.h>
+#include <linux/kmod.h>
+#include <linux/kernel.h>
+#include <linux/usb.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <asm/uaccess.h>
+#include <asm/atomic.h>
+#include <linux/poll.h>
+#include <linux/version.h>
+#include <linux/devfs_fs_kernel.h>
+
+#include "lirc_sasem.h"
+#include "linux/lirc.h"
+#include "lirc_dev.h"
+
+//#define dbg(format, arg...) printk(KERN_DEBUG "%s: " format "\n" , __FILE__ , ## arg)
+
+MODULE_AUTHOR( DRIVER_AUTHOR );
+MODULE_DESCRIPTION( DRIVER_DESC );
+MODULE_LICENSE("GPL");
+
+static int debug = 0;
+
+MODULE_PARM(debug, "i");
+MODULE_PARM_DESC(debug, "enable debug = 1, disable = 0 (default)");
+
+static struct usb_device_id SasemID [] = {
+	{ USB_DEVICE(0x11ba, 0x0101) },
+	{ }
+};
+
+MODULE_DEVICE_TABLE (usb, SasemID);
+
+static struct file_operations SasemFileOps =
+{
+	owner:      THIS_MODULE,
+	read:       SasemFSRead,
+	write:      SasemFSWrite,
+	ioctl:      SasemFSIoctl,
+	open:       SasemFSOpen,
+	release:    SasemFSRelease,
+	poll:       SasemFSPoll,
+};
+
+#ifdef KERNEL_2_5
+static struct usb_class_driver SasemClass =
+{
+	name:		"usb/lcd",
+	fops:		&SasemFileOps,
+	mode:		S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH,
+	minor_base:	SASEM_MINOR,
+};
+#endif
+
+static struct usb_driver SasemDriver =
+{
+	owner:		THIS_MODULE,
+	name:		"Sasem",
+	probe:          SasemProbe,
+	disconnect:     SasemDisconnect,
+#ifndef KERNEL_2_5
+	fops:           &SasemFileOps,
+	minor:		SASEM_MINOR,
+#endif
+	id_table:       SasemID,
+};
+
+static struct SasemDevice *SasemDevice = NULL;
+
+static int __init SasemInit (void)
+{
+	printk(BANNER);
+
+	if (usb_register(&SasemDriver)) {
+		err("USB registration failed");
+		return -ENOSYS;
+	}
+
+	return 0;
+}
+
+static void __exit SasemExit (void)
+{
+	usb_deregister (&SasemDriver);
+}
+
+module_init (SasemInit);
+module_exit (SasemExit);
+
+#ifndef KERNEL_2_5
+static void * SasemProbe(struct usb_device *Device,
+			   unsigned InterfaceNum,
+			   const struct usb_device_id *ID)
+{
+	struct usb_interface_descriptor *lCurrentInterfaceDescriptor;
+#else
+static int SasemProbe(struct usb_interface *Int,
+		      const struct usb_device_id *ID)
+{
+	struct usb_device *Device = NULL;
+	struct usb_host_interface *iface_host = NULL;
+#endif
+	struct SasemDevice *lSasemDevice = NULL;
+	struct usb_endpoint_descriptor *lEndpoint, *lEndpoint2;
+	int lPipe;
+	int lDevnum;
+	struct lirc_plugin *lLircPlugin = NULL;
+	struct lirc_buffer *lLircBuffer = NULL;
+	int lLircMinor = -1;
+	int lMemFailure;
+	char lBuf[63], lName[128]="";
+
+	dbg(" {\n");
+	
+#ifndef KERNEL_2_5
+	lCurrentInterfaceDescriptor = Device->actconfig->interface->
+		altsetting;
+	lEndpoint = lCurrentInterfaceDescriptor->endpoint;
+	lEndpoint2 = lEndpoint + 1;
+#else
+	Device = interface_to_usbdev(Int);
+	iface_host = Int->cur_altsetting;
+	lEndpoint = &(iface_host->endpoint[0].desc);
+	lEndpoint2 = &(iface_host->endpoint[1].desc);
+#endif
+	
+	if (!(lEndpoint->bEndpointAddress & 0x80) ||
+		((lEndpoint->bmAttributes & 3) != 0x03)) {
+		err("OnAir config endpoint error");
+#ifndef KERNEL_2_5
+		return NULL;
+#else
+		return -ENODEV;
+#endif
+	}
+
+	lDevnum = Device->devnum;
+
+	lMemFailure = 0;
+	if (!(lSasemDevice = kmalloc(sizeof(*lSasemDevice), GFP_KERNEL))) {
+		err("kmalloc(sizeof(*lSasemDevice), GFP_KERNEL)) failed");
+		lMemFailure = 1;
+	}
+	else {
+		memset(lSasemDevice, 0, sizeof(*lSasemDevice));
+		if (!(lLircPlugin = 
+			kmalloc(sizeof(*lLircPlugin), GFP_KERNEL))) {
+			err("kmalloc(sizeof(*lLircPlugin), GFP_KERNEL))"
+				"failed");
+			lMemFailure = 2;
+		}
+		else if (!(lLircBuffer = 
+			kmalloc(sizeof(*lLircBuffer), GFP_KERNEL))) {
+			err("kmalloc(sizeof(*lLircBuffer), GFP_KERNEL))"  
+				" failed");
+			lMemFailure = 3;
+		}
+		else if (lirc_buffer_init(lLircBuffer, MAX_INTERRUPT_DATA,
+				4)) {
+			err("lirc_buffer_init failed");
+			lMemFailure = 4;
+		}
+#ifndef KERNEL_2_5
+		else if (!(lSasemDevice->UrbIn = usb_alloc_urb(0))) {
+#else
+		else if (!(lSasemDevice->UrbIn = usb_alloc_urb(0, GFP_KERNEL))) {
+#endif
+			err("usb_alloc_urb(0) failed");
+			lMemFailure = 5;
+		} else {
+
+			memset(lLircPlugin, 0, sizeof(*lLircPlugin));
+			strcpy(lLircPlugin->name, DRIVER_NAME " ");
+			lLircPlugin->minor = -1;
+			lLircPlugin->code_length = MAX_INTERRUPT_DATA*8;
+			lLircPlugin->features = LIRC_CAN_REC_LIRCCODE;
+			lLircPlugin->data = lSasemDevice;
+			
+			lLircPlugin->rbuf = lLircBuffer;
+			lLircPlugin->set_use_inc = &LircSetUseInc;
+			lLircPlugin->set_use_dec = &LircSetUseDec;
+			lLircPlugin->owner = THIS_MODULE;
+
+			if ((lLircMinor = 
+				lirc_register_plugin(lLircPlugin)) < 0) {
+				err("lirc_register_plugin(lLircPlugin))"  
+					" failed");
+				lMemFailure = 9;
+			}
+		}
+	}
+	switch (lMemFailure) {
+	case 9:
+		usb_free_urb(lSasemDevice->UrbIn);
+	case 5:
+	case 4:
+		kfree(lLircBuffer);
+	case 3:
+		kfree(lLircPlugin);
+	case 2:
+		kfree(lSasemDevice);
+	case 1:
+#ifndef KERNEL_2_5
+		return NULL;
+#else
+		return -ENOMEM;
+#endif
+	}
+	
+	lLircPlugin->minor = lLircMinor; 
+	
+	dbg(": init device structure\n");
+	init_MUTEX(&lSasemDevice->SemLock);
+	down(&lSasemDevice->SemLock);
+	lSasemDevice->DescriptorIn = lEndpoint;
+	lSasemDevice->DescriptorOut = lEndpoint2;    
+	lSasemDevice->Device = Device;
+	lSasemDevice->LircPlugin = lLircPlugin;
+	lSasemDevice->Open = 0;
+	lSasemDevice->UrbOut = NULL;
+	lSasemDevice->PressTime.tv_sec = 0; 
+	lSasemDevice->PressTime.tv_usec = 0;
+	init_waitqueue_head(&lSasemDevice->QueueOpen);
+	init_waitqueue_head(&lSasemDevice->QueueWrite);
+
+	dbg(": init inbound URB\n");
+	lPipe = usb_rcvintpipe(lSasemDevice->Device,
+		lSasemDevice->DescriptorIn->bEndpointAddress);
+	
+	usb_fill_int_urb(lSasemDevice->UrbIn, 
+			 lSasemDevice->Device,
+			 lPipe, lSasemDevice->BufferIn,
+			 sizeof(lSasemDevice->BufferIn),
+			 SasemCallbackIn, lSasemDevice, 
+			 lSasemDevice->DescriptorIn->bInterval);
+
+	dbg(": get USB device info\n");
+	if (Device->descriptor.iManufacturer &&
+			usb_string(Device, 
+			Device->descriptor.iManufacturer, 
+			lBuf, 63) > 0) {
+		strncpy(lName, lBuf, 128);
+	}
+	if (Device->descriptor.iProduct &&
+			usb_string(Device, Device->descriptor.iProduct, 
+			lBuf, 63) > 0) {
+		snprintf(lName, 128, "%s %s", lName, lBuf);
+	}
+	printk(DRIVER_NAME "[%d]: %s on usb%d\n", lDevnum, lName,
+		Device->bus->busnum);
+
+	SasemDevice = lSasemDevice;
+	up(&lSasemDevice->SemLock);
+	dbg(" }\n");
+#ifndef KERNEL_2_5
+	return lSasemDevice;
+#else
+	usb_set_intfdata(Int, lSasemDevice);
+	if (usb_register_dev(Int, &SasemClass)) {
+		dbg(": Can't get minor for this device\n");
+		usb_set_intfdata(Int, NULL);
+		return -ENODEV;
+	}
+	return 0;
+#endif
+}
+
+
+#ifndef KERNEL_2_5
+static void SasemDisconnect(struct usb_device *Device, void *Ptr) {
+	struct SasemDevice *lSasemDevice = Ptr;
+#else
+static void SasemDisconnect(struct usb_interface *Int) {
+	struct SasemDevice *lSasemDevice = usb_get_intfdata(Int);
+	usb_set_intfdata(Int, NULL);
+#endif
+
+	dbg(" {\n");
+
+	down(&lSasemDevice->SemLock);
+
+#ifdef KERNEL_2_5
+	usb_deregister_dev(Int, &SasemClass);
+#endif
+
+	dbg(": free inbound URB\n");    
+	usb_unlink_urb(lSasemDevice->UrbIn);
+	usb_free_urb(lSasemDevice->UrbIn);
+	UnregisterFromLirc(lSasemDevice);
+
+	if (lSasemDevice->UrbOut != NULL) {
+		dbg(": free outbound URB\n");    
+		usb_unlink_urb(lSasemDevice->UrbOut);
+		usb_free_urb(lSasemDevice->UrbOut);
+}
+	up(&lSasemDevice->SemLock);
+	kfree (lSasemDevice);
+	dbg(" }\n");
+}
+
+#ifndef KERNEL_2_5
+static void SasemCallbackIn(struct urb *Urb) {
+#else
+static void SasemCallbackIn(struct urb *Urb, struct pt_regs *regs) {
+#endif
+	struct SasemDevice *lSasemDevice;
+	int lDevnum;
+	int lLen;
+	char lBuf[MAX_INTERRUPT_DATA];
+	int li;
+	struct timeval lstv;
+	long llms;
+
+	dbg(" {\n");
+	
+	if (!Urb) {
+		dbg(": Urb == NULL\n");
+		return;
+	}
+
+	if (!(lSasemDevice = Urb->context)) {
+		dbg(": no context\n");
+#ifdef KERNEL_2_5
+		Urb->transfer_flags |= URB_ASYNC_UNLINK;
+#endif
+		usb_unlink_urb(Urb);
+		return;
+	}
+
+	lDevnum = lSasemDevice->Devnum;
+	if (debug) {
+		printk(DRIVER_NAME "[%d]: data received (length %d)\n",
+		       lDevnum, Urb->actual_length);
+		printk(DRIVER_NAME 
+		       " intr_callback called %x %x %x %x %x %x %x %x\n", 
+		       lSasemDevice->BufferIn[0],
+		       lSasemDevice->BufferIn[1],
+		       lSasemDevice->BufferIn[2],
+		       lSasemDevice->BufferIn[3],
+		       lSasemDevice->BufferIn[4],
+		       lSasemDevice->BufferIn[5],
+		       lSasemDevice->BufferIn[6],
+		       lSasemDevice->BufferIn[7]);
+	}
+
+	switch (Urb->status) {
+
+	/* success */
+	case 0:
+		lLen = Urb->actual_length;
+		if (lLen > MAX_INTERRUPT_DATA) return;
+
+		memcpy(lBuf,Urb->transfer_buffer,lLen);
+
+		// is this needed? The OnAir device should always
+		// return 8 bytes
+		for (li = lLen; li < MAX_INTERRUPT_DATA; li++) 
+			lBuf[li] = 0;
+
+		// the OnAir device seems not to be able to signal a
+		// pressed button by repeating its code. Keeping a
+		// button pressed first sends the real code (e.g. 0C
+		// 80 7F 41 BE 00 00 00) and then keeps sending 08 00
+		// 00 00 00 00 00 00 as long as the button is pressed
+		// (notice that in the real key code 80 = !7F and 41 =
+		// !BE is this important? maybe for validation?) maybe
+		// 08 00 00 00 00 00 00 00 is the number of presses?
+		// who knows ...
+		// so lets do the following: if a code != the 08 code
+		// arrives, store it to repeat it if necessary for
+		// LIRC. If an 08 code follows afterwards, send the
+		// old code again to the buffer do this as long as the
+		// 08 code is being sent
+		// example:
+		// Code from Remote          Lirc Buffer
+		//  0C 80 7F 41 BE 00 00 00   0C 80 7F 41 BE 00 00 00
+		//  08 00 00 00 00 00 00 00   0C 80 7F 41 BE 00 00 00
+		//  08 00 00 00 00 00 00 00   0C 80 7F 41 BE 00 00 00
+		//  08 00 00 00 00 00 00 00   0C 80 7F 41 BE 00 00 00
+		//  08 00 00 00 00 00 00 00   0C 80 7F 41 BE 00 00 00
+		//  0C 80 7F 40 BF 00 00 00   0C 80 7F 40 BF 00 00 00
+		//  08 00 00 00 00 00 00 00   0C 80 7F 40 BF 00 00 00
+		//  08 00 00 00 00 00 00 00   0C 80 7F 40 BF 00 00 00
+		//  0C 80 7F 41 BE 00 00 00   0C 80 7F 41 BE 00 00 00
+		
+		// get the time since the last button press
+		do_gettimeofday(&lstv);
+		llms = (lstv.tv_sec - lSasemDevice->PressTime.tv_sec) * 1000 + (lstv.tv_usec - lSasemDevice->PressTime.tv_usec) / 1000;
+
+		if (memcmp(lBuf, SasemCode, MAX_INTERRUPT_DATA) == 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 ((llms < 250) && (lSasemDevice->CodeSaved != 0)) {
+				memcpy(lBuf, &lSasemDevice->LastCode,
+				       MAX_INTERRUPT_DATA);
+				lSasemDevice->PressTime.tv_sec = lstv.tv_sec; 
+				lSasemDevice->PressTime.tv_usec = lstv.tv_usec;
+			}
+			// there was no old code
+			else {
+				// Do Nothing!
+			}
+		}
+		else {
+			// save the current valid code for repeats
+			memcpy(&lSasemDevice->LastCode, lBuf,
+			       MAX_INTERRUPT_DATA);
+			// set flag to signal a valid code was save;
+			// just for safety reasons
+			lSasemDevice->CodeSaved = 1;
+			lSasemDevice->PressTime.tv_sec = lstv.tv_sec; 
+			lSasemDevice->PressTime.tv_usec = lstv.tv_usec;
+		}
+		
+		/* copy 1 code to lirc_buffer */
+		lirc_buffer_write_1(lSasemDevice->LircPlugin->rbuf,
+			lBuf);
+		wake_up(&lSasemDevice->LircPlugin->rbuf->wait_poll);
+		break;
+
+	/* unlink */
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		err(": urb failed, status %d\n", Urb->status);
+#ifdef KERNEL_2_5
+		Urb->transfer_flags |= URB_ASYNC_UNLINK;
+#endif
+		usb_unlink_urb(Urb);
+		return;
+	}
+
+#ifdef KERNEL_2_5
+	/* resubmit urb */
+	usb_submit_urb(Urb, SLAB_ATOMIC);
+#endif
+	dbg(" }\n");
+}
+
+/* lirc stuff */
+
+static int UnregisterFromLirc(struct SasemDevice *SasemDevice) {
+	struct lirc_plugin *lLircPlugin = SasemDevice->LircPlugin;
+	int lDevnum;
+
+	dbg(" {\n");
+	lDevnum = SasemDevice->Devnum;
+	
+	lirc_unregister_plugin(lLircPlugin->minor);
+	
+	printk(DRIVER_NAME "[%d]: usb remote disconnected\n", lDevnum);
+	
+	lirc_buffer_free(lLircPlugin->rbuf);
+	kfree(lLircPlugin->rbuf);
+	kfree(lLircPlugin);
+	dbg(" }\n");
+	return 0;
+}
+
+static int LircSetUseInc(void *Data) {
+	struct SasemDevice *lSasemDevice = Data;
+	int lDevnum;
+
+	dbg(" {\n");
+	if (!lSasemDevice) {
+		err(" no context\n");
+		return -EIO;
+	}
+	
+	lDevnum = lSasemDevice->Devnum;
+
+	if (!lSasemDevice->Connected) {
+		
+		/*
+			this is the trigger from LIRC to start
+			transfering data so the URB is being submitted
+		*/
+
+		if (!lSasemDevice->Device)
+			return -ENOENT;
+		
+		/* set USB device in URB */
+		lSasemDevice->UrbIn->dev = lSasemDevice->Device;
+		
+		/* start communication by submitting URB */
+#ifndef KERNEL_2_5
+		if (usb_submit_urb(lSasemDevice->UrbIn)) {
+#else
+		if (usb_submit_urb(lSasemDevice->UrbIn, SLAB_ATOMIC)) {
+#endif
+			err(" URB submit failed\n");
+			return -EIO;
+		}
+		
+		/* indicate that URB has been submitted */
+		lSasemDevice->Connected = 1;
+	}
+
+	dbg(" }\n");
+	return 0;
+}
+
+static void LircSetUseDec(void *Data) {
+	struct SasemDevice *lSasemDevice = Data;
+	int lDevnum;
+	
+	dbg(" {\n");
+	if (!lSasemDevice) {
+		err(" no context\n");
+		return;
+	}
+
+	lDevnum = lSasemDevice->Devnum;
+
+	if (lSasemDevice->Connected) {
+
+		/*
+			URB has been submitted before so it can be unlinked
+		*/
+
+		down(&lSasemDevice->SemLock);
+		usb_unlink_urb(lSasemDevice->UrbIn);
+		lSasemDevice->Connected = 0;
+		up(&lSasemDevice->SemLock);
+	}
+	dbg(" }\n");
+}
+
+/* FS Operations for LCDProc */
+
+static int SasemFSOpen(struct inode *Inode, struct file *File) {
+	struct SasemDevice *lSasemDevice;
+	int lReturn = 0;
+	int lPipe;
+
+	dbg(" {\n");
+	if (SasemDevice == NULL) {
+		err(" no device\n");
+		return -ENODEV;
+	}
+
+	lSasemDevice = SasemDevice;
+	down(&lSasemDevice->SemLock);
+
+	if (lSasemDevice->Open) {
+		// already open
+		dbg(": already open\n");
+
+		// return error immediately
+		if (File->f_flags & O_NONBLOCK) {
+			up(&lSasemDevice->SemLock);
+			return -EAGAIN;
+		}
+
+		dbg(": open & block\n");
+		// wait for release, on global variable
+		if ((lReturn = 
+			wait_event_interruptible(lSasemDevice->QueueOpen, 
+				SasemDevice !=NULL))) {
+			up(&lSasemDevice->SemLock);
+			return lReturn;
+		}
+	}
+
+	// indicate status as open
+	lSasemDevice->Open=1;
+
+	// handle URB for Out Interface
+#ifndef KERNEL_2_5
+	lSasemDevice->UrbOut = usb_alloc_urb(0);
+#else
+	lSasemDevice->UrbOut = usb_alloc_urb(0, GFP_KERNEL);
+#endif
+
+/*	lPipe = usb_sndintpipe(lSasemDevice->Device,
+			lSasemDevice->DescriptorOut->bEndpointAddress);
+*/    
+	dbg(": init outbound URB\n");
+	lPipe = usb_sndbulkpipe(lSasemDevice->Device,
+			lSasemDevice->DescriptorOut->bEndpointAddress);
+
+	usb_fill_int_urb(lSasemDevice->UrbOut, 
+			lSasemDevice->Device,
+			lPipe, lSasemDevice->BufferOut,
+			sizeof(lSasemDevice->BufferOut),
+			SasemCallbackOut, lSasemDevice, 
+			lSasemDevice->DescriptorOut->bInterval);
+
+	// store pointer to device in file handle
+	File->private_data = lSasemDevice;
+	up(&lSasemDevice->SemLock);
+	dbg(" }\n");
+	return 0;
+}
+
+static int SasemFSRelease(struct inode *Inode, struct file *File) {
+	struct SasemDevice *lpSasemDevice;
+
+	dbg(" {\n");
+	// get pointer to device
+	lpSasemDevice = (struct SasemDevice *)File->private_data;
+	down(&lpSasemDevice->SemLock);
+
+	// is the device open?
+	if ((lpSasemDevice) && (lpSasemDevice->Open)) {
+		dbg(": check open\n");
+
+		// yes, free URB and set status to closed
+		usb_unlink_urb(lpSasemDevice->UrbOut);
+		usb_free_urb(lpSasemDevice->UrbOut);
+		lpSasemDevice->UrbOut = NULL;
+		lpSasemDevice->Open = 0;
+	}
+	up(&lpSasemDevice->SemLock);
+
+	// wake up any anybody who is waiting for release
+	dbg(": wake up\n");
+	wake_up_interruptible(&lpSasemDevice->QueueOpen);
+	dbg(" }\n");
+	return 0;
+}
+
+static ssize_t SasemFSWrite(struct file *File, const char *cBuffer,
+				size_t Count, loff_t *Pos) {
+	struct SasemDevice *lSasemDevice;
+	int lCount = 0;
+	int lResult;
+
+	dbg(" {\n");
+	// sanity check
+	lSasemDevice = (struct SasemDevice *)File->private_data;
+	dbg(": lock\n");
+	down(&lSasemDevice->SemLock);
+
+	dbg(": check device \n");
+	if (lSasemDevice->Device == NULL) {
+		dbg(": device is null \n");
+		up(&lSasemDevice->SemLock);
+		return -ENODEV;
+	}
+
+	// is device open?
+	dbg(": check open \n");
+	if (lSasemDevice->Open == 0) {
+		dbg(": device not open\n");
+		up(&lSasemDevice->SemLock);
+		return -EBADF;
+	}
+
+	if (Count == 0) {
+		up(&lSasemDevice->SemLock);
+		return 0;
+	}
+
+	if (lSasemDevice->UrbOut->status == -EINPROGRESS) {
+		dbg(": status -EINPROGRESS \n");
+		up(&lSasemDevice->SemLock);
+		return 0;
+	}
+
+	if (lSasemDevice->UrbOut->status) {
+		err(": status %d \n", lSasemDevice->UrbOut->status);
+		up(&lSasemDevice->SemLock);
+		return -EAGAIN;
+	}
+
+	memset(lSasemDevice->BufferOut, 0, 
+			sizeof(lSasemDevice->BufferOut));
+	lCount = (Count>MAX_INTERRUPT_DATA)?MAX_INTERRUPT_DATA:Count;
+	copy_from_user(lSasemDevice->BufferOut, cBuffer, lCount);
+
+#ifndef KERNEL_2_5
+	lResult = usb_submit_urb(lSasemDevice->UrbOut);
+#else
+	lResult = usb_submit_urb(lSasemDevice->UrbOut, SLAB_ATOMIC);
+#endif
+
+	if (lResult) {
+		err(": usb_submit_urb failed %d\n", lResult);
+		lCount = lResult;
+	}
+	else {
+		// wait for write to finish
+		//interruptible_sleep_on(&lSasemDevice->QueueWrite);
+		wait_event_interruptible(lSasemDevice->QueueWrite, lSasemDevice->UrbOut->status != -EINPROGRESS);
+	}
+
+	up(&lSasemDevice->SemLock);
+	dbg(" }\n");
+	return lCount;
+}
+
+static ssize_t SasemFSRead(struct file *File, char *cBuffer,
+			   size_t Count, loff_t *Unused_pos)
+{
+	dbg(" {}\n");
+	// no read support
+	return -EINVAL;
+}
+
+static int SasemFSIoctl(struct inode *Inode, struct file *File,
+			unsigned Cmd, unsigned long lArg)
+{
+	int l;
+	char lBuf[30];
+	struct SasemDevice *lSasemDevice;
+
+	dbg(" {\n");
+	// sanity check
+	lSasemDevice = (struct SasemDevice *)File->private_data;
+	if (!lSasemDevice->Device)
+	return -ENOLINK;
+
+	switch (Cmd) {
+
+	case IOCTL_GET_HARD_VERSION:
+		// return device information
+		dbg(": IOCTL_GET_HARD_VERSION\n");
+		l = (lSasemDevice->Device)->descriptor.bcdDevice;
+		sprintf(lBuf,"%1d%1d.%1d%1d",
+				(l & 0xF000)>>12,(l & 0xF00)>>8,
+				(l & 0xF0)>>4,(l & 0xF));
+		if (copy_to_user((void *)lArg, lBuf, strlen(lBuf))!=0)
+			return -EFAULT;
+		break;
+
+	case IOCTL_GET_DRV_VERSION:
+		// return driver information
+		// sprintf(lBuf,"USBLCD Driver Version 1.03");
+		dbg(": IOCTL_GET_DRV_VERSION\n");
+		sprintf(lBuf,DRIVER_DESC);
+		if (copy_to_user((void *)lArg, lBuf, strlen(lBuf))!=0)
+			return -EFAULT;
+		break;  
+
+	default:
+		dbg(": unknown command\n");
+		// command not supported
+		return -ENOIOCTLCMD;
+		break;
+	}
+	dbg(" }\n");
+	return 0;
+}
+
+static unsigned SasemFSPoll(struct file *File, poll_table *Wait) {
+
+	dbg(" {}\n");
+	// no poll support
+	return -EINVAL;
+}
+
+#ifndef KERNEL_2_5
+static void SasemCallbackOut(struct urb *Urb)
+#else
+static void SasemCallbackOut(struct urb *Urb, struct pt_regs *regs)
+#endif
+{
+	struct SasemDevice *lSasemDevice;
+
+	dbg(" {\n");
+
+	lSasemDevice = Urb->context;  
+
+	// sanity check
+	if (Urb->status != 0) {
+		err(": urb failed, status %d\n", Urb->status);
+	}
+	if (waitqueue_active(&lSasemDevice->QueueWrite)) {
+		dbg(": wake up \n");
+		lSasemDevice->UrbOut->dev = lSasemDevice->Device;
+		wake_up(&lSasemDevice->QueueWrite);
+	}
+	dbg(" }\n");
+}
diff -urN --exclude='*.orig' --exclude='*.rej' linux-2.6.11-mm4/drivers/char/lirc/lirc_sasem.h linux-2.6.11-no1/drivers/char/lirc/lirc_sasem.h
--- linux-2.6.11-mm4/drivers/char/lirc/lirc_sasem.h	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.11-no1/drivers/char/lirc/lirc_sasem.h	2005-03-30 00:11:50.305635638 -0500
@@ -0,0 +1,93 @@
+/*      $Id: lirc_sasem.h,v 1.3 2005/03/29 17:51:45 lirc Exp $      */
+
+#ifndef LIRC_SASEM_H
+#define LIRC_SASEM_H
+
+#include "linux/kcompat.h"
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION  	"v0.4"
+#define DATE            	"Mar 2005"
+#define DRIVER_AUTHOR 		"Oliver Stabel <oliver.stabel@gmx.de>"
+#define DRIVER_DESC 		"USB Driver for Sasem Remote Controller V1.1"
+#define DRIVER_SHORTDESC 	"Sasem"
+#define DRIVER_NAME		"lirc_sasem"
+
+#define BANNER \
+  KERN_INFO DRIVER_SHORTDESC " " DRIVER_VERSION " (" DATE ")\n" \
+  KERN_INFO "   by " DRIVER_AUTHOR "\n"
+
+static const char longbanner[] = {
+	DRIVER_DESC ", " DRIVER_VERSION " (" DATE "), by " DRIVER_AUTHOR
+};
+
+#define MAX_INTERRUPT_DATA 8
+#define SASEM_MINOR 144
+
+#include <linux/version.h>
+#include <linux/time.h>
+
+static const char SasemCode[MAX_INTERRUPT_DATA] =
+	{ 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+struct SasemDevice {
+	struct usb_device *Device;
+	struct usb_endpoint_descriptor *DescriptorIn;
+	struct usb_endpoint_descriptor *DescriptorOut;
+	struct urb *UrbIn;
+	struct urb *UrbOut;
+	struct timeval PressTime;
+	unsigned int InterfaceNum;
+	int	Devnum;
+	unsigned char BufferIn[MAX_INTERRUPT_DATA];
+	unsigned char BufferOut[MAX_INTERRUPT_DATA];
+	struct semaphore SemLock;
+
+	char LastCode[MAX_INTERRUPT_DATA];
+	int CodeSaved;
+	
+	/* lirc */
+	struct lirc_plugin *LircPlugin;
+	int Connected;
+
+	/* LCDProc */
+	int Open;
+	wait_queue_head_t QueueWrite;
+	wait_queue_head_t QueueOpen;
+};
+
+#ifndef KERNEL_2_5
+static void * SasemProbe(struct usb_device *Device, unsigned InterfaceNum,
+		const struct usb_device_id *ID);
+static void SasemDisconnect(struct usb_device *Device, void *Ptr);
+static void SasemCallbackIn(struct urb *Urb);
+static void SasemCallbackOut(struct urb *Urb);
+#else
+static int SasemProbe(struct usb_interface *Int,
+		      const struct usb_device_id *ID);
+static void SasemDisconnect(struct usb_interface *Int); 
+static void SasemCallbackIn(struct urb *Urb, struct pt_regs *regs);
+static void SasemCallbackOut(struct urb *Urb, struct pt_regs *regs);
+#endif
+
+/* lirc */
+static int UnregisterFromLirc(struct SasemDevice *SasemDevice);
+static int LircSetUseInc(void *Data);
+static void LircSetUseDec(void *Data);
+
+#define IOCTL_GET_HARD_VERSION  1
+#define IOCTL_GET_DRV_VERSION   2
+
+static int SasemFSOpen(struct inode *Inode, struct file *File);
+static int SasemFSRelease(struct inode *Inode, struct file *File);
+static ssize_t SasemFSWrite(struct file *File, const char *Buffer,
+			size_t Count, loff_t *Pos);
+static ssize_t SasemFSRead(struct file *File, char *Buffer,
+			size_t Count, loff_t *Unused_pos);
+static int SasemFSIoctl(struct inode *Inode, struct file *File,
+			unsigned Cmd, unsigned long Arg);
+static unsigned SasemFSPoll(struct file *File, poll_table *Wait);
+
+#endif
diff -urN --exclude='*.orig' --exclude='*.rej' linux-2.6.11-mm4/drivers/char/lirc/lirc_serial.c linux-2.6.11-no1/drivers/char/lirc/lirc_serial.c
--- linux-2.6.11-mm4/drivers/char/lirc/lirc_serial.c	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.11-no1/drivers/char/lirc/lirc_serial.c	2005-03-30 00:12:39.558807419 -0500
@@ -0,0 +1,1106 @@
+/*      $Id: lirc_serial.c,v 5.65 2005/02/19 15:13:02 lirc Exp $      */
+
+/****************************************************************************
+ ** 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>
+ *
+ *  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
+*/
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+ 
+#include <linux/version.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 2, 18)
+#error "**********************************************************"
+#error " Sorry, this driver needs kernel version 2.2.18 or higher "
+#error "**********************************************************"
+#endif
+
+#include <linux/config.h>
+
+#if defined(CONFIG_SERIAL) || defined(CONFIG_SERIAL_8250)
+#warning "******************************************"
+#warning " Your serial port driver is compiled into "
+#warning " the kernel. You will have to release the "
+#warning " port you want to use for LIRC with:      "
+#warning "    setserial /dev/ttySx uart none        "
+#warning "******************************************"
+#endif
+
+#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 <asm/system.h>
+#include <asm/segment.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/fcntl.h>
+
+#include "linux/lirc.h"
+#include "linux/kcompat.h"
+#include "lirc_dev.h"
+
+#if defined(LIRC_SERIAL_SOFTCARRIER) && !defined(LIRC_SERIAL_TRANSMITTER)
+#warning "Software carrier only affects transmitting"
+#endif
+
+#if defined(rdtsc)
+
+#define USE_RDTSC
+#warning "Note: using rdtsc instruction"
+#endif
+
+#ifdef LIRC_SERIAL_ANIMAX
+#ifdef LIRC_SERIAL_TRANSMITTER
+#warning "******************************************"
+#warning " This receiver does not have a            "
+#warning " transmitter diode                        "
+#warning "******************************************"
+#endif
+#endif
+
+#define LIRC_DRIVER_NAME "lirc_serial"
+
+struct lirc_serial
+{
+	int type;
+	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
+
+#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;
+#else
+static int type=LIRC_HOMEBREW;
+#endif
+
+#ifdef LIRC_SERIAL_SOFTCARRIER
+static int softcarrier=1;
+#else
+static int softcarrier=0;
+#endif
+
+static int share_irq = 0;
+static int debug = 0;
+
+#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 */
+	{
+		LIRC_HOMEBREW,
+		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 */
+	{
+		LIRC_IRDEO,
+		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 */
+	{
+		LIRC_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 */
+	{
+		LIRC_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) */
+	{
+		LIRC_HOMEBREW,
+		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)
+	}
+	
+};
+
+#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;   /* 0 = active high, 1 = active low */
+
+static int io = LIRC_PORT;
+static int irq = LIRC_IRQ;
+
+static struct timeval lasttv = {0, 0};
+
+static struct lirc_buffer rbuf;
+
+static lirc_t wbuf[WBUF_LEN];
+
+static unsigned int freq = 38000;
+static unsigned int duty_cycle = 50;
+
+/* Initialized in init_timing_params() */
+static unsigned long period = 0;
+static unsigned long pulse_width = 0;
+static unsigned long space_width = 0;
+
+#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)
+{
+	return inb(io + offset);
+}
+
+static inline void soutp(int offset, int value)
+{
+	outb(value, io + offset);
+}
+
+static inline void on(void)
+{
+	if (txsense)
+	{
+		soutp(UART_MCR,hardware[type].off);
+	}
+	else
+	{
+		soutp(UART_MCR,hardware[type].on);
+	}
+}
+  
+static inline void off(void)
+{
+	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 = 0;
+
+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=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 void inline rbwrite(lirc_t 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 void inline frbwrite(lirc_t l)
+{
+	/* simple noise filter */
+	static lirc_t pulse=0L,space=0L;
+	static unsigned int ptr=0;
+	
+	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 pt_regs *regs)
+{
+	struct timeval tv;
+	int status,counter,dcd;
+	long deltv;
+	lirc_t data;
+	
+	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 the type lirc_t 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;
+			
+			deltv=tv.tv_sec-lasttv.tv_sec;
+			if(deltv>15) 
+			{
+				dprintk("AIEEEE: %d %d %lx %lx %lx %lx\n",
+					dcd,sense,
+					tv.tv_sec,lasttv.tv_sec,
+					tv.tv_usec,lasttv.tv_usec);
+				data=PULSE_MASK; /* really long time */
+				if(!(dcd^sense)) /* sanity check */
+				{
+				        /* detecting pulse while this
+					   MUST be a space! */
+				        sense=sense ? 0:1;
+				}
+			}
+			else
+			{
+				data=(lirc_t) (deltv*1000000+
+					       tv.tv_usec-
+					       lasttv.tv_usec);
+			};
+			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;
+			}
+			frbwrite(dcd^sense ? data : (data|PULSE_BIT));
+			lasttv=tv;
+			wake_up_interruptible(&rbuf.wait_poll);
+		}
+	} while(!(sinp(UART_IIR) & UART_IIR_NO_INT)); /* still pending ? */
+	return IRQ_RETVAL(IRQ_HANDLED);
+}
+
+static int init_port(void)
+{
+	unsigned long flags;
+
+	
+	/* Reserve io region. */
+	if(request_region(io, 8, LIRC_DRIVER_NAME)==NULL)
+	{
+		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);
+	}
+	
+	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);
+	
+	/* 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(hardware[type].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);
+	
+	/* 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 sec for the power supply */
+		
+		set_current_state(TASK_INTERRUPTIBLE);
+		schedule_timeout(HZ);
+		
+		sense=(sinp(UART_MSR) & hardware[type].signal_pin) ? 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(lirc_t), RBUF_LEN) < 0)
+		return -ENOMEM;
+	
+	/* initialize timestamp */
+	do_gettimeofday(&lasttv);
+
+	result=request_irq(irq,irq_handler,
+			   SA_INTERRUPT | (share_irq ? SA_SHIRQ: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);
+	
+	MOD_INC_USE_COUNT;
+	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);
+	
+	MOD_DEC_USE_COUNT;
+}
+
+static ssize_t lirc_write(struct file *file, const char *buf,
+			 size_t n, loff_t * ppos)
+{
+	int retval,i,count;
+	unsigned long flags;
+	long delta=0;
+	
+	if(!(hardware[type].features&LIRC_CAN_SEND_PULSE))
+	{
+		return(-EBADF);
+	}
+	
+	if(n%sizeof(lirc_t)) return(-EINVAL);
+	retval=verify_area(VERIFY_READ,buf,n);
+	if(retval) return(retval);
+	count=n/sizeof(lirc_t);
+	if(count>WBUF_LEN || count%2==0) return(-EINVAL);
+	copy_from_user(wbuf,buf,n);
+	local_irq_save(flags);
+	if(hardware[type].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,
+	owner:		THIS_MODULE,
+};
+
+#ifdef MODULE
+
+int init_module(void)
+{
+	int result;
+	
+	switch(type)
+	{
+	case LIRC_HOMEBREW:
+	case LIRC_IRDEO:
+	case LIRC_IRDEO_REMOTE:
+	case LIRC_ANIMAX:
+	case LIRC_IGOR:
+		break;
+	default:
+		return(-EINVAL);
+	}
+	if(!softcarrier && hardware[type].type==LIRC_HOMEBREW)
+	{
+		hardware[type].features&=~(LIRC_CAN_SET_SEND_DUTY_CYCLE|
+					   LIRC_CAN_SET_SEND_CARRIER);
+	}
+	if ((result = init_port()) < 0)
+		return result;
+	plugin.features = hardware[type].features;
+	if ((plugin.minor = lirc_register_plugin(&plugin)) < 0) {
+		printk(KERN_ERR  LIRC_DRIVER_NAME  
+		       ": register_chrdev failed!\n");
+		release_region(io, 8);
+		return -EIO;
+	}
+	return 0;
+}
+
+void cleanup_module(void)
+{
+	release_region(io, 8);
+	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");
+MODULE_LICENSE("GPL");
+
+module_param(type, int, 0444);
+MODULE_PARM_DESC(type, "Hardware type (0 = home-brew, 1 = IRdeo,"
+		 " 2 = IRdeo Remote, 3 = AnimaX, 4 = IgorPlug");
+
+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(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");
+
+EXPORT_NO_SYMBOLS;
+
+#endif /* MODULE */
diff -urN --exclude='*.orig' --exclude='*.rej' linux-2.6.11-mm4/drivers/char/lirc/lirc_sir.c linux-2.6.11-no1/drivers/char/lirc/lirc_sir.c
--- linux-2.6.11-mm4/drivers/char/lirc/lirc_sir.c	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.11-no1/drivers/char/lirc/lirc_sir.c	2005-03-30 00:12:39.560807141 -0500
@@ -0,0 +1,1310 @@
+/*
+ * 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>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 2, 18)
+#error "**********************************************************"
+#error " Sorry, this driver needs kernel version 2.2.18 or higher "
+#error "**********************************************************"
+#endif
+#include <linux/module.h>
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+ 
+#include <linux/config.h>
+
+#if !defined(LIRC_ON_SA1100) && !defined(CONFIG_SERIAL_MODULE)
+#warning "******************************************"
+#warning " Your serial port driver is compiled into "
+#warning " the kernel. You will have to release the "
+#warning " port you want to use for LIRC with:      "
+#warning "    setserial /dev/ttySx uart none        "
+#warning "******************************************"
+#endif
+
+#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 <asm/segment.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/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 "linux/lirc.h"
+#include "lirc_dev.h"
+#include "linux/kcompat.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 milli seconds = 87ms*/
+#define TIME_CONST (10000000ul/115200ul)
+
+#endif
+
+#ifdef LIRC_SIR_ACTISYS_ACT200L
+static void init_act200(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"
+
+#ifndef LIRC_SIR_TEKRAM
+#define PULSE '['
+
+/* 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
+static int io = LIRC_PORT;
+static int irq = LIRC_IRQ;
+static int threshold = 3;
+#endif
+
+static spinlock_t timer_lock = SPIN_LOCK_UNLOCKED;
+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 = 0;
+
+static DECLARE_WAIT_QUEUE_HEAD(lirc_read_queue);
+
+static spinlock_t hardware_lock = SPIN_LOCK_UNLOCKED;
+static spinlock_t dev_lock = SPIN_LOCK_UNLOCKED;
+
+static lirc_t rx_buf[RBUF_LEN]; unsigned int rx_tail = 0, rx_head = 0;
+#ifndef LIRC_SIR_TEKRAM
+static lirc_t tx_buf[WBUF_LEN];
+#endif
+
+static int debug = 0;
+#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,
+				 struct pt_regs * regs);
+#ifndef LIRC_SIR_TEKRAM
+static void send_space(unsigned long len);
+static void send_pulse(unsigned long len);
+#endif
+static int init_hardware(void);
+static void drop_hardware(void);
+	/* Initialisation */
+static int init_port(void);
+static void drop_port(void);
+int init_module(void);
+void cleanup_module(void);
+
+#ifdef LIRC_ON_SA1100
+static void inline on(void)
+{
+	PPSR|=PPC_TXD2;
+}
+  
+static void inline 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 (MOD_IN_USE) {
+		spin_unlock(&dev_lock);
+		return -EBUSY;
+	}
+	MOD_INC_USE_COUNT;
+	spin_unlock(&dev_lock);
+	return 0;
+}
+
+static int lirc_close(struct inode * inode, struct file *file)
+{
+	MOD_DEC_USE_COUNT;
+	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(lirc_t)) return(-EINVAL);
+	
+	add_wait_queue(&lirc_read_queue,&wait);
+	set_current_state(TASK_INTERRUPTIBLE);
+	while(n<count)
+	{
+		if(rx_head!=rx_tail)
+		{
+			retval=verify_area(VERIFY_WRITE,
+					   (void *) buf+n,sizeof(lirc_t));
+			if (retval)
+			{
+				return retval;
+			}
+			copy_to_user((void *) buf+n,(void *) (rx_buf+rx_head),
+				     sizeof(lirc_t));
+			rx_head=(rx_head+1)&(RBUF_LEN-1);
+			n+=sizeof(lirc_t);
+		}
+		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;
+#ifdef LIRC_SIR_TEKRAM
+	return(-EBADF);
+#else
+	int i;
+	int retval;
+
+        if(n%sizeof(lirc_t) || (n/sizeof(lirc_t)) > WBUF_LEN)
+		return(-EINVAL);
+	retval = verify_area(VERIFY_READ, buf, n);
+	if (retval)
+		return retval;
+	copy_from_user(tx_buf, buf, n);
+	i = 0;
+	n/=sizeof(lirc_t);
+#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;
+#endif
+}
+
+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;
+#endif
+
+#ifdef LIRC_SIR_TEKRAM
+	if (cmd == LIRC_GET_FEATURES)
+		value = LIRC_CAN_REC_MODE2;
+	else if (cmd == LIRC_GET_SEND_MODE)
+		value = 0;
+	else if (cmd == LIRC_GET_REC_MODE)
+		value = LIRC_MODE_MODE2;
+#elif defined(LIRC_ON_SA1100)
+	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(retval);
+		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;
+	
+#ifdef LIRC_SIR_TEKRAM
+	if (cmd == LIRC_SET_REC_MODE) {
+		if (value != LIRC_MODE_MODE2)
+			retval = -ENOSYS;
+	} else if (cmd == LIRC_SET_SEND_MODE) {
+		retval = -ENOSYS;
+	}
+#else
+	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;
+	}
+#endif
+	return retval;
+}
+
+static void add_read_queue(int flag, unsigned long val)
+{
+	unsigned int new_rx_tail;
+	lirc_t 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)
+{
+#if WE_DONT_USE_LOCAL_OPEN_CLOSE
+	MOD_INC_USE_COUNT;
+#endif
+       return 0;
+}
+
+static void set_use_dec(void* data)
+{
+#if WE_DONT_USE_LOCAL_OPEN_CLOSE
+	MOD_DEC_USE_COUNT;
+#endif
+}
+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,
+       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,
+				 struct pt_regs * regs)
+{
+	unsigned char data;
+	struct timeval curr_tv;
+	static unsigned long deltv;
+#ifdef LIRC_ON_SA1100
+	int status;
+	static int n=0;
+	
+	//printk("interrupt\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)
+		{
+			//printk("add\n");
+			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("transmit fifo not full, shouldn't ever 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);
+				}
+			}
+			while ((lsr = inb(io + UART_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);
+}
+#elif defined(LIRC_SIR_TEKRAM)
+#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;
+
+	if (!bytes_out)
+		bytes_out++;
+	time_left = (long)len - (long)bytes_out * (long)TIME_CONST;
+	while (--bytes_out) {
+		outb(PULSE, io + UART_TX);
+		/* FIXME treba seriozne cakanie z drivers/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("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();
+#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, SA_INTERRUPT,
+			     LIRC_DRIVER_NAME, NULL);
+	if (retval < 0) {
+		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
+
+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
+
+int init_module(void)
+{
+	int retval;
+	
+	retval=init_chrdev();
+	if(retval < 0)
+		return retval;
+	retval = init_lirc_sir();
+	if (retval) {
+		drop_chrdev();
+		return retval;
+	}
+	return 0;
+}
+
+void cleanup_module(void)
+{
+	drop_hardware();
+	drop_chrdev();
+	drop_port();
+	printk(KERN_INFO LIRC_DRIVER_NAME ": Uninstalled.\n");
+}
+
+#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");
+#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");
+
+EXPORT_NO_SYMBOLS;
+
+#endif /* MODULE */
diff -urN --exclude='*.orig' --exclude='*.rej' linux-2.6.11-mm4/drivers/char/lirc/lirc_streamzap.c linux-2.6.11-no1/drivers/char/lirc/lirc_streamzap.c
--- linux-2.6.11-mm4/drivers/char/lirc/lirc_streamzap.c	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.11-no1/drivers/char/lirc/lirc_streamzap.c	2005-03-30 00:12:39.563806725 -0500
@@ -0,0 +1,846 @@
+/*      $Id: lirc_streamzap.c,v 1.10 2005/03/06 14:39:36 lirc Exp $      */
+
+/*
+ * 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>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 0)
+#error "*******************************************************"
+#error "Sorry, this driver needs kernel version 2.4.0 or higher"
+#error "*******************************************************"
+#endif
+
+#include <linux/config.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 <asm/uaccess.h>
+#include <linux/usb.h>
+
+#include "linux/lirc.h"
+#include "linux/kcompat.h"
+#include "lirc_dev.h"
+
+#define DRIVER_VERSION	"$Revision: 1.10 $"
+#define DRIVER_NAME	"lirc_streamzap"
+#define DRIVER_DESC     "Streamzap Remote Control driver"
+
+/* ------------------------------------------------------------------ */
+
+static int debug = 0;
+
+#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 [] = {
+	{ 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 64
+
+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;
+	unsigned long           flush_jiffies;
+};
+
+
+/* local function prototypes */
+#ifdef KERNEL_2_5
+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, struct pt_regs *regs);
+#else
+static void *streamzap_probe(struct usb_device *udev, unsigned int ifnum,
+			     const struct usb_device_id *id);
+static void streamzap_disconnect(struct usb_device *dev, void *ptr);
+static void usb_streamzap_irq(struct urb *urb);
+#endif
+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);
+
+/* usb specific object needed to register this driver with the usb subsystem */
+
+static struct usb_driver streamzap_driver = {
+	.owner =	THIS_MODULE,
+	.name =		DRIVER_NAME,
+	.probe =	streamzap_probe,
+	.disconnect =	streamzap_disconnect,
+	.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 delay_timeout(unsigned long arg)
+{
+	struct usb_streamzap *sz = (struct usb_streamzap *) arg;
+	lirc_t sum=10000;
+	lirc_t data;
+	
+	spin_lock(&sz->timer_lock);
+	while(!lirc_buffer_empty(&sz->delay_buf))
+	{
+		lirc_buffer_read_1( &sz->delay_buf, (unsigned char *) &data);
+		lirc_buffer_write_1(&sz->lirc_buf, (unsigned char *) &data);
+		sum += (data&PULSE_MASK)+STREAMZAP_RESOLUTION/2;
+		if(sum > 1000000/HZ)
+		{
+			break;
+		}
+	}
+	if(!lirc_buffer_empty(&sz->delay_buf))
+	{
+		while(lirc_buffer_available(&sz->delay_buf) < 
+		      STREAMZAP_BUFFER_SIZE/2)
+		{
+			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++;
+			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(&sz->timer_lock);
+}
+
+static inline void flush_delay_buffer(struct usb_streamzap *sz)
+{
+	lirc_t data;
+	int empty = 1;
+	
+	while(!lirc_buffer_empty(&sz->delay_buf))
+	{
+		empty = 0;
+		lirc_buffer_read_1( &sz->delay_buf, (unsigned char *) &data);
+		lirc_buffer_write_1(&sz->lirc_buf, (unsigned char *) &data);
+	}
+	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))
+	{
+		lirc_t data;
+		
+		lirc_buffer_read_1( &sz->delay_buf, (unsigned char *) &data);
+		lirc_buffer_write_1(&sz->lirc_buf, (unsigned char *) &data);
+		
+		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)
+{
+	lirc_t pulse;
+	
+	if(sz->idle)
+	{
+		long deltv;
+		lirc_t 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=(lirc_t) (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 = ((lirc_t) 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)
+{
+	lirc_t space;
+	
+	space = ((lirc_t) 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.
+ */
+#ifdef KERNEL_2_5
+static void usb_streamzap_irq(struct urb *urb, struct pt_regs *regs) 
+#else
+static void usb_streamzap_irq(struct urb *urb) 
+#endif
+{
+	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_jiffies < jiffies) 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]);
+			break;
+		}
+	}
+
+#ifdef KERNEL_2_5
+	/* resubmit only for 2.6 */
+	usb_submit_urb( urb, SLAB_ATOMIC );
+#endif
+
+	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
+ */
+#ifdef KERNEL_2_5
+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;
+#else
+static void *streamzap_probe(struct usb_device *udev, unsigned int ifnum,
+			     const struct usb_device_id *id)
+{
+	struct usb_interface *interface = &udev->actconfig->interface[ifnum];
+	struct usb_interface_descriptor *iface_host;
+#endif
+	int retval = -ENOMEM;
+	struct usb_streamzap *sz = NULL;
+	char buf[63], name[128] = "";
+
+	/***************************************************
+	 * Allocate space for device driver specific data
+	 */
+	if (( sz = kmalloc (sizeof(struct usb_streamzap), GFP_KERNEL)) == NULL )
+		goto error;
+
+	memset(sz, 0, sizeof(*sz));
+        sz->udev = udev;
+        sz->interface = interface;
+	
+	/***************************************************
+	 * Check to ensure endpoint information matches requirements
+	 */
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,4)
+	iface_host = interface->cur_altsetting;
+#else
+	iface_host = &interface->altsetting[interface->act_altsetting];
+#endif
+
+#ifdef KERNEL_2_5
+        if (iface_host->desc.bNumEndpoints != 1) {
+#else
+	if(iface_host->bNumEndpoints != 1) {
+#endif
+#ifdef KERNEL_2_5
+                err("%s: Unexpected desc.bNumEndpoints (%d)", __FUNCTION__,
+		    iface_host->desc.bNumEndpoints);
+#else
+                err("%s: Unexpected desc.bNumEndpoints (%d)", __FUNCTION__,
+		    iface_host->bNumEndpoints);
+#endif
+		retval = -ENODEV;
+                goto error;
+        }
+
+#ifdef KERNEL_2_5
+	sz->endpoint = &(iface_host->endpoint[0].desc);
+#else
+	sz->endpoint = &(iface_host->endpoint[0]);
+#endif
+        if (( sz->endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
+	    != USB_DIR_IN) {
+                err("%s: endpoint doesn't match input device 02%02x",
+		    __FUNCTION__, 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",
+		    __FUNCTION__, sz->endpoint->bmAttributes );
+                retval = -ENODEV;
+                goto error;
+        }
+
+        if ( sz->endpoint->wMaxPacketSize == 0 ) {
+                err("%s: endpoint message size==0? ", __FUNCTION__);
+                retval = -ENODEV;
+                goto error;
+        }
+
+	/***************************************************
+	 * Allocate the USB buffer and IRQ URB 
+	 */
+
+	sz->buf_in_len = sz->endpoint->wMaxPacketSize;
+#ifdef KERNEL_2_5
+        if((sz->buf_in = usb_buffer_alloc(sz->udev, sz->buf_in_len,
+					  SLAB_ATOMIC, &sz->dma_in)) == NULL )
+	{
+                goto error;
+	}
+	if (!( sz->urb_in = usb_alloc_urb(0, GFP_KERNEL)))
+		goto error;
+#else
+	if( (sz->buf_in = kmalloc(sz->buf_in_len, GFP_KERNEL))
+	    == NULL)
+	{
+		goto error;
+	}
+	if( (sz->urb_in = usb_alloc_urb(0)) == NULL)
+	{
+		goto error;
+	}
+#endif
+	/***************************************************
+	 * Connect this device to the LIRC sub-system
+	 */
+
+	if(lirc_buffer_init(&sz->lirc_buf, sizeof(lirc_t),
+			    STREAMZAP_BUFFER_SIZE))
+	{
+		goto error;
+	}
+	if(lirc_buffer_init(&sz->delay_buf, sizeof(lirc_t),
+			    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(lirc_t) * 8;
+	sz->plugin.features = LIRC_CAN_REC_MODE2;
+	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.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);
+
+	/***************************************************
+	 * 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);
+
+#ifdef KERNEL_2_5
+	usb_set_intfdata( interface , sz );
+#endif
+
+	if(lirc_register_plugin(&sz->plugin) < 0)
+	{
+		lirc_buffer_free(&sz->delay_buf);
+		lirc_buffer_free(&sz->lirc_buf);
+		goto error;
+	}
+
+#ifdef KERNEL_2_5
+	return 0;
+#else
+	return sz;
+#endif
+
+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 ) {
+
+		if ( sz->urb_in )
+			usb_free_urb( sz->urb_in );
+
+		if ( sz->buf_in )
+		{
+#ifdef KERNEL_2_5
+			usb_buffer_free(udev, sz->buf_in_len,
+					sz->buf_in, sz->dma_in);
+#else
+			kfree(sz->buf_in);
+#endif
+		}
+		kfree( sz );
+	}
+
+#ifdef KERNEL_2_5
+	return retval;
+#else
+	return NULL;
+#endif
+}
+
+static int streamzap_use_inc(void *data)
+{
+	struct usb_streamzap *sz = data;
+
+	if(!sz)
+	{
+		dprintk("%s called with no context", -1, __FUNCTION__);
+		return -EINVAL;
+	}
+	dprintk("set use inc", sz->plugin.minor);
+
+	MOD_INC_USE_COUNT;
+	
+	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_jiffies = jiffies + HZ;
+	sz->urb_in->dev = sz->udev;
+#ifdef KERNEL_2_5
+	if (usb_submit_urb(sz->urb_in, SLAB_ATOMIC))
+#else
+	if (usb_submit_urb(sz->urb_in))
+#endif
+	{
+		dprintk("open result = -EIO error submitting urb",
+			sz->plugin.minor);
+		MOD_DEC_USE_COUNT;
+		return -EIO;
+	}
+	
+	return 0;
+}
+
+static void streamzap_use_dec(void *data)
+{
+        struct usb_streamzap *sz = data;
+
+        if (!sz) {
+                dprintk("%s called with no context", -1, __FUNCTION__);
+                return;
+        }
+        dprintk("set use dec", sz->plugin.minor);
+	
+	stop_timer(sz);
+	
+	usb_unlink_urb(sz->urb_in);
+	
+        MOD_DEC_USE_COUNT;
+}
+
+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.
+ */
+#ifdef KERNEL_2_5
+static void streamzap_disconnect( struct usb_interface *interface )
+#else
+static void streamzap_disconnect(struct usb_device *dev, void *ptr)
+#endif
+{
+	struct usb_streamzap *sz;
+	int errnum;
+	int minor;
+
+#ifdef KERNEL_2_5
+	sz = usb_get_intfdata( interface );
+#else
+	sz = ptr;
+#endif
+
+	/*
+	 * unregister from the LIRC sub-system
+	 */
+
+        if (( errnum = lirc_unregister_plugin( sz->plugin.minor )) != 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 );
+
+#ifdef KERNEL_2_5
+        usb_buffer_free( sz->udev , sz->buf_in_len, sz->buf_in, sz->dma_in );
+#else
+	kfree(sz->buf_in);
+#endif
+
+	minor = sz->plugin.minor;
+	kfree( sz );
+
+        printk(KERN_INFO DRIVER_NAME "[%d]: disconnected\n", minor);
+}
+
+#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");
+
+EXPORT_NO_SYMBOLS;
+
+#endif /* MODULE */
diff -urN --exclude='*.orig' --exclude='*.rej' linux-2.6.11-mm4/include/linux/kcompat.h linux-2.6.11-no1/include/linux/kcompat.h
--- linux-2.6.11-mm4/include/linux/kcompat.h	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.11-no1/include/linux/kcompat.h	2005-03-30 00:12:39.565806448 -0500
@@ -0,0 +1,203 @@
+/*      $Id: kcompat.h,v 5.11 2005/02/19 15:12:58 lirc Exp $      */
+#define IRCTL_DEV_MAJOR 61
+#define DEV_LIRC "lirc"
+#ifndef _KCOMPAT_H
+#define _KCOMPAT_H
+
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+#define LIRC_HAVE_DEVFS
+#define LIRC_HAVE_DEVFS_26
+#define LIRC_HAVE_SYSFS
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+#define LIRC_HAVE_DEVFS
+#define LIRC_HAVE_DEVFS_24
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 3, 0)
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+static inline void del_timer_sync(struct timer_list * timerlist)
+{
+	start_bh_atomic();
+	del_timer(timerlist);
+	end_bh_atomic();
+}
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0)
+#ifdef daemonize
+#undef daemonize
+#endif
+#define daemonize(name) do {                                           \
+                                                                       \
+	lock_kernel();                                                 \
+	                                                               \
+	exit_mm(current);                                              \
+	exit_files(current);                                           \
+	exit_fs(current);                                              \
+	current->session = 1;                                          \
+	current->pgrp = 1;                                             \
+	current->euid = 0;                                             \
+	current->tty = NULL;                                           \
+	sigfillset(&current->blocked);                                 \
+	                                                               \
+	strcpy(current->comm, name);                                   \
+	                                                               \
+	unlock_kernel();                                               \
+                                                                       \
+} while (0)
+
+/* Not sure when this was introduced, sometime during 2.5.X */
+#define MODULE_PARM_int(x) MODULE_PARM(x, "i")
+#define MODULE_PARM_bool(x) MODULE_PARM(x, "i")
+#define MODULE_PARM_long(x) MODULE_PARM(x, "l")
+#define module_param(x,y,z) MODULE_PARM_##y(x)
+#else
+#include <linux/moduleparam.h>
+#endif /* Linux < 2.6.0 */
+
+#ifdef LIRC_HAVE_DEVFS_24
+#ifdef register_chrdev
+#undef register_chrdev
+#endif
+#define register_chrdev devfs_register_chrdev
+#ifdef unregister_chrdev
+#undef unregister_chrdev
+#endif
+#define unregister_chrdev devfs_unregister_chrdev
+#endif /* DEVFS 2.4 */
+
+#ifndef LIRC_HAVE_SYSFS
+#define class_simple_destroy(x) do { } while(0)
+#define class_simple_create(x,y) NULL
+#define class_simple_device_remove(x) do { } while(0)
+#define class_simple_device_add(x, y, z, xx, yy) 0
+#define IS_ERR(x) 0
+struct class_simple 
+{
+	int notused;
+};	
+#endif /* No SYSFS */
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0)
+#define KERNEL_2_5
+
+/*
+ * We still are using MOD_INC_USE_COUNT/MOD_DEC_USE_COUNT in the set_use_inc 
+ * function of all modules for 2.4 kernel compatibility.
+ * 
+ * For 2.6 kernels reference counting is done in lirc_dev by 
+ * try_module_get()/module_put() because the old approach is racy.
+ * 
+ */
+#ifdef MOD_INC_USE_COUNT
+#undef MOD_INC_USE_COUNT
+#endif
+#define MOD_INC_USE_COUNT
+
+#ifdef MOD_DEC_USE_COUNT
+#undef MOD_DEC_USE_COUNT
+#endif
+#define MOD_DEC_USE_COUNT
+
+#ifdef EXPORT_NO_SYMBOLS
+#undef EXPORT_NO_SYMBOLS
+#endif
+#define EXPORT_NO_SYMBOLS
+
+#else  /* Kernel < 2.5.0 */
+
+static inline int try_module_get(struct module *module)
+{
+	return 1;
+}
+
+static inline void module_put(struct module *module)
+{
+}
+
+#endif /* Kernel >= 2.5.0 */
+
+#ifndef MODULE_LICENSE
+#define MODULE_LICENSE(x)
+#endif
+
+#ifndef MODULE_PARM_DESC
+#define MODULE_PARM_DESC(x,y)
+#endif
+
+#ifndef MODULE_ALIAS_CHARDEV_MAJOR
+#define MODULE_ALIAS_CHARDEV_MAJOR(x)
+#endif
+
+#ifndef MODULE_DEVICE_TABLE
+#define MODULE_DEVICE_TABLE(x,y)
+#endif
+
+#ifndef IRQ_RETVAL
+typedef void irqreturn_t;
+#define IRQ_NONE
+#define IRQ_HANDLED
+#define IRQ_RETVAL(x)
+#endif
+
+#ifndef MOD_IN_USE
+#ifdef CONFIG_MODULE_UNLOAD
+#define MOD_IN_USE module_refcount(THIS_MODULE)
+#else
+#error "LIRC modules currently require"
+#error "  'Loadable module support  --->  Module unloading'"
+#error "to be enabled in the kernel"
+#endif
+#endif
+
+#if !defined(local_irq_save)
+#define local_irq_save(flags) do{ save_flags(flags);cli(); } while(0)
+#endif
+#if !defined(local_irq_restore)
+#define local_irq_restore(flags) do{ restore_flags(flags); } while(0)
+#endif
+
+#if !defined(pci_pretty_name)
+#define pci_pretty_name(dev) ((dev)->name)
+#endif
+
+/*************************** I2C specific *****************************/
+#include <linux/i2c.h>
+
+#ifndef I2C_CLIENT_END
+#error "********************************************************"
+#error " Sorry, this driver needs the new I2C stack.            "
+#error " You can get it at http://www2.lm-sensors.nu/~lm78/.    "
+#error "********************************************************"
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0)
+
+#undef i2c_use_client
+#define i2c_use_client(client_ptr) do { \
+	if ((client_ptr)->adapter->inc_use) \
+		(client_ptr)->adapter->inc_use((client_ptr)->adapter); \
+} while (0)
+
+#undef i2c_release_client
+#define i2c_release_client(client_ptr) do { \
+	if ((client_ptr)->adapter->dec_use) \
+		(client_ptr)->adapter->dec_use((client_ptr)->adapter); \
+} while (0)
+
+#undef i2c_get_clientdata
+#define i2c_get_clientdata(client) ((client)->data)
+
+
+#undef i2c_set_clientdata
+#define i2c_set_clientdata(client_ptr, new_data) do { \
+	(client_ptr)->data = new_data; \
+} while (0)
+
+
+#endif
+
+#endif /* _KCOMPAT_H */
diff -urN --exclude='*.orig' --exclude='*.rej' linux-2.6.11-mm4/include/linux/lirc.h linux-2.6.11-no1/include/linux/lirc.h
--- linux-2.6.11-mm4/include/linux/lirc.h	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.11-no1/include/linux/lirc.h	2005-03-30 00:11:50.216647975 -0500
@@ -0,0 +1,101 @@
+/*      $Id: lirc.h,v 5.9 2005/02/27 15:05:39 lirc Exp $      */
+
+#ifndef _LINUX_LIRC_H
+#define _LINUX_LIRC_H
+
+#if defined (__linux__)
+#include <asm/types.h>
+#include <linux/ioctl.h>
+#else
+#include <sys/types.h>
+typedef u_int32_t __u32;
+#endif
+
+#define PULSE_BIT  0x01000000
+#define PULSE_MASK 0x00FFFFFF
+
+typedef int lirc_t;
+
+/*
+ * 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_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_SEND(x) ((x)&LIRC_CAN_SEND_MASK)
+#define LIRC_CAN_REC(x) ((x)&LIRC_CAN_REC_MASK)
+
+/*
+ * 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)
+
+/* 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)
+
+#endif
