diff -u -p --new-file linux/drivers/net/irda-d6/Config.in linux/drivers/net/irda/Config.in
--- linux/drivers/net/irda-d6/Config.in	Mon Oct 21 14:15:03 2002
+++ linux/drivers/net/irda/Config.in	Thu Oct 31 16:26:11 2002
@@ -3,7 +3,6 @@ comment 'Infrared-port device drivers'
 
 comment 'SIR device drivers'
 dep_tristate 'IrTTY (uses Linux serial driver)' CONFIG_IRTTY_SIR $CONFIG_IRDA
-dep_tristate 'IrPORT (IrDA serial driver)' CONFIG_IRPORT_SIR $CONFIG_IRDA
 
 comment 'Dongle support' 
 bool 'Serial dongle support' CONFIG_DONGLE
@@ -11,6 +10,18 @@ if [ "$CONFIG_DONGLE" != "n" ]; then
    dep_tristate '  ESI JetEye PC dongle' CONFIG_ESI_DONGLE $CONFIG_IRDA
    dep_tristate '  ACTiSYS IR-220L and IR220L+ dongle' CONFIG_ACTISYS_DONGLE $CONFIG_IRDA
    dep_tristate '  Tekram IrMate 210B dongle' CONFIG_TEKRAM_DONGLE $CONFIG_IRDA
+fi
+
+comment 'Old SIR device drivers'
+dep_tristate 'Old IrTTY (broken)' CONFIG_IRTTY_OLD $CONFIG_IRDA
+dep_tristate 'IrPORT (IrDA serial driver)' CONFIG_IRPORT_SIR $CONFIG_IRDA
+
+comment 'Old Dongle support (for Old SIR drivers)' 
+bool 'Old Serial dongle support' CONFIG_DONGLE_OLD
+if [ "$CONFIG_DONGLE_OLD" != "n" ]; then
+   dep_tristate '  ESI JetEye PC dongle' CONFIG_ESI_OLD $CONFIG_IRDA
+   dep_tristate '  ACTiSYS IR-220L and IR220L+ dongle' CONFIG_ACTISYS_OLD $CONFIG_IRDA
+   dep_tristate '  Tekram IrMate 210B dongle' CONFIG_TEKRAM_OLD $CONFIG_IRDA
    dep_tristate '  Greenwich GIrBIL dongle' CONFIG_GIRBIL_DONGLE $CONFIG_IRDA
    dep_tristate '  Parallax LiteLink dongle' CONFIG_LITELINK_DONGLE $CONFIG_IRDA
    dep_tristate '  Microchip MCP2120' CONFIG_MCP2120_DONGLE $CONFIG_IRDA
diff -u -p --new-file linux/drivers/net/irda-d6/Makefile linux/drivers/net/irda/Makefile
--- linux/drivers/net/irda-d6/Makefile	Mon Oct 21 14:15:03 2002
+++ linux/drivers/net/irda/Makefile	Thu Oct 31 16:33:46 2002
@@ -5,10 +5,12 @@
 # Rewritten to use lists instead of if-statements.
 #
 
-export-objs	= irport.o
+export-objs	= irport.o sir_core.o
 
-obj-$(CONFIG_IRTTY_SIR)		+= irtty.o
+# Old SIR drivers (irtty is broken)
+obj-$(CONFIG_IRTTY_OLD)		+= irtty.o
 obj-$(CONFIG_IRPORT_SIR)	+= 		irport.o
+# FIR drivers
 obj-$(CONFIG_USB_IRDA)		+= irda-usb.o
 obj-$(CONFIG_NSC_FIR)		+= nsc-ircc.o
 obj-$(CONFIG_WINBOND_FIR)	+= w83977af_ir.o
@@ -18,9 +20,10 @@ obj-$(CONFIG_TOSHIBA_FIR)	+= donauboe.o
 obj-$(CONFIG_SMC_IRCC_FIR)	+= smc-ircc.o	irport.o
 obj-$(CONFIG_ALI_FIR)		+= ali-ircc.o
 obj-$(CONFIG_VLSI_FIR)		+= vlsi_ir.o
-obj-$(CONFIG_ESI_DONGLE)	+= esi.o
-obj-$(CONFIG_TEKRAM_DONGLE)	+= tekram.o
-obj-$(CONFIG_ACTISYS_DONGLE)	+= actisys.o
+# Old dongle drivers for old SIR drivers
+obj-$(CONFIG_ESI_OLD)		+= esi.o
+obj-$(CONFIG_TEKRAM_OLD)	+= tekram.o
+obj-$(CONFIG_ACTISYS_OLD)	+= actisys.o
 obj-$(CONFIG_GIRBIL_DONGLE)	+= girbil.o
 obj-$(CONFIG_LITELINK_DONGLE)	+= litelink.o
 obj-$(CONFIG_OLD_BELKIN_DONGLE)	+= old_belkin.o
@@ -28,5 +31,14 @@ obj-$(CONFIG_EP7211_IR)		+= ep7211_ir.o
 obj-$(CONFIG_MCP2120_DONGLE)	+= mcp2120.o
 obj-$(CONFIG_ACT200L_DONGLE)	+= act200l.o
 obj-$(CONFIG_MA600_DONGLE)	+= ma600.o
+# New SIR drivers
+obj-$(CONFIG_IRTTY_SIR)		+= irtty-sir.o	sir-dev.o
+# New dongles drivers for new SIR drivers
+obj-$(CONFIG_ESI_DONGLE)	+= esi-sir.o
+obj-$(CONFIG_TEKRAM_DONGLE)	+= tekram-sir.o
+obj-$(CONFIG_ACTISYS_DONGLE)	+= actisys-sir.o
+
+# The SIR helper module
+sir-dev-objs := sir_core.o sir_dev.o sir_dongle.o sir_kthread.o
 
 include $(TOPDIR)/Rules.make
diff -u -p --new-file linux/drivers/net/irda-d6/actisys-sir.c linux/drivers/net/irda/actisys-sir.c
--- linux/drivers/net/irda-d6/actisys-sir.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/irda/actisys-sir.c	Thu Oct 31 16:29:44 2002
@@ -0,0 +1,240 @@
+/*********************************************************************
+ *                
+ * Filename:      actisys.c
+ * Version:       1.1
+ * Description:   Implementation for the ACTiSYS IR-220L and IR-220L+ 
+ *                dongles
+ * Status:        Beta.
+ * Authors:       Dag Brattli <dagb@cs.uit.no> (initially)
+ *		  Jean Tourrilhes <jt@hpl.hp.com> (new version)
+ *		  Martin Diehl <mad@mdiehl.de> (new version for sir_dev)
+ * Created at:    Wed Oct 21 20:02:35 1998
+ * Modified at:   Sun Oct 27 22:02:13 2002
+ * Modified by:   Martin Diehl <mad@mdiehl.de>
+ * 
+ *     Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved.
+ *     Copyright (c) 1999 Jean Tourrilhes
+ *     Copyright (c) 2002 Martin Diehl
+ *      
+ *     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.
+ *  
+ *     Neither Dag Brattli nor University of Tromsø admit liability nor
+ *     provide warranty for any of this software. This material is 
+ *     provided "AS-IS" and at no charge.
+ *     
+ ********************************************************************/
+
+/*
+ * Changelog
+ *
+ * 0.8 -> 0.9999 - Jean
+ *	o New initialisation procedure : much safer and correct
+ *	o New procedure the change speed : much faster and simpler
+ *	o Other cleanups & comments
+ *	Thanks to Lichen Wang @ Actisys for his excellent help...
+ *
+ * 1.0 -> 1.1 - Martin Diehl
+ *	modified for new sir infrastructure
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+
+#include <net/irda/irda.h>
+
+#include "sir-dev.h"
+
+/* 
+ * Define the timing of the pulses we send to the dongle (to reset it, and
+ * to toggle speeds). Basically, the limit here is the propagation speed of
+ * the signals through the serial port, the dongle being much faster.  Any
+ * serial port support 115 kb/s, so we are sure that pulses 8.5 us wide can
+ * go through cleanly . If you are on the wild side, you can try to lower
+ * this value (Actisys recommended me 2 us, and 0 us work for me on a P233!)
+ */
+#define MIN_DELAY 10	/* 10 us to be on the conservative side */
+
+static int actisys_open(struct sir_dev *);
+static int actisys_close(struct sir_dev *);
+static int actisys_change_speed(struct sir_dev *, unsigned);
+static int actisys_reset(struct sir_dev *);
+
+/* These are the baudrates supported, in the order available */
+/* Note : the 220L doesn't support 38400, but we will fix that below */
+static __u32 baud_rates[] = { 9600, 19200, 57600, 115200, 38400 };
+
+#define MAX_SPEEDS (sizeof(baud_rates)/sizeof(baud_rates[0]))
+
+static struct dongle_driver act220l = {
+	.owner		= THIS_MODULE,
+	.driver_name	= "Actisys ACT-220L",
+	.type		= IRDA_ACTISYS_DONGLE,
+	.open		= actisys_open,
+	.close		= actisys_close,
+	.reset		= actisys_reset,
+	.set_speed	= actisys_change_speed,
+};
+
+static struct dongle_driver act220l_plus = {
+	.owner		= THIS_MODULE,
+	.driver_name	= "Actisys ACT-220L+",
+	.type		= IRDA_ACTISYS_PLUS_DONGLE,
+	.open		= actisys_open,
+	.close		= actisys_close,
+	.reset		= actisys_reset,
+	.set_speed	= actisys_change_speed,
+};
+
+int __init actisys_sir_init(void)
+{
+	int ret;
+
+	/* First, register an Actisys 220L dongle */
+	ret = irda_register_dongle(&act220l);
+	if (ret < 0)
+		return ret;
+
+	/* Now, register an Actisys 220L+ dongle */
+	ret = irda_register_dongle(&act220l_plus);
+	if (ret < 0) {
+		irda_unregister_dongle(&act220l);
+		return ret;
+	}
+	return 0;
+}
+
+void __exit actisys_sir_cleanup(void)
+{
+	/* We have to remove both dongles */
+	irda_unregister_dongle(&act220l_plus);
+	irda_unregister_dongle(&act220l);
+}
+
+static int actisys_open(struct sir_dev *dev)
+{
+	struct qos_info *qos = &dev->qos;
+
+	dev->set_dtr_rts(dev, TRUE, TRUE);
+
+	/* Set the speeds we can accept */
+	qos->baud_rate.bits &= IR_9600|IR_19200|IR_38400|IR_57600|IR_115200;
+
+	/* Remove support for 38400 if this is not a 220L+ dongle */
+	if (dev->dongle_drv->type == IRDA_ACTISYS_DONGLE)
+		qos->baud_rate.bits &= ~IR_38400;
+
+	qos->min_turn_time.bits = 0x7f; /* Needs 0.01 ms */
+	irda_qos_bits_to_value(qos);
+
+	return 0;
+}
+
+static int actisys_close(struct sir_dev *dev)
+{
+	/* Power off the dongle */
+	dev->set_dtr_rts(dev, FALSE, FALSE);
+
+	return 0;
+}
+
+/*
+ * Function actisys_change_speed (task)
+ *
+ *    Change speed of the ACTiSYS IR-220L and IR-220L+ type IrDA dongles.
+ *    To cycle through the available baud rates, pulse RTS low for a few us.
+ *
+ *	First, we reset the dongle to always start from a known state.
+ *	Then, we cycle through the speeds by pulsing RTS low and then up.
+ *	The dongle allow us to pulse quite fast, se we can set speed in one go,
+ * which is must faster ( < 100 us) and less complex than what is found
+ * in some other dongle drivers...
+ *	Note that even if the new speed is the same as the current speed,
+ * we reassert the speed. This make sure that things are all right,
+ * and it's fast anyway...
+ *	By the way, this function will work for both type of dongles,
+ * because the additional speed is at the end of the sequence...
+ */
+static int actisys_change_speed(struct sir_dev *dev, unsigned speed)
+{
+	int ret = 0;
+	int i = 0;
+
+        IRDA_DEBUG(4, "%s(), speed=%d (was %d)\n", __FUNCTION__,
+        	speed, dev->speed);
+
+	/* dongle was already resetted from irda_request state machine,
+	 * we are in known state (dongle default)
+	 */
+
+	/* 
+	 * Now, we can set the speed requested. Send RTS pulses until we
+         * reach the target speed 
+	 */
+	for (i=0; i<MAX_SPEEDS; i++) {
+		if (speed == baud_rates[i]) {
+			dev->speed = baud_rates[i];
+			break;
+		}
+		/* Set RTS low for 10 us */
+		dev->set_dtr_rts(dev, TRUE, FALSE);
+		udelay(MIN_DELAY);
+
+		/* Set RTS high for 10 us */
+		dev->set_dtr_rts(dev, TRUE, TRUE);
+		udelay(MIN_DELAY);
+	}
+
+	/* Check if life is sweet... */
+	if (i >= MAX_SPEEDS)
+		ret = -1;  /* This should not happen */
+
+	/* Basta lavoro, on se casse d'ici... */
+	return ret;
+}
+
+/*
+ * Function actisys_reset (task)
+ *
+ *      Reset the Actisys type dongle. Warning, this function must only be
+ *      called with a process context!
+ *
+ * We need to do two things in this function :
+ *	o first make sure that the dongle is in a state where it can operate
+ *	o second put the dongle in a know state
+ *
+ *	The dongle is powered of the RTS and DTR lines. In the dongle, there
+ * is a big capacitor to accomodate the current spikes. This capacitor
+ * takes a least 50 ms to be charged. In theory, the Bios set those lines
+ * up, so by the time we arrive here we should be set. It doesn't hurt
+ * to be on the conservative side, so we will wait...
+ * <Martin : move above comment to irda_config_fsm>
+ *	Then, we set the speed to 9600 b/s to get in a known state (see in
+ * change_speed for details). It is needed because the IrDA stack
+ * has tried to set the speed immediately after our first return,
+ * so before we can be sure the dongle is up and running.
+ */
+
+static int actisys_reset(struct sir_dev *dev)
+{
+	/* Reset the dongle : set DTR low for 10 us */
+	dev->set_dtr_rts(dev, FALSE, TRUE);
+	udelay(MIN_DELAY);
+
+	/* Go back to normal mode */
+	dev->set_dtr_rts(dev, TRUE, TRUE);
+	
+	dev->speed = 9600;	/* That's the default */
+
+	return 0;
+}
+
+MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no> - Jean Tourrilhes <jt@hpl.hp.com>");
+MODULE_DESCRIPTION("ACTiSYS IR-220L and IR-220L+ dongle driver");	
+MODULE_LICENSE("GPL");
+
+module_init(actisys_sir_init);
+module_exit(actisys_sir_cleanup);
diff -u -p --new-file linux/drivers/net/irda-d6/esi-sir.c linux/drivers/net/irda/esi-sir.c
--- linux/drivers/net/irda-d6/esi-sir.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/irda/esi-sir.c	Thu Oct 31 16:30:19 2002
@@ -0,0 +1,145 @@
+/*********************************************************************
+ *                
+ * Filename:      esi.c
+ * Version:       1.6
+ * Description:   Driver for the Extended Systems JetEye PC dongle
+ * Status:        Experimental.
+ * Author:        Dag Brattli <dagb@cs.uit.no>
+ * Created at:    Sat Feb 21 18:54:38 1998
+ * Modified at:   Sun Oct 27 22:01:04 2002
+ * Modified by:   Martin Diehl <mad@mdiehl.de>
+ * 
+ *     Copyright (c) 1999 Dag Brattli, <dagb@cs.uit.no>,
+ *     Copyright (c) 1998 Thomas Davis, <ratbert@radiks.net>,
+ *     Copyright (c) 2002 Martin Diehl, <mad@mdiehl.de>,
+ *     All Rights Reserved.
+ *     
+ *     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/delay.h>
+#include <linux/init.h>
+
+#include <net/irda/irda.h>
+
+#include "sir-dev.h"
+
+static int esi_open(struct sir_dev *);
+static int esi_close(struct sir_dev *);
+static int esi_change_speed(struct sir_dev *, unsigned);
+static int esi_reset(struct sir_dev *);
+
+static struct dongle_driver esi = {
+	.owner		= THIS_MODULE,
+	.driver_name	= "JetEye PC ESI-9680 PC",
+	.type		= IRDA_ESI_DONGLE,
+	.open		= esi_open,
+	.close		= esi_close,
+	.reset		= esi_reset,
+	.set_speed	= esi_change_speed,
+};
+
+static int __init esi_sir_init(void)
+{
+	return irda_register_dongle(&esi);
+}
+
+static void __exit esi_sir_cleanup(void)
+{
+	irda_unregister_dongle(&esi);
+}
+
+static int esi_open(struct sir_dev *dev)
+{
+	struct qos_info *qos = &dev->qos;
+
+	qos->baud_rate.bits &= IR_9600|IR_19200|IR_115200;
+	qos->min_turn_time.bits = 0x01; /* Needs at least 10 ms */
+	irda_qos_bits_to_value(qos);
+
+	/* shouldn't we do set_dtr_rts(FALSE, TRUE) here (power up at 9600)? */
+
+	return 0;
+}
+
+static int esi_close(struct sir_dev *dev)
+{
+	/* Power off dongle */
+	dev->set_dtr_rts(dev, FALSE, FALSE);
+
+	return 0;
+}
+
+/*
+ * Function esi_change_speed (task)
+ *
+ *    Set the speed for the Extended Systems JetEye PC ESI-9680 type dongle
+ *
+ */
+static int esi_change_speed(struct sir_dev *dev, unsigned speed)
+{
+	int dtr, rts;
+	
+	switch (speed) {
+	case 19200:
+		dtr = TRUE;
+		rts = FALSE;
+		break;
+	case 115200:
+		dtr = rts = TRUE;
+		break;
+	default:
+		speed = 9600;
+		/* fall through */
+	case 9600:
+		dtr = FALSE;
+		rts = TRUE;
+		break;
+	}
+
+	/* Change speed of dongle */
+	dev->set_dtr_rts(dev, dtr, rts);
+	dev->speed = speed;
+
+	/* do we need some delay for power stabilization? */
+
+	return 0;
+}
+
+/*
+ * Function esi_reset (task)
+ *
+ *    Reset dongle;
+ *
+ */
+static int esi_reset(struct sir_dev *dev)
+{
+	dev->set_dtr_rts(dev, FALSE, FALSE);
+
+	/* Hm, probably repower to 9600 and some delays? */
+
+	return 0;
+}
+
+MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>");
+MODULE_DESCRIPTION("Extended Systems JetEye PC dongle driver");
+MODULE_LICENSE("GPL");
+
+module_init(esi_sir_init);
+module_exit(esi_sir_cleanup);
+
diff -u -p --new-file linux/drivers/net/irda-d6/irtty-sir.c linux/drivers/net/irda/irtty-sir.c
--- linux/drivers/net/irda-d6/irtty-sir.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/irda/irtty-sir.c	Thu Oct 31 18:19:01 2002
@@ -0,0 +1,684 @@
+/*********************************************************************
+ *                
+ * Filename:      irtty-sir.c
+ * Version:       2.0
+ * Description:   IrDA line discipline implementation
+ * Status:        Experimental.
+ * Author:        Dag Brattli <dagb@cs.uit.no>
+ * Created at:    Tue Dec  9 21:18:38 1997
+ * Modified at:   Sun Oct 27 22:13:30 2002
+ * Modified by:   Martin Diehl <mad@mdiehl.de>
+ * Sources:       slip.c by Laurence Culhane,   <loz@holmes.demon.co.uk>
+ *                          Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+ * 
+ *     Copyright (c) 1998-2000 Dag Brattli,
+ *     Copyright (c) 2002 Martin Diehl,
+ *     All Rights Reserved.
+ *      
+ *     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.
+ *  
+ *     Neither Dag Brattli nor University of Tromsø admit liability nor
+ *     provide warranty for any of this software. This material is 
+ *     provided "AS-IS" and at no charge.
+ *     
+ ********************************************************************/    
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/tty.h>
+#include <linux/init.h>
+#include <asm/uaccess.h>
+#include <linux/smp_lock.h>
+
+#include <net/irda/irda.h>
+#include <net/irda/irda_device.h>
+
+#include "sir-dev.h"
+#include "irtty-sir.h"
+
+MODULE_PARM(qos_mtt_bits, "i");
+MODULE_PARM_DESC(qos_mtt_bits, "Minimum Turn Time");
+
+static int qos_mtt_bits = 0x03;      /* 5 ms or more */
+
+/* ------------------------------------------------------- */
+
+/* device configuration callbacks always invoked with irda-thread context */
+
+/* find out, how many chars we have in buffers below us
+ * this is allowed to lie, i.e. return less chars than we
+ * actually have. The returned value is used to determine
+ * how long the irdathread should wait before doing the
+ * real blocking wait_until_sent()
+ */
+
+static int irtty_chars_in_buffer(struct sir_dev *dev)
+{
+	struct sirtty_cb *priv = dev->priv;
+
+	ASSERT(priv != NULL, return -1;);
+	ASSERT(priv->magic == IRTTY_MAGIC, return -1;);
+
+	return priv->tty->driver.chars_in_buffer(priv->tty);
+}
+
+/* Wait (sleep) until underlaying hardware finished transmission
+ * i.e. hardware buffers are drained
+ * this must block and not return before all characters are really sent
+ *
+ * If the tty sits on top of a 16550A-like uart, there are typically
+ * up to 16 bytes in the fifo - f.e. 9600 bps 8N1 needs 16.7 msec
+ *
+ * With usbserial the uart-fifo is basically replaced by the converter's
+ * outgoing endpoint buffer, which can usually hold 64 bytes (at least).
+ * With pl2303 it appears we are safe with 60msec here.
+ *
+ * I really wish all serial drivers would provide
+ * correct implementation of wait_until_sent()
+ */
+
+#define USBSERIAL_TX_DONE_DELAY	60
+
+static void irtty_wait_until_sent(struct sir_dev *dev)
+{
+	struct sirtty_cb *priv = dev->priv;
+	struct tty_struct *tty;
+
+	ASSERT(priv != NULL, return;);
+	ASSERT(priv->magic == IRTTY_MAGIC, return;);
+
+	tty = priv->tty;
+	if (tty->driver.wait_until_sent) {
+		lock_kernel();
+		tty->driver.wait_until_sent(tty, MSECS_TO_JIFFIES(100));
+		unlock_kernel();
+	}
+	else {
+		set_task_state(current, TASK_UNINTERRUPTIBLE);
+		schedule_timeout(MSECS_TO_JIFFIES(USBSERIAL_TX_DONE_DELAY));
+	}
+}
+
+/* 
+ *  Function irtty_change_speed (dev, speed)
+ *
+ *    Change the speed of the serial port.
+ *
+ * This may sleep in set_termios (usbserial driver f.e.) and must
+ * not be called from interrupt/timer/tasklet therefore.
+ * All such invocations are deferred to kIrDAd now so we can sleep there.
+ */
+
+static int irtty_change_speed(struct sir_dev *dev, unsigned speed)
+{
+	struct sirtty_cb *priv = dev->priv;
+	struct tty_struct *tty;
+        struct termios old_termios;
+	int cflag;
+
+	ASSERT(priv != NULL, return -1;);
+	ASSERT(priv->magic == IRTTY_MAGIC, return -1;);
+
+	tty = priv->tty;
+
+	lock_kernel();
+	old_termios = *(tty->termios);
+	cflag = tty->termios->c_cflag;
+
+	cflag &= ~CBAUD;
+
+	IRDA_DEBUG(2, "%s(), Setting speed to %d\n", __FUNCTION__, speed);
+
+	switch (speed) {
+	case 1200:
+		cflag |= B1200;
+		break;
+	case 2400:
+		cflag |= B2400;
+		break;
+	case 4800:
+		cflag |= B4800;
+		break;
+	case 19200:
+		cflag |= B19200;
+		break;
+	case 38400:
+		cflag |= B38400;
+		break;
+	case 57600:
+		cflag |= B57600;
+		break;
+	case 115200:
+		cflag |= B115200;
+		break;
+	case 9600:
+	default:
+		cflag |= B9600;
+		break;
+	}	
+
+	tty->termios->c_cflag = cflag;
+	if (tty->driver.set_termios)
+		tty->driver.set_termios(tty, &old_termios);
+	unlock_kernel();
+
+	priv->io.speed = speed;
+
+	return 0;
+}
+
+/*
+ * Function irtty_set_dtr_rts (dev, dtr, rts)
+ *
+ *    This function can be used by dongles etc. to set or reset the status
+ *    of the dtr and rts lines
+ */
+
+static int irtty_set_dtr_rts(struct sir_dev *dev, int dtr, int rts)
+{
+	struct sirtty_cb *priv = dev->priv;
+	int arg = 0;
+
+	ASSERT(priv != NULL, return -1;);
+	ASSERT(priv->magic == IRTTY_MAGIC, return -1;);
+
+#ifdef TIOCM_OUT2 /* Not defined for ARM */
+	arg = TIOCM_OUT2;
+#endif
+	if (rts)
+		arg |= TIOCM_RTS;
+	if (dtr)
+		arg |= TIOCM_DTR;
+
+	/*
+	 * The ioctl() function, or actually set_modem_info() in serial.c
+	 * expects a pointer to the argument in user space. This is working
+	 * here because we are always called from the kIrDAd thread which
+	 * has set_fs(KERNEL_DS) permanently set. Therefore copy_from_user()
+	 * is happy with our arg-parameter being local here in kernel space.
+	 */
+
+	lock_kernel();
+	if (priv->tty->driver.ioctl(priv->tty, NULL, TIOCMSET, (unsigned long) &arg)) { 
+		IRDA_DEBUG(2, "%s(), error doing ioctl!\n", __FUNCTION__);
+	}
+	unlock_kernel();
+
+	return 0;
+}
+
+/* ------------------------------------------------------- */
+
+/* called from sir_dev when there is more data to send
+ * context is either netdev->hard_xmit or some transmit-completion bh
+ * i.e. we are under spinlock here and must not sleep.
+ *
+ * Note: as of 2.5.44 the usb-serial driver calls down() on a semaphore
+ * hence we are hitting the might_sleep bugcatcher. IMHO the whole tty-api
+ * would be pretty pointless if write_room/write would be allowed to sleep.
+ * Furthermore other tty ldiscs (like ppp) do also require the driver not
+ * to sleep there. Hence this is considered a current limitation of
+ * usb-serial.
+ */
+
+static int irtty_do_write(struct sir_dev *dev, const unsigned char *ptr, size_t len)
+{
+	struct sirtty_cb *priv = dev->priv;
+	struct tty_struct *tty;
+	int writelen;
+
+	ASSERT(priv != NULL, return -1;);
+	ASSERT(priv->magic == IRTTY_MAGIC, return -1;);
+
+	tty = priv->tty;
+	if (!tty->driver.write)
+		return 0;
+	tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
+	if (tty->driver.write_room) {
+		writelen = tty->driver.write_room(tty);
+		if (writelen > len)
+			writelen = len;
+	}
+	else
+		writelen = len;
+	return tty->driver.write(tty, 0, ptr, writelen);
+}
+
+/* ------------------------------------------------------- */
+
+/* irda line discipline callbacks */
+
+/* 
+ *  Function irtty_receive_buf( tty, cp, count)
+ *
+ *    Handle the 'receiver data ready' interrupt.  This function is called
+ *    by the 'tty_io' module in the kernel when a block of IrDA data has
+ *    been received, which can now be decapsulated and delivered for
+ *    further processing 
+ *
+ * calling context depends on underlying driver and tty->low_latency!
+ * for example (low_latency: 1 / 0):
+ * serial.c:	uart-interrupt / softint
+ * usbserial:	urb-complete-interrupt / softint
+ */
+
+static void irtty_receive_buf(struct tty_struct *tty, const unsigned char *cp,
+			      char *fp, int count) 
+{
+	struct sir_dev *dev;
+	struct sirtty_cb *priv = tty->disc_data;
+	int	i;
+
+	if (unlikely(!priv || priv->magic!=IRTTY_MAGIC))
+		return;
+	/* Please use ASSERT - Fix ASSERT as needed - Jean II */
+
+	if (unlikely(count==0))		/* yes, this happens */
+		return;
+
+	dev = priv->dev;
+	if (!dev) {
+		printk(KERN_ERR "%s(), not ready yet!\n", __FUNCTION__);
+		return;
+	}
+
+	for (i = 0; i < count; i++) {
+		/* 
+		 *  Characters received with a parity error, etc?
+		 */
+ 		if (fp && *fp++) { 
+			IRDA_DEBUG(0, "Framing or parity error!\n");
+			sirdev_receive(dev, NULL, 0);	/* notify sir_dev (updating stats) */
+			return;
+ 		}
+	}
+
+	sirdev_receive(dev, cp, count);
+}
+
+/*
+ * Function irtty_receive_room (tty)
+ *
+ *    Used by the TTY to find out how much data we can receive at a time
+ * 
+*/
+static int irtty_receive_room(struct tty_struct *tty) 
+{
+	struct sirtty_cb *priv = tty->disc_data;
+
+	if (unlikely(!priv || priv->magic!=IRTTY_MAGIC))
+		return 0;
+
+	return 65536;  /* We can handle an infinite amount of data. :-) */
+}
+
+/*
+ * Function irtty_write_wakeup (tty)
+ *
+ *    Called by the driver when there's room for more data.  If we have
+ *    more packets to send, we send them here.
+ *
+ */
+static void irtty_write_wakeup(struct tty_struct *tty) 
+{
+	struct sirtty_cb *priv = tty->disc_data;
+
+	if (unlikely(!priv || priv->magic!=IRTTY_MAGIC))
+		return;
+
+	tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
+
+	if (priv->dev)
+		sirdev_write_complete(priv->dev);
+}
+
+/* ------------------------------------------------------- */
+
+/*
+ * Function irtty_stop_receiver (tty, stop)
+ *
+ */
+
+static inline void irtty_stop_receiver(struct tty_struct *tty, int stop)
+{
+	struct termios old_termios;
+	int cflag;
+
+	lock_kernel();
+	old_termios = *(tty->termios);
+	cflag = tty->termios->c_cflag;
+	
+	if (stop)
+		cflag &= ~CREAD;
+	else
+		cflag |= CREAD;
+
+	tty->termios->c_cflag = cflag;
+	if (tty->driver.set_termios)
+		tty->driver.set_termios(tty, &old_termios);
+	unlock_kernel();
+}
+
+/*****************************************************************/
+
+DECLARE_MUTEX(irtty_sem);		/* serialize ldisc open/close with sir_dev */
+
+/* notifier from sir_dev when irda% device gets opened (ifup) */
+
+static int irtty_start_dev(struct sir_dev *dev)
+{
+	struct sirtty_cb *priv;
+	struct tty_struct *tty;
+
+	/* serialize with ldisc open/close */
+	down(&irtty_sem);
+
+	priv = dev->priv;
+	if (unlikely(!priv || priv->magic!=IRTTY_MAGIC)) {
+		up(&irtty_sem);
+		return -ESTALE;
+	}
+
+	tty = priv->tty;
+
+	if (tty->driver.start)
+		tty->driver.start(tty);
+	/* Make sure we can receive more data */
+	irtty_stop_receiver(tty, FALSE);
+
+	up(&irtty_sem);
+	return 0;
+}
+
+/* notifier from sir_dev when irda% device gets closed (ifdown) */
+
+static int irtty_stop_dev(struct sir_dev *dev)
+{
+	struct sirtty_cb *priv;
+	struct tty_struct *tty;
+
+	/* serialize with ldisc open/close */
+	down(&irtty_sem);
+
+	priv = dev->priv;
+	if (unlikely(!priv || priv->magic!=IRTTY_MAGIC)) {
+		up(&irtty_sem);
+		return -ESTALE;
+	}
+
+	tty = priv->tty;
+
+	/* Make sure we don't receive more data */
+	irtty_stop_receiver(tty, TRUE);
+	if (tty->driver.stop)
+		tty->driver.stop(tty);
+
+	up(&irtty_sem);
+
+	return 0;
+}
+
+/* ------------------------------------------------------- */
+
+struct sir_driver sir_tty_drv = {
+	.owner			= THIS_MODULE,
+	.driver_name		= "sir_tty",
+	.start_dev		= irtty_start_dev,
+	.stop_dev		= irtty_stop_dev,
+	.do_write		= irtty_do_write,
+	.chars_in_buffer	= irtty_chars_in_buffer,
+	.wait_until_sent	= irtty_wait_until_sent,
+	.set_speed		= irtty_change_speed,
+	.set_dtr_rts		= irtty_set_dtr_rts,
+};
+
+/* ------------------------------------------------------- */
+
+/*
+ * Function irtty_ioctl (tty, file, cmd, arg)
+ *
+ *     The Swiss army knife of system calls :-)
+ *
+ */
+static int irtty_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct irtty_info { char name[6]; } info;
+	struct sir_dev *dev;
+	struct sirtty_cb *priv = tty->disc_data;
+	int size = _IOC_SIZE(cmd);
+	int err = 0;
+
+	ASSERT(priv != NULL, return -ENODEV;);
+	ASSERT(priv->magic == IRTTY_MAGIC, return -EBADR;);
+
+	IRDA_DEBUG(3, "%s(cmd=0x%X)\n", __FUNCTION__, cmd);
+
+	dev = priv->dev;
+	ASSERT(dev != NULL, return -1;);
+
+	if (_IOC_DIR(cmd) & _IOC_READ)
+		err = verify_area(VERIFY_WRITE, (void *) arg, size);
+	else if (_IOC_DIR(cmd) & _IOC_WRITE)
+		err = verify_area(VERIFY_READ, (void *) arg, size);
+	if (err)
+		return err;
+	
+	switch (cmd) {
+	case TCGETS:
+	case TCGETA:
+		err = n_tty_ioctl(tty, file, cmd, arg);
+		break;
+
+	case IRTTY_IOCTDONGLE:
+		/* this call blocks for completion */
+		err = sirdev_set_dongle(dev, (IRDA_DONGLE) arg);
+		break;
+
+	case IRTTY_IOCGET:
+		ASSERT(dev->netdev != NULL, return -1;);
+
+		memset(&info, 0, sizeof(info)); 
+		strncpy(info.name, dev->netdev->name, sizeof(info.name)-1);
+
+		if (copy_to_user((void *)arg, &info, sizeof(info)))
+			err = -EFAULT;
+		break;
+	default:
+		err = -ENOIOCTLCMD;
+		break;
+	}
+	return err;
+}
+
+
+/* 
+ *  Function irtty_open(tty)
+ *
+ *    This function is called by the TTY module when the IrDA line
+ *    discipline is called for.  Because we are sure the tty line exists,
+ *    we only have to link it to a free IrDA channel.  
+ */
+static int irtty_open(struct tty_struct *tty) 
+{
+	struct sir_dev *dev;
+	struct sirtty_cb *priv;
+	char hwname[16];
+	int ret = 0;
+
+	/* unfortunately, there's no tty_ldisc->owner field
+	 * so there is some window for SMP race with rmmod
+	 */
+	MOD_INC_USE_COUNT;
+
+	/* First make sure we're not already connected. */
+	if (tty->disc_data != NULL) {
+		priv = tty->disc_data;
+		if (priv && priv->magic == IRTTY_MAGIC) {
+			ret = -EEXIST;
+			goto out;
+		}
+		tty->disc_data = NULL;		/* ### */
+	}
+
+	/* stop the underlying  driver */
+	irtty_stop_receiver(tty, TRUE);
+	if (tty->driver.stop)
+		tty->driver.stop(tty);
+
+	if (tty->driver.flush_buffer)
+		tty->driver.flush_buffer(tty);
+	
+/* from old irtty - but what is it good for?
+ * we _are_ the ldisc and we _dont_ implement flush_buffer!
+ *
+ *	if (tty->ldisc.flush_buffer)
+ *		tty->ldisc.flush_buffer(tty);
+ */
+
+
+	/* create device name - could we use tty_name() here? */
+
+	if (strchr(tty->driver.name, '%')) {
+		sprintf(hwname, tty->driver.name,
+			minor(tty->device) - tty->driver.minor_start +
+			tty->driver.name_base);
+	}
+	else {
+		sprintf(hwname, "%s%d", tty->driver.name,
+			minor(tty->device) - tty->driver.minor_start +
+			tty->driver.name_base);
+	}
+
+	/* apply mtt override */
+	sir_tty_drv.qos_mtt_bits = qos_mtt_bits;
+
+	/* get a sir device instance for this driver */
+	dev = sirdev_get_instance(&sir_tty_drv, hwname);
+	if (!dev) {
+		ret = -ENODEV;
+		goto out;
+	}
+
+	/* allocate private device info block */
+	priv = kmalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		goto out_put;
+	memset(priv, 0, sizeof(*priv));
+
+	priv->magic = IRTTY_MAGIC;
+	priv->tty = tty;
+	priv->dev = dev;
+
+	/* serialize with start_dev - in case we were racing with ifup */
+	down(&irtty_sem);
+
+	dev->priv = priv;
+	tty->disc_data = priv;
+
+	up(&irtty_sem);
+
+	printk(KERN_INFO "%s - done\n", __FUNCTION__);
+
+	return 0;
+
+out_put:
+	sirdev_put_instance(dev);
+out:
+	MOD_DEC_USE_COUNT;
+	return ret;
+}
+
+/* 
+ *  Function irtty_close (tty)
+ *
+ *    Close down a IrDA channel. This means flushing out any pending queues,
+ *    and then restoring the TTY line discipline to what it was before it got
+ *    hooked to IrDA (which usually is TTY again).  
+ */
+static void irtty_close(struct tty_struct *tty) 
+{
+	struct sirtty_cb *priv = tty->disc_data;
+
+	if (!priv  ||  priv->magic != IRTTY_MAGIC)
+		return;
+
+	/* Hm, with a dongle attached the dongle driver wants
+	 * to close the dongle - which requires the use of
+	 * some tty write and/or termios or ioctl operations.
+	 * Are we allowed to call those when already requested
+	 * to shutdown the ldisc?
+	 * If not, we should somehow mark the dev being staled.
+	 * Question remains, how to close the dongle in this case...
+	 * For now let's assume we are granted to issue tty driver calls
+	 * until we return here from the ldisc close. I'm just wondering
+	 * how this behaves with hotpluggable serial hardware like
+	 * rs232-pcmcia card or usb-serial...
+	 *
+	 * priv->tty = NULL?;
+	 */
+
+	/* we are dead now */
+	tty->disc_data = 0;
+
+	sirdev_put_instance(priv->dev);
+
+	/* Stop tty */
+	irtty_stop_receiver(tty, TRUE);
+	tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
+	if (tty->driver.stop)
+		tty->driver.stop(tty);
+
+	kfree(priv);
+
+	MOD_DEC_USE_COUNT;
+}
+
+/* ------------------------------------------------------- */
+
+static struct tty_ldisc irda_ldisc = {
+	.magic		= TTY_LDISC_MAGIC,
+ 	.name		= "irda",
+	.flags		= 0,
+	.open		= irtty_open,
+	.close		= irtty_close,
+	.read		= NULL,
+	.write		= NULL,
+	.ioctl		= irtty_ioctl,
+ 	.poll		= NULL,
+	.receive_buf	= irtty_receive_buf,
+	.receive_room	= irtty_receive_room,
+	.write_wakeup	= irtty_write_wakeup,
+};
+
+/* ------------------------------------------------------- */
+
+static int __init irtty_sir_init(void)
+{
+	int err;
+
+	if ((err = tty_register_ldisc(N_IRDA, &irda_ldisc)) != 0)
+		ERROR("IrDA: can't register line discipline (err = %d)\n",
+			err);
+	return err;
+}
+
+static void __exit irtty_sir_cleanup(void) 
+{
+	int err;
+
+	if ((err = tty_register_ldisc(N_IRDA, NULL))) {
+		ERROR("%s(), can't unregister line discipline (err = %d)\n",
+		      __FUNCTION__, err);
+	}
+}
+
+module_init(irtty_sir_init);
+module_exit(irtty_sir_cleanup);
+
+MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>");
+MODULE_DESCRIPTION("IrDA TTY device driver");
+MODULE_LICENSE("GPL");
+
diff -u -p --new-file linux/drivers/net/irda-d6/irtty-sir.h linux/drivers/net/irda/irtty-sir.h
--- linux/drivers/net/irda-d6/irtty-sir.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/irda/irtty-sir.h	Thu Oct 31 15:53:41 2002
@@ -0,0 +1,34 @@
+/*********************************************************************
+ *
+ *	sir_tty.h:	definitions for the irtty_sir client driver (former irtty)
+ *
+ *	Copyright (c) 2002 Martin Diehl
+ *
+ *	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.
+ *
+ ********************************************************************/
+
+#ifndef IRTTYSIR_H
+#define IRTTYSIR_H
+
+#include <net/irda/irda.h>
+#include <net/irda/irda_device.h>		// chipio_t
+
+#define IRTTY_IOC_MAGIC 'e'
+#define IRTTY_IOCTDONGLE  _IO(IRTTY_IOC_MAGIC, 1)
+#define IRTTY_IOCGET     _IOR(IRTTY_IOC_MAGIC, 2, struct irtty_info)
+#define IRTTY_IOC_MAXNR   2
+
+struct sirtty_cb {
+	magic_t magic;
+
+	struct sir_dev *dev;
+	struct tty_struct  *tty;
+
+	chipio_t io;               /* IrDA controller information */
+};
+
+#endif
diff -u -p --new-file linux/drivers/net/irda-d6/sir-dev.h linux/drivers/net/irda/sir-dev.h
--- linux/drivers/net/irda-d6/sir-dev.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/irda/sir-dev.h	Thu Oct 31 15:54:20 2002
@@ -0,0 +1,203 @@
+/*********************************************************************
+ *
+ *	sir.h:	include file for irda-sir device abstraction layer
+ *
+ *	Copyright (c) 2002 Martin Diehl
+ *
+ *	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.
+ *
+ ********************************************************************/
+
+#ifndef IRDA_SIR_H
+#define IRDA_SIR_H
+
+#include <linux/netdevice.h>
+
+#include <net/irda/irda.h>
+#include <net/irda/irda_device.h>		// iobuff_t
+
+/* FIXME: unify irda_request with sir_fsm! */
+
+struct irda_request {
+	struct list_head lh_request;
+	unsigned long pending;
+	void (*func)(void *);
+	void *data;
+	struct timer_list timer;
+};
+
+struct sir_fsm {
+	struct semaphore	sem;
+	struct irda_request	rq;
+	unsigned		state, substate;
+	int			param;
+	int			result;
+};
+
+#define SIRDEV_STATE_WAIT_TX_COMPLETE	0x0100
+
+/* substates for wait_tx_complete */
+#define SIRDEV_STATE_WAIT_XMIT		0x0101
+#define SIRDEV_STATE_WAIT_UNTIL_SENT	0x0102
+#define SIRDEV_STATE_TX_DONE		0x0103
+
+#define SIRDEV_STATE_DONGLE_OPEN		0x0300
+
+/* 0x0301-0x03ff reserved for individual dongle substates */
+
+#define SIRDEV_STATE_DONGLE_CLOSE	0x0400
+
+/* 0x0401-0x04ff reserved for individual dongle substates */
+
+#define SIRDEV_STATE_SET_DTR_RTS		0x0500
+
+#define SIRDEV_STATE_SET_SPEED		0x0700
+#define SIRDEV_STATE_DONGLE_CHECK	0x0800
+#define SIRDEV_STATE_DONGLE_RESET	0x0900
+
+/* 0x0901-0x09ff reserved for individual dongle substates */
+
+#define SIRDEV_STATE_DONGLE_SPEED	0x0a00
+/* 0x0a01-0x0aff reserved for individual dongle substates */
+
+#define SIRDEV_STATE_PORT_SPEED		0x0b00
+#define SIRDEV_STATE_DONE		0x0c00
+#define SIRDEV_STATE_ERROR		0x0d00
+#define SIRDEV_STATE_COMPLETE		0x0e00
+
+#define SIRDEV_STATE_DEAD		0xffff
+
+
+struct sir_dev;
+
+struct dongle_driver {
+
+	struct module *owner;
+
+	const char *driver_name;
+
+	IRDA_DONGLE type;
+
+	int	(*open)(struct sir_dev *dev);
+	int	(*close)(struct sir_dev *dev);
+	int	(*reset)(struct sir_dev *dev);
+	int	(*set_speed)(struct sir_dev *dev, unsigned speed);
+
+	struct list_head dongle_list;
+};
+
+struct sir_driver {
+
+	struct module *owner;
+
+	const char *driver_name;
+
+	int qos_mtt_bits;
+
+	int (*chars_in_buffer)(struct sir_dev *dev);
+	void (*wait_until_sent)(struct sir_dev *dev);
+	int (*set_speed)(struct sir_dev *dev, unsigned speed);
+	int (*set_dtr_rts)(struct sir_dev *dev, int dtr, int rts);
+
+	int (*do_write)(struct sir_dev *dev, const unsigned char *ptr, size_t len);
+
+	int (*start_dev)(struct sir_dev *dev);
+	int (*stop_dev)(struct sir_dev *dev);
+};
+
+
+/* exported */
+
+extern int irda_register_dongle(struct dongle_driver *new);
+extern int irda_unregister_dongle(struct dongle_driver *drv);
+
+extern struct sir_dev * sirdev_get_instance(const struct sir_driver *drv, const char *name);
+extern int sirdev_put_instance(struct sir_dev *self);
+
+extern int sirdev_set_dongle(struct sir_dev *dev, IRDA_DONGLE type);
+extern void sirdev_write_complete(struct sir_dev *dev);
+extern int sirdev_receive(struct sir_dev *dev, const unsigned char *cp, size_t count);
+
+/* not exported */
+
+extern int sirdev_get_dongle(struct sir_dev *self, IRDA_DONGLE type);
+extern int sirdev_put_dongle(struct sir_dev *self);
+
+extern int sirdev_raw_write(struct sir_dev *dev, const char *buf, int len);
+extern int sirdev_raw_read(struct sir_dev *dev, char *buf, int len);
+extern void sirdev_enable_rx(struct sir_dev *dev);
+
+extern int sirdev_schedule_request(struct sir_dev *dev, int state, unsigned param);
+extern int __init irda_thread_create(void);
+extern void __exit irda_thread_join(void);
+
+/* inline helpers */
+
+static inline int sirdev_schedule_speed(struct sir_dev *dev, unsigned speed)
+{
+	return sirdev_schedule_request(dev, SIRDEV_STATE_SET_SPEED, speed);
+}
+
+static inline int sirdev_schedule_dongle_open(struct sir_dev *dev, int dongle_id)
+{
+	return sirdev_schedule_request(dev, SIRDEV_STATE_DONGLE_OPEN, dongle_id);
+}
+
+static inline int sirdev_schedule_dongle_close(struct sir_dev *dev)
+{
+	return sirdev_schedule_request(dev, SIRDEV_STATE_DONGLE_CLOSE, 0);
+}
+
+static inline int sirdev_schedule_dtr_rts(struct sir_dev *dev, int dtr, int rts)
+{
+	int	dtrrts;
+
+	dtrrts = ((dtr) ? 0x02 : 0x00) | ((rts) ? 0x01 : 0x00);
+	return sirdev_schedule_request(dev, SIRDEV_STATE_SET_DTR_RTS, dtrrts);
+}
+
+#if 0
+static inline int sirdev_schedule_mode(struct sir_dev *dev, int mode)
+{
+	return sirdev_schedule_request(dev, SIRDEV_STATE_SET_MODE, mode);
+}
+#endif
+
+
+struct sir_dev {
+	struct net_device *netdev;
+	struct net_device_stats stats;
+
+	struct irlap_cb    *irlap;
+
+	struct qos_info qos;
+
+	char hwname[32];
+
+	struct sir_fsm fsm;
+	atomic_t enable_rx;
+	spinlock_t tx_lock;
+
+	u32 new_speed;
+ 	u32 flags;
+
+	unsigned	speed;
+
+	iobuff_t tx_buff;          /* Transmit buffer */
+	iobuff_t rx_buff;          /* Receive buffer */
+	struct sk_buff *tx_skb;
+
+	const struct dongle_driver * dongle_drv;
+	const struct sir_driver * drv;
+	void *priv;
+
+	/* dongle callbacks to the SIR device */
+	int (*read)(struct sir_dev *, char *buf, int len);
+	int (*write)(struct sir_dev *, const char *buf, int len);
+	int (*set_dtr_rts)(struct sir_dev *, int dtr, int rts);
+};
+
+#endif	/* IRDA_SIR_H */
diff -u -p --new-file linux/drivers/net/irda-d6/sir_core.c linux/drivers/net/irda/sir_core.c
--- linux/drivers/net/irda-d6/sir_core.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/irda/sir_core.c	Thu Oct 31 16:17:07 2002
@@ -0,0 +1,52 @@
+/*********************************************************************
+ *
+ *	sir_core.c:	module core for irda-sir abstraction layer
+ *
+ *	Copyright (c) 2002 Martin Diehl
+ * 
+ *	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.
+ *
+ ********************************************************************/    
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+
+#include <net/irda/irda.h>
+
+#include "sir-dev.h"
+
+/***************************************************************************/
+
+MODULE_AUTHOR("Martin Diehl <info@mdiehl.de>");
+MODULE_DESCRIPTION("IrDA SIR core");
+MODULE_LICENSE("GPL");
+
+/***************************************************************************/
+
+EXPORT_SYMBOL(irda_register_dongle);
+EXPORT_SYMBOL(irda_unregister_dongle);
+
+EXPORT_SYMBOL(sirdev_get_instance);
+EXPORT_SYMBOL(sirdev_put_instance);
+
+EXPORT_SYMBOL(sirdev_set_dongle);
+EXPORT_SYMBOL(sirdev_write_complete);
+EXPORT_SYMBOL(sirdev_receive);
+
+static int __init sir_core_init(void)
+{
+	return irda_thread_create();
+}
+
+static void __exit sir_core_exit(void)
+{
+	irda_thread_join();
+}
+
+module_init(sir_core_init);
+module_exit(sir_core_exit);
+
diff -u -p --new-file linux/drivers/net/irda-d6/sir_dev.c linux/drivers/net/irda/sir_dev.c
--- linux/drivers/net/irda-d6/sir_dev.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/irda/sir_dev.c	Thu Oct 31 17:50:08 2002
@@ -0,0 +1,673 @@
+/*********************************************************************
+ *
+ *	sir_dev.c:	irda sir network device
+ * 
+ *	Copyright (c) 2002 Martin Diehl
+ * 
+ *	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.
+ *
+ ********************************************************************/    
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/smp_lock.h>
+
+#include <net/irda/irda.h>
+#include <net/irda/wrapper.h>
+#include <net/irda/irda_device.h>
+
+#include "sir-dev.h"
+
+/***************************************************************************/
+
+void sirdev_enable_rx(struct sir_dev *dev)
+{
+	if (unlikely(atomic_read(&dev->enable_rx)))
+		return;
+
+	/* flush rx-buffer - should also help in case of problems with echo cancelation */
+	dev->rx_buff.data = dev->rx_buff.head;
+	dev->tx_buff.len = 0;
+	atomic_set(&dev->enable_rx, 1);
+}
+
+static int sirdev_is_receiving(struct sir_dev *dev)
+{
+	if (!atomic_read(&dev->enable_rx))
+		return 0;
+
+	return (dev->rx_buff.state != OUTSIDE_FRAME);
+}
+
+int sirdev_set_dongle(struct sir_dev *dev, IRDA_DONGLE type)
+{
+	int err;
+
+	IRDA_DEBUG(3, "%s : requesting dongle %d.\n", __FUNCTION__, type);
+
+	err = sirdev_schedule_dongle_open(dev, type);
+	if (unlikely(err))
+		return err;
+	down(&dev->fsm.sem);		/* block until config change completed */
+	err = dev->fsm.result;
+	up(&dev->fsm.sem);
+	return err;
+}
+
+/* used by dongle drivers for dongle programming */
+
+int sirdev_raw_write(struct sir_dev *dev, const char *buf, int len)
+{
+	int ret;
+
+	if (unlikely(len > dev->tx_buff.truesize))
+		return -ENOSPC;
+
+	spin_lock_bh(&dev->tx_lock);		/* serialize with other tx operations */
+	while (dev->tx_buff.len > 0) {		/* wait until tx idle */
+		spin_unlock_bh(&dev->tx_lock);
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(MSECS_TO_JIFFIES(10));
+		spin_lock_bh(&dev->tx_lock);
+	}
+
+	dev->tx_buff.data = dev->tx_buff.head;
+	memcpy(dev->tx_buff.data, buf, len);	
+
+	ret = dev->drv->do_write(dev, dev->tx_buff.data, dev->tx_buff.len);
+	spin_unlock_bh(&dev->tx_lock);
+	return ret;
+}
+
+/* seems some dongle drivers may need this */
+
+int sirdev_raw_read(struct sir_dev *dev, char *buf, int len)
+{
+	int count;
+
+	if (atomic_read(&dev->enable_rx))
+		return -EIO;		/* fail if we expect irda-frames */
+
+	count = (len < dev->rx_buff.len) ? len : dev->rx_buff.len;
+
+	if (count > 0)
+		memcpy(buf, dev->rx_buff.head, count);
+
+	/* forget trailing stuff */
+	dev->rx_buff.data = dev->rx_buff.head;
+	dev->rx_buff.len = 0;
+	dev->rx_buff.state = OUTSIDE_FRAME;
+
+	return count;
+}
+
+/**********************************************************************/
+
+/* called from client driver - likely with bh-context - to indicate
+ * it made some progress with transmission. Hence we send the next
+ * chunk, if any, or complete the skb otherwise
+ */
+
+void sirdev_write_complete(struct sir_dev *dev)
+{
+	struct sk_buff *skb;
+	int actual = 0;
+	int err;
+	
+	spin_lock_bh(&dev->tx_lock);
+
+	IRDA_DEBUG(3, "%s() - dev->tx_buff.len = %d\n",
+		   __FUNCTION__, dev->tx_buff.len);
+
+	if (likely(dev->tx_buff.len > 0))  {
+		/* Write data left in transmit buffer */
+		actual = dev->drv->do_write(dev, dev->tx_buff.data, dev->tx_buff.len);
+
+		if (likely(actual>0)) {
+			dev->tx_buff.data += actual;
+			dev->tx_buff.len  -= actual;
+		}
+		else if (unlikely(actual<0)) {
+			/* could be dropped later when we have tx_timeout to recover */
+			ERROR("%s: drv->do_write failed (%d)\n", __FUNCTION__, actual);
+			if ((skb=dev->tx_skb) != NULL) {
+				dev->tx_skb = NULL;
+				dev_kfree_skb_any(skb);
+				dev->stats.tx_errors++;		      
+				dev->stats.tx_dropped++;		      
+			}
+			dev->tx_buff.len = 0;
+		}
+		if (dev->tx_buff.len > 0) {
+			spin_unlock_bh(&dev->tx_lock);
+			return;
+		}
+	}
+
+	/* we have finished now sending this skb.
+	 * update statistics and free the skb.
+	 * finally we check and trigger a pending speed change, if any.
+	 * if not we switch to rx mode and wake the queue for further
+	 * packets.
+	 * note the scheduled speed request blocks until the lower
+	 * client driver and the corresponding hardware has really
+	 * finished sending all data (xmit fifo drained f.e.)
+	 * before the speed change gets finally done and the queue
+	 * re-activated.
+	 */
+
+	IRDA_DEBUG(5, "%s(), finished with frame!\n", __FUNCTION__);
+		
+	if ((skb=dev->tx_skb) != NULL) {
+		dev->tx_skb = NULL;
+		dev->stats.tx_packets++;		      
+		dev->stats.tx_bytes += skb->len;
+		dev_kfree_skb_any(skb);
+	}
+
+	if (unlikely(dev->new_speed > 0)) {
+		IRDA_DEBUG(5, "%s(), Changing speed!\n", __FUNCTION__);
+		err = sirdev_schedule_speed(dev, dev->new_speed);
+		if (unlikely(err)) {
+			/* should never happen
+			 * forget the speed change and hope the stack recovers
+			 */
+			ERROR("%s - schedule speed change failed: %d\n", __FUNCTION__, err);
+			netif_wake_queue(dev->netdev);
+		}
+		/* else: success
+		 *	speed change in progress now
+		 *	on completion dev->new_speed gets cleared,
+		 *	rx-reenabled and the queue restarted
+		 */
+	}
+	else {
+		sirdev_enable_rx(dev);
+		netif_wake_queue(dev->netdev);
+	}
+
+	spin_unlock_bh(&dev->tx_lock);
+}
+
+/* called from client driver - likely with bh-context - to give us
+ * some more received bytes. We put them into the rx-buffer,
+ * normally unwrapping and building LAP-skb's (unless rx disabled)
+ */
+
+int sirdev_receive(struct sir_dev *dev, const unsigned char *cp, size_t count) 
+{
+	if (!dev || !dev->netdev) {
+		IRDA_DEBUG(0, "%s(), not ready yet!\n", __FUNCTION__);
+		/* Use WARNING instead of IRDA_DEBUG */
+		return -1;
+	}
+
+	if (!dev->irlap) {
+		IRDA_DEBUG(0, "%s - too early: %p / %d!\n", __FUNCTION__, cp, count);
+		/* Use WARNING instead of IRDA_DEBUG */
+		return -1;
+	}
+
+	if (cp==NULL) {
+		/* error already at lower level receive
+		 * just update stats and set media busy
+		 */
+		irda_device_set_media_busy(dev->netdev, TRUE);
+		dev->stats.rx_dropped++;
+		printk(KERN_INFO "%s; rx-drop: %d\n", __FUNCTION__, count);
+		return 0;
+	}
+
+	/* Read the characters into the buffer */
+ 	while (count--) {
+		if (likely(atomic_read(&dev->enable_rx))) {
+			/* Unwrap and destuff one byte */
+			async_unwrap_char(dev->netdev, &dev->stats, 
+				  &dev->rx_buff, *cp++);
+		}
+		else {
+			/* rx not enabled: save the raw bytes and never
+			 * trigger any netif_rx. The received bytes are flushed
+			 * later when we re-enable rx but might be read meanwhile
+			 * by the dongle driver.
+			 */
+			dev->rx_buff.data[dev->rx_buff.len++] = *cp++;
+		}
+
+		/* What should we do when the buffer is full? */
+		if (unlikely(dev->rx_buff.len == dev->rx_buff.truesize))
+			dev->rx_buff.len = 0;
+			
+	}
+
+	return 0;
+}
+
+/**********************************************************************/
+
+/* callbacks from network layer */
+
+static struct net_device_stats *sirdev_get_stats(struct net_device *ndev)
+{
+	struct sir_dev *dev = ndev->priv;
+
+	return (dev) ? &dev->stats : NULL;
+}
+
+static int sirdev_hard_xmit(struct sk_buff *skb, struct net_device *ndev)
+{
+	struct sir_dev *dev = ndev->priv;
+	int actual = 0;
+	int err;
+	s32 speed;
+
+	ASSERT(dev != NULL, return 0;);
+
+	netif_stop_queue(ndev);
+
+	IRDA_DEBUG(3, "%s(), skb->len = %d\n", __FUNCTION__, skb->len);
+
+	speed = irda_get_next_speed(skb);
+	if ((speed != dev->speed) && (speed != -1)) {
+		if (!skb->len) {
+			err = sirdev_schedule_speed(dev, speed);
+			if (unlikely(err == -EWOULDBLOCK)) {
+				/* Failed to initiate the speed change, likely the fsm
+				 * is still busy (pretty unlikely, but...)
+				 * We refuse to accept the skb and return with the queue
+				 * stopped so the network layer will retry after the
+				 * fsm completes and wakes the queue.
+				 */
+				 return 1;
+			}
+			else if (unlikely(err)) {
+				/* other fatal error - forget the speed change and
+				 * hope the stack will recover somehow
+				 */
+				 netif_start_queue(ndev);
+			}
+			/* else: success
+			 *	speed change in progress now
+			 *	on completion the queue gets restarted
+			 */
+
+			dev_kfree_skb_any(skb);
+			return 0;
+		} else
+			dev->new_speed = speed;
+	}
+
+	/* Init tx buffer*/
+	dev->tx_buff.data = dev->tx_buff.head;
+
+	/* Check problems */
+	if(spin_is_locked(&dev->tx_lock)) {
+		IRDA_DEBUG(3, "%s(), write not completed\n", __FUNCTION__);
+	}
+
+	/* serialize with write completion */
+	spin_lock_bh(&dev->tx_lock);
+
+        /* Copy skb to tx_buff while wrapping, stuffing and making CRC */
+	dev->tx_buff.len = async_wrap_skb(skb, dev->tx_buff.data, dev->tx_buff.truesize); 
+
+	/* transmission will start now - disable receive.
+	 * if we are just in the middle of an incoming frame,
+	 * treat it as collision. probably it's a good idea to
+	 * reset the rx_buf OUTSIDE_FRAME in this case too?
+	 */
+	atomic_set(&dev->enable_rx, 0);
+	if (unlikely(sirdev_is_receiving(dev)))
+		dev->stats.collisions++;
+
+	actual = dev->drv->do_write(dev, dev->tx_buff.data, dev->tx_buff.len);
+
+	if (likely(actual > 0)) {
+		dev->tx_skb = skb;
+		ndev->trans_start = jiffies;
+		dev->tx_buff.data += actual;
+		dev->tx_buff.len -= actual;
+	}
+	else if (unlikely(actual < 0)) {
+		/* could be dropped later when we have tx_timeout to recover */
+		ERROR("%s: drv->do_write failed (%d)\n", __FUNCTION__, actual);
+		dev_kfree_skb_any(skb);
+		dev->stats.tx_errors++;		      
+		dev->stats.tx_dropped++;		      
+		netif_wake_queue(ndev);
+	}
+	spin_unlock_bh(&dev->tx_lock);
+
+	return 0;
+}
+
+/* called from network layer with rtnl hold */
+
+static int sirdev_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd)
+{
+	struct if_irda_req *irq = (struct if_irda_req *) rq;
+	struct sir_dev *dev = ndev->priv;
+	int ret = 0;
+
+	ASSERT(dev != NULL, return -1;);
+
+	IRDA_DEBUG(3, "%s(), %s, (cmd=0x%X)\n", __FUNCTION__, ndev->name, cmd);
+	
+	switch (cmd) {
+	case SIOCSBANDWIDTH: /* Set bandwidth */
+		if (!capable(CAP_NET_ADMIN))
+			ret = -EPERM;
+		else
+			ret = sirdev_schedule_speed(dev, irq->ifr_baudrate);
+		/* cannot sleep here for completion
+		 * we are called from network layer with rtnl hold
+		 */
+		break;
+
+	case SIOCSDONGLE: /* Set dongle */
+		if (!capable(CAP_NET_ADMIN))
+			ret = -EPERM;
+		else
+			ret = sirdev_schedule_dongle_open(dev, irq->ifr_dongle);
+		/* cannot sleep here for completion
+		 * we are called from network layer with rtnl hold
+		 */
+		break;
+
+	case SIOCSMEDIABUSY: /* Set media busy */
+		if (!capable(CAP_NET_ADMIN))
+			ret = -EPERM;
+		else
+			irda_device_set_media_busy(dev->netdev, TRUE);
+		break;
+
+	case SIOCGRECEIVING: /* Check if we are receiving right now */
+		irq->ifr_receiving = sirdev_is_receiving(dev);
+		break;
+
+	case SIOCSDTRRTS:
+		if (!capable(CAP_NET_ADMIN))
+			ret = -EPERM;
+		else
+			ret = sirdev_schedule_dtr_rts(dev, irq->ifr_dtr, irq->ifr_rts);
+		/* cannot sleep here for completion
+		 * we are called from network layer with rtnl hold
+		 */
+		break;
+
+	case SIOCSMODE:
+#if 0
+		if (!capable(CAP_NET_ADMIN))
+			ret = -EPERM;
+		else
+			ret = sirdev_schedule_mode(dev, irq->ifr_mode);
+		/* cannot sleep here for completion
+		 * we are called from network layer with rtnl hold
+		 */
+		break;
+#endif
+	default:
+		ret = -EOPNOTSUPP;
+	}
+	
+	return ret;
+}
+
+/* ----------------------------------------------------------------------------- */
+
+#define SIRBUF_ALLOCSIZE 4269	/* worst case size of a wrapped IrLAP frame */
+
+static int sirdev_alloc_buffers(struct sir_dev *dev)
+{
+	dev->rx_buff.truesize = SIRBUF_ALLOCSIZE; 
+	dev->tx_buff.truesize = SIRBUF_ALLOCSIZE;
+
+	dev->rx_buff.head = kmalloc(dev->rx_buff.truesize, GFP_KERNEL);
+	if (dev->rx_buff.head == NULL)
+		return -ENOMEM;
+	memset(dev->rx_buff.head, 0, dev->rx_buff.truesize);
+
+	dev->tx_buff.head = kmalloc(dev->tx_buff.truesize, GFP_KERNEL);
+	if (dev->tx_buff.head == NULL) {
+		kfree(dev->rx_buff.head);
+		dev->rx_buff.head = NULL;
+		return -ENOMEM;
+		memset(dev->tx_buff.head, 0, dev->tx_buff.truesize);
+	}
+
+	dev->tx_buff.data = dev->tx_buff.head;
+	dev->rx_buff.data = dev->rx_buff.head;
+	dev->tx_buff.len = 0;
+	dev->rx_buff.len = 0;
+
+	dev->rx_buff.in_frame = FALSE;
+	dev->rx_buff.state = OUTSIDE_FRAME;
+	return 0;
+};
+
+static void sirdev_free_buffers(struct sir_dev *dev)
+{
+	if (dev->rx_buff.head)
+		kfree(dev->rx_buff.head);
+	if (dev->tx_buff.head)
+		kfree(dev->tx_buff.head);
+	dev->rx_buff.head = dev->tx_buff.head = NULL;
+}
+
+static int sirdev_open(struct net_device *ndev)
+{
+	struct sir_dev *dev = ndev->priv;
+	const struct sir_driver *drv = dev->drv;
+
+	if (!drv)
+		return -ENODEV;
+
+	lock_kernel();		/* serialize with rmmod */
+	/* increase the reference count of the driver module before doing serious stuff */
+	if (drv->owner  &&  !try_inc_mod_count(drv->owner)) {
+		unlock_kernel();
+		return -ESTALE;
+	}
+	unlock_kernel();
+
+	IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
+
+	if (sirdev_alloc_buffers(dev))
+		goto errout_dec;
+
+	if (!dev->drv->start_dev  ||  dev->drv->start_dev(dev))
+		goto errout_free;
+
+	sirdev_enable_rx(dev);
+
+	netif_start_queue(ndev);
+	dev->irlap = irlap_open(ndev, &dev->qos, dev->hwname);
+	if (!dev->irlap)
+		goto errout_stop;
+
+	netif_wake_queue(ndev);
+
+	printk(KERN_INFO "%s - done, speed = %d\n", __FUNCTION__, dev->speed);
+
+	return 0;
+
+errout_stop:
+	atomic_set(&dev->enable_rx, 0);
+	if (dev->drv->stop_dev)
+		dev->drv->stop_dev(dev);
+errout_free:
+	sirdev_free_buffers(dev);
+errout_dec:
+	if (drv->owner)
+		__MOD_DEC_USE_COUNT(drv->owner);
+	return -EAGAIN;
+}
+
+static int sirdev_close(struct net_device *ndev)
+{
+	struct sir_dev *dev = ndev->priv;
+	const struct sir_driver *drv;
+
+	printk(KERN_INFO "%s\n", __FUNCTION__);
+
+	netif_stop_queue(ndev);
+
+	down(&dev->fsm.sem);		/* block on pending config completion */
+
+	atomic_set(&dev->enable_rx, 0);
+
+	if (unlikely(!dev->irlap))
+		goto out;
+	irlap_close(dev->irlap);
+	dev->irlap = NULL;
+
+	drv = dev->drv;
+	if (unlikely(!drv  ||  !dev->priv))
+		goto out;
+
+	if (drv->stop_dev)
+		drv->stop_dev(dev);
+
+	sirdev_free_buffers(dev);
+
+	lock_kernel();
+	if (drv->owner)
+		__MOD_DEC_USE_COUNT(drv->owner);
+	unlock_kernel();
+
+out:
+	dev->speed = 0;
+	up(&dev->fsm.sem);
+	return 0;
+}
+
+/* ----------------------------------------------------------------------------- */
+
+static int sirdev_init(struct net_device *ndev)
+{
+	struct sir_dev *dev = ndev->priv;
+
+	SET_MODULE_OWNER(ndev);
+
+	/* Set up to be a normal IrDA network device driver */
+	irda_device_setup(ndev);
+
+	dev->flags = IFF_SIR | IFF_PIO;
+
+	/* Override the network functions we need to use */
+	ndev->hard_start_xmit = sirdev_hard_xmit;
+	ndev->open = sirdev_open;
+	ndev->stop = sirdev_close;
+	ndev->get_stats = sirdev_get_stats;
+	ndev->do_ioctl = sirdev_ioctl;
+
+	return 0;
+}
+
+
+struct sir_dev * sirdev_get_instance(const struct sir_driver *drv, const char *name)
+{
+	struct net_device *ndev;
+	struct sir_dev *dev;
+
+	printk(KERN_INFO "%s - %s\n", __FUNCTION__, name);
+
+	/* instead of adding tests to protect against drv->do_write==NULL
+	 * at several places we refuse to create a sir_dev instance for
+	 * drivers which dont implement do_write.
+	 */
+	if (!drv ||  !drv->do_write)
+		return NULL;
+
+	/*
+	 *  Allocate new instance of the device
+	 */
+	dev = kmalloc(sizeof(*dev), GFP_KERNEL);
+	if (dev == NULL) {
+		printk(KERN_ERR "IrDA: Can't allocate memory for "
+		       "IrDA control block!\n");
+		goto out;
+	}
+	memset(dev, 0, sizeof(*dev));
+
+	irda_init_max_qos_capabilies(&dev->qos);
+	dev->qos.baud_rate.bits = IR_9600|IR_19200|IR_38400|IR_57600|IR_115200;
+	dev->qos.min_turn_time.bits = drv->qos_mtt_bits;
+	irda_qos_bits_to_value(&dev->qos);
+
+	strncpy(dev->hwname, name, sizeof(dev->hwname)-1);
+
+	ndev = kmalloc(sizeof(*ndev), GFP_KERNEL);
+	if (ndev == NULL)
+		goto out_freedev;
+	memset(ndev, 0, sizeof(*ndev));
+
+	atomic_set(&dev->enable_rx, 0);
+	dev->tx_skb = NULL;
+
+	spin_lock_init(&dev->tx_lock);
+	init_MUTEX(&dev->fsm.sem);
+
+	INIT_LIST_HEAD(&dev->fsm.rq.lh_request);
+	dev->fsm.rq.pending = 0;
+	init_timer(&dev->fsm.rq.timer);
+
+	dev->drv = drv;
+	dev->netdev = ndev;
+
+	ndev->priv = (void *) dev;
+	ndev->init = sirdev_init;
+
+	strcpy(ndev->name, "irda%d");
+	if (register_netdev(ndev)) {
+		ERROR("%s(), register_netdev() failed!\n", __FUNCTION__);
+		goto out_freenetdev;
+	}
+
+	return dev;
+
+out_freenetdev:
+	kfree(ndev);
+out_freedev:
+	kfree(dev);
+out:
+	return NULL;
+}
+
+int sirdev_put_instance(struct sir_dev *dev)
+{
+	int err = 0;
+
+	printk(KERN_INFO "%s\n", __FUNCTION__);
+
+	atomic_set(&dev->enable_rx, 0);
+
+	netif_carrier_off(dev->netdev);
+	netif_device_detach(dev->netdev);
+
+	if (dev->dongle_drv)
+		err = sirdev_schedule_dongle_close(dev);
+	if (err)
+		ERROR("%s - error %d\n", __FUNCTION__, err);
+
+	sirdev_close(dev->netdev);
+
+	down(&dev->fsm.sem);
+	dev->fsm.state = SIRDEV_STATE_DEAD;	/* mark staled */
+	dev->dongle_drv = NULL;
+	dev->priv = NULL;
+	up(&dev->fsm.sem);
+
+	/* Remove netdevice */
+	if (dev->netdev)
+		unregister_netdev(dev->netdev);
+
+	kfree(dev);
+
+	return 0;
+}
+
diff -u -p --new-file linux/drivers/net/irda-d6/sir_dongle.c linux/drivers/net/irda/sir_dongle.c
--- linux/drivers/net/irda-d6/sir_dongle.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/irda/sir_dongle.c	Thu Oct 31 17:05:33 2002
@@ -0,0 +1,146 @@
+/*********************************************************************
+ *
+ *	sir_dongle.c:	manager for serial dongle protocol drivers
+ *
+ *	Copyright (c) 2002 Martin Diehl
+ *
+ *	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.
+ *
+ ********************************************************************/    
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/smp_lock.h>
+#include <linux/kmod.h>
+
+#include <net/irda/irda.h>
+
+#include "sir-dev.h"
+
+/**************************************************************************
+ *
+ * dongle registration and attachment
+ *
+ */
+
+static LIST_HEAD(dongle_list);			/* list of registered dongle drivers */
+static DECLARE_MUTEX(dongle_list_lock);		/* protects the list */
+
+int irda_register_dongle(struct dongle_driver *new)
+{
+	struct list_head *entry;
+	struct dongle_driver *drv;
+
+	IRDA_DEBUG(0, "%s : registering dongle \"%s\" (%d).\n",
+		   __FUNCTION__, new->driver_name, new->type);
+
+	down(&dongle_list_lock);
+	list_for_each(entry, &dongle_list) {
+		drv = list_entry(entry, struct dongle_driver, dongle_list);
+		if (new->type == drv->type) {
+			up(&dongle_list_lock);
+			return -EEXIST;
+		}
+	}
+	list_add(&new->dongle_list, &dongle_list);
+	up(&dongle_list_lock);
+	return 0;
+}
+
+int irda_unregister_dongle(struct dongle_driver *drv)
+{
+	down(&dongle_list_lock);
+	list_del(&drv->dongle_list);
+	up(&dongle_list_lock);
+	return 0;
+}
+
+int sirdev_get_dongle(struct sir_dev *dev, IRDA_DONGLE type)
+{
+	struct list_head *entry;
+	const struct dongle_driver *drv = NULL;
+	int err = -EINVAL;
+
+#ifdef CONFIG_KMOD
+	char modname[30];
+
+	sprintf(modname, "irda-dongle-%d", type);
+	request_module(modname);
+#endif
+
+	if (dev->dongle_drv != NULL)
+		return -EBUSY;
+	
+	/* serialize access to the list of registered dongles */
+	down(&dongle_list_lock);
+
+	list_for_each(entry, &dongle_list) {
+		drv = list_entry(entry, struct dongle_driver, dongle_list);
+		if (drv->type == type)
+			break;
+		else
+			drv = NULL;
+	}
+
+	if (!drv) {
+		err = -ENODEV;
+		goto out_unlock;	/* no such dongle */
+	}
+
+	/* handling of SMP races with dongle module removal - three cases:
+	 * 1) dongle driver was already unregistered - then we haven't found the
+	 *	requested dongle above and are already out here
+	 * 2) the module is already marked deleted but the driver is still
+	 *	registered - then the try_inc_mod_count() below will fail
+	 * 3) the try_inc_mod_count() below succeeds before the module is marked
+	 *	deleted - then sys_delete_module() fails and prevents the removal
+	 *	because the module is in use.
+	 */
+
+	if (drv->owner && !try_inc_mod_count(drv->owner)) {
+		err = -ESTALE;
+		goto out_unlock;	/* rmmod already pending */
+	}
+
+	/* Initialize dongle driver callbacks */
+	dev->read        = sirdev_raw_read;
+	dev->write       = sirdev_raw_write;
+	dev->set_dtr_rts = dev->drv->set_dtr_rts;
+
+	dev->dongle_drv = drv;
+
+	if (!drv->open  ||  (err=drv->open(dev))!=0)
+		goto out_reject;		/* failed to open driver */
+
+	up(&dongle_list_lock);
+	return 0;
+
+out_reject:
+	dev->dongle_drv = NULL;
+	if (drv->owner)
+		__MOD_DEC_USE_COUNT(drv->owner);
+out_unlock:
+	up(&dongle_list_lock);
+	return err;
+}
+
+int sirdev_put_dongle(struct sir_dev *dev)
+{
+	const struct dongle_driver *drv = dev->dongle_drv;
+
+	if (drv) {
+		if (drv->close)
+			drv->close(dev);		/* close this dongle instance */
+
+		dev->dongle_drv = NULL;			/* unlink the dongle driver */
+
+		if (drv->owner)
+			__MOD_DEC_USE_COUNT(drv->owner);/* decrement driver's module refcount */
+	}
+
+	return 0;
+}
diff -u -p --new-file linux/drivers/net/irda-d6/sir_kthread.c linux/drivers/net/irda/sir_kthread.c
--- linux/drivers/net/irda-d6/sir_kthread.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/irda/sir_kthread.c	Thu Oct 31 16:17:55 2002
@@ -0,0 +1,543 @@
+/*********************************************************************
+ *
+ *	sir_kthread.c:		dedicated thread to process scheduled
+ *				sir device setup requests
+ *
+ *	Copyright (c) 2002 Martin Diehl
+ *
+ *	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.
+ *
+ ********************************************************************/    
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/smp_lock.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+
+#include <net/irda/irda.h>
+
+#include "sir-dev.h"
+
+/**************************************************************************
+ *
+ * kIrDAd kernel thread and config state machine
+ *
+ */
+
+struct irda_request_queue {
+	struct list_head request_list;
+	spinlock_t lock;
+	task_t *thread;
+	struct completion exit;
+	wait_queue_head_t kick, done;
+	atomic_t num_pending;
+};
+
+static struct irda_request_queue irda_rq_queue;
+
+static int irda_queue_request(struct irda_request *rq)
+{
+	int ret = 0;
+	unsigned long flags;
+
+	if (!test_and_set_bit(0, &rq->pending)) {
+		spin_lock_irqsave(&irda_rq_queue.lock, flags);
+		list_add_tail(&rq->lh_request, &irda_rq_queue.request_list);
+		wake_up(&irda_rq_queue.kick);
+		atomic_inc(&irda_rq_queue.num_pending);
+		spin_unlock_irqrestore(&irda_rq_queue.lock, flags);
+		ret = 1;
+	}
+	return ret;
+}
+
+static void irda_request_timer(unsigned long data)
+{
+	struct irda_request *rq = (struct irda_request *)data;
+	unsigned long flags;
+	
+	spin_lock_irqsave(&irda_rq_queue.lock, flags);
+	list_add_tail(&rq->lh_request, &irda_rq_queue.request_list);
+	wake_up(&irda_rq_queue.kick);
+	spin_unlock_irqrestore(&irda_rq_queue.lock, flags);
+}
+
+static int irda_queue_delayed_request(struct irda_request *rq, unsigned long delay)
+{
+	int ret = 0;
+	struct timer_list *timer = &rq->timer;
+
+	if (!test_and_set_bit(0, &rq->pending)) {
+		timer->expires = jiffies + delay;
+		timer->function = irda_request_timer;
+		timer->data = (unsigned long)rq;
+		atomic_inc(&irda_rq_queue.num_pending);
+		add_timer(timer);
+		ret = 1;
+	}
+	return ret;
+}
+
+static void run_irda_queue(void)
+{
+	unsigned long flags;
+	struct list_head *entry, *tmp;
+	struct irda_request *rq;
+
+	spin_lock_irqsave(&irda_rq_queue.lock, flags);
+	list_for_each_safe(entry, tmp, &irda_rq_queue.request_list) {
+		rq = list_entry(entry, struct irda_request, lh_request);
+		list_del_init(entry);
+		spin_unlock_irqrestore(&irda_rq_queue.lock, flags);
+
+		clear_bit(0, &rq->pending);
+		rq->func(rq->data);
+
+		if (atomic_dec_and_test(&irda_rq_queue.num_pending))
+			wake_up(&irda_rq_queue.done);
+
+		spin_lock_irqsave(&irda_rq_queue.lock, flags);
+	}
+	spin_unlock_irqrestore(&irda_rq_queue.lock, flags);
+}		
+
+static int irda_rt_prio = 0;		/* MODULE_PARM? */
+
+static int irda_thread(void *startup)
+{
+	DECLARE_WAITQUEUE(wait, current);
+
+	daemonize();
+	strcpy(current->comm, "kIrDAd");
+
+	spin_lock_irq(&current->sig->siglock);
+	sigfillset(&current->blocked);
+	recalc_sigpending();
+	spin_unlock_irq(&current->sig->siglock);
+
+	set_fs(KERNEL_DS);
+
+	if (irda_rt_prio > 0) {
+#if 0		/* works but requires EXPORT_SYMBOL(setscheduler) */
+		struct sched_param param;
+
+		param.sched_priority = irda_rt_prio;
+		setscheduler(0, SCHED_FIFO, &param);
+#endif
+
+#if 0		/* doesn't work - has some tendency to trigger instant reboot!
+		 * looks like we would have to deactivate current on the
+		 * runqueue - which is only possible inside of kernel/sched.h
+		 */
+
+		/* runqueues are per-cpu and we are current on this cpu. Hence
+		 * The tasklist_lock with irq-off protects our runqueue too
+		 * and we don't have to lock it (which would be impossible,
+		 * because it is private in kernel/sched.c)
+		 */
+
+		read_lock_irq(&tasklist_lock);
+		current->rt_priority = (irda_rt_prio<MAX_RT_PRIO)
+					? irda_rt_prio : MAX_RT_PRIO-1;
+		current->policy = SCHED_FIFO;
+		current->prio = MAX_USER_RT_PRIO-1 - irda_rt_prio;
+		read_unlock_irq(&tasklist_lock);
+#endif
+	}
+
+	irda_rq_queue.thread = current;
+
+	complete((struct completion *)startup);
+
+	while (irda_rq_queue.thread != NULL) {
+
+		set_task_state(current, TASK_UNINTERRUPTIBLE);
+		add_wait_queue(&irda_rq_queue.kick, &wait);
+		if (list_empty(&irda_rq_queue.request_list))
+			schedule();
+		else
+			set_task_state(current, TASK_RUNNING);
+		remove_wait_queue(&irda_rq_queue.kick, &wait);
+
+		run_irda_queue();
+	}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,35)
+	reparent_to_init();
+#endif
+	complete_and_exit(&irda_rq_queue.exit, 0);
+	/* never reached */
+	return 0;
+}
+
+
+static void flush_irda_queue(void)
+{
+	if (atomic_read(&irda_rq_queue.num_pending)) {
+
+		DECLARE_WAITQUEUE(wait, current);
+
+		if (!list_empty(&irda_rq_queue.request_list))
+			run_irda_queue();
+
+		set_task_state(current, TASK_UNINTERRUPTIBLE);
+		add_wait_queue(&irda_rq_queue.done, &wait);
+		if (atomic_read(&irda_rq_queue.num_pending))
+			schedule();
+		else
+			set_task_state(current, TASK_RUNNING);
+		remove_wait_queue(&irda_rq_queue.done, &wait);
+	}
+}
+
+/* substate handler of the config-fsm to handle the cases where we want
+ * to wait for transmit completion before changing the port configuration
+ */
+
+static int irda_tx_complete_fsm(struct sir_dev *dev)
+{
+	struct sir_fsm *fsm = &dev->fsm;
+	unsigned next_state, delay;
+	unsigned bytes_left;
+
+	do {
+		next_state = fsm->substate;	/* default: stay in current substate */
+		delay = 0;
+
+		switch(fsm->substate) {
+
+		case SIRDEV_STATE_WAIT_XMIT:
+			if (dev->drv->chars_in_buffer)
+				bytes_left = dev->drv->chars_in_buffer(dev);
+			else
+				bytes_left = 0;
+			if (!bytes_left) {
+				next_state = SIRDEV_STATE_WAIT_UNTIL_SENT;
+				break;
+			}
+
+			if (dev->speed > 115200)
+				delay = (bytes_left*8*10000) / (dev->speed/100);
+			else if (dev->speed > 0)
+				delay = (bytes_left*10*10000) / (dev->speed/100);
+			else
+				delay = 0;
+			/* expected delay (usec) until remaining bytes are sent */
+			if (delay < 100) {
+				udelay(delay);
+				delay = 0;
+				break;
+			}
+			/* sleep some longer delay (msec) */
+			delay = (delay+999) / 1000;
+			break;
+
+		case SIRDEV_STATE_WAIT_UNTIL_SENT:
+			/* block until underlaying hardware buffer are empty */
+			if (dev->drv->wait_until_sent)
+				dev->drv->wait_until_sent(dev);
+			next_state = SIRDEV_STATE_TX_DONE;
+			break;
+
+		case SIRDEV_STATE_TX_DONE:
+			return 0;
+
+		default:
+			ERROR("%s - undefined state\n", __FUNCTION__);
+			return -EINVAL;
+		}
+		fsm->substate = next_state;
+	} while (delay == 0);
+	return delay;
+}
+
+/*
+ * Function irda_config_fsm
+ *
+ * State machine to handle the configuration of the device (and attached dongle, if any).
+ * This handler is scheduled for execution in kIrDAd context, so we can sleep.
+ * however, kIrDAd is shared by all sir_dev devices so we better don't sleep there too
+ * long. Instead, for longer delays we start a timer to reschedule us later.
+ * On entry, fsm->sem is always locked and the netdev xmit queue stopped.
+ * Both must be unlocked/restarted on completion - but only on final exit.
+ */
+
+static void irda_config_fsm(void *data)
+{
+	struct sir_dev *dev = data;
+	struct sir_fsm *fsm = &dev->fsm;
+	int next_state;
+	int ret = -1;
+	unsigned delay;
+
+	IRDA_DEBUG(2, "%s(), <%ld>\n", __FUNCTION__, jiffies); 
+
+	do {
+		IRDA_DEBUG(3, "%s - state=0x%04x / substate=0x%04x\n",
+			__FUNCTION__, fsm->state, fsm->substate);
+
+		next_state = fsm->state;
+		delay = 0;
+
+		switch(fsm->state) {
+
+		case SIRDEV_STATE_DONGLE_OPEN:
+			if (dev->dongle_drv != NULL) {
+				ret = sirdev_put_dongle(dev);
+				if (ret) {
+					fsm->result = -EINVAL;
+					next_state = SIRDEV_STATE_ERROR;
+					break;
+				}
+			}
+
+			/* Initialize dongle */
+			ret = sirdev_get_dongle(dev, fsm->param);
+			if (ret) {
+				fsm->result = ret;
+				next_state = SIRDEV_STATE_ERROR;
+				break;
+			}
+
+			/* Dongles are powered through the modem control lines which
+			 * were just set during open. Before resetting, let's wait for
+			 * the power to stabilize. This is what some dongle drivers did
+			 * in open before, while others didn't - should be safe anyway.
+			 */
+
+			delay = 50;
+			fsm->substate = SIRDEV_STATE_DONGLE_RESET;
+			next_state = SIRDEV_STATE_DONGLE_RESET;
+
+			fsm->param = 9600;
+
+			break;
+
+		case SIRDEV_STATE_DONGLE_CLOSE:
+			/* shouldn't we just treat this as success=? */
+			if (dev->dongle_drv == NULL) {
+				fsm->result = -EINVAL;
+				next_state = SIRDEV_STATE_ERROR;
+				break;
+			}
+
+			ret = sirdev_put_dongle(dev);
+			if (ret) {
+				fsm->result = ret;
+				next_state = SIRDEV_STATE_ERROR;
+				break;
+			}
+			next_state = SIRDEV_STATE_DONE;
+			break;
+
+		case SIRDEV_STATE_SET_DTR_RTS:
+			if (dev->drv->set_dtr_rts) {
+				int	dtr, rts;
+
+				dtr = (fsm->param&0x02) ? TRUE : FALSE;
+				rts = (fsm->param&0x01) ? TRUE : FALSE;
+				ret = dev->drv->set_dtr_rts(dev,dtr,rts);
+			}
+			else
+				ret = -EINVAL;
+			next_state = SIRDEV_STATE_DONE;
+			break;
+
+		case SIRDEV_STATE_SET_SPEED:
+			fsm->substate = SIRDEV_STATE_WAIT_XMIT;
+			next_state = SIRDEV_STATE_DONGLE_CHECK;
+			break;
+
+		case SIRDEV_STATE_DONGLE_CHECK:
+			ret = irda_tx_complete_fsm(dev);
+			if (ret < 0) {
+				fsm->result = ret;
+				next_state = SIRDEV_STATE_ERROR;
+				break;
+			}
+			if ((delay=ret) != 0)
+				break;
+
+			if (dev->dongle_drv) {
+				fsm->substate = SIRDEV_STATE_DONGLE_RESET;
+				next_state = SIRDEV_STATE_DONGLE_RESET;
+			}
+			else {
+				dev->speed = fsm->param;
+				next_state = SIRDEV_STATE_PORT_SPEED;
+			}
+			break;
+
+		case SIRDEV_STATE_DONGLE_RESET:
+			if (dev->dongle_drv->reset) {
+				ret = dev->dongle_drv->reset(dev);	
+				if (ret < 0) {
+					fsm->result = ret;
+					next_state = SIRDEV_STATE_ERROR;
+					break;
+				}
+			}
+			else
+				ret = 0;
+			if ((delay=ret) == 0) {
+				/* set serial port according to dongle default speed */
+				if (dev->drv->set_speed)
+					dev->drv->set_speed(dev, dev->speed);
+				fsm->substate = SIRDEV_STATE_DONGLE_SPEED;
+				next_state = SIRDEV_STATE_DONGLE_SPEED;
+			}
+			break;
+
+		case SIRDEV_STATE_DONGLE_SPEED:				
+			if (dev->dongle_drv->reset) {
+				ret = dev->dongle_drv->set_speed(dev, fsm->param);
+				if (ret < 0) {
+					fsm->result = ret;
+					next_state = SIRDEV_STATE_ERROR;
+					break;
+				}
+			}
+			else
+				ret = 0;
+			if ((delay=ret) == 0)
+				next_state = SIRDEV_STATE_PORT_SPEED;
+			break;
+
+		case SIRDEV_STATE_PORT_SPEED:
+			/* Finally we are ready to change the serial port speed */
+			if (dev->drv->set_speed)
+				dev->drv->set_speed(dev, dev->speed);
+			dev->new_speed = 0;
+			next_state = SIRDEV_STATE_DONE;
+			break;
+
+		case SIRDEV_STATE_DONE:
+			/* Signal network layer so it can send more frames */
+			netif_wake_queue(dev->netdev);
+			next_state = SIRDEV_STATE_COMPLETE;
+			break;
+
+		default:
+			ERROR("%s - undefined state\n", __FUNCTION__);
+			fsm->result = -EINVAL;
+			/* fall thru */
+
+		case SIRDEV_STATE_ERROR:
+			ERROR("%s - error: %d\n", __FUNCTION__, fsm->result);
+
+#if 0	/* don't enable this before we have netdev->tx_timeout to recover */
+			netif_stop_queue(dev->netdev);
+#else
+			netif_wake_queue(dev->netdev);
+#endif
+			/* fall thru */
+
+		case SIRDEV_STATE_COMPLETE:
+			/* config change finished, so we are not busy any longer */
+			sirdev_enable_rx(dev);
+			printk(KERN_INFO "%s - up\n", __FUNCTION__);
+			up(&fsm->sem);
+			return;
+		}
+		fsm->state = next_state;
+	} while(!delay);
+
+	irda_queue_delayed_request(&fsm->rq, MSECS_TO_JIFFIES(delay));
+}
+
+/* schedule some device configuration task for execution by kIrDAd
+ * on behalf of the above state machine.
+ * can be called from process or interrupt/tasklet context.
+ */
+
+int sirdev_schedule_request(struct sir_dev *dev, int initial_state, unsigned param)
+{
+	struct sir_fsm *fsm = &dev->fsm;
+	int xmit_was_down;
+
+//	IRDA_DEBUG(2, "%s - state=0x%04x / param=%u\n", __FUNCTION__, initial_state, param);
+
+	printk(KERN_INFO "%s - state=0x%04x / param=%u\n", __FUNCTION__, initial_state, param);
+
+	if (in_interrupt()) {
+		if (down_trylock(&fsm->sem)) {
+			IRDA_DEBUG(1, "%s(), state machine busy!\n", __FUNCTION__);
+			return -EWOULDBLOCK;
+		}
+	}
+	else
+		down(&fsm->sem);
+	printk(KERN_INFO "%s - down\n", __FUNCTION__);
+
+	if (fsm->state == SIRDEV_STATE_DEAD) {
+		/* race with sirdev_close should never happen */
+		ERROR("%s(), instance staled!\n", __FUNCTION__);
+		printk(KERN_INFO "%s - up\n", __FUNCTION__);
+		up(&fsm->sem);
+		return -ESTALE;		/* or better EPIPE? */
+	}
+
+	xmit_was_down = netif_queue_stopped(dev->netdev);
+	netif_stop_queue(dev->netdev);
+	atomic_set(&dev->enable_rx, 0);
+
+	fsm->state = initial_state;
+	fsm->param = param;
+	fsm->result = 0;
+
+	INIT_LIST_HEAD(&fsm->rq.lh_request);
+	fsm->rq.pending = 0;
+	fsm->rq.func = irda_config_fsm;
+	fsm->rq.data = dev;
+
+	if (!irda_queue_request(&fsm->rq)) {	/* returns 0 on error! */
+		atomic_set(&dev->enable_rx, 1);
+		if (!xmit_was_down)
+			netif_wake_queue(dev->netdev);		
+		printk(KERN_INFO "%s - up\n", __FUNCTION__);
+		up(&fsm->sem);
+		return -EAGAIN;
+	}
+	return 0;
+}
+
+int __init irda_thread_create(void)
+{
+	struct completion startup;
+	int pid;
+
+	spin_lock_init(&irda_rq_queue.lock);
+	irda_rq_queue.thread = NULL;
+	INIT_LIST_HEAD(&irda_rq_queue.request_list);
+	init_waitqueue_head(&irda_rq_queue.kick);
+	init_waitqueue_head(&irda_rq_queue.done);
+	atomic_set(&irda_rq_queue.num_pending, 0);
+
+	init_completion(&startup);
+	pid = kernel_thread(irda_thread, &startup, CLONE_FS|CLONE_FILES);
+	if (pid <= 0)
+		return -EAGAIN;
+	else
+		wait_for_completion(&startup);
+
+	return 0;
+}
+
+void __exit irda_thread_join(void) 
+{
+	if (irda_rq_queue.thread) {
+		flush_irda_queue();
+		init_completion(&irda_rq_queue.exit);
+		irda_rq_queue.thread = NULL;
+		wake_up(&irda_rq_queue.kick);		
+		wait_for_completion(&irda_rq_queue.exit);
+	}
+}
+
diff -u -p --new-file linux/drivers/net/irda-d6/tekram-sir.c linux/drivers/net/irda/tekram-sir.c
--- linux/drivers/net/irda-d6/tekram-sir.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/irda/tekram-sir.c	Thu Oct 31 16:31:09 2002
@@ -0,0 +1,265 @@
+/*********************************************************************
+ *                
+ * Filename:      tekram.c
+ * Version:       1.3
+ * Description:   Implementation of the Tekram IrMate IR-210B dongle
+ * Status:        Experimental.
+ * Author:        Dag Brattli <dagb@cs.uit.no>
+ * Created at:    Wed Oct 21 20:02:35 1998
+ * Modified at:   Sun Oct 27 22:02:38 2002
+ * Modified by:   Martin Diehl <mad@mdiehl.de>
+ * 
+ *     Copyright (c) 1998-1999 Dag Brattli,
+ *     Copyright (c) 2002 Martin Diehl,
+ *     All Rights Reserved.
+ *      
+ *     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.
+ *  
+ *     Neither Dag Brattli nor University of Tromsø admit liability nor
+ *     provide warranty for any of this software. This material is 
+ *     provided "AS-IS" and at no charge.
+ *     
+ ********************************************************************/
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+
+#include <net/irda/irda.h>
+
+#include "sir-dev.h"
+
+MODULE_PARM(tekram_delay, "i");
+MODULE_PARM_DESC(tekram_delay, "tekram dongle write complete delay");
+static int tekram_delay = 50;		/* default is 50 ms */
+
+static int tekram_open(struct sir_dev *);
+static int tekram_close(struct sir_dev *);
+static int tekram_change_speed(struct sir_dev *, unsigned);
+static int tekram_reset(struct sir_dev *);
+
+#define TEKRAM_115200 0x00
+#define TEKRAM_57600  0x01
+#define TEKRAM_38400  0x02
+#define TEKRAM_19200  0x03
+#define TEKRAM_9600   0x04
+
+#define TEKRAM_PW     0x10 /* Pulse select bit */
+
+static struct dongle_driver tekram = {
+	.owner		= THIS_MODULE,
+	.driver_name	= "Tekram IR-210B",
+	.type		= IRDA_TEKRAM_DONGLE,
+	.open		= tekram_open,
+	.close		= tekram_close,
+	.reset		= tekram_reset,
+	.set_speed	= tekram_change_speed,
+};
+
+int __init tekram_sir_init(void)
+{
+	if (tekram_delay < 1  ||  tekram_delay>500)
+		tekram_delay = 200;
+	return irda_register_dongle(&tekram);
+}
+
+void __exit tekram_sir_cleanup(void)
+{
+	irda_unregister_dongle(&tekram);
+}
+
+#define TEKRAM_STATE_POWERED	(SIRDEV_STATE_DONGLE_OPEN + 1)
+
+static int tekram_open(struct sir_dev *dev)
+{
+	unsigned delay = 0;
+	unsigned next_state = dev->fsm.substate;
+	struct qos_info *qos = &dev->qos;
+
+	IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
+
+	switch(dev->fsm.substate) {
+
+	case SIRDEV_STATE_DONGLE_OPEN:
+		dev->set_dtr_rts(dev, TRUE, TRUE);
+		next_state = TEKRAM_STATE_POWERED;
+		delay = 50;
+		break;
+
+	case TEKRAM_STATE_POWERED:
+		qos->baud_rate.bits &= IR_9600|IR_19200|IR_38400|IR_57600|IR_115200;
+		qos->min_turn_time.bits = 0x01; /* Needs at least 10 ms */	
+		irda_qos_bits_to_value(qos);
+		return 0;
+
+	default:
+		ERROR("%s - undefined state\n", __FUNCTION__);
+		return -EINVAL;
+	}
+
+	dev->fsm.substate = next_state;
+
+	return delay;
+}
+
+static int tekram_close(struct sir_dev *dev)
+{
+	IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
+
+	/* Power off dongle */
+	dev->set_dtr_rts(dev, FALSE, FALSE);
+
+	return 0;
+}
+
+/*
+ * Function tekram_change_speed (dev, state, speed)
+ *
+ *    Set the speed for the Tekram IRMate 210 type dongle. Warning, this 
+ *    function must be called with a process context!
+ *
+ *    Algorithm
+ *    1. clear DTR 
+ *    2. set RTS, and wait at least 7 us
+ *    3. send Control Byte to the IR-210 through TXD to set new baud rate
+ *       wait until the stop bit of Control Byte is sent (for 9600 baud rate, 
+ *       it takes about 100 msec)
+ *
+ *	[oops, why 100 msec? sending 1 byte (10 bits) takes 1.05 msec
+ *	 - is this probably to compensate for delays in tty layer?]
+ *
+ *    5. clear RTS (return to NORMAL Operation)
+ *    6. wait at least 50 us, new setting (baud rate, etc) takes effect here 
+ *       after
+ */
+
+#define TEKRAM_STATE_WAIT_SPEED	(SIRDEV_STATE_DONGLE_SPEED + 1)
+
+static int tekram_change_speed(struct sir_dev *dev, unsigned speed)
+{
+	unsigned delay = 0;
+	unsigned next_state = dev->fsm.substate;
+	u8 byte;
+	
+	IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
+
+	switch(dev->fsm.substate) {
+
+	case SIRDEV_STATE_DONGLE_SPEED:
+
+		switch (speed) {
+		default:
+			speed = 9600;
+			/* fall thru */
+		case 9600:
+			byte = TEKRAM_PW|TEKRAM_9600;
+			break;
+		case 19200:
+			byte = TEKRAM_PW|TEKRAM_19200;
+			break;
+		case 38400:
+			byte = TEKRAM_PW|TEKRAM_38400;
+			break;
+		case 57600:
+			byte = TEKRAM_PW|TEKRAM_57600;
+			break;
+		case 115200:
+			byte = TEKRAM_115200;
+			break;
+		}
+
+		/* Set DTR, Clear RTS */
+		dev->set_dtr_rts(dev, TRUE, FALSE);
+	
+		/* Wait at least 7us */
+		udelay(14);
+
+		/* Write control byte */
+		dev->write(dev, &byte, 1);
+		
+		dev->speed = speed;
+
+		next_state = TEKRAM_STATE_WAIT_SPEED;
+		delay = tekram_delay;		/* default: 50 ms */
+		break;
+
+	case TEKRAM_STATE_WAIT_SPEED:
+		/* Set DTR, Set RTS */
+		dev->set_dtr_rts(dev, TRUE, TRUE);
+
+		udelay(50);
+
+		return 0;
+
+	default:
+		ERROR("%s - undefined state\n", __FUNCTION__);
+		return -EINVAL;
+	}
+
+	dev->fsm.substate = next_state;
+
+	return delay;
+}
+
+/*
+ * Function tekram_reset (driver)
+ *
+ *      This function resets the tekram dongle. Warning, this function 
+ *      must be called with a process context!! 
+ *
+ *      Algorithm:
+ *    	  0. Clear RTS and DTR, and wait 50 ms (power off the IR-210 )
+ *        1. clear RTS 
+ *        2. set DTR, and wait at least 1 ms 
+ *        3. clear DTR to SPACE state, wait at least 50 us for further 
+ *         operation
+ */
+
+
+#define TEKRAM_STATE_WAIT_RESET	(SIRDEV_STATE_DONGLE_RESET + 1)
+
+static int tekram_reset(struct sir_dev *dev)
+{
+	unsigned delay = 0;
+	unsigned next_state = dev->fsm.substate;
+
+	IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
+
+	switch(dev->fsm.substate) {
+
+	case SIRDEV_STATE_DONGLE_RESET:
+		/* Clear DTR, Set RTS */
+		dev->set_dtr_rts(dev, FALSE, TRUE); 
+
+		next_state = TEKRAM_STATE_WAIT_RESET;
+		delay = 1;		/* Should sleep 1 ms */
+		break;
+
+	case TEKRAM_STATE_WAIT_RESET:
+		/* Set DTR, Set RTS */
+		dev->set_dtr_rts(dev, TRUE, TRUE);
+	
+		/* Wait at least 50 us */
+		udelay(75);
+
+		return 0;
+
+	default:
+		ERROR("%s - undefined state\n", __FUNCTION__);
+		return -EINVAL;
+	}
+
+	dev->fsm.substate = next_state;
+
+	return delay;
+}
+
+MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>");
+MODULE_DESCRIPTION("Tekram IrMate IR-210B dongle driver");
+MODULE_LICENSE("GPL");
+		
+module_init(tekram_sir_init);
+module_exit(tekram_sir_cleanup);
