Linux-2.6.12-rc2

Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.

Let it rip!
diff --git a/drivers/net/irda/Kconfig b/drivers/net/irda/Kconfig
new file mode 100644
index 0000000..a464841
--- /dev/null
+++ b/drivers/net/irda/Kconfig
@@ -0,0 +1,404 @@
+
+menu "Infrared-port device drivers"
+	depends on IRDA!=n
+
+comment "SIR device drivers"
+
+config IRTTY_SIR
+	tristate "IrTTY (uses Linux serial driver)"
+	depends on IRDA
+	help
+	  Say Y here if you want to build support for the IrTTY line
+	  discipline.  To compile it as a module, choose M here: the module
+	  will be called irtty-sir. IrTTY makes it possible to use Linux's
+	  own serial driver for all IrDA ports that are 16550 compatible.
+	  Most IrDA chips are 16550 compatible so you should probably say Y
+	  to this option.  Using IrTTY will however limit the speed of the
+	  connection to 115200 bps (IrDA SIR mode).
+
+	  If unsure, say Y.
+
+comment "Dongle support"
+
+config DONGLE
+	bool "Serial dongle support"
+	depends on IRTTY_SIR
+	help
+	  Say Y here if you have an infrared device that connects to your
+	  computer's serial port. These devices are called dongles. Then say Y
+	  or M to the driver for your particular dongle below.
+
+	  Note that the answer to this question won't directly affect the
+	  kernel: saying N will just cause the configurator to skip all
+	  the questions about serial dongles.
+
+config ESI_DONGLE
+	tristate "ESI JetEye PC dongle"
+	depends on DONGLE && IRDA
+	help
+	  Say Y here if you want to build support for the Extended Systems
+	  JetEye PC dongle.  To compile it as a module, choose M here. The ESI
+	  dongle attaches to the normal 9-pin serial port connector, and can
+	  currently only be used by IrTTY.  To activate support for ESI
+	  dongles you will have to start irattach like this:
+	  "irattach -d esi".
+
+config ACTISYS_DONGLE
+	tristate "ACTiSYS IR-220L and IR220L+ dongle"
+	depends on DONGLE && IRDA
+	help
+	  Say Y here if you want to build support for the ACTiSYS IR-220L and
+	  IR220L+ dongles.  To compile it as a module, choose M here. The
+	  ACTiSYS dongles attaches to the normal 9-pin serial port connector,
+	  and can currently only be used by IrTTY.  To activate support for
+	  ACTiSYS dongles you will have to start irattach like this:
+	  "irattach -d actisys" or "irattach -d actisys+".
+
+config TEKRAM_DONGLE
+	tristate "Tekram IrMate 210B dongle"
+	depends on DONGLE && IRDA
+	help
+	  Say Y here if you want to build support for the Tekram IrMate 210B
+	  dongle.  To compile it as a module, choose M here. The Tekram dongle
+	  attaches to the normal 9-pin serial port connector, and can
+	  currently only be used by IrTTY.  To activate support for Tekram
+	  dongles you will have to start irattach like this:
+	  "irattach -d tekram".
+
+config LITELINK_DONGLE
+	tristate "Parallax LiteLink dongle"
+	depends on DONGLE && IRDA
+	help
+	  Say Y here if you want to build support for the Parallax Litelink
+	  dongle.  To compile it as a module, choose M here.  The Parallax
+	  dongle attaches to the normal 9-pin serial port connector, and can
+	  currently only be used by IrTTY.  To activate support for Parallax
+	  dongles you will have to start irattach like this:
+	  "irattach -d litelink".
+
+config MA600_DONGLE
+	tristate "Mobile Action MA600 dongle"
+	depends on DONGLE && IRDA && EXPERIMENTAL
+	help
+	  Say Y here if you want to build support for the Mobile Action MA600
+	  dongle.  To compile it as a module, choose M here. The MA600 dongle
+	  attaches to the normal 9-pin serial port connector, and can
+	  currently only be used by IrTTY.  The driver should also support
+	  the MA620 USB version of the dongle, if the integrated USB-to-RS232
+	  converter is supported by usbserial. To activate support for
+	  MA600 dongle you will have to start irattach like this:
+	  "irattach -d ma600".
+
+config GIRBIL_DONGLE
+	tristate "Greenwich GIrBIL dongle"
+	depends on DONGLE && IRDA && EXPERIMENTAL
+	help
+	  Say Y here if you want to build support for the Greenwich GIrBIL
+	  dongle.  If you want to compile it as a module, choose M here.
+	  The Greenwich dongle attaches to the normal 9-pin serial port
+	  connector, and can currently only be used by IrTTY.  To activate
+	  support for Greenwich dongles you will have to start irattach
+	  like this: "irattach -d girbil".
+
+config MCP2120_DONGLE
+	tristate "Microchip MCP2120"
+	depends on DONGLE && IRDA && EXPERIMENTAL
+	help
+	  Say Y here if you want to build support for the Microchip MCP2120
+	  dongle.  If you want to compile it as a module, choose M here.
+	  The MCP2120 dongle attaches to the normal 9-pin serial port
+	  connector, and can currently only be used by IrTTY.  To activate
+	  support for MCP2120 dongles you will have to start irattach
+	  like this: "irattach -d mcp2120".
+
+	  You must build this dongle yourself.  For more information see:
+	  <http://www.eyetap.org/~tangf/irda_sir_linux.html>
+
+config OLD_BELKIN_DONGLE
+	tristate "Old Belkin dongle"
+	depends on DONGLE && IRDA && EXPERIMENTAL
+	help
+	  Say Y here if you want to build support for the Adaptec Airport 1000
+	  and 2000 dongles.  If you want to compile it as a module, choose
+	  M here. Some information is contained in the comments
+	  at the top of <file:drivers/net/irda/old_belkin.c>.
+
+config ACT200L_DONGLE
+	tristate "ACTiSYS IR-200L dongle"
+	depends on DONGLE && IRDA && EXPERIMENTAL
+	help
+	  Say Y here if you want to build support for the ACTiSYS IR-200L
+	  dongle. If you want to compile it as a module, choose M here.
+	  The ACTiSYS IR-200L dongle attaches to the normal 9-pin serial
+	  port connector, and can currently only be used by IrTTY.
+	  To activate support for ACTiSYS IR-200L dongle you will have to
+	  start irattach like this: "irattach -d act200l".
+
+comment "Old SIR device drivers"
+
+config IRPORT_SIR
+	tristate "IrPORT (IrDA serial driver)"
+	depends on IRDA && BROKEN_ON_SMP
+	---help---
+	  Say Y here if you want to build support for the IrPORT IrDA device
+	  driver.  To compile it as a module, choose M here: the module will be
+	  called irport. IrPORT can be used instead of IrTTY and sometimes
+	  this can be better.  One example is if your IrDA port does not
+	  have echo-canceling, which will work OK with IrPORT since this
+	  driver is working in half-duplex mode only.  You don't need to use
+	  irattach with IrPORT, but you just insert it the same way as FIR
+	  drivers (insmod irport io=0x3e8 irq=11).  Notice that IrPORT is a
+	  SIR device driver which means that speed is limited to 115200 bps.
+
+	  If unsure, say Y.
+
+comment "Old Serial dongle support"
+
+config DONGLE_OLD
+	bool "Old Serial dongle support"
+	depends on (IRTTY_OLD || IRPORT_SIR) && BROKEN_ON_SMP
+	help
+	  Say Y here if you have an infrared device that connects to your
+	  computer's serial port. These devices are called dongles. Then say Y
+	  or M to the driver for your particular dongle below.
+
+	  Note that the answer to this question won't directly affect the
+	  kernel: saying N will just cause the configurator to skip all
+	  the questions about serial dongles.
+
+config ESI_DONGLE_OLD
+	tristate "ESI JetEye PC dongle"
+	depends on DONGLE_OLD && IRDA
+	help
+	  Say Y here if you want to build support for the Extended Systems
+	  JetEye PC dongle.  To compile it as a module, choose M here.  The ESI
+	  dongle attaches to the normal 9-pin serial port connector, and can
+	  currently only be used by IrTTY.  To activate support for ESI
+	  dongles you will have to start irattach like this:
+	  "irattach -d esi".
+
+config ACTISYS_DONGLE_OLD
+	tristate "ACTiSYS IR-220L and IR220L+ dongle"
+	depends on DONGLE_OLD && IRDA
+	help
+	  Say Y here if you want to build support for the ACTiSYS IR-220L and
+	  IR220L+ dongles.  To compile it as a module, choose M here.  The
+	  ACTiSYS dongles attaches to the normal 9-pin serial port connector,
+	  and can currently only be used by IrTTY.  To activate support for
+	  ACTiSYS dongles you will have to start irattach like this:
+	  "irattach -d actisys" or "irattach -d actisys+".
+
+config TEKRAM_DONGLE_OLD
+	tristate "Tekram IrMate 210B dongle"
+	depends on DONGLE_OLD && IRDA
+	help
+	  Say Y here if you want to build support for the Tekram IrMate 210B
+	  dongle.  To compile it as a module, choose M here.  The Tekram dongle
+	  attaches to the normal 9-pin serial port connector, and can
+	  currently only be used by IrTTY.  To activate support for Tekram
+	  dongles you will have to start irattach like this:
+	  "irattach -d tekram".
+
+config GIRBIL_DONGLE_OLD
+	tristate "Greenwich GIrBIL dongle"
+	depends on DONGLE_OLD && IRDA
+	help
+	  Say Y here if you want to build support for the Greenwich GIrBIL
+	  dongle.  To compile it as a module, choose M here.  The Greenwich
+	  dongle attaches to the normal 9-pin serial port connector, and can
+	  currently only be used by IrTTY.  To activate support for Greenwich
+	  dongles you will have to insert "irattach -d girbil" in the
+	  /etc/irda/drivers script.
+
+config LITELINK_DONGLE_OLD
+	tristate "Parallax LiteLink dongle"
+	depends on DONGLE_OLD && IRDA
+	help
+	  Say Y here if you want to build support for the Parallax Litelink
+	  dongle.  To compile it as a module, choose M here.  The Parallax
+	  dongle attaches to the normal 9-pin serial port connector, and can
+	  currently only be used by IrTTY.  To activate support for Parallax
+	  dongles you will have to start irattach like this:
+	  "irattach -d litelink".
+
+config MCP2120_DONGLE_OLD
+	tristate "Microchip MCP2120"
+	depends on DONGLE_OLD && IRDA
+	help
+	  Say Y here if you want to build support for the Microchip MCP2120
+	  dongle.  To compile it as a module, choose M here.  The MCP2120 dongle
+	  attaches to the normal 9-pin serial port connector, and can
+	  currently only be used by IrTTY.  To activate support for MCP2120
+	  dongles you will have to insert "irattach -d mcp2120" in the
+	  /etc/irda/drivers script.
+
+	  You must build this dongle yourself.  For more information see:
+	  <http://www.eyetap.org/~tangf/irda_sir_linux.html>
+
+config OLD_BELKIN_DONGLE_OLD
+	tristate "Old Belkin dongle"
+	depends on DONGLE_OLD && IRDA
+	help
+	  Say Y here if you want to build support for the Adaptec Airport 1000
+	  and 2000 dongles.  To compile it as a module, choose M here: the module
+	  will be called old_belkin.  Some information is contained in the
+	  comments at the top of <file:drivers/net/irda/old_belkin.c>.
+
+config ACT200L_DONGLE_OLD
+	tristate "ACTiSYS IR-200L dongle (EXPERIMENTAL)"
+	depends on DONGLE_OLD && EXPERIMENTAL && IRDA
+	help
+	  Say Y here if you want to build support for the ACTiSYS IR-200L
+	  dongle.  To compile it as a module, choose M here.  The ACTiSYS
+	  IR-200L dongle attaches to the normal 9-pin serial port connector,
+	  and can currently only be used by IrTTY. To activate support for
+	  ACTiSYS IR-200L dongles you will have to start irattach like this:
+	  "irattach -d act200l".
+
+config MA600_DONGLE_OLD
+	tristate "Mobile Action MA600 dongle (EXPERIMENTAL)"
+	depends on DONGLE_OLD && EXPERIMENTAL && IRDA
+	---help---
+	  Say Y here if you want to build support for the Mobile Action MA600
+	  dongle.  To compile it as a module, choose M here. The MA600 dongle
+	  attaches to the normal 9-pin serial port connector, and can
+	  currently only be tested on IrCOMM.  To activate support for MA600
+	  dongles you will have to insert "irattach -d ma600" in the
+	  /etc/irda/drivers script.  Note: irutils 0.9.15 requires no
+	  modification. irutils 0.9.9 needs modification. For more
+	  information, download the following tar gzip file.
+
+	  There is a pre-compiled module on
+	  <http://engsvr.ust.hk/~eetwl95/ma600.html>
+
+config EP7211_IR
+	tristate "EP7211 I/R support"
+	depends on DONGLE_OLD && ARCH_EP7211 && IRDA
+
+comment "FIR device drivers"
+
+config USB_IRDA
+	tristate "IrDA USB dongles"
+	depends on IRDA && USB
+	---help---
+	  Say Y here if you want to build support for the USB IrDA FIR Dongle
+	  device driver.  To compile it as a module, choose M here: the module
+	  will be called irda-usb.  IrDA-USB support the various IrDA USB
+	  dongles available and most of their pecularities.  Those dongles
+	  plug in the USB port of your computer, are plug and play, and
+	  support SIR and FIR (4Mbps) speeds.  On the other hand, those
+	  dongles tend to be less efficient than a FIR chipset.
+
+	  Please note that the driver is still experimental.  And of course,
+	  you will need both USB and IrDA support in your kernel...
+
+config SIGMATEL_FIR
+	tristate "SigmaTel STIr4200 bridge (EXPERIMENTAL)"
+	depends on IRDA && USB && EXPERIMENTAL
+	select CRC32
+	---help---
+	  Say Y here if you want to build support for the SigmaTel STIr4200
+	  USB IrDA FIR bridge device driver.  
+
+	  USB bridge based on the SigmaTel STIr4200 don't conform to the
+	  IrDA-USB device class specification, and therefore need their
+	  own specific driver. Those dongles support SIR and FIR (4Mbps)
+	  speeds. 
+
+	  To compile it as a module, choose M here: the module will be called
+	  stir4200.
+
+config NSC_FIR
+	tristate "NSC PC87108/PC87338"
+	depends on IRDA && ISA
+	help
+	  Say Y here if you want to build support for the NSC PC87108 and
+	  PC87338 IrDA chipsets.  This driver supports SIR,
+	  MIR and FIR (4Mbps) speeds.
+
+	  To compile it as a module, choose M here: the module will be called
+	  nsc-ircc.
+
+config WINBOND_FIR
+	tristate "Winbond W83977AF (IR)"
+	depends on IRDA && ISA
+	help
+	  Say Y here if you want to build IrDA support for the Winbond
+	  W83977AF super-io chipset.  This driver should be used for the IrDA
+	  chipset in the Corel NetWinder.  The driver supports SIR, MIR and
+	  FIR (4Mbps) speeds.
+
+	  To compile it as a module, choose M here: the module will be called
+	  w83977af_ir.
+
+config TOSHIBA_FIR
+	tristate "Toshiba Type-O IR Port"
+	depends on IRDA && PCI && !64BIT
+	help
+	  Say Y here if you want to build support for the Toshiba Type-O IR
+	  and Donau oboe chipsets. These chipsets are used by the Toshiba
+	  Libretto 100/110CT, Tecra 8100, Portege 7020 and many more laptops.
+	  To compile it as a module, choose M here: the module will be called
+	  donauboe.
+
+config AU1000_FIR
+	tristate "Alchemy Au1000 SIR/FIR"
+	depends on MIPS_AU1000 && IRDA
+
+config SMC_IRCC_FIR
+	tristate "SMSC IrCC (EXPERIMENTAL)"
+	depends on EXPERIMENTAL && IRDA && ISA
+	help
+	  Say Y here if you want to build support for the SMC Infrared
+	  Communications Controller.  It is used in a wide variety of
+	  laptops (Fujitsu, Sony, Compaq and some Toshiba).
+	  To compile it as a module, choose M here: the module will be called
+	  smsc-ircc2.o.
+
+config ALI_FIR
+	tristate "ALi M5123 FIR (EXPERIMENTAL)"
+	depends on EXPERIMENTAL && IRDA && ISA
+	help
+	  Say Y here if you want to build support for the ALi M5123 FIR
+	  Controller.  The ALi M5123 FIR Controller is embedded in ALi M1543C,
+	  M1535, M1535D, M1535+, M1535D Sourth Bridge.  This driver supports
+	  SIR, MIR and FIR (4Mbps) speeds.
+
+	  To compile it as a module, choose M here: the module will be called
+	  ali-ircc.
+
+config VLSI_FIR
+	tristate "VLSI 82C147 SIR/MIR/FIR (EXPERIMENTAL)"
+	depends on EXPERIMENTAL && IRDA && PCI
+	help
+	  Say Y here if you want to build support for the VLSI 82C147
+	  PCI-IrDA Controller. This controller is used by the HP OmniBook 800
+	  and 5500 notebooks. The driver provides support for SIR, MIR and
+	  FIR (4Mbps) speeds.
+
+	  To compile it as a module, choose M here: the module will be called
+	  vlsi_ir.
+
+config SA1100_FIR
+	tristate "SA1100 Internal IR"
+	depends on ARCH_SA1100 && IRDA
+
+config VIA_FIR
+	tristate "VIA VT8231/VT1211 SIR/MIR/FIR"
+	depends on IRDA && ISA && PCI
+	help
+	  Say Y here if you want to build support for the VIA VT8231
+	  and VIA VT1211 IrDA controllers, found on the motherboards using
+	  those those VIA chipsets. To use this controller, you will need
+	  to plug a specific 5 pins FIR IrDA dongle in the specific
+	  motherboard connector. The driver provides support for SIR, MIR
+	  and FIR (4Mbps) speeds.
+
+	  You will need to specify the 'dongle_id' module parameter to
+	  indicate the FIR dongle attached to the controller.
+
+	  To compile it as a module, choose M here: the module will be called
+	  via-ircc.
+
+endmenu
+
diff --git a/drivers/net/irda/Makefile b/drivers/net/irda/Makefile
new file mode 100644
index 0000000..29a8bd8
--- /dev/null
+++ b/drivers/net/irda/Makefile
@@ -0,0 +1,47 @@
+#
+# Makefile for the Linux IrDA infrared port device drivers.
+#
+# 9 Aug 2000, Christoph Hellwig <hch@infradead.org>
+# Rewritten to use lists instead of if-statements.
+#
+
+# Old SIR drivers 
+obj-$(CONFIG_IRPORT_SIR)	+= 		irport.o
+# FIR drivers
+obj-$(CONFIG_USB_IRDA)		+= irda-usb.o
+obj-$(CONFIG_SIGMATEL_FIR)	+= stir4200.o
+obj-$(CONFIG_NSC_FIR)		+= nsc-ircc.o
+obj-$(CONFIG_WINBOND_FIR)	+= w83977af_ir.o
+obj-$(CONFIG_SA1100_FIR)	+= sa1100_ir.o
+obj-$(CONFIG_TOSHIBA_FIR)	+= donauboe.o
+obj-$(CONFIG_SMC_IRCC_FIR)	+= smsc-ircc2.o
+obj-$(CONFIG_ALI_FIR)		+= ali-ircc.o
+obj-$(CONFIG_VLSI_FIR)		+= vlsi_ir.o
+obj-$(CONFIG_VIA_FIR)		+= via-ircc.o
+# Old dongle drivers for old SIR drivers
+obj-$(CONFIG_ESI_DONGLE_OLD)		+= esi.o
+obj-$(CONFIG_TEKRAM_DONGLE_OLD)	+= tekram.o
+obj-$(CONFIG_ACTISYS_DONGLE_OLD)	+= actisys.o
+obj-$(CONFIG_GIRBIL_DONGLE_OLD)	+= girbil.o
+obj-$(CONFIG_LITELINK_DONGLE_OLD)	+= litelink.o
+obj-$(CONFIG_OLD_BELKIN_DONGLE_OLD)	+= old_belkin.o
+obj-$(CONFIG_MCP2120_DONGLE_OLD)	+= mcp2120.o
+obj-$(CONFIG_ACT200L_DONGLE_OLD)	+= act200l.o
+obj-$(CONFIG_MA600_DONGLE_OLD)	+= ma600.o
+obj-$(CONFIG_EP7211_IR)		+= ep7211_ir.o
+obj-$(CONFIG_AU1000_FIR)	+= au1k_ir.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
+obj-$(CONFIG_LITELINK_DONGLE)	+= litelink-sir.o
+obj-$(CONFIG_GIRBIL_DONGLE)	+= girbil-sir.o
+obj-$(CONFIG_OLD_BELKIN_DONGLE)	+= old_belkin-sir.o
+obj-$(CONFIG_MCP2120_DONGLE)	+= mcp2120-sir.o
+obj-$(CONFIG_ACT200L_DONGLE)	+= act200l-sir.o
+obj-$(CONFIG_MA600_DONGLE)	+= ma600-sir.o
+
+# The SIR helper module
+sir-dev-objs := sir_core.o sir_dev.o sir_dongle.o sir_kthread.o
diff --git a/drivers/net/irda/act200l-sir.c b/drivers/net/irda/act200l-sir.c
new file mode 100644
index 0000000..d8b89c7
--- /dev/null
+++ b/drivers/net/irda/act200l-sir.c
@@ -0,0 +1,257 @@
+/*********************************************************************
+ *
+ * Filename:      act200l.c
+ * Version:       0.8
+ * Description:   Implementation for the ACTiSYS ACT-IR200L dongle
+ * Status:        Experimental.
+ * Author:        SHIMIZU Takuya <tshimizu@ga2.so-net.ne.jp>
+ * Created at:    Fri Aug  3 17:35:42 2001
+ * Modified at:   Fri Aug 17 10:22:40 2001
+ * Modified by:   SHIMIZU Takuya <tshimizu@ga2.so-net.ne.jp>
+ *
+ *     Copyright (c) 2001 SHIMIZU Takuya, 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.
+ *
+ ********************************************************************/
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+
+#include <net/irda/irda.h>
+
+#include "sir-dev.h"
+
+static int act200l_reset(struct sir_dev *dev);
+static int act200l_open(struct sir_dev *dev);
+static int act200l_close(struct sir_dev *dev);
+static int act200l_change_speed(struct sir_dev *dev, unsigned speed);
+
+/* Regsiter 0: Control register #1 */
+#define ACT200L_REG0    0x00
+#define ACT200L_TXEN    0x01 /* Enable transmitter */
+#define ACT200L_RXEN    0x02 /* Enable receiver */
+
+/* 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 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 */
+
+/* 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 struct dongle_driver act200l = {
+	.owner		= THIS_MODULE,
+	.driver_name	= "ACTiSYS ACT-IR200L",
+	.type		= IRDA_ACT200L_DONGLE,
+	.open		= act200l_open,
+	.close		= act200l_close,
+	.reset		= act200l_reset,
+	.set_speed	= act200l_change_speed,
+};
+
+static int __init act200l_sir_init(void)
+{
+	return irda_register_dongle(&act200l);
+}
+
+static void __exit act200l_sir_cleanup(void)
+{
+	irda_unregister_dongle(&act200l);
+}
+
+static int act200l_open(struct sir_dev *dev)
+{
+	struct qos_info *qos = &dev->qos;
+
+	IRDA_DEBUG(2, "%s()\n", __FUNCTION__ );
+
+	/* Power on the dongle */
+	sirdev_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;
+	qos->min_turn_time.bits = 0x03;
+	irda_qos_bits_to_value(qos);
+
+	/* irda thread waits 50 msec for power settling */
+
+	return 0;
+}
+
+static int act200l_close(struct sir_dev *dev)
+{
+	IRDA_DEBUG(2, "%s()\n", __FUNCTION__ );
+
+	/* Power off the dongle */
+	sirdev_set_dtr_rts(dev, FALSE, FALSE);
+
+	return 0;
+}
+
+/*
+ * Function act200l_change_speed (dev, speed)
+ *
+ *    Set the speed for the ACTiSYS ACT-IR200L type dongle.
+ *
+ */
+static int act200l_change_speed(struct sir_dev *dev, unsigned speed)
+{
+	u8 control[3];
+	int ret = 0;
+
+	IRDA_DEBUG(2, "%s()\n", __FUNCTION__ );
+
+	/* Clear DTR and set RTS to enter command mode */
+	sirdev_set_dtr_rts(dev, FALSE, TRUE);
+
+	switch (speed) {
+	default:
+		ret = -EINVAL;
+		/* fall through */
+	case 9600:
+		control[0] = ACT200L_REG8 |  (ACT200L_9600       & 0x0f);
+		control[1] = ACT200L_REG9 | ((ACT200L_9600 >> 4) & 0x0f);
+		break;
+	case 19200:
+		control[0] = ACT200L_REG8 |  (ACT200L_19200       & 0x0f);
+		control[1] = ACT200L_REG9 | ((ACT200L_19200 >> 4) & 0x0f);
+		break;
+	case 38400:
+		control[0] = ACT200L_REG8 |  (ACT200L_38400       & 0x0f);
+		control[1] = ACT200L_REG9 | ((ACT200L_38400 >> 4) & 0x0f);
+		break;
+	case 57600:
+		control[0] = ACT200L_REG8 |  (ACT200L_57600       & 0x0f);
+		control[1] = ACT200L_REG9 | ((ACT200L_57600 >> 4) & 0x0f);
+		break;
+	case 115200:
+		control[0] = ACT200L_REG8 |  (ACT200L_115200       & 0x0f);
+		control[1] = ACT200L_REG9 | ((ACT200L_115200 >> 4) & 0x0f);
+		break;
+	}
+	control[2] = ACT200L_REG1 | ACT200L_LODB | ACT200L_WIDE;
+
+	/* Write control bytes */
+	sirdev_raw_write(dev, control, 3);
+	msleep(5);
+
+	/* Go back to normal mode */
+	sirdev_set_dtr_rts(dev, TRUE, TRUE);
+
+	dev->speed = speed;
+	return ret;
+}
+
+/*
+ * Function act200l_reset (driver)
+ *
+ *    Reset the ACTiSYS ACT-IR200L type dongle.
+ */
+
+#define ACT200L_STATE_WAIT1_RESET	(SIRDEV_STATE_DONGLE_RESET+1)
+#define ACT200L_STATE_WAIT2_RESET	(SIRDEV_STATE_DONGLE_RESET+2)
+
+static int act200l_reset(struct sir_dev *dev)
+{
+	unsigned state = dev->fsm.substate;
+	unsigned delay = 0;
+	u8 control[9] = {
+		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_REG0  | ACT200L_TXEN | ACT200L_RXEN
+	};
+	int ret = 0;
+
+	IRDA_DEBUG(2, "%s()\n", __FUNCTION__ );
+
+	switch (state) {
+	case SIRDEV_STATE_DONGLE_RESET:
+		/* Reset the dongle : set RTS low for 25 ms */
+		sirdev_set_dtr_rts(dev, TRUE, FALSE);
+		state = ACT200L_STATE_WAIT1_RESET;
+		delay = 50;
+		break;
+
+	case ACT200L_STATE_WAIT1_RESET:
+		/* Clear DTR and set RTS to enter command mode */
+		sirdev_set_dtr_rts(dev, FALSE, TRUE);
+
+		udelay(25);			/* better wait for some short while */
+
+		/* Write control bytes */
+		sirdev_raw_write(dev, control, sizeof(control));
+		state = ACT200L_STATE_WAIT2_RESET;
+		delay = 15;
+		break;
+
+	case ACT200L_STATE_WAIT2_RESET:
+		/* Go back to normal mode */
+		sirdev_set_dtr_rts(dev, TRUE, TRUE);
+		dev->speed = 9600;
+		break;
+	default:
+		IRDA_ERROR("%s(), unknown state %d\n", __FUNCTION__, state);
+		ret = -1;
+		break;
+	}
+	dev->fsm.substate = state;
+	return (delay > 0) ? delay : ret;
+}
+
+MODULE_AUTHOR("SHIMIZU Takuya <tshimizu@ga2.so-net.ne.jp>");
+MODULE_DESCRIPTION("ACTiSYS ACT-IR200L dongle driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("irda-dongle-10"); /* IRDA_ACT200L_DONGLE */
+
+module_init(act200l_sir_init);
+module_exit(act200l_sir_cleanup);
diff --git a/drivers/net/irda/act200l.c b/drivers/net/irda/act200l.c
new file mode 100644
index 0000000..756cd44
--- /dev/null
+++ b/drivers/net/irda/act200l.c
@@ -0,0 +1,297 @@
+/*********************************************************************
+ *
+ * Filename:      act200l.c
+ * Version:       0.8
+ * Description:   Implementation for the ACTiSYS ACT-IR200L dongle
+ * Status:        Experimental.
+ * Author:        SHIMIZU Takuya <tshimizu@ga2.so-net.ne.jp>
+ * Created at:    Fri Aug  3 17:35:42 2001
+ * Modified at:   Fri Aug 17 10:22:40 2001
+ * Modified by:   SHIMIZU Takuya <tshimizu@ga2.so-net.ne.jp>
+ *
+ *     Copyright (c) 2001 SHIMIZU Takuya, 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.
+ *
+ ********************************************************************/
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/tty.h>
+#include <linux/init.h>
+
+#include <net/irda/irda.h>
+#include <net/irda/irda_device.h>
+
+static int  act200l_reset(struct irda_task *task);
+static void act200l_open(dongle_t *self, struct qos_info *qos);
+static void act200l_close(dongle_t *self);
+static int  act200l_change_speed(struct irda_task *task);
+
+/* Regsiter 0: Control register #1 */
+#define ACT200L_REG0    0x00
+#define ACT200L_TXEN    0x01 /* Enable transmitter */
+#define ACT200L_RXEN    0x02 /* Enable receiver */
+
+/* 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 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 */
+
+/* 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 struct dongle_reg dongle = {
+	.type = IRDA_ACT200L_DONGLE,
+	.open = act200l_open,
+	.close = act200l_close,
+	.reset = act200l_reset,
+	.change_speed = act200l_change_speed,
+	.owner = THIS_MODULE,
+};
+
+static int __init act200l_init(void)
+{
+	return irda_device_register_dongle(&dongle);
+}
+
+static void __exit act200l_cleanup(void)
+{
+	irda_device_unregister_dongle(&dongle);
+}
+
+static void act200l_open(dongle_t *self, struct qos_info *qos)
+{
+	IRDA_DEBUG(2, "%s()\n", __FUNCTION__ );
+
+	/* Power on the dongle */
+	self->set_dtr_rts(self->dev, TRUE, TRUE);
+
+	/* Set the speeds we can accept */
+	qos->baud_rate.bits &= IR_9600|IR_19200|IR_38400|IR_57600|IR_115200;
+	qos->min_turn_time.bits = 0x03;
+}
+
+static void act200l_close(dongle_t *self)
+{
+	IRDA_DEBUG(2, "%s()\n", __FUNCTION__ );
+
+	/* Power off the dongle */
+	self->set_dtr_rts(self->dev, FALSE, FALSE);
+}
+
+/*
+ * Function act200l_change_speed (dev, speed)
+ *
+ *    Set the speed for the ACTiSYS ACT-IR200L type dongle.
+ *
+ */
+static int act200l_change_speed(struct irda_task *task)
+{
+	dongle_t *self = (dongle_t *) task->instance;
+	__u32 speed = (__u32) task->param;
+	__u8 control[3];
+	int ret = 0;
+
+	IRDA_DEBUG(2, "%s()\n", __FUNCTION__ );
+
+	self->speed_task = task;
+
+	switch (task->state) {
+	case IRDA_TASK_INIT:
+		if (irda_task_execute(self, act200l_reset, NULL, task,
+				(void *) speed))
+		{
+			/* Dongle need more time to reset */
+			irda_task_next_state(task, IRDA_TASK_CHILD_WAIT);
+
+			/* Give reset 1 sec to finish */
+			ret = msecs_to_jiffies(1000);
+		}
+		break;
+	case IRDA_TASK_CHILD_WAIT:
+		IRDA_WARNING("%s(), resetting dongle timed out!\n",
+			     __FUNCTION__);
+		ret = -1;
+		break;
+	case IRDA_TASK_CHILD_DONE:
+		/* Clear DTR and set RTS to enter command mode */
+		self->set_dtr_rts(self->dev, FALSE, TRUE);
+
+		switch (speed) {
+		case 9600:
+		default:
+			control[0] = ACT200L_REG8 |  (ACT200L_9600       & 0x0f);
+			control[1] = ACT200L_REG9 | ((ACT200L_9600 >> 4) & 0x0f);
+			break;
+		case 19200:
+			control[0] = ACT200L_REG8 |  (ACT200L_19200       & 0x0f);
+			control[1] = ACT200L_REG9 | ((ACT200L_19200 >> 4) & 0x0f);
+			break;
+		case 38400:
+			control[0] = ACT200L_REG8 |  (ACT200L_38400       & 0x0f);
+			control[1] = ACT200L_REG9 | ((ACT200L_38400 >> 4) & 0x0f);
+			break;
+		case 57600:
+			control[0] = ACT200L_REG8 |  (ACT200L_57600       & 0x0f);
+			control[1] = ACT200L_REG9 | ((ACT200L_57600 >> 4) & 0x0f);
+			break;
+		case 115200:
+			control[0] = ACT200L_REG8 |  (ACT200L_115200       & 0x0f);
+			control[1] = ACT200L_REG9 | ((ACT200L_115200 >> 4) & 0x0f);
+			break;
+		}
+		control[2] = ACT200L_REG1 | ACT200L_LODB | ACT200L_WIDE;
+
+		/* Write control bytes */
+		self->write(self->dev, control, 3);
+		irda_task_next_state(task, IRDA_TASK_WAIT);
+		ret = msecs_to_jiffies(5);
+		break;
+	case IRDA_TASK_WAIT:
+		/* Go back to normal mode */
+		self->set_dtr_rts(self->dev, TRUE, TRUE);
+
+		irda_task_next_state(task, IRDA_TASK_DONE);
+		self->speed_task = NULL;
+		break;
+	default:
+		IRDA_ERROR("%s(), unknown state %d\n",
+			   __FUNCTION__, task->state);
+		irda_task_next_state(task, IRDA_TASK_DONE);
+		self->speed_task = NULL;
+		ret = -1;
+		break;
+	}
+	return ret;
+}
+
+/*
+ * Function act200l_reset (driver)
+ *
+ *    Reset the ACTiSYS ACT-IR200L type dongle.
+ */
+static int act200l_reset(struct irda_task *task)
+{
+	dongle_t *self = (dongle_t *) task->instance;
+	__u8 control[9] = {
+		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_REG0  | ACT200L_TXEN | ACT200L_RXEN
+	};
+	int ret = 0;
+
+	IRDA_DEBUG(2, "%s()\n", __FUNCTION__ );
+
+	self->reset_task = task;
+
+	switch (task->state) {
+	case IRDA_TASK_INIT:
+		/* Power on the dongle */
+		self->set_dtr_rts(self->dev, TRUE, TRUE);
+
+		irda_task_next_state(task, IRDA_TASK_WAIT1);
+		ret = msecs_to_jiffies(50);
+		break;
+	case IRDA_TASK_WAIT1:
+		/* Reset the dongle : set RTS low for 25 ms */
+		self->set_dtr_rts(self->dev, TRUE, FALSE);
+
+		irda_task_next_state(task, IRDA_TASK_WAIT2);
+		ret = msecs_to_jiffies(50);
+		break;
+	case IRDA_TASK_WAIT2:
+		/* Clear DTR and set RTS to enter command mode */
+		self->set_dtr_rts(self->dev, FALSE, TRUE);
+
+		/* Write control bytes */
+		self->write(self->dev, control, 9);
+		irda_task_next_state(task, IRDA_TASK_WAIT3);
+		ret = msecs_to_jiffies(15);
+		break;
+	case IRDA_TASK_WAIT3:
+		/* Go back to normal mode */
+		self->set_dtr_rts(self->dev, TRUE, TRUE);
+
+		irda_task_next_state(task, IRDA_TASK_DONE);
+		self->reset_task = NULL;
+		break;
+	default:
+		IRDA_ERROR("%s(), unknown state %d\n",
+			   __FUNCTION__, task->state);
+		irda_task_next_state(task, IRDA_TASK_DONE);
+		self->reset_task = NULL;
+		ret = -1;
+		break;
+	}
+	return ret;
+}
+
+MODULE_AUTHOR("SHIMIZU Takuya <tshimizu@ga2.so-net.ne.jp>");
+MODULE_DESCRIPTION("ACTiSYS ACT-IR200L dongle driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("irda-dongle-10"); /* IRDA_ACT200L_DONGLE */
+
+/*
+ * Function init_module (void)
+ *
+ *    Initialize ACTiSYS ACT-IR200L module
+ *
+ */
+module_init(act200l_init);
+
+/*
+ * Function cleanup_module (void)
+ *
+ *    Cleanup ACTiSYS ACT-IR200L module
+ *
+ */
+module_exit(act200l_cleanup);
diff --git a/drivers/net/irda/actisys-sir.c b/drivers/net/irda/actisys-sir.c
new file mode 100644
index 0000000..9715ab5
--- /dev/null
+++ b/drivers/net/irda/actisys-sir.c
@@ -0,0 +1,246 @@
+/*********************************************************************
+ *                
+ * 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 unsigned 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,
+};
+
+static 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;
+}
+
+static 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;
+
+	sirdev_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);
+
+	/* irda thread waits 50 msec for power settling */
+
+	return 0;
+}
+
+static int actisys_close(struct sir_dev *dev)
+{
+	/* Power off the dongle */
+	sirdev_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 = speed;
+			break;
+		}
+		/* Set RTS low for 10 us */
+		sirdev_set_dtr_rts(dev, TRUE, FALSE);
+		udelay(MIN_DELAY);
+
+		/* Set RTS high for 10 us */
+		sirdev_set_dtr_rts(dev, TRUE, TRUE);
+		udelay(MIN_DELAY);
+	}
+
+	/* Check if life is sweet... */
+	if (i >= MAX_SPEEDS) {
+		actisys_reset(dev);
+		ret = -EINVAL;  /* 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 accommodate 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 */
+	sirdev_set_dtr_rts(dev, FALSE, TRUE);
+	udelay(MIN_DELAY);
+
+	/* Go back to normal mode */
+	sirdev_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_ALIAS("irda-dongle-2"); /* IRDA_ACTISYS_DONGLE */
+MODULE_ALIAS("irda-dongle-3"); /* IRDA_ACTISYS_PLUS_DONGLE */
+
+module_init(actisys_sir_init);
+module_exit(actisys_sir_cleanup);
diff --git a/drivers/net/irda/actisys.c b/drivers/net/irda/actisys.c
new file mode 100644
index 0000000..b2e31f4
--- /dev/null
+++ b/drivers/net/irda/actisys.c
@@ -0,0 +1,288 @@
+/*********************************************************************
+ *                
+ * Filename:      actisys.c
+ * Version:       1.0
+ * 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)
+ * Created at:    Wed Oct 21 20:02:35 1998
+ * Modified at:   Fri Dec 17 09:10:43 1999
+ * Modified by:   Dag Brattli <dagb@cs.uit.no>
+ * 
+ *     Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved.
+ *     Copyright (c) 1999 Jean Tourrilhes
+ *      
+ *     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...
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/tty.h>
+#include <linux/init.h>
+
+#include <net/irda/irda.h>
+#include <net/irda/irda_device.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_change_speed(struct irda_task *task);
+static int  actisys_reset(struct irda_task *task);
+static void actisys_open(dongle_t *self, struct qos_info *qos);
+static void actisys_close(dongle_t *self);
+
+/* 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 5
+
+static struct dongle_reg dongle = {
+	.type = IRDA_ACTISYS_DONGLE,
+	.open = actisys_open,
+	.close = actisys_close,
+	.reset = actisys_reset,
+	.change_speed = actisys_change_speed,
+	.owner = THIS_MODULE,
+};
+
+static struct dongle_reg dongle_plus = {
+	.type = IRDA_ACTISYS_PLUS_DONGLE,
+	.open = actisys_open,
+	.close = actisys_close,
+	.reset = actisys_reset,
+	.change_speed = actisys_change_speed,
+	.owner = THIS_MODULE,
+};
+
+/*
+ * Function actisys_change_speed (task)
+ *
+ *	There is two model of Actisys dongle we are dealing with,
+ * the 220L and 220L+. At this point, only irattach knows with
+ * kind the user has requested (it was an argument on irattach
+ * command line).
+ *	So, we register a dongle of each sort and let irattach
+ * pick the right one...
+ */
+static int __init actisys_init(void)
+{
+	int ret;
+
+	/* First, register an Actisys 220L dongle */
+	ret = irda_device_register_dongle(&dongle);
+	if (ret < 0)
+		return ret;
+	/* Now, register an Actisys 220L+ dongle */
+	ret = irda_device_register_dongle(&dongle_plus);
+	if (ret < 0) {
+		irda_device_unregister_dongle(&dongle);
+		return ret;
+	}	
+	return 0;
+}
+
+static void __exit actisys_cleanup(void)
+{
+	/* We have to remove both dongles */
+	irda_device_unregister_dongle(&dongle);
+	irda_device_unregister_dongle(&dongle_plus);
+}
+
+static void actisys_open(dongle_t *self, struct qos_info *qos)
+{
+	/* Power on the dongle */
+	self->set_dtr_rts(self->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 (self->issue->type == IRDA_ACTISYS_DONGLE)
+		qos->baud_rate.bits &= ~IR_38400;
+	
+	qos->min_turn_time.bits = 0x7f; /* Needs 0.01 ms */
+}
+
+static void actisys_close(dongle_t *self)
+{
+	/* Power off the dongle */
+	self->set_dtr_rts(self->dev, FALSE, FALSE);
+}
+
+/*
+ * 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 irda_task *task)
+{
+	dongle_t *self = (dongle_t *) task->instance;
+	__u32 speed = (__u32) task->param;	/* Target speed */
+	int ret = 0;
+	int i = 0;
+
+        IRDA_DEBUG(4, "%s(), speed=%d (was %d)\n", __FUNCTION__, speed, 
+		   self->speed);
+
+	/* Go to a known state by reseting the dongle */
+
+	/* Reset the dongle : set DTR low for 10 us */
+	self->set_dtr_rts(self->dev, FALSE, TRUE);
+	udelay(MIN_DELAY);
+
+	/* Go back to normal mode (we are now at 9600 b/s) */
+	self->set_dtr_rts(self->dev, TRUE, TRUE);
+ 
+	/* 
+	 * 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]) {
+			self->speed = baud_rates[i];
+			break;
+		}
+		/* Make sure previous pulse is finished */
+		udelay(MIN_DELAY);
+
+		/* Set RTS low for 10 us */
+		self->set_dtr_rts(self->dev, TRUE, FALSE);
+		udelay(MIN_DELAY);
+
+		/* Set RTS high for 10 us */
+		self->set_dtr_rts(self->dev, TRUE, TRUE);
+	}
+
+	/* Check if life is sweet... */
+	if (i >= MAX_SPEEDS)
+		ret = -1;  /* This should not happen */
+
+	/* Basta lavoro, on se casse d'ici... */
+	irda_task_next_state(task, IRDA_TASK_DONE);
+
+	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 accommodate 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...
+ *	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 irda_task *task)
+{
+	dongle_t *self = (dongle_t *) task->instance;
+	int ret = 0;
+
+	IRDA_ASSERT(task != NULL, return -1;);
+
+	self->reset_task = task;
+
+	switch (task->state) {
+	case IRDA_TASK_INIT:
+		/* Set both DTR & RTS to power up the dongle */
+		/* In theory redundant with power up in actisys_open() */
+		self->set_dtr_rts(self->dev, TRUE, TRUE);
+		
+		/* Sleep 50 ms to make sure capacitor is charged */
+		ret = msecs_to_jiffies(50);
+		irda_task_next_state(task, IRDA_TASK_WAIT);
+		break;
+	case IRDA_TASK_WAIT:			
+		/* Reset the dongle : set DTR low for 10 us */
+		self->set_dtr_rts(self->dev, FALSE, TRUE);
+		udelay(MIN_DELAY);
+
+		/* Go back to normal mode */
+		self->set_dtr_rts(self->dev, TRUE, TRUE);
+	
+		irda_task_next_state(task, IRDA_TASK_DONE);
+		self->reset_task = NULL;
+		self->speed = 9600;	/* That's the default */
+		break;
+	default:
+		IRDA_ERROR("%s(), unknown state %d\n",
+			   __FUNCTION__, task->state);
+		irda_task_next_state(task, IRDA_TASK_DONE);
+		self->reset_task = NULL;
+		ret = -1;
+		break;
+	}
+	return ret;
+}
+
+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_ALIAS("irda-dongle-2"); /* IRDA_ACTISYS_DONGLE */
+MODULE_ALIAS("irda-dongle-3"); /* IRDA_ACTISYS_PLUS_DONGLE */
+
+		
+/*
+ * Function init_module (void)
+ *
+ *    Initialize Actisys module
+ *
+ */
+module_init(actisys_init);
+
+/*
+ * Function cleanup_module (void)
+ *
+ *    Cleanup Actisys module
+ *
+ */
+module_exit(actisys_cleanup);
diff --git a/drivers/net/irda/ali-ircc.c b/drivers/net/irda/ali-ircc.c
new file mode 100644
index 0000000..9bf3468
--- /dev/null
+++ b/drivers/net/irda/ali-ircc.c
@@ -0,0 +1,2277 @@
+/*********************************************************************
+ *                
+ * Filename:      ali-ircc.h
+ * Version:       0.5
+ * Description:   Driver for the ALI M1535D and M1543C FIR Controller
+ * Status:        Experimental.
+ * Author:        Benjamin Kong <benjamin_kong@ali.com.tw>
+ * Created at:    2000/10/16 03:46PM
+ * Modified at:   2001/1/3 02:55PM
+ * Modified by:   Benjamin Kong <benjamin_kong@ali.com.tw>
+ * Modified at:   2003/11/6 and support for ALi south-bridge chipsets M1563
+ * Modified by:   Clear Zhang <clear_zhang@ali.com.tw>
+ * 
+ *     Copyright (c) 2000 Benjamin Kong <benjamin_kong@ali.com.tw>
+ *     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.
+ *  
+ ********************************************************************/
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/rtnetlink.h>
+#include <linux/serial_reg.h>
+#include <linux/dma-mapping.h>
+
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/byteorder.h>
+
+#include <linux/pm.h>
+
+#include <net/irda/wrapper.h>
+#include <net/irda/irda.h>
+#include <net/irda/irda_device.h>
+
+#include "ali-ircc.h"
+
+#define CHIP_IO_EXTENT 8
+#define BROKEN_DONGLE_ID
+
+static char *driver_name = "ali-ircc";
+
+/* Module parameters */
+static int qos_mtt_bits = 0x07;  /* 1 ms or more */
+
+/* Use BIOS settions by default, but user may supply module parameters */
+static unsigned int io[]  = { ~0, ~0, ~0, ~0 };
+static unsigned int irq[] = { 0, 0, 0, 0 };
+static unsigned int dma[] = { 0, 0, 0, 0 };
+
+static int  ali_ircc_probe_53(ali_chip_t *chip, chipio_t *info);
+static int  ali_ircc_init_43(ali_chip_t *chip, chipio_t *info);
+static int  ali_ircc_init_53(ali_chip_t *chip, chipio_t *info);
+
+/* These are the currently known ALi sourth-bridge chipsets, the only one difference
+ * is that M1543C doesn't support HP HDSL-3600
+ */
+static ali_chip_t chips[] =
+{
+	{ "M1543", { 0x3f0, 0x370 }, 0x51, 0x23, 0x20, 0x43, ali_ircc_probe_53, ali_ircc_init_43 },
+	{ "M1535", { 0x3f0, 0x370 }, 0x51, 0x23, 0x20, 0x53, ali_ircc_probe_53, ali_ircc_init_53 },
+	{ "M1563", { 0x3f0, 0x370 }, 0x51, 0x23, 0x20, 0x63, ali_ircc_probe_53, ali_ircc_init_53 },
+	{ NULL }
+};
+
+/* Max 4 instances for now */
+static struct ali_ircc_cb *dev_self[] = { NULL, NULL, NULL, NULL };
+
+/* Dongle Types */
+static char *dongle_types[] = {
+	"TFDS6000",
+	"HP HSDL-3600",
+	"HP HSDL-1100",	
+	"No dongle connected",
+};
+
+/* Some prototypes */
+static int  ali_ircc_open(int i, chipio_t *info);
+
+static int  ali_ircc_close(struct ali_ircc_cb *self);
+
+static int  ali_ircc_setup(chipio_t *info);
+static int  ali_ircc_is_receiving(struct ali_ircc_cb *self);
+static int  ali_ircc_net_open(struct net_device *dev);
+static int  ali_ircc_net_close(struct net_device *dev);
+static int  ali_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+static int  ali_ircc_pmproc(struct pm_dev *dev, pm_request_t rqst, void *data);
+static void ali_ircc_change_speed(struct ali_ircc_cb *self, __u32 baud);
+static void ali_ircc_suspend(struct ali_ircc_cb *self);
+static void ali_ircc_wakeup(struct ali_ircc_cb *self);
+static struct net_device_stats *ali_ircc_net_get_stats(struct net_device *dev);
+
+/* SIR function */
+static int  ali_ircc_sir_hard_xmit(struct sk_buff *skb, struct net_device *dev);
+static irqreturn_t ali_ircc_sir_interrupt(struct ali_ircc_cb *self);
+static void ali_ircc_sir_receive(struct ali_ircc_cb *self);
+static void ali_ircc_sir_write_wakeup(struct ali_ircc_cb *self);
+static int  ali_ircc_sir_write(int iobase, int fifo_size, __u8 *buf, int len);
+static void ali_ircc_sir_change_speed(struct ali_ircc_cb *priv, __u32 speed);
+
+/* FIR function */
+static int  ali_ircc_fir_hard_xmit(struct sk_buff *skb, struct net_device *dev);
+static void ali_ircc_fir_change_speed(struct ali_ircc_cb *priv, __u32 speed);
+static irqreturn_t ali_ircc_fir_interrupt(struct ali_ircc_cb *self);
+static int  ali_ircc_dma_receive(struct ali_ircc_cb *self); 
+static int  ali_ircc_dma_receive_complete(struct ali_ircc_cb *self);
+static int  ali_ircc_dma_xmit_complete(struct ali_ircc_cb *self);
+static void ali_ircc_dma_xmit(struct ali_ircc_cb *self);
+
+/* My Function */
+static int  ali_ircc_read_dongle_id (int i, chipio_t *info);
+static void ali_ircc_change_dongle_speed(struct ali_ircc_cb *priv, int speed);
+
+/* ALi chip function */
+static void SIR2FIR(int iobase);
+static void FIR2SIR(int iobase);
+static void SetCOMInterrupts(struct ali_ircc_cb *self , unsigned char enable);
+
+/*
+ * Function ali_ircc_init ()
+ *
+ *    Initialize chip. Find out whay kinds of chips we are dealing with
+ *    and their configuation registers address
+ */
+static int __init ali_ircc_init(void)
+{
+	ali_chip_t *chip;
+	chipio_t info;
+	int ret = -ENODEV;
+	int cfg, cfg_base;
+	int reg, revision;
+	int i = 0;
+	
+	IRDA_DEBUG(2, "%s(), ---------------- Start ----------------\n", __FUNCTION__);
+	
+	/* Probe for all the ALi chipsets we know about */
+	for (chip= chips; chip->name; chip++, i++) 
+	{
+		IRDA_DEBUG(2, "%s(), Probing for %s ...\n", __FUNCTION__, chip->name);
+				
+		/* Try all config registers for this chip */
+		for (cfg=0; cfg<2; cfg++)
+		{
+			cfg_base = chip->cfg[cfg];
+			if (!cfg_base)
+				continue;
+				
+			memset(&info, 0, sizeof(chipio_t));
+			info.cfg_base = cfg_base;
+			info.fir_base = io[i];
+			info.dma = dma[i];
+			info.irq = irq[i];
+			
+			
+			/* Enter Configuration */
+			outb(chip->entr1, cfg_base);
+			outb(chip->entr2, cfg_base);
+			
+			/* Select Logical Device 5 Registers (UART2) */
+			outb(0x07, cfg_base);
+			outb(0x05, cfg_base+1);
+			
+			/* Read Chip Identification Register */
+			outb(chip->cid_index, cfg_base);	
+			reg = inb(cfg_base+1);	
+				
+			if (reg == chip->cid_value)
+			{
+				IRDA_DEBUG(2, "%s(), Chip found at 0x%03x\n", __FUNCTION__, cfg_base);
+					   
+				outb(0x1F, cfg_base);
+				revision = inb(cfg_base+1);
+				IRDA_DEBUG(2, "%s(), Found %s chip, revision=%d\n", __FUNCTION__,
+					   chip->name, revision);					
+				
+				/* 
+				 * If the user supplies the base address, then
+				 * we init the chip, if not we probe the values
+				 * set by the BIOS
+				 */				
+				if (io[i] < 2000)
+				{
+					chip->init(chip, &info);
+				}
+				else
+				{
+					chip->probe(chip, &info);	
+				}
+				
+				if (ali_ircc_open(i, &info) == 0)
+					ret = 0;
+				i++;				
+			}
+			else
+			{
+				IRDA_DEBUG(2, "%s(), No %s chip at 0x%03x\n", __FUNCTION__, chip->name, cfg_base);
+			}
+			/* Exit configuration */
+			outb(0xbb, cfg_base);
+		}
+	}		
+		
+	IRDA_DEBUG(2, "%s(), ----------------- End -----------------\n", __FUNCTION__);					   		
+	return ret;
+}
+
+/*
+ * Function ali_ircc_cleanup ()
+ *
+ *    Close all configured chips
+ *
+ */
+static void __exit ali_ircc_cleanup(void)
+{
+	int i;
+
+	IRDA_DEBUG(2, "%s(), ---------------- Start ----------------\n", __FUNCTION__);	
+	
+	pm_unregister_all(ali_ircc_pmproc);
+
+	for (i=0; i < 4; i++) {
+		if (dev_self[i])
+			ali_ircc_close(dev_self[i]);
+	}
+	
+	IRDA_DEBUG(2, "%s(), ----------------- End -----------------\n", __FUNCTION__);
+}
+
+/*
+ * Function ali_ircc_open (int i, chipio_t *inf)
+ *
+ *    Open driver instance
+ *
+ */
+static int ali_ircc_open(int i, chipio_t *info)
+{
+	struct net_device *dev;
+	struct ali_ircc_cb *self;
+	struct pm_dev *pmdev;
+	int dongle_id;
+	int err;
+			
+	IRDA_DEBUG(2, "%s(), ---------------- Start ----------------\n", __FUNCTION__);	
+	
+	/* Set FIR FIFO and DMA Threshold */
+	if ((ali_ircc_setup(info)) == -1)
+		return -1;
+		
+	dev = alloc_irdadev(sizeof(*self));
+	if (dev == NULL) {
+		IRDA_ERROR("%s(), can't allocate memory for control block!\n",
+			   __FUNCTION__);
+		return -ENOMEM;
+	}
+
+	self = dev->priv;
+	self->netdev = dev;
+	spin_lock_init(&self->lock);
+   
+	/* Need to store self somewhere */
+	dev_self[i] = self;
+	self->index = i;
+
+	/* Initialize IO */
+	self->io.cfg_base  = info->cfg_base;	/* In ali_ircc_probe_53 assign 		*/
+	self->io.fir_base  = info->fir_base;	/* info->sir_base = info->fir_base 	*/
+	self->io.sir_base  = info->sir_base; 	/* ALi SIR and FIR use the same address */
+        self->io.irq       = info->irq;
+        self->io.fir_ext   = CHIP_IO_EXTENT;
+        self->io.dma       = info->dma;
+        self->io.fifo_size = 16;		/* SIR: 16, FIR: 32 Benjamin 2000/11/1 */
+	
+	/* Reserve the ioports that we need */
+	if (!request_region(self->io.fir_base, self->io.fir_ext, driver_name)) {
+		IRDA_WARNING("%s(), can't get iobase of 0x%03x\n", __FUNCTION__,
+			self->io.fir_base);
+		err = -ENODEV;
+		goto err_out1;
+	}
+
+	/* Initialize QoS for this device */
+	irda_init_max_qos_capabilies(&self->qos);
+	
+	/* The only value we must override it the baudrate */
+	self->qos.baud_rate.bits = IR_9600|IR_19200|IR_38400|IR_57600|
+		IR_115200|IR_576000|IR_1152000|(IR_4000000 << 8); // benjamin 2000/11/8 05:27PM
+			
+	self->qos.min_turn_time.bits = qos_mtt_bits;
+			
+	irda_qos_bits_to_value(&self->qos);
+	
+	/* Max DMA buffer size needed = (data_size + 6) * (window_size) + 6; */
+	self->rx_buff.truesize = 14384; 
+	self->tx_buff.truesize = 14384;
+
+	/* Allocate memory if needed */
+	self->rx_buff.head =
+		dma_alloc_coherent(NULL, self->rx_buff.truesize,
+				   &self->rx_buff_dma, GFP_KERNEL);
+	if (self->rx_buff.head == NULL) {
+		err = -ENOMEM;
+		goto err_out2;
+	}
+	memset(self->rx_buff.head, 0, self->rx_buff.truesize);
+	
+	self->tx_buff.head =
+		dma_alloc_coherent(NULL, self->tx_buff.truesize,
+				   &self->tx_buff_dma, GFP_KERNEL);
+	if (self->tx_buff.head == NULL) {
+		err = -ENOMEM;
+		goto err_out3;
+	}
+	memset(self->tx_buff.head, 0, self->tx_buff.truesize);
+
+	self->rx_buff.in_frame = FALSE;
+	self->rx_buff.state = OUTSIDE_FRAME;
+	self->tx_buff.data = self->tx_buff.head;
+	self->rx_buff.data = self->rx_buff.head;
+	
+	/* Reset Tx queue info */
+	self->tx_fifo.len = self->tx_fifo.ptr = self->tx_fifo.free = 0;
+	self->tx_fifo.tail = self->tx_buff.head;
+
+	
+	/* Keep track of module usage */
+	SET_MODULE_OWNER(dev);
+
+	/* Override the network functions we need to use */
+	dev->hard_start_xmit = ali_ircc_sir_hard_xmit;
+	dev->open            = ali_ircc_net_open;
+	dev->stop            = ali_ircc_net_close;
+	dev->do_ioctl        = ali_ircc_net_ioctl;
+	dev->get_stats	     = ali_ircc_net_get_stats;
+
+	err = register_netdev(dev);
+	if (err) {
+		IRDA_ERROR("%s(), register_netdev() failed!\n", __FUNCTION__);
+		goto err_out4;
+	}
+	IRDA_MESSAGE("IrDA: Registered device %s\n", dev->name);
+
+	/* Check dongle id */
+	dongle_id = ali_ircc_read_dongle_id(i, info);
+	IRDA_MESSAGE("%s(), %s, Found dongle: %s\n", __FUNCTION__, driver_name, dongle_types[dongle_id]);
+		
+	self->io.dongle_id = dongle_id;
+	
+        pmdev = pm_register(PM_SYS_DEV, PM_SYS_IRDA, ali_ircc_pmproc);
+        if (pmdev)
+                pmdev->data = self;
+
+	IRDA_DEBUG(2, "%s(), ----------------- End -----------------\n", __FUNCTION__);
+	
+	return 0;
+
+ err_out4:
+	dma_free_coherent(NULL, self->tx_buff.truesize,
+			  self->tx_buff.head, self->tx_buff_dma);
+ err_out3:
+	dma_free_coherent(NULL, self->rx_buff.truesize,
+			  self->rx_buff.head, self->rx_buff_dma);
+ err_out2:
+	release_region(self->io.fir_base, self->io.fir_ext);
+ err_out1:
+	dev_self[i] = NULL;
+	free_netdev(dev);
+	return err;
+}
+
+
+/*
+ * Function ali_ircc_close (self)
+ *
+ *    Close driver instance
+ *
+ */
+static int __exit ali_ircc_close(struct ali_ircc_cb *self)
+{
+	int iobase;
+
+	IRDA_DEBUG(4, "%s(), ---------------- Start ----------------\n", __FUNCTION__);
+
+	IRDA_ASSERT(self != NULL, return -1;);
+
+        iobase = self->io.fir_base;
+
+	/* Remove netdevice */
+	unregister_netdev(self->netdev);
+
+	/* Release the PORT that this driver is using */
+	IRDA_DEBUG(4, "%s(), Releasing Region %03x\n", __FUNCTION__, self->io.fir_base);
+	release_region(self->io.fir_base, self->io.fir_ext);
+
+	if (self->tx_buff.head)
+		dma_free_coherent(NULL, self->tx_buff.truesize,
+				  self->tx_buff.head, self->tx_buff_dma);
+	
+	if (self->rx_buff.head)
+		dma_free_coherent(NULL, self->rx_buff.truesize,
+				  self->rx_buff.head, self->rx_buff_dma);
+
+	dev_self[self->index] = NULL;
+	free_netdev(self->netdev);
+	
+	IRDA_DEBUG(2, "%s(), ----------------- End -----------------\n", __FUNCTION__);
+	
+	return 0;
+}
+
+/*
+ * Function ali_ircc_init_43 (chip, info)
+ *
+ *    Initialize the ALi M1543 chip. 
+ */
+static int ali_ircc_init_43(ali_chip_t *chip, chipio_t *info) 
+{
+	/* All controller information like I/O address, DMA channel, IRQ
+	 * are set by BIOS
+	 */
+	
+	return 0;
+}
+
+/*
+ * Function ali_ircc_init_53 (chip, info)
+ *
+ *    Initialize the ALi M1535 chip. 
+ */
+static int ali_ircc_init_53(ali_chip_t *chip, chipio_t *info) 
+{
+	/* All controller information like I/O address, DMA channel, IRQ
+	 * are set by BIOS
+	 */
+	
+	return 0;
+}
+
+/*
+ * Function ali_ircc_probe_53 (chip, info)
+ *    	
+ *	Probes for the ALi M1535D or M1535
+ */
+static int ali_ircc_probe_53(ali_chip_t *chip, chipio_t *info)
+{
+	int cfg_base = info->cfg_base;
+	int hi, low, reg;
+	
+	IRDA_DEBUG(2, "%s(), ---------------- Start ----------------\n", __FUNCTION__);
+	
+	/* Enter Configuration */
+	outb(chip->entr1, cfg_base);
+	outb(chip->entr2, cfg_base);
+	
+	/* Select Logical Device 5 Registers (UART2) */
+	outb(0x07, cfg_base);
+	outb(0x05, cfg_base+1);
+	
+	/* Read address control register */
+	outb(0x60, cfg_base);
+	hi = inb(cfg_base+1);	
+	outb(0x61, cfg_base);
+	low = inb(cfg_base+1);
+	info->fir_base = (hi<<8) + low;
+	
+	info->sir_base = info->fir_base;
+	
+	IRDA_DEBUG(2, "%s(), probing fir_base=0x%03x\n", __FUNCTION__, info->fir_base);
+		
+	/* Read IRQ control register */
+	outb(0x70, cfg_base);
+	reg = inb(cfg_base+1);
+	info->irq = reg & 0x0f;
+	IRDA_DEBUG(2, "%s(), probing irq=%d\n", __FUNCTION__, info->irq);
+	
+	/* Read DMA channel */
+	outb(0x74, cfg_base);
+	reg = inb(cfg_base+1);
+	info->dma = reg & 0x07;
+	
+	if(info->dma == 0x04)
+		IRDA_WARNING("%s(), No DMA channel assigned !\n", __FUNCTION__);
+	else
+		IRDA_DEBUG(2, "%s(), probing dma=%d\n", __FUNCTION__, info->dma);
+	
+	/* Read Enabled Status */
+	outb(0x30, cfg_base);
+	reg = inb(cfg_base+1);
+	info->enabled = (reg & 0x80) && (reg & 0x01);
+	IRDA_DEBUG(2, "%s(), probing enabled=%d\n", __FUNCTION__, info->enabled);
+	
+	/* Read Power Status */
+	outb(0x22, cfg_base);
+	reg = inb(cfg_base+1);
+	info->suspended = (reg & 0x20);
+	IRDA_DEBUG(2, "%s(), probing suspended=%d\n", __FUNCTION__, info->suspended);
+	
+	/* Exit configuration */
+	outb(0xbb, cfg_base);
+		
+	IRDA_DEBUG(2, "%s(), ----------------- End -----------------\n", __FUNCTION__);	
+	
+	return 0;	
+}
+
+/*
+ * Function ali_ircc_setup (info)
+ *
+ *    	Set FIR FIFO and DMA Threshold
+ *	Returns non-negative on success.
+ *
+ */
+static int ali_ircc_setup(chipio_t *info)
+{
+	unsigned char tmp;
+	int version;
+	int iobase = info->fir_base;
+	
+	IRDA_DEBUG(2, "%s(), ---------------- Start ----------------\n", __FUNCTION__);
+	
+	/* Locking comments :
+	 * Most operations here need to be protected. We are called before
+	 * the device instance is created in ali_ircc_open(), therefore 
+	 * nobody can bother us - Jean II */
+
+	/* Switch to FIR space */
+	SIR2FIR(iobase);
+	
+	/* Master Reset */
+	outb(0x40, iobase+FIR_MCR); // benjamin 2000/11/30 11:45AM
+	
+	/* Read FIR ID Version Register */
+	switch_bank(iobase, BANK3);
+	version = inb(iobase+FIR_ID_VR);
+	
+	/* Should be 0x00 in the M1535/M1535D */
+	if(version != 0x00)
+	{
+		IRDA_ERROR("%s, Wrong chip version %02x\n", driver_name, version);
+		return -1;
+	}
+	
+	// IRDA_MESSAGE("%s, Found chip at base=0x%03x\n", driver_name, info->cfg_base);
+	
+	/* Set FIR FIFO Threshold Register */
+	switch_bank(iobase, BANK1);
+	outb(RX_FIFO_Threshold, iobase+FIR_FIFO_TR);
+	
+	/* Set FIR DMA Threshold Register */
+	outb(RX_DMA_Threshold, iobase+FIR_DMA_TR);
+	
+	/* CRC enable */
+	switch_bank(iobase, BANK2);
+	outb(inb(iobase+FIR_IRDA_CR) | IRDA_CR_CRC, iobase+FIR_IRDA_CR);
+	
+	/* NDIS driver set TX Length here BANK2 Alias 3, Alias4*/
+	
+	/* Switch to Bank 0 */
+	switch_bank(iobase, BANK0);
+	
+	tmp = inb(iobase+FIR_LCR_B);
+	tmp &=~0x20; // disable SIP
+	tmp |= 0x80; // these two steps make RX mode
+	tmp &= 0xbf;	
+	outb(tmp, iobase+FIR_LCR_B);
+		
+	/* Disable Interrupt */
+	outb(0x00, iobase+FIR_IER);
+	
+	
+	/* Switch to SIR space */
+	FIR2SIR(iobase);
+	
+	IRDA_MESSAGE("%s, driver loaded (Benjamin Kong)\n", driver_name);
+	
+	/* Enable receive interrupts */ 
+	// outb(UART_IER_RDI, iobase+UART_IER); //benjamin 2000/11/23 01:25PM
+	// Turn on the interrupts in ali_ircc_net_open
+	
+	IRDA_DEBUG(2, "%s(), ----------------- End ------------------\n", __FUNCTION__);	
+	
+	return 0;
+}
+
+/*
+ * Function ali_ircc_read_dongle_id (int index, info)
+ *
+ * Try to read dongle indentification. This procedure needs to be executed
+ * once after power-on/reset. It also needs to be used whenever you suspect
+ * that the user may have plugged/unplugged the IrDA Dongle.
+ */
+static int ali_ircc_read_dongle_id (int i, chipio_t *info)
+{
+	int dongle_id, reg;
+	int cfg_base = info->cfg_base;
+	
+	IRDA_DEBUG(2, "%s(), ---------------- Start ----------------\n", __FUNCTION__);
+		
+	/* Enter Configuration */
+	outb(chips[i].entr1, cfg_base);
+	outb(chips[i].entr2, cfg_base);
+	
+	/* Select Logical Device 5 Registers (UART2) */
+	outb(0x07, cfg_base);
+	outb(0x05, cfg_base+1);
+	
+	/* Read Dongle ID */
+	outb(0xf0, cfg_base);
+	reg = inb(cfg_base+1);	
+	dongle_id = ((reg>>6)&0x02) | ((reg>>5)&0x01);
+	IRDA_DEBUG(2, "%s(), probing dongle_id=%d, dongle_types=%s\n", __FUNCTION__, 
+		dongle_id, dongle_types[dongle_id]);
+	
+	/* Exit configuration */
+	outb(0xbb, cfg_base);
+			
+	IRDA_DEBUG(2, "%s(), ----------------- End ------------------\n", __FUNCTION__);	
+	
+	return dongle_id;
+}
+
+/*
+ * Function ali_ircc_interrupt (irq, dev_id, regs)
+ *
+ *    An interrupt from the chip has arrived. Time to do some work
+ *
+ */
+static irqreturn_t ali_ircc_interrupt(int irq, void *dev_id,
+					struct pt_regs *regs)
+{
+	struct net_device *dev = (struct net_device *) dev_id;
+	struct ali_ircc_cb *self;
+	int ret;
+		
+	IRDA_DEBUG(2, "%s(), ---------------- Start ----------------\n", __FUNCTION__);
+		
+ 	if (!dev) {
+		IRDA_WARNING("%s: irq %d for unknown device.\n", driver_name, irq);
+		return IRQ_NONE;
+	}	
+	
+	self = (struct ali_ircc_cb *) dev->priv;
+	
+	spin_lock(&self->lock);
+	
+	/* Dispatch interrupt handler for the current speed */
+	if (self->io.speed > 115200)
+		ret = ali_ircc_fir_interrupt(self);
+	else
+		ret = ali_ircc_sir_interrupt(self);
+		
+	spin_unlock(&self->lock);
+	
+	IRDA_DEBUG(2, "%s(), ----------------- End ------------------\n", __FUNCTION__);
+	return ret;
+}
+/*
+ * Function ali_ircc_fir_interrupt(irq, struct ali_ircc_cb *self)
+ *
+ *    Handle MIR/FIR interrupt
+ *
+ */
+static irqreturn_t ali_ircc_fir_interrupt(struct ali_ircc_cb *self)
+{
+	__u8 eir, OldMessageCount;
+	int iobase, tmp;
+	
+	IRDA_DEBUG(1, "%s(), ---------------- Start ----------------\n", __FUNCTION__);
+	
+	iobase = self->io.fir_base;
+	
+	switch_bank(iobase, BANK0);	
+	self->InterruptID = inb(iobase+FIR_IIR);		
+	self->BusStatus = inb(iobase+FIR_BSR);	
+	
+	OldMessageCount = (self->LineStatus + 1) & 0x07;
+	self->LineStatus = inb(iobase+FIR_LSR);	
+	//self->ier = inb(iobase+FIR_IER); 		2000/12/1 04:32PM
+	eir = self->InterruptID & self->ier; /* Mask out the interesting ones */ 
+	
+	IRDA_DEBUG(1, "%s(), self->InterruptID = %x\n", __FUNCTION__,self->InterruptID);
+	IRDA_DEBUG(1, "%s(), self->LineStatus = %x\n", __FUNCTION__,self->LineStatus);
+	IRDA_DEBUG(1, "%s(), self->ier = %x\n", __FUNCTION__,self->ier);
+	IRDA_DEBUG(1, "%s(), eir = %x\n", __FUNCTION__,eir);
+	
+	/* Disable interrupts */
+	 SetCOMInterrupts(self, FALSE);
+	
+	/* Tx or Rx Interrupt */
+	
+	if (eir & IIR_EOM) 
+	{		
+		if (self->io.direction == IO_XMIT) /* TX */
+		{
+			IRDA_DEBUG(1, "%s(), ******* IIR_EOM (Tx) *******\n", __FUNCTION__);
+			
+			if(ali_ircc_dma_xmit_complete(self))
+			{
+				if (irda_device_txqueue_empty(self->netdev)) 
+				{
+					/* Prepare for receive */
+					ali_ircc_dma_receive(self);					
+					self->ier = IER_EOM;									
+				}
+			}
+			else
+			{
+				self->ier = IER_EOM; 					
+			}
+									
+		}	
+		else /* RX */
+		{
+			IRDA_DEBUG(1, "%s(), ******* IIR_EOM (Rx) *******\n", __FUNCTION__);
+			
+			if(OldMessageCount > ((self->LineStatus+1) & 0x07))
+			{
+				self->rcvFramesOverflow = TRUE;	
+				IRDA_DEBUG(1, "%s(), ******* self->rcvFramesOverflow = TRUE ******** \n", __FUNCTION__);
+			}
+						
+			if (ali_ircc_dma_receive_complete(self))
+			{
+				IRDA_DEBUG(1, "%s(), ******* receive complete ******** \n", __FUNCTION__);
+				
+				self->ier = IER_EOM;				
+			}
+			else
+			{
+				IRDA_DEBUG(1, "%s(), ******* Not receive complete ******** \n", __FUNCTION__);
+				
+				self->ier = IER_EOM | IER_TIMER;								
+			}	
+		
+		}		
+	}
+	/* Timer Interrupt */
+	else if (eir & IIR_TIMER)
+	{	
+		if(OldMessageCount > ((self->LineStatus+1) & 0x07))
+		{
+			self->rcvFramesOverflow = TRUE;	
+			IRDA_DEBUG(1, "%s(), ******* self->rcvFramesOverflow = TRUE ******* \n", __FUNCTION__);
+		}
+		/* Disable Timer */
+		switch_bank(iobase, BANK1);
+		tmp = inb(iobase+FIR_CR);
+		outb( tmp& ~CR_TIMER_EN, iobase+FIR_CR);
+		
+		/* Check if this is a Tx timer interrupt */
+		if (self->io.direction == IO_XMIT)
+		{
+			ali_ircc_dma_xmit(self);
+			
+			/* Interrupt on EOM */
+			self->ier = IER_EOM;
+									
+		}
+		else /* Rx */
+		{
+			if(ali_ircc_dma_receive_complete(self)) 
+			{
+				self->ier = IER_EOM;
+			}
+			else
+			{
+				self->ier = IER_EOM | IER_TIMER;
+			}	
+		}		
+	}
+	
+	/* Restore Interrupt */	
+	SetCOMInterrupts(self, TRUE);	
+		
+	IRDA_DEBUG(1, "%s(), ----------------- End ---------------\n", __FUNCTION__);
+	return IRQ_RETVAL(eir);
+}
+
+/*
+ * Function ali_ircc_sir_interrupt (irq, self, eir)
+ *
+ *    Handle SIR interrupt
+ *
+ */
+static irqreturn_t ali_ircc_sir_interrupt(struct ali_ircc_cb *self)
+{
+	int iobase;
+	int iir, lsr;
+	
+	IRDA_DEBUG(2, "%s(), ---------------- Start ----------------\n", __FUNCTION__);
+	
+	iobase = self->io.sir_base;
+
+	iir = inb(iobase+UART_IIR) & UART_IIR_ID;
+	if (iir) {	
+		/* Clear interrupt */
+		lsr = inb(iobase+UART_LSR);
+
+		IRDA_DEBUG(4, "%s(), iir=%02x, lsr=%02x, iobase=%#x\n", __FUNCTION__, 
+			   iir, lsr, iobase);
+
+		switch (iir) 
+		{
+			case UART_IIR_RLSI:
+				IRDA_DEBUG(2, "%s(), RLSI\n", __FUNCTION__);
+				break;
+			case UART_IIR_RDI:
+				/* Receive interrupt */
+				ali_ircc_sir_receive(self);
+				break;
+			case UART_IIR_THRI:
+				if (lsr & UART_LSR_THRE)
+				{
+					/* Transmitter ready for data */
+					ali_ircc_sir_write_wakeup(self);				
+				}				
+				break;
+			default:
+				IRDA_DEBUG(0, "%s(), unhandled IIR=%#x\n", __FUNCTION__, iir);
+				break;
+		} 
+		
+	}
+	
+	
+	IRDA_DEBUG(2, "%s(), ----------------- End ------------------\n", __FUNCTION__);	
+
+	return IRQ_RETVAL(iir);
+}
+
+
+/*
+ * Function ali_ircc_sir_receive (self)
+ *
+ *    Receive one frame from the infrared port
+ *
+ */
+static void ali_ircc_sir_receive(struct ali_ircc_cb *self) 
+{
+	int boguscount = 0;
+	int iobase;
+	
+	IRDA_DEBUG(2, "%s(), ---------------- Start ----------------\n", __FUNCTION__);
+	IRDA_ASSERT(self != NULL, return;);
+
+	iobase = self->io.sir_base;
+
+	/*  
+	 * Receive all characters in Rx FIFO, unwrap and unstuff them. 
+         * async_unwrap_char will deliver all found frames  
+	 */
+	do {
+		async_unwrap_char(self->netdev, &self->stats, &self->rx_buff, 
+				  inb(iobase+UART_RX));
+
+		/* Make sure we don't stay here too long */
+		if (boguscount++ > 32) {
+			IRDA_DEBUG(2,"%s(), breaking!\n", __FUNCTION__);
+			break;
+		}
+	} while (inb(iobase+UART_LSR) & UART_LSR_DR);	
+	
+	IRDA_DEBUG(2, "%s(), ----------------- End ------------------\n", __FUNCTION__ );	
+}
+
+/*
+ * Function ali_ircc_sir_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 ali_ircc_sir_write_wakeup(struct ali_ircc_cb *self)
+{
+	int actual = 0;
+	int iobase;	
+
+	IRDA_ASSERT(self != NULL, return;);
+
+	IRDA_DEBUG(2, "%s(), ---------------- Start ----------------\n", __FUNCTION__ );
+	
+	iobase = self->io.sir_base;
+
+	/* Finished with frame?  */
+	if (self->tx_buff.len > 0)  
+	{
+		/* Write data left in transmit buffer */
+		actual = ali_ircc_sir_write(iobase, self->io.fifo_size, 
+				      self->tx_buff.data, self->tx_buff.len);
+		self->tx_buff.data += actual;
+		self->tx_buff.len  -= actual;
+	} 
+	else 
+	{
+		if (self->new_speed) 
+		{
+			/* We must wait until all data are gone */
+			while(!(inb(iobase+UART_LSR) & UART_LSR_TEMT))
+				IRDA_DEBUG(1, "%s(), UART_LSR_THRE\n", __FUNCTION__ );
+			
+			IRDA_DEBUG(1, "%s(), Changing speed! self->new_speed = %d\n", __FUNCTION__ , self->new_speed);
+			ali_ircc_change_speed(self, self->new_speed);
+			self->new_speed = 0;			
+			
+			// benjamin 2000/11/10 06:32PM
+			if (self->io.speed > 115200)
+			{
+				IRDA_DEBUG(2, "%s(), ali_ircc_change_speed from UART_LSR_TEMT \n", __FUNCTION__ );				
+					
+				self->ier = IER_EOM;
+				// SetCOMInterrupts(self, TRUE);							
+				return;							
+			}
+		}
+		else
+		{
+			netif_wake_queue(self->netdev);	
+		}
+			
+		self->stats.tx_packets++;
+		
+		/* Turn on receive interrupts */
+		outb(UART_IER_RDI, iobase+UART_IER);
+	}
+		
+	IRDA_DEBUG(2, "%s(), ----------------- End ------------------\n", __FUNCTION__ );	
+}
+
+static void ali_ircc_change_speed(struct ali_ircc_cb *self, __u32 baud)
+{
+	struct net_device *dev = self->netdev;
+	int iobase;
+	
+	IRDA_DEBUG(1, "%s(), ---------------- Start ----------------\n", __FUNCTION__ );
+	
+	IRDA_DEBUG(2, "%s(), setting speed = %d \n", __FUNCTION__ , baud);
+	
+	/* This function *must* be called with irq off and spin-lock.
+	 * - Jean II */
+
+	iobase = self->io.fir_base;
+	
+	SetCOMInterrupts(self, FALSE); // 2000/11/24 11:43AM
+	
+	/* Go to MIR, FIR Speed */
+	if (baud > 115200)
+	{
+		
+					
+		ali_ircc_fir_change_speed(self, baud);			
+		
+		/* Install FIR xmit handler*/
+		dev->hard_start_xmit = ali_ircc_fir_hard_xmit;		
+				
+		/* Enable Interuupt */
+		self->ier = IER_EOM; // benjamin 2000/11/20 07:24PM					
+				
+		/* Be ready for incomming frames */
+		ali_ircc_dma_receive(self);	// benajmin 2000/11/8 07:46PM not complete
+	}	
+	/* Go to SIR Speed */
+	else
+	{
+		ali_ircc_sir_change_speed(self, baud);
+				
+		/* Install SIR xmit handler*/
+		dev->hard_start_xmit = ali_ircc_sir_hard_xmit;
+	}
+	
+		
+	SetCOMInterrupts(self, TRUE);	// 2000/11/24 11:43AM
+		
+	netif_wake_queue(self->netdev);	
+	
+	IRDA_DEBUG(2, "%s(), ----------------- End ------------------\n", __FUNCTION__ );	
+}
+
+static void ali_ircc_fir_change_speed(struct ali_ircc_cb *priv, __u32 baud)
+{
+		
+	int iobase; 
+	struct ali_ircc_cb *self = (struct ali_ircc_cb *) priv;
+	struct net_device *dev;
+
+	IRDA_DEBUG(1, "%s(), ---------------- Start ----------------\n", __FUNCTION__ );
+		
+	IRDA_ASSERT(self != NULL, return;);
+
+	dev = self->netdev;
+	iobase = self->io.fir_base;
+	
+	IRDA_DEBUG(1, "%s(), self->io.speed = %d, change to speed = %d\n", __FUNCTION__ ,self->io.speed,baud);
+	
+	/* Come from SIR speed */
+	if(self->io.speed <=115200)
+	{
+		SIR2FIR(iobase);
+	}
+		
+	/* Update accounting for new speed */
+	self->io.speed = baud;
+		
+	// Set Dongle Speed mode
+	ali_ircc_change_dongle_speed(self, baud);
+		
+	IRDA_DEBUG(1, "%s(), ----------------- End ------------------\n", __FUNCTION__ );	
+}
+
+/*
+ * Function ali_sir_change_speed (self, speed)
+ *
+ *    Set speed of IrDA port to specified baudrate
+ *
+ */
+static void ali_ircc_sir_change_speed(struct ali_ircc_cb *priv, __u32 speed)
+{
+	struct ali_ircc_cb *self = (struct ali_ircc_cb *) priv;
+	unsigned long flags;
+	int iobase; 
+	int fcr;    /* FIFO control reg */
+	int lcr;    /* Line control reg */
+	int divisor;
+
+	IRDA_DEBUG(1, "%s(), ---------------- Start ----------------\n", __FUNCTION__ );
+	
+	IRDA_DEBUG(1, "%s(), Setting speed to: %d\n", __FUNCTION__ , speed);
+
+	IRDA_ASSERT(self != NULL, return;);
+
+	iobase = self->io.sir_base;
+	
+	/* Come from MIR or FIR speed */
+	if(self->io.speed >115200)
+	{	
+		// Set Dongle Speed mode first
+		ali_ircc_change_dongle_speed(self, speed);
+			
+		FIR2SIR(iobase);
+	}
+		
+	// Clear Line and Auxiluary status registers 2000/11/24 11:47AM
+		
+	inb(iobase+UART_LSR);
+	inb(iobase+UART_SCR);
+		
+	/* Update accounting for new speed */
+	self->io.speed = speed;
+
+	spin_lock_irqsave(&self->lock, flags);
+
+	divisor = 115200/speed;
+	
+	fcr = UART_FCR_ENABLE_FIFO;
+
+	/* 
+	 * Use trigger level 1 to avoid 3 ms. timeout delay at 9600 bps, and
+	 * almost 1,7 ms at 19200 bps. At speeds above that we can just forget
+	 * about this timeout since it will always be fast enough. 
+	 */
+	if (self->io.speed < 38400)
+		fcr |= UART_FCR_TRIGGER_1;
+	else 
+		fcr |= UART_FCR_TRIGGER_14;
+        
+	/* IrDA ports use 8N1 */
+	lcr = UART_LCR_WLEN8;
+	
+	outb(UART_LCR_DLAB | lcr, iobase+UART_LCR); /* Set DLAB */
+	outb(divisor & 0xff,      iobase+UART_DLL); /* Set speed */
+	outb(divisor >> 8,	  iobase+UART_DLM);
+	outb(lcr,		  iobase+UART_LCR); /* Set 8N1	*/
+	outb(fcr,		  iobase+UART_FCR); /* Enable FIFO's */
+
+	/* without this, the conection will be broken after come back from FIR speed,
+	   but with this, the SIR connection is harder to established */
+	outb((UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2), iobase+UART_MCR);
+	
+	spin_unlock_irqrestore(&self->lock, flags);
+	
+	IRDA_DEBUG(1, "%s(), ----------------- End ------------------\n", __FUNCTION__ );	
+}
+
+static void ali_ircc_change_dongle_speed(struct ali_ircc_cb *priv, int speed)
+{
+	
+	struct ali_ircc_cb *self = (struct ali_ircc_cb *) priv;
+	int iobase,dongle_id;
+	int tmp = 0;
+			
+	IRDA_DEBUG(1, "%s(), ---------------- Start ----------------\n", __FUNCTION__ );	
+	
+	iobase = self->io.fir_base; 	/* or iobase = self->io.sir_base; */
+	dongle_id = self->io.dongle_id;
+	
+	/* We are already locked, no need to do it again */
+		
+	IRDA_DEBUG(1, "%s(), Set Speed for %s , Speed = %d\n", __FUNCTION__ , dongle_types[dongle_id], speed);		
+	
+	switch_bank(iobase, BANK2);
+	tmp = inb(iobase+FIR_IRDA_CR);
+		
+	/* IBM type dongle */
+	if(dongle_id == 0)
+	{				
+		if(speed == 4000000)
+		{
+			//	      __ __	
+			// SD/MODE __|     |__ __
+			//               __ __ 
+			// IRTX    __ __|     |__
+			//         T1 T2 T3 T4 T5
+			
+			tmp &=  ~IRDA_CR_HDLC;		// HDLC=0
+			tmp |= IRDA_CR_CRC;	   	// CRC=1
+			
+			switch_bank(iobase, BANK2);
+			outb(tmp, iobase+FIR_IRDA_CR);
+			
+      			// T1 -> SD/MODE:0 IRTX:0
+      			tmp &= ~0x09;
+      			tmp |= 0x02;
+      			outb(tmp, iobase+FIR_IRDA_CR);
+      			udelay(2);
+      			
+      			// T2 -> SD/MODE:1 IRTX:0
+      			tmp &= ~0x01;
+      			tmp |= 0x0a;
+      			outb(tmp, iobase+FIR_IRDA_CR);
+      			udelay(2);
+      			
+      			// T3 -> SD/MODE:1 IRTX:1
+      			tmp |= 0x0b;
+      			outb(tmp, iobase+FIR_IRDA_CR);
+      			udelay(2);
+      			
+      			// T4 -> SD/MODE:0 IRTX:1
+      			tmp &= ~0x08;
+      			tmp |= 0x03;
+      			outb(tmp, iobase+FIR_IRDA_CR);
+      			udelay(2);
+      			
+      			// T5 -> SD/MODE:0 IRTX:0
+      			tmp &= ~0x09;
+      			tmp |= 0x02;
+      			outb(tmp, iobase+FIR_IRDA_CR);
+      			udelay(2);
+      			
+      			// reset -> Normal TX output Signal
+      			outb(tmp & ~0x02, iobase+FIR_IRDA_CR);      			
+		}
+		else /* speed <=1152000 */
+		{	
+			//	      __	
+			// SD/MODE __|  |__
+			//
+			// IRTX    ________
+			//         T1 T2 T3  
+			
+			/* MIR 115200, 57600 */
+			if (speed==1152000)
+			{
+				tmp |= 0xA0;	   //HDLC=1, 1.152Mbps=1
+      			}
+      			else
+      			{
+				tmp &=~0x80;	   //HDLC 0.576Mbps
+				tmp |= 0x20;	   //HDLC=1,
+      			}			
+      			
+      			tmp |= IRDA_CR_CRC;	   	// CRC=1
+      			
+      			switch_bank(iobase, BANK2);
+      			outb(tmp, iobase+FIR_IRDA_CR);
+						
+			/* MIR 115200, 57600 */	
+						
+			//switch_bank(iobase, BANK2);			
+			// T1 -> SD/MODE:0 IRTX:0
+      			tmp &= ~0x09;
+      			tmp |= 0x02;
+      			outb(tmp, iobase+FIR_IRDA_CR);
+      			udelay(2);
+      			
+      			// T2 -> SD/MODE:1 IRTX:0
+      			tmp &= ~0x01;     
+      			tmp |= 0x0a;      
+      			outb(tmp, iobase+FIR_IRDA_CR);
+      			
+      			// T3 -> SD/MODE:0 IRTX:0
+      			tmp &= ~0x09;
+      			tmp |= 0x02;
+      			outb(tmp, iobase+FIR_IRDA_CR);
+      			udelay(2);
+      			
+      			// reset -> Normal TX output Signal
+      			outb(tmp & ~0x02, iobase+FIR_IRDA_CR);      						
+		}		
+	}
+	else if (dongle_id == 1) /* HP HDSL-3600 */
+	{
+		switch(speed)
+		{
+		case 4000000:
+			tmp &=  ~IRDA_CR_HDLC;	// HDLC=0
+			break;	
+			
+		case 1152000:
+			tmp |= 0xA0;	   	// HDLC=1, 1.152Mbps=1
+      			break;
+      			
+      		case 576000:
+      			tmp &=~0x80;	   	// HDLC 0.576Mbps
+			tmp |= 0x20;	   	// HDLC=1,
+			break;
+      		}			
+			
+		tmp |= IRDA_CR_CRC;	   	// CRC=1
+			
+		switch_bank(iobase, BANK2);
+      		outb(tmp, iobase+FIR_IRDA_CR);		
+	}
+	else /* HP HDSL-1100 */
+	{
+		if(speed <= 115200) /* SIR */
+		{
+			
+			tmp &= ~IRDA_CR_FIR_SIN;	// HP sin select = 0
+			
+			switch_bank(iobase, BANK2);
+      			outb(tmp, iobase+FIR_IRDA_CR);			
+		}
+		else /* MIR FIR */
+		{	
+			
+			switch(speed)
+			{
+			case 4000000:
+				tmp &=  ~IRDA_CR_HDLC;	// HDLC=0
+				break;	
+			
+			case 1152000:
+				tmp |= 0xA0;	   	// HDLC=1, 1.152Mbps=1
+      				break;
+      			
+      			case 576000:
+      				tmp &=~0x80;	   	// HDLC 0.576Mbps
+				tmp |= 0x20;	   	// HDLC=1,
+				break;
+      			}			
+			
+			tmp |= IRDA_CR_CRC;	   	// CRC=1
+			tmp |= IRDA_CR_FIR_SIN;		// HP sin select = 1
+			
+			switch_bank(iobase, BANK2);
+      			outb(tmp, iobase+FIR_IRDA_CR);			
+		}
+	}
+			
+	switch_bank(iobase, BANK0);
+	
+	IRDA_DEBUG(1, "%s(), ----------------- End ------------------\n", __FUNCTION__ );		
+}
+
+/*
+ * Function ali_ircc_sir_write (driver)
+ *
+ *    Fill Tx FIFO with transmit data
+ *
+ */
+static int ali_ircc_sir_write(int iobase, int fifo_size, __u8 *buf, int len)
+{
+	int actual = 0;
+	
+	IRDA_DEBUG(2, "%s(), ---------------- Start ----------------\n", __FUNCTION__ );
+		
+	/* Tx FIFO should be empty! */
+	if (!(inb(iobase+UART_LSR) & UART_LSR_THRE)) {
+		IRDA_DEBUG(0, "%s(), failed, fifo not empty!\n", __FUNCTION__ );
+		return 0;
+	}
+        
+	/* Fill FIFO with current frame */
+	while ((fifo_size-- > 0) && (actual < len)) {
+		/* Transmit next byte */
+		outb(buf[actual], iobase+UART_TX);
+
+		actual++;
+	}
+	
+        IRDA_DEBUG(2, "%s(), ----------------- End ------------------\n", __FUNCTION__ );	
+	return actual;
+}
+
+/*
+ * Function ali_ircc_net_open (dev)
+ *
+ *    Start the device
+ *
+ */
+static int ali_ircc_net_open(struct net_device *dev)
+{
+	struct ali_ircc_cb *self;
+	int iobase;
+	char hwname[32];
+		
+	IRDA_DEBUG(2, "%s(), ---------------- Start ----------------\n", __FUNCTION__ );
+	
+	IRDA_ASSERT(dev != NULL, return -1;);
+	
+	self = (struct ali_ircc_cb *) dev->priv;
+	
+	IRDA_ASSERT(self != NULL, return 0;);
+	
+	iobase = self->io.fir_base;
+	
+	/* Request IRQ and install Interrupt Handler */
+	if (request_irq(self->io.irq, ali_ircc_interrupt, 0, dev->name, dev)) 
+	{
+		IRDA_WARNING("%s, unable to allocate irq=%d\n", driver_name,
+			     self->io.irq);
+		return -EAGAIN;
+	}
+	
+	/*
+	 * Always allocate the DMA channel after the IRQ, and clean up on 
+	 * failure.
+	 */
+	if (request_dma(self->io.dma, dev->name)) {
+		IRDA_WARNING("%s, unable to allocate dma=%d\n", driver_name,
+			     self->io.dma);
+		free_irq(self->io.irq, self);
+		return -EAGAIN;
+	}
+	
+	/* Turn on interrups */
+	outb(UART_IER_RDI , iobase+UART_IER);
+
+	/* Ready to play! */
+	netif_start_queue(dev); //benjamin by irport
+	
+	/* Give self a hardware name */
+	sprintf(hwname, "ALI-FIR @ 0x%03x", self->io.fir_base);
+
+	/* 
+	 * Open new IrLAP layer instance, now that everything should be
+	 * initialized properly 
+	 */
+	self->irlap = irlap_open(dev, &self->qos, hwname);
+		
+	IRDA_DEBUG(2, "%s(), ----------------- End ------------------\n", __FUNCTION__ );	
+	
+	return 0;
+}
+
+/*
+ * Function ali_ircc_net_close (dev)
+ *
+ *    Stop the device
+ *
+ */
+static int ali_ircc_net_close(struct net_device *dev)
+{	
+
+	struct ali_ircc_cb *self;
+	//int iobase;
+			
+	IRDA_DEBUG(4, "%s(), ---------------- Start ----------------\n", __FUNCTION__ );
+		
+	IRDA_ASSERT(dev != NULL, return -1;);
+
+	self = (struct ali_ircc_cb *) dev->priv;
+	IRDA_ASSERT(self != NULL, return 0;);
+
+	/* Stop device */
+	netif_stop_queue(dev);
+	
+	/* Stop and remove instance of IrLAP */
+	if (self->irlap)
+		irlap_close(self->irlap);
+	self->irlap = NULL;
+		
+	disable_dma(self->io.dma);
+
+	/* Disable interrupts */
+	SetCOMInterrupts(self, FALSE);
+	       
+	free_irq(self->io.irq, dev);
+	free_dma(self->io.dma);
+
+	IRDA_DEBUG(2, "%s(), ----------------- End ------------------\n", __FUNCTION__ );	
+	
+	return 0;
+}
+
+/*
+ * Function ali_ircc_fir_hard_xmit (skb, dev)
+ *
+ *    Transmit the frame
+ *
+ */
+static int ali_ircc_fir_hard_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct ali_ircc_cb *self;
+	unsigned long flags;
+	int iobase;
+	__u32 speed;
+	int mtt, diff;
+	
+	IRDA_DEBUG(1, "%s(), ---------------- Start -----------------\n", __FUNCTION__ );	
+	
+	self = (struct ali_ircc_cb *) dev->priv;
+	iobase = self->io.fir_base;
+
+	netif_stop_queue(dev);
+	
+	/* Make sure tests *& speed change are atomic */
+	spin_lock_irqsave(&self->lock, flags);
+	
+	/* Note : you should make sure that speed changes are not going
+	 * to corrupt any outgoing frame. Look at nsc-ircc for the gory
+	 * details - Jean II */
+
+	/* Check if we need to change the speed */
+	speed = irda_get_next_speed(skb);
+	if ((speed != self->io.speed) && (speed != -1)) {
+		/* Check for empty frame */
+		if (!skb->len) {
+			ali_ircc_change_speed(self, speed); 
+			dev->trans_start = jiffies;
+			spin_unlock_irqrestore(&self->lock, flags);
+			dev_kfree_skb(skb);
+			return 0;
+		} else
+			self->new_speed = speed;
+	}
+
+	/* Register and copy this frame to DMA memory */
+	self->tx_fifo.queue[self->tx_fifo.free].start = self->tx_fifo.tail;
+	self->tx_fifo.queue[self->tx_fifo.free].len = skb->len;
+	self->tx_fifo.tail += skb->len;
+
+	self->stats.tx_bytes += skb->len;
+
+	memcpy(self->tx_fifo.queue[self->tx_fifo.free].start, skb->data, 
+	       skb->len);
+	
+	self->tx_fifo.len++;
+	self->tx_fifo.free++;
+
+	/* Start transmit only if there is currently no transmit going on */
+	if (self->tx_fifo.len == 1) 
+	{
+		/* Check if we must wait the min turn time or not */
+		mtt = irda_get_mtt(skb);
+				
+		if (mtt) 
+		{
+			/* Check how much time we have used already */
+			do_gettimeofday(&self->now);
+			
+			diff = self->now.tv_usec - self->stamp.tv_usec;
+			/* self->stamp is set from ali_ircc_dma_receive_complete() */
+							
+			IRDA_DEBUG(1, "%s(), ******* diff = %d ******* \n", __FUNCTION__ , diff);	
+			
+			if (diff < 0) 
+				diff += 1000000;
+			
+			/* Check if the mtt is larger than the time we have
+			 * already used by all the protocol processing
+			 */
+			if (mtt > diff)
+			{				
+				mtt -= diff;
+								
+				/* 
+				 * Use timer if delay larger than 1000 us, and
+				 * use udelay for smaller values which should
+				 * be acceptable
+				 */
+				if (mtt > 500) 
+				{
+					/* Adjust for timer resolution */
+					mtt = (mtt+250) / 500; 	/* 4 discard, 5 get advanced, Let's round off */
+					
+					IRDA_DEBUG(1, "%s(), ************** mtt = %d ***********\n", __FUNCTION__ , mtt);	
+					
+					/* Setup timer */
+					if (mtt == 1) /* 500 us */
+					{
+						switch_bank(iobase, BANK1);
+						outb(TIMER_IIR_500, iobase+FIR_TIMER_IIR);
+					}	
+					else if (mtt == 2) /* 1 ms */
+					{
+						switch_bank(iobase, BANK1);
+						outb(TIMER_IIR_1ms, iobase+FIR_TIMER_IIR);
+					}					
+					else /* > 2ms -> 4ms */
+					{
+						switch_bank(iobase, BANK1);
+						outb(TIMER_IIR_2ms, iobase+FIR_TIMER_IIR);
+					}
+					
+					
+					/* Start timer */
+					outb(inb(iobase+FIR_CR) | CR_TIMER_EN, iobase+FIR_CR);
+					self->io.direction = IO_XMIT;
+					
+					/* Enable timer interrupt */
+					self->ier = IER_TIMER;
+					SetCOMInterrupts(self, TRUE);					
+					
+					/* Timer will take care of the rest */
+					goto out; 
+				} 
+				else
+					udelay(mtt);
+			} // if (if (mtt > diff)
+		}// if (mtt) 
+				
+		/* Enable EOM interrupt */
+		self->ier = IER_EOM;
+		SetCOMInterrupts(self, TRUE);
+		
+		/* Transmit frame */
+		ali_ircc_dma_xmit(self);
+	} // if (self->tx_fifo.len == 1) 
+	
+ out:
+ 	
+	/* Not busy transmitting anymore if window is not full */
+	if (self->tx_fifo.free < MAX_TX_WINDOW)
+		netif_wake_queue(self->netdev);
+	
+	/* Restore bank register */
+	switch_bank(iobase, BANK0);
+
+	dev->trans_start = jiffies;
+	spin_unlock_irqrestore(&self->lock, flags);
+	dev_kfree_skb(skb);
+
+	IRDA_DEBUG(1, "%s(), ----------------- End ------------------\n", __FUNCTION__ );	
+	return 0;	
+}
+
+
+static void ali_ircc_dma_xmit(struct ali_ircc_cb *self)
+{
+	int iobase, tmp;
+	unsigned char FIFO_OPTI, Hi, Lo;
+	
+	
+	IRDA_DEBUG(1, "%s(), ---------------- Start -----------------\n", __FUNCTION__ );	
+	
+	iobase = self->io.fir_base;
+	
+	/* FIFO threshold , this method comes from NDIS5 code */
+	
+	if(self->tx_fifo.queue[self->tx_fifo.ptr].len < TX_FIFO_Threshold)
+		FIFO_OPTI = self->tx_fifo.queue[self->tx_fifo.ptr].len-1;
+	else
+		FIFO_OPTI = TX_FIFO_Threshold;
+	
+	/* Disable DMA */
+	switch_bank(iobase, BANK1);
+	outb(inb(iobase+FIR_CR) & ~CR_DMA_EN, iobase+FIR_CR);
+	
+	self->io.direction = IO_XMIT;
+	
+	irda_setup_dma(self->io.dma, 
+		       ((u8 *)self->tx_fifo.queue[self->tx_fifo.ptr].start -
+			self->tx_buff.head) + self->tx_buff_dma,
+		       self->tx_fifo.queue[self->tx_fifo.ptr].len, 
+		       DMA_TX_MODE);
+		
+	/* Reset Tx FIFO */
+	switch_bank(iobase, BANK0);
+	outb(LCR_A_FIFO_RESET, iobase+FIR_LCR_A);
+	
+	/* Set Tx FIFO threshold */
+	if (self->fifo_opti_buf!=FIFO_OPTI) 
+	{
+		switch_bank(iobase, BANK1);
+	    	outb(FIFO_OPTI, iobase+FIR_FIFO_TR) ;
+	    	self->fifo_opti_buf=FIFO_OPTI;
+	}
+	
+	/* Set Tx DMA threshold */
+	switch_bank(iobase, BANK1);
+	outb(TX_DMA_Threshold, iobase+FIR_DMA_TR);
+	
+	/* Set max Tx frame size */
+	Hi = (self->tx_fifo.queue[self->tx_fifo.ptr].len >> 8) & 0x0f;
+	Lo = self->tx_fifo.queue[self->tx_fifo.ptr].len & 0xff;
+	switch_bank(iobase, BANK2);
+	outb(Hi, iobase+FIR_TX_DSR_HI);
+	outb(Lo, iobase+FIR_TX_DSR_LO);
+	
+	/* Disable SIP , Disable Brick Wall (we don't support in TX mode), Change to TX mode */
+	switch_bank(iobase, BANK0);	
+	tmp = inb(iobase+FIR_LCR_B);
+	tmp &= ~0x20; // Disable SIP
+	outb(((unsigned char)(tmp & 0x3f) | LCR_B_TX_MODE) & ~LCR_B_BW, iobase+FIR_LCR_B);
+	IRDA_DEBUG(1, "%s(), ******* Change to TX mode: FIR_LCR_B = 0x%x ******* \n", __FUNCTION__ , inb(iobase+FIR_LCR_B));
+	
+	outb(0, iobase+FIR_LSR);
+			
+	/* Enable DMA and Burst Mode */
+	switch_bank(iobase, BANK1);
+	outb(inb(iobase+FIR_CR) | CR_DMA_EN | CR_DMA_BURST, iobase+FIR_CR);
+	
+	switch_bank(iobase, BANK0); 
+	
+	IRDA_DEBUG(1, "%s(), ----------------- End ------------------\n", __FUNCTION__ );
+}
+
+static int  ali_ircc_dma_xmit_complete(struct ali_ircc_cb *self)
+{
+	int iobase;
+	int ret = TRUE;
+	
+	IRDA_DEBUG(1, "%s(), ---------------- Start -----------------\n", __FUNCTION__ );	
+	
+	iobase = self->io.fir_base;
+	
+	/* Disable DMA */
+	switch_bank(iobase, BANK1);
+	outb(inb(iobase+FIR_CR) & ~CR_DMA_EN, iobase+FIR_CR);
+	
+	/* Check for underrun! */
+	switch_bank(iobase, BANK0);
+	if((inb(iobase+FIR_LSR) & LSR_FRAME_ABORT) == LSR_FRAME_ABORT)
+	
+	{
+		IRDA_ERROR("%s(), ********* LSR_FRAME_ABORT *********\n", __FUNCTION__);	
+		self->stats.tx_errors++;
+		self->stats.tx_fifo_errors++;		
+	}
+	else 
+	{
+		self->stats.tx_packets++;
+	}
+
+	/* Check if we need to change the speed */
+	if (self->new_speed) 
+	{
+		ali_ircc_change_speed(self, self->new_speed);
+		self->new_speed = 0;
+	}
+
+	/* Finished with this frame, so prepare for next */
+	self->tx_fifo.ptr++;
+	self->tx_fifo.len--;
+
+	/* Any frames to be sent back-to-back? */
+	if (self->tx_fifo.len) 
+	{
+		ali_ircc_dma_xmit(self);
+		
+		/* Not finished yet! */
+		ret = FALSE;
+	} 
+	else 
+	{	/* Reset Tx FIFO info */
+		self->tx_fifo.len = self->tx_fifo.ptr = self->tx_fifo.free = 0;
+		self->tx_fifo.tail = self->tx_buff.head;
+	}
+
+	/* Make sure we have room for more frames */
+	if (self->tx_fifo.free < MAX_TX_WINDOW) {
+		/* Not busy transmitting anymore */
+		/* Tell the network layer, that we can accept more frames */
+		netif_wake_queue(self->netdev);
+	}
+		
+	switch_bank(iobase, BANK0); 
+	
+	IRDA_DEBUG(1, "%s(), ----------------- End ------------------\n", __FUNCTION__ );	
+	return ret;
+}
+
+/*
+ * Function ali_ircc_dma_receive (self)
+ *
+ *    Get ready for receiving a frame. The device will initiate a DMA
+ *    if it starts to receive a frame.
+ *
+ */
+static int ali_ircc_dma_receive(struct ali_ircc_cb *self) 
+{
+	int iobase, tmp;
+	
+	IRDA_DEBUG(1, "%s(), ---------------- Start -----------------\n", __FUNCTION__ );	
+	
+	iobase = self->io.fir_base;
+	
+	/* Reset Tx FIFO info */
+	self->tx_fifo.len = self->tx_fifo.ptr = self->tx_fifo.free = 0;
+	self->tx_fifo.tail = self->tx_buff.head;
+		
+	/* Disable DMA */
+	switch_bank(iobase, BANK1);
+	outb(inb(iobase+FIR_CR) & ~CR_DMA_EN, iobase+FIR_CR);
+	
+	/* Reset Message Count */
+	switch_bank(iobase, BANK0);
+	outb(0x07, iobase+FIR_LSR);
+		
+	self->rcvFramesOverflow = FALSE;	
+	
+	self->LineStatus = inb(iobase+FIR_LSR) ;
+	
+	/* Reset Rx FIFO info */
+	self->io.direction = IO_RECV;
+	self->rx_buff.data = self->rx_buff.head;
+		
+	/* Reset Rx FIFO */
+	// switch_bank(iobase, BANK0);
+	outb(LCR_A_FIFO_RESET, iobase+FIR_LCR_A); 
+	
+	self->st_fifo.len = self->st_fifo.pending_bytes = 0;
+	self->st_fifo.tail = self->st_fifo.head = 0;
+		
+	irda_setup_dma(self->io.dma, self->rx_buff_dma, self->rx_buff.truesize,
+		       DMA_RX_MODE);
+	 
+	/* Set Receive Mode,Brick Wall */
+	//switch_bank(iobase, BANK0);
+	tmp = inb(iobase+FIR_LCR_B);
+	outb((unsigned char)(tmp &0x3f) | LCR_B_RX_MODE | LCR_B_BW , iobase + FIR_LCR_B); // 2000/12/1 05:16PM
+	IRDA_DEBUG(1, "%s(), *** Change To RX mode: FIR_LCR_B = 0x%x *** \n", __FUNCTION__ , inb(iobase+FIR_LCR_B));
+			
+	/* Set Rx Threshold */
+	switch_bank(iobase, BANK1);
+	outb(RX_FIFO_Threshold, iobase+FIR_FIFO_TR);
+	outb(RX_DMA_Threshold, iobase+FIR_DMA_TR);
+		
+	/* Enable DMA and Burst Mode */
+	// switch_bank(iobase, BANK1);
+	outb(CR_DMA_EN | CR_DMA_BURST, iobase+FIR_CR);
+				
+	switch_bank(iobase, BANK0); 
+	IRDA_DEBUG(1, "%s(), ----------------- End ------------------\n", __FUNCTION__ );	
+	return 0;
+}
+
+static int  ali_ircc_dma_receive_complete(struct ali_ircc_cb *self)
+{
+	struct st_fifo *st_fifo;
+	struct sk_buff *skb;
+	__u8 status, MessageCount;
+	int len, i, iobase, val;	
+
+	IRDA_DEBUG(1, "%s(), ---------------- Start -----------------\n", __FUNCTION__ );	
+
+	st_fifo = &self->st_fifo;		
+	iobase = self->io.fir_base;	
+		
+	switch_bank(iobase, BANK0);
+	MessageCount = inb(iobase+ FIR_LSR)&0x07;
+	
+	if (MessageCount > 0)	
+		IRDA_DEBUG(0, "%s(), Messsage count = %d,\n", __FUNCTION__ , MessageCount);	
+		
+	for (i=0; i<=MessageCount; i++)
+	{
+		/* Bank 0 */
+		switch_bank(iobase, BANK0);
+		status = inb(iobase+FIR_LSR);
+		
+		switch_bank(iobase, BANK2);
+		len = inb(iobase+FIR_RX_DSR_HI) & 0x0f;
+		len = len << 8; 
+		len |= inb(iobase+FIR_RX_DSR_LO);
+		
+		IRDA_DEBUG(1, "%s(), RX Length = 0x%.2x,\n", __FUNCTION__ , len);	
+		IRDA_DEBUG(1, "%s(), RX Status = 0x%.2x,\n", __FUNCTION__ , status);
+		
+		if (st_fifo->tail >= MAX_RX_WINDOW) {
+			IRDA_DEBUG(0, "%s(), window is full!\n", __FUNCTION__ );
+			continue;
+		}
+			
+		st_fifo->entries[st_fifo->tail].status = status;
+		st_fifo->entries[st_fifo->tail].len = len;
+		st_fifo->pending_bytes += len;
+		st_fifo->tail++;
+		st_fifo->len++;
+	}
+			
+	for (i=0; i<=MessageCount; i++)
+	{	
+		/* Get first entry */
+		status = st_fifo->entries[st_fifo->head].status;
+		len    = st_fifo->entries[st_fifo->head].len;
+		st_fifo->pending_bytes -= len;
+		st_fifo->head++;
+		st_fifo->len--;			
+		
+		/* Check for errors */
+		if ((status & 0xd8) || self->rcvFramesOverflow || (len==0)) 		
+		{
+			IRDA_DEBUG(0,"%s(), ************* RX Errors ************ \n", __FUNCTION__ );	
+			
+			/* Skip frame */
+			self->stats.rx_errors++;
+			
+			self->rx_buff.data += len;
+			
+			if (status & LSR_FIFO_UR) 
+			{
+				self->stats.rx_frame_errors++;
+				IRDA_DEBUG(0,"%s(), ************* FIFO Errors ************ \n", __FUNCTION__ );
+			}	
+			if (status & LSR_FRAME_ERROR)
+			{
+				self->stats.rx_frame_errors++;
+				IRDA_DEBUG(0,"%s(), ************* FRAME Errors ************ \n", __FUNCTION__ );
+			}
+							
+			if (status & LSR_CRC_ERROR) 
+			{
+				self->stats.rx_crc_errors++;
+				IRDA_DEBUG(0,"%s(), ************* CRC Errors ************ \n", __FUNCTION__ );
+			}
+			
+			if(self->rcvFramesOverflow)
+			{
+				self->stats.rx_frame_errors++;
+				IRDA_DEBUG(0,"%s(), ************* Overran DMA buffer ************ \n", __FUNCTION__ );								
+			}
+			if(len == 0)
+			{
+				self->stats.rx_frame_errors++;
+				IRDA_DEBUG(0,"%s(), ********** Receive Frame Size = 0 ********* \n", __FUNCTION__ );
+			}
+		}	 
+		else 
+		{
+			
+			if (st_fifo->pending_bytes < 32) 
+			{
+				switch_bank(iobase, BANK0);
+				val = inb(iobase+FIR_BSR);	
+				if ((val& BSR_FIFO_NOT_EMPTY)== 0x80) 
+				{
+					IRDA_DEBUG(0, "%s(), ************* BSR_FIFO_NOT_EMPTY ************ \n", __FUNCTION__ );
+					
+					/* Put this entry back in fifo */
+					st_fifo->head--;
+					st_fifo->len++;
+					st_fifo->pending_bytes += len;
+					st_fifo->entries[st_fifo->head].status = status;
+					st_fifo->entries[st_fifo->head].len = len;
+						
+					/*  
+		 			* DMA not finished yet, so try again 
+		 			* later, set timer value, resolution 
+		 			* 500 us 
+		 			*/
+					 
+					switch_bank(iobase, BANK1);
+					outb(TIMER_IIR_500, iobase+FIR_TIMER_IIR); // 2001/1/2 05:07PM
+					
+					/* Enable Timer */
+					outb(inb(iobase+FIR_CR) | CR_TIMER_EN, iobase+FIR_CR);
+						
+					return FALSE; /* I'll be back! */
+				}
+			}		
+			
+			/* 
+			 * Remember the time we received this frame, so we can
+			 * reduce the min turn time a bit since we will know
+			 * how much time we have used for protocol processing
+			 */
+			do_gettimeofday(&self->stamp);
+
+			skb = dev_alloc_skb(len+1);
+			if (skb == NULL)  
+			{
+				IRDA_WARNING("%s(), memory squeeze, "
+					     "dropping frame.\n",
+					     __FUNCTION__);
+				self->stats.rx_dropped++;
+
+				return FALSE;
+			}
+			
+			/* Make sure IP header gets aligned */
+			skb_reserve(skb, 1); 
+			
+			/* Copy frame without CRC, CRC is removed by hardware*/
+			skb_put(skb, len);
+			memcpy(skb->data, self->rx_buff.data, len);
+
+			/* Move to next frame */
+			self->rx_buff.data += len;
+			self->stats.rx_bytes += len;
+			self->stats.rx_packets++;
+
+			skb->dev = self->netdev;
+			skb->mac.raw  = skb->data;
+			skb->protocol = htons(ETH_P_IRDA);
+			netif_rx(skb);
+			self->netdev->last_rx = jiffies;
+		}
+	}
+	
+	switch_bank(iobase, BANK0);	
+		
+	IRDA_DEBUG(1, "%s(), ----------------- End ------------------\n", __FUNCTION__ );	
+	return TRUE;
+}
+
+
+
+/*
+ * Function ali_ircc_sir_hard_xmit (skb, dev)
+ *
+ *    Transmit the frame!
+ *
+ */
+static int ali_ircc_sir_hard_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct ali_ircc_cb *self;
+	unsigned long flags;
+	int iobase;
+	__u32 speed;
+	
+	IRDA_DEBUG(2, "%s(), ---------------- Start ----------------\n", __FUNCTION__ );
+	
+	IRDA_ASSERT(dev != NULL, return 0;);
+	
+	self = (struct ali_ircc_cb *) dev->priv;
+	IRDA_ASSERT(self != NULL, return 0;);
+
+	iobase = self->io.sir_base;
+
+	netif_stop_queue(dev);
+	
+	/* Make sure tests *& speed change are atomic */
+	spin_lock_irqsave(&self->lock, flags);
+
+	/* Note : you should make sure that speed changes are not going
+	 * to corrupt any outgoing frame. Look at nsc-ircc for the gory
+	 * details - Jean II */
+
+	/* Check if we need to change the speed */
+	speed = irda_get_next_speed(skb);
+	if ((speed != self->io.speed) && (speed != -1)) {
+		/* Check for empty frame */
+		if (!skb->len) {
+			ali_ircc_change_speed(self, speed); 
+			dev->trans_start = jiffies;
+			spin_unlock_irqrestore(&self->lock, flags);
+			dev_kfree_skb(skb);
+			return 0;
+		} else
+			self->new_speed = speed;
+	}
+
+	/* Init tx buffer */
+	self->tx_buff.data = self->tx_buff.head;
+
+        /* Copy skb to tx_buff while wrapping, stuffing and making CRC */
+	self->tx_buff.len = async_wrap_skb(skb, self->tx_buff.data, 
+					   self->tx_buff.truesize);
+	
+	self->stats.tx_bytes += self->tx_buff.len;
+
+	/* Turn on transmit finished interrupt. Will fire immediately!  */
+	outb(UART_IER_THRI, iobase+UART_IER); 
+
+	dev->trans_start = jiffies;
+	spin_unlock_irqrestore(&self->lock, flags);
+
+	dev_kfree_skb(skb);
+	
+	IRDA_DEBUG(2, "%s(), ----------------- End ------------------\n", __FUNCTION__ );	
+	
+	return 0;	
+}
+
+
+/*
+ * Function ali_ircc_net_ioctl (dev, rq, cmd)
+ *
+ *    Process IOCTL commands for this device
+ *
+ */
+static int ali_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+	struct if_irda_req *irq = (struct if_irda_req *) rq;
+	struct ali_ircc_cb *self;
+	unsigned long flags;
+	int ret = 0;
+	
+	IRDA_DEBUG(2, "%s(), ---------------- Start ----------------\n", __FUNCTION__ );
+	
+	IRDA_ASSERT(dev != NULL, return -1;);
+
+	self = dev->priv;
+
+	IRDA_ASSERT(self != NULL, return -1;);
+
+	IRDA_DEBUG(2, "%s(), %s, (cmd=0x%X)\n", __FUNCTION__ , dev->name, cmd);
+	
+	switch (cmd) {
+	case SIOCSBANDWIDTH: /* Set bandwidth */
+		IRDA_DEBUG(1, "%s(), SIOCSBANDWIDTH\n", __FUNCTION__ );
+		/*
+		 * This function will also be used by IrLAP to change the
+		 * speed, so we still must allow for speed change within
+		 * interrupt context.
+		 */
+		if (!in_interrupt() && !capable(CAP_NET_ADMIN))
+			return -EPERM;
+		
+		spin_lock_irqsave(&self->lock, flags);
+		ali_ircc_change_speed(self, irq->ifr_baudrate);		
+		spin_unlock_irqrestore(&self->lock, flags);
+		break;
+	case SIOCSMEDIABUSY: /* Set media busy */
+		IRDA_DEBUG(1, "%s(), SIOCSMEDIABUSY\n", __FUNCTION__ );
+		if (!capable(CAP_NET_ADMIN))
+			return -EPERM;
+		irda_device_set_media_busy(self->netdev, TRUE);
+		break;
+	case SIOCGRECEIVING: /* Check if we are receiving right now */
+		IRDA_DEBUG(2, "%s(), SIOCGRECEIVING\n", __FUNCTION__ );
+		/* This is protected */
+		irq->ifr_receiving = ali_ircc_is_receiving(self);
+		break;
+	default:
+		ret = -EOPNOTSUPP;
+	}
+	
+	IRDA_DEBUG(2, "%s(), ----------------- End ------------------\n", __FUNCTION__ );	
+	
+	return ret;
+}
+
+/*
+ * Function ali_ircc_is_receiving (self)
+ *
+ *    Return TRUE is we are currently receiving a frame
+ *
+ */
+static int ali_ircc_is_receiving(struct ali_ircc_cb *self)
+{
+	unsigned long flags;
+	int status = FALSE;
+	int iobase;		
+	
+	IRDA_DEBUG(2, "%s(), ---------------- Start -----------------\n", __FUNCTION__ );
+	
+	IRDA_ASSERT(self != NULL, return FALSE;);
+
+	spin_lock_irqsave(&self->lock, flags);
+
+	if (self->io.speed > 115200) 
+	{
+		iobase = self->io.fir_base;
+		
+		switch_bank(iobase, BANK1);
+		if((inb(iobase+FIR_FIFO_FR) & 0x3f) != 0) 		
+		{
+			/* We are receiving something */
+			IRDA_DEBUG(1, "%s(), We are receiving something\n", __FUNCTION__ );
+			status = TRUE;
+		}
+		switch_bank(iobase, BANK0);		
+	} 
+	else
+	{ 
+		status = (self->rx_buff.state != OUTSIDE_FRAME);
+	}
+	
+	spin_unlock_irqrestore(&self->lock, flags);
+	
+	IRDA_DEBUG(2, "%s(), ----------------- End ------------------\n", __FUNCTION__ );
+	
+	return status;
+}
+
+static struct net_device_stats *ali_ircc_net_get_stats(struct net_device *dev)
+{
+	struct ali_ircc_cb *self = (struct ali_ircc_cb *) dev->priv;
+	
+	IRDA_DEBUG(2, "%s(), ---------------- Start ----------------\n", __FUNCTION__ );
+		
+	IRDA_DEBUG(2, "%s(), ----------------- End ------------------\n", __FUNCTION__ );	
+	
+	return &self->stats;
+}
+
+static void ali_ircc_suspend(struct ali_ircc_cb *self)
+{
+	IRDA_DEBUG(2, "%s(), ---------------- Start ----------------\n", __FUNCTION__ );
+	
+	IRDA_MESSAGE("%s, Suspending\n", driver_name);
+
+	if (self->io.suspended)
+		return;
+
+	ali_ircc_net_close(self->netdev);
+
+	self->io.suspended = 1;
+	
+	IRDA_DEBUG(2, "%s(), ----------------- End ------------------\n", __FUNCTION__ );	
+}
+
+static void ali_ircc_wakeup(struct ali_ircc_cb *self)
+{
+	IRDA_DEBUG(2, "%s(), ---------------- Start ----------------\n", __FUNCTION__ );
+	
+	if (!self->io.suspended)
+		return;
+	
+	ali_ircc_net_open(self->netdev);
+	
+	IRDA_MESSAGE("%s, Waking up\n", driver_name);
+
+	self->io.suspended = 0;
+	
+	IRDA_DEBUG(2, "%s(), ----------------- End ------------------\n", __FUNCTION__ );	
+}
+
+static int ali_ircc_pmproc(struct pm_dev *dev, pm_request_t rqst, void *data)
+{
+        struct ali_ircc_cb *self = (struct ali_ircc_cb*) dev->data;
+        
+        IRDA_DEBUG(2, "%s(), ---------------- Start ----------------\n", __FUNCTION__ );
+	
+        if (self) {
+                switch (rqst) {
+                case PM_SUSPEND:
+                        ali_ircc_suspend(self);
+                        break;
+                case PM_RESUME:
+                        ali_ircc_wakeup(self);
+                        break;
+                }
+        }
+        
+        IRDA_DEBUG(2, "%s(), ----------------- End ------------------\n", __FUNCTION__ );	
+        
+	return 0;
+}
+
+
+/* ALi Chip Function */
+
+static void SetCOMInterrupts(struct ali_ircc_cb *self , unsigned char enable)
+{
+	
+	unsigned char newMask;
+	
+	int iobase = self->io.fir_base; /* or sir_base */
+
+	IRDA_DEBUG(2, "%s(), -------- Start -------- ( Enable = %d )\n", __FUNCTION__ , enable);	
+	
+	/* Enable the interrupt which we wish to */
+	if (enable){
+		if (self->io.direction == IO_XMIT)
+		{
+			if (self->io.speed > 115200) /* FIR, MIR */
+			{
+				newMask = self->ier;
+			}
+			else /* SIR */
+			{
+				newMask = UART_IER_THRI | UART_IER_RDI;
+			}
+		}
+		else {
+			if (self->io.speed > 115200) /* FIR, MIR */
+			{
+				newMask = self->ier;
+			}
+			else /* SIR */
+			{
+				newMask = UART_IER_RDI;
+			}
+		}
+	}
+	else /* Disable all the interrupts */
+	{
+		newMask = 0x00;
+
+	}
+
+	//SIR and FIR has different registers
+	if (self->io.speed > 115200)
+	{	
+		switch_bank(iobase, BANK0);
+		outb(newMask, iobase+FIR_IER);
+	}
+	else
+		outb(newMask, iobase+UART_IER);
+		
+	IRDA_DEBUG(2, "%s(), ----------------- End ------------------\n", __FUNCTION__ );	
+}
+
+static void SIR2FIR(int iobase)
+{
+	//unsigned char tmp;
+		
+	IRDA_DEBUG(1, "%s(), ---------------- Start ----------------\n", __FUNCTION__ );
+	
+	/* Already protected (change_speed() or setup()), no need to lock.
+	 * Jean II */
+	
+	outb(0x28, iobase+UART_MCR);
+	outb(0x68, iobase+UART_MCR);
+	outb(0x88, iobase+UART_MCR);		
+	
+	outb(0x60, iobase+FIR_MCR); 	/*  Master Reset */
+	outb(0x20, iobase+FIR_MCR); 	/*  Master Interrupt Enable */
+	
+	//tmp = inb(iobase+FIR_LCR_B);	/* SIP enable */
+	//tmp |= 0x20;
+	//outb(tmp, iobase+FIR_LCR_B);	
+	
+	IRDA_DEBUG(1, "%s(), ----------------- End ------------------\n", __FUNCTION__ );	
+}
+
+static void FIR2SIR(int iobase)
+{
+	unsigned char val;
+	
+	IRDA_DEBUG(1, "%s(), ---------------- Start ----------------\n", __FUNCTION__ );
+	
+	/* Already protected (change_speed() or setup()), no need to lock.
+	 * Jean II */
+	
+	outb(0x20, iobase+FIR_MCR); 	/* IRQ to low */
+	outb(0x00, iobase+UART_IER); 	
+		
+	outb(0xA0, iobase+FIR_MCR); 	/* Don't set master reset */
+	outb(0x00, iobase+UART_FCR);
+	outb(0x07, iobase+UART_FCR);		
+	
+	val = inb(iobase+UART_RX);
+	val = inb(iobase+UART_LSR);
+	val = inb(iobase+UART_MSR);
+	
+	IRDA_DEBUG(1, "%s(), ----------------- End ------------------\n", __FUNCTION__ );
+}
+
+MODULE_AUTHOR("Benjamin Kong <benjamin_kong@ali.com.tw>");
+MODULE_DESCRIPTION("ALi FIR Controller Driver");
+MODULE_LICENSE("GPL");
+
+
+module_param_array(io, int, NULL, 0);
+MODULE_PARM_DESC(io, "Base I/O addresses");
+module_param_array(irq, int, NULL, 0);
+MODULE_PARM_DESC(irq, "IRQ lines");
+module_param_array(dma, int, NULL, 0);
+MODULE_PARM_DESC(dma, "DMA channels");
+
+module_init(ali_ircc_init);
+module_exit(ali_ircc_cleanup);
diff --git a/drivers/net/irda/ali-ircc.h b/drivers/net/irda/ali-ircc.h
new file mode 100644
index 0000000..e489c66
--- /dev/null
+++ b/drivers/net/irda/ali-ircc.h
@@ -0,0 +1,231 @@
+/*********************************************************************
+ *                
+ * Filename:      ali-ircc.h
+ * Version:       0.5
+ * Description:   Driver for the ALI M1535D and M1543C FIR Controller
+ * Status:        Experimental.
+ * Author:        Benjamin Kong <benjamin_kong@ali.com.tw>
+ * Created at:    2000/10/16 03:46PM
+ * Modified at:   2001/1/3 02:56PM
+ * Modified by:   Benjamin Kong <benjamin_kong@ali.com.tw>
+ * 
+ *     Copyright (c) 2000 Benjamin Kong <benjamin_kong@ali.com.tw>
+ *     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.
+ *  
+ ********************************************************************/
+
+#ifndef ALI_IRCC_H
+#define ALI_IRCC_H
+
+#include <linux/time.h>
+
+#include <linux/spinlock.h>
+#include <linux/pm.h>
+#include <linux/types.h>
+#include <asm/io.h>
+
+/* SIR Register */
+/* Usr definition of linux/serial_reg.h */
+
+/* FIR Register */
+#define BANK0		0x20
+#define BANK1		0x21
+#define BANK2		0x22
+#define BANK3		0x23
+
+#define FIR_MCR		0x07	/* Master Control Register */
+
+/* Bank 0 */
+#define FIR_DR		0x00	/* Alias 0, FIR Data Register (R/W) */ 
+#define FIR_IER		0x01	/* Alias 1, FIR Interrupt Enable Register (R/W) */
+#define FIR_IIR		0x02	/* Alias 2, FIR Interrupt Identification Register (Read only) */
+#define FIR_LCR_A	0x03	/* Alias 3, FIR Line Control Register A (R/W) */
+#define FIR_LCR_B	0x04	/* Alias 4, FIR Line Control Register B (R/W) */
+#define FIR_LSR		0x05	/* Alias 5, FIR Line Status Register (R/W) */
+#define FIR_BSR		0x06	/* Alias 6, FIR Bus Status Register (Read only) */
+
+
+	/* Alias 1 */
+	#define	IER_FIFO	0x10	/* FIR FIFO Interrupt Enable */	
+	#define	IER_TIMER	0x20 	/* Timer Interrupt Enable */ 
+	#define	IER_EOM		0x40	/* End of Message Interrupt Enable */
+	#define IER_ACT		0x80	/* Active Frame Interrupt Enable */
+	
+	/* Alias 2 */
+	#define IIR_FIFO	0x10	/* FIR FIFO Interrupt */
+	#define IIR_TIMER	0x20	/* Timer Interrupt */
+	#define IIR_EOM		0x40	/* End of Message Interrupt */
+	#define IIR_ACT		0x80	/* Active Frame Interrupt */	
+	
+	/* Alias 3 */
+	#define LCR_A_FIFO_RESET 0x80	/* FIFO Reset */
+
+	/* Alias 4 */
+	#define	LCR_B_BW	0x10	/* Brick Wall */
+	#define LCR_B_SIP	0x20	/* SIP Enable */
+	#define	LCR_B_TX_MODE 	0x40	/* Transmit Mode */
+	#define LCR_B_RX_MODE	0x80	/* Receive Mode */
+	
+	/* Alias 5 */	
+	#define LSR_FIR_LSA	0x00	/* FIR Line Status Address */
+	#define LSR_FRAME_ABORT	0x08	/* Frame Abort */
+	#define LSR_CRC_ERROR	0x10	/* CRC Error */
+	#define LSR_SIZE_ERROR	0x20	/* Size Error */
+	#define LSR_FRAME_ERROR	0x40	/* Frame Error */
+	#define LSR_FIFO_UR	0x80	/* FIFO Underrun */
+	#define LSR_FIFO_OR	0x80	/* FIFO Overrun */
+		
+	/* Alias 6 */
+	#define BSR_FIFO_NOT_EMPTY	0x80	/* FIFO Not Empty */
+	
+/* Bank 1 */
+#define	FIR_CR		0x00 	/* Alias 0, FIR Configuration Register (R/W) */
+#define FIR_FIFO_TR	0x01   	/* Alias 1, FIR FIFO Threshold Register (R/W) */
+#define FIR_DMA_TR	0x02	/* Alias 2, FIR DMA Threshold Register (R/W) */
+#define FIR_TIMER_IIR	0x03	/* Alias 3, FIR Timer interrupt interval register (W/O) */
+#define FIR_FIFO_FR	0x03	/* Alias 3, FIR FIFO Flag register (R/O) */
+#define FIR_FIFO_RAR	0x04 	/* Alias 4, FIR FIFO Read Address register (R/O) */
+#define FIR_FIFO_WAR	0x05	/* Alias 5, FIR FIFO Write Address register (R/O) */
+#define FIR_TR		0x06	/* Alias 6, Test REgister (W/O) */
+
+	/* Alias 0 */
+	#define CR_DMA_EN	0x01	/* DMA Enable */
+	#define CR_DMA_BURST	0x02	/* DMA Burst Mode */
+	#define CR_TIMER_EN 	0x08	/* Timer Enable */
+	
+	/* Alias 3 */
+	#define TIMER_IIR_500	0x00	/* 500 us */
+	#define TIMER_IIR_1ms	0x01	/* 1   ms */
+	#define TIMER_IIR_2ms	0x02	/* 2   ms */
+	#define TIMER_IIR_4ms	0x03	/* 4   ms */
+	
+/* Bank 2 */
+#define FIR_IRDA_CR	0x00	/* Alias 0, IrDA Control Register (R/W) */
+#define FIR_BOF_CR	0x01	/* Alias 1, BOF Count Register (R/W) */
+#define FIR_BW_CR	0x02	/* Alias 2, Brick Wall Count Register (R/W) */
+#define FIR_TX_DSR_HI	0x03	/* Alias 3, TX Data Size Register (high) (R/W) */
+#define FIR_TX_DSR_LO	0x04	/* Alias 4, TX Data Size Register (low) (R/W) */
+#define FIR_RX_DSR_HI	0x05	/* Alias 5, RX Data Size Register (high) (R/W) */
+#define FIR_RX_DSR_LO	0x06	/* Alias 6, RX Data Size Register (low) (R/W) */
+	
+	/* Alias 0 */
+	#define IRDA_CR_HDLC1152 0x80	/* 1.152Mbps HDLC Select */
+	#define IRDA_CR_CRC	0X40	/* CRC Select. */
+	#define IRDA_CR_HDLC	0x20	/* HDLC select. */
+	#define IRDA_CR_HP_MODE 0x10	/* HP mode (read only) */
+	#define IRDA_CR_SD_ST	0x08	/* SD/MODE State.  */
+	#define IRDA_CR_FIR_SIN 0x04	/* FIR SIN Select. */
+	#define IRDA_CR_ITTX_0	0x02	/* SOUT State. IRTX force to 0 */
+	#define IRDA_CR_ITTX_1	0x03	/* SOUT State. IRTX force to 1 */
+	
+/* Bank 3 */
+#define FIR_ID_VR	0x00	/* Alias 0, FIR ID Version Register (R/O) */
+#define FIR_MODULE_CR	0x01	/* Alias 1, FIR Module Control Register (R/W) */
+#define FIR_IO_BASE_HI	0x02	/* Alias 2, FIR Higher I/O Base Address Register (R/O) */
+#define FIR_IO_BASE_LO	0x03	/* Alias 3, FIR Lower I/O Base Address Register (R/O) */
+#define FIR_IRQ_CR	0x04	/* Alias 4, FIR IRQ Channel Register (R/O) */
+#define FIR_DMA_CR	0x05	/* Alias 5, FIR DMA Channel Register (R/O) */
+
+struct ali_chip {
+	char *name;
+	int cfg[2];
+	unsigned char entr1;
+	unsigned char entr2;
+	unsigned char cid_index;
+	unsigned char cid_value;
+	int (*probe)(struct ali_chip *chip, chipio_t *info);
+	int (*init)(struct ali_chip *chip, chipio_t *info); 
+};
+typedef struct ali_chip ali_chip_t;
+
+
+/* DMA modes needed */
+#define DMA_TX_MODE     0x08    /* Mem to I/O, ++, demand. */
+#define DMA_RX_MODE     0x04    /* I/O to mem, ++, demand. */
+
+#define MAX_TX_WINDOW 	7
+#define MAX_RX_WINDOW 	7
+
+#define TX_FIFO_Threshold	8
+#define RX_FIFO_Threshold	1
+#define TX_DMA_Threshold	1
+#define RX_DMA_Threshold	1
+
+/* For storing entries in the status FIFO */
+
+struct st_fifo_entry {
+	int status;
+	int len;
+};
+
+struct st_fifo {
+	struct st_fifo_entry entries[MAX_RX_WINDOW];
+	int pending_bytes;
+	int head;
+	int tail;
+	int len;
+};
+
+struct frame_cb {
+	void *start; /* Start of frame in DMA mem */
+	int len;     /* Lenght of frame in DMA mem */
+};
+
+struct tx_fifo {
+	struct frame_cb queue[MAX_TX_WINDOW]; /* Info about frames in queue */
+	int             ptr;                  /* Currently being sent */
+	int             len;                  /* Lenght of queue */
+	int             free;                 /* Next free slot */
+	void           *tail;                 /* Next free start in DMA mem */
+};
+
+/* Private data for each instance */
+struct ali_ircc_cb {
+
+	struct st_fifo st_fifo;    /* Info about received frames */
+	struct tx_fifo tx_fifo;    /* Info about frames to be transmitted */
+
+	struct net_device *netdev;     /* Yes! we are some kind of netdevice */
+	struct net_device_stats stats;
+	
+	struct irlap_cb *irlap;    /* The link layer we are binded to */
+	struct qos_info qos;       /* QoS capabilities for this device */
+	
+	chipio_t io;               /* IrDA controller information */
+	iobuff_t tx_buff;          /* Transmit buffer */
+	iobuff_t rx_buff;          /* Receive buffer */
+	dma_addr_t tx_buff_dma;
+	dma_addr_t rx_buff_dma;
+
+	__u8 ier;                  /* Interrupt enable register */
+	
+	__u8 InterruptID;	   /* Interrupt ID */	
+	__u8 BusStatus;		   /* Bus Status */	
+	__u8 LineStatus;	   /* Line Status */	
+	
+	unsigned char rcvFramesOverflow;
+		
+	struct timeval stamp;
+	struct timeval now;
+
+	spinlock_t lock;           /* For serializing operations */
+	
+	__u32 new_speed;
+	int index;                 /* Instance index */
+	
+	unsigned char fifo_opti_buf;
+
+        struct pm_dev *dev;
+};
+
+static inline void switch_bank(int iobase, int bank)
+{
+		outb(bank, iobase+FIR_MCR);
+}
+
+#endif /* ALI_IRCC_H */
diff --git a/drivers/net/irda/au1000_ircc.h b/drivers/net/irda/au1000_ircc.h
new file mode 100644
index 0000000..7a31d465
--- /dev/null
+++ b/drivers/net/irda/au1000_ircc.h
@@ -0,0 +1,127 @@
+/*
+ *
+ * BRIEF MODULE DESCRIPTION
+ *	Au1000 IrDA driver.
+ *
+ * Copyright 2001 MontaVista Software Inc.
+ * Author: MontaVista Software, Inc.
+ *         	ppopov@mvista.com or source@mvista.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  SOFTWARE  IS PROVIDED   ``AS  IS'' AND   ANY  EXPRESS OR IMPLIED
+ *  WARRANTIES,   INCLUDING, BUT NOT  LIMITED  TO, THE IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
+ *  NO  EVENT  SHALL   THE AUTHOR  BE    LIABLE FOR ANY   DIRECT, INDIRECT,
+ *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ *  NOT LIMITED   TO, PROCUREMENT OF  SUBSTITUTE GOODS  OR SERVICES; LOSS OF
+ *  USE, DATA,  OR PROFITS; OR  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ *  ANY THEORY OF LIABILITY, WHETHER IN  CONTRACT, STRICT LIABILITY, OR TORT
+ *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ *  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  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.
+ */
+
+#ifndef AU1000_IRCC_H
+#define AU1000_IRCC_H
+
+#include <linux/time.h>
+
+#include <linux/spinlock.h>
+#include <linux/pm.h>
+#include <asm/io.h>
+
+#define NUM_IR_IFF          1
+#define NUM_IR_DESC        64
+#define RING_SIZE_4       0x0
+#define RING_SIZE_16      0x3
+#define RING_SIZE_64      0xF
+#define MAX_NUM_IR_DESC    64
+#define MAX_BUF_SIZE     2048
+
+#define BPS_115200          0
+#define BPS_57600           1
+#define BPS_38400           2
+#define BPS_19200           5
+#define BPS_9600           11
+#define BPS_2400           47
+
+/* Ring descriptor flags */
+#define AU_OWN           (1<<7) /* tx,rx */
+
+#define IR_DIS_CRC       (1<<6) /* tx */
+#define IR_BAD_CRC       (1<<5) /* tx */
+#define IR_NEED_PULSE    (1<<4) /* tx */
+#define IR_FORCE_UNDER   (1<<3) /* tx */
+#define IR_DISABLE_TX    (1<<2) /* tx */
+#define IR_HW_UNDER      (1<<0) /* tx */
+#define IR_TX_ERROR      (IR_DIS_CRC|IR_BAD_CRC|IR_HW_UNDER)
+
+#define IR_PHY_ERROR     (1<<6) /* rx */
+#define IR_CRC_ERROR     (1<<5) /* rx */
+#define IR_MAX_LEN       (1<<4) /* rx */
+#define IR_FIFO_OVER     (1<<3) /* rx */
+#define IR_SIR_ERROR     (1<<2) /* rx */
+#define IR_RX_ERROR      (IR_PHY_ERROR|IR_CRC_ERROR| \
+		IR_MAX_LEN|IR_FIFO_OVER|IR_SIR_ERROR)
+
+typedef struct db_dest {
+	struct db_dest *pnext;
+	volatile u32 *vaddr;
+	dma_addr_t dma_addr;
+} db_dest_t;
+
+
+typedef struct ring_desc {
+	u8 count_0;               /* 7:0  */
+	u8 count_1;               /* 12:8 */
+	u8 reserved;
+	u8 flags;
+	u8 addr_0;                /* 7:0   */
+	u8 addr_1;                /* 15:8  */
+	u8 addr_2;                /* 23:16 */
+	u8 addr_3;                /* 31:24 */
+} ring_dest_t;
+
+
+/* Private data for each instance */
+struct au1k_private {
+
+	db_dest_t *pDBfree;
+	db_dest_t db[2*NUM_IR_DESC];
+	volatile ring_dest_t *rx_ring[NUM_IR_DESC];
+	volatile ring_dest_t *tx_ring[NUM_IR_DESC];
+	db_dest_t *rx_db_inuse[NUM_IR_DESC];
+	db_dest_t *tx_db_inuse[NUM_IR_DESC];
+	u32 rx_head;
+	u32 tx_head;
+	u32 tx_tail;
+	u32 tx_full;
+
+	iobuff_t rx_buff;
+
+	struct net_device *netdev;
+	struct net_device_stats stats;
+	
+	struct timeval stamp;
+	struct timeval now;
+	struct qos_info		qos;
+	struct irlap_cb		*irlap;
+	
+	u8 open;
+	u32 speed;
+	u32 newspeed;
+	
+	u32 intr_work_done; /* number of Rx and Tx pkts processed in the isr */
+	struct timer_list timer;
+
+	spinlock_t lock;           /* For serializing operations */
+        struct pm_dev *dev;
+};
+#endif /* AU1000_IRCC_H */
diff --git a/drivers/net/irda/au1k_ir.c b/drivers/net/irda/au1k_ir.c
new file mode 100644
index 0000000..e6b1985
--- /dev/null
+++ b/drivers/net/irda/au1k_ir.c
@@ -0,0 +1,851 @@
+/*
+ * Alchemy Semi Au1000 IrDA driver
+ *
+ * Copyright 2001 MontaVista Software Inc.
+ * Author: MontaVista Software, Inc.
+ *         	ppopov@mvista.com or source@mvista.com
+ *
+ *  This program is free software; you can distribute it and/or modify it
+ *  under the terms of the GNU General Public License (Version 2) as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope 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/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/slab.h>
+#include <linux/rtnetlink.h>
+#include <linux/interrupt.h>
+#include <linux/pm.h>
+#include <linux/bitops.h>
+
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <asm/au1000.h>
+#if defined(CONFIG_MIPS_PB1000) || defined(CONFIG_MIPS_PB1100)
+#include <asm/pb1000.h>
+#elif defined(CONFIG_MIPS_DB1000) || defined(CONFIG_MIPS_DB1100)
+#include <asm/db1x00.h>
+#else 
+#error au1k_ir: unsupported board
+#endif
+
+#include <net/irda/irda.h>
+#include <net/irda/irmod.h>
+#include <net/irda/wrapper.h>
+#include <net/irda/irda_device.h>
+#include "au1000_ircc.h"
+
+static int au1k_irda_net_init(struct net_device *);
+static int au1k_irda_start(struct net_device *);
+static int au1k_irda_stop(struct net_device *dev);
+static int au1k_irda_hard_xmit(struct sk_buff *, struct net_device *);
+static int au1k_irda_rx(struct net_device *);
+static void au1k_irda_interrupt(int, void *, struct pt_regs *);
+static void au1k_tx_timeout(struct net_device *);
+static struct net_device_stats *au1k_irda_stats(struct net_device *);
+static int au1k_irda_ioctl(struct net_device *, struct ifreq *, int);
+static int au1k_irda_set_speed(struct net_device *dev, int speed);
+
+static void *dma_alloc(size_t, dma_addr_t *);
+static void dma_free(void *, size_t);
+
+static int qos_mtt_bits = 0x07;  /* 1 ms or more */
+static struct net_device *ir_devs[NUM_IR_IFF];
+static char version[] __devinitdata =
+    "au1k_ircc:1.2 ppopov@mvista.com\n";
+
+#define RUN_AT(x) (jiffies + (x))
+
+#if defined(CONFIG_MIPS_DB1000) || defined(CONFIG_MIPS_DB1100)
+static BCSR * const bcsr = (BCSR *)0xAE000000;
+#endif
+
+static DEFINE_SPINLOCK(ir_lock);
+
+/*
+ * IrDA peripheral bug. You have to read the register
+ * twice to get the right value.
+ */
+u32 read_ir_reg(u32 addr) 
+{ 
+	readl(addr);
+	return readl(addr);
+}
+
+
+/*
+ * Buffer allocation/deallocation routines. The buffer descriptor returned
+ * has the virtual and dma address of a buffer suitable for 
+ * both, receive and transmit operations.
+ */
+static db_dest_t *GetFreeDB(struct au1k_private *aup)
+{
+	db_dest_t *pDB;
+	pDB = aup->pDBfree;
+
+	if (pDB) {
+		aup->pDBfree = pDB->pnext;
+	}
+	return pDB;
+}
+
+static void ReleaseDB(struct au1k_private *aup, db_dest_t *pDB)
+{
+	db_dest_t *pDBfree = aup->pDBfree;
+	if (pDBfree)
+		pDBfree->pnext = pDB;
+	aup->pDBfree = pDB;
+}
+
+
+/*
+  DMA memory allocation, derived from pci_alloc_consistent.
+  However, the Au1000 data cache is coherent (when programmed
+  so), therefore we return KSEG0 address, not KSEG1.
+*/
+static void *dma_alloc(size_t size, dma_addr_t * dma_handle)
+{
+	void *ret;
+	int gfp = GFP_ATOMIC | GFP_DMA;
+
+	ret = (void *) __get_free_pages(gfp, get_order(size));
+
+	if (ret != NULL) {
+		memset(ret, 0, size);
+		*dma_handle = virt_to_bus(ret);
+		ret = (void *)KSEG0ADDR(ret);
+	}
+	return ret;
+}
+
+
+static void dma_free(void *vaddr, size_t size)
+{
+	vaddr = (void *)KSEG0ADDR(vaddr);
+	free_pages((unsigned long) vaddr, get_order(size));
+}
+
+
+static void 
+setup_hw_rings(struct au1k_private *aup, u32 rx_base, u32 tx_base)
+{
+	int i;
+	for (i=0; i<NUM_IR_DESC; i++) {
+		aup->rx_ring[i] = (volatile ring_dest_t *) 
+			(rx_base + sizeof(ring_dest_t)*i);
+	}
+	for (i=0; i<NUM_IR_DESC; i++) {
+		aup->tx_ring[i] = (volatile ring_dest_t *) 
+			(tx_base + sizeof(ring_dest_t)*i);
+	}
+}
+
+static int au1k_irda_init(void)
+{
+	static unsigned version_printed = 0;
+	struct au1k_private *aup;
+	struct net_device *dev;
+	int err;
+
+	if (version_printed++ == 0) printk(version);
+
+	dev = alloc_irdadev(sizeof(struct au1k_private));
+	if (!dev)
+		return -ENOMEM;
+
+	dev->irq = AU1000_IRDA_RX_INT; /* TX has its own interrupt */
+	err = au1k_irda_net_init(dev);
+	if (err)
+		goto out;
+	err = register_netdev(dev);
+	if (err)
+		goto out1;
+	ir_devs[0] = dev;
+	printk(KERN_INFO "IrDA: Registered device %s\n", dev->name);
+	return 0;
+
+out1:
+	aup = netdev_priv(dev);
+	dma_free((void *)aup->db[0].vaddr,
+		MAX_BUF_SIZE * 2*NUM_IR_DESC);
+	dma_free((void *)aup->rx_ring[0],
+		2 * MAX_NUM_IR_DESC*(sizeof(ring_dest_t)));
+	kfree(aup->rx_buff.head);
+out:
+	free_netdev(dev);
+	return err;
+}
+
+static int au1k_irda_init_iobuf(iobuff_t *io, int size)
+{
+	io->head = kmalloc(size, GFP_KERNEL);
+	if (io->head != NULL) {
+		io->truesize = size;
+		io->in_frame = FALSE;
+		io->state    = OUTSIDE_FRAME;
+		io->data     = io->head;
+	}
+	return io->head ? 0 : -ENOMEM;
+}
+
+static int au1k_irda_net_init(struct net_device *dev)
+{
+	struct au1k_private *aup = netdev_priv(dev);
+	int i, retval = 0, err;
+	db_dest_t *pDB, *pDBfree;
+	dma_addr_t temp;
+
+	err = au1k_irda_init_iobuf(&aup->rx_buff, 14384);
+	if (err)
+		goto out1;
+
+	dev->open = au1k_irda_start;
+	dev->hard_start_xmit = au1k_irda_hard_xmit;
+	dev->stop = au1k_irda_stop;
+	dev->get_stats = au1k_irda_stats;
+	dev->do_ioctl = au1k_irda_ioctl;
+	dev->tx_timeout = au1k_tx_timeout;
+
+	irda_init_max_qos_capabilies(&aup->qos);
+
+	/* The only value we must override it the baudrate */
+	aup->qos.baud_rate.bits = IR_9600|IR_19200|IR_38400|IR_57600|
+		IR_115200|IR_576000 |(IR_4000000 << 8);
+	
+	aup->qos.min_turn_time.bits = qos_mtt_bits;
+	irda_qos_bits_to_value(&aup->qos);
+
+	retval = -ENOMEM;
+
+	/* Tx ring follows rx ring + 512 bytes */
+	/* we need a 1k aligned buffer */
+	aup->rx_ring[0] = (ring_dest_t *)
+		dma_alloc(2*MAX_NUM_IR_DESC*(sizeof(ring_dest_t)), &temp);
+	if (!aup->rx_ring[0])
+		goto out2;
+
+	/* allocate the data buffers */
+	aup->db[0].vaddr = 
+		(void *)dma_alloc(MAX_BUF_SIZE * 2*NUM_IR_DESC, &temp);
+	if (!aup->db[0].vaddr)
+		goto out3;
+
+	setup_hw_rings(aup, (u32)aup->rx_ring[0], (u32)aup->rx_ring[0] + 512);
+
+	pDBfree = NULL;
+	pDB = aup->db;
+	for (i=0; i<(2*NUM_IR_DESC); i++) {
+		pDB->pnext = pDBfree;
+		pDBfree = pDB;
+		pDB->vaddr = 
+			(u32 *)((unsigned)aup->db[0].vaddr + MAX_BUF_SIZE*i);
+		pDB->dma_addr = (dma_addr_t)virt_to_bus(pDB->vaddr);
+		pDB++;
+	}
+	aup->pDBfree = pDBfree;
+
+	/* attach a data buffer to each descriptor */
+	for (i=0; i<NUM_IR_DESC; i++) {
+		pDB = GetFreeDB(aup);
+		if (!pDB) goto out;
+		aup->rx_ring[i]->addr_0 = (u8)(pDB->dma_addr & 0xff);
+		aup->rx_ring[i]->addr_1 = (u8)((pDB->dma_addr>>8) & 0xff);
+		aup->rx_ring[i]->addr_2 = (u8)((pDB->dma_addr>>16) & 0xff);
+		aup->rx_ring[i]->addr_3 = (u8)((pDB->dma_addr>>24) & 0xff);
+		aup->rx_db_inuse[i] = pDB;
+	}
+	for (i=0; i<NUM_IR_DESC; i++) {
+		pDB = GetFreeDB(aup);
+		if (!pDB) goto out;
+		aup->tx_ring[i]->addr_0 = (u8)(pDB->dma_addr & 0xff);
+		aup->tx_ring[i]->addr_1 = (u8)((pDB->dma_addr>>8) & 0xff);
+		aup->tx_ring[i]->addr_2 = (u8)((pDB->dma_addr>>16) & 0xff);
+		aup->tx_ring[i]->addr_3 = (u8)((pDB->dma_addr>>24) & 0xff);
+		aup->tx_ring[i]->count_0 = 0;
+		aup->tx_ring[i]->count_1 = 0;
+		aup->tx_ring[i]->flags = 0;
+		aup->tx_db_inuse[i] = pDB;
+	}
+
+#if defined(CONFIG_MIPS_DB1000) || defined(CONFIG_MIPS_DB1100)
+	/* power on */
+	bcsr->resets &= ~BCSR_RESETS_IRDA_MODE_MASK;
+	bcsr->resets |= BCSR_RESETS_IRDA_MODE_FULL;
+	au_sync();
+#endif
+
+	return 0;
+
+out3:
+	dma_free((void *)aup->rx_ring[0],
+		2 * MAX_NUM_IR_DESC*(sizeof(ring_dest_t)));
+out2:
+	kfree(aup->rx_buff.head);
+out1:
+	printk(KERN_ERR "au1k_init_module failed.  Returns %d\n", retval);
+	return retval;
+}
+
+
+static int au1k_init(struct net_device *dev)
+{
+	struct au1k_private *aup = netdev_priv(dev);
+	int i;
+	u32 control;
+	u32 ring_address;
+
+	/* bring the device out of reset */
+	control = 0xe; /* coherent, clock enable, one half system clock */
+			  
+#ifndef CONFIG_CPU_LITTLE_ENDIAN
+	control |= 1;
+#endif
+	aup->tx_head = 0;
+	aup->tx_tail = 0;
+	aup->rx_head = 0;
+
+	for (i=0; i<NUM_IR_DESC; i++) {
+		aup->rx_ring[i]->flags = AU_OWN;
+	}
+
+	writel(control, IR_INTERFACE_CONFIG);
+	au_sync_delay(10);
+
+	writel(read_ir_reg(IR_ENABLE) & ~0x8000, IR_ENABLE); /* disable PHY */
+	au_sync_delay(1);
+
+	writel(MAX_BUF_SIZE, IR_MAX_PKT_LEN);
+
+	ring_address = (u32)virt_to_phys((void *)aup->rx_ring[0]);
+	writel(ring_address >> 26, IR_RING_BASE_ADDR_H);
+	writel((ring_address >> 10) & 0xffff, IR_RING_BASE_ADDR_L);
+
+	writel(RING_SIZE_64<<8 | RING_SIZE_64<<12, IR_RING_SIZE);
+
+	writel(1<<2 | IR_ONE_PIN, IR_CONFIG_2); /* 48MHz */
+	writel(0, IR_RING_ADDR_CMPR);
+
+	au1k_irda_set_speed(dev, 9600);
+	return 0;
+}
+
+static int au1k_irda_start(struct net_device *dev)
+{
+	int retval;
+	char hwname[32];
+	struct au1k_private *aup = netdev_priv(dev);
+
+	if ((retval = au1k_init(dev))) {
+		printk(KERN_ERR "%s: error in au1k_init\n", dev->name);
+		return retval;
+	}
+
+	if ((retval = request_irq(AU1000_IRDA_TX_INT, &au1k_irda_interrupt, 
+					0, dev->name, dev))) {
+		printk(KERN_ERR "%s: unable to get IRQ %d\n", 
+				dev->name, dev->irq);
+		return retval;
+	}
+	if ((retval = request_irq(AU1000_IRDA_RX_INT, &au1k_irda_interrupt, 
+					0, dev->name, dev))) {
+		free_irq(AU1000_IRDA_TX_INT, dev);
+		printk(KERN_ERR "%s: unable to get IRQ %d\n", 
+				dev->name, dev->irq);
+		return retval;
+	}
+
+	/* Give self a hardware name */
+	sprintf(hwname, "Au1000 SIR/FIR");
+	aup->irlap = irlap_open(dev, &aup->qos, hwname);
+	netif_start_queue(dev);
+
+	writel(read_ir_reg(IR_CONFIG_2) | 1<<8, IR_CONFIG_2); /* int enable */
+
+	aup->timer.expires = RUN_AT((3*HZ)); 
+	aup->timer.data = (unsigned long)dev;
+	return 0;
+}
+
+static int au1k_irda_stop(struct net_device *dev)
+{
+	struct au1k_private *aup = netdev_priv(dev);
+
+	/* disable interrupts */
+	writel(read_ir_reg(IR_CONFIG_2) & ~(1<<8), IR_CONFIG_2);
+	writel(0, IR_CONFIG_1); 
+	writel(0, IR_INTERFACE_CONFIG); /* disable clock */
+	au_sync();
+
+	if (aup->irlap) {
+		irlap_close(aup->irlap);
+		aup->irlap = NULL;
+	}
+
+	netif_stop_queue(dev);
+	del_timer(&aup->timer);
+
+	/* disable the interrupt */
+	free_irq(AU1000_IRDA_TX_INT, dev);
+	free_irq(AU1000_IRDA_RX_INT, dev);
+	return 0;
+}
+
+static void __exit au1k_irda_exit(void)
+{
+	struct net_device *dev = ir_devs[0];
+	struct au1k_private *aup = netdev_priv(dev);
+
+	unregister_netdev(dev);
+
+	dma_free((void *)aup->db[0].vaddr,
+		MAX_BUF_SIZE * 2*NUM_IR_DESC);
+	dma_free((void *)aup->rx_ring[0],
+		2 * MAX_NUM_IR_DESC*(sizeof(ring_dest_t)));
+	kfree(aup->rx_buff.head);
+	free_netdev(dev);
+}
+
+
+static inline void 
+update_tx_stats(struct net_device *dev, u32 status, u32 pkt_len)
+{
+	struct au1k_private *aup = netdev_priv(dev);
+	struct net_device_stats *ps = &aup->stats;
+
+	ps->tx_packets++;
+	ps->tx_bytes += pkt_len;
+
+	if (status & IR_TX_ERROR) {
+		ps->tx_errors++;
+		ps->tx_aborted_errors++;
+	}
+}
+
+
+static void au1k_tx_ack(struct net_device *dev)
+{
+	struct au1k_private *aup = netdev_priv(dev);
+	volatile ring_dest_t *ptxd;
+
+	ptxd = aup->tx_ring[aup->tx_tail];
+	while (!(ptxd->flags & AU_OWN) && (aup->tx_tail != aup->tx_head)) {
+		update_tx_stats(dev, ptxd->flags, 
+				ptxd->count_1<<8 | ptxd->count_0);
+		ptxd->count_0 = 0;
+		ptxd->count_1 = 0;
+		au_sync();
+
+		aup->tx_tail = (aup->tx_tail + 1) & (NUM_IR_DESC - 1);
+		ptxd = aup->tx_ring[aup->tx_tail];
+
+		if (aup->tx_full) {
+			aup->tx_full = 0;
+			netif_wake_queue(dev);
+		}
+	}
+
+	if (aup->tx_tail == aup->tx_head) {
+		if (aup->newspeed) {
+			au1k_irda_set_speed(dev, aup->newspeed);
+			aup->newspeed = 0;
+		}
+		else {
+			writel(read_ir_reg(IR_CONFIG_1) & ~IR_TX_ENABLE, 
+					IR_CONFIG_1); 
+			au_sync();
+			writel(read_ir_reg(IR_CONFIG_1) | IR_RX_ENABLE, 
+					IR_CONFIG_1); 
+			writel(0, IR_RING_PROMPT);
+			au_sync();
+		}
+	}
+}
+
+
+/*
+ * Au1000 transmit routine.
+ */
+static int au1k_irda_hard_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct au1k_private *aup = netdev_priv(dev);
+	int speed = irda_get_next_speed(skb);
+	volatile ring_dest_t *ptxd;
+	u32 len;
+
+	u32 flags;
+	db_dest_t *pDB;
+
+	if (speed != aup->speed && speed != -1) {
+		aup->newspeed = speed;
+	}
+
+	if ((skb->len == 0) && (aup->newspeed)) {
+		if (aup->tx_tail == aup->tx_head) {
+			au1k_irda_set_speed(dev, speed);
+			aup->newspeed = 0;
+		}
+		dev_kfree_skb(skb);
+		return 0;
+	}
+
+	ptxd = aup->tx_ring[aup->tx_head];
+	flags = ptxd->flags;
+
+	if (flags & AU_OWN) {
+		printk(KERN_DEBUG "%s: tx_full\n", dev->name);
+		netif_stop_queue(dev);
+		aup->tx_full = 1;
+		return 1;
+	}
+	else if (((aup->tx_head + 1) & (NUM_IR_DESC - 1)) == aup->tx_tail) {
+		printk(KERN_DEBUG "%s: tx_full\n", dev->name);
+		netif_stop_queue(dev);
+		aup->tx_full = 1;
+		return 1;
+	}
+
+	pDB = aup->tx_db_inuse[aup->tx_head];
+
+#if 0
+	if (read_ir_reg(IR_RX_BYTE_CNT) != 0) {
+		printk("tx warning: rx byte cnt %x\n", 
+				read_ir_reg(IR_RX_BYTE_CNT));
+	}
+#endif
+	
+	if (aup->speed == 4000000) {
+		/* FIR */
+		memcpy((void *)pDB->vaddr, skb->data, skb->len);
+		ptxd->count_0 = skb->len & 0xff;
+		ptxd->count_1 = (skb->len >> 8) & 0xff;
+
+	}
+	else {
+		/* SIR */
+		len = async_wrap_skb(skb, (u8 *)pDB->vaddr, MAX_BUF_SIZE);
+		ptxd->count_0 = len & 0xff;
+		ptxd->count_1 = (len >> 8) & 0xff;
+		ptxd->flags |= IR_DIS_CRC;
+		au_writel(au_readl(0xae00000c) & ~(1<<13), 0xae00000c);
+	}
+	ptxd->flags |= AU_OWN;
+	au_sync();
+
+	writel(read_ir_reg(IR_CONFIG_1) | IR_TX_ENABLE, IR_CONFIG_1); 
+	writel(0, IR_RING_PROMPT);
+	au_sync();
+
+	dev_kfree_skb(skb);
+	aup->tx_head = (aup->tx_head + 1) & (NUM_IR_DESC - 1);
+	dev->trans_start = jiffies;
+	return 0;
+}
+
+
+static inline void 
+update_rx_stats(struct net_device *dev, u32 status, u32 count)
+{
+	struct au1k_private *aup = netdev_priv(dev);
+	struct net_device_stats *ps = &aup->stats;
+
+	ps->rx_packets++;
+
+	if (status & IR_RX_ERROR) {
+		ps->rx_errors++;
+		if (status & (IR_PHY_ERROR|IR_FIFO_OVER))
+			ps->rx_missed_errors++;
+		if (status & IR_MAX_LEN)
+			ps->rx_length_errors++;
+		if (status & IR_CRC_ERROR)
+			ps->rx_crc_errors++;
+	}
+	else 
+		ps->rx_bytes += count;
+}
+
+/*
+ * Au1000 receive routine.
+ */
+static int au1k_irda_rx(struct net_device *dev)
+{
+	struct au1k_private *aup = netdev_priv(dev);
+	struct sk_buff *skb;
+	volatile ring_dest_t *prxd;
+	u32 flags, count;
+	db_dest_t *pDB;
+
+	prxd = aup->rx_ring[aup->rx_head];
+	flags = prxd->flags;
+
+	while (!(flags & AU_OWN))  {
+		pDB = aup->rx_db_inuse[aup->rx_head];
+		count = prxd->count_1<<8 | prxd->count_0;
+		if (!(flags & IR_RX_ERROR))  {
+			/* good frame */
+			update_rx_stats(dev, flags, count);
+			skb=alloc_skb(count+1,GFP_ATOMIC);
+			if (skb == NULL) {
+				aup->stats.rx_dropped++;
+				continue;
+			}
+			skb_reserve(skb, 1);
+			if (aup->speed == 4000000)
+				skb_put(skb, count);
+			else
+				skb_put(skb, count-2);
+			memcpy(skb->data, (void *)pDB->vaddr, count-2);
+			skb->dev = dev;
+			skb->mac.raw = skb->data;
+			skb->protocol = htons(ETH_P_IRDA);
+			netif_rx(skb);
+			prxd->count_0 = 0;
+			prxd->count_1 = 0;
+		}
+		prxd->flags |= AU_OWN;
+		aup->rx_head = (aup->rx_head + 1) & (NUM_IR_DESC - 1);
+		writel(0, IR_RING_PROMPT);
+		au_sync();
+
+		/* next descriptor */
+		prxd = aup->rx_ring[aup->rx_head];
+		flags = prxd->flags;
+		dev->last_rx = jiffies;
+
+	}
+	return 0;
+}
+
+
+void au1k_irda_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct net_device *dev = (struct net_device *) dev_id;
+
+	if (dev == NULL) {
+		printk(KERN_ERR "%s: isr: null dev ptr\n", dev->name);
+		return;
+	}
+
+	writel(0, IR_INT_CLEAR); /* ack irda interrupts */
+
+	au1k_irda_rx(dev);
+	au1k_tx_ack(dev);
+}
+
+
+/*
+ * The Tx ring has been full longer than the watchdog timeout
+ * value. The transmitter must be hung?
+ */
+static void au1k_tx_timeout(struct net_device *dev)
+{
+	u32 speed;
+	struct au1k_private *aup = netdev_priv(dev);
+
+	printk(KERN_ERR "%s: tx timeout\n", dev->name);
+	speed = aup->speed;
+	aup->speed = 0;
+	au1k_irda_set_speed(dev, speed);
+	aup->tx_full = 0;
+	netif_wake_queue(dev);
+}
+
+
+/*
+ * Set the IrDA communications speed.
+ */
+static int 
+au1k_irda_set_speed(struct net_device *dev, int speed)
+{
+	unsigned long flags;
+	struct au1k_private *aup = netdev_priv(dev);
+	u32 control;
+	int ret = 0, timeout = 10, i;
+	volatile ring_dest_t *ptxd;
+#if defined(CONFIG_MIPS_DB1000) || defined(CONFIG_MIPS_DB1100)
+	unsigned long irda_resets;
+#endif
+
+	if (speed == aup->speed)
+		return ret;
+
+	spin_lock_irqsave(&ir_lock, flags);
+
+	/* disable PHY first */
+	writel(read_ir_reg(IR_ENABLE) & ~0x8000, IR_ENABLE);
+
+	/* disable RX/TX */
+	writel(read_ir_reg(IR_CONFIG_1) & ~(IR_RX_ENABLE|IR_TX_ENABLE), 
+			IR_CONFIG_1);
+	au_sync_delay(1);
+	while (read_ir_reg(IR_ENABLE) & (IR_RX_STATUS | IR_TX_STATUS)) {
+		mdelay(1);
+		if (!timeout--) {
+			printk(KERN_ERR "%s: rx/tx disable timeout\n",
+					dev->name);
+			break;
+		}
+	}
+
+	/* disable DMA */
+	writel(read_ir_reg(IR_CONFIG_1) & ~IR_DMA_ENABLE, IR_CONFIG_1);
+	au_sync_delay(1);
+
+	/* 
+	 *  After we disable tx/rx. the index pointers
+ 	 * go back to zero.
+	 */
+	aup->tx_head = aup->tx_tail = aup->rx_head = 0;
+	for (i=0; i<NUM_IR_DESC; i++) {
+		ptxd = aup->tx_ring[i];
+		ptxd->flags = 0;
+		ptxd->count_0 = 0;
+		ptxd->count_1 = 0;
+	}
+
+	for (i=0; i<NUM_IR_DESC; i++) {
+		ptxd = aup->rx_ring[i];
+		ptxd->count_0 = 0;
+		ptxd->count_1 = 0;
+		ptxd->flags = AU_OWN;
+	}
+
+	if (speed == 4000000) {
+#if defined(CONFIG_MIPS_DB1000) || defined(CONFIG_MIPS_DB1100)
+		bcsr->resets |= BCSR_RESETS_FIR_SEL;
+#else /* Pb1000 and Pb1100 */
+		writel(1<<13, CPLD_AUX1);
+#endif
+	}
+	else {
+#if defined(CONFIG_MIPS_DB1000) || defined(CONFIG_MIPS_DB1100)
+		bcsr->resets &= ~BCSR_RESETS_FIR_SEL;
+#else /* Pb1000 and Pb1100 */
+		writel(readl(CPLD_AUX1) & ~(1<<13), CPLD_AUX1);
+#endif
+	}
+
+	switch (speed) {
+	case 9600:	
+		writel(11<<10 | 12<<5, IR_WRITE_PHY_CONFIG); 
+		writel(IR_SIR_MODE, IR_CONFIG_1); 
+		break;
+	case 19200:	
+		writel(5<<10 | 12<<5, IR_WRITE_PHY_CONFIG); 
+		writel(IR_SIR_MODE, IR_CONFIG_1); 
+		break;
+	case 38400:
+		writel(2<<10 | 12<<5, IR_WRITE_PHY_CONFIG); 
+		writel(IR_SIR_MODE, IR_CONFIG_1); 
+		break;
+	case 57600:	
+		writel(1<<10 | 12<<5, IR_WRITE_PHY_CONFIG); 
+		writel(IR_SIR_MODE, IR_CONFIG_1); 
+		break;
+	case 115200: 
+		writel(12<<5, IR_WRITE_PHY_CONFIG); 
+		writel(IR_SIR_MODE, IR_CONFIG_1); 
+		break;
+	case 4000000:
+		writel(0xF, IR_WRITE_PHY_CONFIG);
+		writel(IR_FIR|IR_DMA_ENABLE|IR_RX_ENABLE, IR_CONFIG_1); 
+		break;
+	default:
+		printk(KERN_ERR "%s unsupported speed %x\n", dev->name, speed);
+		ret = -EINVAL;
+		break;
+	}
+
+	aup->speed = speed;
+	writel(read_ir_reg(IR_ENABLE) | 0x8000, IR_ENABLE);
+	au_sync();
+
+	control = read_ir_reg(IR_ENABLE);
+	writel(0, IR_RING_PROMPT);
+	au_sync();
+
+	if (control & (1<<14)) {
+		printk(KERN_ERR "%s: configuration error\n", dev->name);
+	}
+	else {
+		if (control & (1<<11))
+			printk(KERN_DEBUG "%s Valid SIR config\n", dev->name);
+		if (control & (1<<12))
+			printk(KERN_DEBUG "%s Valid MIR config\n", dev->name);
+		if (control & (1<<13))
+			printk(KERN_DEBUG "%s Valid FIR config\n", dev->name);
+		if (control & (1<<10))
+			printk(KERN_DEBUG "%s TX enabled\n", dev->name);
+		if (control & (1<<9))
+			printk(KERN_DEBUG "%s RX enabled\n", dev->name);
+	}
+
+	spin_unlock_irqrestore(&ir_lock, flags);
+	return ret;
+}
+
+static int 
+au1k_irda_ioctl(struct net_device *dev, struct ifreq *ifreq, int cmd)
+{
+	struct if_irda_req *rq = (struct if_irda_req *)ifreq;
+	struct au1k_private *aup = netdev_priv(dev);
+	int ret = -EOPNOTSUPP;
+
+	switch (cmd) {
+	case SIOCSBANDWIDTH:
+		if (capable(CAP_NET_ADMIN)) {
+			/*
+			 * We are unable to set the speed if the
+			 * device is not running.
+			 */
+			if (aup->open)
+				ret = au1k_irda_set_speed(dev,
+						rq->ifr_baudrate);
+			else {
+				printk(KERN_ERR "%s ioctl: !netif_running\n",
+						dev->name);
+				ret = 0;
+			}
+		}
+		break;
+
+	case SIOCSMEDIABUSY:
+		ret = -EPERM;
+		if (capable(CAP_NET_ADMIN)) {
+			irda_device_set_media_busy(dev, TRUE);
+			ret = 0;
+		}
+		break;
+
+	case SIOCGRECEIVING:
+		rq->ifr_receiving = 0;
+		break;
+	default:
+		break;
+	}
+	return ret;
+}
+
+
+static struct net_device_stats *au1k_irda_stats(struct net_device *dev)
+{
+	struct au1k_private *aup = netdev_priv(dev);
+	return &aup->stats;
+}
+
+MODULE_AUTHOR("Pete Popov <ppopov@mvista.com>");
+MODULE_DESCRIPTION("Au1000 IrDA Device Driver");
+
+module_init(au1k_irda_init);
+module_exit(au1k_irda_exit);
diff --git a/drivers/net/irda/donauboe.c b/drivers/net/irda/donauboe.c
new file mode 100644
index 0000000..0a08c53
--- /dev/null
+++ b/drivers/net/irda/donauboe.c
@@ -0,0 +1,1789 @@
+/*****************************************************************
+ *
+ * Filename:		donauboe.c
+ * Version: 		2.17
+ * Description:   Driver for the Toshiba OBOE (or type-O or 701)
+ *                FIR Chipset, also supports the DONAUOBOE (type-DO
+ *                or d01) FIR chipset which as far as I know is
+ *                register compatible.
+ * Documentation: http://libxg.free.fr/irda/lib-irda.html
+ * Status:        Experimental.
+ * Author:        James McKenzie <james@fishsoup.dhs.org>
+ * Created at:    Sat May 8  12:35:27 1999
+ * Modified:      Paul Bristow <paul.bristow@technologist.com>
+ * Modified:      Mon Nov 11 19:10:05 1999
+ * Modified:      James McKenzie <james@fishsoup.dhs.org>
+ * Modified:      Thu Mar 16 12:49:00 2000 (Substantial rewrite)
+ * Modified:      Sat Apr 29 00:23:03 2000 (Added DONAUOBOE support)
+ * Modified:      Wed May 24 23:45:02 2000 (Fixed chipio_t structure)
+ * Modified: 2.13 Christian Gennerat <christian.gennerat@polytechnique.org>
+ * Modified: 2.13 dim jan 07 21:57:39 2001 (tested with kernel 2.4 & irnet/ppp)
+ * Modified: 2.14 Christian Gennerat <christian.gennerat@polytechnique.org>
+ * Modified: 2.14 lun fev 05 17:55:59 2001 (adapted to patch-2.4.1-pre8-irda1)
+ * Modified: 2.15 Martin Lucina <mato@kotelna.sk>
+ * Modified: 2.15 Fri Jun 21 20:40:59 2002 (sync with 2.4.18, substantial fixes)
+ * Modified: 2.16 Martin Lucina <mato@kotelna.sk>
+ * Modified: 2.16 Sat Jun 22 18:54:29 2002 (fix freeregion, default to verbose)
+ * Modified: 2.17 Christian Gennerat <christian.gennerat@polytechnique.org>
+ * Modified: 2.17 jeu sep 12 08:50:20 2002 (save_flags();cli(); replaced by spinlocks)
+ * Modified: 2.18 Christian Gennerat <christian.gennerat@polytechnique.org>
+ * Modified: 2.18 ven jan 10 03:14:16 2003 Change probe default options
+ *
+ *     Copyright (c) 1999 James McKenzie, 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 James McKenzie nor Cambridge University admit liability nor
+ *     provide warranty for any of this software. This material is
+ *     provided "AS-IS" and at no charge.
+ *
+ *     Applicable Models : Libretto 100/110CT and many more.
+ *     Toshiba refers to this chip as the type-O IR port,
+ *     or the type-DO IR port.
+ *
+ ********************************************************************/
+
+/* Look at toshoboe.h (currently in include/net/irda) for details of */
+/* Where to get documentation on the chip         */
+
+
+static char *rcsid =
+  "$Id: donauboe.c V2.18 ven jan 10 03:14:16 2003$";
+
+/* See below for a description of the logic in this driver */
+
+/* User servicable parts */
+/* USE_PROBE Create the code which probes the chip and does a few tests */
+/* do_probe module parameter Enable this code */
+/* Probe code is very useful for understanding how the hardware works */
+/* Use it with various combinations of TT_LEN, RX_LEN */
+/* Strongly recomended, disable if the probe fails on your machine */
+/* and send me <james@fishsoup.dhs.org> the output of dmesg */
+#define USE_PROBE 1
+#undef  USE_PROBE
+
+/* Trace Transmit ring, interrupts, Receive ring or not ? */
+#define PROBE_VERBOSE 1
+
+/* Debug option, examine sent and received raw data */
+/* Irdadump is better, but does not see all packets. enable it if you want. */
+#undef DUMP_PACKETS
+
+/* MIR mode has not been tested. Some behaviour is different */
+/* Seems to work against an Ericsson R520 for me. -Martin */
+#define USE_MIR
+
+/* Schedule back to back hardware transmits wherever possible, otherwise */
+/* we need an interrupt for every frame, unset if oboe works for a bit and */
+/* then hangs */
+#define OPTIMIZE_TX
+
+/* Set the number of slots in the rings */
+/* If you get rx/tx fifo overflows at high bitrates, you can try increasing */
+/* these */
+
+#define RING_SIZE (OBOE_RING_SIZE_RX8 | OBOE_RING_SIZE_TX8)
+#define TX_SLOTS    8
+#define RX_SLOTS    8
+
+
+/* Less user servicable parts below here */
+
+/* Test, Transmit and receive buffer sizes, adjust at your peril */
+/* remarks: nfs usually needs 1k blocks */
+/* remarks: in SIR mode, CRC is received, -> RX_LEN=TX_LEN+2 */
+/* remarks: test accepts large blocks. Standard is 0x80 */
+/* When TT_LEN > RX_LEN (SIR mode) data is stored in successive slots. */
+/* When 3 or more slots are needed for each test packet, */
+/* data received in the first slots is overwritten, even */
+/* if OBOE_CTL_RX_HW_OWNS is not set, without any error! */
+#define TT_LEN      0x80
+#define TX_LEN      0xc00
+#define RX_LEN      0xc04
+/* Real transmitted length (SIR mode) is about 14+(2%*TX_LEN) more */
+/* long than user-defined length (see async_wrap_skb) and is less then 4K */
+/* Real received length is (max RX_LEN) differs from user-defined */
+/* length only b the CRC (2 or 4 bytes) */
+#define BUF_SAFETY  0x7a
+#define RX_BUF_SZ   (RX_LEN)
+#define TX_BUF_SZ   (TX_LEN+BUF_SAFETY)
+
+
+/* Logic of the netdev part of this driver                             */
+
+/* The RX ring is filled with buffers, when a packet arrives           */
+/* it is DMA'd into the buffer which is marked used and RxDone called  */
+/* RxDone forms an skb (and checks the CRC if in SIR mode) and ships   */
+/* the packet off upstairs */
+
+/* The transmitter on the oboe chip can work in one of two modes       */
+/* for each ring->tx[] the transmitter can either                      */
+/* a) transmit the packet, leave the trasmitter enabled and proceed to */
+/*    the next ring                                                    */
+/* OR                                                                  */
+/* b) transmit the packet, switch off the transmitter and issue TxDone */
+
+/* All packets are entered into the ring in mode b), if the ring was   */
+/* empty the transmitter is started.                                   */
+
+/* If OPTIMIZE_TX is defined then in TxDone if the ring contains       */
+/* more than one packet, all but the last are set to mode a) [HOWEVER  */
+/* the hardware may not notice this, this is why we start in mode b) ] */
+/* then restart the transmitter                                        */
+
+/* If OPTIMIZE_TX is not defined then we just restart the transmitter  */
+/* if the ring isn't empty */
+
+/* Speed changes are delayed until the TxRing is empty                 */
+/* mtt is handled by generating packets with bad CRCs, before the data */
+
+/* TODO: */
+/* check the mtt works ok      */
+/* finish the watchdog         */
+
+/* No user servicable parts below here */
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/rtnetlink.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+
+#include <net/irda/wrapper.h>
+#include <net/irda/irda.h>
+//#include <net/irda/irmod.h>
+//#include <net/irda/irlap_frame.h>
+#include <net/irda/irda_device.h>
+#include <net/irda/crc.h>
+
+#include "donauboe.h"
+
+#define INB(port)       inb_p(port)
+#define OUTB(val,port)  outb_p(val,port)
+#define OUTBP(val,port) outb_p(val,port)
+
+#define PROMPT  OUTB(OBOE_PROMPT_BIT,OBOE_PROMPT);
+
+#if PROBE_VERBOSE
+#define PROBE_DEBUG(args...) (printk (args))
+#else
+#define PROBE_DEBUG(args...) ;
+#endif
+
+/* Set the DMA to be byte at a time */
+#define CONFIG0H_DMA_OFF OBOE_CONFIG0H_RCVANY
+#define CONFIG0H_DMA_ON_NORX CONFIG0H_DMA_OFF| OBOE_CONFIG0H_ENDMAC
+#define CONFIG0H_DMA_ON CONFIG0H_DMA_ON_NORX | OBOE_CONFIG0H_ENRX
+
+static struct pci_device_id toshoboe_pci_tbl[] = {
+	{ PCI_VENDOR_ID_TOSHIBA, PCI_DEVICE_ID_FIR701, PCI_ANY_ID, PCI_ANY_ID, },
+	{ PCI_VENDOR_ID_TOSHIBA, PCI_DEVICE_ID_FIRD01, PCI_ANY_ID, PCI_ANY_ID, },
+	{ }			/* Terminating entry */
+};
+MODULE_DEVICE_TABLE(pci, toshoboe_pci_tbl);
+
+#define DRIVER_NAME "toshoboe"
+static char *driver_name = DRIVER_NAME;
+
+static int max_baud = 4000000;
+#ifdef USE_PROBE
+static int do_probe = 0;
+#endif
+
+
+/**********************************************************************/
+static int
+toshoboe_checkfcs (unsigned char *buf, int len)
+{
+  int i;
+  union
+  {
+    __u16 value;
+    __u8 bytes[2];
+  }
+  fcs;
+
+  fcs.value = INIT_FCS;
+
+  for (i = 0; i < len; ++i)
+    fcs.value = irda_fcs (fcs.value, *(buf++));
+
+  return (fcs.value == GOOD_FCS);
+}
+
+/***********************************************************************/
+/* Generic chip handling code */
+#ifdef DUMP_PACKETS
+static unsigned char dump[50];
+static void
+_dumpbufs (unsigned char *data, int len, char tete)
+{
+int i,j;
+char head=tete;
+for (i=0;i<len;i+=16) {
+    for (j=0;j<16 && i+j<len;j++) { sprintf(&dump[3*j],"%02x.",data[i+j]); }
+    dump [3*j]=0;
+    IRDA_DEBUG (2, "%c%s\n",head , dump);
+    head='+';
+    }
+}
+#endif
+
+#ifdef USE_PROBE
+/* Dump the registers */
+static void
+toshoboe_dumpregs (struct toshoboe_cb *self)
+{
+  __u32 ringbase;
+
+  IRDA_DEBUG (4, "%s()\n", __FUNCTION__);
+
+  ringbase = INB (OBOE_RING_BASE0) << 10;
+  ringbase |= INB (OBOE_RING_BASE1) << 18;
+  ringbase |= INB (OBOE_RING_BASE2) << 26;
+
+  printk (KERN_ERR DRIVER_NAME ": Register dump:\n");
+  printk (KERN_ERR "Interrupts: Tx:%d Rx:%d TxUnder:%d RxOver:%d Sip:%d\n",
+          self->int_tx, self->int_rx, self->int_txunder, self->int_rxover,
+          self->int_sip);
+  printk (KERN_ERR "RX %02x TX %02x RingBase %08x\n",
+          INB (OBOE_RXSLOT), INB (OBOE_TXSLOT), ringbase);
+  printk (KERN_ERR "RING_SIZE %02x IER %02x ISR %02x\n",
+          INB (OBOE_RING_SIZE), INB (OBOE_IER), INB (OBOE_ISR));
+  printk (KERN_ERR "CONFIG1 %02x STATUS %02x\n",
+          INB (OBOE_CONFIG1), INB (OBOE_STATUS));
+  printk (KERN_ERR "CONFIG0 %02x%02x ENABLE %02x%02x\n",
+          INB (OBOE_CONFIG0H), INB (OBOE_CONFIG0L),
+          INB (OBOE_ENABLEH), INB (OBOE_ENABLEL));
+  printk (KERN_ERR "NEW_PCONFIG %02x%02x CURR_PCONFIG %02x%02x\n",
+          INB (OBOE_NEW_PCONFIGH), INB (OBOE_NEW_PCONFIGL),
+          INB (OBOE_CURR_PCONFIGH), INB (OBOE_CURR_PCONFIGL));
+  printk (KERN_ERR "MAXLEN %02x%02x RXCOUNT %02x%02x\n",
+          INB (OBOE_MAXLENH), INB (OBOE_MAXLENL),
+          INB (OBOE_RXCOUNTL), INB (OBOE_RXCOUNTH));
+
+  if (self->ring)
+    {
+      int i;
+      ringbase = virt_to_bus (self->ring);
+      printk (KERN_ERR "Ring at %08x:\n", ringbase);
+      printk (KERN_ERR "RX:");
+      for (i = 0; i < RX_SLOTS; ++i)
+        printk (" (%d,%02x)",self->ring->rx[i].len,self->ring->rx[i].control);
+      printk ("\n");
+      printk (KERN_ERR "TX:");
+      for (i = 0; i < RX_SLOTS; ++i)
+        printk (" (%d,%02x)",self->ring->tx[i].len,self->ring->tx[i].control);
+      printk ("\n");
+    }
+}
+#endif
+
+/*Don't let the chip look at memory */
+static void
+toshoboe_disablebm (struct toshoboe_cb *self)
+{
+  __u8 command;
+  IRDA_DEBUG (4, "%s()\n", __FUNCTION__);
+
+  pci_read_config_byte (self->pdev, PCI_COMMAND, &command);
+  command &= ~PCI_COMMAND_MASTER;
+  pci_write_config_byte (self->pdev, PCI_COMMAND, command);
+
+}
+
+/* Shutdown the chip and point the taskfile reg somewhere else */
+static void
+toshoboe_stopchip (struct toshoboe_cb *self)
+{
+  IRDA_DEBUG (4, "%s()\n", __FUNCTION__);
+
+  /*Disable interrupts */
+  OUTB (0x0, OBOE_IER);
+  /*Disable DMA, Disable Rx, Disable Tx */
+  OUTB (CONFIG0H_DMA_OFF, OBOE_CONFIG0H);
+  /*Disable SIR MIR FIR, Tx and Rx */
+  OUTB (0x00, OBOE_ENABLEH);
+  /*Point the ring somewhere safe */
+  OUTB (0x3f, OBOE_RING_BASE2);
+  OUTB (0xff, OBOE_RING_BASE1);
+  OUTB (0xff, OBOE_RING_BASE0);
+
+  OUTB (RX_LEN >> 8, OBOE_MAXLENH);
+  OUTB (RX_LEN & 0xff, OBOE_MAXLENL);
+
+  /*Acknoledge any pending interrupts */
+  OUTB (0xff, OBOE_ISR);
+
+  /*Why */
+  OUTB (OBOE_ENABLEH_PHYANDCLOCK, OBOE_ENABLEH);
+
+  /*switch it off */
+  OUTB (OBOE_CONFIG1_OFF, OBOE_CONFIG1);
+
+  toshoboe_disablebm (self);
+}
+
+/* Transmitter initialization */
+static void
+toshoboe_start_DMA (struct toshoboe_cb *self, int opts)
+{
+  OUTB (0x0, OBOE_ENABLEH);
+  OUTB (CONFIG0H_DMA_ON | opts,  OBOE_CONFIG0H);
+  OUTB (OBOE_ENABLEH_PHYANDCLOCK, OBOE_ENABLEH);
+  PROMPT;
+}
+
+/*Set the baud rate */
+static void
+toshoboe_setbaud (struct toshoboe_cb *self)
+{
+  __u16 pconfig = 0;
+  __u8 config0l = 0;
+
+  IRDA_DEBUG (2, "%s(%d/%d)\n", __FUNCTION__, self->speed, self->io.speed);
+
+  switch (self->speed)
+    {
+    case 2400:
+    case 4800:
+    case 9600:
+    case 19200:
+    case 38400:
+    case 57600:
+    case 115200:
+#ifdef USE_MIR
+    case 1152000:
+#endif
+    case 4000000:
+      break;
+    default:
+
+      printk (KERN_ERR DRIVER_NAME ": switch to unsupported baudrate %d\n",
+              self->speed);
+      return;
+    }
+
+  switch (self->speed)
+    {
+      /* For SIR the preamble is done by adding XBOFs */
+      /* to the packet */
+      /* set to filtered SIR mode, filter looks for BOF and EOF */
+    case 2400:
+      pconfig |= 47 << OBOE_PCONFIG_BAUDSHIFT;
+      pconfig |= 25 << OBOE_PCONFIG_WIDTHSHIFT;
+      break;
+    case 4800:
+      pconfig |= 23 << OBOE_PCONFIG_BAUDSHIFT;
+      pconfig |= 25 << OBOE_PCONFIG_WIDTHSHIFT;
+      break;
+    case 9600:
+      pconfig |= 11 << OBOE_PCONFIG_BAUDSHIFT;
+      pconfig |= 25 << OBOE_PCONFIG_WIDTHSHIFT;
+      break;
+    case 19200:
+      pconfig |= 5 << OBOE_PCONFIG_BAUDSHIFT;
+      pconfig |= 25 << OBOE_PCONFIG_WIDTHSHIFT;
+      break;
+    case 38400:
+      pconfig |= 2 << OBOE_PCONFIG_BAUDSHIFT;
+      pconfig |= 25 << OBOE_PCONFIG_WIDTHSHIFT;
+      break;
+    case 57600:
+      pconfig |= 1 << OBOE_PCONFIG_BAUDSHIFT;
+      pconfig |= 25 << OBOE_PCONFIG_WIDTHSHIFT;
+      break;
+    case 115200:
+      pconfig |= 0 << OBOE_PCONFIG_BAUDSHIFT;
+      pconfig |= 25 << OBOE_PCONFIG_WIDTHSHIFT;
+      break;
+    default:
+      /*Set to packet based reception */
+      OUTB (RX_LEN >> 8, OBOE_MAXLENH);
+      OUTB (RX_LEN & 0xff, OBOE_MAXLENL);
+      break;
+    }
+
+  switch (self->speed)
+    {
+    case 2400:
+    case 4800:
+    case 9600:
+    case 19200:
+    case 38400:
+    case 57600:
+    case 115200:
+      config0l = OBOE_CONFIG0L_ENSIR;
+      if (self->async)
+        {
+          /*Set to character based reception */
+          /*System will lock if MAXLEN=0 */
+          /*so have to be careful */
+          OUTB (0x01, OBOE_MAXLENH);
+          OUTB (0x01, OBOE_MAXLENL);
+          OUTB (0x00, OBOE_MAXLENH);
+        }
+      else
+        {
+          /*Set to packet based reception */
+          config0l |= OBOE_CONFIG0L_ENSIRF;
+          OUTB (RX_LEN >> 8, OBOE_MAXLENH);
+          OUTB (RX_LEN & 0xff, OBOE_MAXLENL);
+        }
+      break;
+
+#ifdef USE_MIR
+      /* MIR mode */
+      /* Set for 16 bit CRC and enable MIR */
+      /* Preamble now handled by the chip */
+    case 1152000:
+      pconfig |= 0 << OBOE_PCONFIG_BAUDSHIFT;
+      pconfig |= 8 << OBOE_PCONFIG_WIDTHSHIFT;
+      pconfig |= 1 << OBOE_PCONFIG_PREAMBLESHIFT;
+      config0l = OBOE_CONFIG0L_CRC16 | OBOE_CONFIG0L_ENMIR;
+      break;
+#endif
+      /* FIR mode */
+      /* Set for 32 bit CRC and enable FIR */
+      /* Preamble handled by the chip */
+    case 4000000:
+      pconfig |= 0 << OBOE_PCONFIG_BAUDSHIFT;
+      /* Documentation says 14, but toshiba use 15 in their drivers */
+      pconfig |= 15 << OBOE_PCONFIG_PREAMBLESHIFT;
+      config0l = OBOE_CONFIG0L_ENFIR;
+      break;
+    }
+
+  /* Copy into new PHY config buffer */
+  OUTBP (pconfig >> 8, OBOE_NEW_PCONFIGH);
+  OUTB (pconfig & 0xff, OBOE_NEW_PCONFIGL);
+  OUTB (config0l, OBOE_CONFIG0L);
+
+  /* Now make OBOE copy from new PHY to current PHY */
+  OUTB (0x0, OBOE_ENABLEH);
+  OUTB (OBOE_ENABLEH_PHYANDCLOCK, OBOE_ENABLEH);
+  PROMPT;
+
+  /* speed change executed */
+  self->new_speed = 0;
+  self->io.speed = self->speed;
+}
+
+/*Let the chip look at memory */
+static void
+toshoboe_enablebm (struct toshoboe_cb *self)
+{
+  IRDA_DEBUG (4, "%s()\n", __FUNCTION__);
+  pci_set_master (self->pdev);
+}
+
+/*setup the ring */
+static void
+toshoboe_initring (struct toshoboe_cb *self)
+{
+  int i;
+
+  IRDA_DEBUG (4, "%s()\n", __FUNCTION__);
+
+  for (i = 0; i < TX_SLOTS; ++i)
+    {
+      self->ring->tx[i].len = 0;
+      self->ring->tx[i].control = 0x00;
+      self->ring->tx[i].address = virt_to_bus (self->tx_bufs[i]);
+    }
+
+  for (i = 0; i < RX_SLOTS; ++i)
+    {
+      self->ring->rx[i].len = RX_LEN;
+      self->ring->rx[i].len = 0;
+      self->ring->rx[i].address = virt_to_bus (self->rx_bufs[i]);
+      self->ring->rx[i].control = OBOE_CTL_RX_HW_OWNS;
+    }
+}
+
+static void
+toshoboe_resetptrs (struct toshoboe_cb *self)
+{
+  /* Can reset pointers by twidling DMA */
+  OUTB (0x0, OBOE_ENABLEH);
+  OUTBP (CONFIG0H_DMA_OFF, OBOE_CONFIG0H);
+  OUTB (OBOE_ENABLEH_PHYANDCLOCK, OBOE_ENABLEH);
+
+  self->rxs = inb_p (OBOE_RXSLOT) & OBOE_SLOT_MASK;
+  self->txs = inb_p (OBOE_TXSLOT) & OBOE_SLOT_MASK;
+}
+
+/* Called in locked state */
+static void
+toshoboe_initptrs (struct toshoboe_cb *self)
+{
+
+  /* spin_lock_irqsave(self->spinlock, flags); */
+  /* save_flags (flags); */
+
+  /* Can reset pointers by twidling DMA */
+  toshoboe_resetptrs (self);
+
+  OUTB (0x0, OBOE_ENABLEH);
+  OUTB (CONFIG0H_DMA_ON, OBOE_CONFIG0H);
+  OUTB (OBOE_ENABLEH_PHYANDCLOCK, OBOE_ENABLEH);
+
+  self->txpending = 0;
+
+  /* spin_unlock_irqrestore(self->spinlock, flags); */
+  /* restore_flags (flags); */
+}
+
+/* Wake the chip up and get it looking at the rings */
+/* Called in locked state */
+static void
+toshoboe_startchip (struct toshoboe_cb *self)
+{
+  __u32 physaddr;
+
+  IRDA_DEBUG (4, "%s()\n", __FUNCTION__);
+
+  toshoboe_initring (self);
+  toshoboe_enablebm (self);
+  OUTBP (OBOE_CONFIG1_RESET, OBOE_CONFIG1);
+  OUTBP (OBOE_CONFIG1_ON, OBOE_CONFIG1);
+
+  /* Stop the clocks */
+  OUTB (0, OBOE_ENABLEH);
+
+  /*Set size of rings */
+  OUTB (RING_SIZE, OBOE_RING_SIZE);
+
+  /*Acknoledge any pending interrupts */
+  OUTB (0xff, OBOE_ISR);
+
+  /*Enable ints */
+  OUTB (OBOE_INT_TXDONE  | OBOE_INT_RXDONE |
+        OBOE_INT_TXUNDER | OBOE_INT_RXOVER | OBOE_INT_SIP , OBOE_IER);
+
+  /*Acknoledge any pending interrupts */
+  OUTB (0xff, OBOE_ISR);
+
+  /*Set the maximum packet length to 0xfff (4095) */
+  OUTB (RX_LEN >> 8, OBOE_MAXLENH);
+  OUTB (RX_LEN & 0xff, OBOE_MAXLENL);
+
+  /*Shutdown DMA */
+  OUTB (CONFIG0H_DMA_OFF, OBOE_CONFIG0H);
+
+  /*Find out where the rings live */
+  physaddr = virt_to_bus (self->ring);
+
+  IRDA_ASSERT ((physaddr & 0x3ff) == 0,
+	       printk (KERN_ERR DRIVER_NAME "ring not correctly aligned\n");
+	       return;);
+
+  OUTB ((physaddr >> 10) & 0xff, OBOE_RING_BASE0);
+  OUTB ((physaddr >> 18) & 0xff, OBOE_RING_BASE1);
+  OUTB ((physaddr >> 26) & 0x3f, OBOE_RING_BASE2);
+
+  /*Enable DMA controler in byte mode and RX */
+  OUTB (CONFIG0H_DMA_ON, OBOE_CONFIG0H);
+
+  /* Start up the clocks */
+  OUTB (OBOE_ENABLEH_PHYANDCLOCK, OBOE_ENABLEH);
+
+  /*set to sensible speed */
+  self->speed = 9600;
+  toshoboe_setbaud (self);
+  toshoboe_initptrs (self);
+}
+
+static void
+toshoboe_isntstuck (struct toshoboe_cb *self)
+{
+}
+
+static void
+toshoboe_checkstuck (struct toshoboe_cb *self)
+{
+  unsigned long flags;
+
+  if (0)
+    {
+      spin_lock_irqsave(&self->spinlock, flags);
+
+      /* This will reset the chip completely */
+      printk (KERN_ERR DRIVER_NAME ": Resetting chip\n");
+
+      toshoboe_stopchip (self);
+      toshoboe_startchip (self);
+      spin_unlock_irqrestore(&self->spinlock, flags);
+    }
+}
+
+/*Generate packet of about mtt us long */
+static int
+toshoboe_makemttpacket (struct toshoboe_cb *self, void *buf, int mtt)
+{
+  int xbofs;
+
+  xbofs = ((int) (mtt/100)) * (int) (self->speed);
+  xbofs=xbofs/80000; /*Eight bits per byte, and mtt is in us*/
+  xbofs++;
+
+  IRDA_DEBUG (2, DRIVER_NAME
+      ": generated mtt of %d bytes for %d us at %d baud\n"
+	  , xbofs,mtt,self->speed);
+
+  if (xbofs > TX_LEN)
+    {
+      printk (KERN_ERR DRIVER_NAME ": wanted %d bytes MTT but TX_LEN is %d\n",
+              xbofs, TX_LEN);
+      xbofs = TX_LEN;
+    }
+
+  /*xbofs will do for SIR, MIR and FIR,SIR mode doesn't generate a checksum anyway */
+  memset (buf, XBOF, xbofs);
+
+  return xbofs;
+}
+
+static int toshoboe_invalid_dev(int irq)
+{
+  printk (KERN_WARNING DRIVER_NAME ": irq %d for unknown device.\n", irq);
+  return 1;
+}
+
+#ifdef USE_PROBE
+/***********************************************************************/
+/* Probe code */
+
+static void
+toshoboe_dumptx (struct toshoboe_cb *self)
+{
+  int i;
+  PROBE_DEBUG(KERN_WARNING "TX:");
+  for (i = 0; i < RX_SLOTS; ++i)
+    PROBE_DEBUG(" (%d,%02x)",self->ring->tx[i].len,self->ring->tx[i].control);
+  PROBE_DEBUG(" [%d]\n",self->speed);
+}
+
+static void
+toshoboe_dumprx (struct toshoboe_cb *self, int score)
+{
+  int i;
+  PROBE_DEBUG(" %d\nRX:",score);
+  for (i = 0; i < RX_SLOTS; ++i)
+    PROBE_DEBUG(" (%d,%02x)",self->ring->rx[i].len,self->ring->rx[i].control);
+  PROBE_DEBUG("\n");
+}
+
+static inline int
+stuff_byte (__u8 byte, __u8 * buf)
+{
+  switch (byte)
+    {
+    case BOF:                  /* FALLTHROUGH */
+    case EOF:                  /* FALLTHROUGH */
+    case CE:
+      /* Insert transparently coded */
+      buf[0] = CE;              /* Send link escape */
+      buf[1] = byte ^ IRDA_TRANS; /* Complement bit 5 */
+      return 2;
+      /* break; */
+    default:
+      /* Non-special value, no transparency required */
+      buf[0] = byte;
+      return 1;
+      /* break; */
+    }
+}
+
+static irqreturn_t
+toshoboe_probeinterrupt (int irq, void *dev_id, struct pt_regs *regs)
+{
+  struct toshoboe_cb *self = (struct toshoboe_cb *) dev_id;
+  __u8 irqstat;
+
+  if (self == NULL && toshoboe_invalid_dev(irq))
+    return IRQ_NONE;
+
+  irqstat = INB (OBOE_ISR);
+
+/* was it us */
+  if (!(irqstat & OBOE_INT_MASK))
+    return IRQ_NONE;
+
+/* Ack all the interrupts */
+  OUTB (irqstat, OBOE_ISR);
+
+  if (irqstat & OBOE_INT_TXDONE)
+    {
+      int txp;
+
+      self->int_tx++;
+      PROBE_DEBUG("T");
+
+      txp = INB (OBOE_TXSLOT) & OBOE_SLOT_MASK;
+      if (self->ring->tx[txp].control & OBOE_CTL_TX_HW_OWNS)
+        {
+          self->int_tx+=100;
+          PROBE_DEBUG("S");
+          toshoboe_start_DMA(self, OBOE_CONFIG0H_ENTX | OBOE_CONFIG0H_LOOP);
+        }
+    }
+
+  if (irqstat & OBOE_INT_RXDONE) {
+    self->int_rx++;
+    PROBE_DEBUG("R"); }
+  if (irqstat & OBOE_INT_TXUNDER) {
+    self->int_txunder++;
+    PROBE_DEBUG("U"); }
+  if (irqstat & OBOE_INT_RXOVER) {
+    self->int_rxover++;
+    PROBE_DEBUG("O"); }
+  if (irqstat & OBOE_INT_SIP) {
+    self->int_sip++;
+    PROBE_DEBUG("I"); }
+  return IRQ_HANDLED;
+}
+
+static int
+toshoboe_maketestpacket (unsigned char *buf, int badcrc, int fir)
+{
+  int i;
+  int len = 0;
+  union
+  {
+    __u16 value;
+    __u8 bytes[2];
+  }
+  fcs;
+
+  if (fir)
+    {
+      memset (buf, 0, TT_LEN);
+      return (TT_LEN);
+    }
+
+  fcs.value = INIT_FCS;
+
+  memset (buf, XBOF, 10);
+  len += 10;
+  buf[len++] = BOF;
+
+  for (i = 0; i < TT_LEN; ++i)
+    {
+      len += stuff_byte (i, buf + len);
+      fcs.value = irda_fcs (fcs.value, i);
+    }
+
+  len += stuff_byte (fcs.bytes[0] ^ badcrc, buf + len);
+  len += stuff_byte (fcs.bytes[1] ^ badcrc, buf + len);
+  buf[len++] = EOF;
+  len++;
+  return len;
+}
+
+static int
+toshoboe_probefail (struct toshoboe_cb *self, char *msg)
+{
+  printk (KERN_ERR DRIVER_NAME "probe(%d) failed %s\n",self-> speed, msg);
+  toshoboe_dumpregs (self);
+  toshoboe_stopchip (self);
+  free_irq (self->io.irq, (void *) self);
+  return 0;
+}
+
+static int
+toshoboe_numvalidrcvs (struct toshoboe_cb *self)
+{
+  int i, ret = 0;
+  for (i = 0; i < RX_SLOTS; ++i)
+    if ((self->ring->rx[i].control & 0xe0) == 0)
+      ret++;
+
+  return ret;
+}
+
+static int
+toshoboe_numrcvs (struct toshoboe_cb *self)
+{
+  int i, ret = 0;
+  for (i = 0; i < RX_SLOTS; ++i)
+    if (!(self->ring->rx[i].control & OBOE_CTL_RX_HW_OWNS))
+      ret++;
+
+  return ret;
+}
+
+static int
+toshoboe_probe (struct toshoboe_cb *self)
+{
+  int i, j, n;
+#ifdef USE_MIR
+  int bauds[] = { 9600, 115200, 4000000, 1152000 };
+#else
+  int bauds[] = { 9600, 115200, 4000000 };
+#endif
+  unsigned long flags;
+
+  IRDA_DEBUG (4, "%s()\n", __FUNCTION__);
+
+  if (request_irq (self->io.irq, toshoboe_probeinterrupt,
+                   self->io.irqflags, "toshoboe", (void *) self))
+    {
+      printk (KERN_ERR DRIVER_NAME ": probe failed to allocate irq %d\n",
+              self->io.irq);
+      return 0;
+    }
+
+  /* test 1: SIR filter and back to back */
+
+  for (j = 0; j < (sizeof (bauds) / sizeof (int)); ++j)
+    {
+      int fir = (j > 1);
+      toshoboe_stopchip (self);
+
+
+      spin_lock_irqsave(&self->spinlock, flags);
+      /*Address is already setup */
+      toshoboe_startchip (self);
+      self->int_rx = self->int_tx = 0;
+      self->speed = bauds[j];
+      toshoboe_setbaud (self);
+      toshoboe_initptrs (self);
+      spin_unlock_irqrestore(&self->spinlock, flags);
+
+      self->ring->tx[self->txs].control =
+/*   (FIR only) OBOE_CTL_TX_SIP needed for switching to next slot */
+/*    MIR: all received data is stored in one slot */
+        (fir) ? OBOE_CTL_TX_HW_OWNS | OBOE_CTL_TX_RTCENTX
+              : OBOE_CTL_TX_HW_OWNS ;
+      self->ring->tx[self->txs].len =
+        toshoboe_maketestpacket (self->tx_bufs[self->txs], 0, fir);
+      self->txs++;
+      self->txs %= TX_SLOTS;
+
+      self->ring->tx[self->txs].control =
+        (fir) ? OBOE_CTL_TX_HW_OWNS | OBOE_CTL_TX_SIP
+              : OBOE_CTL_TX_HW_OWNS | OBOE_CTL_TX_RTCENTX ;
+      self->ring->tx[self->txs].len =
+        toshoboe_maketestpacket (self->tx_bufs[self->txs], 0, fir);
+      self->txs++;
+      self->txs %= TX_SLOTS;
+
+      self->ring->tx[self->txs].control =
+        (fir) ? OBOE_CTL_TX_HW_OWNS | OBOE_CTL_TX_RTCENTX
+              : OBOE_CTL_TX_HW_OWNS ;
+      self->ring->tx[self->txs].len =
+        toshoboe_maketestpacket (self->tx_bufs[self->txs], 0, fir);
+      self->txs++;
+      self->txs %= TX_SLOTS;
+
+      self->ring->tx[self->txs].control =
+        (fir) ? OBOE_CTL_TX_HW_OWNS | OBOE_CTL_TX_RTCENTX
+              | OBOE_CTL_TX_SIP     | OBOE_CTL_TX_BAD_CRC
+              : OBOE_CTL_TX_HW_OWNS | OBOE_CTL_TX_RTCENTX ;
+      self->ring->tx[self->txs].len =
+        toshoboe_maketestpacket (self->tx_bufs[self->txs], 0, fir);
+      self->txs++;
+      self->txs %= TX_SLOTS;
+
+      toshoboe_dumptx (self);
+      /* Turn on TX and RX and loopback */
+      toshoboe_start_DMA(self, OBOE_CONFIG0H_ENTX | OBOE_CONFIG0H_LOOP);
+
+      i = 0;
+      n = fir ? 1 : 4;
+      while (toshoboe_numvalidrcvs (self) != n)
+        {
+          if (i > 4800)
+              return toshoboe_probefail (self, "filter test");
+          udelay ((9600*(TT_LEN+16))/self->speed);
+          i++;
+        }
+
+      n = fir ? 203 : 102;
+      while ((toshoboe_numrcvs(self) != self->int_rx) || (self->int_tx != n))
+        {
+          if (i > 4800)
+              return toshoboe_probefail (self, "interrupt test");
+          udelay ((9600*(TT_LEN+16))/self->speed);
+          i++;
+        }
+     toshoboe_dumprx (self,i);
+
+     }
+
+  /* test 2: SIR in char at a time */
+
+  toshoboe_stopchip (self);
+  self->int_rx = self->int_tx = 0;
+
+  spin_lock_irqsave(&self->spinlock, flags);
+  toshoboe_startchip (self);
+  spin_unlock_irqrestore(&self->spinlock, flags);
+
+  self->async = 1;
+  self->speed = 115200;
+  toshoboe_setbaud (self);
+  self->ring->tx[self->txs].control =
+    OBOE_CTL_TX_RTCENTX | OBOE_CTL_TX_HW_OWNS;
+  self->ring->tx[self->txs].len = 4;
+
+  ((unsigned char *) self->tx_bufs[self->txs])[0] = 'f';
+  ((unsigned char *) self->tx_bufs[self->txs])[1] = 'i';
+  ((unsigned char *) self->tx_bufs[self->txs])[2] = 's';
+  ((unsigned char *) self->tx_bufs[self->txs])[3] = 'h';
+  toshoboe_dumptx (self);
+  toshoboe_start_DMA(self, OBOE_CONFIG0H_ENTX | OBOE_CONFIG0H_LOOP);
+
+  i = 0;
+  while (toshoboe_numvalidrcvs (self) != 4)
+    {
+      if (i > 100)
+          return toshoboe_probefail (self, "Async test");
+      udelay (100);
+      i++;
+    }
+
+  while ((toshoboe_numrcvs (self) != self->int_rx) || (self->int_tx != 1))
+    {
+      if (i > 100)
+          return toshoboe_probefail (self, "Async interrupt test");
+      udelay (100);
+      i++;
+    }
+  toshoboe_dumprx (self,i);
+
+  self->async = 0;
+  self->speed = 9600;
+  toshoboe_setbaud (self);
+  toshoboe_stopchip (self);
+
+  free_irq (self->io.irq, (void *) self);
+
+  printk (KERN_WARNING DRIVER_NAME ": Self test passed ok\n");
+
+  return 1;
+}
+#endif
+
+/******************************************************************/
+/* Netdev style code */
+
+/* Transmit something */
+static int
+toshoboe_hard_xmit (struct sk_buff *skb, struct net_device *dev)
+{
+  struct toshoboe_cb *self;
+  __s32 speed;
+  int mtt, len, ctl;
+  unsigned long flags;
+  struct irda_skb_cb *cb = (struct irda_skb_cb *) skb->cb;
+
+  self = (struct toshoboe_cb *) dev->priv;
+
+  IRDA_ASSERT (self != NULL, return 0; );
+
+  IRDA_DEBUG (1, "%s.tx:%x(%x)%x\n", __FUNCTION__
+      ,skb->len,self->txpending,INB (OBOE_ENABLEH));
+  if (!cb->magic) {
+      IRDA_DEBUG (2, "%s.Not IrLAP:%x\n", __FUNCTION__, cb->magic);
+#ifdef DUMP_PACKETS
+      _dumpbufs(skb->data,skb->len,'>');
+#endif
+    }
+
+  /* change speed pending, wait for its execution */
+  if (self->new_speed)
+      return -EBUSY;
+
+  /* device stopped (apm) wait for restart */
+  if (self->stopped)
+      return -EBUSY;
+
+  toshoboe_checkstuck (self);
+
+  dev->trans_start = jiffies;
+
+ /* Check if we need to change the speed */
+  /* But not now. Wait after transmission if mtt not required */
+  speed=irda_get_next_speed(skb);
+  if ((speed != self->io.speed) && (speed != -1))
+    {
+      spin_lock_irqsave(&self->spinlock, flags);
+
+      if (self->txpending || skb->len)
+        {
+          self->new_speed = speed;
+          IRDA_DEBUG (1, "%s: Queued TxDone scheduled speed change %d\n" ,
+		      __FUNCTION__, speed);
+          /* if no data, that's all! */
+          if (!skb->len)
+            {
+	      spin_unlock_irqrestore(&self->spinlock, flags);
+              dev_kfree_skb (skb);
+              return 0;
+            }
+          /* True packet, go on, but */
+          /* do not accept anything before change speed execution */
+          netif_stop_queue(dev);
+          /* ready to process TxDone interrupt */
+	  spin_unlock_irqrestore(&self->spinlock, flags);
+        }
+      else
+        {
+          /* idle and no data, change speed now */
+          self->speed = speed;
+          toshoboe_setbaud (self);
+	  spin_unlock_irqrestore(&self->spinlock, flags);
+          dev_kfree_skb (skb);
+          return 0;
+        }
+
+    }
+
+  if ((mtt = irda_get_mtt(skb)))
+    {
+      /* This is fair since the queue should be empty anyway */
+      spin_lock_irqsave(&self->spinlock, flags);
+
+      if (self->txpending)
+        {
+	  spin_unlock_irqrestore(&self->spinlock, flags);
+          return -EBUSY;
+        }
+
+      /* If in SIR mode we need to generate a string of XBOFs */
+      /* In MIR and FIR we need to generate a string of data */
+      /* which we will add a wrong checksum to */
+
+      mtt = toshoboe_makemttpacket (self, self->tx_bufs[self->txs], mtt);
+      IRDA_DEBUG (1, "%s.mtt:%x(%x)%d\n", __FUNCTION__
+          ,skb->len,mtt,self->txpending);
+      if (mtt)
+        {
+          self->ring->tx[self->txs].len = mtt & 0xfff;
+
+          ctl = OBOE_CTL_TX_HW_OWNS | OBOE_CTL_TX_RTCENTX;
+          if (INB (OBOE_ENABLEH) & OBOE_ENABLEH_FIRON)
+            {
+              ctl |= OBOE_CTL_TX_BAD_CRC | OBOE_CTL_TX_SIP ;
+            }
+#ifdef USE_MIR
+          else if (INB (OBOE_ENABLEH) & OBOE_ENABLEH_MIRON)
+            {
+              ctl |= OBOE_CTL_TX_BAD_CRC;
+            }
+#endif
+          self->ring->tx[self->txs].control = ctl;
+
+          OUTB (0x0, OBOE_ENABLEH);
+          /* It is only a timer. Do not send mtt packet outside! */
+          toshoboe_start_DMA(self, OBOE_CONFIG0H_ENTX | OBOE_CONFIG0H_LOOP);
+
+          self->txpending++;
+
+          self->txs++;
+          self->txs %= TX_SLOTS;
+
+        }
+      else
+        {
+          printk(KERN_ERR DRIVER_NAME ": problem with mtt packet - ignored\n");
+        }
+      spin_unlock_irqrestore(&self->spinlock, flags);
+    }
+
+#ifdef DUMP_PACKETS
+dumpbufs(skb->data,skb->len,'>');
+#endif
+
+  spin_lock_irqsave(&self->spinlock, flags);
+
+  if (self->ring->tx[self->txs].control & OBOE_CTL_TX_HW_OWNS)
+    {
+      IRDA_DEBUG (0, "%s.ful:%x(%x)%x\n", __FUNCTION__
+          ,skb->len, self->ring->tx[self->txs].control, self->txpending);
+      toshoboe_start_DMA(self, OBOE_CONFIG0H_ENTX);
+      spin_unlock_irqrestore(&self->spinlock, flags);
+      return -EBUSY;
+    }
+
+  if (INB (OBOE_ENABLEH) & OBOE_ENABLEH_SIRON)
+    {
+      len = async_wrap_skb (skb, self->tx_bufs[self->txs], TX_BUF_SZ);
+    }
+  else
+    {
+      len = skb->len;
+      memcpy (self->tx_bufs[self->txs], skb->data, len);
+    }
+  self->ring->tx[self->txs].len = len & 0x0fff;
+
+  /*Sometimes the HW doesn't see us assert RTCENTX in the interrupt code */
+  /*later this plays safe, we garuntee the last packet to be transmitted */
+  /*has RTCENTX set */
+
+  ctl = OBOE_CTL_TX_HW_OWNS | OBOE_CTL_TX_RTCENTX;
+  if (INB (OBOE_ENABLEH) & OBOE_ENABLEH_FIRON)
+    {
+      ctl |= OBOE_CTL_TX_SIP ;
+    }
+  self->ring->tx[self->txs].control = ctl;
+
+  /* If transmitter is idle start in one-shot mode */
+
+  if (!self->txpending)
+      toshoboe_start_DMA(self, OBOE_CONFIG0H_ENTX);
+
+  self->txpending++;
+
+  self->txs++;
+  self->txs %= TX_SLOTS;
+
+  spin_unlock_irqrestore(&self->spinlock, flags);
+  dev_kfree_skb (skb);
+
+  return 0;
+}
+
+/*interrupt handler */
+static irqreturn_t
+toshoboe_interrupt (int irq, void *dev_id, struct pt_regs *regs)
+{
+  struct toshoboe_cb *self = (struct toshoboe_cb *) dev_id;
+  __u8 irqstat;
+  struct sk_buff *skb = NULL;
+
+  if (self == NULL && toshoboe_invalid_dev(irq))
+    return IRQ_NONE;
+
+  irqstat = INB (OBOE_ISR);
+
+/* was it us */
+  if (!(irqstat & OBOE_INT_MASK))
+      return IRQ_NONE;
+
+/* Ack all the interrupts */
+  OUTB (irqstat, OBOE_ISR);
+
+  toshoboe_isntstuck (self);
+
+/* Txdone */
+  if (irqstat & OBOE_INT_TXDONE)
+    {
+      int txp, txpc;
+      int i;
+
+      txp = self->txpending;
+      self->txpending = 0;
+
+      for (i = 0; i < TX_SLOTS; ++i)
+        {
+          if (self->ring->tx[i].control & OBOE_CTL_TX_HW_OWNS)
+              self->txpending++;
+        }
+      IRDA_DEBUG (1, "%s.txd(%x)%x/%x\n", __FUNCTION__
+          ,irqstat,txp,self->txpending);
+
+      txp = INB (OBOE_TXSLOT) & OBOE_SLOT_MASK;
+
+      /* Got anything queued ? start it together */
+      if (self->ring->tx[txp].control & OBOE_CTL_TX_HW_OWNS)
+        {
+          txpc = txp;
+#ifdef OPTIMIZE_TX
+          while (self->ring->tx[txpc].control & OBOE_CTL_TX_HW_OWNS)
+            {
+              txp = txpc;
+              txpc++;
+              txpc %= TX_SLOTS;
+              self->stats.tx_packets++;
+              if (self->ring->tx[txpc].control & OBOE_CTL_TX_HW_OWNS)
+                  self->ring->tx[txp].control &= ~OBOE_CTL_TX_RTCENTX;
+            }
+          self->stats.tx_packets--;
+#else
+          self->stats.tx_packets++;
+#endif
+          toshoboe_start_DMA(self, OBOE_CONFIG0H_ENTX);
+        }
+
+      if ((!self->txpending) && (self->new_speed))
+        {
+          self->speed = self->new_speed;
+          IRDA_DEBUG (1, "%s: Executed TxDone scheduled speed change %d\n",
+		      __FUNCTION__, self->speed);
+          toshoboe_setbaud (self);
+        }
+
+      /* Tell network layer that we want more frames */
+      if (!self->new_speed)
+          netif_wake_queue(self->netdev);
+    }
+
+  if (irqstat & OBOE_INT_RXDONE)
+    {
+      while (!(self->ring->rx[self->rxs].control & OBOE_CTL_RX_HW_OWNS))
+        {
+          int len = self->ring->rx[self->rxs].len;
+          skb = NULL;
+          IRDA_DEBUG (3, "%s.rcv:%x(%x)\n", __FUNCTION__
+		      ,len,self->ring->rx[self->rxs].control);
+
+#ifdef DUMP_PACKETS
+dumpbufs(self->rx_bufs[self->rxs],len,'<');
+#endif
+
+          if (self->ring->rx[self->rxs].control == 0)
+            {
+              __u8 enable = INB (OBOE_ENABLEH);
+
+              /* In SIR mode we need to check the CRC as this */
+              /* hasn't been done by the hardware */
+              if (enable & OBOE_ENABLEH_SIRON)
+                {
+                  if (!toshoboe_checkfcs (self->rx_bufs[self->rxs], len))
+                      len = 0;
+                  /*Trim off the CRC */
+                  if (len > 1)
+                      len -= 2;
+                  else
+                      len = 0;
+                  IRDA_DEBUG (1, "%s.SIR:%x(%x)\n", __FUNCTION__, len,enable);
+                }
+
+#ifdef USE_MIR
+              else if (enable & OBOE_ENABLEH_MIRON)
+                {
+                  if (len > 1)
+                      len -= 2;
+                  else
+                      len = 0;
+                  IRDA_DEBUG (2, "%s.MIR:%x(%x)\n", __FUNCTION__, len,enable);
+                }
+#endif
+              else if (enable & OBOE_ENABLEH_FIRON)
+                {
+                  if (len > 3)
+                      len -= 4;   /*FIXME: check this */
+                  else
+                      len = 0;
+                  IRDA_DEBUG (1, "%s.FIR:%x(%x)\n", __FUNCTION__, len,enable);
+                }
+              else
+                  IRDA_DEBUG (0, "%s.?IR:%x(%x)\n", __FUNCTION__, len,enable);
+
+              if (len)
+                {
+                  skb = dev_alloc_skb (len + 1);
+                  if (skb)
+                    {
+                      skb_reserve (skb, 1);
+
+                      skb_put (skb, len);
+                      memcpy (skb->data, self->rx_bufs[self->rxs], len);
+
+                      self->stats.rx_packets++;
+                      skb->dev = self->netdev;
+                      skb->mac.raw = skb->data;
+                      skb->protocol = htons (ETH_P_IRDA);
+                    }
+                  else
+                    {
+                      printk (KERN_INFO
+                              "%s(), memory squeeze, dropping frame.\n",
+			      __FUNCTION__);
+                    }
+                }
+            }
+          else
+            {
+            /* TODO: =========================================== */
+            /*  if OBOE_CTL_RX_LENGTH, our buffers are too small */
+            /* (MIR or FIR) data is lost. */
+            /* (SIR) data is splitted in several slots. */
+            /* we have to join all the received buffers received */
+            /*in a large buffer before checking CRC. */
+            IRDA_DEBUG (0, "%s.err:%x(%x)\n", __FUNCTION__
+                ,len,self->ring->rx[self->rxs].control);
+            }
+
+          self->ring->rx[self->rxs].len = 0x0;
+          self->ring->rx[self->rxs].control = OBOE_CTL_RX_HW_OWNS;
+
+          self->rxs++;
+          self->rxs %= RX_SLOTS;
+
+          if (skb)
+              netif_rx (skb);
+
+        }
+    }
+
+  if (irqstat & OBOE_INT_TXUNDER)
+    {
+      printk (KERN_WARNING DRIVER_NAME ": tx fifo underflow\n");
+    }
+  if (irqstat & OBOE_INT_RXOVER)
+    {
+      printk (KERN_WARNING DRIVER_NAME ": rx fifo overflow\n");
+    }
+/* This must be useful for something... */
+  if (irqstat & OBOE_INT_SIP)
+    {
+      self->int_sip++;
+      IRDA_DEBUG (1, "%s.sip:%x(%x)%x\n", __FUNCTION__
+	      ,self->int_sip,irqstat,self->txpending);
+    }
+  return IRQ_HANDLED;
+}
+
+
+static int
+toshoboe_net_open (struct net_device *dev)
+{
+  struct toshoboe_cb *self;
+  unsigned long flags;
+
+  IRDA_DEBUG (4, "%s()\n", __FUNCTION__);
+
+  IRDA_ASSERT (dev != NULL, return -1; );
+  self = (struct toshoboe_cb *) dev->priv;
+
+  IRDA_ASSERT (self != NULL, return 0; );
+
+  if (self->async)
+    return -EBUSY;
+
+  if (self->stopped)
+    return 0;
+
+  if (request_irq (self->io.irq, toshoboe_interrupt,
+                   SA_SHIRQ | SA_INTERRUPT, dev->name, (void *) self))
+    {
+      return -EAGAIN;
+    }
+
+  spin_lock_irqsave(&self->spinlock, flags);
+  toshoboe_startchip (self);
+  spin_unlock_irqrestore(&self->spinlock, flags);
+
+  /* Ready to play! */
+  netif_start_queue(dev);
+
+  /*
+   * Open new IrLAP layer instance, now that everything should be
+   * initialized properly
+   */
+  self->irlap = irlap_open (dev, &self->qos, driver_name);
+
+  self->irdad = 1;
+
+  return 0;
+}
+
+static int
+toshoboe_net_close (struct net_device *dev)
+{
+  struct toshoboe_cb *self;
+
+  IRDA_DEBUG (4, "%s()\n", __FUNCTION__);
+
+  IRDA_ASSERT (dev != NULL, return -1; );
+  self = (struct toshoboe_cb *) dev->priv;
+
+  /* Stop device */
+  netif_stop_queue(dev);
+
+  /* Stop and remove instance of IrLAP */
+  if (self->irlap)
+    irlap_close (self->irlap);
+  self->irlap = NULL;
+
+  self->irdad = 0;
+
+  free_irq (self->io.irq, (void *) self);
+
+  if (!self->stopped)
+    {
+      toshoboe_stopchip (self);
+    }
+
+  return 0;
+}
+
+/*
+ * Function toshoboe_net_ioctl (dev, rq, cmd)
+ *
+ *    Process IOCTL commands for this device
+ *
+ */
+static int
+toshoboe_net_ioctl (struct net_device *dev, struct ifreq *rq, int cmd)
+{
+  struct if_irda_req *irq = (struct if_irda_req *) rq;
+  struct toshoboe_cb *self;
+  unsigned long flags;
+  int ret = 0;
+
+  IRDA_ASSERT (dev != NULL, return -1; );
+
+  self = dev->priv;
+
+  IRDA_ASSERT (self != NULL, return -1; );
+
+  IRDA_DEBUG (5, "%s(), %s, (cmd=0x%X)\n", __FUNCTION__, dev->name, cmd);
+
+  /* Disable interrupts & save flags */
+  spin_lock_irqsave(&self->spinlock, flags);
+
+  switch (cmd)
+    {
+    case SIOCSBANDWIDTH:       /* Set bandwidth */
+      /* This function will also be used by IrLAP to change the
+       * speed, so we still must allow for speed change within
+       * interrupt context.
+       */
+      IRDA_DEBUG (1, "%s(BANDWIDTH), %s, (%X/%ld\n", __FUNCTION__
+          ,dev->name, INB (OBOE_STATUS), irq->ifr_baudrate );
+      if (!in_interrupt () && !capable (CAP_NET_ADMIN))
+        return -EPERM;
+
+      /* self->speed=irq->ifr_baudrate; */
+      /* toshoboe_setbaud(self); */
+      /* Just change speed once - inserted by Paul Bristow */
+      self->new_speed = irq->ifr_baudrate;
+      break;
+    case SIOCSMEDIABUSY:       /* Set media busy */
+      IRDA_DEBUG (1, "%s(MEDIABUSY), %s, (%X/%x)\n", __FUNCTION__
+          ,dev->name, INB (OBOE_STATUS), capable (CAP_NET_ADMIN) );
+      if (!capable (CAP_NET_ADMIN))
+        return -EPERM;
+      irda_device_set_media_busy (self->netdev, TRUE);
+      break;
+    case SIOCGRECEIVING:       /* Check if we are receiving right now */
+      irq->ifr_receiving = (INB (OBOE_STATUS) & OBOE_STATUS_RXBUSY) ? 1 : 0;
+      IRDA_DEBUG (3, "%s(RECEIVING), %s, (%X/%x)\n", __FUNCTION__
+          ,dev->name, INB (OBOE_STATUS), irq->ifr_receiving );
+      break;
+    default:
+      IRDA_DEBUG (1, "%s(?), %s, (cmd=0x%X)\n", __FUNCTION__, dev->name, cmd);
+      ret = -EOPNOTSUPP;
+    }
+
+  spin_unlock_irqrestore(&self->spinlock, flags);
+  return ret;
+
+}
+
+MODULE_DESCRIPTION("Toshiba OBOE IrDA Device Driver");
+MODULE_AUTHOR("James McKenzie <james@fishsoup.dhs.org>");
+MODULE_LICENSE("GPL");
+
+module_param (max_baud, int, 0);
+MODULE_PARM_DESC(max_baud, "Maximum baud rate");
+
+#ifdef USE_PROBE
+module_param (do_probe, bool, 0);
+MODULE_PARM_DESC(do_probe, "Enable/disable chip probing and self-test");
+#endif
+
+static void
+toshoboe_close (struct pci_dev *pci_dev)
+{
+  int i;
+  struct toshoboe_cb *self = (struct toshoboe_cb*)pci_get_drvdata(pci_dev);
+
+  IRDA_DEBUG (4, "%s()\n", __FUNCTION__);
+
+  IRDA_ASSERT (self != NULL, return; );
+
+  if (!self->stopped)
+    {
+      toshoboe_stopchip (self);
+    }
+
+  release_region (self->io.fir_base, self->io.fir_ext);
+
+  for (i = 0; i < TX_SLOTS; ++i)
+    {
+      kfree (self->tx_bufs[i]);
+      self->tx_bufs[i] = NULL;
+    }
+
+  for (i = 0; i < RX_SLOTS; ++i)
+    {
+      kfree (self->rx_bufs[i]);
+      self->rx_bufs[i] = NULL;
+    }
+
+  unregister_netdev(self->netdev);
+
+  kfree (self->ringbuf);
+  self->ringbuf = NULL;
+  self->ring = NULL;
+
+  free_netdev(self->netdev);
+}
+
+static int
+toshoboe_open (struct pci_dev *pci_dev, const struct pci_device_id *pdid)
+{
+  struct toshoboe_cb *self;
+  struct net_device *dev;
+  int i = 0;
+  int ok = 0;
+  int err;
+
+  IRDA_DEBUG (4, "%s()\n", __FUNCTION__);
+
+  if ((err=pci_enable_device(pci_dev)))
+    return err;
+
+  dev = alloc_irdadev(sizeof (struct toshoboe_cb));
+  if (dev == NULL)
+    {
+      printk (KERN_ERR DRIVER_NAME ": can't allocate memory for "
+              "IrDA control block\n");
+      return -ENOMEM;
+    }
+
+  self = dev->priv;
+  self->netdev = dev;
+  self->pdev = pci_dev;
+  self->base = pci_resource_start(pci_dev,0);
+
+  self->io.fir_base = self->base;
+  self->io.fir_ext = OBOE_IO_EXTENT;
+  self->io.irq = pci_dev->irq;
+  self->io.irqflags = SA_SHIRQ | SA_INTERRUPT;
+
+  self->speed = self->io.speed = 9600;
+  self->async = 0;
+
+  /* Lock the port that we need */
+  if (NULL==request_region (self->io.fir_base, self->io.fir_ext, driver_name))
+    {
+      printk (KERN_ERR DRIVER_NAME ": can't get iobase of 0x%03x\n"
+	      ,self->io.fir_base);
+      err = -EBUSY;
+      goto freeself;
+    }
+
+  spin_lock_init(&self->spinlock);
+
+  irda_init_max_qos_capabilies (&self->qos);
+  self->qos.baud_rate.bits = 0;
+
+  if (max_baud >= 2400)
+    self->qos.baud_rate.bits |= IR_2400;
+  /*if (max_baud>=4800) idev->qos.baud_rate.bits|=IR_4800; */
+  if (max_baud >= 9600)
+    self->qos.baud_rate.bits |= IR_9600;
+  if (max_baud >= 19200)
+    self->qos.baud_rate.bits |= IR_19200;
+  if (max_baud >= 115200)
+    self->qos.baud_rate.bits |= IR_115200;
+#ifdef USE_MIR
+  if (max_baud >= 1152000)
+    {
+      self->qos.baud_rate.bits |= IR_1152000;
+    }
+#endif
+  if (max_baud >= 4000000)
+    {
+      self->qos.baud_rate.bits |= (IR_4000000 << 8);
+    }
+
+  /*FIXME: work this out... */
+  self->qos.min_turn_time.bits = 0xff;
+
+  irda_qos_bits_to_value (&self->qos);
+
+  /* Allocate twice the size to guarantee alignment */
+  self->ringbuf = (void *) kmalloc (OBOE_RING_LEN << 1, GFP_KERNEL);
+  if (!self->ringbuf)
+    {
+      printk (KERN_ERR DRIVER_NAME ": can't allocate DMA buffers\n");
+      err = -ENOMEM;
+      goto freeregion;
+    }
+
+#if (BITS_PER_LONG == 64)
+#error broken on 64-bit:  casts pointer to 32-bit, and then back to pointer.
+#endif
+
+  /*We need to align the taskfile on a taskfile size boundary */
+  {
+    unsigned long addr;
+
+    addr = (__u32) self->ringbuf;
+    addr &= ~(OBOE_RING_LEN - 1);
+    addr += OBOE_RING_LEN;
+    self->ring = (struct OboeRing *) addr;
+  }
+
+  memset (self->ring, 0, OBOE_RING_LEN);
+  self->io.mem_base = (__u32) self->ring;
+
+  ok = 1;
+  for (i = 0; i < TX_SLOTS; ++i)
+    {
+      self->tx_bufs[i] = kmalloc (TX_BUF_SZ, GFP_KERNEL);
+      if (!self->tx_bufs[i])
+        ok = 0;
+    }
+
+  for (i = 0; i < RX_SLOTS; ++i)
+    {
+      self->rx_bufs[i] = kmalloc (RX_BUF_SZ, GFP_KERNEL);
+      if (!self->rx_bufs[i])
+        ok = 0;
+    }
+
+  if (!ok)
+    {
+      printk (KERN_ERR DRIVER_NAME ": can't allocate rx/tx buffers\n");
+      err = -ENOMEM;
+      goto freebufs;
+    }
+
+
+#ifdef USE_PROBE
+  if (do_probe)
+    if (!toshoboe_probe (self))
+      {
+        err = -ENODEV;
+        goto freebufs;
+      }
+#endif
+
+  SET_MODULE_OWNER(dev);
+  SET_NETDEV_DEV(dev, &pci_dev->dev);
+  dev->hard_start_xmit = toshoboe_hard_xmit;
+  dev->open = toshoboe_net_open;
+  dev->stop = toshoboe_net_close;
+  dev->do_ioctl = toshoboe_net_ioctl;
+
+  err = register_netdev(dev);
+  if (err)
+    {
+      printk (KERN_ERR DRIVER_NAME ": register_netdev() failed\n");
+      err = -ENOMEM;
+      goto freebufs;
+    }
+  printk (KERN_INFO "IrDA: Registered device %s\n", dev->name);
+
+  pci_set_drvdata(pci_dev,self);
+
+  printk (KERN_INFO DRIVER_NAME ": Using multiple tasks, version %s\n", rcsid);
+
+  return 0;
+
+freebufs:
+  for (i = 0; i < TX_SLOTS; ++i)
+    if (self->tx_bufs[i])
+      kfree (self->tx_bufs[i]);
+  for (i = 0; i < RX_SLOTS; ++i)
+    if (self->rx_bufs[i])
+      kfree (self->rx_bufs[i]);
+  kfree(self->ringbuf);
+
+freeregion:
+  release_region (self->io.fir_base, self->io.fir_ext);
+
+freeself:
+  free_netdev(dev);
+
+  return err;
+}
+
+static int
+toshoboe_gotosleep (struct pci_dev *pci_dev, pm_message_t crap)
+{
+  struct toshoboe_cb *self = (struct toshoboe_cb*)pci_get_drvdata(pci_dev);
+  unsigned long flags;
+  int i = 10;
+
+  IRDA_DEBUG (4, "%s()\n", __FUNCTION__);
+
+  if (!self || self->stopped)
+    return 0;
+
+  if ((!self->irdad) && (!self->async))
+    return 0;
+
+/* Flush all packets */
+  while ((i--) && (self->txpending))
+    udelay (10000);
+
+  spin_lock_irqsave(&self->spinlock, flags);
+
+  toshoboe_stopchip (self);
+  self->stopped = 1;
+  self->txpending = 0;
+
+  spin_unlock_irqrestore(&self->spinlock, flags);
+  return 0;
+}
+
+static int
+toshoboe_wakeup (struct pci_dev *pci_dev)
+{
+  struct toshoboe_cb *self = (struct toshoboe_cb*)pci_get_drvdata(pci_dev);
+  unsigned long flags;
+
+  IRDA_DEBUG (4, "%s()\n", __FUNCTION__);
+
+  if (!self || !self->stopped)
+    return 0;
+
+  if ((!self->irdad) && (!self->async))
+    return 0;
+
+  spin_lock_irqsave(&self->spinlock, flags);
+
+  toshoboe_startchip (self);
+  self->stopped = 0;
+
+  netif_wake_queue(self->netdev);
+  spin_unlock_irqrestore(&self->spinlock, flags);
+  return 0;
+}
+
+static struct pci_driver donauboe_pci_driver = {
+	.name		= "donauboe",
+	.id_table	= toshoboe_pci_tbl,
+	.probe		= toshoboe_open,
+	.remove		= toshoboe_close,
+	.suspend	= toshoboe_gotosleep,
+	.resume		= toshoboe_wakeup 
+};
+
+static int __init
+donauboe_init (void)
+{
+  return pci_module_init(&donauboe_pci_driver);
+}
+
+static void __exit
+donauboe_cleanup (void)
+{
+  pci_unregister_driver(&donauboe_pci_driver);
+}
+
+module_init(donauboe_init);
+module_exit(donauboe_cleanup);
diff --git a/drivers/net/irda/donauboe.h b/drivers/net/irda/donauboe.h
new file mode 100644
index 0000000..2ab173d
--- /dev/null
+++ b/drivers/net/irda/donauboe.h
@@ -0,0 +1,363 @@
+/*********************************************************************
+ *                
+ * Filename:      toshoboe.h
+ * Version:       2.16
+ * Description:   Driver for the Toshiba OBOE (or type-O or 701)
+ *                FIR Chipset, also supports the DONAUOBOE (type-DO
+ *                or d01) FIR chipset which as far as I know is
+ *                register compatible.
+ * Status:        Experimental.
+ * Author:        James McKenzie <james@fishsoup.dhs.org>
+ * Created at:    Sat May 8  12:35:27 1999
+ * Modified: 2.16 Martin Lucina <mato@kotelna.sk>
+ * Modified: 2.16 Sat Jun 22 18:54:29 2002 (sync headers)
+ * Modified: 2.17 Christian Gennerat <christian.gennerat@polytechnique.org>
+ * Modified: 2.17 jeu sep 12 08:50:20 2002 (add lock to be used by spinlocks)
+ * 
+ *     Copyright (c) 1999 James McKenzie, 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 James McKenzie nor Cambridge University admit liability nor
+ *     provide warranty for any of this software. This material is 
+ *     provided "AS-IS" and at no charge.
+ * 
+ *     Applicable Models : Libretto 100/110CT and many more.
+ *     Toshiba refers to this chip as the type-O IR port,
+ *     or the type-DO IR port.
+ *
+ * IrDA chip set list from Toshiba Computer Engineering Corp.
+ * model			method	maker	controler		Version 
+ * Portege 320CT	FIR,SIR Toshiba Oboe(Triangle) 
+ * Portege 3010CT	FIR,SIR Toshiba Oboe(Sydney) 
+ * Portege 3015CT	FIR,SIR Toshiba Oboe(Sydney) 
+ * Portege 3020CT	FIR,SIR Toshiba Oboe(Sydney) 
+ * Portege 7020CT	FIR,SIR ?		?
+ * 
+ * Satell. 4090XCDT	FIR,SIR ?		?
+ * 
+ * Libretto 100CT	FIR,SIR Toshiba Oboe 
+ * Libretto 1000CT	FIR,SIR Toshiba Oboe 
+ * 
+ * TECRA750DVD		FIR,SIR Toshiba Oboe(Triangle)	REV ID=14h 
+ * TECRA780			FIR,SIR Toshiba Oboe(Sandlot)	REV ID=32h,33h 
+ * TECRA750CDT		FIR,SIR Toshiba Oboe(Triangle)	REV ID=13h,14h 
+ * TECRA8000		FIR,SIR Toshiba Oboe(ISKUR)		REV ID=23h 
+ * 
+ ********************************************************************/
+
+/* The documentation for this chip is allegedly released         */
+/* However I have not seen it, not have I managed to contact     */
+/* anyone who has. HOWEVER the chip bears a striking resemblence */
+/* to the IrDA controller in the Toshiba RISC TMPR3922 chip      */
+/* the documentation for this is freely available at             */
+/* http://www.toshiba.com/taec/components/Generic/TMPR3922.shtml */
+/* The mapping between the registers in that document and the    */
+/* Registers in the 701 oboe chip are as follows    */
+
+
+/* 3922 reg     701 regs, by bit numbers                        */
+/*               7- 0  15- 8  24-16  31-25                      */
+/* $28            0x0    0x1                                    */
+/* $2c                                     SEE NOTE 1           */
+/* $30            0x6    0x7                                    */
+/* $34            0x8    0x9               SEE NOTE 2           */
+/* $38           0x10   0x11                                    */
+/* $3C                   0xe               SEE NOTE 3           */
+/* $40           0x12   0x13                                    */
+/* $44           0x14   0x15                                    */
+/* $48           0x16   0x17                                    */
+/* $4c           0x18   0x19                                    */
+/* $50           0x1a   0x1b                                    */
+
+/* FIXME: could be 0x1b 0x1a here */
+
+/* $54           0x1d   0x1c                                    */
+/* $5C           0xf                       SEE NOTE 4           */
+/* $130                                    SEE NOTE 5           */
+/* $134                                    SEE NOTE 6           */
+/*                                                              */
+/* NOTES:                                                       */
+/* 1. The pointer to ring is packed in most unceremoniusly      */
+/*    701 Register      Address bits    (A9-A0 must be zero)    */
+/*            0x4:      A17 A16 A15 A14 A13 A12 A11 A10         */
+/*            0x5:      A25 A24 A23 A22 A21 A20 A19 A18         */
+/*            0x2:        0   0 A31 A30 A29 A28 A27 A26         */
+/*                                                              */
+/* 2. The M$ drivers do a write 0x1 to 0x9, however the 3922    */
+/*    documentation would suggest that a write of 0x1 to 0x8    */
+/*    would be more appropriate.                                */
+/*                                                              */
+/* 3. This assignment is tenuous at best, register 0xe seems to */
+/*    have bits arranged 0 0 0 R/W R/W R/W R/W R/W              */
+/*    if either of the lower two bits are set the chip seems to */
+/*    switch off                                                */
+/*                                                              */
+/* 4. Bits 7-4 seem to be different 4 seems just to be generic  */
+/*    receiver busy flag                                        */
+/*                                                              */
+/* 5. and 6. The IER and ISR have a different bit assignment    */
+/*    The lower three bits of both read back as ones            */
+/* ISR is register 0xc, IER is register 0xd                     */
+/*           7      6      5      4      3      2      1      0 */
+/* 0xc: TxDone RxDone TxUndr RxOver SipRcv      1      1      1 */
+/* 0xd: TxDone RxDone TxUndr RxOver SipRcv      1      1      1 */
+/* TxDone xmitt done (generated only if generate interrupt bit  */
+/*   is set in the ring)                                        */
+/* RxDone recv completed (or other recv condition if you set it */
+/*   up                                                         */
+/* TxUnder underflow in Transmit FIFO                           */
+/* RxOver  overflow in Recv FIFO                                */
+/* SipRcv  received serial gap  (or other condition you set)    */
+/* Interrupts are enabled by writing a one to the IER register  */
+/* Interrupts are cleared by writting a one to the ISR register */
+/*                                                              */
+/* 6. The remaining registers: 0x6 and 0x3 appear to be         */
+/*    reserved parts of 16 or 32 bit registersthe remainder     */
+/*    0xa 0xb 0x1e 0x1f could possibly be (by their behaviour)  */
+/*    the Unicast Filter register at $58.                       */
+/*                                                              */
+/* 7. While the core obviously expects 32 bit accesses all the  */
+/*    M$ drivers do 8 bit accesses, infact the Miniport ones    */
+/*    write and read back the byte serveral times (why?)        */
+
+
+#ifndef TOSHOBOE_H
+#define TOSHOBOE_H
+
+/* Registers */
+
+#define OBOE_IO_EXTENT	0x1f
+
+/*Receive and transmit slot pointers */
+#define OBOE_REG(i)	(i+(self->base))
+#define OBOE_RXSLOT	OBOE_REG(0x0)
+#define OBOE_TXSLOT	OBOE_REG(0x1)
+#define OBOE_SLOT_MASK	0x3f
+
+#define OBOE_TXRING_OFFSET		0x200
+#define OBOE_TXRING_OFFSET_IN_SLOTS	0x40
+
+/*pointer to the ring */
+#define OBOE_RING_BASE0	OBOE_REG(0x4)
+#define OBOE_RING_BASE1	OBOE_REG(0x5)
+#define OBOE_RING_BASE2	OBOE_REG(0x2)
+#define OBOE_RING_BASE3	OBOE_REG(0x3)
+
+/*Number of slots in the ring */
+#define OBOE_RING_SIZE  OBOE_REG(0x7)
+#define OBOE_RING_SIZE_RX4	0x00
+#define OBOE_RING_SIZE_RX8	0x01
+#define OBOE_RING_SIZE_RX16	0x03
+#define OBOE_RING_SIZE_RX32	0x07
+#define OBOE_RING_SIZE_RX64	0x0f
+#define OBOE_RING_SIZE_TX4	0x00
+#define OBOE_RING_SIZE_TX8	0x10
+#define OBOE_RING_SIZE_TX16	0x30
+#define OBOE_RING_SIZE_TX32	0x70
+#define OBOE_RING_SIZE_TX64	0xf0
+
+#define OBOE_RING_MAX_SIZE	64
+
+/*Causes the gubbins to re-examine the ring */
+#define OBOE_PROMPT	OBOE_REG(0x9)
+#define OBOE_PROMPT_BIT		0x1
+
+/* Interrupt Status Register */
+#define OBOE_ISR	OBOE_REG(0xc)
+/* Interrupt Enable Register */
+#define OBOE_IER	OBOE_REG(0xd)
+/* Interrupt bits for IER and ISR */
+#define OBOE_INT_TXDONE		0x80
+#define OBOE_INT_RXDONE		0x40
+#define OBOE_INT_TXUNDER	0x20
+#define OBOE_INT_RXOVER		0x10
+#define OBOE_INT_SIP		0x08
+#define OBOE_INT_MASK		0xf8
+
+/*Reset Register */
+#define OBOE_CONFIG1	OBOE_REG(0xe)
+#define OBOE_CONFIG1_RST	0x01
+#define OBOE_CONFIG1_DISABLE	0x02
+#define OBOE_CONFIG1_4		0x08
+#define OBOE_CONFIG1_8		0x08
+
+#define OBOE_CONFIG1_ON		0x8
+#define OBOE_CONFIG1_RESET	0xf
+#define OBOE_CONFIG1_OFF	0xe
+
+#define OBOE_STATUS	OBOE_REG(0xf)
+#define OBOE_STATUS_RXBUSY	0x10
+#define OBOE_STATUS_FIRRX	0x04
+#define OBOE_STATUS_MIRRX	0x02
+#define OBOE_STATUS_SIRRX	0x01
+
+
+/*Speed control registers */
+#define OBOE_CONFIG0L	OBOE_REG(0x10)
+#define OBOE_CONFIG0H	OBOE_REG(0x11)
+
+#define OBOE_CONFIG0H_TXONLOOP  0x80 /*Transmit when looping (dangerous) */
+#define OBOE_CONFIG0H_LOOP	0x40 /*Loopback Tx->Rx */
+#define OBOE_CONFIG0H_ENTX	0x10 /*Enable Tx */
+#define OBOE_CONFIG0H_ENRX	0x08 /*Enable Rx */
+#define OBOE_CONFIG0H_ENDMAC	0x04 /*Enable/reset* the DMA controller */
+#define OBOE_CONFIG0H_RCVANY	0x02 /*DMA mode 1=bytes, 0=dwords */
+
+#define OBOE_CONFIG0L_CRC16	0x80 /*CRC 1=16 bit 0=32 bit */
+#define OBOE_CONFIG0L_ENFIR	0x40 /*Enable FIR */
+#define OBOE_CONFIG0L_ENMIR	0x20 /*Enable MIR */
+#define OBOE_CONFIG0L_ENSIR	0x10 /*Enable SIR */
+#define OBOE_CONFIG0L_ENSIRF	0x08 /*Enable SIR framer */
+#define OBOE_CONFIG0L_SIRTEST	0x04 /*Enable SIR framer in MIR and FIR */
+#define OBOE_CONFIG0L_INVERTTX  0x02 /*Invert Tx Line */
+#define OBOE_CONFIG0L_INVERTRX  0x01 /*Invert Rx Line */
+
+#define OBOE_BOF	OBOE_REG(0x12)
+#define OBOE_EOF	OBOE_REG(0x13)
+
+#define OBOE_ENABLEL	OBOE_REG(0x14)
+#define OBOE_ENABLEH	OBOE_REG(0x15)
+
+#define OBOE_ENABLEH_PHYANDCLOCK	0x80 /*Toggle low to copy config in */
+#define OBOE_ENABLEH_CONFIGERR		0x40
+#define OBOE_ENABLEH_FIRON		0x20
+#define OBOE_ENABLEH_MIRON		0x10
+#define OBOE_ENABLEH_SIRON		0x08
+#define OBOE_ENABLEH_ENTX		0x04
+#define OBOE_ENABLEH_ENRX		0x02
+#define OBOE_ENABLEH_CRC16		0x01
+
+#define OBOE_ENABLEL_BROADCAST		0x01
+
+#define OBOE_CURR_PCONFIGL		OBOE_REG(0x16) /*Current config */
+#define OBOE_CURR_PCONFIGH		OBOE_REG(0x17)
+
+#define OBOE_NEW_PCONFIGL		OBOE_REG(0x18)
+#define OBOE_NEW_PCONFIGH		OBOE_REG(0x19)
+
+#define OBOE_PCONFIGH_BAUDMASK		0xfc
+#define OBOE_PCONFIGH_WIDTHMASK		0x04
+#define OBOE_PCONFIGL_WIDTHMASK		0xe0
+#define OBOE_PCONFIGL_PREAMBLEMASK	0x1f
+
+#define OBOE_PCONFIG_BAUDMASK		0xfc00
+#define OBOE_PCONFIG_BAUDSHIFT		10
+#define OBOE_PCONFIG_WIDTHMASK		0x04e0
+#define OBOE_PCONFIG_WIDTHSHIFT		5
+#define OBOE_PCONFIG_PREAMBLEMASK	0x001f
+#define OBOE_PCONFIG_PREAMBLESHIFT	0
+
+#define OBOE_MAXLENL			OBOE_REG(0x1a)
+#define OBOE_MAXLENH			OBOE_REG(0x1b)
+
+#define OBOE_RXCOUNTH			OBOE_REG(0x1c) /*Reset on recipt */
+#define OBOE_RXCOUNTL			OBOE_REG(0x1d) /*of whole packet */
+
+/* The PCI ID of the OBOE chip */
+#ifndef PCI_DEVICE_ID_FIR701
+#define PCI_DEVICE_ID_FIR701 	0x0701
+#endif
+
+#ifndef PCI_DEVICE_ID_FIRD01
+#define PCI_DEVICE_ID_FIRD01 	0x0d01
+#endif
+
+struct OboeSlot
+{
+  __u16 len;                    /*Tweleve bits of packet length */
+  __u8 unused;
+  __u8 control;                 /*Slot control/status see below */
+  __u32 address;                /*Slot buffer address */
+}
+__attribute__ ((packed));
+
+#define OBOE_NTASKS OBOE_TXRING_OFFSET_IN_SLOTS
+
+struct OboeRing
+{
+  struct OboeSlot rx[OBOE_NTASKS];
+  struct OboeSlot tx[OBOE_NTASKS];
+};
+
+#define OBOE_RING_LEN (sizeof(struct OboeRing))
+
+
+#define OBOE_CTL_TX_HW_OWNS	0x80 /*W/R This slot owned by the hardware */
+#define OBOE_CTL_TX_DISTX_CRC	0x40 /*W Disable CRC generation for [FM]IR */
+#define OBOE_CTL_TX_BAD_CRC     0x20 /*W Generate bad CRC */
+#define OBOE_CTL_TX_SIP		0x10   /*W Generate an SIP after xmittion */
+#define OBOE_CTL_TX_MKUNDER	0x08 /*W Generate an underrun error */
+#define OBOE_CTL_TX_RTCENTX	0x04 /*W Enable receiver and generate TXdone */
+     /*  After this slot is processed        */
+#define OBOE_CTL_TX_UNDER	0x01  /*R Set by hardware to indicate underrun */
+
+
+#define OBOE_CTL_RX_HW_OWNS	0x80 /*W/R This slot owned by hardware */
+#define OBOE_CTL_RX_PHYERR	0x40 /*R Decoder error on receiption */
+#define OBOE_CTL_RX_CRCERR	0x20 /*R CRC error only set for [FM]IR */
+#define OBOE_CTL_RX_LENGTH	0x10 /*R Packet > max Rx length  */
+#define OBOE_CTL_RX_OVER	0x08   /*R set to indicate an overflow */
+#define OBOE_CTL_RX_SIRBAD	0x04 /*R SIR had BOF in packet or ABORT sequence */
+#define OBOE_CTL_RX_RXEOF	0x02  /*R Finished receiving on this slot */
+
+
+struct toshoboe_cb
+{
+  struct net_device *netdev;    /* Yes! we are some kind of netdevice */
+  struct net_device_stats stats;
+  struct tty_driver ttydev;
+
+  struct irlap_cb *irlap;       /* The link layer we are binded to */
+
+  chipio_t io;                  /* IrDA controller information */
+  struct qos_info qos;          /* QoS capabilities for this device */
+
+  __u32 flags;                  /* Interface flags */
+
+  struct pci_dev *pdev;         /*PCI device */
+  int base;                     /*IO base */
+
+
+  int txpending;                /*how many tx's are pending */
+  int txs, rxs;                 /*Which slots are we at  */
+
+  int irdad;                    /*Driver under control of netdev end  */
+  int async;                    /*Driver under control of async end   */
+
+
+  int stopped;                  /*Stopped by some or other APM stuff */
+
+  int filter;                   /*In SIR mode do we want to receive
+                                   frames or byte ranges */
+
+  void *ringbuf;                /*The ring buffer */
+  struct OboeRing *ring;        /*The ring */
+
+  void *tx_bufs[OBOE_RING_MAX_SIZE]; /*The buffers   */
+  void *rx_bufs[OBOE_RING_MAX_SIZE];
+
+
+  int speed;                    /*Current setting of the speed */
+  int new_speed;                /*Set to request a speed change */
+
+/* The spinlock protect critical parts of the driver.
+ *	Locking is done like this :
+ *		spin_lock_irqsave(&self->spinlock, flags);
+ *	Releasing the lock :
+ *		spin_unlock_irqrestore(&self->spinlock, flags);
+ */
+  spinlock_t spinlock;		
+  /* Used for the probe and diagnostics code */
+  int int_rx;
+  int int_tx;
+  int int_txunder;
+  int int_rxover;
+  int int_sip;
+};
+
+
+#endif
diff --git a/drivers/net/irda/ep7211_ir.c b/drivers/net/irda/ep7211_ir.c
new file mode 100644
index 0000000..3189626
--- /dev/null
+++ b/drivers/net/irda/ep7211_ir.c
@@ -0,0 +1,122 @@
+/*
+ * IR port driver for the Cirrus Logic EP7211 processor.
+ *
+ * Copyright 2001, Blue Mug Inc.  All rights reserved.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/tty.h>
+#include <linux/init.h>
+
+#include <net/irda/irda.h>
+#include <net/irda/irda_device.h>
+
+#include <asm/io.h>
+#include <asm/hardware.h>
+
+#define MIN_DELAY 25      /* 15 us, but wait a little more to be sure */
+#define MAX_DELAY 10000   /* 1 ms */
+
+static void ep7211_ir_open(dongle_t *self, struct qos_info *qos);
+static void ep7211_ir_close(dongle_t *self);
+static int  ep7211_ir_change_speed(struct irda_task *task);
+static int  ep7211_ir_reset(struct irda_task *task);
+
+static struct dongle_reg dongle = {
+	.type = IRDA_EP7211_IR,
+	.open = ep7211_ir_open,
+	.close = ep7211_ir_close,
+	.reset = ep7211_ir_reset,
+	.change_speed = ep7211_ir_change_speed,
+	.owner = THIS_MODULE,
+};
+
+static void ep7211_ir_open(dongle_t *self, struct qos_info *qos)
+{
+	unsigned int syscon1, flags;
+
+	save_flags(flags); cli();
+
+	/* Turn on the SIR encoder. */
+	syscon1 = clps_readl(SYSCON1);
+	syscon1 |= SYSCON1_SIREN;
+	clps_writel(syscon1, SYSCON1);
+
+	/* XXX: We should disable modem status interrupts on the first
+		UART (interrupt #14). */
+
+	restore_flags(flags);
+}
+
+static void ep7211_ir_close(dongle_t *self)
+{
+	unsigned int syscon1, flags;
+
+	save_flags(flags); cli();
+
+	/* Turn off the SIR encoder. */
+	syscon1 = clps_readl(SYSCON1);
+	syscon1 &= ~SYSCON1_SIREN;
+	clps_writel(syscon1, SYSCON1);
+
+	/* XXX: If we've disabled the modem status interrupts, we should
+		reset them back to their original state. */
+
+	restore_flags(flags);
+}
+
+/*
+ * Function ep7211_ir_change_speed (task)
+ *
+ *    Change speed of the EP7211 I/R port. We don't really have to do anything
+ *    for the EP7211 as long as the rate is being changed at the serial port
+ *    level.
+ */
+static int ep7211_ir_change_speed(struct irda_task *task)
+{
+	irda_task_next_state(task, IRDA_TASK_DONE);
+	return 0;
+}
+
+/*
+ * Function ep7211_ir_reset (task)
+ *
+ *      Reset the EP7211 I/R. We don't really have to do anything.
+ *
+ */
+static int ep7211_ir_reset(struct irda_task *task)
+{
+	irda_task_next_state(task, IRDA_TASK_DONE);
+	return 0;
+}
+
+/*
+ * Function ep7211_ir_init(void)
+ *
+ *    Initialize EP7211 I/R module
+ *
+ */
+static int __init ep7211_ir_init(void)
+{
+	return irda_device_register_dongle(&dongle);
+}
+
+/*
+ * Function ep7211_ir_cleanup(void)
+ *
+ *    Cleanup EP7211 I/R module
+ *
+ */
+static void __exit ep7211_ir_cleanup(void)
+{
+	irda_device_unregister_dongle(&dongle);
+}
+
+MODULE_AUTHOR("Jon McClintock <jonm@bluemug.com>");
+MODULE_DESCRIPTION("EP7211 I/R driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("irda-dongle-8"); /* IRDA_EP7211_IR */
+		
+module_init(ep7211_ir_init);
+module_exit(ep7211_ir_cleanup);
diff --git a/drivers/net/irda/esi-sir.c b/drivers/net/irda/esi-sir.c
new file mode 100644
index 0000000..a908df7
--- /dev/null
+++ b/drivers/net/irda/esi-sir.c
@@ -0,0 +1,159 @@
+/*********************************************************************
+ *                
+ * 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;
+
+	/* Power up and set dongle to 9600 baud */
+	sirdev_set_dtr_rts(dev, FALSE, TRUE);
+
+	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);
+
+	/* irda thread waits 50 msec for power settling */
+
+	return 0;
+}
+
+static int esi_close(struct sir_dev *dev)
+{
+	/* Power off dongle */
+	sirdev_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
+ * Apparently (see old esi-driver) no delays are needed here...
+ *
+ */
+static int esi_change_speed(struct sir_dev *dev, unsigned speed)
+{
+	int ret = 0;
+	int dtr, rts;
+	
+	switch (speed) {
+	case 19200:
+		dtr = TRUE;
+		rts = FALSE;
+		break;
+	case 115200:
+		dtr = rts = TRUE;
+		break;
+	default:
+		ret = -EINVAL;
+		speed = 9600;
+		/* fall through */
+	case 9600:
+		dtr = FALSE;
+		rts = TRUE;
+		break;
+	}
+
+	/* Change speed of dongle */
+	sirdev_set_dtr_rts(dev, dtr, rts);
+	dev->speed = speed;
+
+	return ret;
+}
+
+/*
+ * Function esi_reset (task)
+ *
+ *    Reset dongle;
+ *
+ */
+static int esi_reset(struct sir_dev *dev)
+{
+	sirdev_set_dtr_rts(dev, FALSE, FALSE);
+
+	/* Hm, the old esi-driver left the dongle unpowered relying on
+	 * the following speed change to repower. This might work for
+	 * the esi because we only need the modem lines. However, now the
+	 * general rule is reset must bring the dongle to some working
+	 * well-known state because speed change might write to registers.
+	 * The old esi-driver didn't any delay here - let's hope it' fine.
+	 */
+
+	sirdev_set_dtr_rts(dev, FALSE, TRUE);
+	dev->speed = 9600;
+
+	return 0;
+}
+
+MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>");
+MODULE_DESCRIPTION("Extended Systems JetEye PC dongle driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("irda-dongle-1"); /* IRDA_ESI_DONGLE */
+
+module_init(esi_sir_init);
+module_exit(esi_sir_cleanup);
+
diff --git a/drivers/net/irda/esi.c b/drivers/net/irda/esi.c
new file mode 100644
index 0000000..d3a61af
--- /dev/null
+++ b/drivers/net/irda/esi.c
@@ -0,0 +1,149 @@
+/*********************************************************************
+ *                
+ * Filename:      esi.c
+ * Version:       1.5
+ * 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:   Fri Dec 17 09:14:04 1999
+ * Modified by:   Dag Brattli <dagb@cs.uit.no>
+ * 
+ *     Copyright (c) 1999 Dag Brattli, <dagb@cs.uit.no>,
+ *     Copyright (c) 1998 Thomas Davis, <ratbert@radiks.net>,
+ *     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/tty.h>
+#include <linux/init.h>
+
+#include <net/irda/irda.h>
+#include <net/irda/irda_device.h>
+
+static void esi_open(dongle_t *self, struct qos_info *qos);
+static void esi_close(dongle_t *self);
+static int  esi_change_speed(struct irda_task *task);
+static int  esi_reset(struct irda_task *task);
+
+static struct dongle_reg dongle = {
+	.type = IRDA_ESI_DONGLE,
+	.open = esi_open,
+	.close = esi_close,
+	.reset = esi_reset,
+	.change_speed = esi_change_speed,
+	.owner = THIS_MODULE,
+};
+
+static int __init esi_init(void)
+{
+	return irda_device_register_dongle(&dongle);
+}
+
+static void __exit esi_cleanup(void)
+{
+	irda_device_unregister_dongle(&dongle);
+}
+
+static void esi_open(dongle_t *self, struct qos_info *qos)
+{
+	qos->baud_rate.bits &= IR_9600|IR_19200|IR_115200;
+	qos->min_turn_time.bits = 0x01; /* Needs at least 10 ms */
+}
+
+static void esi_close(dongle_t *dongle)
+{		
+	/* Power off dongle */
+	dongle->set_dtr_rts(dongle->dev, FALSE, FALSE);
+}
+
+/*
+ * Function esi_change_speed (task)
+ *
+ *    Set the speed for the Extended Systems JetEye PC ESI-9680 type dongle
+ *
+ */
+static int esi_change_speed(struct irda_task *task)
+{
+	dongle_t *self = (dongle_t *) task->instance;
+	__u32 speed = (__u32) task->param;
+	int dtr, rts;
+	
+	switch (speed) {
+	case 19200:
+		dtr = TRUE;
+		rts = FALSE;
+		break;
+	case 115200:
+		dtr = rts = TRUE;
+		break;
+	case 9600:
+	default:
+		dtr = FALSE;
+		rts = TRUE;
+		break;
+	}
+
+	/* Change speed of dongle */
+	self->set_dtr_rts(self->dev, dtr, rts);
+	self->speed = speed;
+
+	irda_task_next_state(task, IRDA_TASK_DONE);
+
+	return 0;
+}
+
+/*
+ * Function esi_reset (task)
+ *
+ *    Reset dongle;
+ *
+ */
+static int esi_reset(struct irda_task *task)
+{
+	dongle_t *self = (dongle_t *) task->instance;
+	
+	self->set_dtr_rts(self->dev, FALSE, FALSE);
+	irda_task_next_state(task, IRDA_TASK_DONE);
+
+	return 0;
+}
+
+MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>");
+MODULE_DESCRIPTION("Extended Systems JetEye PC dongle driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("irda-dongle-1"); /* IRDA_ESI_DONGLE */
+
+/*
+ * Function init_module (void)
+ *
+ *    Initialize ESI module
+ *
+ */
+module_init(esi_init);
+
+/*
+ * Function cleanup_module (void)
+ *
+ *    Cleanup ESI module
+ *
+ */
+module_exit(esi_cleanup);
+
diff --git a/drivers/net/irda/girbil-sir.c b/drivers/net/irda/girbil-sir.c
new file mode 100644
index 0000000..0d2fe87f
--- /dev/null
+++ b/drivers/net/irda/girbil-sir.c
@@ -0,0 +1,258 @@
+/*********************************************************************
+ *
+ * Filename:      girbil.c
+ * Version:       1.2
+ * Description:   Implementation for the Greenwich GIrBIL dongle
+ * Status:        Experimental.
+ * Author:        Dag Brattli <dagb@cs.uit.no>
+ * Created at:    Sat Feb  6 21:02:33 1999
+ * Modified at:   Fri Dec 17 09:13:20 1999
+ * Modified by:   Dag Brattli <dagb@cs.uit.no>
+ *
+ *     Copyright (c) 1999 Dag Brattli, 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"
+
+static int girbil_reset(struct sir_dev *dev);
+static int girbil_open(struct sir_dev *dev);
+static int girbil_close(struct sir_dev *dev);
+static int girbil_change_speed(struct sir_dev *dev, unsigned speed);
+
+/* Control register 1 */
+#define GIRBIL_TXEN    0x01 /* Enable transmitter */
+#define GIRBIL_RXEN    0x02 /* Enable receiver */
+#define GIRBIL_ECAN    0x04 /* Cancel self emmited data */
+#define GIRBIL_ECHO    0x08 /* Echo control characters */
+
+/* LED Current Register (0x2) */
+#define GIRBIL_HIGH    0x20
+#define GIRBIL_MEDIUM  0x21
+#define GIRBIL_LOW     0x22
+
+/* Baud register (0x3) */
+#define GIRBIL_2400    0x30
+#define GIRBIL_4800    0x31
+#define GIRBIL_9600    0x32
+#define GIRBIL_19200   0x33
+#define GIRBIL_38400   0x34
+#define GIRBIL_57600   0x35
+#define GIRBIL_115200  0x36
+
+/* Mode register (0x4) */
+#define GIRBIL_IRDA    0x40
+#define GIRBIL_ASK     0x41
+
+/* Control register 2 (0x5) */
+#define GIRBIL_LOAD    0x51 /* Load the new baud rate value */
+
+static struct dongle_driver girbil = {
+	.owner		= THIS_MODULE,
+	.driver_name	= "Greenwich GIrBIL",
+	.type		= IRDA_GIRBIL_DONGLE,
+	.open		= girbil_open,
+	.close		= girbil_close,
+	.reset		= girbil_reset,
+	.set_speed	= girbil_change_speed,
+};
+
+static int __init girbil_sir_init(void)
+{
+	return irda_register_dongle(&girbil);
+}
+
+static void __exit girbil_sir_cleanup(void)
+{
+	irda_unregister_dongle(&girbil);
+}
+
+static int girbil_open(struct sir_dev *dev)
+{
+	struct qos_info *qos = &dev->qos;
+
+	IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
+
+	/* Power on dongle */
+	sirdev_set_dtr_rts(dev, TRUE, TRUE);
+
+	qos->baud_rate.bits &= IR_9600|IR_19200|IR_38400|IR_57600|IR_115200;
+	qos->min_turn_time.bits = 0x03;
+	irda_qos_bits_to_value(qos);
+
+	/* irda thread waits 50 msec for power settling */
+
+	return 0;
+}
+
+static int girbil_close(struct sir_dev *dev)
+{
+	IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
+
+	/* Power off dongle */
+	sirdev_set_dtr_rts(dev, FALSE, FALSE);
+
+	return 0;
+}
+
+/*
+ * Function girbil_change_speed (dev, speed)
+ *
+ *    Set the speed for the Girbil type dongle.
+ *
+ */
+
+#define GIRBIL_STATE_WAIT_SPEED	(SIRDEV_STATE_DONGLE_SPEED + 1)
+
+static int girbil_change_speed(struct sir_dev *dev, unsigned speed)
+{
+	unsigned state = dev->fsm.substate;
+	unsigned delay = 0;
+	u8 control[2];
+	static int ret = 0;
+
+	IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
+
+	/* dongle alread reset - port and dongle at default speed */
+
+	switch(state) {
+
+	case SIRDEV_STATE_DONGLE_SPEED:
+
+		/* Set DTR and Clear RTS to enter command mode */
+		sirdev_set_dtr_rts(dev, FALSE, TRUE);
+
+		udelay(25);		/* better wait a little while */
+
+		ret = 0;
+		switch (speed) {
+		default:
+			ret = -EINVAL;
+			/* fall through */
+		case 9600:
+			control[0] = GIRBIL_9600;
+			break;
+		case 19200:
+			control[0] = GIRBIL_19200;
+			break;
+		case 34800:
+			control[0] = GIRBIL_38400;
+			break;
+		case 57600:
+			control[0] = GIRBIL_57600;
+			break;
+		case 115200:
+			control[0] = GIRBIL_115200;
+			break;
+		}
+		control[1] = GIRBIL_LOAD;
+	
+		/* Write control bytes */
+		sirdev_raw_write(dev, control, 2);
+
+		dev->speed = speed;
+
+		state = GIRBIL_STATE_WAIT_SPEED;
+		delay = 100;
+		break;
+
+	case GIRBIL_STATE_WAIT_SPEED:
+		/* Go back to normal mode */
+		sirdev_set_dtr_rts(dev, TRUE, TRUE);
+
+		udelay(25);		/* better wait a little while */
+		break;
+
+	default:
+		IRDA_ERROR("%s - undefined state %d\n", __FUNCTION__, state);
+		ret = -EINVAL;
+		break;
+	}
+	dev->fsm.substate = state;
+	return (delay > 0) ? delay : ret;
+}
+
+/*
+ * Function girbil_reset (driver)
+ *
+ *      This function resets the girbil dongle.
+ *
+ *      Algorithm:
+ *    	  0. set RTS, and wait at least 5 ms
+ *        1. clear RTS
+ */
+
+
+#define GIRBIL_STATE_WAIT1_RESET	(SIRDEV_STATE_DONGLE_RESET + 1)
+#define GIRBIL_STATE_WAIT2_RESET	(SIRDEV_STATE_DONGLE_RESET + 2)
+#define GIRBIL_STATE_WAIT3_RESET	(SIRDEV_STATE_DONGLE_RESET + 3)
+
+static int girbil_reset(struct sir_dev *dev)
+{
+	unsigned state = dev->fsm.substate;
+	unsigned delay = 0;
+	u8 control = GIRBIL_TXEN | GIRBIL_RXEN;
+	int ret = 0;
+
+	IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
+
+	switch (state) {
+	case SIRDEV_STATE_DONGLE_RESET:
+		/* Reset dongle */
+		sirdev_set_dtr_rts(dev, TRUE, FALSE);
+		/* Sleep at least 5 ms */
+		delay = 20;
+		state = GIRBIL_STATE_WAIT1_RESET;
+		break;
+
+	case GIRBIL_STATE_WAIT1_RESET:
+		/* Set DTR and clear RTS to enter command mode */
+		sirdev_set_dtr_rts(dev, FALSE, TRUE);
+		delay = 20;
+		state = GIRBIL_STATE_WAIT2_RESET;
+		break;
+
+	case GIRBIL_STATE_WAIT2_RESET:
+		/* Write control byte */
+		sirdev_raw_write(dev, &control, 1);
+		delay = 20;
+		state = GIRBIL_STATE_WAIT3_RESET;
+		break;
+
+	case GIRBIL_STATE_WAIT3_RESET:
+		/* Go back to normal mode */
+		sirdev_set_dtr_rts(dev, TRUE, TRUE);
+		dev->speed = 9600;
+		break;
+
+	default:
+		IRDA_ERROR("%s(), undefined state %d\n", __FUNCTION__, state);
+		ret = -1;
+		break;
+	}
+	dev->fsm.substate = state;
+	return (delay > 0) ? delay : ret;
+}
+
+MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>");
+MODULE_DESCRIPTION("Greenwich GIrBIL dongle driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("irda-dongle-4"); /* IRDA_GIRBIL_DONGLE */
+
+module_init(girbil_sir_init);
+module_exit(girbil_sir_cleanup);
diff --git a/drivers/net/irda/girbil.c b/drivers/net/irda/girbil.c
new file mode 100644
index 0000000..248aeb0c
--- /dev/null
+++ b/drivers/net/irda/girbil.c
@@ -0,0 +1,250 @@
+/*********************************************************************
+ *                
+ * Filename:      girbil.c
+ * Version:       1.2
+ * Description:   Implementation for the Greenwich GIrBIL dongle
+ * Status:        Experimental.
+ * Author:        Dag Brattli <dagb@cs.uit.no>
+ * Created at:    Sat Feb  6 21:02:33 1999
+ * Modified at:   Fri Dec 17 09:13:20 1999
+ * Modified by:   Dag Brattli <dagb@cs.uit.no>
+ * 
+ *     Copyright (c) 1999 Dag Brattli, 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/tty.h>
+#include <linux/init.h>
+
+#include <net/irda/irda.h>
+#include <net/irda/irda_device.h>
+
+static int  girbil_reset(struct irda_task *task);
+static void girbil_open(dongle_t *self, struct qos_info *qos);
+static void girbil_close(dongle_t *self);
+static int  girbil_change_speed(struct irda_task *task);
+
+/* Control register 1 */
+#define GIRBIL_TXEN    0x01 /* Enable transmitter */
+#define GIRBIL_RXEN    0x02 /* Enable receiver */
+#define GIRBIL_ECAN    0x04 /* Cancel self emmited data */
+#define GIRBIL_ECHO    0x08 /* Echo control characters */
+
+/* LED Current Register (0x2) */
+#define GIRBIL_HIGH    0x20
+#define GIRBIL_MEDIUM  0x21
+#define GIRBIL_LOW     0x22
+
+/* Baud register (0x3) */
+#define GIRBIL_2400    0x30
+#define GIRBIL_4800    0x31	
+#define GIRBIL_9600    0x32
+#define GIRBIL_19200   0x33
+#define GIRBIL_38400   0x34	
+#define GIRBIL_57600   0x35	
+#define GIRBIL_115200  0x36
+
+/* Mode register (0x4) */
+#define GIRBIL_IRDA    0x40
+#define GIRBIL_ASK     0x41
+
+/* Control register 2 (0x5) */
+#define GIRBIL_LOAD    0x51 /* Load the new baud rate value */
+
+static struct dongle_reg dongle = {
+	.type = IRDA_GIRBIL_DONGLE,
+	.open = girbil_open,
+	.close = girbil_close,
+	.reset = girbil_reset,
+	.change_speed = girbil_change_speed,
+	.owner = THIS_MODULE,
+};
+
+static int __init girbil_init(void)
+{
+	return irda_device_register_dongle(&dongle);
+}
+
+static void __exit girbil_cleanup(void)
+{
+	irda_device_unregister_dongle(&dongle);
+}
+
+static void girbil_open(dongle_t *self, struct qos_info *qos)
+{
+	qos->baud_rate.bits &= IR_9600|IR_19200|IR_38400|IR_57600|IR_115200;
+	qos->min_turn_time.bits = 0x03;
+}
+
+static void girbil_close(dongle_t *self)
+{
+	/* Power off dongle */
+	self->set_dtr_rts(self->dev, FALSE, FALSE);
+}
+
+/*
+ * Function girbil_change_speed (dev, speed)
+ *
+ *    Set the speed for the Girbil type dongle.
+ *
+ */
+static int girbil_change_speed(struct irda_task *task)
+{
+	dongle_t *self = (dongle_t *) task->instance;
+	__u32 speed = (__u32) task->param;
+	__u8 control[2];
+	int ret = 0;
+
+	self->speed_task = task;
+
+	switch (task->state) {
+	case IRDA_TASK_INIT:
+		/* Need to reset the dongle and go to 9600 bps before
+                   programming */
+		if (irda_task_execute(self, girbil_reset, NULL, task, 
+				      (void *) speed))
+		{
+			/* Dongle need more time to reset */
+			irda_task_next_state(task, IRDA_TASK_CHILD_WAIT);
+
+			/* Give reset 1 sec to finish */
+			ret = msecs_to_jiffies(1000);
+		}
+		break;
+	case IRDA_TASK_CHILD_WAIT:
+		IRDA_WARNING("%s(), resetting dongle timed out!\n",
+			     __FUNCTION__);
+		ret = -1;
+		break;
+	case IRDA_TASK_CHILD_DONE:
+		/* Set DTR and Clear RTS to enter command mode */
+		self->set_dtr_rts(self->dev, FALSE, TRUE);
+
+		switch (speed) {
+		case 9600:
+		default:
+			control[0] = GIRBIL_9600;
+			break;
+		case 19200:
+			control[0] = GIRBIL_19200;
+			break;
+		case 34800:
+			control[0] = GIRBIL_38400;
+			break;
+		case 57600:
+			control[0] = GIRBIL_57600;
+			break;
+		case 115200:
+			control[0] = GIRBIL_115200;
+			break;
+		}
+		control[1] = GIRBIL_LOAD;
+		
+		/* Write control bytes */
+		self->write(self->dev, control, 2);
+		irda_task_next_state(task, IRDA_TASK_WAIT);
+		ret = msecs_to_jiffies(100);
+		break;
+	case IRDA_TASK_WAIT:
+		/* Go back to normal mode */
+		self->set_dtr_rts(self->dev, TRUE, TRUE);
+		irda_task_next_state(task, IRDA_TASK_DONE);
+		self->speed_task = NULL;
+		break;
+	default:
+		IRDA_ERROR("%s(), unknown state %d\n",
+			   __FUNCTION__, task->state);
+		irda_task_next_state(task, IRDA_TASK_DONE);
+		self->speed_task = NULL;
+		ret = -1;
+		break;
+	}
+	return ret;
+}
+
+/*
+ * Function girbil_reset (driver)
+ *
+ *      This function resets the girbil dongle.
+ *
+ *      Algorithm:
+ *    	  0. set RTS, and wait at least 5 ms 
+ *        1. clear RTS 
+ */
+static int girbil_reset(struct irda_task *task)
+{
+	dongle_t *self = (dongle_t *) task->instance;
+	__u8 control = GIRBIL_TXEN | GIRBIL_RXEN;
+	int ret = 0;
+
+	self->reset_task = task;
+
+	switch (task->state) {
+	case IRDA_TASK_INIT:
+		/* Reset dongle */
+		self->set_dtr_rts(self->dev, TRUE, FALSE);
+		irda_task_next_state(task, IRDA_TASK_WAIT1);
+		/* Sleep at least 5 ms */
+		ret = msecs_to_jiffies(20);
+		break;
+	case IRDA_TASK_WAIT1:
+		/* Set DTR and clear RTS to enter command mode */
+		self->set_dtr_rts(self->dev, FALSE, TRUE);
+		irda_task_next_state(task, IRDA_TASK_WAIT2);
+		ret = msecs_to_jiffies(20);
+		break;
+	case IRDA_TASK_WAIT2:
+		/* Write control byte */
+		self->write(self->dev, &control, 1);
+		irda_task_next_state(task, IRDA_TASK_WAIT3);
+		ret = msecs_to_jiffies(20);
+		break;
+	case IRDA_TASK_WAIT3:
+		/* Go back to normal mode */
+		self->set_dtr_rts(self->dev, TRUE, TRUE);
+		irda_task_next_state(task, IRDA_TASK_DONE);
+		self->reset_task = NULL;
+		break;
+	default:
+		IRDA_ERROR("%s(), unknown state %d\n",
+			   __FUNCTION__, task->state);
+		irda_task_next_state(task, IRDA_TASK_DONE);
+		self->reset_task = NULL;
+		ret = -1;
+		break;
+	}
+	return ret;
+}
+
+MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>");
+MODULE_DESCRIPTION("Greenwich GIrBIL dongle driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("irda-dongle-4"); /* IRDA_GIRBIL_DONGLE */
+	
+/*
+ * Function init_module (void)
+ *
+ *    Initialize Girbil module
+ *
+ */
+module_init(girbil_init);
+
+/*
+ * Function cleanup_module (void)
+ *
+ *    Cleanup Girbil module
+ *
+ */
+module_exit(girbil_cleanup);
+
diff --git a/drivers/net/irda/irda-usb.c b/drivers/net/irda/irda-usb.c
new file mode 100644
index 0000000..46e0022
--- /dev/null
+++ b/drivers/net/irda/irda-usb.c
@@ -0,0 +1,1602 @@
+/*****************************************************************************
+ *
+ * Filename:      irda-usb.c
+ * Version:       0.9b
+ * Description:   IrDA-USB Driver
+ * Status:        Experimental 
+ * Author:        Dag Brattli <dag@brattli.net>
+ *
+ *	Copyright (C) 2000, Roman Weissgaerber <weissg@vienna.at>
+ *      Copyright (C) 2001, Dag Brattli <dag@brattli.net>
+ *      Copyright (C) 2001, Jean Tourrilhes <jt@hpl.hp.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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *****************************************************************************/
+
+/*
+ *			    IMPORTANT NOTE
+ *			    --------------
+ *
+ * As of kernel 2.5.20, this is the state of compliance and testing of
+ * this driver (irda-usb) with regards to the USB low level drivers...
+ *
+ * This driver has been tested SUCCESSFULLY with the following drivers :
+ *	o usb-uhci-hcd	(For Intel/Via USB controllers)
+ *	o uhci-hcd	(Alternate/JE driver for Intel/Via USB controllers)
+ *	o ohci-hcd	(For other USB controllers)
+ *
+ * This driver has NOT been tested with the following drivers :
+ *	o ehci-hcd	(USB 2.0 controllers)
+ *
+ * Note that all HCD drivers do URB_ZERO_PACKET and timeout properly,
+ * so we don't have to worry about that anymore.
+ * One common problem is the failure to set the address on the dongle,
+ * but this happens before the driver gets loaded...
+ *
+ * Jean II
+ */
+
+/*------------------------------------------------------------------*/
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/slab.h>
+#include <linux/rtnetlink.h>
+#include <linux/usb.h>
+
+#include "irda-usb.h"
+
+/*------------------------------------------------------------------*/
+
+static int qos_mtt_bits = 0;
+
+/* These are the currently known IrDA USB dongles. Add new dongles here */
+static struct usb_device_id dongles[] = {
+	/* ACTiSYS Corp.,  ACT-IR2000U FIR-USB Adapter */
+	{ USB_DEVICE(0x9c4, 0x011), .driver_info = IUC_SPEED_BUG | IUC_NO_WINDOW },
+	/* Look like ACTiSYS, Report : IBM Corp., IBM UltraPort IrDA */
+	{ USB_DEVICE(0x4428, 0x012), .driver_info = IUC_SPEED_BUG | IUC_NO_WINDOW },
+	/* KC Technology Inc.,  KC-180 USB IrDA Device */
+	{ USB_DEVICE(0x50f, 0x180), .driver_info = IUC_SPEED_BUG | IUC_NO_WINDOW },
+	/* Extended Systems, Inc.,  XTNDAccess IrDA USB (ESI-9685) */
+	{ USB_DEVICE(0x8e9, 0x100), .driver_info = IUC_SPEED_BUG | IUC_NO_WINDOW },
+	{ .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS |
+	               USB_DEVICE_ID_MATCH_INT_SUBCLASS,
+	  .bInterfaceClass = USB_CLASS_APP_SPEC,
+	  .bInterfaceSubClass = USB_CLASS_IRDA,
+	  .driver_info = IUC_DEFAULT, },
+	{ }, /* The end */
+};
+
+/*
+ * Important note :
+ * Devices based on the SigmaTel chipset (0x66f, 0x4200) are not designed
+ * using the "USB-IrDA specification" (yes, there exist such a thing), and
+ * therefore not supported by this driver (don't add them above).
+ * There is a Linux driver, stir4200, that support those USB devices.
+ * Jean II
+ */
+
+MODULE_DEVICE_TABLE(usb, dongles);
+
+/*------------------------------------------------------------------*/
+
+static struct irda_class_desc *irda_usb_find_class_desc(struct usb_interface *intf);
+static void irda_usb_disconnect(struct usb_interface *intf);
+static void irda_usb_change_speed_xbofs(struct irda_usb_cb *self);
+static int irda_usb_hard_xmit(struct sk_buff *skb, struct net_device *dev);
+static int irda_usb_open(struct irda_usb_cb *self);
+static void irda_usb_close(struct irda_usb_cb *self);
+static void speed_bulk_callback(struct urb *urb, struct pt_regs *regs);
+static void write_bulk_callback(struct urb *urb, struct pt_regs *regs);
+static void irda_usb_receive(struct urb *urb, struct pt_regs *regs);
+static int irda_usb_net_open(struct net_device *dev);
+static int irda_usb_net_close(struct net_device *dev);
+static int irda_usb_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+static void irda_usb_net_timeout(struct net_device *dev);
+static struct net_device_stats *irda_usb_net_get_stats(struct net_device *dev);
+
+/************************ TRANSMIT ROUTINES ************************/
+/*
+ * Receive packets from the IrDA stack and send them on the USB pipe.
+ * Handle speed change, timeout and lot's of ugliness...
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * Function irda_usb_build_header(self, skb, header)
+ *
+ *   Builds USB-IrDA outbound header
+ *
+ * When we send an IrDA frame over an USB pipe, we add to it a 1 byte
+ * header. This function create this header with the proper values.
+ *
+ * Important note : the USB-IrDA spec 1.0 say very clearly in chapter 5.4.2.2
+ * that the setting of the link speed and xbof number in this outbound header
+ * should be applied *AFTER* the frame has been sent.
+ * Unfortunately, some devices are not compliant with that... It seems that
+ * reading the spec is far too difficult...
+ * Jean II
+ */
+static void irda_usb_build_header(struct irda_usb_cb *self,
+				  __u8 *header,
+				  int	force)
+{
+	/* Set the negotiated link speed */
+	if (self->new_speed != -1) {
+		/* Hum... Ugly hack :-(
+		 * Some device are not compliant with the spec and change
+		 * parameters *before* sending the frame. - Jean II
+		 */
+		if ((self->capability & IUC_SPEED_BUG) &&
+		    (!force) && (self->speed != -1)) {
+			/* No speed and xbofs change here
+			 * (we'll do it later in the write callback) */
+			IRDA_DEBUG(2, "%s(), not changing speed yet\n", __FUNCTION__);
+			*header = 0;
+			return;
+		}
+
+		IRDA_DEBUG(2, "%s(), changing speed to %d\n", __FUNCTION__, self->new_speed);
+		self->speed = self->new_speed;
+		/* We will do ` self->new_speed = -1; ' in the completion
+		 * handler just in case the current URB fail - Jean II */
+
+		switch (self->speed) {
+		case 2400:
+		        *header = SPEED_2400;
+			break;
+		default:
+		case 9600:
+			*header = SPEED_9600;
+			break;
+		case 19200:
+			*header = SPEED_19200;
+			break;
+		case 38400:
+			*header = SPEED_38400;
+			break;
+		case 57600:
+		        *header = SPEED_57600;
+			break;
+		case 115200:
+		        *header = SPEED_115200;
+			break;
+		case 576000:
+		        *header = SPEED_576000;
+			break;
+		case 1152000:
+		        *header = SPEED_1152000;
+			break;
+		case 4000000:
+		        *header = SPEED_4000000;
+			self->new_xbofs = 0;
+			break;
+		}
+	} else
+		/* No change */
+		*header = 0;
+	
+	/* Set the negotiated additional XBOFS */
+	if (self->new_xbofs != -1) {
+		IRDA_DEBUG(2, "%s(), changing xbofs to %d\n", __FUNCTION__, self->new_xbofs);
+		self->xbofs = self->new_xbofs;
+		/* We will do ` self->new_xbofs = -1; ' in the completion
+		 * handler just in case the current URB fail - Jean II */
+
+		switch (self->xbofs) {
+		case 48:
+			*header |= 0x10;
+			break;
+		case 28:
+		case 24:	/* USB spec 1.0 says 24 */
+			*header |= 0x20;
+			break;
+		default:
+		case 12:
+			*header |= 0x30;
+			break;
+		case 5: /* Bug in IrLAP spec? (should be 6) */
+		case 6:
+			*header |= 0x40;
+			break;
+		case 3:
+			*header |= 0x50;
+			break;
+		case 2:
+			*header |= 0x60;
+			break;
+		case 1:
+			*header |= 0x70;
+			break;
+		case 0:
+			*header |= 0x80;
+			break;
+		}
+	}
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Send a command to change the speed of the dongle
+ * Need to be called with spinlock on.
+ */
+static void irda_usb_change_speed_xbofs(struct irda_usb_cb *self)
+{
+	__u8 *frame;
+	struct urb *urb;
+	int ret;
+
+	IRDA_DEBUG(2, "%s(), speed=%d, xbofs=%d\n", __FUNCTION__,
+		   self->new_speed, self->new_xbofs);
+
+	/* Grab the speed URB */
+	urb = self->speed_urb;
+	if (urb->status != 0) {
+		IRDA_WARNING("%s(), URB still in use!\n", __FUNCTION__);
+		return;
+	}
+
+	/* Allocate the fake frame */
+	frame = self->speed_buff;
+
+	/* Set the new speed and xbofs in this fake frame */
+	irda_usb_build_header(self, frame, 1);
+
+	/* Submit the 0 length IrDA frame to trigger new speed settings */
+        usb_fill_bulk_urb(urb, self->usbdev,
+		      usb_sndbulkpipe(self->usbdev, self->bulk_out_ep),
+                      frame, IRDA_USB_SPEED_MTU,
+                      speed_bulk_callback, self);
+	urb->transfer_buffer_length = USB_IRDA_HEADER;
+	urb->transfer_flags = URB_ASYNC_UNLINK;
+
+	/* Irq disabled -> GFP_ATOMIC */
+	if ((ret = usb_submit_urb(urb, GFP_ATOMIC))) {
+		IRDA_WARNING("%s(), failed Speed URB\n", __FUNCTION__);
+	}
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Speed URB callback
+ * Now, we can only get called for the speed URB.
+ */
+static void speed_bulk_callback(struct urb *urb, struct pt_regs *regs)
+{
+	struct irda_usb_cb *self = urb->context;
+	
+	IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
+
+	/* We should always have a context */
+	IRDA_ASSERT(self != NULL, return;);
+	/* We should always be called for the speed URB */
+	IRDA_ASSERT(urb == self->speed_urb, return;);
+
+	/* Check for timeout and other USB nasties */
+	if (urb->status != 0) {
+		/* I get a lot of -ECONNABORTED = -103 here - Jean II */
+		IRDA_DEBUG(0, "%s(), URB complete status %d, transfer_flags 0x%04X\n", __FUNCTION__, urb->status, urb->transfer_flags);
+
+		/* Don't do anything here, that might confuse the USB layer.
+		 * Instead, we will wait for irda_usb_net_timeout(), the
+		 * network layer watchdog, to fix the situation.
+		 * Jean II */
+		/* A reset of the dongle might be welcomed here - Jean II */
+		return;
+	}
+
+	/* urb is now available */
+	//urb->status = 0; -> tested above
+
+	/* New speed and xbof is now commited in hardware */
+	self->new_speed = -1;
+	self->new_xbofs = -1;
+
+	/* Allow the stack to send more packets */
+	netif_wake_queue(self->netdev);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Send an IrDA frame to the USB dongle (for transmission)
+ */
+static int irda_usb_hard_xmit(struct sk_buff *skb, struct net_device *netdev)
+{
+	struct irda_usb_cb *self = netdev->priv;
+	struct urb *urb = self->tx_urb;
+	unsigned long flags;
+	s32 speed;
+	s16 xbofs;
+	int res, mtt;
+	int	err = 1;	/* Failed */
+
+	IRDA_DEBUG(4, "%s() on %s\n", __FUNCTION__, netdev->name);
+
+	netif_stop_queue(netdev);
+
+	/* Protect us from USB callbacks, net watchdog and else. */
+	spin_lock_irqsave(&self->lock, flags);
+
+	/* Check if the device is still there.
+	 * We need to check self->present under the spinlock because
+	 * of irda_usb_disconnect() is synchronous - Jean II */
+	if (!self->present) {
+		IRDA_DEBUG(0, "%s(), Device is gone...\n", __FUNCTION__);
+		goto drop;
+	}
+
+	/* Check if we need to change the number of xbofs */
+        xbofs = irda_get_next_xbofs(skb);
+        if ((xbofs != self->xbofs) && (xbofs != -1)) {
+		self->new_xbofs = xbofs;
+	}
+
+        /* Check if we need to change the speed */
+	speed = irda_get_next_speed(skb);
+	if ((speed != self->speed) && (speed != -1)) {
+		/* Set the desired speed */
+		self->new_speed = speed;
+
+		/* Check for empty frame */
+		if (!skb->len) {
+			/* IrLAP send us an empty frame to make us change the
+			 * speed. Changing speed with the USB adapter is in
+			 * fact sending an empty frame to the adapter, so we
+			 * could just let the present function do its job.
+			 * However, we would wait for min turn time,
+			 * do an extra memcpy and increment packet counters...
+			 * Jean II */
+			irda_usb_change_speed_xbofs(self);
+			netdev->trans_start = jiffies;
+			/* Will netif_wake_queue() in callback */
+			err = 0;	/* No error */
+			goto drop;
+		}
+	}
+
+	if (urb->status != 0) {
+		IRDA_WARNING("%s(), URB still in use!\n", __FUNCTION__);
+		goto drop;
+	}
+
+	/* Make sure there is room for IrDA-USB header. The actual
+	 * allocation will be done lower in skb_push().
+	 * Also, we don't use directly skb_cow(), because it require
+	 * headroom >= 16, which force unnecessary copies - Jean II */
+	if (skb_headroom(skb) < USB_IRDA_HEADER) {
+		IRDA_DEBUG(0, "%s(), Insuficient skb headroom.\n", __FUNCTION__);
+		if (skb_cow(skb, USB_IRDA_HEADER)) {
+			IRDA_WARNING("%s(), failed skb_cow() !!!\n", __FUNCTION__);
+			goto drop;
+		}
+	}
+
+	/* Change setting for next frame */
+	irda_usb_build_header(self, skb_push(skb, USB_IRDA_HEADER), 0);
+
+	/* FIXME: Make macro out of this one */
+	((struct irda_skb_cb *)skb->cb)->context = self;
+
+        usb_fill_bulk_urb(urb, self->usbdev, 
+		      usb_sndbulkpipe(self->usbdev, self->bulk_out_ep),
+                      skb->data, IRDA_SKB_MAX_MTU,
+                      write_bulk_callback, skb);
+	urb->transfer_buffer_length = skb->len;
+	/* Note : unlink *must* be Asynchronous because of the code in 
+	 * irda_usb_net_timeout() -> call in irq - Jean II */
+	urb->transfer_flags = URB_ASYNC_UNLINK;
+	/* This flag (URB_ZERO_PACKET) indicates that what we send is not
+	 * a continuous stream of data but separate packets.
+	 * In this case, the USB layer will insert an empty USB frame (TD)
+	 * after each of our packets that is exact multiple of the frame size.
+	 * This is how the dongle will detect the end of packet - Jean II */
+	urb->transfer_flags |= URB_ZERO_PACKET;
+
+	/* Generate min turn time. FIXME: can we do better than this? */
+	/* Trying to a turnaround time at this level is trying to measure
+	 * processor clock cycle with a wrist-watch, approximate at best...
+	 *
+	 * What we know is the last time we received a frame over USB.
+	 * Due to latency over USB that depend on the USB load, we don't
+	 * know when this frame was received over IrDA (a few ms before ?)
+	 * Then, same story for our outgoing frame...
+	 *
+	 * In theory, the USB dongle is supposed to handle the turnaround
+	 * by itself (spec 1.0, chater 4, page 6). Who knows ??? That's
+	 * why this code is enabled only for dongles that doesn't meet
+	 * the spec.
+	 * Jean II */
+	if (self->capability & IUC_NO_TURN) {
+		mtt = irda_get_mtt(skb);
+		if (mtt) {
+			int diff;
+			do_gettimeofday(&self->now);
+			diff = self->now.tv_usec - self->stamp.tv_usec;
+#ifdef IU_USB_MIN_RTT
+			/* Factor in USB delays -> Get rid of udelay() that
+			 * would be lost in the noise - Jean II */
+			diff += IU_USB_MIN_RTT;
+#endif /* IU_USB_MIN_RTT */
+			/* If the usec counter did wraparound, the diff will
+			 * go negative (tv_usec is a long), so we need to
+			 * correct it by one second. Jean II */
+			if (diff < 0)
+				diff += 1000000;
+
+		        /* Check if the mtt is larger than the time we have
+			 * already used by all the protocol processing
+			 */
+			if (mtt > diff) {
+				mtt -= diff;
+				if (mtt > 1000)
+					mdelay(mtt/1000);
+				else
+					udelay(mtt);
+			}
+		}
+	}
+	
+	/* Ask USB to send the packet - Irq disabled -> GFP_ATOMIC */
+	if ((res = usb_submit_urb(urb, GFP_ATOMIC))) {
+		IRDA_WARNING("%s(), failed Tx URB\n", __FUNCTION__);
+		self->stats.tx_errors++;
+		/* Let USB recover : We will catch that in the watchdog */
+		/*netif_start_queue(netdev);*/
+	} else {
+		/* Increment packet stats */
+		self->stats.tx_packets++;
+                self->stats.tx_bytes += skb->len;
+		
+		netdev->trans_start = jiffies;
+	}
+	spin_unlock_irqrestore(&self->lock, flags);
+	
+	return 0;
+
+drop:
+	/* Drop silently the skb and exit */
+	dev_kfree_skb(skb);
+	spin_unlock_irqrestore(&self->lock, flags);
+	return err;		/* Usually 1 */
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Note : this function will be called only for tx_urb...
+ */
+static void write_bulk_callback(struct urb *urb, struct pt_regs *regs)
+{
+	unsigned long flags;
+	struct sk_buff *skb = urb->context;
+	struct irda_usb_cb *self = ((struct irda_skb_cb *) skb->cb)->context;
+	
+	IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
+
+	/* We should always have a context */
+	IRDA_ASSERT(self != NULL, return;);
+	/* We should always be called for the speed URB */
+	IRDA_ASSERT(urb == self->tx_urb, return;);
+
+	/* Free up the skb */
+	dev_kfree_skb_any(skb);
+	urb->context = NULL;
+
+	/* Check for timeout and other USB nasties */
+	if (urb->status != 0) {
+		/* I get a lot of -ECONNABORTED = -103 here - Jean II */
+		IRDA_DEBUG(0, "%s(), URB complete status %d, transfer_flags 0x%04X\n", __FUNCTION__, urb->status, urb->transfer_flags);
+
+		/* Don't do anything here, that might confuse the USB layer,
+		 * and we could go in recursion and blow the kernel stack...
+		 * Instead, we will wait for irda_usb_net_timeout(), the
+		 * network layer watchdog, to fix the situation.
+		 * Jean II */
+		/* A reset of the dongle might be welcomed here - Jean II */
+		return;
+	}
+
+	/* urb is now available */
+	//urb->status = 0; -> tested above
+
+	/* Make sure we read self->present properly */
+	spin_lock_irqsave(&self->lock, flags);
+
+	/* If the network is closed, stop everything */
+	if ((!self->netopen) || (!self->present)) {
+		IRDA_DEBUG(0, "%s(), Network is gone...\n", __FUNCTION__);
+		spin_unlock_irqrestore(&self->lock, flags);
+		return;
+	}
+
+	/* If changes to speed or xbofs is pending... */
+	if ((self->new_speed != -1) || (self->new_xbofs != -1)) {
+		if ((self->new_speed != self->speed) ||
+		    (self->new_xbofs != self->xbofs)) {
+			/* We haven't changed speed yet (because of
+			 * IUC_SPEED_BUG), so do it now - Jean II */
+			IRDA_DEBUG(1, "%s(), Changing speed now...\n", __FUNCTION__);
+			irda_usb_change_speed_xbofs(self);
+		} else {
+			/* New speed and xbof is now commited in hardware */
+			self->new_speed = -1;
+			self->new_xbofs = -1;
+			/* Done, waiting for next packet */
+			netif_wake_queue(self->netdev);
+		}
+	} else {
+		/* Otherwise, allow the stack to send more packets */
+		netif_wake_queue(self->netdev);
+	}
+	spin_unlock_irqrestore(&self->lock, flags);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Watchdog timer from the network layer.
+ * After a predetermined timeout, if we don't give confirmation that
+ * the packet has been sent (i.e. no call to netif_wake_queue()),
+ * the network layer will call this function.
+ * Note that URB that we submit have also a timeout. When the URB timeout
+ * expire, the normal URB callback is called (write_bulk_callback()).
+ */
+static void irda_usb_net_timeout(struct net_device *netdev)
+{
+	unsigned long flags;
+	struct irda_usb_cb *self = netdev->priv;
+	struct urb *urb;
+	int	done = 0;	/* If we have made any progress */
+
+	IRDA_DEBUG(0, "%s(), Network layer thinks we timed out!\n", __FUNCTION__);
+	IRDA_ASSERT(self != NULL, return;);
+
+	/* Protect us from USB callbacks, net Tx and else. */
+	spin_lock_irqsave(&self->lock, flags);
+
+	/* self->present *MUST* be read under spinlock */
+	if (!self->present) {
+		IRDA_WARNING("%s(), device not present!\n", __FUNCTION__);
+		netif_stop_queue(netdev);
+		spin_unlock_irqrestore(&self->lock, flags);
+		return;
+	}
+
+	/* Check speed URB */
+	urb = self->speed_urb;
+	if (urb->status != 0) {
+		IRDA_DEBUG(0, "%s: Speed change timed out, urb->status=%d, urb->transfer_flags=0x%04X\n", netdev->name, urb->status, urb->transfer_flags);
+
+		switch (urb->status) {
+		case -EINPROGRESS:
+			usb_unlink_urb(urb);
+			/* Note : above will  *NOT* call netif_wake_queue()
+			 * in completion handler, we will come back here.
+			 * Jean II */
+			done = 1;
+			break;
+		case -ECONNABORTED:		/* -103 */
+		case -ECONNRESET:		/* -104 */
+		case -ETIMEDOUT:		/* -110 */
+		case -ENOENT:			/* -2 (urb unlinked by us)  */
+		default:			/* ??? - Play safe */
+			urb->status = 0;
+			netif_wake_queue(self->netdev);
+			done = 1;
+			break;
+		}
+	}
+
+	/* Check Tx URB */
+	urb = self->tx_urb;
+	if (urb->status != 0) {
+		struct sk_buff *skb = urb->context;
+
+		IRDA_DEBUG(0, "%s: Tx timed out, urb->status=%d, urb->transfer_flags=0x%04X\n", netdev->name, urb->status, urb->transfer_flags);
+
+		/* Increase error count */
+		self->stats.tx_errors++;
+
+#ifdef IU_BUG_KICK_TIMEOUT
+		/* Can't be a bad idea to reset the speed ;-) - Jean II */
+		if(self->new_speed == -1)
+			self->new_speed = self->speed;
+		if(self->new_xbofs == -1)
+			self->new_xbofs = self->xbofs;
+		irda_usb_change_speed_xbofs(self);
+#endif /* IU_BUG_KICK_TIMEOUT */
+
+		switch (urb->status) {
+		case -EINPROGRESS:
+			usb_unlink_urb(urb);
+			/* Note : above will  *NOT* call netif_wake_queue()
+			 * in completion handler, because urb->status will
+			 * be -ENOENT. We will fix that at the next watchdog,
+			 * leaving more time to USB to recover...
+			 * Also, we are in interrupt, so we need to have
+			 * URB_ASYNC_UNLINK to work properly...
+			 * Jean II */
+			done = 1;
+			break;
+		case -ECONNABORTED:		/* -103 */
+		case -ECONNRESET:		/* -104 */
+		case -ETIMEDOUT:		/* -110 */
+		case -ENOENT:			/* -2 (urb unlinked by us)  */
+		default:			/* ??? - Play safe */
+			if(skb != NULL) {
+				dev_kfree_skb_any(skb);
+				urb->context = NULL;
+			}
+			urb->status = 0;
+			netif_wake_queue(self->netdev);
+			done = 1;
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&self->lock, flags);
+
+	/* Maybe we need a reset */
+	/* Note : Some drivers seem to use a usb_set_interface() when they
+	 * need to reset the hardware. Hum...
+	 */
+
+	/* if(done == 0) */
+}
+
+/************************* RECEIVE ROUTINES *************************/
+/*
+ * Receive packets from the USB layer stack and pass them to the IrDA stack.
+ * Try to work around USB failures...
+ */
+
+/*
+ * Note :
+ * Some of you may have noticed that most dongle have an interrupt in pipe
+ * that we don't use. Here is the little secret...
+ * When we hang a Rx URB on the bulk in pipe, it generates some USB traffic
+ * in every USB frame. This is unnecessary overhead.
+ * The interrupt in pipe will generate an event every time a packet is
+ * received. Reading an interrupt pipe adds minimal overhead, but has some
+ * latency (~1ms).
+ * If we are connected (speed != 9600), we want to minimise latency, so
+ * we just always hang the Rx URB and ignore the interrupt.
+ * If we are not connected (speed == 9600), there is usually no Rx traffic,
+ * and we want to minimise the USB overhead. In this case we should wait
+ * on the interrupt pipe and hang the Rx URB only when an interrupt is
+ * received.
+ * Jean II
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * Submit a Rx URB to the USB layer to handle reception of a frame
+ * Mostly called by the completion callback of the previous URB.
+ *
+ * Jean II
+ */
+static void irda_usb_submit(struct irda_usb_cb *self, struct sk_buff *skb, struct urb *urb)
+{
+	struct irda_skb_cb *cb;
+	int ret;
+
+	IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
+
+	/* This should never happen */
+	IRDA_ASSERT(skb != NULL, return;);
+	IRDA_ASSERT(urb != NULL, return;);
+
+	/* Save ourselves in the skb */
+	cb = (struct irda_skb_cb *) skb->cb;
+	cb->context = self;
+
+	/* Reinitialize URB */
+	usb_fill_bulk_urb(urb, self->usbdev, 
+		      usb_rcvbulkpipe(self->usbdev, self->bulk_in_ep), 
+		      skb->data, skb->truesize,
+                      irda_usb_receive, skb);
+	/* Note : unlink *must* be synchronous because of the code in 
+	 * irda_usb_net_close() -> free the skb - Jean II */
+	urb->status = 0;
+
+	/* Can be called from irda_usb_receive (irq handler) -> GFP_ATOMIC */
+	ret = usb_submit_urb(urb, GFP_ATOMIC);
+	if (ret) {
+		/* If this ever happen, we are in deep s***.
+		 * Basically, the Rx path will stop... */
+		IRDA_WARNING("%s(), Failed to submit Rx URB %d\n",
+			     __FUNCTION__, ret);
+	}
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Function irda_usb_receive(urb)
+ *
+ *     Called by the USB subsystem when a frame has been received
+ *
+ */
+static void irda_usb_receive(struct urb *urb, struct pt_regs *regs)
+{
+	struct sk_buff *skb = (struct sk_buff *) urb->context;
+	struct irda_usb_cb *self; 
+	struct irda_skb_cb *cb;
+	struct sk_buff *newskb;
+	struct sk_buff *dataskb;
+	int		docopy;
+
+	IRDA_DEBUG(2, "%s(), len=%d\n", __FUNCTION__, urb->actual_length);
+	
+	/* Find ourselves */
+	cb = (struct irda_skb_cb *) skb->cb;
+	IRDA_ASSERT(cb != NULL, return;);
+	self = (struct irda_usb_cb *) cb->context;
+	IRDA_ASSERT(self != NULL, return;);
+
+	/* If the network is closed or the device gone, stop everything */
+	if ((!self->netopen) || (!self->present)) {
+		IRDA_DEBUG(0, "%s(), Network is gone!\n", __FUNCTION__);
+		/* Don't re-submit the URB : will stall the Rx path */
+		return;
+	}
+	
+	/* Check the status */
+	if (urb->status != 0) {
+		switch (urb->status) {
+		case -EILSEQ:
+			self->stats.rx_errors++;
+			self->stats.rx_crc_errors++;	
+			break;
+		case -ECONNRESET:		/* -104 */
+			IRDA_DEBUG(0, "%s(), Connection Reset (-104), transfer_flags 0x%04X \n", __FUNCTION__, urb->transfer_flags);
+			/* uhci_cleanup_unlink() is going to kill the Rx
+			 * URB just after we return. No problem, at this
+			 * point the URB will be idle ;-) - Jean II */
+			break;
+		default:
+			IRDA_DEBUG(0, "%s(), RX status %d,transfer_flags 0x%04X \n", __FUNCTION__, urb->status, urb->transfer_flags);
+			break;
+		}
+		goto done;
+	}
+	
+	/* Check for empty frames */
+	if (urb->actual_length <= USB_IRDA_HEADER) {
+		IRDA_WARNING("%s(), empty frame!\n", __FUNCTION__);
+		goto done;
+	}
+
+	/*  
+	 * Remember the time we received this frame, so we can
+	 * reduce the min turn time a bit since we will know
+	 * how much time we have used for protocol processing
+	 */
+        do_gettimeofday(&self->stamp);
+
+	/* Check if we need to copy the data to a new skb or not.
+	 * For most frames, we use ZeroCopy and pass the already
+	 * allocated skb up the stack.
+	 * If the frame is small, it is more efficient to copy it
+	 * to save memory (copy will be fast anyway - that's
+	 * called Rx-copy-break). Jean II */
+	docopy = (urb->actual_length < IRDA_RX_COPY_THRESHOLD);
+
+	/* Allocate a new skb */
+	newskb = dev_alloc_skb(docopy ? urb->actual_length : IRDA_SKB_MAX_MTU);
+	if (!newskb)  {
+		self->stats.rx_dropped++;
+		/* We could deliver the current skb, but this would stall
+		 * the Rx path. Better drop the packet... Jean II */
+		goto done;  
+	}
+
+	/* Make sure IP header get aligned (IrDA header is 5 bytes) */
+	/* But IrDA-USB header is 1 byte. Jean II */
+	//skb_reserve(newskb, USB_IRDA_HEADER - 1);
+
+	if(docopy) {
+		/* Copy packet, so we can recycle the original */
+		memcpy(newskb->data, skb->data, urb->actual_length);
+		/* Deliver this new skb */
+		dataskb = newskb;
+		/* And hook the old skb to the URB
+		 * Note : we don't need to "clean up" the old skb,
+		 * as we never touched it. Jean II */
+	} else {
+		/* We are using ZeroCopy. Deliver old skb */
+		dataskb = skb;
+		/* And hook the new skb to the URB */
+		skb = newskb;
+	}
+
+	/* Set proper length on skb & remove USB-IrDA header */
+	skb_put(dataskb, urb->actual_length);
+	skb_pull(dataskb, USB_IRDA_HEADER);
+
+	/* Ask the networking layer to queue the packet for the IrDA stack */
+	dataskb->dev = self->netdev;
+	dataskb->mac.raw  = dataskb->data;
+	dataskb->protocol = htons(ETH_P_IRDA);
+	netif_rx(dataskb);
+
+	/* Keep stats up to date */
+	self->stats.rx_bytes += dataskb->len;
+	self->stats.rx_packets++;
+	self->netdev->last_rx = jiffies;
+
+done:
+	/* Note : at this point, the URB we've just received (urb)
+	 * is still referenced by the USB layer. For example, if we
+	 * have received a -ECONNRESET, uhci_cleanup_unlink() will
+	 * continue to process it (in fact, cleaning it up).
+	 * If we were to submit this URB, disaster would ensue.
+	 * Therefore, we submit our idle URB, and put this URB in our
+	 * idle slot....
+	 * Jean II */
+	/* Note : with this scheme, we could submit the idle URB before
+	 * processing the Rx URB. Another time... Jean II */
+
+	/* Submit the idle URB to replace the URB we've just received */
+	irda_usb_submit(self, skb, self->idle_rx_urb);
+	/* Recycle Rx URB : Now, the idle URB is the present one */
+	urb->context = NULL;
+	self->idle_rx_urb = urb;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Callbak from IrDA layer. IrDA wants to know if we have
+ * started receiving anything.
+ */
+static int irda_usb_is_receiving(struct irda_usb_cb *self)
+{
+	/* Note : because of the way UHCI works, it's almost impossible
+	 * to get this info. The Controller DMA directly to memory and
+	 * signal only when the whole frame is finished. To know if the
+	 * first TD of the URB has been filled or not seems hard work...
+	 *
+	 * The other solution would be to use the "receiving" command
+	 * on the default decriptor with a usb_control_msg(), but that
+	 * would add USB traffic and would return result only in the
+	 * next USB frame (~1ms).
+	 *
+	 * I've been told that current dongles send status info on their
+	 * interrupt endpoint, and that's what the Windows driver uses
+	 * to know this info. Unfortunately, this is not yet in the spec...
+	 *
+	 * Jean II
+	 */
+
+	return 0; /* For now */
+}
+
+/********************** IRDA DEVICE CALLBACKS **********************/
+/*
+ * Main calls from the IrDA/Network subsystem.
+ * Mostly registering a new irda-usb device and removing it....
+ * We only deal with the IrDA side of the business, the USB side will
+ * be dealt with below...
+ */
+
+
+/*------------------------------------------------------------------*/
+/*
+ * Function irda_usb_net_open (dev)
+ *
+ *    Network device is taken up. Usually this is done by "ifconfig irda0 up" 
+ *   
+ * Note : don't mess with self->netopen - Jean II
+ */
+static int irda_usb_net_open(struct net_device *netdev)
+{
+	struct irda_usb_cb *self;
+	char	hwname[16];
+	int i;
+	
+	IRDA_DEBUG(1, "%s()\n", __FUNCTION__);
+
+	IRDA_ASSERT(netdev != NULL, return -1;);
+	self = (struct irda_usb_cb *) netdev->priv;
+	IRDA_ASSERT(self != NULL, return -1;);
+
+	/* Can only open the device if it's there */
+	if(!self->present) {
+		IRDA_WARNING("%s(), device not present!\n", __FUNCTION__);
+		return -1;
+	}
+
+	/* Initialise default speed and xbofs value
+	 * (IrLAP will change that soon) */
+	self->speed = -1;
+	self->xbofs = -1;
+	self->new_speed = -1;
+	self->new_xbofs = -1;
+
+	/* To do *before* submitting Rx urbs and starting net Tx queue
+	 * Jean II */
+	self->netopen = 1;
+
+	/* 
+	 * Now that everything should be initialized properly,
+	 * Open new IrLAP layer instance to take care of us...
+	 * Note : will send immediately a speed change...
+	 */
+	sprintf(hwname, "usb#%d", self->usbdev->devnum);
+	self->irlap = irlap_open(netdev, &self->qos, hwname);
+	IRDA_ASSERT(self->irlap != NULL, return -1;);
+
+	/* Allow IrLAP to send data to us */
+	netif_start_queue(netdev);
+
+	/* We submit all the Rx URB except for one that we keep idle.
+	 * Need to be initialised before submitting other USBs, because
+	 * in some cases as soon as we submit the URBs the USB layer
+	 * will trigger a dummy receive - Jean II */
+	self->idle_rx_urb = self->rx_urb[IU_MAX_ACTIVE_RX_URBS];
+	self->idle_rx_urb->context = NULL;
+
+	/* Now that we can pass data to IrLAP, allow the USB layer
+	 * to send us some data... */
+	for (i = 0; i < IU_MAX_ACTIVE_RX_URBS; i++) {
+		struct sk_buff *skb = dev_alloc_skb(IRDA_SKB_MAX_MTU);
+		if (!skb) {
+			/* If this ever happen, we are in deep s***.
+			 * Basically, we can't start the Rx path... */
+			IRDA_WARNING("%s(), Failed to allocate Rx skb\n",
+				     __FUNCTION__);
+			return -1;
+		}
+		//skb_reserve(newskb, USB_IRDA_HEADER - 1);
+		irda_usb_submit(self, skb, self->rx_urb[i]);
+	}
+
+	/* Ready to play !!! */
+	return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Function irda_usb_net_close (self)
+ *
+ *    Network device is taken down. Usually this is done by 
+ *    "ifconfig irda0 down" 
+ */
+static int irda_usb_net_close(struct net_device *netdev)
+{
+	struct irda_usb_cb *self;
+	int	i;
+
+	IRDA_DEBUG(1, "%s()\n", __FUNCTION__);
+
+	IRDA_ASSERT(netdev != NULL, return -1;);
+	self = (struct irda_usb_cb *) netdev->priv;
+	IRDA_ASSERT(self != NULL, return -1;);
+
+	/* Clear this flag *before* unlinking the urbs and *before*
+	 * stopping the network Tx queue - Jean II */
+	self->netopen = 0;
+
+	/* Stop network Tx queue */
+	netif_stop_queue(netdev);
+
+	/* Deallocate all the Rx path buffers (URBs and skb) */
+	for (i = 0; i < IU_MAX_RX_URBS; i++) {
+		struct urb *urb = self->rx_urb[i];
+		struct sk_buff *skb = (struct sk_buff *) urb->context;
+		/* Cancel the receive command */
+		usb_kill_urb(urb);
+		/* The skb is ours, free it */
+		if(skb) {
+			dev_kfree_skb(skb);
+			urb->context = NULL;
+		}
+	}
+	/* Cancel Tx and speed URB - need to be synchronous to avoid races */
+	self->tx_urb->transfer_flags &= ~URB_ASYNC_UNLINK;
+	usb_kill_urb(self->tx_urb);
+	self->speed_urb->transfer_flags &= ~URB_ASYNC_UNLINK;
+	usb_kill_urb(self->speed_urb);
+
+	/* Stop and remove instance of IrLAP */
+	if (self->irlap)
+		irlap_close(self->irlap);
+	self->irlap = NULL;
+
+	return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * IOCTLs : Extra out-of-band network commands...
+ */
+static int irda_usb_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+	unsigned long flags;
+	struct if_irda_req *irq = (struct if_irda_req *) rq;
+	struct irda_usb_cb *self;
+	int ret = 0;
+
+	IRDA_ASSERT(dev != NULL, return -1;);
+	self = dev->priv;
+	IRDA_ASSERT(self != NULL, return -1;);
+
+	IRDA_DEBUG(2, "%s(), %s, (cmd=0x%X)\n", __FUNCTION__, dev->name, cmd);
+
+	switch (cmd) {
+	case SIOCSBANDWIDTH: /* Set bandwidth */
+		if (!capable(CAP_NET_ADMIN))
+			return -EPERM;
+		/* Protect us from USB callbacks, net watchdog and else. */
+		spin_lock_irqsave(&self->lock, flags);
+		/* Check if the device is still there */
+		if(self->present) {
+			/* Set the desired speed */
+			self->new_speed = irq->ifr_baudrate;
+			irda_usb_change_speed_xbofs(self);
+		}
+		spin_unlock_irqrestore(&self->lock, flags);
+		break;
+	case SIOCSMEDIABUSY: /* Set media busy */
+		if (!capable(CAP_NET_ADMIN))
+			return -EPERM;
+		/* Check if the IrDA stack is still there */
+		if(self->netopen)
+			irda_device_set_media_busy(self->netdev, TRUE);
+		break;
+	case SIOCGRECEIVING: /* Check if we are receiving right now */
+		irq->ifr_receiving = irda_usb_is_receiving(self);
+		break;
+	default:
+		ret = -EOPNOTSUPP;
+	}
+	
+	return ret;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Get device stats (for /proc/net/dev and ifconfig)
+ */
+static struct net_device_stats *irda_usb_net_get_stats(struct net_device *dev)
+{
+	struct irda_usb_cb *self = dev->priv;
+	return &self->stats;
+}
+
+/********************* IRDA CONFIG SUBROUTINES *********************/
+/*
+ * Various subroutines dealing with IrDA and network stuff we use to
+ * configure and initialise each irda-usb instance.
+ * These functions are used below in the main calls of the driver...
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * Set proper values in the IrDA QOS structure
+ */
+static inline void irda_usb_init_qos(struct irda_usb_cb *self)
+{
+	struct irda_class_desc *desc;
+
+	IRDA_DEBUG(3, "%s()\n", __FUNCTION__);
+	
+	desc = self->irda_desc;
+	
+	/* Initialize QoS for this device */
+	irda_init_max_qos_capabilies(&self->qos);
+
+	/* See spec section 7.2 for meaning.
+	 * Values are little endian (as most USB stuff), the IrDA stack
+	 * use it in native order (see parameters.c). - Jean II */
+	self->qos.baud_rate.bits       = le16_to_cpu(desc->wBaudRate);
+	self->qos.min_turn_time.bits   = desc->bmMinTurnaroundTime;
+	self->qos.additional_bofs.bits = desc->bmAdditionalBOFs;
+	self->qos.window_size.bits     = desc->bmWindowSize;
+	self->qos.data_size.bits       = desc->bmDataSize;
+
+	IRDA_DEBUG(0, "%s(), dongle says speed=0x%X, size=0x%X, window=0x%X, bofs=0x%X, turn=0x%X\n", 
+		__FUNCTION__, self->qos.baud_rate.bits, self->qos.data_size.bits, self->qos.window_size.bits, self->qos.additional_bofs.bits, self->qos.min_turn_time.bits);
+
+	/* Don't always trust what the dongle tell us */
+	if(self->capability & IUC_SIR_ONLY)
+		self->qos.baud_rate.bits	&= 0x00ff;
+	if(self->capability & IUC_SMALL_PKT)
+		self->qos.data_size.bits	 = 0x07;
+	if(self->capability & IUC_NO_WINDOW)
+		self->qos.window_size.bits	 = 0x01;
+	if(self->capability & IUC_MAX_WINDOW)
+		self->qos.window_size.bits	 = 0x7f;
+	if(self->capability & IUC_MAX_XBOFS)
+		self->qos.additional_bofs.bits	 = 0x01;
+
+#if 1
+	/* Module parameter can override the rx window size */
+	if (qos_mtt_bits)
+		self->qos.min_turn_time.bits = qos_mtt_bits;
+#endif	    
+	/* 
+	 * Note : most of those values apply only for the receive path,
+	 * the transmit path will be set differently - Jean II 
+	 */
+	irda_qos_bits_to_value(&self->qos);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Initialise the network side of the irda-usb instance
+ * Called when a new USB instance is registered in irda_usb_probe()
+ */
+static inline int irda_usb_open(struct irda_usb_cb *self)
+{
+	struct net_device *netdev = self->netdev;
+
+	IRDA_DEBUG(1, "%s()\n", __FUNCTION__);
+
+	irda_usb_init_qos(self);
+
+	/* Override the network functions we need to use */
+	netdev->hard_start_xmit = irda_usb_hard_xmit;
+	netdev->tx_timeout	= irda_usb_net_timeout;
+	netdev->watchdog_timeo  = 250*HZ/1000;	/* 250 ms > USB timeout */
+	netdev->open            = irda_usb_net_open;
+	netdev->stop            = irda_usb_net_close;
+	netdev->get_stats	= irda_usb_net_get_stats;
+	netdev->do_ioctl        = irda_usb_net_ioctl;
+
+	return register_netdev(netdev);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Cleanup the network side of the irda-usb instance
+ * Called when a USB instance is removed in irda_usb_disconnect()
+ */
+static inline void irda_usb_close(struct irda_usb_cb *self)
+{
+	IRDA_DEBUG(1, "%s()\n", __FUNCTION__);
+
+	/* Remove netdevice */
+	unregister_netdev(self->netdev);
+
+	/* Remove the speed buffer */
+	if (self->speed_buff != NULL) {
+		kfree(self->speed_buff);
+		self->speed_buff = NULL;
+	}
+}
+
+/********************** USB CONFIG SUBROUTINES **********************/
+/*
+ * Various subroutines dealing with USB stuff we use to configure and
+ * initialise each irda-usb instance.
+ * These functions are used below in the main calls of the driver...
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * Function irda_usb_parse_endpoints(dev, ifnum)
+ *
+ *    Parse the various endpoints and find the one we need.
+ *
+ * The endpoint are the pipes used to communicate with the USB device.
+ * The spec defines 2 endpoints of type bulk transfer, one in, and one out.
+ * These are used to pass frames back and forth with the dongle.
+ * Most dongle have also an interrupt endpoint, that will be probably
+ * documented in the next spec...
+ */
+static inline int irda_usb_parse_endpoints(struct irda_usb_cb *self, struct usb_host_endpoint *endpoint, int ennum)
+{
+	int i;		/* Endpoint index in table */
+		
+	/* Init : no endpoints */
+	self->bulk_in_ep = 0;
+	self->bulk_out_ep = 0;
+	self->bulk_int_ep = 0;
+
+	/* Let's look at all those endpoints */
+	for(i = 0; i < ennum; i++) {
+		/* All those variables will get optimised by the compiler,
+		 * so let's aim for clarity... - Jean II */
+		__u8 ep;	/* Endpoint address */
+		__u8 dir;	/* Endpoint direction */
+		__u8 attr;	/* Endpoint attribute */
+		__u16 psize;	/* Endpoint max packet size in bytes */
+
+		/* Get endpoint address, direction and attribute */
+		ep = endpoint[i].desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
+		dir = endpoint[i].desc.bEndpointAddress & USB_ENDPOINT_DIR_MASK;
+		attr = endpoint[i].desc.bmAttributes;
+		psize = le16_to_cpu(endpoint[i].desc.wMaxPacketSize);
+
+		/* Is it a bulk endpoint ??? */
+		if(attr == USB_ENDPOINT_XFER_BULK) {
+			/* We need to find an IN and an OUT */
+			if(dir == USB_DIR_IN) {
+				/* This is our Rx endpoint */
+				self->bulk_in_ep = ep;
+			} else {
+				/* This is our Tx endpoint */
+				self->bulk_out_ep = ep;
+				self->bulk_out_mtu = psize;
+			}
+		} else {
+			if((attr == USB_ENDPOINT_XFER_INT) &&
+			   (dir == USB_DIR_IN)) {
+				/* This is our interrupt endpoint */
+				self->bulk_int_ep = ep;
+			} else {
+				IRDA_ERROR("%s(), Unrecognised endpoint %02X.\n", __FUNCTION__, ep);
+			}
+		}
+	}
+
+	IRDA_DEBUG(0, "%s(), And our endpoints are : in=%02X, out=%02X (%d), int=%02X\n",
+		__FUNCTION__, self->bulk_in_ep, self->bulk_out_ep, self->bulk_out_mtu, self->bulk_int_ep);
+	/* Should be 8, 16, 32 or 64 bytes */
+	IRDA_ASSERT(self->bulk_out_mtu == 64, ;);
+
+	return((self->bulk_in_ep != 0) && (self->bulk_out_ep != 0));
+}
+
+#ifdef IU_DUMP_CLASS_DESC
+/*------------------------------------------------------------------*/
+/*
+ * Function usb_irda_dump_class_desc(desc)
+ *
+ *    Prints out the contents of the IrDA class descriptor
+ *
+ */
+static inline void irda_usb_dump_class_desc(struct irda_class_desc *desc)
+{
+	/* Values are little endian */
+	printk("bLength=%x\n", desc->bLength);
+	printk("bDescriptorType=%x\n", desc->bDescriptorType);
+	printk("bcdSpecRevision=%x\n", le16_to_cpu(desc->bcdSpecRevision)); 
+	printk("bmDataSize=%x\n", desc->bmDataSize);
+	printk("bmWindowSize=%x\n", desc->bmWindowSize);
+	printk("bmMinTurnaroundTime=%d\n", desc->bmMinTurnaroundTime);
+	printk("wBaudRate=%x\n", le16_to_cpu(desc->wBaudRate));
+	printk("bmAdditionalBOFs=%x\n", desc->bmAdditionalBOFs);
+	printk("bIrdaRateSniff=%x\n", desc->bIrdaRateSniff);
+	printk("bMaxUnicastList=%x\n", desc->bMaxUnicastList);
+}
+#endif /* IU_DUMP_CLASS_DESC */
+
+/*------------------------------------------------------------------*/
+/*
+ * Function irda_usb_find_class_desc(intf)
+ *
+ *    Returns instance of IrDA class descriptor, or NULL if not found
+ *
+ * The class descriptor is some extra info that IrDA USB devices will
+ * offer to us, describing their IrDA characteristics. We will use that in
+ * irda_usb_init_qos()
+ */
+static inline struct irda_class_desc *irda_usb_find_class_desc(struct usb_interface *intf)
+{
+	struct usb_device *dev = interface_to_usbdev (intf);
+	struct irda_class_desc *desc;
+	int ret;
+
+	desc = kmalloc(sizeof (*desc), GFP_KERNEL);
+	if (desc == NULL) 
+		return NULL;
+	memset(desc, 0, sizeof(*desc));
+
+	/* USB-IrDA class spec 1.0:
+	 *	6.1.3: Standard "Get Descriptor" Device Request is not
+	 *	       appropriate to retrieve class-specific descriptor
+	 *	6.2.5: Class Specific "Get Class Descriptor" Interface Request
+	 *	       is mandatory and returns the USB-IrDA class descriptor
+	 */
+
+	ret = usb_control_msg(dev, usb_rcvctrlpipe(dev,0),
+		IU_REQ_GET_CLASS_DESC,
+		USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+		0, intf->altsetting->desc.bInterfaceNumber, desc,
+		sizeof(*desc), 500);
+	
+	IRDA_DEBUG(1, "%s(), ret=%d\n", __FUNCTION__, ret);
+	if (ret < sizeof(*desc)) {
+		IRDA_WARNING("usb-irda: class_descriptor read %s (%d)\n",
+			     (ret<0) ? "failed" : "too short", ret);
+	}
+	else if (desc->bDescriptorType != USB_DT_IRDA) {
+		IRDA_WARNING("usb-irda: bad class_descriptor type\n");
+	}
+	else {
+#ifdef IU_DUMP_CLASS_DESC
+		irda_usb_dump_class_desc(desc);
+#endif	/* IU_DUMP_CLASS_DESC */
+
+		return desc;
+	}
+	kfree(desc);
+	return NULL;
+}
+
+/*********************** USB DEVICE CALLBACKS ***********************/
+/*
+ * Main calls from the USB subsystem.
+ * Mostly registering a new irda-usb device and removing it....
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * This routine is called by the USB subsystem for each new device
+ * in the system. We need to check if the device is ours, and in
+ * this case start handling it.
+ * The USB layer protect us from reentrancy (via BKL), so we don't need
+ * to spinlock in there... Jean II
+ */
+static int irda_usb_probe(struct usb_interface *intf,
+			  const struct usb_device_id *id)
+{
+	struct net_device *net;
+	struct usb_device *dev = interface_to_usbdev(intf);
+	struct irda_usb_cb *self = NULL;
+	struct usb_host_interface *interface;
+	struct irda_class_desc *irda_desc;
+	int ret = -ENOMEM;
+	int i;		/* Driver instance index / Rx URB index */
+
+	/* Note : the probe make sure to call us only for devices that
+	 * matches the list of dongle (top of the file). So, we
+	 * don't need to check if the dongle is really ours.
+	 * Jean II */
+
+	IRDA_MESSAGE("IRDA-USB found at address %d, Vendor: %x, Product: %x\n",
+		     dev->devnum, le16_to_cpu(dev->descriptor.idVendor),
+		     le16_to_cpu(dev->descriptor.idProduct));
+
+	net = alloc_irdadev(sizeof(*self));
+	if (!net) 
+		goto err_out;
+
+	SET_MODULE_OWNER(net);
+	SET_NETDEV_DEV(net, &intf->dev);
+	self = net->priv;
+	self->netdev = net;
+	spin_lock_init(&self->lock);
+
+	/* Create all of the needed urbs */
+	for (i = 0; i < IU_MAX_RX_URBS; i++) {
+		self->rx_urb[i] = usb_alloc_urb(0, GFP_KERNEL);
+		if (!self->rx_urb[i]) {
+			goto err_out_1;
+		}
+	}
+	self->tx_urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!self->tx_urb) {
+		goto err_out_1;
+	}
+	self->speed_urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!self->speed_urb) {
+		goto err_out_2;
+	}
+
+	/* Is this really necessary? (no, except maybe for broken devices) */
+	if (usb_reset_configuration (dev) < 0) {
+		err("reset_configuration failed");
+		ret = -EIO;
+		goto err_out_3;
+	}
+
+	/* Is this really necessary? */
+	/* Note : some driver do hardcode the interface number, some others
+	 * specify an alternate, but very few driver do like this.
+	 * Jean II */
+	ret = usb_set_interface(dev, intf->altsetting->desc.bInterfaceNumber, 0);
+	IRDA_DEBUG(1, "usb-irda: set interface %d result %d\n", intf->altsetting->desc.bInterfaceNumber, ret);
+	switch (ret) {
+		case 0:
+			break;
+		case -EPIPE:		/* -EPIPE = -32 */
+			/* Martin Diehl says if we get a -EPIPE we should
+			 * be fine and we don't need to do a usb_clear_halt().
+			 * - Jean II */
+			IRDA_DEBUG(0, "%s(), Received -EPIPE, ignoring...\n", __FUNCTION__);
+			break;
+		default:
+			IRDA_DEBUG(0, "%s(), Unknown error %d\n", __FUNCTION__, ret);
+			ret = -EIO;
+			goto err_out_3;
+	}
+
+	/* Find our endpoints */
+	interface = intf->cur_altsetting;
+	if(!irda_usb_parse_endpoints(self, interface->endpoint,
+				     interface->desc.bNumEndpoints)) {
+		IRDA_ERROR("%s(), Bogus endpoints...\n", __FUNCTION__);
+		ret = -EIO;
+		goto err_out_3;
+	}
+
+	/* Find IrDA class descriptor */
+	irda_desc = irda_usb_find_class_desc(intf);
+	ret = -ENODEV;
+	if (irda_desc == NULL)
+		goto err_out_3;
+
+	self->irda_desc =  irda_desc;
+	self->present = 1;
+	self->netopen = 0;
+	self->capability = id->driver_info;
+	self->usbdev = dev;
+	self->usbintf = intf;
+
+	/* Allocate the buffer for speed changes */
+	/* Don't change this buffer size and allocation without doing
+	 * some heavy and complete testing. Don't ask why :-(
+	 * Jean II */
+	self->speed_buff = (char *) kmalloc(IRDA_USB_SPEED_MTU, GFP_KERNEL);
+	if (self->speed_buff == NULL) 
+		goto err_out_3;
+
+	memset(self->speed_buff, 0, IRDA_USB_SPEED_MTU);
+
+	ret = irda_usb_open(self);
+	if (ret) 
+		goto err_out_4;
+
+	IRDA_MESSAGE("IrDA: Registered device %s\n", net->name);
+	usb_set_intfdata(intf, self);
+	return 0;
+
+err_out_4:
+	kfree(self->speed_buff);
+err_out_3:
+	/* Free all urbs that we may have created */
+	usb_free_urb(self->speed_urb);
+err_out_2:
+	usb_free_urb(self->tx_urb);
+err_out_1:
+	for (i = 0; i < IU_MAX_RX_URBS; i++) {
+		if (self->rx_urb[i])
+			usb_free_urb(self->rx_urb[i]);
+	}
+	free_netdev(net);
+err_out:
+	return ret;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * The current irda-usb device is removed, the USB layer tell us
+ * to shut it down...
+ * One of the constraints is that when we exit this function,
+ * we cannot use the usb_device no more. Gone. Destroyed. kfree().
+ * Most other subsystem allow you to destroy the instance at a time
+ * when it's convenient to you, to postpone it to a later date, but
+ * not the USB subsystem.
+ * So, we must make bloody sure that everything gets deactivated.
+ * Jean II
+ */
+static void irda_usb_disconnect(struct usb_interface *intf)
+{
+	unsigned long flags;
+	struct irda_usb_cb *self = usb_get_intfdata(intf);
+	int i;
+
+	IRDA_DEBUG(1, "%s()\n", __FUNCTION__);
+
+	usb_set_intfdata(intf, NULL);
+	if (!self)
+		return;
+
+	/* Make sure that the Tx path is not executing. - Jean II */
+	spin_lock_irqsave(&self->lock, flags);
+
+	/* Oups ! We are not there any more.
+	 * This will stop/desactivate the Tx path. - Jean II */
+	self->present = 0;
+
+	/* We need to have irq enabled to unlink the URBs. That's OK,
+	 * at this point the Tx path is gone - Jean II */
+	spin_unlock_irqrestore(&self->lock, flags);
+
+	/* Hum... Check if networking is still active (avoid races) */
+	if((self->netopen) || (self->irlap)) {
+		/* Accept no more transmissions */
+		/*netif_device_detach(self->netdev);*/
+		netif_stop_queue(self->netdev);
+		/* Stop all the receive URBs */
+		for (i = 0; i < IU_MAX_RX_URBS; i++)
+			usb_kill_urb(self->rx_urb[i]);
+		/* Cancel Tx and speed URB.
+		 * Toggle flags to make sure it's synchronous. */
+		self->tx_urb->transfer_flags &= ~URB_ASYNC_UNLINK;
+		usb_kill_urb(self->tx_urb);
+		self->speed_urb->transfer_flags &= ~URB_ASYNC_UNLINK;
+		usb_kill_urb(self->speed_urb);
+	}
+
+	/* Cleanup the device stuff */
+	irda_usb_close(self);
+	/* No longer attached to USB bus */
+	self->usbdev = NULL;
+	self->usbintf = NULL;
+
+	/* Clean up our urbs */
+	for (i = 0; i < IU_MAX_RX_URBS; i++)
+		usb_free_urb(self->rx_urb[i]);
+	/* Clean up Tx and speed URB */
+	usb_free_urb(self->tx_urb);
+	usb_free_urb(self->speed_urb);
+
+	/* Free self and network device */
+	free_netdev(self->netdev);
+	IRDA_DEBUG(0, "%s(), USB IrDA Disconnected\n", __FUNCTION__);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * USB device callbacks
+ */
+static struct usb_driver irda_driver = {
+	.owner		= THIS_MODULE,
+	.name		= "irda-usb",
+	.probe		= irda_usb_probe,
+	.disconnect	= irda_usb_disconnect,
+	.id_table	= dongles,
+};
+
+/************************* MODULE CALLBACKS *************************/
+/*
+ * Deal with module insertion/removal
+ * Mostly tell USB about our existence
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * Module insertion
+ */
+static int __init usb_irda_init(void)
+{
+	int	ret;
+
+	ret = usb_register(&irda_driver);
+	if (ret < 0)
+		return ret;
+
+	IRDA_MESSAGE("USB IrDA support registered\n");
+	return 0;
+}
+module_init(usb_irda_init);
+
+/*------------------------------------------------------------------*/
+/*
+ * Module removal
+ */
+static void __exit usb_irda_cleanup(void)
+{
+	/* Deregister the driver and remove all pending instances */
+	usb_deregister(&irda_driver);
+}
+module_exit(usb_irda_cleanup);
+
+/*------------------------------------------------------------------*/
+/*
+ * Module parameters
+ */
+module_param(qos_mtt_bits, int, 0);
+MODULE_PARM_DESC(qos_mtt_bits, "Minimum Turn Time");
+MODULE_AUTHOR("Roman Weissgaerber <weissg@vienna.at>, Dag Brattli <dag@brattli.net> and Jean Tourrilhes <jt@hpl.hp.com>");
+MODULE_DESCRIPTION("IrDA-USB Dongle Driver"); 
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/irda/irda-usb.h b/drivers/net/irda/irda-usb.h
new file mode 100644
index 0000000..bd8f665
--- /dev/null
+++ b/drivers/net/irda/irda-usb.h
@@ -0,0 +1,163 @@
+/*****************************************************************************
+ *
+ * Filename:      irda-usb.h
+ * Version:       0.9b
+ * Description:   IrDA-USB Driver
+ * Status:        Experimental 
+ * Author:        Dag Brattli <dag@brattli.net>
+ *
+ *	Copyright (C) 2001, Roman Weissgaerber <weissg@vienna.at>
+ *      Copyright (C) 2000, Dag Brattli <dag@brattli.net>
+ *      Copyright (C) 2001, Jean Tourrilhes <jt@hpl.hp.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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *****************************************************************************/
+
+#include <linux/time.h>
+
+#include <net/irda/irda.h>
+#include <net/irda/irda_device.h>      /* struct irlap_cb */
+
+#define RX_COPY_THRESHOLD 200
+#define IRDA_USB_MAX_MTU 2051
+#define IRDA_USB_SPEED_MTU 64		/* Weird, but work like this */
+
+/* Maximum number of active URB on the Rx path
+ * This is the amount of buffers the we keep between the USB harware and the
+ * IrDA stack.
+ *
+ * Note : the network layer does also queue the packets between us and the
+ * IrDA stack, and is actually pretty fast and efficient in doing that.
+ * Therefore, we don't need to have a large number of URBs, and we can
+ * perfectly live happy with only one. We certainly don't need to keep the
+ * full IrTTP window around here...
+ * I repeat for those who have trouble to understand : 1 URB is plenty
+ * good enough to handle back-to-back (brickwalled) frames. I tried it,
+ * it works (it's the hardware that has trouble doing it).
+ *
+ * Having 2 URBs would allow the USB stack to process one URB while we take
+ * care of the other and then swap the URBs...
+ * On the other hand, increasing the number of URB will have penalities
+ * in term of latency and will interact with the link management in IrLAP...
+ * Jean II */
+#define IU_MAX_ACTIVE_RX_URBS	1	/* Don't touch !!! */
+
+/* When a Rx URB is passed back to us, we can't reuse it immediately,
+ * because it may still be referenced by the USB layer. Therefore we
+ * need to keep one extra URB in the Rx path.
+ * Jean II */
+#define IU_MAX_RX_URBS	(IU_MAX_ACTIVE_RX_URBS + 1)
+
+/* Various ugly stuff to try to workaround generic problems */
+/* Send speed command in case of timeout, just for trying to get things sane */
+#define IU_BUG_KICK_TIMEOUT
+/* Show the USB class descriptor */
+#undef IU_DUMP_CLASS_DESC 
+/* Assume a minimum round trip latency for USB transfer (in us)...
+ * USB transfer are done in the next USB slot if there is no traffic
+ * (1/19 msec) and is done at 12 Mb/s :
+ * Waiting for slot + tx = (53us + 16us) * 2 = 137us minimum.
+ * Rx notification will only be done at the end of the USB frame period :
+ * OHCI : frame period = 1ms
+ * UHCI : frame period = 1ms, but notification can take 2 or 3 ms :-(
+ * EHCI : frame period = 125us */
+#define IU_USB_MIN_RTT		500	/* This should be safe in most cases */
+
+/* Inbound header */
+#define MEDIA_BUSY    0x80
+
+#define SPEED_2400    0x01
+#define SPEED_9600    0x02
+#define SPEED_19200   0x03
+#define SPEED_38400   0x04
+#define SPEED_57600   0x05
+#define SPEED_115200  0x06
+#define SPEED_576000  0x07
+#define SPEED_1152000 0x08
+#define SPEED_4000000 0x09
+
+/* Basic capabilities */
+#define IUC_DEFAULT	0x00	/* Basic device compliant with 1.0 spec */
+/* Main bugs */
+#define IUC_SPEED_BUG	0x01	/* Device doesn't set speed after the frame */
+#define IUC_NO_WINDOW	0x02	/* Device doesn't behave with big Rx window */
+#define IUC_NO_TURN	0x04	/* Device doesn't do turnaround by itself */
+/* Not currently used */
+#define IUC_SIR_ONLY	0x08	/* Device doesn't behave at FIR speeds */
+#define IUC_SMALL_PKT	0x10	/* Device doesn't behave with big Rx packets */
+#define IUC_MAX_WINDOW	0x20	/* Device underestimate the Rx window */
+#define IUC_MAX_XBOFS	0x40	/* Device need more xbofs than advertised */
+
+/* USB class definitions */
+#define USB_IRDA_HEADER   0x01
+#define USB_CLASS_IRDA    0x02 /* USB_CLASS_APP_SPEC subclass */ 
+#define USB_DT_IRDA       0x21
+
+struct irda_class_desc {
+	__u8  bLength;
+	__u8  bDescriptorType;
+	__u16 bcdSpecRevision;
+	__u8  bmDataSize;
+	__u8  bmWindowSize;
+	__u8  bmMinTurnaroundTime;
+	__u16 wBaudRate;
+	__u8  bmAdditionalBOFs;
+	__u8  bIrdaRateSniff;
+	__u8  bMaxUnicastList;
+} __attribute__ ((packed));
+
+/* class specific interface request to get the IrDA-USB class descriptor
+ * (6.2.5, USB-IrDA class spec 1.0) */
+
+#define IU_REQ_GET_CLASS_DESC	0x06
+
+struct irda_usb_cb {
+	struct irda_class_desc *irda_desc;
+	struct usb_device *usbdev;	/* init: probe_irda */
+	struct usb_interface *usbintf;	/* init: probe_irda */
+	int netopen;			/* Device is active for network */
+	int present;			/* Device is present on the bus */
+	__u32 capability;		/* Capability of the hardware */
+	__u8  bulk_in_ep;		/* Rx Endpoint assignments */
+	__u8  bulk_out_ep;		/* Tx Endpoint assignments */
+	__u16 bulk_out_mtu;		/* Max Tx packet size in bytes */
+	__u8  bulk_int_ep;		/* Interrupt Endpoint assignments */
+
+	wait_queue_head_t wait_q;	/* for timeouts */
+
+	struct urb *rx_urb[IU_MAX_RX_URBS];	/* URBs used to receive data frames */
+	struct urb *idle_rx_urb;	/* Pointer to idle URB in Rx path */
+	struct urb *tx_urb;		/* URB used to send data frames */
+	struct urb *speed_urb;		/* URB used to send speed commands */
+	
+	struct net_device *netdev;	/* Yes! we are some kind of netdev. */
+	struct net_device_stats stats;
+	struct irlap_cb   *irlap;	/* The link layer we are binded to */
+	struct qos_info qos;
+	hashbin_t *tx_list;		/* Queued transmit skb's */
+	char *speed_buff;		/* Buffer for speed changes */
+
+	struct timeval stamp;
+	struct timeval now;
+
+	spinlock_t lock;		/* For serializing operations */
+
+	__u16 xbofs;			/* Current xbofs setting */
+	__s16 new_xbofs;		/* xbofs we need to set */
+	__u32 speed;			/* Current speed */
+	__s32 new_speed;		/* speed we need to set */
+};
+
diff --git a/drivers/net/irda/irport.c b/drivers/net/irda/irport.c
new file mode 100644
index 0000000..5971315
--- /dev/null
+++ b/drivers/net/irda/irport.c
@@ -0,0 +1,1146 @@
+/*********************************************************************
+ * 
+ * Filename:	  irport.c
+ * Version:	  1.0
+ * Description:   Half duplex serial port SIR driver for IrDA. 
+ * Status:	  Experimental.
+ * Author:	  Dag Brattli <dagb@cs.uit.no>
+ * Created at:	  Sun Aug  3 13:49:59 1997
+ * Modified at:   Fri Jan 28 20:22:38 2000
+ * Modified by:   Dag Brattli <dagb@cs.uit.no>
+ * Sources:	  serial.c by Linus Torvalds 
+ * 
+ *     Copyright (c) 1997, 1998, 1999-2000 Dag Brattli, All Rights Reserved.
+ *     Copyright (c) 2000-2003 Jean Tourrilhes, 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
+ *
+ *     This driver is ment to be a small half duplex serial driver to be
+ *     used for IR-chipsets that has a UART (16550) compatibility mode. 
+ *     Eventually it will replace irtty, because of irtty has some 
+ *     problems that is hard to get around when we don't have control
+ *     over the serial driver. This driver may also be used by FIR 
+ *     drivers to handle SIR mode for them.
+ *
+ ********************************************************************/
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/skbuff.h>
+#include <linux/serial_reg.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/rtnetlink.h>
+#include <linux/bitops.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+
+#include <net/irda/irda.h>
+#include <net/irda/wrapper.h>
+#include "irport.h"
+
+#define IO_EXTENT 8
+
+/* 
+ * Currently you'll need to set these values using insmod like this:
+ * insmod irport io=0x3e8 irq=11
+ */
+static unsigned int io[]  = { ~0, ~0, ~0, ~0 };
+static unsigned int irq[] = { 0, 0, 0, 0 };
+
+static unsigned int qos_mtt_bits = 0x03;
+
+static struct irport_cb *dev_self[] = { NULL, NULL, NULL, NULL};
+static char *driver_name = "irport";
+
+static inline void irport_write_wakeup(struct irport_cb *self);
+static inline int  irport_write(int iobase, int fifo_size, __u8 *buf, int len);
+static inline void irport_receive(struct irport_cb *self);
+
+static int  irport_net_ioctl(struct net_device *dev, struct ifreq *rq, 
+			     int cmd);
+static inline int  irport_is_receiving(struct irport_cb *self);
+static int  irport_set_dtr_rts(struct net_device *dev, int dtr, int rts);
+static int  irport_raw_write(struct net_device *dev, __u8 *buf, int len);
+static struct net_device_stats *irport_net_get_stats(struct net_device *dev);
+static int irport_change_speed_complete(struct irda_task *task);
+static void irport_timeout(struct net_device *dev);
+
+static irqreturn_t irport_interrupt(int irq, void *dev_id,
+				    struct pt_regs *regs);
+static int irport_hard_xmit(struct sk_buff *skb, struct net_device *dev);
+static void irport_change_speed(void *priv, __u32 speed);
+static int irport_net_open(struct net_device *dev);
+static int irport_net_close(struct net_device *dev);
+
+static struct irport_cb *
+irport_open(int i, unsigned int iobase, unsigned int irq)
+{
+	struct net_device *dev;
+	struct irport_cb *self;
+
+	IRDA_DEBUG(1, "%s()\n", __FUNCTION__);
+
+	/* Lock the port that we need */
+	if (!request_region(iobase, IO_EXTENT, driver_name)) {
+		IRDA_DEBUG(0, "%s(), can't get iobase of 0x%03x\n",
+			   __FUNCTION__, iobase);
+		goto err_out1;
+	}
+
+	/*
+	 *  Allocate new instance of the driver
+	 */
+	dev = alloc_irdadev(sizeof(struct irport_cb));
+	if (!dev) {
+		IRDA_ERROR("%s(), can't allocate memory for "
+			   "irda device!\n", __FUNCTION__);
+		goto err_out2;
+	}
+
+	self = dev->priv;
+	spin_lock_init(&self->lock);
+
+	/* Need to store self somewhere */
+	dev_self[i] = self;
+	self->priv = self;
+	self->index = i;
+
+	/* Initialize IO */
+	self->io.sir_base  = iobase;
+        self->io.sir_ext   = IO_EXTENT;
+        self->io.irq       = irq;
+        self->io.fifo_size = 16;		/* 16550A and compatible */
+
+	/* Initialize QoS for this device */
+	irda_init_max_qos_capabilies(&self->qos);
+	
+	self->qos.baud_rate.bits = IR_9600|IR_19200|IR_38400|IR_57600|
+		IR_115200;
+
+	self->qos.min_turn_time.bits = qos_mtt_bits;
+	irda_qos_bits_to_value(&self->qos);
+	
+	/* Bootstrap ZeroCopy Rx */
+	self->rx_buff.truesize = IRDA_SKB_MAX_MTU;
+	self->rx_buff.skb = __dev_alloc_skb(self->rx_buff.truesize,
+					    GFP_KERNEL);
+	if (self->rx_buff.skb == NULL) {
+		IRDA_ERROR("%s(), can't allocate memory for "
+			   "receive buffer!\n", __FUNCTION__);
+		goto err_out3;
+	}
+	skb_reserve(self->rx_buff.skb, 1);
+	self->rx_buff.head = self->rx_buff.skb->data;
+	/* No need to memset the buffer, unless you are really pedantic */
+
+	/* Finish setup the Rx buffer descriptor */
+	self->rx_buff.in_frame = FALSE;
+	self->rx_buff.state = OUTSIDE_FRAME;
+	self->rx_buff.data = self->rx_buff.head;
+
+	/* Specify how much memory we want */
+	self->tx_buff.truesize = 4000;
+	
+	/* Allocate memory if needed */
+	if (self->tx_buff.truesize > 0) {
+		self->tx_buff.head = (__u8 *) kmalloc(self->tx_buff.truesize, 
+						      GFP_KERNEL);
+		if (self->tx_buff.head == NULL) {
+			IRDA_ERROR("%s(), can't allocate memory for "
+				   "transmit buffer!\n", __FUNCTION__);
+			goto err_out4;
+		}
+		memset(self->tx_buff.head, 0, self->tx_buff.truesize);
+	}	
+	self->tx_buff.data = self->tx_buff.head;
+
+	self->netdev = dev;
+	/* Keep track of module usage */
+	SET_MODULE_OWNER(dev);
+
+	/* May be overridden by piggyback drivers */
+	self->interrupt    = irport_interrupt;
+	self->change_speed = irport_change_speed;
+
+	/* Override the network functions we need to use */
+	dev->hard_start_xmit = irport_hard_xmit;
+	dev->tx_timeout	     = irport_timeout;
+	dev->watchdog_timeo  = HZ;  /* Allow time enough for speed change */
+	dev->open            = irport_net_open;
+	dev->stop            = irport_net_close;
+	dev->get_stats	     = irport_net_get_stats;
+	dev->do_ioctl        = irport_net_ioctl;
+
+	/* Make ifconfig display some details */
+	dev->base_addr = iobase;
+	dev->irq = irq;
+
+	if (register_netdev(dev)) {
+		IRDA_ERROR("%s(), register_netdev() failed!\n", __FUNCTION__);
+		goto err_out5;
+	}
+	IRDA_MESSAGE("IrDA: Registered device %s (irport io=0x%X irq=%d)\n",
+		dev->name, iobase, irq);
+
+	return self;
+ err_out5:
+	kfree(self->tx_buff.head);
+ err_out4:
+	kfree_skb(self->rx_buff.skb);
+ err_out3:
+	free_netdev(dev);
+	dev_self[i] = NULL;
+ err_out2:
+	release_region(iobase, IO_EXTENT);
+ err_out1:
+	return NULL;
+}
+
+static int irport_close(struct irport_cb *self)
+{
+	IRDA_ASSERT(self != NULL, return -1;);
+
+	/* We are not using any dongle anymore! */
+	if (self->dongle)
+		irda_device_dongle_cleanup(self->dongle);
+	self->dongle = NULL;
+	
+	/* Remove netdevice */
+	unregister_netdev(self->netdev);
+
+	/* Release the IO-port that this driver is using */
+	IRDA_DEBUG(0 , "%s(), Releasing Region %03x\n", 
+		   __FUNCTION__, self->io.sir_base);
+	release_region(self->io.sir_base, self->io.sir_ext);
+
+	if (self->tx_buff.head)
+		kfree(self->tx_buff.head);
+	
+	if (self->rx_buff.skb)
+		kfree_skb(self->rx_buff.skb);
+	self->rx_buff.skb = NULL;
+	
+	/* Remove ourselves */
+	dev_self[self->index] = NULL;
+	free_netdev(self->netdev);
+	
+	return 0;
+}
+
+static void irport_stop(struct irport_cb *self)
+{
+	int iobase;
+
+	iobase = self->io.sir_base;
+
+	/* We can't lock, we may be called from a FIR driver - Jean II */
+
+	/* We are not transmitting any more */
+	self->transmitting = 0;
+
+	/* Reset UART */
+	outb(0, iobase+UART_MCR);
+	
+	/* Turn off interrupts */
+	outb(0, iobase+UART_IER);
+}
+
+static void irport_start(struct irport_cb *self)
+{
+	int iobase;
+
+	iobase = self->io.sir_base;
+
+	irport_stop(self);
+	
+	/* We can't lock, we may be called from a FIR driver - Jean II */
+
+	/* Initialize UART */
+	outb(UART_LCR_WLEN8, iobase+UART_LCR);  /* Reset DLAB */
+	outb((UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2), iobase+UART_MCR);
+	
+	/* Turn on interrups */
+	outb(UART_IER_RLSI | UART_IER_RDI |UART_IER_THRI, iobase+UART_IER);
+}
+
+/*
+ * Function irport_probe (void)
+ *
+ *    Start IO port 
+ *
+ */
+int irport_probe(int iobase)
+{
+	IRDA_DEBUG(4, "%s(), iobase=%#x\n", __FUNCTION__, iobase);
+
+	return 0;
+}
+
+/*
+ * Function irport_get_fcr (speed)
+ *
+ *    Compute value of fcr
+ *
+ */
+static inline unsigned int irport_get_fcr(__u32 speed)
+{
+	unsigned int fcr;    /* FIFO control reg */
+
+	/* Enable fifos */
+	fcr = UART_FCR_ENABLE_FIFO;
+
+	/* 
+	 * Use trigger level 1 to avoid 3 ms. timeout delay at 9600 bps, and
+	 * almost 1,7 ms at 19200 bps. At speeds above that we can just forget
+	 * about this timeout since it will always be fast enough. 
+	 */
+	if (speed < 38400)
+		fcr |= UART_FCR_TRIGGER_1;
+	else 
+		//fcr |= UART_FCR_TRIGGER_14;
+		fcr |= UART_FCR_TRIGGER_8;
+
+	return(fcr);
+}
+ 
+/*
+ * Function irport_change_speed (self, speed)
+ *
+ *    Set speed of IrDA port to specified baudrate
+ *
+ * This function should be called with irq off and spin-lock.
+ */
+static void irport_change_speed(void *priv, __u32 speed)
+{
+	struct irport_cb *self = (struct irport_cb *) priv;
+	int iobase; 
+	unsigned int fcr;    /* FIFO control reg */
+	unsigned int lcr;    /* Line control reg */
+	int divisor;
+
+	IRDA_ASSERT(self != NULL, return;);
+	IRDA_ASSERT(speed != 0, return;);
+
+	IRDA_DEBUG(1, "%s(), Setting speed to: %d - iobase=%#x\n",
+		    __FUNCTION__, speed, self->io.sir_base);
+
+	/* We can't lock, we may be called from a FIR driver - Jean II */
+
+	iobase = self->io.sir_base;
+	
+	/* Update accounting for new speed */
+	self->io.speed = speed;
+
+	/* Turn off interrupts */
+	outb(0, iobase+UART_IER); 
+
+	divisor = SPEED_MAX/speed;
+	
+	/* Get proper fifo configuration */
+	fcr = irport_get_fcr(speed);
+
+	/* IrDA ports use 8N1 */
+	lcr = UART_LCR_WLEN8;
+	
+	outb(UART_LCR_DLAB | lcr, iobase+UART_LCR); /* Set DLAB */
+	outb(divisor & 0xff,      iobase+UART_DLL); /* Set speed */
+	outb(divisor >> 8,	  iobase+UART_DLM);
+	outb(lcr,		  iobase+UART_LCR); /* Set 8N1	*/
+	outb(fcr,		  iobase+UART_FCR); /* Enable FIFO's */
+
+	/* Turn on interrups */
+	/* This will generate a fatal interrupt storm.
+	 * People calling us will do that properly - Jean II */
+	//outb(/*UART_IER_RLSI|*/UART_IER_RDI/*|UART_IER_THRI*/, iobase+UART_IER);
+}
+
+/*
+ * Function __irport_change_speed (instance, state, param)
+ *
+ *    State machine for changing speed of the device. We do it this way since
+ *    we cannot use schedule_timeout() when we are in interrupt context
+ *
+ */
+int __irport_change_speed(struct irda_task *task)
+{
+	struct irport_cb *self;
+	__u32 speed = (__u32) task->param;
+	unsigned long flags = 0;
+	int wasunlocked = 0;
+	int ret = 0;
+
+	IRDA_DEBUG(2, "%s(), <%ld>\n", __FUNCTION__, jiffies); 
+
+	self = (struct irport_cb *) task->instance;
+
+	IRDA_ASSERT(self != NULL, return -1;);
+
+	/* Locking notes : this function may be called from irq context with
+	 * spinlock, via irport_write_wakeup(), or from non-interrupt without
+	 * spinlock (from the task timer). Yuck !
+	 * This is ugly, and unsafe is the spinlock is not already aquired.
+	 * This will be fixed when irda-task get rewritten.
+	 * Jean II */
+	if (!spin_is_locked(&self->lock)) {
+		spin_lock_irqsave(&self->lock, flags);
+		wasunlocked = 1;
+	}
+
+	switch (task->state) {
+	case IRDA_TASK_INIT:
+	case IRDA_TASK_WAIT:
+		/* Are we ready to change speed yet? */
+		if (self->tx_buff.len > 0) {
+			task->state = IRDA_TASK_WAIT;
+
+			/* Try again later */
+			ret = msecs_to_jiffies(20);
+			break;
+		}
+
+		if (self->dongle)
+			irda_task_next_state(task, IRDA_TASK_CHILD_INIT);
+		else
+			irda_task_next_state(task, IRDA_TASK_CHILD_DONE);
+		break;
+	case IRDA_TASK_CHILD_INIT:
+		/* Go to default speed */
+		self->change_speed(self->priv, 9600);
+
+		/* Change speed of dongle */
+		if (irda_task_execute(self->dongle,
+				      self->dongle->issue->change_speed, 
+				      NULL, task, (void *) speed))
+		{
+			/* Dongle need more time to change its speed */
+			irda_task_next_state(task, IRDA_TASK_CHILD_WAIT);
+
+			/* Give dongle 1 sec to finish */
+			ret = msecs_to_jiffies(1000);
+		} else
+			/* Child finished immediately */
+			irda_task_next_state(task, IRDA_TASK_CHILD_DONE);
+		break;
+	case IRDA_TASK_CHILD_WAIT:
+		IRDA_WARNING("%s(), changing speed of dongle timed out!\n", __FUNCTION__);
+		ret = -1;		
+		break;
+	case IRDA_TASK_CHILD_DONE:
+		/* Finally we are ready to change the speed */
+		self->change_speed(self->priv, speed);
+		
+		irda_task_next_state(task, IRDA_TASK_DONE);
+		break;
+	default:
+		IRDA_ERROR("%s(), unknown state %d\n",
+			   __FUNCTION__, task->state);
+		irda_task_next_state(task, IRDA_TASK_DONE);
+		ret = -1;
+		break;
+	}
+	/* Put stuff in the state we found them - Jean II */
+	if(wasunlocked) {
+		spin_unlock_irqrestore(&self->lock, flags);
+	}
+
+	return ret;
+}
+
+/*
+ * Function irport_change_speed_complete (task)
+ *
+ *    Called when the change speed operation completes
+ *
+ */
+static int irport_change_speed_complete(struct irda_task *task)
+{
+	struct irport_cb *self;
+
+	IRDA_DEBUG(1, "%s()\n", __FUNCTION__);
+
+	self = (struct irport_cb *) task->instance;
+
+	IRDA_ASSERT(self != NULL, return -1;);
+	IRDA_ASSERT(self->netdev != NULL, return -1;);
+
+	/* Finished changing speed, so we are not busy any longer */
+	/* Signal network layer so it can try to send the frame */
+
+	netif_wake_queue(self->netdev);
+	
+	return 0;
+}
+
+/*
+ * Function irport_timeout (struct net_device *dev)
+ *
+ *    The networking layer thinks we timed out.
+ *
+ */
+
+static void irport_timeout(struct net_device *dev)
+{
+	struct irport_cb *self;
+	int iobase;
+	int iir, lsr;
+	unsigned long flags;
+
+	self = (struct irport_cb *) dev->priv;
+	IRDA_ASSERT(self != NULL, return;);
+	iobase = self->io.sir_base;
+	
+	IRDA_WARNING("%s: transmit timed out, jiffies = %ld, trans_start = %ld\n",
+		dev->name, jiffies, dev->trans_start);
+	spin_lock_irqsave(&self->lock, flags);
+
+	/* Debug what's happening... */
+
+	/* Get interrupt status */
+	lsr = inb(iobase+UART_LSR);
+	/* Read interrupt register */
+	iir = inb(iobase+UART_IIR);
+	IRDA_DEBUG(0, "%s(), iir=%02x, lsr=%02x, iobase=%#x\n", 
+		   __FUNCTION__, iir, lsr, iobase);
+
+	IRDA_DEBUG(0, "%s(), transmitting=%d, remain=%d, done=%d\n", 
+		   __FUNCTION__, self->transmitting, self->tx_buff.len,
+		   self->tx_buff.data - self->tx_buff.head);
+
+	/* Now, restart the port */
+	irport_start(self);
+	self->change_speed(self->priv, self->io.speed);
+	/* This will re-enable irqs */
+	outb(/*UART_IER_RLSI|*/UART_IER_RDI/*|UART_IER_THRI*/, iobase+UART_IER);
+	dev->trans_start = jiffies;
+	spin_unlock_irqrestore(&self->lock, flags);
+
+	netif_wake_queue(dev);
+}
+ 
+/*
+ * Function irport_wait_hw_transmitter_finish ()
+ *
+ *    Wait for the real end of HW transmission
+ *
+ * The UART is a strict FIFO, and we get called only when we have finished
+ * pushing data to the FIFO, so the maximum amount of time we must wait
+ * is only for the FIFO to drain out.
+ *
+ * We use a simple calibrated loop. We may need to adjust the loop
+ * delay (udelay) to balance I/O traffic and latency. And we also need to
+ * adjust the maximum timeout.
+ * It would probably be better to wait for the proper interrupt,
+ * but it doesn't seem to be available.
+ *
+ * We can't use jiffies or kernel timers because :
+ * 1) We are called from the interrupt handler, which disable softirqs,
+ * so jiffies won't be increased
+ * 2) Jiffies granularity is usually very coarse (10ms), and we don't
+ * want to wait that long to detect stuck hardware.
+ * Jean II
+ */
+
+static void irport_wait_hw_transmitter_finish(struct irport_cb *self)
+{
+	int iobase;
+	int count = 1000;	/* 1 ms */
+	
+	iobase = self->io.sir_base;
+
+	/* Calibrated busy loop */
+	while((count-- > 0) && !(inb(iobase+UART_LSR) & UART_LSR_TEMT))
+		udelay(1);
+
+	if(count == 0)
+		IRDA_DEBUG(0, "%s(): stuck transmitter\n", __FUNCTION__);
+}
+
+/*
+ * Function irport_hard_start_xmit (struct sk_buff *skb, struct net_device *dev)
+ *
+ *    Transmits the current frame until FIFO is full, then
+ *    waits until the next transmitt interrupt, and continues until the
+ *    frame is transmitted.
+ */
+static int irport_hard_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct irport_cb *self;
+	unsigned long flags;
+	int iobase;
+	s32 speed;
+
+	IRDA_DEBUG(1, "%s()\n", __FUNCTION__);
+
+	IRDA_ASSERT(dev != NULL, return 0;);
+	
+	self = (struct irport_cb *) dev->priv;
+	IRDA_ASSERT(self != NULL, return 0;);
+
+	iobase = self->io.sir_base;
+
+	netif_stop_queue(dev);
+
+	/* Make sure tests & speed change are atomic */
+	spin_lock_irqsave(&self->lock, flags);
+
+	/* Check if we need to change the speed */
+	speed = irda_get_next_speed(skb);
+	if ((speed != self->io.speed) && (speed != -1)) {
+		/* Check for empty frame */
+		if (!skb->len) {
+			/*
+			 * We send frames one by one in SIR mode (no
+			 * pipelining), so at this point, if we were sending
+			 * a previous frame, we just received the interrupt
+			 * telling us it is finished (UART_IIR_THRI).
+			 * Therefore, waiting for the transmitter to really
+			 * finish draining the fifo won't take too long.
+			 * And the interrupt handler is not expected to run.
+			 * - Jean II */
+			irport_wait_hw_transmitter_finish(self);
+			/* Better go there already locked - Jean II */
+			irda_task_execute(self, __irport_change_speed, 
+					  irport_change_speed_complete, 
+					  NULL, (void *) speed);
+			dev->trans_start = jiffies;
+			spin_unlock_irqrestore(&self->lock, flags);
+			dev_kfree_skb(skb);
+			return 0;
+		} else
+			self->new_speed = speed;
+	}
+
+	/* Init tx buffer */
+	self->tx_buff.data = self->tx_buff.head;
+
+        /* Copy skb to tx_buff while wrapping, stuffing and making CRC */
+	self->tx_buff.len = async_wrap_skb(skb, self->tx_buff.data, 
+					   self->tx_buff.truesize);
+	
+	self->stats.tx_bytes += self->tx_buff.len;
+
+	/* We are transmitting */
+	self->transmitting = 1;
+
+	/* Turn on transmit finished interrupt. Will fire immediately!  */
+	outb(UART_IER_THRI, iobase+UART_IER); 
+
+	dev->trans_start = jiffies;
+	spin_unlock_irqrestore(&self->lock, flags);
+
+	dev_kfree_skb(skb);
+	
+	return 0;
+}
+        
+/*
+ * Function irport_write (driver)
+ *
+ *    Fill Tx FIFO with transmit data
+ *
+ * Called only from irport_write_wakeup()
+ */
+static inline int irport_write(int iobase, int fifo_size, __u8 *buf, int len)
+{
+	int actual = 0;
+
+	/* Fill FIFO with current frame */
+	while ((actual < fifo_size) && (actual < len)) {
+		/* Transmit next byte */
+		outb(buf[actual], iobase+UART_TX);
+
+		actual++;
+	}
+        
+	return actual;
+}
+
+/*
+ * Function irport_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.
+ *
+ * Called only from irport_interrupt()
+ * Make sure this function is *not* called while we are receiving,
+ * otherwise we will reset fifo and loose data :-(
+ */
+static inline void irport_write_wakeup(struct irport_cb *self)
+{
+	int actual = 0;
+	int iobase;
+	unsigned int fcr;
+
+	IRDA_ASSERT(self != NULL, return;);
+
+	IRDA_DEBUG(4, "%s()\n", __FUNCTION__);
+
+	iobase = self->io.sir_base;
+
+	/* Finished with frame?  */
+	if (self->tx_buff.len > 0)  {
+		/* Write data left in transmit buffer */
+		actual = irport_write(iobase, self->io.fifo_size, 
+				      self->tx_buff.data, self->tx_buff.len);
+		self->tx_buff.data += actual;
+		self->tx_buff.len  -= actual;
+	} else {
+		/* 
+		 *  Now serial buffer is almost free & we can start 
+		 *  transmission of another packet. But first we must check
+		 *  if we need to change the speed of the hardware
+		 */
+		if (self->new_speed) {
+			irport_wait_hw_transmitter_finish(self);
+			irda_task_execute(self, __irport_change_speed, 
+					  irport_change_speed_complete, 
+					  NULL, (void *) self->new_speed);
+			self->new_speed = 0;
+		} else {
+			/* Tell network layer that we want more frames */
+			netif_wake_queue(self->netdev);
+		}
+		self->stats.tx_packets++;
+
+		/* 
+		 * Reset Rx FIFO to make sure that all reflected transmit data
+		 * is discarded. This is needed for half duplex operation
+		 */
+		fcr = irport_get_fcr(self->io.speed);
+		fcr |= UART_FCR_CLEAR_RCVR;
+		outb(fcr, iobase+UART_FCR);
+
+		/* Finished transmitting */
+		self->transmitting = 0;
+
+		/* Turn on receive interrupts */
+		outb(UART_IER_RDI, iobase+UART_IER);
+
+		IRDA_DEBUG(1, "%s() : finished Tx\n", __FUNCTION__);
+	}
+}
+
+/*
+ * Function irport_receive (self)
+ *
+ *    Receive one frame from the infrared port
+ *
+ * Called only from irport_interrupt()
+ */
+static inline void irport_receive(struct irport_cb *self) 
+{
+	int boguscount = 0;
+	int iobase;
+
+	IRDA_ASSERT(self != NULL, return;);
+
+	iobase = self->io.sir_base;
+
+	/*  
+	 * Receive all characters in Rx FIFO, unwrap and unstuff them. 
+         * async_unwrap_char will deliver all found frames  
+	 */
+	do {
+		async_unwrap_char(self->netdev, &self->stats, &self->rx_buff, 
+				  inb(iobase+UART_RX));
+
+		/* Make sure we don't stay here too long */
+		if (boguscount++ > 32) {
+			IRDA_DEBUG(2,"%s(), breaking!\n", __FUNCTION__);
+			break;
+		}
+	} while (inb(iobase+UART_LSR) & UART_LSR_DR);	
+}
+
+/*
+ * Function irport_interrupt (irq, dev_id, regs)
+ *
+ *    Interrupt handler
+ */
+static irqreturn_t irport_interrupt(int irq, void *dev_id,
+				    struct pt_regs *regs) 
+{
+	struct net_device *dev = (struct net_device *) dev_id;
+	struct irport_cb *self;
+	int boguscount = 0;
+	int iobase;
+	int iir, lsr;
+	int handled = 0;
+
+	if (!dev) {
+		IRDA_WARNING("%s() irq %d for unknown device.\n", __FUNCTION__, irq);
+		return IRQ_NONE;
+	}
+	self = (struct irport_cb *) dev->priv;
+
+	spin_lock(&self->lock);
+
+	iobase = self->io.sir_base;
+
+	/* Cut'n'paste interrupt routine from serial.c
+	 * This version try to minimise latency and I/O operations.
+	 * Simplified and modified to enforce half duplex operation.
+	 * - Jean II */
+
+	/* Check status even is iir reg is cleared, more robust and
+	 * eliminate a read on the I/O bus - Jean II */
+	do {
+		/* Get interrupt status ; Clear interrupt */
+		lsr = inb(iobase+UART_LSR);
+		
+		/* Are we receiving or transmitting ? */
+		if(!self->transmitting) {
+			/* Received something ? */
+			if (lsr & UART_LSR_DR)
+				irport_receive(self);
+		} else {
+			/* Room in Tx fifo ? */
+			if (lsr & (UART_LSR_THRE | UART_LSR_TEMT))
+				irport_write_wakeup(self);
+		}
+
+		/* A bit hackish, but working as expected... Jean II */
+		if(lsr & (UART_LSR_THRE | UART_LSR_TEMT | UART_LSR_DR))
+			handled = 1;
+
+		/* Make sure we don't stay here to long */
+		if (boguscount++ > 10) {
+			IRDA_WARNING("%s() irq handler looping : lsr=%02x\n",
+				     __FUNCTION__, lsr);
+			break;
+		}
+
+		/* Read interrupt register */
+ 	        iir = inb(iobase+UART_IIR);
+
+		/* Enable this debug only when no other options and at low
+		 * bit rates, otherwise it may cause Rx overruns (lsr=63).
+		 * - Jean II */
+		IRDA_DEBUG(6, "%s(), iir=%02x, lsr=%02x, iobase=%#x\n", 
+			    __FUNCTION__, iir, lsr, iobase);
+
+		/* As long as interrupt pending... */
+	} while ((iir & UART_IIR_NO_INT) == 0);
+
+	spin_unlock(&self->lock);
+	return IRQ_RETVAL(handled);
+}
+
+/*
+ * Function irport_net_open (dev)
+ *
+ *    Network device is taken up. Usually this is done by "ifconfig irda0 up" 
+ *   
+ */
+static int irport_net_open(struct net_device *dev)
+{
+	struct irport_cb *self;
+	int iobase;
+	char hwname[16];
+	unsigned long flags;
+
+	IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
+
+	IRDA_ASSERT(dev != NULL, return -1;);
+	self = (struct irport_cb *) dev->priv;
+
+	iobase = self->io.sir_base;
+
+	if (request_irq(self->io.irq, self->interrupt, 0, dev->name, 
+			(void *) dev)) {
+		IRDA_DEBUG(0, "%s(), unable to allocate irq=%d\n",
+			   __FUNCTION__, self->io.irq);
+		return -EAGAIN;
+	}
+
+	spin_lock_irqsave(&self->lock, flags);
+	/* Init uart */
+	irport_start(self);
+	/* Set 9600 bauds per default, including at the dongle */
+	irda_task_execute(self, __irport_change_speed, 
+			  irport_change_speed_complete, 
+			  NULL, (void *) 9600);
+	spin_unlock_irqrestore(&self->lock, flags);
+
+
+	/* Give self a hardware name */
+	sprintf(hwname, "SIR @ 0x%03x", self->io.sir_base);
+
+	/* 
+	 * Open new IrLAP layer instance, now that everything should be
+	 * initialized properly 
+	 */
+	self->irlap = irlap_open(dev, &self->qos, hwname);
+
+	/* Ready to play! */
+
+	netif_start_queue(dev);
+
+	return 0;
+}
+
+/*
+ * Function irport_net_close (self)
+ *
+ *    Network device is taken down. Usually this is done by 
+ *    "ifconfig irda0 down" 
+ */
+static int irport_net_close(struct net_device *dev)
+{
+	struct irport_cb *self;
+	int iobase;
+	unsigned long flags;
+
+	IRDA_DEBUG(4, "%s()\n", __FUNCTION__);
+
+	IRDA_ASSERT(dev != NULL, return -1;);
+	self = (struct irport_cb *) dev->priv;
+
+	IRDA_ASSERT(self != NULL, return -1;);
+
+	iobase = self->io.sir_base;
+
+	/* Stop device */
+	netif_stop_queue(dev);
+	
+	/* Stop and remove instance of IrLAP */
+	if (self->irlap)
+		irlap_close(self->irlap);
+	self->irlap = NULL;
+
+	spin_lock_irqsave(&self->lock, flags);
+	irport_stop(self);
+	spin_unlock_irqrestore(&self->lock, flags);
+
+	free_irq(self->io.irq, dev);
+
+	return 0;
+}
+
+/*
+ * Function irport_is_receiving (self)
+ *
+ *    Returns true is we are currently receiving data
+ *
+ */
+static inline int irport_is_receiving(struct irport_cb *self)
+{
+	return (self->rx_buff.state != OUTSIDE_FRAME);
+}
+
+/*
+ * Function irport_set_dtr_rts (tty, dtr, rts)
+ *
+ *    This function can be used by dongles etc. to set or reset the status
+ *    of the dtr and rts lines
+ */
+static int irport_set_dtr_rts(struct net_device *dev, int dtr, int rts)
+{
+	struct irport_cb *self = dev->priv;
+	int iobase;
+
+	IRDA_ASSERT(self != NULL, return -1;);
+
+	iobase = self->io.sir_base;
+
+	if (dtr)
+		dtr = UART_MCR_DTR;
+	if (rts)
+		rts = UART_MCR_RTS;
+
+	outb(dtr|rts|UART_MCR_OUT2, iobase+UART_MCR);
+
+	return 0;
+}
+
+static int irport_raw_write(struct net_device *dev, __u8 *buf, int len)
+{
+	struct irport_cb *self = (struct irport_cb *) dev->priv;
+	int actual = 0;
+	int iobase;
+
+	IRDA_ASSERT(self != NULL, return -1;);
+
+	iobase = self->io.sir_base;
+
+	/* Tx FIFO should be empty! */
+	if (!(inb(iobase+UART_LSR) & UART_LSR_THRE)) {
+		IRDA_DEBUG( 0, "%s(), failed, fifo not empty!\n", __FUNCTION__);
+		return -1;
+	}
+        
+	/* Fill FIFO with current frame */
+	while (actual < len) {
+		/* Transmit next byte */
+		outb(buf[actual], iobase+UART_TX);
+		actual++;
+	}
+
+	return actual;
+}
+
+/*
+ * Function irport_net_ioctl (dev, rq, cmd)
+ *
+ *    Process IOCTL commands for this device
+ *
+ */
+static int irport_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+	struct if_irda_req *irq = (struct if_irda_req *) rq;
+	struct irport_cb *self;
+	dongle_t *dongle;
+	unsigned long flags;
+	int ret = 0;
+
+	IRDA_ASSERT(dev != NULL, return -1;);
+
+	self = dev->priv;
+
+	IRDA_ASSERT(self != NULL, return -1;);
+
+	IRDA_DEBUG(2, "%s(), %s, (cmd=0x%X)\n", __FUNCTION__, dev->name, cmd);
+	
+	switch (cmd) {
+	case SIOCSBANDWIDTH: /* Set bandwidth */
+		if (!capable(CAP_NET_ADMIN))
+			ret = -EPERM;
+                else
+			irda_task_execute(self, __irport_change_speed, NULL, 
+					  NULL, (void *) irq->ifr_baudrate);
+		break;
+	case SIOCSDONGLE: /* Set dongle */
+		if (!capable(CAP_NET_ADMIN)) {
+			ret = -EPERM;
+			break;
+		}
+
+		/* Locking :
+		 * irda_device_dongle_init() can't be locked.
+		 * irda_task_execute() doesn't need to be locked.
+		 * Jean II
+		 */
+
+		/* Initialize dongle */
+		dongle = irda_device_dongle_init(dev, irq->ifr_dongle);
+		if (!dongle)
+			break;
+		
+		dongle->set_mode    = NULL;
+		dongle->read        = NULL;
+		dongle->write       = irport_raw_write;
+		dongle->set_dtr_rts = irport_set_dtr_rts;
+		
+		/* Now initialize the dongle!  */
+		dongle->issue->open(dongle, &self->qos);
+		
+		/* Reset dongle */
+		irda_task_execute(dongle, dongle->issue->reset, NULL, NULL, 
+				  NULL);	
+
+		/* Make dongle available to driver only now to avoid
+		 * race conditions - Jean II */
+		self->dongle = dongle;
+		break;
+	case SIOCSMEDIABUSY: /* Set media busy */
+		if (!capable(CAP_NET_ADMIN)) {
+			ret = -EPERM;
+			break;
+		}
+
+		irda_device_set_media_busy(self->netdev, TRUE);
+		break;
+	case SIOCGRECEIVING: /* Check if we are receiving right now */
+		irq->ifr_receiving = irport_is_receiving(self);
+		break;
+	case SIOCSDTRRTS:
+		if (!capable(CAP_NET_ADMIN)) {
+			ret = -EPERM;
+			break;
+		}
+
+		/* No real need to lock... */
+		spin_lock_irqsave(&self->lock, flags);
+		irport_set_dtr_rts(dev, irq->ifr_dtr, irq->ifr_rts);
+		spin_unlock_irqrestore(&self->lock, flags);
+		break;
+	default:
+		ret = -EOPNOTSUPP;
+	}
+	
+	return ret;
+}
+
+static struct net_device_stats *irport_net_get_stats(struct net_device *dev)
+{
+	struct irport_cb *self = (struct irport_cb *) dev->priv;
+	
+	return &self->stats;
+}
+
+static int __init irport_init(void)
+{
+ 	int i;
+
+ 	for (i=0; (io[i] < 2000) && (i < 4); i++) {
+ 		if (irport_open(i, io[i], irq[i]) != NULL)
+ 			return 0;
+ 	}
+	/* 
+	 * Maybe something failed, but we can still be usable for FIR drivers 
+	 */
+ 	return 0;
+}
+
+/*
+ * Function irport_cleanup ()
+ *
+ *    Close all configured ports
+ *
+ */
+static void __exit irport_cleanup(void)
+{
+ 	int i;
+
+        IRDA_DEBUG( 4, "%s()\n", __FUNCTION__);
+
+	for (i=0; i < 4; i++) {
+ 		if (dev_self[i])
+ 			irport_close(dev_self[i]);
+ 	}
+}
+
+MODULE_PARM(io, "1-4i");
+MODULE_PARM_DESC(io, "Base I/O addresses");
+MODULE_PARM(irq, "1-4i");
+MODULE_PARM_DESC(irq, "IRQ lines");
+
+MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>");
+MODULE_DESCRIPTION("Half duplex serial driver for IrDA SIR mode");
+MODULE_LICENSE("GPL");
+
+module_init(irport_init);
+module_exit(irport_cleanup);
+
diff --git a/drivers/net/irda/irport.h b/drivers/net/irda/irport.h
new file mode 100644
index 0000000..fc89c8c
--- /dev/null
+++ b/drivers/net/irda/irport.h
@@ -0,0 +1,80 @@
+/*********************************************************************
+ *                
+ * Filename:      irport.h
+ * Version:       0.1
+ * Description:   Serial driver for IrDA
+ * Status:        Experimental.
+ * Author:        Dag Brattli <dagb@cs.uit.no>
+ * Created at:    Sun Aug  3 13:49:59 1997
+ * Modified at:   Fri Jan 14 10:21:10 2000
+ * Modified by:   Dag Brattli <dagb@cs.uit.no>
+ * 
+ *     Copyright (c) 1997, 1998-2000 Dag Brattli <dagb@cs.uit.no>
+ *     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.
+ *
+ ********************************************************************/
+
+#ifndef IRPORT_H
+#define IRPORT_H
+
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/types.h>
+#include <linux/spinlock.h>
+
+#include <net/irda/irda_device.h>
+
+#define SPEED_DEFAULT 9600
+#define SPEED_MAX     115200
+
+/*
+ * These are the supported serial types.
+ */
+#define PORT_UNKNOWN    0
+#define PORT_8250       1
+#define PORT_16450      2
+#define PORT_16550      3
+#define PORT_16550A     4
+#define PORT_CIRRUS     5
+#define PORT_16650      6
+#define PORT_MAX        6  
+
+#define FRAME_MAX_SIZE 2048
+
+struct irport_cb {
+	struct net_device *netdev; /* Yes! we are some kind of netdevice */
+	struct net_device_stats stats;
+
+	struct irlap_cb *irlap;    /* The link layer we are attached to */
+
+	chipio_t io;               /* IrDA controller information */
+	iobuff_t tx_buff;          /* Transmit buffer */
+	iobuff_t rx_buff;          /* Receive buffer */
+
+	struct qos_info qos;       /* QoS capabilities for this device */
+	dongle_t *dongle;          /* Dongle driver */
+
+ 	__u32 flags;               /* Interface flags */
+	__u32 new_speed;
+	int mode;
+	int index;                 /* Instance index */
+	int transmitting;	   /* Are we transmitting ? */
+
+	spinlock_t lock;           /* For serializing operations */
+
+	/* For piggyback drivers */
+	void *priv;                
+	void (*change_speed)(void *priv, __u32 speed);
+	int (*interrupt)(int irq, void *dev_id, struct pt_regs *regs);
+};
+
+#endif /* IRPORT_H */
diff --git a/drivers/net/irda/irtty-sir.c b/drivers/net/irda/irtty-sir.c
new file mode 100644
index 0000000..7d23aa3
--- /dev/null
+++ b/drivers/net/irda/irtty-sir.c
@@ -0,0 +1,642 @@
+/*********************************************************************
+ *                
+ * 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 <linux/delay.h>
+
+#include <net/irda/irda.h>
+#include <net/irda/irda_device.h>
+
+#include "sir-dev.h"
+#include "irtty-sir.h"
+
+static int qos_mtt_bits = 0x03;      /* 5 ms or more */
+
+module_param(qos_mtt_bits, int, 0);
+MODULE_PARM_DESC(qos_mtt_bits, "Minimum Turn Time");
+
+/* ------------------------------------------------------- */
+
+/* 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;
+
+	IRDA_ASSERT(priv != NULL, return -1;);
+	IRDA_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;
+
+	IRDA_ASSERT(priv != NULL, return;);
+	IRDA_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 {
+		msleep(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;
+
+	IRDA_ASSERT(priv != NULL, return -1;);
+	IRDA_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 set = 0;
+	int clear = 0;
+
+	IRDA_ASSERT(priv != NULL, return -1;);
+	IRDA_ASSERT(priv->magic == IRTTY_MAGIC, return -1;);
+
+	if (rts)
+		set |= TIOCM_RTS;
+	else
+		clear |= TIOCM_RTS;
+	if (dtr)
+		set |= TIOCM_DTR;
+	else
+		clear |= TIOCM_DTR;
+
+	/*
+	 * We can't use ioctl() because it expects a non-null file structure,
+	 * and we don't have that here.
+	 * This function is not yet defined for all tty driver, so
+	 * let's be careful... Jean II
+	 */
+	IRDA_ASSERT(priv->tty->driver->tiocmset != NULL, return -1;);
+	priv->tty->driver->tiocmset(priv->tty, NULL, set, clear);
+
+	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.
+ */
+
+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;
+
+	IRDA_ASSERT(priv != NULL, return -1;);
+	IRDA_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, 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;
+
+	IRDA_ASSERT(priv != NULL, return;);
+	IRDA_ASSERT(priv->magic == IRTTY_MAGIC, return;);
+
+	if (unlikely(count==0))		/* yes, this happens */
+		return;
+
+	dev = priv->dev;
+	if (!dev) {
+		IRDA_WARNING("%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;
+
+	IRDA_ASSERT(priv != NULL, return 0;);
+	IRDA_ASSERT(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;
+
+	IRDA_ASSERT(priv != NULL, return;);
+	IRDA_ASSERT(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();
+}
+
+/*****************************************************************/
+
+/* serialize ldisc open/close with sir_dev */
+static DECLARE_MUTEX(irtty_sem);
+
+/* 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;
+}
+
+/* ------------------------------------------------------- */
+
+static 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 err = 0;
+
+	IRDA_ASSERT(priv != NULL, return -ENODEV;);
+	IRDA_ASSERT(priv->magic == IRTTY_MAGIC, return -EBADR;);
+
+	IRDA_DEBUG(3, "%s(cmd=0x%X)\n", __FUNCTION__, cmd);
+
+	dev = priv->dev;
+	IRDA_ASSERT(dev != NULL, return -1;);
+
+	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:
+		IRDA_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 __user *)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;
+	int ret = 0;
+
+	/* Module stuff handled via irda_ldisc.owner - Jean II */
+
+	/* 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);
+	
+	/* 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, tty->name);
+	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);
+
+	IRDA_DEBUG(0, "%s - %s: irda line discipline opened\n", __FUNCTION__, tty->name);
+
+	return 0;
+
+out_put:
+	sirdev_put_instance(dev);
+out:
+	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;
+
+	IRDA_ASSERT(priv != NULL, return;);
+	IRDA_ASSERT(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 = NULL;
+
+	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);
+
+	IRDA_DEBUG(0, "%s - %s: irda line discipline closed\n", __FUNCTION__, tty->name);
+}
+
+/* ------------------------------------------------------- */
+
+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,
+	.owner		= THIS_MODULE,
+};
+
+/* ------------------------------------------------------- */
+
+static int __init irtty_sir_init(void)
+{
+	int err;
+
+	if ((err = tty_register_ldisc(N_IRDA, &irda_ldisc)) != 0)
+		IRDA_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))) {
+		IRDA_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_ALIAS_LDISC(N_IRDA);
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/net/irda/irtty-sir.h b/drivers/net/irda/irtty-sir.h
new file mode 100644
index 0000000..b132d8f
--- /dev/null
+++ b/drivers/net/irda/irtty-sir.h
@@ -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 --git a/drivers/net/irda/litelink-sir.c b/drivers/net/irda/litelink-sir.c
new file mode 100644
index 0000000..73261c5
--- /dev/null
+++ b/drivers/net/irda/litelink-sir.c
@@ -0,0 +1,209 @@
+/*********************************************************************
+ *                
+ * Filename:      litelink.c
+ * Version:       1.1
+ * Description:   Driver for the Parallax LiteLink dongle
+ * Status:        Stable
+ * Author:        Dag Brattli <dagb@cs.uit.no>
+ * Created at:    Fri May  7 12:50:33 1999
+ * Modified at:   Fri Dec 17 09:14:23 1999
+ * Modified by:   Dag Brattli <dagb@cs.uit.no>
+ * 
+ *     Copyright (c) 1999 Dag Brattli, 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
+ *     
+ ********************************************************************/
+
+/*
+ * Modified at:   Thu Jan 15 2003
+ * Modified by:   Eugene Crosser <crosser@average.org>
+ *
+ * Convert to "new" IRDA infrastructure for kernel 2.6
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+
+#include <net/irda/irda.h>
+
+#include "sir-dev.h"
+
+#define MIN_DELAY 25      /* 15 us, but wait a little more to be sure */
+#define MAX_DELAY 10000   /* 1 ms */
+
+static int litelink_open(struct sir_dev *dev);
+static int litelink_close(struct sir_dev *dev);
+static int litelink_change_speed(struct sir_dev *dev, unsigned speed);
+static int litelink_reset(struct sir_dev *dev);
+
+/* These are the baudrates supported - 9600 must be last one! */
+static unsigned baud_rates[] = { 115200, 57600, 38400, 19200, 9600 };
+
+static struct dongle_driver litelink = {
+	.owner		= THIS_MODULE,
+	.driver_name	= "Parallax LiteLink",
+	.type		= IRDA_LITELINK_DONGLE,
+	.open		= litelink_open,
+	.close		= litelink_close,
+	.reset		= litelink_reset,
+	.set_speed	= litelink_change_speed,
+};
+
+static int __init litelink_sir_init(void)
+{
+	return irda_register_dongle(&litelink);
+}
+
+static void __exit litelink_sir_cleanup(void)
+{
+	irda_unregister_dongle(&litelink);
+}
+
+static int litelink_open(struct sir_dev *dev)
+{
+	struct qos_info *qos = &dev->qos;
+
+	IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
+
+	/* Power up dongle */
+	sirdev_set_dtr_rts(dev, TRUE, TRUE);
+
+	/* Set the speeds we can accept */
+	qos->baud_rate.bits &= IR_115200|IR_57600|IR_38400|IR_19200|IR_9600;
+	qos->min_turn_time.bits = 0x7f; /* Needs 0.01 ms */
+	irda_qos_bits_to_value(qos);
+
+	/* irda thread waits 50 msec for power settling */
+
+	return 0;
+}
+
+static int litelink_close(struct sir_dev *dev)
+{
+	IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
+
+	/* Power off dongle */
+	sirdev_set_dtr_rts(dev, FALSE, FALSE);
+
+	return 0;
+}
+
+/*
+ * Function litelink_change_speed (task)
+ *
+ *    Change speed of the Litelink dongle. To cycle through the available 
+ *    baud rates, pulse RTS low for a few ms.  
+ */
+static int litelink_change_speed(struct sir_dev *dev, unsigned speed)
+{
+        int i;
+
+	IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
+
+	/* dongle already reset by irda-thread - current speed (dongle and
+	 * port) is the default speed (115200 for litelink!)
+	 */
+
+	/* Cycle through avaiable baudrates until we reach the correct one */
+	for (i = 0; baud_rates[i] != speed; i++) {
+
+		/* end-of-list reached due to invalid speed request */
+		if (baud_rates[i] == 9600)
+			break;
+
+		/* Set DTR, clear RTS */
+		sirdev_set_dtr_rts(dev, FALSE, TRUE);
+
+		/* Sleep a minimum of 15 us */
+		udelay(MIN_DELAY);
+
+		/* Set DTR, Set RTS */
+		sirdev_set_dtr_rts(dev, TRUE, TRUE);
+
+		/* Sleep a minimum of 15 us */
+		udelay(MIN_DELAY);
+        }
+
+	dev->speed = baud_rates[i];
+
+	/* invalid baudrate should not happen - but if, we return -EINVAL and
+	 * the dongle configured for 9600 so the stack has a chance to recover
+	 */
+
+	return (dev->speed == speed) ? 0 : -EINVAL;
+}
+
+/*
+ * Function litelink_reset (task)
+ *
+ *      Reset the Litelink type dongle.
+ *
+ */
+static int litelink_reset(struct sir_dev *dev)
+{
+	IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
+
+	/* probably the power-up can be dropped here, but with only
+	 * 15 usec delay it's not worth the risk unless somebody with
+	 * the hardware confirms it doesn't break anything...
+	 */
+
+	/* Power on dongle */
+	sirdev_set_dtr_rts(dev, TRUE, TRUE);
+
+	/* Sleep a minimum of 15 us */
+	udelay(MIN_DELAY);
+
+	/* Clear RTS to reset dongle */
+	sirdev_set_dtr_rts(dev, TRUE, FALSE);
+
+	/* Sleep a minimum of 15 us */
+	udelay(MIN_DELAY);
+
+	/* Go back to normal mode */
+	sirdev_set_dtr_rts(dev, TRUE, TRUE);
+
+	/* Sleep a minimum of 15 us */
+	udelay(MIN_DELAY);
+
+	/* This dongles speed defaults to 115200 bps */
+	dev->speed = 115200;
+
+	return 0;
+}
+
+MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>");
+MODULE_DESCRIPTION("Parallax Litelink dongle driver");	
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("irda-dongle-5"); /* IRDA_LITELINK_DONGLE */
+
+/*
+ * Function init_module (void)
+ *
+ *    Initialize Litelink module
+ *
+ */
+module_init(litelink_sir_init);
+
+/*
+ * Function cleanup_module (void)
+ *
+ *    Cleanup Litelink module
+ *
+ */
+module_exit(litelink_sir_cleanup);
diff --git a/drivers/net/irda/litelink.c b/drivers/net/irda/litelink.c
new file mode 100644
index 0000000..7db1143
--- /dev/null
+++ b/drivers/net/irda/litelink.c
@@ -0,0 +1,179 @@
+/*********************************************************************
+ *                
+ * Filename:      litelink.c
+ * Version:       1.1
+ * Description:   Driver for the Parallax LiteLink dongle
+ * Status:        Stable
+ * Author:        Dag Brattli <dagb@cs.uit.no>
+ * Created at:    Fri May  7 12:50:33 1999
+ * Modified at:   Fri Dec 17 09:14:23 1999
+ * Modified by:   Dag Brattli <dagb@cs.uit.no>
+ * 
+ *     Copyright (c) 1999 Dag Brattli, 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/tty.h>
+#include <linux/init.h>
+
+#include <net/irda/irda.h>
+#include <net/irda/irda_device.h>
+
+#define MIN_DELAY 25      /* 15 us, but wait a little more to be sure */
+#define MAX_DELAY 10000   /* 1 ms */
+
+static void litelink_open(dongle_t *self, struct qos_info *qos);
+static void litelink_close(dongle_t *self);
+static int  litelink_change_speed(struct irda_task *task);
+static int  litelink_reset(struct irda_task *task);
+
+/* These are the baudrates supported */
+static __u32 baud_rates[] = { 115200, 57600, 38400, 19200, 9600 };
+
+static struct dongle_reg dongle = {
+	.type = IRDA_LITELINK_DONGLE,
+	.open = litelink_open,
+	.close = litelink_close,
+	.reset = litelink_reset,
+	.change_speed = litelink_change_speed,
+	.owner = THIS_MODULE,
+};
+
+static int __init litelink_init(void)
+{
+	return irda_device_register_dongle(&dongle);
+}
+
+static void __exit litelink_cleanup(void)
+{
+	irda_device_unregister_dongle(&dongle);
+}
+
+static void litelink_open(dongle_t *self, struct qos_info *qos)
+{
+	qos->baud_rate.bits &= IR_9600|IR_19200|IR_38400|IR_57600|IR_115200;
+	qos->min_turn_time.bits = 0x7f; /* Needs 0.01 ms */
+}
+
+static void litelink_close(dongle_t *self)
+{
+	/* Power off dongle */
+	self->set_dtr_rts(self->dev, FALSE, FALSE);
+}
+
+/*
+ * Function litelink_change_speed (task)
+ *
+ *    Change speed of the Litelink dongle. To cycle through the available 
+ *    baud rates, pulse RTS low for a few ms.  
+ */
+static int litelink_change_speed(struct irda_task *task)
+{
+	dongle_t *self = (dongle_t *) task->instance;
+	__u32 speed = (__u32) task->param;
+        int i;
+	
+	/* Clear RTS to reset dongle */
+	self->set_dtr_rts(self->dev, TRUE, FALSE);
+
+	/* Sleep a minimum of 15 us */
+	udelay(MIN_DELAY);
+
+	/* Go back to normal mode */
+	self->set_dtr_rts(self->dev, TRUE, TRUE);
+	
+	/* Sleep a minimum of 15 us */
+	udelay(MIN_DELAY);
+	
+	/* Cycle through avaiable baudrates until we reach the correct one */
+	for (i=0; i<5 && baud_rates[i] != speed; i++) {
+		/* Set DTR, clear RTS */
+		self->set_dtr_rts(self->dev, FALSE, TRUE);
+		
+		/* Sleep a minimum of 15 us */
+		udelay(MIN_DELAY);
+		
+		/* Set DTR, Set RTS */
+		self->set_dtr_rts(self->dev, TRUE, TRUE);
+		
+		/* Sleep a minimum of 15 us */
+		udelay(MIN_DELAY);
+        }
+	irda_task_next_state(task, IRDA_TASK_DONE);
+
+	return 0;
+}
+
+/*
+ * Function litelink_reset (task)
+ *
+ *      Reset the Litelink type dongle.
+ *
+ */
+static int litelink_reset(struct irda_task *task)
+{
+	dongle_t *self = (dongle_t *) task->instance;
+
+	/* Power on dongle */
+	self->set_dtr_rts(self->dev, TRUE, TRUE);
+
+	/* Sleep a minimum of 15 us */
+	udelay(MIN_DELAY);
+
+	/* Clear RTS to reset dongle */
+	self->set_dtr_rts(self->dev, TRUE, FALSE);
+
+	/* Sleep a minimum of 15 us */
+	udelay(MIN_DELAY);
+
+	/* Go back to normal mode */
+	self->set_dtr_rts(self->dev, TRUE, TRUE);
+	
+	/* Sleep a minimum of 15 us */
+	udelay(MIN_DELAY);
+
+	/* This dongles speed defaults to 115200 bps */
+	self->speed = 115200;
+
+	irda_task_next_state(task, IRDA_TASK_DONE);
+
+	return 0;
+}
+
+MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>");
+MODULE_DESCRIPTION("Parallax Litelink dongle driver");	
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("irda-dongle-5"); /* IRDA_LITELINK_DONGLE */
+		
+/*
+ * Function init_module (void)
+ *
+ *    Initialize Litelink module
+ *
+ */
+module_init(litelink_init);
+
+/*
+ * Function cleanup_module (void)
+ *
+ *    Cleanup Litelink module
+ *
+ */
+module_exit(litelink_cleanup);
diff --git a/drivers/net/irda/ma600-sir.c b/drivers/net/irda/ma600-sir.c
new file mode 100644
index 0000000..ebed168
--- /dev/null
+++ b/drivers/net/irda/ma600-sir.c
@@ -0,0 +1,264 @@
+/*********************************************************************
+ *                
+ * Filename:      ma600.c
+ * Version:       0.1
+ * Description:   Implementation of the MA600 dongle
+ * Status:        Experimental.
+ * Author:        Leung <95Etwl@alumni.ee.ust.hk> http://www.engsvr.ust/~eetwl95
+ * Created at:    Sat Jun 10 20:02:35 2000
+ * Modified at:   Sat Aug 16 09:34:13 2003
+ * Modified by:   Martin Diehl <mad@mdiehl.de> (modified for new sir_dev)
+ *
+ * Note: very thanks to Mr. Maru Wang <maru@mobileaction.com.tw> for providing 
+ *       information on the MA600 dongle
+ * 
+ *     Copyright (c) 2000 Leung, 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 <linux/sched.h>
+
+#include <net/irda/irda.h>
+
+#include "sir-dev.h"
+
+static int ma600_open(struct sir_dev *);
+static int ma600_close(struct sir_dev *);
+static int ma600_change_speed(struct sir_dev *, unsigned);
+static int ma600_reset(struct sir_dev *);
+
+/* control byte for MA600 */
+#define MA600_9600	0x00
+#define MA600_19200	0x01
+#define MA600_38400	0x02
+#define MA600_57600	0x03
+#define MA600_115200	0x04
+#define MA600_DEV_ID1	0x05
+#define MA600_DEV_ID2	0x06
+#define MA600_2400	0x08
+
+static struct dongle_driver ma600 = {
+	.owner          = THIS_MODULE,
+	.driver_name    = "MA600",
+	.type           = IRDA_MA600_DONGLE,
+	.open           = ma600_open,
+	.close          = ma600_close,
+	.reset          = ma600_reset,
+	.set_speed      = ma600_change_speed,
+};
+
+
+static int __init ma600_sir_init(void)
+{
+	IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
+	return irda_register_dongle(&ma600);
+}
+
+static void __exit ma600_sir_cleanup(void)
+{
+	IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
+	irda_unregister_dongle(&ma600);
+}
+
+/*
+	Power on:
+		(0) Clear RTS and DTR for 1 second
+		(1) Set RTS and DTR for 1 second
+		(2) 9600 bps now
+	Note: assume RTS, DTR are clear before
+*/
+static int ma600_open(struct sir_dev *dev)
+{
+	struct qos_info *qos = &dev->qos;
+
+	IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
+
+	sirdev_set_dtr_rts(dev, TRUE, TRUE);
+
+	/* Explicitly set the speeds we can accept */
+	qos->baud_rate.bits &= IR_2400|IR_9600|IR_19200|IR_38400
+				|IR_57600|IR_115200;
+	/* Hm, 0x01 means 10ms - for >= 1ms we would need 0x07 */
+	qos->min_turn_time.bits = 0x01;		/* Needs at least 1 ms */	
+	irda_qos_bits_to_value(qos);
+
+	/* irda thread waits 50 msec for power settling */
+
+	return 0;
+}
+
+static int ma600_close(struct sir_dev *dev)
+{
+	IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
+
+	/* Power off dongle */
+	sirdev_set_dtr_rts(dev, FALSE, FALSE);
+
+	return 0;
+}
+
+static __u8 get_control_byte(__u32 speed)
+{
+	__u8 byte;
+
+	switch (speed) {
+	default:
+	case 115200:
+		byte = MA600_115200;
+		break;
+	case 57600:
+		byte = MA600_57600;
+		break;
+	case 38400:
+		byte = MA600_38400;
+		break;
+	case 19200:
+		byte = MA600_19200;
+		break;
+	case 9600:
+		byte = MA600_9600;
+		break;
+	case 2400:
+		byte = MA600_2400;
+		break;
+	}
+
+	return byte;
+}
+
+/*
+ * Function ma600_change_speed (dev, speed)
+ *
+ *    Set the speed for the MA600 type dongle.
+ *
+ *    The dongle has already been reset to a known state (dongle default)
+ *    We cycle through speeds by pulsing RTS low and then high.
+ */
+
+/*
+ * Function ma600_change_speed (dev, speed)
+ *
+ *    Set the speed for the MA600 type dongle.
+ *
+ *    Algorithm
+ *    1. Reset (already done by irda thread state machine)
+ *    2. clear RTS, set DTR and wait for 1ms
+ *    3. send Control Byte to the MA600 through TXD to set new baud rate
+ *       wait until the stop bit of Control Byte is sent (for 9600 baud rate, 
+ *       it takes about 10 msec)
+ *    4. set RTS, set DTR (return to NORMAL Operation)
+ *    5. wait at least 10 ms, new setting (baud rate, etc) takes effect here 
+ *       after
+ */
+
+/* total delays are only about 20ms - let's just sleep for now to
+ * avoid the state machine complexity before we get things working
+ */
+
+static int ma600_change_speed(struct sir_dev *dev, unsigned speed)
+{
+	u8	byte;
+	
+	IRDA_DEBUG(2, "%s(), speed=%d (was %d)\n", __FUNCTION__,
+		speed, dev->speed);
+
+	/* dongle already reset, dongle and port at default speed (9600) */
+
+	/* Set RTS low for 1 ms */
+	sirdev_set_dtr_rts(dev, TRUE, FALSE);
+	mdelay(1);
+
+	/* Write control byte */
+	byte = get_control_byte(speed);
+	sirdev_raw_write(dev, &byte, sizeof(byte));
+
+	/* Wait at least 10ms: fake wait_until_sent - 10 bits at 9600 baud*/
+	msleep(15);					/* old ma600 uses 15ms */
+
+#if 1
+	/* read-back of the control byte. ma600 is the first dongle driver
+	 * which uses this so there might be some unidentified issues.
+	 * Disable this in case of problems with readback.
+	 */
+
+	sirdev_raw_read(dev, &byte, sizeof(byte));
+	if (byte != get_control_byte(speed))  {
+		IRDA_WARNING("%s(): bad control byte read-back %02x != %02x\n",
+			     __FUNCTION__, (unsigned) byte,
+			     (unsigned) get_control_byte(speed));
+		return -1;
+	}
+	else
+		IRDA_DEBUG(2, "%s() control byte write read OK\n", __FUNCTION__);
+#endif
+
+	/* Set DTR, Set RTS */
+	sirdev_set_dtr_rts(dev, TRUE, TRUE);
+
+	/* Wait at least 10ms */
+	msleep(10);
+
+	/* dongle is now switched to the new speed */
+	dev->speed = speed;
+
+	return 0;
+}
+
+/*
+ * Function ma600_reset (dev)
+ *
+ *      This function resets the ma600 dongle.
+ *
+ *      Algorithm:
+ *    	  0. DTR=0, RTS=1 and wait 10 ms
+ *    	  1. DTR=1, RTS=1 and wait 10 ms
+ *        2. 9600 bps now
+ */
+
+/* total delays are only about 20ms - let's just sleep for now to
+ * avoid the state machine complexity before we get things working
+ */
+
+int ma600_reset(struct sir_dev *dev)
+{
+	IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
+
+	/* Reset the dongle : set DTR low for 10 ms */
+	sirdev_set_dtr_rts(dev, FALSE, TRUE);
+	msleep(10);
+
+	/* Go back to normal mode */
+	sirdev_set_dtr_rts(dev, TRUE, TRUE);
+	msleep(10);
+
+	dev->speed = 9600;      /* That's the dongle-default */
+
+	return 0;
+}
+
+MODULE_AUTHOR("Leung <95Etwl@alumni.ee.ust.hk> http://www.engsvr.ust/~eetwl95");
+MODULE_DESCRIPTION("MA600 dongle driver version 0.1");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("irda-dongle-11"); /* IRDA_MA600_DONGLE */
+		
+module_init(ma600_sir_init);
+module_exit(ma600_sir_cleanup);
+
diff --git a/drivers/net/irda/ma600.c b/drivers/net/irda/ma600.c
new file mode 100644
index 0000000..f5e6836
--- /dev/null
+++ b/drivers/net/irda/ma600.c
@@ -0,0 +1,354 @@
+/*********************************************************************
+ *                
+ * Filename:      ma600.c
+ * Version:       0.1
+ * Description:   Implementation of the MA600 dongle
+ * Status:        Experimental.
+ * Author:        Leung <95Etwl@alumni.ee.ust.hk> http://www.engsvr.ust/~eetwl95
+ * Created at:    Sat Jun 10 20:02:35 2000
+ * Modified at:   
+ * Modified by:   
+ *
+ * Note: very thanks to Mr. Maru Wang <maru@mobileaction.com.tw> for providing 
+ *       information on the MA600 dongle
+ * 
+ *     Copyright (c) 2000 Leung, 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
+ *     
+ ********************************************************************/
+
+/* define this macro for release version */
+//#define NDEBUG
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/tty.h>
+#include <linux/init.h>
+
+#include <net/irda/irda.h>
+#include <net/irda/irda_device.h>
+
+#ifndef NDEBUG
+	#undef IRDA_DEBUG
+	#define IRDA_DEBUG(n, args...) (printk(KERN_DEBUG args))
+
+	#undef ASSERT
+	#define ASSERT(expr, func) \
+	if(!(expr)) { \
+	        printk( "Assertion failed! %s,%s,%s,line=%d\n",\
+        	#expr,__FILE__,__FUNCTION__,__LINE__); \
+	        func}
+#endif
+
+/* convert hex value to ascii hex */
+static const char hexTbl[] = "0123456789ABCDEF";
+
+
+static void ma600_open(dongle_t *self, struct qos_info *qos);
+static void ma600_close(dongle_t *self);
+static int  ma600_change_speed(struct irda_task *task);
+static int  ma600_reset(struct irda_task *task);
+
+/* control byte for MA600 */
+#define MA600_9600	0x00
+#define MA600_19200	0x01
+#define MA600_38400	0x02
+#define MA600_57600	0x03
+#define MA600_115200	0x04
+#define MA600_DEV_ID1	0x05
+#define MA600_DEV_ID2	0x06
+#define MA600_2400	0x08
+
+static struct dongle_reg dongle = {
+	.type = IRDA_MA600_DONGLE,
+	.open = ma600_open,
+	.close = ma600_close,
+	.reset = ma600_reset,
+	.change_speed = ma600_change_speed,
+	.owner = THIS_MODULE,
+};
+
+static int __init ma600_init(void)
+{
+	IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
+	return irda_device_register_dongle(&dongle);
+}
+
+static void __exit ma600_cleanup(void)
+{
+	IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
+	irda_device_unregister_dongle(&dongle);
+}
+
+/*
+	Power on:
+		(0) Clear RTS and DTR for 1 second
+		(1) Set RTS and DTR for 1 second
+		(2) 9600 bps now
+	Note: assume RTS, DTR are clear before
+*/
+static void ma600_open(dongle_t *self, struct qos_info *qos)
+{
+	IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
+
+	qos->baud_rate.bits &= IR_2400|IR_9600|IR_19200|IR_38400
+				|IR_57600|IR_115200;
+	qos->min_turn_time.bits = 0x01;		/* Needs at least 1 ms */	
+	irda_qos_bits_to_value(qos);
+
+	//self->set_dtr_rts(self->dev, FALSE, FALSE);
+	// should wait 1 second
+
+	self->set_dtr_rts(self->dev, TRUE, TRUE);
+	// should wait 1 second
+}
+
+static void ma600_close(dongle_t *self)
+{
+	IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
+
+	/* Power off dongle */
+	self->set_dtr_rts(self->dev, FALSE, FALSE);
+}
+
+static __u8 get_control_byte(__u32 speed)
+{
+	__u8 byte;
+
+	switch (speed) {
+	default:
+	case 115200:
+		byte = MA600_115200;
+		break;
+	case 57600:
+		byte = MA600_57600;
+		break;
+	case 38400:
+		byte = MA600_38400;
+		break;
+	case 19200:
+		byte = MA600_19200;
+		break;
+	case 9600:
+		byte = MA600_9600;
+		break;
+	case 2400:
+		byte = MA600_2400;
+		break;
+	}
+
+	return byte;
+}
+
+/*
+ * Function ma600_change_speed (dev, state, speed)
+ *
+ *    Set the speed for the MA600 type dongle. Warning, this 
+ *    function must be called with a process context!
+ *
+ *    Algorithm
+ *    1. Reset
+ *    2. clear RTS, set DTR and wait for 1ms
+ *    3. send Control Byte to the MA600 through TXD to set new baud rate
+ *       wait until the stop bit of Control Byte is sent (for 9600 baud rate, 
+ *       it takes about 10 msec)
+ *    4. set RTS, set DTR (return to NORMAL Operation)
+ *    5. wait at least 10 ms, new setting (baud rate, etc) takes effect here 
+ *       after
+ */
+static int ma600_change_speed(struct irda_task *task)
+{
+	dongle_t *self = (dongle_t *) task->instance;
+	__u32 speed = (__u32) task->param;
+	static __u8 byte;
+	__u8 byte_echo;
+	int ret = 0;
+	
+	IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
+
+	ASSERT(task != NULL, return -1;);
+
+	if (self->speed_task && self->speed_task != task) {
+		IRDA_DEBUG(0, "%s(), busy!\n", __FUNCTION__);
+		return msecs_to_jiffies(10);
+	} else {
+		self->speed_task = task;
+	}
+
+	switch (task->state) {
+	case IRDA_TASK_INIT:
+	case IRDA_TASK_CHILD_INIT:
+		/* 
+		 * Need to reset the dongle and go to 9600 bps before
+                 * programming 
+		 */
+		if (irda_task_execute(self, ma600_reset, NULL, task, 
+				      (void *) speed)) {
+			/* Dongle need more time to reset */
+			irda_task_next_state(task, IRDA_TASK_CHILD_WAIT);
+	
+			/* give 1 second to finish */
+			ret = msecs_to_jiffies(1000);
+		} else {
+			irda_task_next_state(task, IRDA_TASK_CHILD_DONE);
+		}
+		break;
+
+	case IRDA_TASK_CHILD_WAIT:
+		IRDA_WARNING("%s(), resetting dongle timed out!\n",
+			     __FUNCTION__);
+		ret = -1;
+		break;
+
+	case IRDA_TASK_CHILD_DONE:
+		/* Set DTR, Clear RTS */
+		self->set_dtr_rts(self->dev, TRUE, FALSE);
+	
+		ret = msecs_to_jiffies(1);		/* Sleep 1 ms */
+		irda_task_next_state(task, IRDA_TASK_WAIT);
+		break;
+
+	case IRDA_TASK_WAIT:
+		speed = (__u32) task->param;
+		byte = get_control_byte(speed);
+
+		/* Write control byte */
+		self->write(self->dev, &byte, sizeof(byte));
+		
+		irda_task_next_state(task, IRDA_TASK_WAIT1);
+
+		/* Wait at least 10 ms */
+		ret = msecs_to_jiffies(15);
+		break;
+
+	case IRDA_TASK_WAIT1:
+		/* Read control byte echo */
+		self->read(self->dev, &byte_echo, sizeof(byte_echo));
+
+		if(byte != byte_echo) {
+			/* if control byte != echo, I don't know what to do */
+			printk(KERN_WARNING "%s() control byte written != read!\n", __FUNCTION__);
+			printk(KERN_WARNING "control byte = 0x%c%c\n", 
+			       hexTbl[(byte>>4)&0x0f], hexTbl[byte&0x0f]);
+			printk(KERN_WARNING "byte echo = 0x%c%c\n", 
+			       hexTbl[(byte_echo>>4) & 0x0f], 
+			       hexTbl[byte_echo & 0x0f]);
+		#ifndef NDEBUG
+		} else {
+			IRDA_DEBUG(2, "%s() control byte write read OK\n", __FUNCTION__);
+		#endif
+		}
+
+		/* Set DTR, Set RTS */
+		self->set_dtr_rts(self->dev, TRUE, TRUE);
+
+		irda_task_next_state(task, IRDA_TASK_WAIT2);
+
+		/* Wait at least 10 ms */
+		ret = msecs_to_jiffies(10);
+		break;
+
+	case IRDA_TASK_WAIT2:
+		irda_task_next_state(task, IRDA_TASK_DONE);
+		self->speed_task = NULL;
+		break;
+
+	default:
+		IRDA_ERROR("%s(), unknown state %d\n",
+			   __FUNCTION__, task->state);
+		irda_task_next_state(task, IRDA_TASK_DONE);
+		self->speed_task = NULL;
+		ret = -1;
+		break;
+	}
+	return ret;
+}
+
+/*
+ * Function ma600_reset (driver)
+ *
+ *      This function resets the ma600 dongle. Warning, this function 
+ *      must be called with a process context!! 
+ *
+ *      Algorithm:
+ *    	  0. DTR=0, RTS=1 and wait 10 ms
+ *    	  1. DTR=1, RTS=1 and wait 10 ms
+ *        2. 9600 bps now
+ */
+int ma600_reset(struct irda_task *task)
+{
+	dongle_t *self = (dongle_t *) task->instance;
+	int ret = 0;
+
+	IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
+
+	ASSERT(task != NULL, return -1;);
+
+	if (self->reset_task && self->reset_task != task) {
+		IRDA_DEBUG(0, "%s(), busy!\n", __FUNCTION__);
+		return msecs_to_jiffies(10);
+	} else
+		self->reset_task = task;
+	
+	switch (task->state) {
+	case IRDA_TASK_INIT:
+		/* Clear DTR and Set RTS */
+		self->set_dtr_rts(self->dev, FALSE, TRUE);
+		irda_task_next_state(task, IRDA_TASK_WAIT1);
+		ret = msecs_to_jiffies(10);		/* Sleep 10 ms */
+		break;
+	case IRDA_TASK_WAIT1:
+		/* Set DTR and RTS */
+		self->set_dtr_rts(self->dev, TRUE, TRUE);
+		irda_task_next_state(task, IRDA_TASK_WAIT2);
+		ret = msecs_to_jiffies(10);		/* Sleep 10 ms */
+		break;
+	case IRDA_TASK_WAIT2:
+		irda_task_next_state(task, IRDA_TASK_DONE);
+		self->reset_task = NULL;
+		break;
+	default:
+		IRDA_ERROR("%s(), unknown state %d\n",
+			   __FUNCTION__, task->state);
+		irda_task_next_state(task, IRDA_TASK_DONE);		
+		self->reset_task = NULL;
+		ret = -1;
+	}
+	return ret;
+}
+
+MODULE_AUTHOR("Leung <95Etwl@alumni.ee.ust.hk> http://www.engsvr.ust/~eetwl95");
+MODULE_DESCRIPTION("MA600 dongle driver version 0.1");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("irda-dongle-11"); /* IRDA_MA600_DONGLE */
+		
+/*
+ * Function init_module (void)
+ *
+ *    Initialize MA600 module
+ *
+ */
+module_init(ma600_init);
+
+/*
+ * Function cleanup_module (void)
+ *
+ *    Cleanup MA600 module
+ *
+ */
+module_exit(ma600_cleanup);
+
diff --git a/drivers/net/irda/mcp2120-sir.c b/drivers/net/irda/mcp2120-sir.c
new file mode 100644
index 0000000..67bd016
--- /dev/null
+++ b/drivers/net/irda/mcp2120-sir.c
@@ -0,0 +1,230 @@
+/*********************************************************************
+ *            
+ *    
+ * Filename:      mcp2120.c
+ * Version:       1.0
+ * Description:   Implementation for the MCP2120 (Microchip)
+ * Status:        Experimental.
+ * Author:        Felix Tang (tangf@eyetap.org)
+ * Created at:    Sun Mar 31 19:32:12 EST 2002
+ * Based on code by:   Dag Brattli <dagb@cs.uit.no>
+ * 
+ *     Copyright (c) 2002 Felix Tang, 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.
+ *  
+ ********************************************************************/
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+
+#include <net/irda/irda.h>
+
+#include "sir-dev.h"
+
+static int mcp2120_reset(struct sir_dev *dev);
+static int mcp2120_open(struct sir_dev *dev);
+static int mcp2120_close(struct sir_dev *dev);
+static int mcp2120_change_speed(struct sir_dev *dev, unsigned speed);
+
+#define MCP2120_9600    0x87
+#define MCP2120_19200   0x8B
+#define MCP2120_38400   0x85
+#define MCP2120_57600   0x83
+#define MCP2120_115200  0x81
+
+#define MCP2120_COMMIT  0x11
+
+static struct dongle_driver mcp2120 = {
+	.owner		= THIS_MODULE,
+	.driver_name	= "Microchip MCP2120",
+	.type		= IRDA_MCP2120_DONGLE,
+	.open		= mcp2120_open,
+	.close		= mcp2120_close,
+	.reset		= mcp2120_reset,
+	.set_speed	= mcp2120_change_speed,
+};
+
+static int __init mcp2120_sir_init(void)
+{
+	return irda_register_dongle(&mcp2120);
+}
+
+static void __exit mcp2120_sir_cleanup(void)
+{
+	irda_unregister_dongle(&mcp2120);
+}
+
+static int mcp2120_open(struct sir_dev *dev)
+{
+	struct qos_info *qos = &dev->qos;
+
+	IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
+
+	/* seems no explicit power-on required here and reset switching it on anyway */
+
+	qos->baud_rate.bits &= IR_9600|IR_19200|IR_38400|IR_57600|IR_115200;
+	qos->min_turn_time.bits = 0x01;
+	irda_qos_bits_to_value(qos);
+
+	return 0;
+}
+
+static int mcp2120_close(struct sir_dev *dev)
+{
+	IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
+
+	/* Power off dongle */
+        /* reset and inhibit mcp2120 */
+	sirdev_set_dtr_rts(dev, TRUE, TRUE);
+	// sirdev_set_dtr_rts(dev, FALSE, FALSE);
+
+	return 0;
+}
+
+/*
+ * Function mcp2120_change_speed (dev, speed)
+ *
+ *    Set the speed for the MCP2120.
+ *
+ */
+
+#define MCP2120_STATE_WAIT_SPEED	(SIRDEV_STATE_DONGLE_SPEED+1)
+
+static int mcp2120_change_speed(struct sir_dev *dev, unsigned speed)
+{
+	unsigned state = dev->fsm.substate;
+	unsigned delay = 0;
+	u8 control[2];
+	static int ret = 0;
+
+	IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
+
+	switch (state) {
+	case SIRDEV_STATE_DONGLE_SPEED:
+		/* Set DTR to enter command mode */
+		sirdev_set_dtr_rts(dev, TRUE, FALSE);
+                udelay(500);
+
+		ret = 0;
+		switch (speed) {
+		default:
+			speed = 9600;
+			ret = -EINVAL;
+			/* fall through */
+		case 9600:
+			control[0] = MCP2120_9600;
+                        //printk("mcp2120 9600\n");
+			break;
+		case 19200:
+			control[0] = MCP2120_19200;
+                        //printk("mcp2120 19200\n");
+			break;
+		case 34800:
+			control[0] = MCP2120_38400;
+                        //printk("mcp2120 38400\n");
+			break;
+		case 57600:
+			control[0] = MCP2120_57600;
+                        //printk("mcp2120 57600\n");
+			break;
+		case 115200:
+                        control[0] = MCP2120_115200;
+                        //printk("mcp2120 115200\n");
+			break;
+		}
+		control[1] = MCP2120_COMMIT;
+	
+		/* Write control bytes */
+		sirdev_raw_write(dev, control, 2);
+		dev->speed = speed;
+
+		state = MCP2120_STATE_WAIT_SPEED;
+		delay = 100;
+                //printk("mcp2120_change_speed: dongle_speed\n");
+		break;
+
+	case MCP2120_STATE_WAIT_SPEED:
+		/* Go back to normal mode */
+		sirdev_set_dtr_rts(dev, FALSE, FALSE);
+                //printk("mcp2120_change_speed: mcp_wait\n");
+		break;
+
+	default:
+		IRDA_ERROR("%s(), undefine state %d\n", __FUNCTION__, state);
+		ret = -EINVAL;
+		break;
+	}
+	dev->fsm.substate = state;
+	return (delay > 0) ? delay : ret;
+}
+
+/*
+ * Function mcp2120_reset (driver)
+ *
+ *      This function resets the mcp2120 dongle.
+ *      
+ *      Info: -set RTS to reset mcp2120
+ *            -set DTR to set mcp2120 software command mode
+ *            -mcp2120 defaults to 9600 baud after reset
+ *
+ *      Algorithm:
+ *      0. Set RTS to reset mcp2120.
+ *      1. Clear RTS and wait for device reset timer of 30 ms (max).
+ *      
+ */
+
+#define MCP2120_STATE_WAIT1_RESET	(SIRDEV_STATE_DONGLE_RESET+1)
+#define MCP2120_STATE_WAIT2_RESET	(SIRDEV_STATE_DONGLE_RESET+2)
+
+static int mcp2120_reset(struct sir_dev *dev)
+{
+	unsigned state = dev->fsm.substate;
+	unsigned delay = 0;
+	int ret = 0;
+
+	IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
+
+	switch (state) {
+	case SIRDEV_STATE_DONGLE_RESET:
+                //printk("mcp2120_reset: dongle_reset\n");
+		/* Reset dongle by setting RTS*/
+		sirdev_set_dtr_rts(dev, TRUE, TRUE);
+		state = MCP2120_STATE_WAIT1_RESET;
+		delay = 50;
+		break;
+
+	case MCP2120_STATE_WAIT1_RESET:
+                //printk("mcp2120_reset: mcp2120_wait1\n");
+                /* clear RTS and wait for at least 30 ms. */
+		sirdev_set_dtr_rts(dev, FALSE, FALSE);
+		state = MCP2120_STATE_WAIT2_RESET;
+		delay = 50;
+		break;
+
+	case MCP2120_STATE_WAIT2_RESET:
+                //printk("mcp2120_reset mcp2120_wait2\n");
+		/* Go back to normal mode */
+		sirdev_set_dtr_rts(dev, FALSE, FALSE);
+		break;
+
+	default:
+		IRDA_ERROR("%s(), undefined state %d\n", __FUNCTION__, state);
+		ret = -EINVAL;
+		break;
+	}
+	dev->fsm.substate = state;
+	return (delay > 0) ? delay : ret;
+}
+
+MODULE_AUTHOR("Felix Tang <tangf@eyetap.org>");
+MODULE_DESCRIPTION("Microchip MCP2120");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("irda-dongle-9"); /* IRDA_MCP2120_DONGLE */
+
+module_init(mcp2120_sir_init);
+module_exit(mcp2120_sir_cleanup);
diff --git a/drivers/net/irda/mcp2120.c b/drivers/net/irda/mcp2120.c
new file mode 100644
index 0000000..5e6199e
--- /dev/null
+++ b/drivers/net/irda/mcp2120.c
@@ -0,0 +1,240 @@
+/*********************************************************************
+ *            
+ *    
+ * Filename:      mcp2120.c
+ * Version:       1.0
+ * Description:   Implementation for the MCP2120 (Microchip)
+ * Status:        Experimental.
+ * Author:        Felix Tang (tangf@eyetap.org)
+ * Created at:    Sun Mar 31 19:32:12 EST 2002
+ * Based on code by:   Dag Brattli <dagb@cs.uit.no>
+ * 
+ *     Copyright (c) 2002 Felix Tang, 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.
+ *  
+ ********************************************************************/
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/tty.h>
+#include <linux/init.h>
+
+#include <net/irda/irda.h>
+#include <net/irda/irda_device.h>
+
+static int  mcp2120_reset(struct irda_task *task);
+static void mcp2120_open(dongle_t *self, struct qos_info *qos);
+static void mcp2120_close(dongle_t *self);
+static int  mcp2120_change_speed(struct irda_task *task);
+
+#define MCP2120_9600    0x87
+#define MCP2120_19200   0x8B
+#define MCP2120_38400   0x85
+#define MCP2120_57600   0x83
+#define MCP2120_115200  0x81
+
+#define MCP2120_COMMIT  0x11
+
+static struct dongle_reg dongle = {
+	.type = IRDA_MCP2120_DONGLE,
+	.open = mcp2120_open,
+	.close = mcp2120_close,
+	.reset = mcp2120_reset,
+	.change_speed = mcp2120_change_speed,
+	.owner = THIS_MODULE,
+};
+
+static int __init mcp2120_init(void)
+{
+	return irda_device_register_dongle(&dongle);
+}
+
+static void __exit mcp2120_cleanup(void)
+{
+	irda_device_unregister_dongle(&dongle);
+}
+
+static void mcp2120_open(dongle_t *self, struct qos_info *qos)
+{
+	qos->baud_rate.bits &= IR_9600|IR_19200|IR_38400|IR_57600|IR_115200;
+	qos->min_turn_time.bits = 0x01;
+}
+
+static void mcp2120_close(dongle_t *self)
+{
+	/* Power off dongle */
+        /* reset and inhibit mcp2120 */
+	self->set_dtr_rts(self->dev, TRUE, TRUE);
+	//self->set_dtr_rts(self->dev, FALSE, FALSE);
+}
+
+/*
+ * Function mcp2120_change_speed (dev, speed)
+ *
+ *    Set the speed for the MCP2120.
+ *
+ */
+static int mcp2120_change_speed(struct irda_task *task)
+{
+	dongle_t *self = (dongle_t *) task->instance;
+	__u32 speed = (__u32) task->param;
+	__u8 control[2];
+	int ret = 0;
+
+	self->speed_task = task;
+
+	switch (task->state) {
+	case IRDA_TASK_INIT:
+		/* Need to reset the dongle and go to 9600 bps before
+                   programming */
+                //printk("Dmcp2120_change_speed irda_task_init\n");
+		if (irda_task_execute(self, mcp2120_reset, NULL, task, 
+				      (void *) speed))
+		{
+			/* Dongle need more time to reset */
+			irda_task_next_state(task, IRDA_TASK_CHILD_WAIT);
+
+			/* Give reset 1 sec to finish */
+			ret = msecs_to_jiffies(1000);
+		}
+		break;
+	case IRDA_TASK_CHILD_WAIT:
+		IRDA_WARNING("%s(), resetting dongle timed out!\n",
+			     __FUNCTION__);
+		ret = -1;
+		break;
+	case IRDA_TASK_CHILD_DONE:
+		/* Set DTR to enter command mode */
+		self->set_dtr_rts(self->dev, TRUE, FALSE);
+                udelay(500);
+
+		switch (speed) {
+		case 9600:
+		default:
+			control[0] = MCP2120_9600;
+                        //printk("mcp2120 9600\n");
+			break;
+		case 19200:
+			control[0] = MCP2120_19200;
+                        //printk("mcp2120 19200\n");
+			break;
+		case 34800:
+			control[0] = MCP2120_38400;
+                        //printk("mcp2120 38400\n");
+			break;
+		case 57600:
+			control[0] = MCP2120_57600;
+                        //printk("mcp2120 57600\n");
+			break;
+		case 115200:
+                        control[0] = MCP2120_115200;
+                        //printk("mcp2120 115200\n");
+			break;
+		}
+	        control[1] = MCP2120_COMMIT;
+	
+		/* Write control bytes */
+                self->write(self->dev, control, 2);
+ 
+                irda_task_next_state(task, IRDA_TASK_WAIT);
+		ret = msecs_to_jiffies(100);
+                //printk("mcp2120_change_speed irda_child_done\n");
+		break;
+	case IRDA_TASK_WAIT:
+		/* Go back to normal mode */
+		self->set_dtr_rts(self->dev, FALSE, FALSE);
+		irda_task_next_state(task, IRDA_TASK_DONE);
+		self->speed_task = NULL;
+                //printk("mcp2120_change_speed irda_task_wait\n");
+		break;
+	default:
+		IRDA_ERROR("%s(), unknown state %d\n",
+			   __FUNCTION__, task->state);
+		irda_task_next_state(task, IRDA_TASK_DONE);
+		self->speed_task = NULL;
+		ret = -1;
+		break;
+	}
+	return ret;
+}
+
+/*
+ * Function mcp2120_reset (driver)
+ *
+ *      This function resets the mcp2120 dongle.
+ *      
+ *      Info: -set RTS to reset mcp2120
+ *            -set DTR to set mcp2120 software command mode
+ *            -mcp2120 defaults to 9600 baud after reset
+ *
+ *      Algorithm:
+ *      0. Set RTS to reset mcp2120.
+ *      1. Clear RTS and wait for device reset timer of 30 ms (max).
+ *      
+ */
+
+
+static int mcp2120_reset(struct irda_task *task)
+{
+	dongle_t *self = (dongle_t *) task->instance;
+	int ret = 0;
+
+	self->reset_task = task;
+
+	switch (task->state) {
+	case IRDA_TASK_INIT:
+                //printk("mcp2120_reset irda_task_init\n");
+		/* Reset dongle by setting RTS*/
+		self->set_dtr_rts(self->dev, TRUE, TRUE);
+		irda_task_next_state(task, IRDA_TASK_WAIT1);
+		ret = msecs_to_jiffies(50);
+		break;
+	case IRDA_TASK_WAIT1:
+                //printk("mcp2120_reset irda_task_wait1\n");
+                /* clear RTS and wait for at least 30 ms. */
+		self->set_dtr_rts(self->dev, FALSE, FALSE);
+		irda_task_next_state(task, IRDA_TASK_WAIT2);
+		ret = msecs_to_jiffies(50);
+		break;
+	case IRDA_TASK_WAIT2:
+                //printk("mcp2120_reset irda_task_wait2\n");
+		/* Go back to normal mode */
+		self->set_dtr_rts(self->dev, FALSE, FALSE);
+		irda_task_next_state(task, IRDA_TASK_DONE);
+		self->reset_task = NULL;
+		break;
+	default:
+		IRDA_ERROR("%s(), unknown state %d\n",
+			   __FUNCTION__, task->state);
+		irda_task_next_state(task, IRDA_TASK_DONE);
+		self->reset_task = NULL;
+		ret = -1;
+		break;
+	}
+	return ret;
+}
+
+MODULE_AUTHOR("Felix Tang <tangf@eyetap.org>");
+MODULE_DESCRIPTION("Microchip MCP2120");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("irda-dongle-9"); /* IRDA_MCP2120_DONGLE */
+	
+/*
+ * Function init_module (void)
+ *
+ *    Initialize MCP2120 module
+ *
+ */
+module_init(mcp2120_init);
+
+/*
+ * Function cleanup_module (void)
+ *
+ *    Cleanup MCP2120 module
+ *
+ */
+module_exit(mcp2120_cleanup);
diff --git a/drivers/net/irda/nsc-ircc.c b/drivers/net/irda/nsc-ircc.c
new file mode 100644
index 0000000..805714e
--- /dev/null
+++ b/drivers/net/irda/nsc-ircc.c
@@ -0,0 +1,2222 @@
+/*********************************************************************
+ *                
+ * Filename:      nsc-ircc.c
+ * Version:       1.0
+ * Description:   Driver for the NSC PC'108 and PC'338 IrDA chipsets
+ * Status:        Stable.
+ * Author:        Dag Brattli <dagb@cs.uit.no>
+ * Created at:    Sat Nov  7 21:43:15 1998
+ * Modified at:   Wed Mar  1 11:29:34 2000
+ * Modified by:   Dag Brattli <dagb@cs.uit.no>
+ * 
+ *     Copyright (c) 1998-2000 Dag Brattli <dagb@cs.uit.no>
+ *     Copyright (c) 1998 Lichen Wang, <lwang@actisys.com>
+ *     Copyright (c) 1998 Actisys Corp., www.actisys.com
+ *     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.
+ *
+ *     Notice that all functions that needs to access the chip in _any_
+ *     way, must save BSR register on entry, and restore it on exit. 
+ *     It is _very_ important to follow this policy!
+ *
+ *         __u8 bank;
+ *     
+ *         bank = inb(iobase+BSR);
+ *  
+ *         do_your_stuff_here();
+ *
+ *         outb(bank, iobase+BSR);
+ *
+ *    If you find bugs in this file, its very likely that the same bug
+ *    will also be in w83977af_ir.c since the implementations are quite
+ *    similar.
+ *     
+ ********************************************************************/
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/rtnetlink.h>
+#include <linux/dma-mapping.h>
+
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/byteorder.h>
+
+#include <linux/pm.h>
+
+#include <net/irda/wrapper.h>
+#include <net/irda/irda.h>
+#include <net/irda/irda_device.h>
+
+#include "nsc-ircc.h"
+
+#define CHIP_IO_EXTENT 8
+#define BROKEN_DONGLE_ID
+
+static char *driver_name = "nsc-ircc";
+
+/* Module parameters */
+static int qos_mtt_bits = 0x07;  /* 1 ms or more */
+static int dongle_id;
+
+/* Use BIOS settions by default, but user may supply module parameters */
+static unsigned int io[]  = { ~0, ~0, ~0, ~0 };
+static unsigned int irq[] = { 0, 0, 0, 0, 0 };
+static unsigned int dma[] = { 0, 0, 0, 0, 0 };
+
+static int nsc_ircc_probe_108(nsc_chip_t *chip, chipio_t *info);
+static int nsc_ircc_probe_338(nsc_chip_t *chip, chipio_t *info);
+static int nsc_ircc_probe_39x(nsc_chip_t *chip, chipio_t *info);
+static int nsc_ircc_init_108(nsc_chip_t *chip, chipio_t *info);
+static int nsc_ircc_init_338(nsc_chip_t *chip, chipio_t *info);
+static int nsc_ircc_init_39x(nsc_chip_t *chip, chipio_t *info);
+
+/* These are the known NSC chips */
+static nsc_chip_t chips[] = {
+/*  Name, {cfg registers}, chip id index reg, chip id expected value, revision mask */
+	{ "PC87108", { 0x150, 0x398, 0xea }, 0x05, 0x10, 0xf0, 
+	  nsc_ircc_probe_108, nsc_ircc_init_108 },
+	{ "PC87338", { 0x398, 0x15c, 0x2e }, 0x08, 0xb0, 0xf8, 
+	  nsc_ircc_probe_338, nsc_ircc_init_338 },
+	/* Contributed by Steffen Pingel - IBM X40 */
+	{ "PC8738x", { 0x164e, 0x4e, 0x0 }, 0x20, 0xf4, 0xff,
+	  nsc_ircc_probe_39x, nsc_ircc_init_39x },
+	/* Contributed by Jan Frey - IBM A30/A31 */
+	{ "PC8739x", { 0x2e, 0x4e, 0x0 }, 0x20, 0xea, 0xff, 
+	  nsc_ircc_probe_39x, nsc_ircc_init_39x },
+	{ NULL }
+};
+
+/* Max 4 instances for now */
+static struct nsc_ircc_cb *dev_self[] = { NULL, NULL, NULL, NULL };
+
+static char *dongle_types[] = {
+	"Differential serial interface",
+	"Differential serial interface",
+	"Reserved",
+	"Reserved",
+	"Sharp RY5HD01",
+	"Reserved",
+	"Single-ended serial interface",
+	"Consumer-IR only",
+	"HP HSDL-2300, HP HSDL-3600/HSDL-3610",
+	"IBM31T1100 or Temic TFDS6000/TFDS6500",
+	"Reserved",
+	"Reserved",
+	"HP HSDL-1100/HSDL-2100",
+	"HP HSDL-1100/HSDL-2100",
+	"Supports SIR Mode only",
+	"No dongle connected",
+};
+
+/* Some prototypes */
+static int  nsc_ircc_open(int i, chipio_t *info);
+static int  nsc_ircc_close(struct nsc_ircc_cb *self);
+static int  nsc_ircc_setup(chipio_t *info);
+static void nsc_ircc_pio_receive(struct nsc_ircc_cb *self);
+static int  nsc_ircc_dma_receive(struct nsc_ircc_cb *self); 
+static int  nsc_ircc_dma_receive_complete(struct nsc_ircc_cb *self, int iobase);
+static int  nsc_ircc_hard_xmit_sir(struct sk_buff *skb, struct net_device *dev);
+static int  nsc_ircc_hard_xmit_fir(struct sk_buff *skb, struct net_device *dev);
+static int  nsc_ircc_pio_write(int iobase, __u8 *buf, int len, int fifo_size);
+static void nsc_ircc_dma_xmit(struct nsc_ircc_cb *self, int iobase);
+static __u8 nsc_ircc_change_speed(struct nsc_ircc_cb *self, __u32 baud);
+static int  nsc_ircc_is_receiving(struct nsc_ircc_cb *self);
+static int  nsc_ircc_read_dongle_id (int iobase);
+static void nsc_ircc_init_dongle_interface (int iobase, int dongle_id);
+
+static int  nsc_ircc_net_open(struct net_device *dev);
+static int  nsc_ircc_net_close(struct net_device *dev);
+static int  nsc_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+static struct net_device_stats *nsc_ircc_net_get_stats(struct net_device *dev);
+static int nsc_ircc_pmproc(struct pm_dev *dev, pm_request_t rqst, void *data);
+
+/*
+ * Function nsc_ircc_init ()
+ *
+ *    Initialize chip. Just try to find out how many chips we are dealing with
+ *    and where they are
+ */
+static int __init nsc_ircc_init(void)
+{
+	chipio_t info;
+	nsc_chip_t *chip;
+	int ret = -ENODEV;
+	int cfg_base;
+	int cfg, id;
+	int reg;
+	int i = 0;
+
+	/* Probe for all the NSC chipsets we know about */
+	for (chip=chips; chip->name ; chip++) {
+		IRDA_DEBUG(2, "%s(), Probing for %s ...\n", __FUNCTION__,
+			   chip->name);
+		
+		/* Try all config registers for this chip */
+		for (cfg=0; cfg<3; cfg++) {
+			cfg_base = chip->cfg[cfg];
+			if (!cfg_base)
+				continue;
+			
+			memset(&info, 0, sizeof(chipio_t));
+			info.cfg_base = cfg_base;
+			info.fir_base = io[i];
+			info.dma = dma[i];
+			info.irq = irq[i];
+
+			/* Read index register */
+			reg = inb(cfg_base);
+			if (reg == 0xff) {
+				IRDA_DEBUG(2, "%s() no chip at 0x%03x\n", __FUNCTION__, cfg_base);
+				continue;
+			}
+			
+			/* Read chip identification register */
+			outb(chip->cid_index, cfg_base);
+			id = inb(cfg_base+1);
+			if ((id & chip->cid_mask) == chip->cid_value) {
+				IRDA_DEBUG(2, "%s() Found %s chip, revision=%d\n",
+					   __FUNCTION__, chip->name, id & ~chip->cid_mask);
+				/* 
+				 * If the user supplies the base address, then
+				 * we init the chip, if not we probe the values
+				 * set by the BIOS
+				 */				
+				if (io[i] < 0x2000) {
+					chip->init(chip, &info);
+				} else
+					chip->probe(chip, &info);
+
+				if (nsc_ircc_open(i, &info) == 0)
+					ret = 0;
+				i++;
+			} else {
+				IRDA_DEBUG(2, "%s(), Wrong chip id=0x%02x\n", __FUNCTION__, id);
+			}
+		} 
+		
+	}
+
+	return ret;
+}
+
+/*
+ * Function nsc_ircc_cleanup ()
+ *
+ *    Close all configured chips
+ *
+ */
+static void __exit nsc_ircc_cleanup(void)
+{
+	int i;
+
+	pm_unregister_all(nsc_ircc_pmproc);
+
+	for (i=0; i < 4; i++) {
+		if (dev_self[i])
+			nsc_ircc_close(dev_self[i]);
+	}
+}
+
+/*
+ * Function nsc_ircc_open (iobase, irq)
+ *
+ *    Open driver instance
+ *
+ */
+static int __init nsc_ircc_open(int i, chipio_t *info)
+{
+	struct net_device *dev;
+	struct nsc_ircc_cb *self;
+        struct pm_dev *pmdev;
+	void *ret;
+	int err;
+
+	IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
+
+	IRDA_MESSAGE("%s, Found chip at base=0x%03x\n", driver_name,
+		     info->cfg_base);
+
+	if ((nsc_ircc_setup(info)) == -1)
+		return -1;
+
+	IRDA_MESSAGE("%s, driver loaded (Dag Brattli)\n", driver_name);
+
+	dev = alloc_irdadev(sizeof(struct nsc_ircc_cb));
+	if (dev == NULL) {
+		IRDA_ERROR("%s(), can't allocate memory for "
+			   "control block!\n", __FUNCTION__);
+		return -ENOMEM;
+	}
+
+	self = dev->priv;
+	self->netdev = dev;
+	spin_lock_init(&self->lock);
+   
+	/* Need to store self somewhere */
+	dev_self[i] = self;
+	self->index = i;
+
+	/* Initialize IO */
+	self->io.cfg_base  = info->cfg_base;
+	self->io.fir_base  = info->fir_base;
+        self->io.irq       = info->irq;
+        self->io.fir_ext   = CHIP_IO_EXTENT;
+        self->io.dma       = info->dma;
+        self->io.fifo_size = 32;
+	
+	/* Reserve the ioports that we need */
+	ret = request_region(self->io.fir_base, self->io.fir_ext, driver_name);
+	if (!ret) {
+		IRDA_WARNING("%s(), can't get iobase of 0x%03x\n",
+			     __FUNCTION__, self->io.fir_base);
+		err = -ENODEV;
+		goto out1;
+	}
+
+	/* Initialize QoS for this device */
+	irda_init_max_qos_capabilies(&self->qos);
+	
+	/* The only value we must override it the baudrate */
+	self->qos.baud_rate.bits = IR_9600|IR_19200|IR_38400|IR_57600|
+		IR_115200|IR_576000|IR_1152000 |(IR_4000000 << 8);
+	
+	self->qos.min_turn_time.bits = qos_mtt_bits;
+	irda_qos_bits_to_value(&self->qos);
+	
+	/* Max DMA buffer size needed = (data_size + 6) * (window_size) + 6; */
+	self->rx_buff.truesize = 14384; 
+	self->tx_buff.truesize = 14384;
+
+	/* Allocate memory if needed */
+	self->rx_buff.head =
+		dma_alloc_coherent(NULL, self->rx_buff.truesize,
+				   &self->rx_buff_dma, GFP_KERNEL);
+	if (self->rx_buff.head == NULL) {
+		err = -ENOMEM;
+		goto out2;
+
+	}
+	memset(self->rx_buff.head, 0, self->rx_buff.truesize);
+	
+	self->tx_buff.head =
+		dma_alloc_coherent(NULL, self->tx_buff.truesize,
+				   &self->tx_buff_dma, GFP_KERNEL);
+	if (self->tx_buff.head == NULL) {
+		err = -ENOMEM;
+		goto out3;
+	}
+	memset(self->tx_buff.head, 0, self->tx_buff.truesize);
+
+	self->rx_buff.in_frame = FALSE;
+	self->rx_buff.state = OUTSIDE_FRAME;
+	self->tx_buff.data = self->tx_buff.head;
+	self->rx_buff.data = self->rx_buff.head;
+	
+	/* Reset Tx queue info */
+	self->tx_fifo.len = self->tx_fifo.ptr = self->tx_fifo.free = 0;
+	self->tx_fifo.tail = self->tx_buff.head;
+
+	/* Override the network functions we need to use */
+	SET_MODULE_OWNER(dev);
+	dev->hard_start_xmit = nsc_ircc_hard_xmit_sir;
+	dev->open            = nsc_ircc_net_open;
+	dev->stop            = nsc_ircc_net_close;
+	dev->do_ioctl        = nsc_ircc_net_ioctl;
+	dev->get_stats	     = nsc_ircc_net_get_stats;
+
+	err = register_netdev(dev);
+	if (err) {
+		IRDA_ERROR("%s(), register_netdev() failed!\n", __FUNCTION__);
+		goto out4;
+	}
+	IRDA_MESSAGE("IrDA: Registered device %s\n", dev->name);
+
+	/* Check if user has supplied a valid dongle id or not */
+	if ((dongle_id <= 0) ||
+	    (dongle_id >= (sizeof(dongle_types) / sizeof(dongle_types[0]))) ) {
+		dongle_id = nsc_ircc_read_dongle_id(self->io.fir_base);
+		
+		IRDA_MESSAGE("%s, Found dongle: %s\n", driver_name,
+			     dongle_types[dongle_id]);
+	} else {
+		IRDA_MESSAGE("%s, Using dongle: %s\n", driver_name,
+			     dongle_types[dongle_id]);
+	}
+	
+	self->io.dongle_id = dongle_id;
+	nsc_ircc_init_dongle_interface(self->io.fir_base, dongle_id);
+
+        pmdev = pm_register(PM_SYS_DEV, PM_SYS_IRDA, nsc_ircc_pmproc);
+        if (pmdev)
+                pmdev->data = self;
+
+	return 0;
+ out4:
+	dma_free_coherent(NULL, self->tx_buff.truesize,
+			  self->tx_buff.head, self->tx_buff_dma);
+ out3:
+	dma_free_coherent(NULL, self->rx_buff.truesize,
+			  self->rx_buff.head, self->rx_buff_dma);
+ out2:
+	release_region(self->io.fir_base, self->io.fir_ext);
+ out1:
+	free_netdev(dev);
+	dev_self[i] = NULL;
+	return err;
+}
+
+/*
+ * Function nsc_ircc_close (self)
+ *
+ *    Close driver instance
+ *
+ */
+static int __exit nsc_ircc_close(struct nsc_ircc_cb *self)
+{
+	int iobase;
+
+	IRDA_DEBUG(4, "%s()\n", __FUNCTION__);
+
+	IRDA_ASSERT(self != NULL, return -1;);
+
+        iobase = self->io.fir_base;
+
+	/* Remove netdevice */
+	unregister_netdev(self->netdev);
+
+	/* Release the PORT that this driver is using */
+	IRDA_DEBUG(4, "%s(), Releasing Region %03x\n", 
+		   __FUNCTION__, self->io.fir_base);
+	release_region(self->io.fir_base, self->io.fir_ext);
+
+	if (self->tx_buff.head)
+		dma_free_coherent(NULL, self->tx_buff.truesize,
+				  self->tx_buff.head, self->tx_buff_dma);
+	
+	if (self->rx_buff.head)
+		dma_free_coherent(NULL, self->rx_buff.truesize,
+				  self->rx_buff.head, self->rx_buff_dma);
+
+	dev_self[self->index] = NULL;
+	free_netdev(self->netdev);
+	
+	return 0;
+}
+
+/*
+ * Function nsc_ircc_init_108 (iobase, cfg_base, irq, dma)
+ *
+ *    Initialize the NSC '108 chip
+ *
+ */
+static int nsc_ircc_init_108(nsc_chip_t *chip, chipio_t *info)
+{
+	int cfg_base = info->cfg_base;
+	__u8 temp=0;
+
+	outb(2, cfg_base);      /* Mode Control Register (MCTL) */
+	outb(0x00, cfg_base+1); /* Disable device */
+	
+	/* Base Address and Interrupt Control Register (BAIC) */
+	outb(CFG_108_BAIC, cfg_base);
+	switch (info->fir_base) {
+	case 0x3e8: outb(0x14, cfg_base+1); break;
+	case 0x2e8: outb(0x15, cfg_base+1); break;
+	case 0x3f8: outb(0x16, cfg_base+1); break;
+	case 0x2f8: outb(0x17, cfg_base+1); break;
+	default: IRDA_ERROR("%s(), invalid base_address", __FUNCTION__);
+	}
+	
+	/* Control Signal Routing Register (CSRT) */
+	switch (info->irq) {
+	case 3:  temp = 0x01; break;
+	case 4:  temp = 0x02; break;
+	case 5:  temp = 0x03; break;
+	case 7:  temp = 0x04; break;
+	case 9:  temp = 0x05; break;
+	case 11: temp = 0x06; break;
+	case 15: temp = 0x07; break;
+	default: IRDA_ERROR("%s(), invalid irq", __FUNCTION__);
+	}
+	outb(CFG_108_CSRT, cfg_base);
+	
+	switch (info->dma) {	
+	case 0: outb(0x08+temp, cfg_base+1); break;
+	case 1: outb(0x10+temp, cfg_base+1); break;
+	case 3: outb(0x18+temp, cfg_base+1); break;
+	default: IRDA_ERROR("%s(), invalid dma", __FUNCTION__);
+	}
+	
+	outb(CFG_108_MCTL, cfg_base);      /* Mode Control Register (MCTL) */
+	outb(0x03, cfg_base+1); /* Enable device */
+
+	return 0;
+}
+
+/*
+ * Function nsc_ircc_probe_108 (chip, info)
+ *
+ *    
+ *
+ */
+static int nsc_ircc_probe_108(nsc_chip_t *chip, chipio_t *info) 
+{
+	int cfg_base = info->cfg_base;
+	int reg;
+
+	/* Read address and interrupt control register (BAIC) */
+	outb(CFG_108_BAIC, cfg_base);
+	reg = inb(cfg_base+1);
+	
+	switch (reg & 0x03) {
+	case 0:
+		info->fir_base = 0x3e8;
+		break;
+	case 1:
+		info->fir_base = 0x2e8;
+		break;
+	case 2:
+		info->fir_base = 0x3f8;
+		break;
+	case 3:
+		info->fir_base = 0x2f8;
+		break;
+	}
+	info->sir_base = info->fir_base;
+	IRDA_DEBUG(2, "%s(), probing fir_base=0x%03x\n", __FUNCTION__,
+		   info->fir_base);
+
+	/* Read control signals routing register (CSRT) */
+	outb(CFG_108_CSRT, cfg_base);
+	reg = inb(cfg_base+1);
+
+	switch (reg & 0x07) {
+	case 0:
+		info->irq = -1;
+		break;
+	case 1:
+		info->irq = 3;
+		break;
+	case 2:
+		info->irq = 4;
+		break;
+	case 3:
+		info->irq = 5;
+		break;
+	case 4:
+		info->irq = 7;
+		break;
+	case 5:
+		info->irq = 9;
+		break;
+	case 6:
+		info->irq = 11;
+		break;
+	case 7:
+		info->irq = 15;
+		break;
+	}
+	IRDA_DEBUG(2, "%s(), probing irq=%d\n", __FUNCTION__, info->irq);
+
+	/* Currently we only read Rx DMA but it will also be used for Tx */
+	switch ((reg >> 3) & 0x03) {
+	case 0:
+		info->dma = -1;
+		break;
+	case 1:
+		info->dma = 0;
+		break;
+	case 2:
+		info->dma = 1;
+		break;
+	case 3:
+		info->dma = 3;
+		break;
+	}
+	IRDA_DEBUG(2, "%s(), probing dma=%d\n", __FUNCTION__, info->dma);
+
+	/* Read mode control register (MCTL) */
+	outb(CFG_108_MCTL, cfg_base);
+	reg = inb(cfg_base+1);
+
+	info->enabled = reg & 0x01;
+	info->suspended = !((reg >> 1) & 0x01);
+
+	return 0;
+}
+
+/*
+ * Function nsc_ircc_init_338 (chip, info)
+ *
+ *    Initialize the NSC '338 chip. Remember that the 87338 needs two 
+ *    consecutive writes to the data registers while CPU interrupts are
+ *    disabled. The 97338 does not require this, but shouldn't be any
+ *    harm if we do it anyway.
+ */
+static int nsc_ircc_init_338(nsc_chip_t *chip, chipio_t *info) 
+{
+	/* No init yet */
+	
+	return 0;
+}
+
+/*
+ * Function nsc_ircc_probe_338 (chip, info)
+ *
+ *    
+ *
+ */
+static int nsc_ircc_probe_338(nsc_chip_t *chip, chipio_t *info) 
+{
+	int cfg_base = info->cfg_base;
+	int reg, com = 0;
+	int pnp;
+
+	/* Read funtion enable register (FER) */
+	outb(CFG_338_FER, cfg_base);
+	reg = inb(cfg_base+1);
+
+	info->enabled = (reg >> 2) & 0x01;
+
+	/* Check if we are in Legacy or PnP mode */
+	outb(CFG_338_PNP0, cfg_base);
+	reg = inb(cfg_base+1);
+	
+	pnp = (reg >> 3) & 0x01;
+	if (pnp) {
+		IRDA_DEBUG(2, "(), Chip is in PnP mode\n");
+		outb(0x46, cfg_base);
+		reg = (inb(cfg_base+1) & 0xfe) << 2;
+
+		outb(0x47, cfg_base);
+		reg |= ((inb(cfg_base+1) & 0xfc) << 8);
+
+		info->fir_base = reg;
+	} else {
+		/* Read function address register (FAR) */
+		outb(CFG_338_FAR, cfg_base);
+		reg = inb(cfg_base+1);
+		
+		switch ((reg >> 4) & 0x03) {
+		case 0:
+			info->fir_base = 0x3f8;
+			break;
+		case 1:
+			info->fir_base = 0x2f8;
+			break;
+		case 2:
+			com = 3;
+			break;
+		case 3:
+			com = 4;
+			break;
+		}
+		
+		if (com) {
+			switch ((reg >> 6) & 0x03) {
+			case 0:
+				if (com == 3)
+					info->fir_base = 0x3e8;
+				else
+					info->fir_base = 0x2e8;
+				break;
+			case 1:
+				if (com == 3)
+					info->fir_base = 0x338;
+				else
+					info->fir_base = 0x238;
+				break;
+			case 2:
+				if (com == 3)
+					info->fir_base = 0x2e8;
+				else
+					info->fir_base = 0x2e0;
+				break;
+			case 3:
+				if (com == 3)
+					info->fir_base = 0x220;
+				else
+					info->fir_base = 0x228;
+				break;
+			}
+		}
+	}
+	info->sir_base = info->fir_base;
+
+	/* Read PnP register 1 (PNP1) */
+	outb(CFG_338_PNP1, cfg_base);
+	reg = inb(cfg_base+1);
+	
+	info->irq = reg >> 4;
+	
+	/* Read PnP register 3 (PNP3) */
+	outb(CFG_338_PNP3, cfg_base);
+	reg = inb(cfg_base+1);
+
+	info->dma = (reg & 0x07) - 1;
+
+	/* Read power and test register (PTR) */
+	outb(CFG_338_PTR, cfg_base);
+	reg = inb(cfg_base+1);
+
+	info->suspended = reg & 0x01;
+
+	return 0;
+}
+
+
+/*
+ * Function nsc_ircc_init_39x (chip, info)
+ *
+ *    Now that we know it's a '39x (see probe below), we need to
+ *    configure it so we can use it.
+ *
+ * The NSC '338 chip is a Super I/O chip with a "bank" architecture,
+ * the configuration of the different functionality (serial, parallel,
+ * floppy...) are each in a different bank (Logical Device Number).
+ * The base address, irq and dma configuration registers are common
+ * to all functionalities (index 0x30 to 0x7F).
+ * There is only one configuration register specific to the
+ * serial port, CFG_39X_SPC.
+ * JeanII
+ *
+ * Note : this code was written by Jan Frey <janfrey@web.de>
+ */
+static int nsc_ircc_init_39x(nsc_chip_t *chip, chipio_t *info) 
+{
+	int cfg_base = info->cfg_base;
+	int enabled;
+
+	/* User is shure about his config... accept it. */
+	IRDA_DEBUG(2, "%s(): nsc_ircc_init_39x (user settings): "
+		   "io=0x%04x, irq=%d, dma=%d\n", 
+		   __FUNCTION__, info->fir_base, info->irq, info->dma);
+
+	/* Access bank for SP2 */
+	outb(CFG_39X_LDN, cfg_base);
+	outb(0x02, cfg_base+1);
+
+	/* Configure SP2 */
+
+	/* We want to enable the device if not enabled */
+	outb(CFG_39X_ACT, cfg_base);
+	enabled = inb(cfg_base+1) & 0x01;
+	
+	if (!enabled) {
+		/* Enable the device */
+		outb(CFG_39X_SIOCF1, cfg_base);
+		outb(0x01, cfg_base+1);
+		/* May want to update info->enabled. Jean II */
+	}
+
+	/* Enable UART bank switching (bit 7) ; Sets the chip to normal
+	 * power mode (wake up from sleep mode) (bit 1) */
+	outb(CFG_39X_SPC, cfg_base);
+	outb(0x82, cfg_base+1);
+
+	return 0;
+}
+
+/*
+ * Function nsc_ircc_probe_39x (chip, info)
+ *
+ *    Test if we really have a '39x chip at the given address
+ *
+ * Note : this code was written by Jan Frey <janfrey@web.de>
+ */
+static int nsc_ircc_probe_39x(nsc_chip_t *chip, chipio_t *info) 
+{
+	int cfg_base = info->cfg_base;
+	int reg1, reg2, irq, irqt, dma1, dma2;
+	int enabled, susp;
+
+	IRDA_DEBUG(2, "%s(), nsc_ircc_probe_39x, base=%d\n",
+		   __FUNCTION__, cfg_base);
+
+	/* This function should be executed with irq off to avoid
+	 * another driver messing with the Super I/O bank - Jean II */
+
+	/* Access bank for SP2 */
+	outb(CFG_39X_LDN, cfg_base);
+	outb(0x02, cfg_base+1);
+
+	/* Read infos about SP2 ; store in info struct */
+	outb(CFG_39X_BASEH, cfg_base);
+	reg1 = inb(cfg_base+1);
+	outb(CFG_39X_BASEL, cfg_base);
+	reg2 = inb(cfg_base+1);
+	info->fir_base = (reg1 << 8) | reg2;
+
+	outb(CFG_39X_IRQNUM, cfg_base);
+	irq = inb(cfg_base+1);
+	outb(CFG_39X_IRQSEL, cfg_base);
+	irqt = inb(cfg_base+1);
+	info->irq = irq;
+
+	outb(CFG_39X_DMA0, cfg_base);
+	dma1 = inb(cfg_base+1);
+	outb(CFG_39X_DMA1, cfg_base);
+	dma2 = inb(cfg_base+1);
+	info->dma = dma1 -1;
+
+	outb(CFG_39X_ACT, cfg_base);
+	info->enabled = enabled = inb(cfg_base+1) & 0x01;
+	
+	outb(CFG_39X_SPC, cfg_base);
+	susp = 1 - ((inb(cfg_base+1) & 0x02) >> 1);
+
+	IRDA_DEBUG(2, "%s(): io=0x%02x%02x, irq=%d (type %d), rxdma=%d, txdma=%d, enabled=%d (suspended=%d)\n", __FUNCTION__, reg1,reg2,irq,irqt,dma1,dma2,enabled,susp);
+
+	/* Configure SP2 */
+
+	/* We want to enable the device if not enabled */
+	outb(CFG_39X_ACT, cfg_base);
+	enabled = inb(cfg_base+1) & 0x01;
+	
+	if (!enabled) {
+		/* Enable the device */
+		outb(CFG_39X_SIOCF1, cfg_base);
+		outb(0x01, cfg_base+1);
+		/* May want to update info->enabled. Jean II */
+	}
+
+	/* Enable UART bank switching (bit 7) ; Sets the chip to normal
+	 * power mode (wake up from sleep mode) (bit 1) */
+	outb(CFG_39X_SPC, cfg_base);
+	outb(0x82, cfg_base+1);
+
+	return 0;
+}
+
+/*
+ * Function nsc_ircc_setup (info)
+ *
+ *    Returns non-negative on success.
+ *
+ */
+static int nsc_ircc_setup(chipio_t *info)
+{
+	int version;
+	int iobase = info->fir_base;
+
+	/* Read the Module ID */
+	switch_bank(iobase, BANK3);
+	version = inb(iobase+MID);
+
+	IRDA_DEBUG(2, "%s() Driver %s Found chip version %02x\n",
+		   __FUNCTION__, driver_name, version);
+
+	/* Should be 0x2? */
+	if (0x20 != (version & 0xf0)) {
+		IRDA_ERROR("%s, Wrong chip version %02x\n",
+			   driver_name, version);
+		return -1;
+	}
+
+	/* Switch to advanced mode */
+	switch_bank(iobase, BANK2);
+	outb(ECR1_EXT_SL, iobase+ECR1);
+	switch_bank(iobase, BANK0);
+	
+	/* Set FIFO threshold to TX17, RX16, reset and enable FIFO's */
+	switch_bank(iobase, BANK0);
+	outb(FCR_RXTH|FCR_TXTH|FCR_TXSR|FCR_RXSR|FCR_FIFO_EN, iobase+FCR);
+
+	outb(0x03, iobase+LCR); 	/* 8 bit word length */
+	outb(MCR_SIR, iobase+MCR); 	/* Start at SIR-mode, also clears LSR*/
+
+	/* Set FIFO size to 32 */
+	switch_bank(iobase, BANK2);
+	outb(EXCR2_RFSIZ|EXCR2_TFSIZ, iobase+EXCR2);
+
+	/* IRCR2: FEND_MD is not set */
+	switch_bank(iobase, BANK5);
+ 	outb(0x02, iobase+4);
+
+	/* Make sure that some defaults are OK */
+	switch_bank(iobase, BANK6);
+	outb(0x20, iobase+0); /* Set 32 bits FIR CRC */
+	outb(0x0a, iobase+1); /* Set MIR pulse width */
+	outb(0x0d, iobase+2); /* Set SIR pulse width to 1.6us */
+	outb(0x2a, iobase+4); /* Set beginning frag, and preamble length */
+
+	/* Enable receive interrupts */
+	switch_bank(iobase, BANK0);
+	outb(IER_RXHDL_IE, iobase+IER);
+
+	return 0;
+}
+
+/*
+ * Function nsc_ircc_read_dongle_id (void)
+ *
+ * Try to read dongle indentification. This procedure needs to be executed
+ * once after power-on/reset. It also needs to be used whenever you suspect
+ * that the user may have plugged/unplugged the IrDA Dongle.
+ */
+static int nsc_ircc_read_dongle_id (int iobase)
+{
+	int dongle_id;
+	__u8 bank;
+
+	bank = inb(iobase+BSR);
+
+	/* Select Bank 7 */
+	switch_bank(iobase, BANK7);
+	
+	/* IRCFG4: IRSL0_DS and IRSL21_DS are cleared */
+	outb(0x00, iobase+7);
+	
+	/* ID0, 1, and 2 are pulled up/down very slowly */
+	udelay(50);
+	
+	/* IRCFG1: read the ID bits */
+	dongle_id = inb(iobase+4) & 0x0f;
+
+#ifdef BROKEN_DONGLE_ID
+	if (dongle_id == 0x0a)
+		dongle_id = 0x09;
+#endif	
+	/* Go back to  bank 0 before returning */
+	switch_bank(iobase, BANK0);
+
+	outb(bank, iobase+BSR);
+
+	return dongle_id;
+}
+
+/*
+ * Function nsc_ircc_init_dongle_interface (iobase, dongle_id)
+ *
+ *     This function initializes the dongle for the transceiver that is
+ *     used. This procedure needs to be executed once after
+ *     power-on/reset. It also needs to be used whenever you suspect that
+ *     the dongle is changed. 
+ */
+static void nsc_ircc_init_dongle_interface (int iobase, int dongle_id)
+{
+	int bank;
+
+	/* Save current bank */
+	bank = inb(iobase+BSR);
+
+	/* Select Bank 7 */
+	switch_bank(iobase, BANK7);
+	
+	/* IRCFG4: set according to dongle_id */
+	switch (dongle_id) {
+	case 0x00: /* same as */
+	case 0x01: /* Differential serial interface */
+		IRDA_DEBUG(0, "%s(), %s not defined by irda yet\n",
+			   __FUNCTION__, dongle_types[dongle_id]); 
+		break;
+	case 0x02: /* same as */
+	case 0x03: /* Reserved */
+		IRDA_DEBUG(0, "%s(), %s not defined by irda yet\n",
+			   __FUNCTION__, dongle_types[dongle_id]); 
+		break;
+	case 0x04: /* Sharp RY5HD01 */
+		break;
+	case 0x05: /* Reserved, but this is what the Thinkpad reports */
+		IRDA_DEBUG(0, "%s(), %s not defined by irda yet\n",
+			   __FUNCTION__, dongle_types[dongle_id]); 
+		break;
+	case 0x06: /* Single-ended serial interface */
+		IRDA_DEBUG(0, "%s(), %s not defined by irda yet\n",
+			   __FUNCTION__, dongle_types[dongle_id]); 
+		break;
+	case 0x07: /* Consumer-IR only */
+		IRDA_DEBUG(0, "%s(), %s is not for IrDA mode\n",
+			   __FUNCTION__, dongle_types[dongle_id]); 
+		break;
+	case 0x08: /* HP HSDL-2300, HP HSDL-3600/HSDL-3610 */
+		IRDA_DEBUG(0, "%s(), %s\n",
+			   __FUNCTION__, dongle_types[dongle_id]);
+		break;
+	case 0x09: /* IBM31T1100 or Temic TFDS6000/TFDS6500 */
+		outb(0x28, iobase+7); /* Set irsl[0-2] as output */
+		break;
+	case 0x0A: /* same as */
+	case 0x0B: /* Reserved */
+		IRDA_DEBUG(0, "%s(), %s not defined by irda yet\n",
+			   __FUNCTION__, dongle_types[dongle_id]); 
+		break;
+	case 0x0C: /* same as */
+	case 0x0D: /* HP HSDL-1100/HSDL-2100 */
+		/* 
+		 * Set irsl0 as input, irsl[1-2] as output, and separate 
+		 * inputs are used for SIR and MIR/FIR 
+		 */
+		outb(0x48, iobase+7); 
+		break;
+	case 0x0E: /* Supports SIR Mode only */
+		outb(0x28, iobase+7); /* Set irsl[0-2] as output */
+		break;
+	case 0x0F: /* No dongle connected */
+		IRDA_DEBUG(0, "%s(), %s\n",
+			   __FUNCTION__, dongle_types[dongle_id]); 
+
+		switch_bank(iobase, BANK0);
+		outb(0x62, iobase+MCR);
+		break;
+	default: 
+		IRDA_DEBUG(0, "%s(), invalid dongle_id %#x", 
+			   __FUNCTION__, dongle_id);
+	}
+	
+	/* IRCFG1: IRSL1 and 2 are set to IrDA mode */
+	outb(0x00, iobase+4);
+
+	/* Restore bank register */
+	outb(bank, iobase+BSR);
+	
+} /* set_up_dongle_interface */
+
+/*
+ * Function nsc_ircc_change_dongle_speed (iobase, speed, dongle_id)
+ *
+ *    Change speed of the attach dongle
+ *
+ */
+static void nsc_ircc_change_dongle_speed(int iobase, int speed, int dongle_id)
+{
+	__u8 bank;
+
+	/* Save current bank */
+	bank = inb(iobase+BSR);
+
+	/* Select Bank 7 */
+	switch_bank(iobase, BANK7);
+	
+	/* IRCFG1: set according to dongle_id */
+	switch (dongle_id) {
+	case 0x00: /* same as */
+	case 0x01: /* Differential serial interface */
+		IRDA_DEBUG(0, "%s(), %s not defined by irda yet\n",
+			   __FUNCTION__, dongle_types[dongle_id]); 
+		break;
+	case 0x02: /* same as */
+	case 0x03: /* Reserved */
+		IRDA_DEBUG(0, "%s(), %s not defined by irda yet\n",
+			   __FUNCTION__, dongle_types[dongle_id]); 
+		break;
+	case 0x04: /* Sharp RY5HD01 */
+		break;
+	case 0x05: /* Reserved */
+		IRDA_DEBUG(0, "%s(), %s not defined by irda yet\n",
+			   __FUNCTION__, dongle_types[dongle_id]); 
+		break;
+	case 0x06: /* Single-ended serial interface */
+		IRDA_DEBUG(0, "%s(), %s not defined by irda yet\n",
+			   __FUNCTION__, dongle_types[dongle_id]); 
+		break;
+	case 0x07: /* Consumer-IR only */
+		IRDA_DEBUG(0, "%s(), %s is not for IrDA mode\n",
+			   __FUNCTION__, dongle_types[dongle_id]); 
+		break;
+	case 0x08: /* HP HSDL-2300, HP HSDL-3600/HSDL-3610 */
+		IRDA_DEBUG(0, "%s(), %s\n", 
+			   __FUNCTION__, dongle_types[dongle_id]); 
+		outb(0x00, iobase+4);
+		if (speed > 115200)
+			outb(0x01, iobase+4);
+		break;
+	case 0x09: /* IBM31T1100 or Temic TFDS6000/TFDS6500 */
+		outb(0x01, iobase+4);
+
+		if (speed == 4000000) {
+			/* There was a cli() there, but we now are already
+			 * under spin_lock_irqsave() - JeanII */
+			outb(0x81, iobase+4);
+			outb(0x80, iobase+4);
+		} else
+			outb(0x00, iobase+4);
+		break;
+	case 0x0A: /* same as */
+	case 0x0B: /* Reserved */
+		IRDA_DEBUG(0, "%s(), %s not defined by irda yet\n",
+			   __FUNCTION__, dongle_types[dongle_id]); 
+		break;
+	case 0x0C: /* same as */
+	case 0x0D: /* HP HSDL-1100/HSDL-2100 */
+		break;
+	case 0x0E: /* Supports SIR Mode only */
+		break;
+	case 0x0F: /* No dongle connected */
+		IRDA_DEBUG(0, "%s(), %s is not for IrDA mode\n",
+			   __FUNCTION__, dongle_types[dongle_id]);
+
+		switch_bank(iobase, BANK0); 
+		outb(0x62, iobase+MCR);
+		break;
+	default: 
+		IRDA_DEBUG(0, "%s(), invalid data_rate\n", __FUNCTION__);
+	}
+	/* Restore bank register */
+	outb(bank, iobase+BSR);
+}
+
+/*
+ * Function nsc_ircc_change_speed (self, baud)
+ *
+ *    Change the speed of the device
+ *
+ * This function *must* be called with irq off and spin-lock.
+ */
+static __u8 nsc_ircc_change_speed(struct nsc_ircc_cb *self, __u32 speed)
+{
+	struct net_device *dev = self->netdev;
+	__u8 mcr = MCR_SIR;
+	int iobase; 
+	__u8 bank;
+	__u8 ier;                  /* Interrupt enable register */
+
+	IRDA_DEBUG(2, "%s(), speed=%d\n", __FUNCTION__, speed);
+
+	IRDA_ASSERT(self != NULL, return 0;);
+
+	iobase = self->io.fir_base;
+
+	/* Update accounting for new speed */
+	self->io.speed = speed;
+
+	/* Save current bank */
+	bank = inb(iobase+BSR);
+
+	/* Disable interrupts */
+	switch_bank(iobase, BANK0);
+	outb(0, iobase+IER);
+
+	/* Select Bank 2 */
+	switch_bank(iobase, BANK2);
+
+	outb(0x00, iobase+BGDH);
+	switch (speed) {
+	case 9600:   outb(0x0c, iobase+BGDL); break;
+	case 19200:  outb(0x06, iobase+BGDL); break;
+	case 38400:  outb(0x03, iobase+BGDL); break;
+	case 57600:  outb(0x02, iobase+BGDL); break;
+	case 115200: outb(0x01, iobase+BGDL); break;
+	case 576000:
+		switch_bank(iobase, BANK5);
+		
+		/* IRCR2: MDRS is set */
+		outb(inb(iobase+4) | 0x04, iobase+4);
+	       
+		mcr = MCR_MIR;
+		IRDA_DEBUG(0, "%s(), handling baud of 576000\n", __FUNCTION__);
+		break;
+	case 1152000:
+		mcr = MCR_MIR;
+		IRDA_DEBUG(0, "%s(), handling baud of 1152000\n", __FUNCTION__);
+		break;
+	case 4000000:
+		mcr = MCR_FIR;
+		IRDA_DEBUG(0, "%s(), handling baud of 4000000\n", __FUNCTION__);
+		break;
+	default:
+		mcr = MCR_FIR;
+		IRDA_DEBUG(0, "%s(), unknown baud rate of %d\n", 
+			   __FUNCTION__, speed);
+		break;
+	}
+
+	/* Set appropriate speed mode */
+	switch_bank(iobase, BANK0);
+	outb(mcr | MCR_TX_DFR, iobase+MCR);
+
+	/* Give some hits to the transceiver */
+	nsc_ircc_change_dongle_speed(iobase, speed, self->io.dongle_id);
+
+	/* Set FIFO threshold to TX17, RX16 */
+	switch_bank(iobase, BANK0);
+	outb(0x00, iobase+FCR);
+	outb(FCR_FIFO_EN, iobase+FCR);
+	outb(FCR_RXTH|     /* Set Rx FIFO threshold */
+	     FCR_TXTH|     /* Set Tx FIFO threshold */
+	     FCR_TXSR|     /* Reset Tx FIFO */
+	     FCR_RXSR|     /* Reset Rx FIFO */
+	     FCR_FIFO_EN,  /* Enable FIFOs */
+	     iobase+FCR);
+	
+	/* Set FIFO size to 32 */
+	switch_bank(iobase, BANK2);
+	outb(EXCR2_RFSIZ|EXCR2_TFSIZ, iobase+EXCR2);
+	
+	/* Enable some interrupts so we can receive frames */
+	switch_bank(iobase, BANK0); 
+	if (speed > 115200) {
+		/* Install FIR xmit handler */
+		dev->hard_start_xmit = nsc_ircc_hard_xmit_fir;
+		ier = IER_SFIF_IE;
+		nsc_ircc_dma_receive(self);
+	} else {
+		/* Install SIR xmit handler */
+		dev->hard_start_xmit = nsc_ircc_hard_xmit_sir;
+		ier = IER_RXHDL_IE;
+	}
+	/* Set our current interrupt mask */
+	outb(ier, iobase+IER);
+    	
+	/* Restore BSR */
+	outb(bank, iobase+BSR);
+
+	/* Make sure interrupt handlers keep the proper interrupt mask */
+	return(ier);
+}
+
+/*
+ * Function nsc_ircc_hard_xmit (skb, dev)
+ *
+ *    Transmit the frame!
+ *
+ */
+static int nsc_ircc_hard_xmit_sir(struct sk_buff *skb, struct net_device *dev)
+{
+	struct nsc_ircc_cb *self;
+	unsigned long flags;
+	int iobase;
+	__s32 speed;
+	__u8 bank;
+	
+	self = (struct nsc_ircc_cb *) dev->priv;
+
+	IRDA_ASSERT(self != NULL, return 0;);
+
+	iobase = self->io.fir_base;
+
+	netif_stop_queue(dev);
+		
+	/* Make sure tests *& speed change are atomic */
+	spin_lock_irqsave(&self->lock, flags);
+	
+	/* Check if we need to change the speed */
+	speed = irda_get_next_speed(skb);
+	if ((speed != self->io.speed) && (speed != -1)) {
+		/* Check for empty frame. */
+		if (!skb->len) {
+			/* If we just sent a frame, we get called before
+			 * the last bytes get out (because of the SIR FIFO).
+			 * If this is the case, let interrupt handler change
+			 * the speed itself... Jean II */
+			if (self->io.direction == IO_RECV) {
+				nsc_ircc_change_speed(self, speed); 
+				/* TODO : For SIR->SIR, the next packet
+				 * may get corrupted - Jean II */
+				netif_wake_queue(dev);
+			} else {
+				self->new_speed = speed;
+				/* Queue will be restarted after speed change
+				 * to make sure packets gets through the
+				 * proper xmit handler - Jean II */
+			}
+			dev->trans_start = jiffies;
+			spin_unlock_irqrestore(&self->lock, flags);
+			dev_kfree_skb(skb);
+			return 0;
+		} else
+			self->new_speed = speed;
+	}
+
+	/* Save current bank */
+	bank = inb(iobase+BSR);
+	
+	self->tx_buff.data = self->tx_buff.head;
+	
+	self->tx_buff.len = async_wrap_skb(skb, self->tx_buff.data, 
+					   self->tx_buff.truesize);
+
+	self->stats.tx_bytes += self->tx_buff.len;
+	
+	/* Add interrupt on tx low level (will fire immediately) */
+	switch_bank(iobase, BANK0);
+	outb(IER_TXLDL_IE, iobase+IER);
+	
+	/* Restore bank register */
+	outb(bank, iobase+BSR);
+
+	dev->trans_start = jiffies;
+	spin_unlock_irqrestore(&self->lock, flags);
+
+	dev_kfree_skb(skb);
+
+	return 0;
+}
+
+static int nsc_ircc_hard_xmit_fir(struct sk_buff *skb, struct net_device *dev)
+{
+	struct nsc_ircc_cb *self;
+	unsigned long flags;
+	int iobase;
+	__s32 speed;
+	__u8 bank;
+	int mtt, diff;
+	
+	self = (struct nsc_ircc_cb *) dev->priv;
+	iobase = self->io.fir_base;
+
+	netif_stop_queue(dev);
+	
+	/* Make sure tests *& speed change are atomic */
+	spin_lock_irqsave(&self->lock, flags);
+
+	/* Check if we need to change the speed */
+	speed = irda_get_next_speed(skb);
+	if ((speed != self->io.speed) && (speed != -1)) {
+		/* Check for empty frame. */
+		if (!skb->len) {
+			/* If we are currently transmitting, defer to
+			 * interrupt handler. - Jean II */
+			if(self->tx_fifo.len == 0) {
+				nsc_ircc_change_speed(self, speed); 
+				netif_wake_queue(dev);
+			} else {
+				self->new_speed = speed;
+				/* Keep queue stopped :
+				 * the speed change operation may change the
+				 * xmit handler, and we want to make sure
+				 * the next packet get through the proper
+				 * Tx path, so block the Tx queue until
+				 * the speed change has been done.
+				 * Jean II */
+			}
+			dev->trans_start = jiffies;
+			spin_unlock_irqrestore(&self->lock, flags);
+			dev_kfree_skb(skb);
+			return 0;
+		} else {
+			/* Change speed after current frame */
+			self->new_speed = speed;
+		}
+	}
+
+	/* Save current bank */
+	bank = inb(iobase+BSR);
+
+	/* Register and copy this frame to DMA memory */
+	self->tx_fifo.queue[self->tx_fifo.free].start = self->tx_fifo.tail;
+	self->tx_fifo.queue[self->tx_fifo.free].len = skb->len;
+	self->tx_fifo.tail += skb->len;
+
+	self->stats.tx_bytes += skb->len;
+
+	memcpy(self->tx_fifo.queue[self->tx_fifo.free].start, skb->data, 
+	       skb->len);
+	
+	self->tx_fifo.len++;
+	self->tx_fifo.free++;
+
+	/* Start transmit only if there is currently no transmit going on */
+	if (self->tx_fifo.len == 1) {
+		/* Check if we must wait the min turn time or not */
+		mtt = irda_get_mtt(skb);
+		if (mtt) {
+			/* Check how much time we have used already */
+			do_gettimeofday(&self->now);
+			diff = self->now.tv_usec - self->stamp.tv_usec;
+			if (diff < 0) 
+				diff += 1000000;
+			
+			/* Check if the mtt is larger than the time we have
+			 * already used by all the protocol processing
+			 */
+			if (mtt > diff) {
+				mtt -= diff;
+
+				/* 
+				 * Use timer if delay larger than 125 us, and
+				 * use udelay for smaller values which should
+				 * be acceptable
+				 */
+				if (mtt > 125) {
+					/* Adjust for timer resolution */
+					mtt = mtt / 125;
+					
+					/* Setup timer */
+					switch_bank(iobase, BANK4);
+					outb(mtt & 0xff, iobase+TMRL);
+					outb((mtt >> 8) & 0x0f, iobase+TMRH);
+					
+					/* Start timer */
+					outb(IRCR1_TMR_EN, iobase+IRCR1);
+					self->io.direction = IO_XMIT;
+					
+					/* Enable timer interrupt */
+					switch_bank(iobase, BANK0);
+					outb(IER_TMR_IE, iobase+IER);
+					
+					/* Timer will take care of the rest */
+					goto out; 
+				} else
+					udelay(mtt);
+			}
+		}		
+		/* Enable DMA interrupt */
+		switch_bank(iobase, BANK0);
+		outb(IER_DMA_IE, iobase+IER);
+
+		/* Transmit frame */
+		nsc_ircc_dma_xmit(self, iobase);
+	}
+ out:
+	/* Not busy transmitting anymore if window is not full,
+	 * and if we don't need to change speed */
+	if ((self->tx_fifo.free < MAX_TX_WINDOW) && (self->new_speed == 0))
+		netif_wake_queue(self->netdev);
+
+	/* Restore bank register */
+	outb(bank, iobase+BSR);
+
+	dev->trans_start = jiffies;
+	spin_unlock_irqrestore(&self->lock, flags);
+	dev_kfree_skb(skb);
+
+	return 0;
+}
+
+/*
+ * Function nsc_ircc_dma_xmit (self, iobase)
+ *
+ *    Transmit data using DMA
+ *
+ */
+static void nsc_ircc_dma_xmit(struct nsc_ircc_cb *self, int iobase)
+{
+	int bsr;
+
+	/* Save current bank */
+	bsr = inb(iobase+BSR);
+
+	/* Disable DMA */
+	switch_bank(iobase, BANK0);
+	outb(inb(iobase+MCR) & ~MCR_DMA_EN, iobase+MCR);
+	
+	self->io.direction = IO_XMIT;
+	
+	/* Choose transmit DMA channel  */ 
+	switch_bank(iobase, BANK2);
+	outb(ECR1_DMASWP|ECR1_DMANF|ECR1_EXT_SL, iobase+ECR1);
+	
+	irda_setup_dma(self->io.dma, 
+		       ((u8 *)self->tx_fifo.queue[self->tx_fifo.ptr].start -
+			self->tx_buff.head) + self->tx_buff_dma,
+		       self->tx_fifo.queue[self->tx_fifo.ptr].len, 
+		       DMA_TX_MODE);
+
+	/* Enable DMA and SIR interaction pulse */
+ 	switch_bank(iobase, BANK0);	
+	outb(inb(iobase+MCR)|MCR_TX_DFR|MCR_DMA_EN|MCR_IR_PLS, iobase+MCR);
+
+	/* Restore bank register */
+	outb(bsr, iobase+BSR);
+}
+
+/*
+ * Function nsc_ircc_pio_xmit (self, iobase)
+ *
+ *    Transmit data using PIO. Returns the number of bytes that actually
+ *    got transferred
+ *
+ */
+static int nsc_ircc_pio_write(int iobase, __u8 *buf, int len, int fifo_size)
+{
+	int actual = 0;
+	__u8 bank;
+	
+	IRDA_DEBUG(4, "%s()\n", __FUNCTION__);
+
+	/* Save current bank */
+	bank = inb(iobase+BSR);
+
+	switch_bank(iobase, BANK0);
+	if (!(inb_p(iobase+LSR) & LSR_TXEMP)) {
+		IRDA_DEBUG(4, "%s(), warning, FIFO not empty yet!\n",
+			   __FUNCTION__);
+
+		/* FIFO may still be filled to the Tx interrupt threshold */
+		fifo_size -= 17;
+	}
+
+	/* Fill FIFO with current frame */
+	while ((fifo_size-- > 0) && (actual < len)) {
+		/* Transmit next byte */
+		outb(buf[actual++], iobase+TXD);
+	}
+        
+	IRDA_DEBUG(4, "%s(), fifo_size %d ; %d sent of %d\n", 
+		   __FUNCTION__, fifo_size, actual, len);
+	
+	/* Restore bank */
+	outb(bank, iobase+BSR);
+
+	return actual;
+}
+
+/*
+ * Function nsc_ircc_dma_xmit_complete (self)
+ *
+ *    The transfer of a frame in finished. This function will only be called 
+ *    by the interrupt handler
+ *
+ */
+static int nsc_ircc_dma_xmit_complete(struct nsc_ircc_cb *self)
+{
+	int iobase;
+	__u8 bank;
+	int ret = TRUE;
+
+	IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
+
+	iobase = self->io.fir_base;
+
+	/* Save current bank */
+	bank = inb(iobase+BSR);
+
+	/* Disable DMA */
+	switch_bank(iobase, BANK0);
+        outb(inb(iobase+MCR) & ~MCR_DMA_EN, iobase+MCR);
+	
+	/* Check for underrrun! */
+	if (inb(iobase+ASCR) & ASCR_TXUR) {
+		self->stats.tx_errors++;
+		self->stats.tx_fifo_errors++;
+		
+		/* Clear bit, by writing 1 into it */
+		outb(ASCR_TXUR, iobase+ASCR);
+	} else {
+		self->stats.tx_packets++;
+	}
+
+	/* Finished with this frame, so prepare for next */
+	self->tx_fifo.ptr++;
+	self->tx_fifo.len--;
+
+	/* Any frames to be sent back-to-back? */
+	if (self->tx_fifo.len) {
+		nsc_ircc_dma_xmit(self, iobase);
+		
+		/* Not finished yet! */
+		ret = FALSE;
+	} else {
+		/* Reset Tx FIFO info */
+		self->tx_fifo.len = self->tx_fifo.ptr = self->tx_fifo.free = 0;
+		self->tx_fifo.tail = self->tx_buff.head;
+	}
+
+	/* Make sure we have room for more frames and
+	 * that we don't need to change speed */
+	if ((self->tx_fifo.free < MAX_TX_WINDOW) && (self->new_speed == 0)) {
+		/* Not busy transmitting anymore */
+		/* Tell the network layer, that we can accept more frames */
+		netif_wake_queue(self->netdev);
+	}
+
+	/* Restore bank */
+	outb(bank, iobase+BSR);
+	
+	return ret;
+}
+
+/*
+ * Function nsc_ircc_dma_receive (self)
+ *
+ *    Get ready for receiving a frame. The device will initiate a DMA
+ *    if it starts to receive a frame.
+ *
+ */
+static int nsc_ircc_dma_receive(struct nsc_ircc_cb *self) 
+{
+	int iobase;
+	__u8 bsr;
+
+	iobase = self->io.fir_base;
+
+	/* Reset Tx FIFO info */
+	self->tx_fifo.len = self->tx_fifo.ptr = self->tx_fifo.free = 0;
+	self->tx_fifo.tail = self->tx_buff.head;
+
+	/* Save current bank */
+	bsr = inb(iobase+BSR);
+
+	/* Disable DMA */
+	switch_bank(iobase, BANK0);
+	outb(inb(iobase+MCR) & ~MCR_DMA_EN, iobase+MCR);
+
+	/* Choose DMA Rx, DMA Fairness, and Advanced mode */
+	switch_bank(iobase, BANK2);
+	outb(ECR1_DMANF|ECR1_EXT_SL, iobase+ECR1);
+
+	self->io.direction = IO_RECV;
+	self->rx_buff.data = self->rx_buff.head;
+	
+	/* Reset Rx FIFO. This will also flush the ST_FIFO */
+	switch_bank(iobase, BANK0);
+	outb(FCR_RXSR|FCR_FIFO_EN, iobase+FCR);
+
+	self->st_fifo.len = self->st_fifo.pending_bytes = 0;
+	self->st_fifo.tail = self->st_fifo.head = 0;
+	
+	irda_setup_dma(self->io.dma, self->rx_buff_dma, self->rx_buff.truesize,
+		       DMA_RX_MODE);
+
+	/* Enable DMA */
+	switch_bank(iobase, BANK0);
+	outb(inb(iobase+MCR)|MCR_DMA_EN, iobase+MCR);
+
+	/* Restore bank register */
+	outb(bsr, iobase+BSR);
+	
+	return 0;
+}
+
+/*
+ * Function nsc_ircc_dma_receive_complete (self)
+ *
+ *    Finished with receiving frames
+ *
+ *    
+ */
+static int nsc_ircc_dma_receive_complete(struct nsc_ircc_cb *self, int iobase)
+{
+	struct st_fifo *st_fifo;
+	struct sk_buff *skb;
+	__u8 status;
+	__u8 bank;
+	int len;
+
+	st_fifo = &self->st_fifo;
+
+	/* Save current bank */
+	bank = inb(iobase+BSR);
+	
+	/* Read all entries in status FIFO */
+	switch_bank(iobase, BANK5);
+	while ((status = inb(iobase+FRM_ST)) & FRM_ST_VLD) {
+		/* We must empty the status FIFO no matter what */
+		len = inb(iobase+RFLFL) | ((inb(iobase+RFLFH) & 0x1f) << 8);
+
+		if (st_fifo->tail >= MAX_RX_WINDOW) {
+			IRDA_DEBUG(0, "%s(), window is full!\n", __FUNCTION__);
+			continue;
+		}
+			
+		st_fifo->entries[st_fifo->tail].status = status;
+		st_fifo->entries[st_fifo->tail].len = len;
+		st_fifo->pending_bytes += len;
+		st_fifo->tail++;
+		st_fifo->len++;
+	}
+	/* Try to process all entries in status FIFO */
+	while (st_fifo->len > 0) {
+		/* Get first entry */
+		status = st_fifo->entries[st_fifo->head].status;
+		len    = st_fifo->entries[st_fifo->head].len;
+		st_fifo->pending_bytes -= len;
+		st_fifo->head++;
+		st_fifo->len--;
+
+		/* Check for errors */
+		if (status & FRM_ST_ERR_MSK) {
+			if (status & FRM_ST_LOST_FR) {
+				/* Add number of lost frames to stats */
+				self->stats.rx_errors += len;	
+			} else {
+				/* Skip frame */
+				self->stats.rx_errors++;
+				
+				self->rx_buff.data += len;
+			
+				if (status & FRM_ST_MAX_LEN)
+					self->stats.rx_length_errors++;
+				
+				if (status & FRM_ST_PHY_ERR) 
+					self->stats.rx_frame_errors++;
+				
+				if (status & FRM_ST_BAD_CRC) 
+					self->stats.rx_crc_errors++;
+			}
+			/* The errors below can be reported in both cases */
+			if (status & FRM_ST_OVR1)
+				self->stats.rx_fifo_errors++;		       
+			
+			if (status & FRM_ST_OVR2)
+				self->stats.rx_fifo_errors++;
+		} else {
+			/*  
+			 * First we must make sure that the frame we
+			 * want to deliver is all in main memory. If we
+			 * cannot tell, then we check if the Rx FIFO is
+			 * empty. If not then we will have to take a nap
+			 * and try again later.  
+			 */
+			if (st_fifo->pending_bytes < self->io.fifo_size) {
+				switch_bank(iobase, BANK0);
+				if (inb(iobase+LSR) & LSR_RXDA) {
+					/* Put this entry back in fifo */
+					st_fifo->head--;
+					st_fifo->len++;
+					st_fifo->pending_bytes += len;
+					st_fifo->entries[st_fifo->head].status = status;
+					st_fifo->entries[st_fifo->head].len = len;
+					/*  
+					 * DMA not finished yet, so try again 
+					 * later, set timer value, resolution 
+					 * 125 us 
+					 */
+					switch_bank(iobase, BANK4);
+					outb(0x02, iobase+TMRL); /* x 125 us */
+					outb(0x00, iobase+TMRH);
+
+					/* Start timer */
+					outb(IRCR1_TMR_EN, iobase+IRCR1);
+
+					/* Restore bank register */
+					outb(bank, iobase+BSR);
+					
+					return FALSE; /* I'll be back! */
+				}
+			}
+
+			/* 
+			 * Remember the time we received this frame, so we can
+			 * reduce the min turn time a bit since we will know
+			 * how much time we have used for protocol processing
+			 */
+			do_gettimeofday(&self->stamp);
+
+			skb = dev_alloc_skb(len+1);
+			if (skb == NULL)  {
+				IRDA_WARNING("%s(), memory squeeze, "
+					     "dropping frame.\n",
+					     __FUNCTION__);
+				self->stats.rx_dropped++;
+
+				/* Restore bank register */
+				outb(bank, iobase+BSR);
+
+				return FALSE;
+			}
+			
+			/* Make sure IP header gets aligned */
+			skb_reserve(skb, 1); 
+
+			/* Copy frame without CRC */
+			if (self->io.speed < 4000000) {
+				skb_put(skb, len-2);
+				memcpy(skb->data, self->rx_buff.data, len-2);
+			} else {
+				skb_put(skb, len-4);
+				memcpy(skb->data, self->rx_buff.data, len-4);
+			}
+
+			/* Move to next frame */
+			self->rx_buff.data += len;
+			self->stats.rx_bytes += len;
+			self->stats.rx_packets++;
+
+			skb->dev = self->netdev;
+			skb->mac.raw  = skb->data;
+			skb->protocol = htons(ETH_P_IRDA);
+			netif_rx(skb);
+			self->netdev->last_rx = jiffies;
+		}
+	}
+	/* Restore bank register */
+	outb(bank, iobase+BSR);
+
+	return TRUE;
+}
+
+/*
+ * Function nsc_ircc_pio_receive (self)
+ *
+ *    Receive all data in receiver FIFO
+ *
+ */
+static void nsc_ircc_pio_receive(struct nsc_ircc_cb *self) 
+{
+	__u8 byte;
+	int iobase;
+
+	iobase = self->io.fir_base;
+	
+	/*  Receive all characters in Rx FIFO */
+	do {
+		byte = inb(iobase+RXD);
+		async_unwrap_char(self->netdev, &self->stats, &self->rx_buff, 
+				  byte);
+	} while (inb(iobase+LSR) & LSR_RXDA); /* Data available */	
+}
+
+/*
+ * Function nsc_ircc_sir_interrupt (self, eir)
+ *
+ *    Handle SIR interrupt
+ *
+ */
+static void nsc_ircc_sir_interrupt(struct nsc_ircc_cb *self, int eir)
+{
+	int actual;
+
+	/* Check if transmit FIFO is low on data */
+	if (eir & EIR_TXLDL_EV) {
+		/* Write data left in transmit buffer */
+		actual = nsc_ircc_pio_write(self->io.fir_base, 
+					   self->tx_buff.data, 
+					   self->tx_buff.len, 
+					   self->io.fifo_size);
+		self->tx_buff.data += actual;
+		self->tx_buff.len  -= actual;
+		
+		self->io.direction = IO_XMIT;
+
+		/* Check if finished */
+		if (self->tx_buff.len > 0)
+			self->ier = IER_TXLDL_IE;
+		else { 
+
+			self->stats.tx_packets++;
+			netif_wake_queue(self->netdev);
+			self->ier = IER_TXEMP_IE;
+		}
+			
+	}
+	/* Check if transmission has completed */
+	if (eir & EIR_TXEMP_EV) {
+		/* Turn around and get ready to receive some data */
+		self->io.direction = IO_RECV;
+		self->ier = IER_RXHDL_IE;
+		/* Check if we need to change the speed?
+		 * Need to be after self->io.direction to avoid race with
+		 * nsc_ircc_hard_xmit_sir() - Jean II */
+		if (self->new_speed) {
+			IRDA_DEBUG(2, "%s(), Changing speed!\n", __FUNCTION__);
+			self->ier = nsc_ircc_change_speed(self,
+							  self->new_speed);
+			self->new_speed = 0;
+			netif_wake_queue(self->netdev);
+
+			/* Check if we are going to FIR */
+			if (self->io.speed > 115200) {
+				/* No need to do anymore SIR stuff */
+				return;
+			}
+		}
+	}
+
+	/* Rx FIFO threshold or timeout */
+	if (eir & EIR_RXHDL_EV) {
+		nsc_ircc_pio_receive(self);
+
+		/* Keep receiving */
+		self->ier = IER_RXHDL_IE;
+	}
+}
+
+/*
+ * Function nsc_ircc_fir_interrupt (self, eir)
+ *
+ *    Handle MIR/FIR interrupt
+ *
+ */
+static void nsc_ircc_fir_interrupt(struct nsc_ircc_cb *self, int iobase, 
+				   int eir)
+{
+	__u8 bank;
+
+	bank = inb(iobase+BSR);
+	
+	/* Status FIFO event*/
+	if (eir & EIR_SFIF_EV) {
+		/* Check if DMA has finished */
+		if (nsc_ircc_dma_receive_complete(self, iobase)) {
+			/* Wait for next status FIFO interrupt */
+			self->ier = IER_SFIF_IE;
+		} else {
+			self->ier = IER_SFIF_IE | IER_TMR_IE;
+		}
+	} else if (eir & EIR_TMR_EV) { /* Timer finished */
+		/* Disable timer */
+		switch_bank(iobase, BANK4);
+		outb(0, iobase+IRCR1);
+
+		/* Clear timer event */
+		switch_bank(iobase, BANK0);
+		outb(ASCR_CTE, iobase+ASCR);
+
+		/* Check if this is a Tx timer interrupt */
+		if (self->io.direction == IO_XMIT) {
+			nsc_ircc_dma_xmit(self, iobase);
+
+			/* Interrupt on DMA */
+			self->ier = IER_DMA_IE;
+		} else {
+			/* Check (again) if DMA has finished */
+			if (nsc_ircc_dma_receive_complete(self, iobase)) {
+				self->ier = IER_SFIF_IE;
+			} else {
+				self->ier = IER_SFIF_IE | IER_TMR_IE;
+			}
+		}
+	} else if (eir & EIR_DMA_EV) {
+		/* Finished with all transmissions? */
+		if (nsc_ircc_dma_xmit_complete(self)) {
+			if(self->new_speed != 0) {
+				/* As we stop the Tx queue, the speed change
+				 * need to be done when the Tx fifo is
+				 * empty. Ask for a Tx done interrupt */
+				self->ier = IER_TXEMP_IE;
+			} else {
+				/* Check if there are more frames to be
+				 * transmitted */
+				if (irda_device_txqueue_empty(self->netdev)) {
+					/* Prepare for receive */
+					nsc_ircc_dma_receive(self);
+					self->ier = IER_SFIF_IE;
+				} else
+					IRDA_WARNING("%s(), potential "
+						     "Tx queue lockup !\n",
+						     __FUNCTION__);
+			}
+		} else {
+			/*  Not finished yet, so interrupt on DMA again */
+			self->ier = IER_DMA_IE;
+		}
+	} else if (eir & EIR_TXEMP_EV) {
+		/* The Tx FIFO has totally drained out, so now we can change
+		 * the speed... - Jean II */
+		self->ier = nsc_ircc_change_speed(self, self->new_speed);
+		self->new_speed = 0;
+		netif_wake_queue(self->netdev);
+		/* Note : nsc_ircc_change_speed() restarted Rx fifo */
+	}
+
+	outb(bank, iobase+BSR);
+}
+
+/*
+ * Function nsc_ircc_interrupt (irq, dev_id, regs)
+ *
+ *    An interrupt from the chip has arrived. Time to do some work
+ *
+ */
+static irqreturn_t nsc_ircc_interrupt(int irq, void *dev_id,
+				struct pt_regs *regs)
+{
+	struct net_device *dev = (struct net_device *) dev_id;
+	struct nsc_ircc_cb *self;
+	__u8 bsr, eir;
+	int iobase;
+
+	if (!dev) {
+		IRDA_WARNING("%s: irq %d for unknown device.\n",
+			     driver_name, irq);
+		return IRQ_NONE;
+	}
+	self = (struct nsc_ircc_cb *) dev->priv;
+
+	spin_lock(&self->lock);	
+
+	iobase = self->io.fir_base;
+
+	bsr = inb(iobase+BSR); 	/* Save current bank */
+
+	switch_bank(iobase, BANK0);	
+	self->ier = inb(iobase+IER); 
+	eir = inb(iobase+EIR) & self->ier; /* Mask out the interesting ones */ 
+
+	outb(0, iobase+IER); /* Disable interrupts */
+	
+	if (eir) {
+		/* Dispatch interrupt handler for the current speed */
+		if (self->io.speed > 115200)
+			nsc_ircc_fir_interrupt(self, iobase, eir);
+		else
+			nsc_ircc_sir_interrupt(self, eir);
+	}
+	
+	outb(self->ier, iobase+IER); /* Restore interrupts */
+	outb(bsr, iobase+BSR);       /* Restore bank register */
+
+	spin_unlock(&self->lock);
+	return IRQ_RETVAL(eir);
+}
+
+/*
+ * Function nsc_ircc_is_receiving (self)
+ *
+ *    Return TRUE is we are currently receiving a frame
+ *
+ */
+static int nsc_ircc_is_receiving(struct nsc_ircc_cb *self)
+{
+	unsigned long flags;
+	int status = FALSE;
+	int iobase;
+	__u8 bank;
+
+	IRDA_ASSERT(self != NULL, return FALSE;);
+
+	spin_lock_irqsave(&self->lock, flags);
+
+	if (self->io.speed > 115200) {
+		iobase = self->io.fir_base;
+
+		/* Check if rx FIFO is not empty */
+		bank = inb(iobase+BSR);
+		switch_bank(iobase, BANK2);
+		if ((inb(iobase+RXFLV) & 0x3f) != 0) {
+			/* We are receiving something */
+			status =  TRUE;
+		}
+		outb(bank, iobase+BSR);
+	} else 
+		status = (self->rx_buff.state != OUTSIDE_FRAME);
+	
+	spin_unlock_irqrestore(&self->lock, flags);
+
+	return status;
+}
+
+/*
+ * Function nsc_ircc_net_open (dev)
+ *
+ *    Start the device
+ *
+ */
+static int nsc_ircc_net_open(struct net_device *dev)
+{
+	struct nsc_ircc_cb *self;
+	int iobase;
+	char hwname[32];
+	__u8 bank;
+	
+	IRDA_DEBUG(4, "%s()\n", __FUNCTION__);
+	
+	IRDA_ASSERT(dev != NULL, return -1;);
+	self = (struct nsc_ircc_cb *) dev->priv;
+	
+	IRDA_ASSERT(self != NULL, return 0;);
+	
+	iobase = self->io.fir_base;
+	
+	if (request_irq(self->io.irq, nsc_ircc_interrupt, 0, dev->name, dev)) {
+		IRDA_WARNING("%s, unable to allocate irq=%d\n",
+			     driver_name, self->io.irq);
+		return -EAGAIN;
+	}
+	/*
+	 * Always allocate the DMA channel after the IRQ, and clean up on 
+	 * failure.
+	 */
+	if (request_dma(self->io.dma, dev->name)) {
+		IRDA_WARNING("%s, unable to allocate dma=%d\n",
+			     driver_name, self->io.dma);
+		free_irq(self->io.irq, dev);
+		return -EAGAIN;
+	}
+	
+	/* Save current bank */
+	bank = inb(iobase+BSR);
+	
+	/* turn on interrupts */
+	switch_bank(iobase, BANK0);
+	outb(IER_LS_IE | IER_RXHDL_IE, iobase+IER);
+
+	/* Restore bank register */
+	outb(bank, iobase+BSR);
+
+	/* Ready to play! */
+	netif_start_queue(dev);
+	
+	/* Give self a hardware name */
+	sprintf(hwname, "NSC-FIR @ 0x%03x", self->io.fir_base);
+
+	/* 
+	 * Open new IrLAP layer instance, now that everything should be
+	 * initialized properly 
+	 */
+	self->irlap = irlap_open(dev, &self->qos, hwname);
+
+	return 0;
+}
+
+/*
+ * Function nsc_ircc_net_close (dev)
+ *
+ *    Stop the device
+ *
+ */
+static int nsc_ircc_net_close(struct net_device *dev)
+{
+	struct nsc_ircc_cb *self;
+	int iobase;
+	__u8 bank;
+
+	IRDA_DEBUG(4, "%s()\n", __FUNCTION__);
+	
+	IRDA_ASSERT(dev != NULL, return -1;);
+
+	self = (struct nsc_ircc_cb *) dev->priv;
+	IRDA_ASSERT(self != NULL, return 0;);
+
+	/* Stop device */
+	netif_stop_queue(dev);
+	
+	/* Stop and remove instance of IrLAP */
+	if (self->irlap)
+		irlap_close(self->irlap);
+	self->irlap = NULL;
+	
+	iobase = self->io.fir_base;
+
+	disable_dma(self->io.dma);
+
+	/* Save current bank */
+	bank = inb(iobase+BSR);
+
+	/* Disable interrupts */
+	switch_bank(iobase, BANK0);
+	outb(0, iobase+IER); 
+       
+	free_irq(self->io.irq, dev);
+	free_dma(self->io.dma);
+
+	/* Restore bank register */
+	outb(bank, iobase+BSR);
+
+	return 0;
+}
+
+/*
+ * Function nsc_ircc_net_ioctl (dev, rq, cmd)
+ *
+ *    Process IOCTL commands for this device
+ *
+ */
+static int nsc_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+	struct if_irda_req *irq = (struct if_irda_req *) rq;
+	struct nsc_ircc_cb *self;
+	unsigned long flags;
+	int ret = 0;
+
+	IRDA_ASSERT(dev != NULL, return -1;);
+
+	self = dev->priv;
+
+	IRDA_ASSERT(self != NULL, return -1;);
+
+	IRDA_DEBUG(2, "%s(), %s, (cmd=0x%X)\n", __FUNCTION__, dev->name, cmd);
+	
+	switch (cmd) {
+	case SIOCSBANDWIDTH: /* Set bandwidth */
+		if (!capable(CAP_NET_ADMIN)) {
+			ret = -EPERM;
+			break;
+		}
+		spin_lock_irqsave(&self->lock, flags);
+		nsc_ircc_change_speed(self, irq->ifr_baudrate);
+		spin_unlock_irqrestore(&self->lock, flags);
+		break;
+	case SIOCSMEDIABUSY: /* Set media busy */
+		if (!capable(CAP_NET_ADMIN)) {
+			ret = -EPERM;
+			break;
+		}
+		irda_device_set_media_busy(self->netdev, TRUE);
+		break;
+	case SIOCGRECEIVING: /* Check if we are receiving right now */
+		/* This is already protected */
+		irq->ifr_receiving = nsc_ircc_is_receiving(self);
+		break;
+	default:
+		ret = -EOPNOTSUPP;
+	}
+	return ret;
+}
+
+static struct net_device_stats *nsc_ircc_net_get_stats(struct net_device *dev)
+{
+	struct nsc_ircc_cb *self = (struct nsc_ircc_cb *) dev->priv;
+	
+	return &self->stats;
+}
+
+static void nsc_ircc_suspend(struct nsc_ircc_cb *self)
+{
+	IRDA_MESSAGE("%s, Suspending\n", driver_name);
+
+	if (self->io.suspended)
+		return;
+
+	nsc_ircc_net_close(self->netdev);
+
+	self->io.suspended = 1;
+}
+
+static void nsc_ircc_wakeup(struct nsc_ircc_cb *self)
+{
+	if (!self->io.suspended)
+		return;
+
+	nsc_ircc_setup(&self->io);
+	nsc_ircc_net_open(self->netdev);
+	
+	IRDA_MESSAGE("%s, Waking up\n", driver_name);
+
+	self->io.suspended = 0;
+}
+
+static int nsc_ircc_pmproc(struct pm_dev *dev, pm_request_t rqst, void *data)
+{
+        struct nsc_ircc_cb *self = (struct nsc_ircc_cb*) dev->data;
+        if (self) {
+                switch (rqst) {
+                case PM_SUSPEND:
+                        nsc_ircc_suspend(self);
+                        break;
+                case PM_RESUME:
+                        nsc_ircc_wakeup(self);
+                        break;
+                }
+        }
+	return 0;
+}
+
+MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>");
+MODULE_DESCRIPTION("NSC IrDA Device Driver");
+MODULE_LICENSE("GPL");
+
+
+module_param(qos_mtt_bits, int, 0);
+MODULE_PARM_DESC(qos_mtt_bits, "Minimum Turn Time");
+module_param_array(io, int, NULL, 0);
+MODULE_PARM_DESC(io, "Base I/O addresses");
+module_param_array(irq, int, NULL, 0);
+MODULE_PARM_DESC(irq, "IRQ lines");
+module_param_array(dma, int, NULL, 0);
+MODULE_PARM_DESC(dma, "DMA channels");
+module_param(dongle_id, int, 0);
+MODULE_PARM_DESC(dongle_id, "Type-id of used dongle");
+
+module_init(nsc_ircc_init);
+module_exit(nsc_ircc_cleanup);
+
diff --git a/drivers/net/irda/nsc-ircc.h b/drivers/net/irda/nsc-ircc.h
new file mode 100644
index 0000000..6edf7e5
--- /dev/null
+++ b/drivers/net/irda/nsc-ircc.h
@@ -0,0 +1,280 @@
+/*********************************************************************
+ *                
+ * Filename:      nsc-ircc.h
+ * Version:       
+ * Description:   
+ * Status:        Experimental.
+ * Author:        Dag Brattli <dagb@cs.uit.no>
+ * Created at:    Fri Nov 13 14:37:40 1998
+ * Modified at:   Sun Jan 23 17:47:00 2000
+ * Modified by:   Dag Brattli <dagb@cs.uit.no>
+ * 
+ *     Copyright (c) 1998-2000 Dag Brattli <dagb@cs.uit.no>
+ *     Copyright (c) 1998 Lichen Wang, <lwang@actisys.com>
+ *     Copyright (c) 1998 Actisys Corp., www.actisys.com
+ *     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.
+ *     
+ ********************************************************************/
+
+#ifndef NSC_IRCC_H
+#define NSC_IRCC_H
+
+#include <linux/time.h>
+
+#include <linux/spinlock.h>
+#include <linux/pm.h>
+#include <linux/types.h>
+#include <asm/io.h>
+
+/* DMA modes needed */
+#define DMA_TX_MODE     0x08    /* Mem to I/O, ++, demand. */
+#define DMA_RX_MODE     0x04    /* I/O to mem, ++, demand. */
+
+/* Config registers for the '108 */
+#define CFG_108_BAIC 0x00
+#define CFG_108_CSRT 0x01
+#define CFG_108_MCTL 0x02
+
+/* Config registers for the '338 */
+#define CFG_338_FER  0x00
+#define CFG_338_FAR  0x01
+#define CFG_338_PTR  0x02
+#define CFG_338_PNP0 0x1b
+#define CFG_338_PNP1 0x1c
+#define CFG_338_PNP3 0x4f
+
+/* Config registers for the '39x (in the logical device bank) */
+#define CFG_39X_LDN	0x07	/* Logical device number (Super I/O bank) */
+#define CFG_39X_SIOCF1	0x21	/* SuperI/O Config */
+#define CFG_39X_ACT	0x30	/* Device activation */
+#define CFG_39X_BASEH	0x60	/* Device base address (high bits) */
+#define CFG_39X_BASEL	0x61	/* Device base address (low bits) */
+#define CFG_39X_IRQNUM	0x70	/* Interrupt number & wake up enable */
+#define CFG_39X_IRQSEL	0x71	/* Interrupt select (edge/level + polarity) */
+#define CFG_39X_DMA0	0x74	/* DMA 0 configuration */
+#define CFG_39X_DMA1	0x75	/* DMA 1 configuration */
+#define CFG_39X_SPC	0xF0	/* Serial port configuration register */
+
+/* Flags for configuration register CRF0 */
+#define APEDCRC		0x02
+#define ENBNKSEL	0x01
+
+/* Set 0 */
+#define TXD             0x00 /* Transmit data port */
+#define RXD             0x00 /* Receive data port */
+
+/* Register 1 */
+#define IER		0x01 /* Interrupt Enable Register*/
+#define IER_RXHDL_IE    0x01 /* Receiver high data level interrupt */
+#define IER_TXLDL_IE    0x02 /* Transeiver low data level interrupt */
+#define IER_LS_IE	0x04//* Link Status Interrupt */
+#define IER_ETXURI      0x04 /* Tx underrun */
+#define IER_DMA_IE	0x10 /* DMA finished interrupt */
+#define IER_TXEMP_IE    0x20
+#define IER_SFIF_IE     0x40 /* Frame status FIFO intr */
+#define IER_TMR_IE      0x80 /* Timer event */
+
+#define FCR		0x02 /* (write only) */
+#define FCR_FIFO_EN     0x01 /* Enable FIFO's */
+#define FCR_RXSR        0x02 /* Rx FIFO soft reset */
+#define FCR_TXSR        0x04 /* Tx FIFO soft reset */
+#define FCR_RXTH	0x40 /* Rx FIFO threshold (set to 16) */
+#define FCR_TXTH	0x20 /* Tx FIFO threshold (set to 17) */
+
+#define EIR		0x02 /* (read only) */
+#define EIR_RXHDL_EV	0x01
+#define EIR_TXLDL_EV    0x02
+#define EIR_LS_EV	0x04
+#define EIR_DMA_EV	0x10
+#define EIR_TXEMP_EV	0x20
+#define EIR_SFIF_EV     0x40
+#define EIR_TMR_EV      0x80
+
+#define LCR             0x03 /* Link control register */
+#define LCR_WLS_8       0x03 /* 8 bits */
+
+#define BSR 	        0x03 /* Bank select register */
+#define BSR_BKSE        0x80
+#define BANK0 	        LCR_WLS_8 /* Must make sure that we set 8N1 */
+#define BANK1	        0x80
+#define BANK2	        0xe0
+#define BANK3	        0xe4
+#define BANK4	        0xe8
+#define BANK5	        0xec
+#define BANK6	        0xf0
+#define BANK7     	0xf4
+
+#define MCR		0x04 /* Mode Control Register */
+#define MCR_MODE_MASK	~(0xd0)
+#define MCR_UART        0x00
+#define MCR_RESERVED  	0x20	
+#define MCR_SHARP_IR    0x40
+#define MCR_SIR         0x60
+#define MCR_MIR  	0x80
+#define MCR_FIR		0xa0
+#define MCR_CEIR        0xb0
+#define MCR_IR_PLS      0x10
+#define MCR_DMA_EN	0x04
+#define MCR_EN_IRQ	0x08
+#define MCR_TX_DFR	0x08
+
+#define LSR             0x05 /* Link status register */
+#define LSR_RXDA        0x01 /* Receiver data available */
+#define LSR_TXRDY       0x20 /* Transmitter ready */
+#define LSR_TXEMP       0x40 /* Transmitter empty */
+
+#define ASCR            0x07 /* Auxillary Status and Control Register */
+#define ASCR_RXF_TOUT   0x01 /* Rx FIFO timeout */
+#define ASCR_FEND_INF   0x02 /* Frame end bytes in rx FIFO */
+#define ASCR_S_EOT      0x04 /* Set end of transmission */
+#define ASCT_RXBSY      0x20 /* Rx busy */
+#define ASCR_TXUR       0x40 /* Transeiver underrun */
+#define ASCR_CTE        0x80 /* Clear timer event */
+
+/* Bank 2 */
+#define BGDL            0x00 /* Baud Generator Divisor Port (Low Byte) */
+#define BGDH            0x01 /* Baud Generator Divisor Port (High Byte) */
+
+#define ECR1		0x02 /* Extended Control Register 1 */
+#define ECR1_EXT_SL	0x01 /* Extended Mode Select */
+#define ECR1_DMANF	0x02 /* DMA Fairness */
+#define ECR1_DMATH      0x04 /* DMA Threshold */
+#define ECR1_DMASWP	0x08 /* DMA Swap */
+
+#define EXCR2		0x04
+#define EXCR2_TFSIZ	0x01 /* Rx FIFO size = 32 */
+#define EXCR2_RFSIZ	0x04 /* Tx FIFO size = 32 */
+
+#define TXFLV           0x06 /* Tx FIFO level */
+#define RXFLV           0x07 /* Rx FIFO level */
+
+/* Bank 3 */
+#define MID		0x00
+
+/* Bank 4 */
+#define TMRL            0x00 /* Timer low byte */
+#define TMRH            0x01 /* Timer high byte */
+#define IRCR1           0x02 /* Infrared control register 1 */
+#define IRCR1_TMR_EN    0x01 /* Timer enable */
+
+#define TFRLL		0x04
+#define TFRLH		0x05
+#define RFRLL		0x06
+#define RFRLH		0x07
+
+/* Bank 5 */
+#define IRCR2           0x04 /* Infrared control register 2 */
+#define IRCR2_MDRS      0x04 /* MIR data rate select */
+#define IRCR2_FEND_MD   0x20 /* */
+
+#define FRM_ST          0x05 /* Frame status FIFO */
+#define FRM_ST_VLD      0x80 /* Frame status FIFO data valid */
+#define FRM_ST_ERR_MSK  0x5f
+#define FRM_ST_LOST_FR  0x40 /* Frame lost */
+#define FRM_ST_MAX_LEN  0x10 /* Max frame len exceeded */
+#define FRM_ST_PHY_ERR  0x08 /* Physical layer error */
+#define FRM_ST_BAD_CRC  0x04 
+#define FRM_ST_OVR1     0x02 /* Rx FIFO overrun */
+#define FRM_ST_OVR2     0x01 /* Frame status FIFO overrun */
+
+#define RFLFL           0x06
+#define RFLFH           0x07
+
+/* Bank 6 */
+#define IR_CFG2		0x00
+#define IR_CFG2_DIS_CRC	0x02
+
+/* Bank 7 */
+#define IRM_CR		0x07 /* Infrared module control register */
+#define IRM_CR_IRX_MSL	0x40
+#define IRM_CR_AF_MNT   0x80 /* Automatic format */
+
+/* NSC chip information */
+struct nsc_chip {
+	char *name;          /* Name of chipset */
+	int cfg[3];          /* Config registers */
+	u_int8_t cid_index;  /* Chip identification index reg */
+	u_int8_t cid_value;  /* Chip identification expected value */
+	u_int8_t cid_mask;   /* Chip identification revision mask */
+
+	/* Functions for probing and initializing the specific chip */
+	int (*probe)(struct nsc_chip *chip, chipio_t *info);
+	int (*init)(struct nsc_chip *chip, chipio_t *info);
+};
+typedef struct nsc_chip nsc_chip_t;
+
+/* For storing entries in the status FIFO */
+struct st_fifo_entry {
+	int status;
+	int len;
+};
+
+#define MAX_TX_WINDOW 7
+#define MAX_RX_WINDOW 7
+
+struct st_fifo {
+	struct st_fifo_entry entries[MAX_RX_WINDOW];
+	int pending_bytes;
+	int head;
+	int tail;
+	int len;
+};
+
+struct frame_cb {
+	void *start; /* Start of frame in DMA mem */
+	int len;     /* Lenght of frame in DMA mem */
+};
+
+struct tx_fifo {
+	struct frame_cb queue[MAX_TX_WINDOW]; /* Info about frames in queue */
+	int             ptr;                  /* Currently being sent */
+	int             len;                  /* Lenght of queue */
+	int             free;                 /* Next free slot */
+	void           *tail;                 /* Next free start in DMA mem */
+};
+
+/* Private data for each instance */
+struct nsc_ircc_cb {
+	struct st_fifo st_fifo;    /* Info about received frames */
+	struct tx_fifo tx_fifo;    /* Info about frames to be transmitted */
+
+	struct net_device *netdev;     /* Yes! we are some kind of netdevice */
+	struct net_device_stats stats;
+	
+	struct irlap_cb *irlap;    /* The link layer we are binded to */
+	struct qos_info qos;       /* QoS capabilities for this device */
+	
+	chipio_t io;               /* IrDA controller information */
+	iobuff_t tx_buff;          /* Transmit buffer */
+	iobuff_t rx_buff;          /* Receive buffer */
+	dma_addr_t tx_buff_dma;
+	dma_addr_t rx_buff_dma;
+
+	__u8 ier;                  /* Interrupt enable register */
+
+	struct timeval stamp;
+	struct timeval now;
+
+	spinlock_t lock;           /* For serializing operations */
+	
+	__u32 new_speed;
+	int index;                 /* Instance index */
+
+        struct pm_dev *dev;
+};
+
+static inline void switch_bank(int iobase, int bank)
+{
+		outb(bank, iobase+BSR);
+}
+
+#endif /* NSC_IRCC_H */
diff --git a/drivers/net/irda/old_belkin-sir.c b/drivers/net/irda/old_belkin-sir.c
new file mode 100644
index 0000000..8c22c73
--- /dev/null
+++ b/drivers/net/irda/old_belkin-sir.c
@@ -0,0 +1,156 @@
+/*********************************************************************
+ *                
+ * Filename:      old_belkin.c
+ * Version:       1.1
+ * Description:   Driver for the Belkin (old) SmartBeam dongle
+ * Status:        Experimental...
+ * Author:        Jean Tourrilhes <jt@hpl.hp.com>
+ * Created at:    22/11/99
+ * Modified at:   Fri Dec 17 09:13:32 1999
+ * Modified by:   Dag Brattli <dagb@cs.uit.no>
+ * 
+ *     Copyright (c) 1999 Jean Tourrilhes, 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 <net/irda/irda_device.h>
+
+#include "sir-dev.h"
+
+/*
+ * Belkin is selling a dongle called the SmartBeam.
+ * In fact, there is two hardware version of this dongle, of course with
+ * the same name and looking the exactly same (grrr...).
+ * I guess that I've got the old one, because inside I don't have
+ * a jumper for IrDA/ASK...
+ *
+ * As far as I can make it from info on their web site, the old dongle 
+ * support only 9600 b/s, which make our life much simpler as far as
+ * the driver is concerned, but you might not like it very much ;-)
+ * The new SmartBeam does 115 kb/s, and I've not tested it...
+ *
+ * Belkin claim that the correct driver for the old dongle (in Windows)
+ * is the generic Parallax 9500a driver, but the Linux LiteLink driver
+ * fails for me (probably because Linux-IrDA doesn't rate fallback),
+ * so I created this really dumb driver...
+ *
+ * In fact, this driver doesn't do much. The only thing it does is to
+ * prevent Linux-IrDA to use any other speed than 9600 b/s ;-) This
+ * driver is called "old_belkin" so that when the new SmartBeam is supported
+ * its driver can be called "belkin" instead of "new_belkin".
+ *
+ * Note : this driver was written without any info/help from Belkin,
+ * so a lot of info here might be totally wrong. Blame me ;-)
+ */
+
+static int old_belkin_open(struct sir_dev *dev);
+static int old_belkin_close(struct sir_dev *dev);
+static int old_belkin_change_speed(struct sir_dev *dev, unsigned speed);
+static int old_belkin_reset(struct sir_dev *dev);
+
+static struct dongle_driver old_belkin = {
+	.owner		= THIS_MODULE,
+	.driver_name	= "Old Belkin SmartBeam",
+	.type		= IRDA_OLD_BELKIN_DONGLE,
+	.open		= old_belkin_open,
+	.close		= old_belkin_close,
+	.reset		= old_belkin_reset,
+	.set_speed	= old_belkin_change_speed,
+};
+
+static int __init old_belkin_sir_init(void)
+{
+	return irda_register_dongle(&old_belkin);
+}
+
+static void __exit old_belkin_sir_cleanup(void)
+{
+	irda_unregister_dongle(&old_belkin);
+}
+
+static int old_belkin_open(struct sir_dev *dev)
+{
+	struct qos_info *qos = &dev->qos;
+
+	IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
+
+	/* Power on dongle */
+	sirdev_set_dtr_rts(dev, TRUE, TRUE);
+
+	/* Not too fast, please... */
+	qos->baud_rate.bits &= IR_9600;
+	/* Needs at least 10 ms (totally wild guess, can do probably better) */
+	qos->min_turn_time.bits = 0x01;
+	irda_qos_bits_to_value(qos);
+
+	/* irda thread waits 50 msec for power settling */
+
+	return 0;
+}
+
+static int old_belkin_close(struct sir_dev *dev)
+{
+	IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
+
+	/* Power off dongle */
+	sirdev_set_dtr_rts(dev, FALSE, FALSE);
+
+	return 0;
+}
+
+/*
+ * Function old_belkin_change_speed (task)
+ *
+ *    With only one speed available, not much to do...
+ */
+static int old_belkin_change_speed(struct sir_dev *dev, unsigned speed)
+{
+	IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
+
+	dev->speed = 9600;
+	return (speed==dev->speed) ? 0 : -EINVAL;
+}
+
+/*
+ * Function old_belkin_reset (task)
+ *
+ *      Reset the Old-Belkin type dongle.
+ *
+ */
+static int old_belkin_reset(struct sir_dev *dev)
+{
+	IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
+
+	/* This dongles speed "defaults" to 9600 bps ;-) */
+	dev->speed = 9600;
+
+	return 0;
+}
+
+MODULE_AUTHOR("Jean Tourrilhes <jt@hpl.hp.com>");
+MODULE_DESCRIPTION("Belkin (old) SmartBeam dongle driver");	
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("irda-dongle-7"); /* IRDA_OLD_BELKIN_DONGLE */
+
+module_init(old_belkin_sir_init);
+module_exit(old_belkin_sir_cleanup);
diff --git a/drivers/net/irda/old_belkin.c b/drivers/net/irda/old_belkin.c
new file mode 100644
index 0000000..26f81fd
--- /dev/null
+++ b/drivers/net/irda/old_belkin.c
@@ -0,0 +1,164 @@
+/*********************************************************************
+ *                
+ * Filename:      old_belkin.c
+ * Version:       1.1
+ * Description:   Driver for the Belkin (old) SmartBeam dongle
+ * Status:        Experimental...
+ * Author:        Jean Tourrilhes <jt@hpl.hp.com>
+ * Created at:    22/11/99
+ * Modified at:   Fri Dec 17 09:13:32 1999
+ * Modified by:   Dag Brattli <dagb@cs.uit.no>
+ * 
+ *     Copyright (c) 1999 Jean Tourrilhes, 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/tty.h>
+#include <linux/init.h>
+
+#include <net/irda/irda.h>
+#include <net/irda/irda_device.h>
+
+/*
+ * Belkin is selling a dongle called the SmartBeam.
+ * In fact, there is two hardware version of this dongle, of course with
+ * the same name and looking the exactly same (grrr...).
+ * I guess that I've got the old one, because inside I don't have
+ * a jumper for IrDA/ASK...
+ *
+ * As far as I can make it from info on their web site, the old dongle 
+ * support only 9600 b/s, which make our life much simpler as far as
+ * the driver is concerned, but you might not like it very much ;-)
+ * The new SmartBeam does 115 kb/s, and I've not tested it...
+ *
+ * Belkin claim that the correct driver for the old dongle (in Windows)
+ * is the generic Parallax 9500a driver, but the Linux LiteLink driver
+ * fails for me (probably because Linux-IrDA doesn't rate fallback),
+ * so I created this really dumb driver...
+ *
+ * In fact, this driver doesn't do much. The only thing it does is to
+ * prevent Linux-IrDA to use any other speed than 9600 b/s ;-) This
+ * driver is called "old_belkin" so that when the new SmartBeam is supported
+ * its driver can be called "belkin" instead of "new_belkin".
+ *
+ * Note : this driver was written without any info/help from Belkin,
+ * so a lot of info here might be totally wrong. Blame me ;-)
+ */
+
+/* Let's guess */
+#define MIN_DELAY 25      /* 15 us, but wait a little more to be sure */
+
+static void old_belkin_open(dongle_t *self, struct qos_info *qos);
+static void old_belkin_close(dongle_t *self);
+static int  old_belkin_change_speed(struct irda_task *task);
+static int  old_belkin_reset(struct irda_task *task);
+
+/* These are the baudrates supported */
+/* static __u32 baud_rates[] = { 9600 }; */
+
+static struct dongle_reg dongle = {
+	.type = IRDA_OLD_BELKIN_DONGLE,
+	.open = old_belkin_open,
+	.close = old_belkin_close,
+	.reset = old_belkin_reset,
+	.change_speed = old_belkin_change_speed,
+	.owner = THIS_MODULE,
+};
+
+static int __init old_belkin_init(void)
+{
+	return irda_device_register_dongle(&dongle);
+}
+
+static void __exit old_belkin_cleanup(void)
+{
+	irda_device_unregister_dongle(&dongle);
+}
+
+static void old_belkin_open(dongle_t *self, struct qos_info *qos)
+{
+	/* Not too fast, please... */
+	qos->baud_rate.bits &= IR_9600;
+	/* Needs at least 10 ms (totally wild guess, can do probably better) */
+	qos->min_turn_time.bits = 0x01;
+}
+
+static void old_belkin_close(dongle_t *self)
+{
+	/* Power off dongle */
+	self->set_dtr_rts(self->dev, FALSE, FALSE);
+}
+
+/*
+ * Function old_belkin_change_speed (task)
+ *
+ *    With only one speed available, not much to do...
+ */
+static int old_belkin_change_speed(struct irda_task *task)
+{
+	irda_task_next_state(task, IRDA_TASK_DONE);
+
+	return 0;
+}
+
+/*
+ * Function old_belkin_reset (task)
+ *
+ *      Reset the Old-Belkin type dongle.
+ *
+ */
+static int old_belkin_reset(struct irda_task *task)
+{
+	dongle_t *self = (dongle_t *) task->instance;
+
+	/* Power on dongle */
+	self->set_dtr_rts(self->dev, TRUE, TRUE);
+
+	/* Sleep a minimum of 15 us */
+	udelay(MIN_DELAY);
+
+	/* This dongles speed "defaults" to 9600 bps ;-) */
+	self->speed = 9600;
+
+	irda_task_next_state(task, IRDA_TASK_DONE);
+
+	return 0;
+}
+
+MODULE_AUTHOR("Jean Tourrilhes <jt@hpl.hp.com>");
+MODULE_DESCRIPTION("Belkin (old) SmartBeam dongle driver");	
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("irda-dongle-7"); /* IRDA_OLD_BELKIN_DONGLE */
+
+/*
+ * Function init_module (void)
+ *
+ *    Initialize Old-Belkin module
+ *
+ */
+module_init(old_belkin_init);
+
+/*
+ * Function cleanup_module (void)
+ *
+ *    Cleanup Old-Belkin module
+ *
+ */
+module_exit(old_belkin_cleanup);
diff --git a/drivers/net/irda/sa1100_ir.c b/drivers/net/irda/sa1100_ir.c
new file mode 100644
index 0000000..89f5096
--- /dev/null
+++ b/drivers/net/irda/sa1100_ir.c
@@ -0,0 +1,1045 @@
+/*
+ *  linux/drivers/net/irda/sa1100_ir.c
+ *
+ *  Copyright (C) 2000-2001 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ *  Infra-red driver for the StrongARM SA1100 embedded microprocessor
+ *
+ *  Note that we don't have to worry about the SA1111's DMA bugs in here,
+ *  so we use the straight forward dma_map_* functions with a null pointer.
+ *
+ *  This driver takes one kernel command line parameter, sa1100ir=, with
+ *  the following options:
+ *	max_rate:baudrate	- set the maximum baud rate
+ *	power_leve:level	- set the transmitter power level
+ *	tx_lpm:0|1		- set transmit low power mode
+ */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/slab.h>
+#include <linux/rtnetlink.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+
+#include <net/irda/irda.h>
+#include <net/irda/wrapper.h>
+#include <net/irda/irda_device.h>
+
+#include <asm/irq.h>
+#include <asm/dma.h>
+#include <asm/hardware.h>
+#include <asm/mach/irda.h>
+
+static int power_level = 3;
+static int tx_lpm;
+static int max_rate = 4000000;
+
+struct sa1100_irda {
+	unsigned char		hscr0;
+	unsigned char		utcr4;
+	unsigned char		power;
+	unsigned char		open;
+
+	int			speed;
+	int			newspeed;
+
+	struct sk_buff		*txskb;
+	struct sk_buff		*rxskb;
+	dma_addr_t		txbuf_dma;
+	dma_addr_t		rxbuf_dma;
+	dma_regs_t		*txdma;
+	dma_regs_t		*rxdma;
+
+	struct net_device_stats	stats;
+	struct device		*dev;
+	struct irda_platform_data *pdata;
+	struct irlap_cb		*irlap;
+	struct qos_info		qos;
+
+	iobuff_t		tx_buff;
+	iobuff_t		rx_buff;
+};
+
+#define IS_FIR(si)		((si)->speed >= 4000000)
+
+#define HPSIR_MAX_RXLEN		2047
+
+/*
+ * Allocate and map the receive buffer, unless it is already allocated.
+ */
+static int sa1100_irda_rx_alloc(struct sa1100_irda *si)
+{
+	if (si->rxskb)
+		return 0;
+
+	si->rxskb = alloc_skb(HPSIR_MAX_RXLEN + 1, GFP_ATOMIC);
+
+	if (!si->rxskb) {
+		printk(KERN_ERR "sa1100_ir: out of memory for RX SKB\n");
+		return -ENOMEM;
+	}
+
+	/*
+	 * Align any IP headers that may be contained
+	 * within the frame.
+	 */
+	skb_reserve(si->rxskb, 1);
+
+	si->rxbuf_dma = dma_map_single(si->dev, si->rxskb->data,
+					HPSIR_MAX_RXLEN,
+					DMA_FROM_DEVICE);
+	return 0;
+}
+
+/*
+ * We want to get here as soon as possible, and get the receiver setup.
+ * We use the existing buffer.
+ */
+static void sa1100_irda_rx_dma_start(struct sa1100_irda *si)
+{
+	if (!si->rxskb) {
+		printk(KERN_ERR "sa1100_ir: rx buffer went missing\n");
+		return;
+	}
+
+	/*
+	 * First empty receive FIFO
+	 */
+	Ser2HSCR0 = si->hscr0 | HSCR0_HSSP;
+
+	/*
+	 * Enable the DMA, receiver and receive interrupt.
+	 */
+	sa1100_clear_dma(si->rxdma);
+	sa1100_start_dma(si->rxdma, si->rxbuf_dma, HPSIR_MAX_RXLEN);
+	Ser2HSCR0 = si->hscr0 | HSCR0_HSSP | HSCR0_RXE;
+}
+
+/*
+ * Set the IrDA communications speed.
+ */
+static int sa1100_irda_set_speed(struct sa1100_irda *si, int speed)
+{
+	unsigned long flags;
+	int brd, ret = -EINVAL;
+
+	switch (speed) {
+	case 9600:	case 19200:	case 38400:
+	case 57600:	case 115200:
+		brd = 3686400 / (16 * speed) - 1;
+
+		/*
+		 * Stop the receive DMA.
+		 */
+		if (IS_FIR(si))
+			sa1100_stop_dma(si->rxdma);
+
+		local_irq_save(flags);
+
+		Ser2UTCR3 = 0;
+		Ser2HSCR0 = HSCR0_UART;
+
+		Ser2UTCR1 = brd >> 8;
+		Ser2UTCR2 = brd;
+
+		/*
+		 * Clear status register
+		 */
+		Ser2UTSR0 = UTSR0_REB | UTSR0_RBB | UTSR0_RID;
+		Ser2UTCR3 = UTCR3_RIE | UTCR3_RXE | UTCR3_TXE;
+
+		if (si->pdata->set_speed)
+			si->pdata->set_speed(si->dev, speed);
+
+		si->speed = speed;
+
+		local_irq_restore(flags);
+		ret = 0;
+		break;
+
+	case 4000000:
+		local_irq_save(flags);
+
+		si->hscr0 = 0;
+
+		Ser2HSSR0 = 0xff;
+		Ser2HSCR0 = si->hscr0 | HSCR0_HSSP;
+		Ser2UTCR3 = 0;
+
+		si->speed = speed;
+
+		if (si->pdata->set_speed)
+			si->pdata->set_speed(si->dev, speed);
+
+		sa1100_irda_rx_alloc(si);
+		sa1100_irda_rx_dma_start(si);
+
+		local_irq_restore(flags);
+
+		break;
+
+	default:
+		break;
+	}
+
+	return ret;
+}
+
+/*
+ * Control the power state of the IrDA transmitter.
+ * State:
+ *  0 - off
+ *  1 - short range, lowest power
+ *  2 - medium range, medium power
+ *  3 - maximum range, high power
+ *
+ * Currently, only assabet is known to support this.
+ */
+static int
+__sa1100_irda_set_power(struct sa1100_irda *si, unsigned int state)
+{
+	int ret = 0;
+	if (si->pdata->set_power)
+		ret = si->pdata->set_power(si->dev, state);
+	return ret;
+}
+
+static inline int
+sa1100_set_power(struct sa1100_irda *si, unsigned int state)
+{
+	int ret;
+
+	ret = __sa1100_irda_set_power(si, state);
+	if (ret == 0)
+		si->power = state;
+
+	return ret;
+}
+
+static int sa1100_irda_startup(struct sa1100_irda *si)
+{
+	int ret;
+
+	/*
+	 * Ensure that the ports for this device are setup correctly.
+	 */
+	if (si->pdata->startup)
+		si->pdata->startup(si->dev);
+
+	/*
+	 * Configure PPC for IRDA - we want to drive TXD2 low.
+	 * We also want to drive this pin low during sleep.
+	 */
+	PPSR &= ~PPC_TXD2;
+	PSDR &= ~PPC_TXD2;
+	PPDR |= PPC_TXD2;
+
+	/*
+	 * Enable HP-SIR modulation, and ensure that the port is disabled.
+	 */
+	Ser2UTCR3 = 0;
+	Ser2HSCR0 = HSCR0_UART;
+	Ser2UTCR4 = si->utcr4;
+	Ser2UTCR0 = UTCR0_8BitData;
+	Ser2HSCR2 = HSCR2_TrDataH | HSCR2_RcDataL;
+
+	/*
+	 * Clear status register
+	 */
+	Ser2UTSR0 = UTSR0_REB | UTSR0_RBB | UTSR0_RID;
+
+	ret = sa1100_irda_set_speed(si, si->speed = 9600);
+	if (ret) {
+		Ser2UTCR3 = 0;
+		Ser2HSCR0 = 0;
+
+		if (si->pdata->shutdown)
+			si->pdata->shutdown(si->dev);
+	}
+
+	return ret;
+}
+
+static void sa1100_irda_shutdown(struct sa1100_irda *si)
+{
+	/*
+	 * Stop all DMA activity.
+	 */
+	sa1100_stop_dma(si->rxdma);
+	sa1100_stop_dma(si->txdma);
+
+	/* Disable the port. */
+	Ser2UTCR3 = 0;
+	Ser2HSCR0 = 0;
+
+	if (si->pdata->shutdown)
+		si->pdata->shutdown(si->dev);
+}
+
+#ifdef CONFIG_PM
+/*
+ * Suspend the IrDA interface.
+ */
+static int sa1100_irda_suspend(struct device *_dev, u32 state, u32 level)
+{
+	struct net_device *dev = dev_get_drvdata(_dev);
+	struct sa1100_irda *si;
+
+	if (!dev || level != SUSPEND_DISABLE)
+		return 0;
+
+	si = dev->priv;
+	if (si->open) {
+		/*
+		 * Stop the transmit queue
+		 */
+		netif_device_detach(dev);
+		disable_irq(dev->irq);
+		sa1100_irda_shutdown(si);
+		__sa1100_irda_set_power(si, 0);
+	}
+
+	return 0;
+}
+
+/*
+ * Resume the IrDA interface.
+ */
+static int sa1100_irda_resume(struct device *_dev, u32 level)
+{
+	struct net_device *dev = dev_get_drvdata(_dev);
+	struct sa1100_irda *si;
+
+	if (!dev || level != RESUME_ENABLE)
+		return 0;
+
+	si = dev->priv;
+	if (si->open) {
+		/*
+		 * If we missed a speed change, initialise at the new speed
+		 * directly.  It is debatable whether this is actually
+		 * required, but in the interests of continuing from where
+		 * we left off it is desireable.  The converse argument is
+		 * that we should re-negotiate at 9600 baud again.
+		 */
+		if (si->newspeed) {
+			si->speed = si->newspeed;
+			si->newspeed = 0;
+		}
+
+		sa1100_irda_startup(si);
+		__sa1100_irda_set_power(si, si->power);
+		enable_irq(dev->irq);
+
+		/*
+		 * This automatically wakes up the queue
+		 */
+		netif_device_attach(dev);
+	}
+
+	return 0;
+}
+#else
+#define sa1100_irda_suspend	NULL
+#define sa1100_irda_resume	NULL
+#endif
+
+/*
+ * HP-SIR format interrupt service routines.
+ */
+static void sa1100_irda_hpsir_irq(struct net_device *dev)
+{
+	struct sa1100_irda *si = dev->priv;
+	int status;
+
+	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 stat, data;
+
+		stat = Ser2UTSR1;
+		data = Ser2UTDR;
+
+		if (stat & (UTSR1_FRE | UTSR1_ROR)) {
+			si->stats.rx_errors++;
+			if (stat & UTSR1_FRE)
+				si->stats.rx_frame_errors++;
+			if (stat & UTSR1_ROR)
+				si->stats.rx_fifo_errors++;
+		} else
+			async_unwrap_char(dev, &si->stats, &si->rx_buff, data);
+
+		status = Ser2UTSR0;
+	}
+
+	/*
+	 * We must clear certain bits.
+	 */
+	Ser2UTSR0 = status & (UTSR0_RID | UTSR0_RBB | UTSR0_REB);
+
+	if (status & UTSR0_RFS) {
+		/*
+		 * There are at least 4 bytes in the FIFO.  Read 3 bytes
+		 * and leave the rest to the block below.
+		 */
+		async_unwrap_char(dev, &si->stats, &si->rx_buff, Ser2UTDR);
+		async_unwrap_char(dev, &si->stats, &si->rx_buff, Ser2UTDR);
+		async_unwrap_char(dev, &si->stats, &si->rx_buff, Ser2UTDR);
+	}
+
+	if (status & (UTSR0_RFS | UTSR0_RID)) {
+		/*
+		 * Fifo contains more than 1 character.
+		 */
+		do {
+			async_unwrap_char(dev, &si->stats, &si->rx_buff,
+					  Ser2UTDR);
+		} while (Ser2UTSR1 & UTSR1_RNE);
+
+		dev->last_rx = jiffies;
+	}
+
+	if (status & UTSR0_TFS && si->tx_buff.len) {
+		/*
+		 * Transmitter FIFO is not full
+		 */
+		do {
+			Ser2UTDR = *si->tx_buff.data++;
+			si->tx_buff.len -= 1;
+		} while (Ser2UTSR1 & UTSR1_TNF && si->tx_buff.len);
+
+		if (si->tx_buff.len == 0) {
+			si->stats.tx_packets++;
+			si->stats.tx_bytes += si->tx_buff.data -
+					      si->tx_buff.head;
+
+			/*
+			 * We need to ensure that the transmitter has
+			 * finished.
+			 */
+			do
+				rmb();
+			while (Ser2UTSR1 & UTSR1_TBY);
+
+			/*
+			 * Ok, we've finished transmitting.  Now enable
+			 * the receiver.  Sometimes we get a receive IRQ
+			 * immediately after a transmit...
+			 */
+			Ser2UTSR0 = UTSR0_REB | UTSR0_RBB | UTSR0_RID;
+			Ser2UTCR3 = UTCR3_RIE | UTCR3_RXE | UTCR3_TXE;
+
+			if (si->newspeed) {
+				sa1100_irda_set_speed(si, si->newspeed);
+				si->newspeed = 0;
+			}
+
+			/* I'm hungry! */
+			netif_wake_queue(dev);
+		}
+	}
+}
+
+static void sa1100_irda_fir_error(struct sa1100_irda *si, struct net_device *dev)
+{
+	struct sk_buff *skb = si->rxskb;
+	dma_addr_t dma_addr;
+	unsigned int len, stat, data;
+
+	if (!skb) {
+		printk(KERN_ERR "sa1100_ir: SKB is NULL!\n");
+		return;
+	}
+
+	/*
+	 * Get the current data position.
+	 */
+	dma_addr = sa1100_get_dma_pos(si->rxdma);
+	len = dma_addr - si->rxbuf_dma;
+	if (len > HPSIR_MAX_RXLEN)
+		len = HPSIR_MAX_RXLEN;
+	dma_unmap_single(si->dev, si->rxbuf_dma, len, DMA_FROM_DEVICE);
+
+	do {
+		/*
+		 * Read Status, and then Data.
+		 */
+		stat = Ser2HSSR1;
+		rmb();
+		data = Ser2HSDR;
+
+		if (stat & (HSSR1_CRE | HSSR1_ROR)) {
+			si->stats.rx_errors++;
+			if (stat & HSSR1_CRE)
+				si->stats.rx_crc_errors++;
+			if (stat & HSSR1_ROR)
+				si->stats.rx_frame_errors++;
+		} else
+			skb->data[len++] = data;
+
+		/*
+		 * If we hit the end of frame, there's
+		 * no point in continuing.
+		 */
+		if (stat & HSSR1_EOF)
+			break;
+	} while (Ser2HSSR0 & HSSR0_EIF);
+
+	if (stat & HSSR1_EOF) {
+		si->rxskb = NULL;
+
+		skb_put(skb, len);
+		skb->dev = dev;
+		skb->mac.raw = skb->data;
+		skb->protocol = htons(ETH_P_IRDA);
+		si->stats.rx_packets++;
+		si->stats.rx_bytes += len;
+
+		/*
+		 * Before we pass the buffer up, allocate a new one.
+		 */
+		sa1100_irda_rx_alloc(si);
+
+		netif_rx(skb);
+		dev->last_rx = jiffies;
+	} else {
+		/*
+		 * Remap the buffer.
+		 */
+		si->rxbuf_dma = dma_map_single(si->dev, si->rxskb->data,
+						HPSIR_MAX_RXLEN,
+						DMA_FROM_DEVICE);
+	}
+}
+
+/*
+ * FIR format interrupt service routine.  We only have to
+ * handle RX events; transmit events go via the TX DMA handler.
+ *
+ * No matter what, we disable RX, process, and the restart RX.
+ */
+static void sa1100_irda_fir_irq(struct net_device *dev)
+{
+	struct sa1100_irda *si = dev->priv;
+
+	/*
+	 * Stop RX DMA
+	 */
+	sa1100_stop_dma(si->rxdma);
+
+	/*
+	 * Framing error - we throw away the packet completely.
+	 * Clearing RXE flushes the error conditions and data
+	 * from the fifo.
+	 */
+	if (Ser2HSSR0 & (HSSR0_FRE | HSSR0_RAB)) {
+		si->stats.rx_errors++;
+
+		if (Ser2HSSR0 & HSSR0_FRE)
+			si->stats.rx_frame_errors++;
+
+		/*
+		 * Clear out the DMA...
+		 */
+		Ser2HSCR0 = si->hscr0 | HSCR0_HSSP;
+
+		/*
+		 * Clear selected status bits now, so we
+		 * don't miss them next time around.
+		 */
+		Ser2HSSR0 = HSSR0_FRE | HSSR0_RAB;
+	}
+
+	/*
+	 * Deal with any receive errors.  The any of the lowest
+	 * 8 bytes in the FIFO may contain an error.  We must read
+	 * them one by one.  The "error" could even be the end of
+	 * packet!
+	 */
+	if (Ser2HSSR0 & HSSR0_EIF)
+		sa1100_irda_fir_error(si, dev);
+
+	/*
+	 * No matter what happens, we must restart reception.
+	 */
+	sa1100_irda_rx_dma_start(si);
+}
+
+static irqreturn_t sa1100_irda_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct net_device *dev = dev_id;
+	if (IS_FIR(((struct sa1100_irda *)dev->priv)))
+		sa1100_irda_fir_irq(dev);
+	else
+		sa1100_irda_hpsir_irq(dev);
+	return IRQ_HANDLED;
+}
+
+/*
+ * TX DMA completion handler.
+ */
+static void sa1100_irda_txdma_irq(void *id)
+{
+	struct net_device *dev = id;
+	struct sa1100_irda *si = dev->priv;
+	struct sk_buff *skb = si->txskb;
+
+	si->txskb = NULL;
+
+	/*
+	 * Wait for the transmission to complete.  Unfortunately,
+	 * the hardware doesn't give us an interrupt to indicate
+	 * "end of frame".
+	 */
+	do
+		rmb();
+	while (!(Ser2HSSR0 & HSSR0_TUR) || Ser2HSSR1 & HSSR1_TBY);
+
+	/*
+	 * Clear the transmit underrun bit.
+	 */
+	Ser2HSSR0 = HSSR0_TUR;
+
+	/*
+	 * Do we need to change speed?  Note that we're lazy
+	 * here - we don't free the old rxskb.  We don't need
+	 * to allocate a buffer either.
+	 */
+	if (si->newspeed) {
+		sa1100_irda_set_speed(si, si->newspeed);
+		si->newspeed = 0;
+	}
+
+	/*
+	 * Start reception.  This disables the transmitter for
+	 * us.  This will be using the existing RX buffer.
+	 */
+	sa1100_irda_rx_dma_start(si);
+
+	/*
+	 * Account and free the packet.
+	 */
+	if (skb) {
+		dma_unmap_single(si->dev, si->txbuf_dma, skb->len, DMA_TO_DEVICE);
+		si->stats.tx_packets ++;
+		si->stats.tx_bytes += skb->len;
+		dev_kfree_skb_irq(skb);
+	}
+
+	/*
+	 * Make sure that the TX queue is available for sending
+	 * (for retries).  TX has priority over RX at all times.
+	 */
+	netif_wake_queue(dev);
+}
+
+static int sa1100_irda_hard_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct sa1100_irda *si = dev->priv;
+	int speed = irda_get_next_speed(skb);
+
+	/*
+	 * Does this packet contain a request to change the interface
+	 * speed?  If so, remember it until we complete the transmission
+	 * of this frame.
+	 */
+	if (speed != si->speed && speed != -1)
+		si->newspeed = speed;
+
+	/*
+	 * If this is an empty frame, we can bypass a lot.
+	 */
+	if (skb->len == 0) {
+		if (si->newspeed) {
+			si->newspeed = 0;
+			sa1100_irda_set_speed(si, speed);
+		}
+		dev_kfree_skb(skb);
+		return 0;
+	}
+
+	if (!IS_FIR(si)) {
+		netif_stop_queue(dev);
+
+		si->tx_buff.data = si->tx_buff.head;
+		si->tx_buff.len  = async_wrap_skb(skb, si->tx_buff.data,
+						  si->tx_buff.truesize);
+
+		/*
+		 * Set the transmit interrupt enable.  This will fire
+		 * off an interrupt immediately.  Note that we disable
+		 * the receiver so we won't get spurious characteres
+		 * received.
+		 */
+		Ser2UTCR3 = UTCR3_TIE | UTCR3_TXE;
+
+		dev_kfree_skb(skb);
+	} else {
+		int mtt = irda_get_mtt(skb);
+
+		/*
+		 * We must not be transmitting...
+		 */
+		if (si->txskb)
+			BUG();
+
+		netif_stop_queue(dev);
+
+		si->txskb = skb;
+		si->txbuf_dma = dma_map_single(si->dev, skb->data,
+					 skb->len, DMA_TO_DEVICE);
+
+		sa1100_start_dma(si->txdma, si->txbuf_dma, skb->len);
+
+		/*
+		 * If we have a mean turn-around time, impose the specified
+		 * specified delay.  We could shorten this by timing from
+		 * the point we received the packet.
+		 */
+		if (mtt)
+			udelay(mtt);
+
+		Ser2HSCR0 = si->hscr0 | HSCR0_HSSP | HSCR0_TXE;
+	}
+
+	dev->trans_start = jiffies;
+
+	return 0;
+}
+
+static int
+sa1100_irda_ioctl(struct net_device *dev, struct ifreq *ifreq, int cmd)
+{
+	struct if_irda_req *rq = (struct if_irda_req *)ifreq;
+	struct sa1100_irda *si = dev->priv;
+	int ret = -EOPNOTSUPP;
+
+	switch (cmd) {
+	case SIOCSBANDWIDTH:
+		if (capable(CAP_NET_ADMIN)) {
+			/*
+			 * We are unable to set the speed if the
+			 * device is not running.
+			 */
+			if (si->open) {
+				ret = sa1100_irda_set_speed(si,
+						rq->ifr_baudrate);
+			} else {
+				printk("sa1100_irda_ioctl: SIOCSBANDWIDTH: !netif_running\n");
+				ret = 0;
+			}
+		}
+		break;
+
+	case SIOCSMEDIABUSY:
+		ret = -EPERM;
+		if (capable(CAP_NET_ADMIN)) {
+			irda_device_set_media_busy(dev, TRUE);
+			ret = 0;
+		}
+		break;
+
+	case SIOCGRECEIVING:
+		rq->ifr_receiving = IS_FIR(si) ? 0
+					: si->rx_buff.state != OUTSIDE_FRAME;
+		break;
+
+	default:
+		break;
+	}
+		
+	return ret;
+}
+
+static struct net_device_stats *sa1100_irda_stats(struct net_device *dev)
+{
+	struct sa1100_irda *si = dev->priv;
+	return &si->stats;
+}
+
+static int sa1100_irda_start(struct net_device *dev)
+{
+	struct sa1100_irda *si = dev->priv;
+	int err;
+
+	si->speed = 9600;
+
+	err = request_irq(dev->irq, sa1100_irda_irq, 0, dev->name, dev);
+	if (err)
+		goto err_irq;
+
+	err = sa1100_request_dma(DMA_Ser2HSSPRd, "IrDA receive",
+				 NULL, NULL, &si->rxdma);
+	if (err)
+		goto err_rx_dma;
+
+	err = sa1100_request_dma(DMA_Ser2HSSPWr, "IrDA transmit",
+				 sa1100_irda_txdma_irq, dev, &si->txdma);
+	if (err)
+		goto err_tx_dma;
+
+	/*
+	 * The interrupt must remain disabled for now.
+	 */
+	disable_irq(dev->irq);
+
+	/*
+	 * Setup the serial port for the specified speed.
+	 */
+	err = sa1100_irda_startup(si);
+	if (err)
+		goto err_startup;
+
+	/*
+	 * Open a new IrLAP layer instance.
+	 */
+	si->irlap = irlap_open(dev, &si->qos, "sa1100");
+	err = -ENOMEM;
+	if (!si->irlap)
+		goto err_irlap;
+
+	/*
+	 * Now enable the interrupt and start the queue
+	 */
+	si->open = 1;
+	sa1100_set_power(si, power_level); /* low power mode */
+	enable_irq(dev->irq);
+	netif_start_queue(dev);
+	return 0;
+
+err_irlap:
+	si->open = 0;
+	sa1100_irda_shutdown(si);
+err_startup:
+	sa1100_free_dma(si->txdma);
+err_tx_dma:
+	sa1100_free_dma(si->rxdma);
+err_rx_dma:
+	free_irq(dev->irq, dev);
+err_irq:
+	return err;
+}
+
+static int sa1100_irda_stop(struct net_device *dev)
+{
+	struct sa1100_irda *si = dev->priv;
+
+	disable_irq(dev->irq);
+	sa1100_irda_shutdown(si);
+
+	/*
+	 * If we have been doing DMA receive, make sure we
+	 * tidy that up cleanly.
+	 */
+	if (si->rxskb) {
+		dma_unmap_single(si->dev, si->rxbuf_dma, HPSIR_MAX_RXLEN,
+				 DMA_FROM_DEVICE);
+		dev_kfree_skb(si->rxskb);
+		si->rxskb = NULL;
+	}
+
+	/* Stop IrLAP */
+	if (si->irlap) {
+		irlap_close(si->irlap);
+		si->irlap = NULL;
+	}
+
+	netif_stop_queue(dev);
+	si->open = 0;
+
+	/*
+	 * Free resources
+	 */
+	sa1100_free_dma(si->txdma);
+	sa1100_free_dma(si->rxdma);
+	free_irq(dev->irq, dev);
+
+	sa1100_set_power(si, 0);
+
+	return 0;
+}
+
+static int sa1100_irda_init_iobuf(iobuff_t *io, int size)
+{
+	io->head = kmalloc(size, GFP_KERNEL | GFP_DMA);
+	if (io->head != NULL) {
+		io->truesize = size;
+		io->in_frame = FALSE;
+		io->state    = OUTSIDE_FRAME;
+		io->data     = io->head;
+	}
+	return io->head ? 0 : -ENOMEM;
+}
+
+static int sa1100_irda_probe(struct device *_dev)
+{
+	struct platform_device *pdev = to_platform_device(_dev);
+	struct net_device *dev;
+	struct sa1100_irda *si;
+	unsigned int baudrate_mask;
+	int err;
+
+	if (!pdev->dev.platform_data)
+		return -EINVAL;
+
+	err = request_mem_region(__PREG(Ser2UTCR0), 0x24, "IrDA") ? 0 : -EBUSY;
+	if (err)
+		goto err_mem_1;
+	err = request_mem_region(__PREG(Ser2HSCR0), 0x1c, "IrDA") ? 0 : -EBUSY;
+	if (err)
+		goto err_mem_2;
+	err = request_mem_region(__PREG(Ser2HSCR2), 0x04, "IrDA") ? 0 : -EBUSY;
+	if (err)
+		goto err_mem_3;
+
+	dev = alloc_irdadev(sizeof(struct sa1100_irda));
+	if (!dev)
+		goto err_mem_4;
+
+	si = dev->priv;
+	si->dev = &pdev->dev;
+	si->pdata = pdev->dev.platform_data;
+
+	/*
+	 * Initialise the HP-SIR buffers
+	 */
+	err = sa1100_irda_init_iobuf(&si->rx_buff, 14384);
+	if (err)
+		goto err_mem_5;
+	err = sa1100_irda_init_iobuf(&si->tx_buff, 4000);
+	if (err)
+		goto err_mem_5;
+
+	dev->hard_start_xmit	= sa1100_irda_hard_xmit;
+	dev->open		= sa1100_irda_start;
+	dev->stop		= sa1100_irda_stop;
+	dev->do_ioctl		= sa1100_irda_ioctl;
+	dev->get_stats		= sa1100_irda_stats;
+	dev->irq		= IRQ_Ser2ICP;
+
+	irda_init_max_qos_capabilies(&si->qos);
+
+	/*
+	 * We support original IRDA up to 115k2. (we don't currently
+	 * support 4Mbps).  Min Turn Time set to 1ms or greater.
+	 */
+	baudrate_mask = IR_9600;
+
+	switch (max_rate) {
+	case 4000000:		baudrate_mask |= IR_4000000 << 8;
+	case 115200:		baudrate_mask |= IR_115200;
+	case 57600:		baudrate_mask |= IR_57600;
+	case 38400:		baudrate_mask |= IR_38400;
+	case 19200:		baudrate_mask |= IR_19200;
+	}
+		
+	si->qos.baud_rate.bits &= baudrate_mask;
+	si->qos.min_turn_time.bits = 7;
+
+	irda_qos_bits_to_value(&si->qos);
+
+	si->utcr4 = UTCR4_HPSIR;
+	if (tx_lpm)
+		si->utcr4 |= UTCR4_Z1_6us;
+
+	/*
+	 * Initially enable HP-SIR modulation, and ensure that the port
+	 * is disabled.
+	 */
+	Ser2UTCR3 = 0;
+	Ser2UTCR4 = si->utcr4;
+	Ser2HSCR0 = HSCR0_UART;
+
+	err = register_netdev(dev);
+	if (err == 0)
+		dev_set_drvdata(&pdev->dev, dev);
+
+	if (err) {
+ err_mem_5:
+		kfree(si->tx_buff.head);
+		kfree(si->rx_buff.head);
+		free_netdev(dev);
+ err_mem_4:
+		release_mem_region(__PREG(Ser2HSCR2), 0x04);
+ err_mem_3:
+		release_mem_region(__PREG(Ser2HSCR0), 0x1c);
+ err_mem_2:
+		release_mem_region(__PREG(Ser2UTCR0), 0x24);
+	}
+ err_mem_1:
+	return err;
+}
+
+static int sa1100_irda_remove(struct device *_dev)
+{
+	struct net_device *dev = dev_get_drvdata(_dev);
+
+	if (dev) {
+		struct sa1100_irda *si = dev->priv;
+		unregister_netdev(dev);
+		kfree(si->tx_buff.head);
+		kfree(si->rx_buff.head);
+		free_netdev(dev);
+	}
+
+	release_mem_region(__PREG(Ser2HSCR2), 0x04);
+	release_mem_region(__PREG(Ser2HSCR0), 0x1c);
+	release_mem_region(__PREG(Ser2UTCR0), 0x24);
+
+	return 0;
+}
+
+static struct device_driver sa1100ir_driver = {
+	.name		= "sa11x0-ir",
+	.bus		= &platform_bus_type,
+	.probe		= sa1100_irda_probe,
+	.remove		= sa1100_irda_remove,
+	.suspend	= sa1100_irda_suspend,
+	.resume		= sa1100_irda_resume,
+};
+
+static int __init sa1100_irda_init(void)
+{
+	/*
+	 * Limit power level a sensible range.
+	 */
+	if (power_level < 1)
+		power_level = 1;
+	if (power_level > 3)
+		power_level = 3;
+
+	return driver_register(&sa1100ir_driver);
+}
+
+static void __exit sa1100_irda_exit(void)
+{
+	driver_unregister(&sa1100ir_driver);
+}
+
+module_init(sa1100_irda_init);
+module_exit(sa1100_irda_exit);
+module_param(power_level, int, 0);
+module_param(tx_lpm, int, 0);
+module_param(max_rate, int, 0);
+
+MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
+MODULE_DESCRIPTION("StrongARM SA1100 IrDA driver");
+MODULE_LICENSE("GPL");
+MODULE_PARM_DESC(power_level, "IrDA power level, 1 (low) to 3 (high)");
+MODULE_PARM_DESC(tx_lpm, "Enable transmitter low power (1.6us) mode");
+MODULE_PARM_DESC(max_rate, "Maximum baud rate (4000000, 115200, 57600, 38400, 19200, 9600)");
diff --git a/drivers/net/irda/sir-dev.h b/drivers/net/irda/sir-dev.h
new file mode 100644
index 0000000..f0b8bc3
--- /dev/null
+++ b/drivers/net/irda/sir-dev.h
@@ -0,0 +1,202 @@
+/*********************************************************************
+ *
+ *	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);
+
+/* low level helpers for SIR device/dongle setup */
+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 int sirdev_set_dtr_rts(struct sir_dev *dev, int dtr, int rts);
+
+/* not exported */
+
+extern int sirdev_get_dongle(struct sir_dev *self, IRDA_DONGLE type);
+extern int sirdev_put_dongle(struct sir_dev *self);
+
+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;
+	int raw_tx;
+	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;
+
+};
+
+#endif	/* IRDA_SIR_H */
diff --git a/drivers/net/irda/sir_core.c b/drivers/net/irda/sir_core.c
new file mode 100644
index 0000000..a49f910
--- /dev/null
+++ b/drivers/net/irda/sir_core.c
@@ -0,0 +1,56 @@
+/*********************************************************************
+ *
+ *	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);
+
+EXPORT_SYMBOL(sirdev_raw_write);
+EXPORT_SYMBOL(sirdev_raw_read);
+EXPORT_SYMBOL(sirdev_set_dtr_rts);
+
+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 --git a/drivers/net/irda/sir_dev.c b/drivers/net/irda/sir_dev.c
new file mode 100644
index 0000000..efc5a88
--- /dev/null
+++ b/drivers/net/irda/sir_dev.c
@@ -0,0 +1,677 @@
+/*********************************************************************
+ *
+ *	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 <linux/delay.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->rx_buff.len = 0;
+	dev->rx_buff.in_frame = FALSE;
+	dev->rx_buff.state = OUTSIDE_FRAME;
+	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)
+{
+	unsigned long flags;
+	int ret;
+
+	if (unlikely(len > dev->tx_buff.truesize))
+		return -ENOSPC;
+
+	spin_lock_irqsave(&dev->tx_lock, flags);	/* serialize with other tx operations */
+	while (dev->tx_buff.len > 0) {			/* wait until tx idle */
+		spin_unlock_irqrestore(&dev->tx_lock, flags);
+		msleep(10);
+		spin_lock_irqsave(&dev->tx_lock, flags);
+	}
+
+	dev->tx_buff.data = dev->tx_buff.head;
+	memcpy(dev->tx_buff.data, buf, len);	
+	dev->tx_buff.len = len;
+
+	ret = dev->drv->do_write(dev, dev->tx_buff.data, dev->tx_buff.len);
+	if (ret > 0) {
+		IRDA_DEBUG(3, "%s(), raw-tx started\n", __FUNCTION__);
+
+		dev->tx_buff.data += ret;
+		dev->tx_buff.len -= ret;
+		dev->raw_tx = 1;
+		ret = len;		/* all data is going to be sent */
+	}
+	spin_unlock_irqrestore(&dev->tx_lock, flags);
+	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.data, count);
+		dev->rx_buff.data += count;
+		dev->rx_buff.len -= count;
+	}
+
+	/* remaining stuff gets flushed when re-enabling normal rx */
+
+	return count;
+}
+
+int sirdev_set_dtr_rts(struct sir_dev *dev, int dtr, int rts)
+{
+	int ret = -ENXIO;
+	if (dev->drv->set_dtr_rts != 0)
+		ret =  dev->drv->set_dtr_rts(dev, dtr, rts);
+	return ret;
+}
+	
+/**********************************************************************/
+
+/* 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)
+{
+	unsigned long flags;
+	struct sk_buff *skb;
+	int actual = 0;
+	int err;
+	
+	spin_lock_irqsave(&dev->tx_lock, flags);
+
+	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 */
+			IRDA_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)
+			goto done;	/* more data to send later */
+	}
+
+	if (unlikely(dev->raw_tx != 0)) {
+		/* in raw mode we are just done now after the buffer was sent
+		 * completely. Since this was requested by some dongle driver
+		 * running under the control of the irda-thread we must take
+		 * care here not to re-enable the queue. The queue will be
+		 * restarted when the irda-thread has completed the request.
+		 */
+
+		IRDA_DEBUG(3, "%s(), raw-tx done\n", __FUNCTION__);
+		dev->raw_tx = 0;
+		goto done;	/* no post-frame handling in raw mode */
+	}
+
+	/* 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
+			 */
+			IRDA_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);
+	}
+
+done:
+	spin_unlock_irqrestore(&dev->tx_lock, flags);
+}
+
+/* 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_WARNING("%s(), not ready yet!\n", __FUNCTION__);
+		return -1;
+	}
+
+	if (!dev->irlap) {
+		IRDA_WARNING("%s - too early: %p / %zd!\n",
+			     __FUNCTION__, cp, count);
+		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++;
+		IRDA_DEBUG(0, "%s; rx-drop: %zd\n", __FUNCTION__, count);
+		return 0;
+	}
+
+	/* Read the characters into the buffer */
+	if (likely(atomic_read(&dev->enable_rx))) {
+		while (count--)
+			/* Unwrap and destuff one byte */
+			async_unwrap_char(dev->netdev, &dev->stats, 
+					  &dev->rx_buff, *cp++);
+	} else {
+		while (count--) {
+			/* 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;
+	unsigned long flags;
+	int actual = 0;
+	int err;
+	s32 speed;
+
+	IRDA_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_irqsave(&dev->tx_lock, flags);
+
+        /* 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 */
+		IRDA_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_irqrestore(&dev->tx_lock, flags);
+
+	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;
+
+	IRDA_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->tx_buff.truesize = SIRBUF_ALLOCSIZE;
+	dev->rx_buff.truesize = IRDA_SKB_MAX_MTU; 
+
+	/* Bootstrap ZeroCopy Rx */
+	dev->rx_buff.skb = __dev_alloc_skb(dev->rx_buff.truesize, GFP_KERNEL);
+	if (dev->rx_buff.skb == NULL)
+		return -ENOMEM;
+	skb_reserve(dev->rx_buff.skb, 1);
+	dev->rx_buff.head = dev->rx_buff.skb->data;
+
+	dev->tx_buff.head = kmalloc(dev->tx_buff.truesize, GFP_KERNEL);
+	if (dev->tx_buff.head == NULL) {
+		kfree_skb(dev->rx_buff.skb);
+		dev->rx_buff.skb = NULL;
+		dev->rx_buff.head = NULL;
+		return -ENOMEM;
+	}
+
+	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.skb)
+		kfree_skb(dev->rx_buff.skb);
+	if (dev->tx_buff.head)
+		kfree(dev->tx_buff.head);
+	dev->rx_buff.head = dev->tx_buff.head = NULL;
+	dev->rx_buff.skb = 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;
+
+	/* increase the reference count of the driver module before doing serious stuff */
+	if (!try_module_get(drv->owner))
+		return -ESTALE;
+
+	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);
+	dev->raw_tx = 0;
+
+	netif_start_queue(ndev);
+	dev->irlap = irlap_open(ndev, &dev->qos, dev->hwname);
+	if (!dev->irlap)
+		goto errout_stop;
+
+	netif_wake_queue(ndev);
+
+	IRDA_DEBUG(2, "%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:
+	module_put(drv->owner);
+	return -EAGAIN;
+}
+
+static int sirdev_close(struct net_device *ndev)
+{
+	struct sir_dev *dev = ndev->priv;
+	const struct sir_driver *drv;
+
+//	IRDA_DEBUG(0, "%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);
+	module_put(drv->owner);
+
+out:
+	dev->speed = 0;
+	up(&dev->fsm.sem);
+	return 0;
+}
+
+/* ----------------------------------------------------------------------------- */
+
+struct sir_dev * sirdev_get_instance(const struct sir_driver *drv, const char *name)
+{
+	struct net_device *ndev;
+	struct sir_dev *dev;
+
+	IRDA_DEBUG(0, "%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 don't implement do_write.
+	 */
+	if (!drv ||  !drv->do_write)
+		return NULL;
+
+	/*
+	 *  Allocate new instance of the device
+	 */
+	ndev = alloc_irdadev(sizeof(*dev));
+	if (ndev == NULL) {
+		IRDA_ERROR("%s - Can't allocate memory for IrDA control block!\n", __FUNCTION__);
+		goto out;
+	}
+	dev = ndev->priv;
+
+	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);
+
+	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;
+
+	SET_MODULE_OWNER(ndev);
+
+	/* 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;
+
+	if (register_netdev(ndev)) {
+		IRDA_ERROR("%s(), register_netdev() failed!\n", __FUNCTION__);
+		goto out_freenetdev;
+	}
+
+	return dev;
+
+out_freenetdev:
+	free_netdev(ndev);
+out:
+	return NULL;
+}
+
+int sirdev_put_instance(struct sir_dev *dev)
+{
+	int err = 0;
+
+	IRDA_DEBUG(0, "%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)
+		IRDA_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 */
+	unregister_netdev(dev->netdev);
+
+	free_netdev(dev->netdev);
+
+	return 0;
+}
+
diff --git a/drivers/net/irda/sir_dongle.c b/drivers/net/irda/sir_dongle.c
new file mode 100644
index 0000000..c5b7674
--- /dev/null
+++ b/drivers/net/irda/sir_dongle.c
@@ -0,0 +1,134 @@
+/*********************************************************************
+ *
+ *	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
+	request_module("irda-dongle-%d", type);
+#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_module_get() below will fail
+	 * 3) the try_module_get() below succeeds before the module is marked
+	 *	deleted - then sys_delete_module() fails and prevents the removal
+	 *	because the module is in use.
+	 */
+
+	if (!try_module_get(drv->owner)) {
+		err = -ESTALE;
+		goto out_unlock;	/* rmmod already pending */
+	}
+	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;
+	module_put(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 */
+		module_put(drv->owner);/* decrement driver's module refcount */
+	}
+
+	return 0;
+}
diff --git a/drivers/net/irda/sir_kthread.c b/drivers/net/irda/sir_kthread.c
new file mode 100644
index 0000000..18cea10
--- /dev/null
+++ b/drivers/net/irda/sir_kthread.c
@@ -0,0 +1,502 @@
+/*********************************************************************
+ *
+ *	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 <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_thread(void *startup)
+{
+	DECLARE_WAITQUEUE(wait, current);
+
+	daemonize("kIrDAd");
+
+	irda_rq_queue.thread = current;
+
+	complete((struct completion *)startup);
+
+	while (irda_rq_queue.thread != NULL) {
+
+		/* We use TASK_INTERRUPTIBLE, rather than
+		 * TASK_UNINTERRUPTIBLE.  Andrew Morton made this
+		 * change ; he told me that it is safe, because "signal
+		 * blocking is now handled in daemonize()", he added
+		 * that the problem is that "uninterruptible sleep
+		 * contributes to load average", making user worry.
+		 * Jean II */
+		set_task_state(current, TASK_INTERRUPTIBLE);
+		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);
+
+		/* make swsusp happy with our thread */
+		if (current->flags & PF_FREEZE)
+			refrigerator(PF_FREEZE);
+
+		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:
+			IRDA_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:
+			ret = sirdev_set_dtr_rts(dev,
+				(fsm->param&0x02) ? TRUE : FALSE,
+				(fsm->param&0x01) ? TRUE : FALSE);
+			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:
+			IRDA_ERROR("%s - undefined state\n", __FUNCTION__);
+			fsm->result = -EINVAL;
+			/* fall thru */
+
+		case SIRDEV_STATE_ERROR:
+			IRDA_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);
+			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);
+
+	if (down_trylock(&fsm->sem)) {
+		if (in_interrupt()  ||  in_atomic()  ||  irqs_disabled()) {
+			IRDA_DEBUG(1, "%s(), state machine busy!\n", __FUNCTION__);
+			return -EWOULDBLOCK;
+		} else
+			down(&fsm->sem);
+	}
+
+	if (fsm->state == SIRDEV_STATE_DEAD) {
+		/* race with sirdev_close should never happen */
+		IRDA_ERROR("%s(), instance staled!\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);		
+		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 --git a/drivers/net/irda/smsc-ircc2.c b/drivers/net/irda/smsc-ircc2.c
new file mode 100644
index 0000000..10125a1
--- /dev/null
+++ b/drivers/net/irda/smsc-ircc2.c
@@ -0,0 +1,2396 @@
+/*********************************************************************
+ * $Id: smsc-ircc2.c,v 1.19.2.5 2002/10/27 11:34:26 dip Exp $
+ *
+ * Description:   Driver for the SMC Infrared Communications Controller
+ * Status:        Experimental.
+ * Author:        Daniele Peri (peri@csai.unipa.it)
+ * Created at:    
+ * Modified at:   
+ * Modified by:   
+ * 
+ *     Copyright (c) 2002      Daniele Peri
+ *     All Rights Reserved.
+ *     Copyright (c) 2002      Jean Tourrilhes
+ *
+ *
+ * Based on smc-ircc.c:
+ *
+ *     Copyright (c) 2001      Stefani Seibold
+ *     Copyright (c) 1999-2001 Dag Brattli
+ *     Copyright (c) 1998-1999 Thomas Davis, 
+ *
+ *	and irport.c:
+ *
+ *     Copyright (c) 1997, 1998, 1999-2000 Dag Brattli, 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/kernel.h>
+#include <linux/types.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/rtnetlink.h>
+#include <linux/serial_reg.h>
+#include <linux/dma-mapping.h>
+
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/byteorder.h>
+
+#include <linux/spinlock.h>
+#include <linux/pm.h>
+
+#include <net/irda/wrapper.h>
+#include <net/irda/irda.h>
+#include <net/irda/irda_device.h>
+
+#include "smsc-ircc2.h"
+#include "smsc-sio.h"
+
+/* Types */
+
+struct smsc_transceiver {
+	char *name;
+	void (*set_for_speed)(int fir_base, u32 speed);	
+	int  (*probe)(int fir_base);
+};
+typedef struct smsc_transceiver smsc_transceiver_t;
+
+#if 0
+struct smc_chip {
+	char *name;
+	u16 flags;
+	u8 devid;
+	u8 rev;
+};
+typedef struct smc_chip smc_chip_t;
+#endif
+
+struct smsc_chip {
+	char *name;
+	#if 0
+	u8	type;
+	#endif
+	u16 flags;
+	u8 devid;
+	u8 rev;
+};
+typedef struct smsc_chip smsc_chip_t;
+
+struct smsc_chip_address {
+	unsigned int cfg_base;
+	unsigned int type;
+};
+typedef struct smsc_chip_address smsc_chip_address_t;
+
+/* Private data for each instance */
+struct smsc_ircc_cb {
+	struct net_device *netdev;     /* Yes! we are some kind of netdevice */
+	struct net_device_stats stats;
+	struct irlap_cb    *irlap; /* The link layer we are binded to */
+	
+	chipio_t io;               /* IrDA controller information */
+	iobuff_t tx_buff;          /* Transmit buffer */
+	iobuff_t rx_buff;          /* Receive buffer */
+	dma_addr_t tx_buff_dma;
+	dma_addr_t rx_buff_dma;
+
+	struct qos_info qos;       /* QoS capabilities for this device */
+
+	spinlock_t lock;           /* For serializing operations */
+	
+	__u32 new_speed;
+	__u32 flags;               /* Interface flags */
+
+	int tx_buff_offsets[10];   /* Offsets between frames in tx_buff */
+	int tx_len;                /* Number of frames in tx_buff */
+
+	int transceiver;
+	struct pm_dev *pmdev;
+};
+
+/* Constants */
+
+static const char *driver_name = "smsc-ircc2";
+#define	DIM(x)	(sizeof(x)/(sizeof(*(x))))
+#define SMSC_IRCC2_C_IRDA_FALLBACK_SPEED	9600
+#define SMSC_IRCC2_C_DEFAULT_TRANSCEIVER	1
+#define SMSC_IRCC2_C_NET_TIMEOUT			0
+#define SMSC_IRCC2_C_SIR_STOP			0
+
+/* Prototypes */
+
+static int smsc_ircc_open(unsigned int firbase, unsigned int sirbase, u8 dma, u8 irq);
+static int smsc_ircc_present(unsigned int fir_base, unsigned int sir_base);
+static void smsc_ircc_setup_io(struct smsc_ircc_cb *self, unsigned int fir_base, unsigned int sir_base, u8 dma, u8 irq);
+static void smsc_ircc_setup_qos(struct smsc_ircc_cb *self);
+static void smsc_ircc_init_chip(struct smsc_ircc_cb *self);
+static int __exit smsc_ircc_close(struct smsc_ircc_cb *self);
+static int  smsc_ircc_dma_receive(struct smsc_ircc_cb *self, int iobase); 
+static void smsc_ircc_dma_receive_complete(struct smsc_ircc_cb *self, int iobase);
+static void smsc_ircc_sir_receive(struct smsc_ircc_cb *self);
+static int  smsc_ircc_hard_xmit_sir(struct sk_buff *skb, struct net_device *dev);
+static int  smsc_ircc_hard_xmit_fir(struct sk_buff *skb, struct net_device *dev);
+static void smsc_ircc_dma_xmit(struct smsc_ircc_cb *self, int iobase, int bofs);
+static void smsc_ircc_dma_xmit_complete(struct smsc_ircc_cb *self, int iobase);
+static void smsc_ircc_change_speed(void *priv, u32 speed);
+static void smsc_ircc_set_sir_speed(void *priv, u32 speed);
+static irqreturn_t smsc_ircc_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static irqreturn_t smsc_ircc_interrupt_sir(struct net_device *dev);
+static void smsc_ircc_sir_start(struct smsc_ircc_cb *self);
+#if SMSC_IRCC2_C_SIR_STOP
+static void smsc_ircc_sir_stop(struct smsc_ircc_cb *self);
+#endif
+static void smsc_ircc_sir_write_wakeup(struct smsc_ircc_cb *self);
+static int  smsc_ircc_sir_write(int iobase, int fifo_size, __u8 *buf, int len);
+static int  smsc_ircc_net_open(struct net_device *dev);
+static int  smsc_ircc_net_close(struct net_device *dev);
+static int  smsc_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+#if SMSC_IRCC2_C_NET_TIMEOUT
+static void smsc_ircc_timeout(struct net_device *dev);
+#endif
+static struct net_device_stats *smsc_ircc_net_get_stats(struct net_device *dev);
+static int  smsc_ircc_pmproc(struct pm_dev *dev, pm_request_t rqst, void *data);
+static int smsc_ircc_is_receiving(struct smsc_ircc_cb *self);
+static void smsc_ircc_probe_transceiver(struct smsc_ircc_cb *self);
+static void smsc_ircc_set_transceiver_for_speed(struct smsc_ircc_cb *self, u32 speed);
+static void smsc_ircc_sir_wait_hw_transmitter_finish(struct smsc_ircc_cb *self);
+
+/* Probing */
+static int __init smsc_ircc_look_for_chips(void);
+static const smsc_chip_t * __init smsc_ircc_probe(unsigned short cfg_base,u8 reg,const smsc_chip_t *chip,char *type);
+static int __init smsc_superio_flat(const smsc_chip_t *chips, unsigned short cfg_base, char *type);
+static int __init smsc_superio_paged(const smsc_chip_t *chips, unsigned short cfg_base, char *type);
+static int __init smsc_superio_fdc(unsigned short cfg_base);
+static int __init smsc_superio_lpc(unsigned short cfg_base);
+
+/* Transceivers specific functions */
+
+static void smsc_ircc_set_transceiver_toshiba_sat1800(int fir_base, u32 speed);
+static int  smsc_ircc_probe_transceiver_toshiba_sat1800(int fir_base);
+static void smsc_ircc_set_transceiver_smsc_ircc_fast_pin_select(int fir_base, u32 speed);
+static int  smsc_ircc_probe_transceiver_smsc_ircc_fast_pin_select(int fir_base);
+static void smsc_ircc_set_transceiver_smsc_ircc_atc(int fir_base, u32 speed);
+static int  smsc_ircc_probe_transceiver_smsc_ircc_atc(int fir_base);
+
+/* Power Management */
+
+static void smsc_ircc_suspend(struct smsc_ircc_cb *self);
+static void smsc_ircc_wakeup(struct smsc_ircc_cb *self);
+static int smsc_ircc_pmproc(struct pm_dev *dev, pm_request_t rqst, void *data);
+
+
+/* Transceivers for SMSC-ircc */
+
+static smsc_transceiver_t smsc_transceivers[]=
+{
+	{ "Toshiba Satellite 1800 (GP data pin select)", smsc_ircc_set_transceiver_toshiba_sat1800, smsc_ircc_probe_transceiver_toshiba_sat1800},
+	{ "Fast pin select", smsc_ircc_set_transceiver_smsc_ircc_fast_pin_select, smsc_ircc_probe_transceiver_smsc_ircc_fast_pin_select},
+	{ "ATC IRMode", smsc_ircc_set_transceiver_smsc_ircc_atc, smsc_ircc_probe_transceiver_smsc_ircc_atc},
+	{ NULL, NULL}
+};
+#define SMSC_IRCC2_C_NUMBER_OF_TRANSCEIVERS (DIM(smsc_transceivers)-1)
+
+/*  SMC SuperIO chipsets definitions */
+
+#define	KEY55_1	0	/* SuperIO Configuration mode with Key <0x55> */
+#define	KEY55_2	1	/* SuperIO Configuration mode with Key <0x55,0x55> */
+#define	NoIRDA	2	/* SuperIO Chip has no IRDA Port */
+#define	SIR	0	/* SuperIO Chip has only slow IRDA */
+#define	FIR	4	/* SuperIO Chip has fast IRDA */
+#define	SERx4	8	/* SuperIO Chip supports 115,2 KBaud * 4=460,8 KBaud */
+
+static smsc_chip_t __initdata fdc_chips_flat[]=
+{
+	/* Base address 0x3f0 or 0x370 */
+	{ "37C44",	KEY55_1|NoIRDA,		0x00, 0x00 }, /* This chip cannot be detected */
+	{ "37C665GT",	KEY55_2|NoIRDA,		0x65, 0x01 },
+	{ "37C665GT",	KEY55_2|NoIRDA,		0x66, 0x01 },
+	{ "37C669",	KEY55_2|SIR|SERx4,	0x03, 0x02 },
+	{ "37C669",	KEY55_2|SIR|SERx4,	0x04, 0x02 }, /* ID? */
+	{ "37C78",	KEY55_2|NoIRDA,		0x78, 0x00 },
+	{ "37N769",	KEY55_1|FIR|SERx4,	0x28, 0x00 },
+	{ "37N869",	KEY55_1|FIR|SERx4,	0x29, 0x00 },
+	{ NULL }
+};
+
+static smsc_chip_t __initdata fdc_chips_paged[]=
+{
+	/* Base address 0x3f0 or 0x370 */
+	{ "37B72X",	KEY55_1|SIR|SERx4,	0x4c, 0x00 },
+	{ "37B77X",	KEY55_1|SIR|SERx4,	0x43, 0x00 },
+	{ "37B78X",	KEY55_1|SIR|SERx4,	0x44, 0x00 },
+	{ "37B80X",	KEY55_1|SIR|SERx4,	0x42, 0x00 },
+	{ "37C67X",	KEY55_1|FIR|SERx4,	0x40, 0x00 },
+	{ "37C93X",	KEY55_2|SIR|SERx4,	0x02, 0x01 },
+	{ "37C93XAPM",	KEY55_1|SIR|SERx4,	0x30, 0x01 },
+	{ "37C93XFR",	KEY55_2|FIR|SERx4,	0x03, 0x01 },
+	{ "37M707",	KEY55_1|SIR|SERx4,	0x42, 0x00 },
+	{ "37M81X",	KEY55_1|SIR|SERx4,	0x4d, 0x00 },
+	{ "37N958FR",	KEY55_1|FIR|SERx4,	0x09, 0x04 },
+	{ "37N971",	KEY55_1|FIR|SERx4,	0x0a, 0x00 },
+	{ "37N972",	KEY55_1|FIR|SERx4,	0x0b, 0x00 },
+	{ NULL }
+};
+
+static smsc_chip_t __initdata lpc_chips_flat[]=
+{
+	/* Base address 0x2E or 0x4E */
+	{ "47N227",	KEY55_1|FIR|SERx4,	0x5a, 0x00 },
+	{ "47N267",	KEY55_1|FIR|SERx4,	0x5e, 0x00 },
+	{ NULL }
+};
+
+static smsc_chip_t __initdata lpc_chips_paged[]=
+{
+	/* Base address 0x2E or 0x4E */
+	{ "47B27X",	KEY55_1|SIR|SERx4,	0x51, 0x00 },
+	{ "47B37X",	KEY55_1|SIR|SERx4,	0x52, 0x00 },
+	{ "47M10X",	KEY55_1|SIR|SERx4,	0x59, 0x00 },
+	{ "47M120",	KEY55_1|NoIRDA|SERx4,	0x5c, 0x00 },
+	{ "47M13X",	KEY55_1|SIR|SERx4,	0x59, 0x00 },
+	{ "47M14X",	KEY55_1|SIR|SERx4,	0x5f, 0x00 },
+	{ "47N252",	KEY55_1|FIR|SERx4,	0x0e, 0x00 },
+	{ "47S42X",	KEY55_1|SIR|SERx4,	0x57, 0x00 },
+	{ NULL }
+};
+
+#define SMSCSIO_TYPE_FDC	1
+#define SMSCSIO_TYPE_LPC	2
+#define SMSCSIO_TYPE_FLAT	4
+#define SMSCSIO_TYPE_PAGED	8
+
+static smsc_chip_address_t __initdata possible_addresses[]=
+{
+	{0x3f0, SMSCSIO_TYPE_FDC|SMSCSIO_TYPE_FLAT|SMSCSIO_TYPE_PAGED},
+	{0x370, SMSCSIO_TYPE_FDC|SMSCSIO_TYPE_FLAT|SMSCSIO_TYPE_PAGED},
+	{0xe0, SMSCSIO_TYPE_FDC|SMSCSIO_TYPE_FLAT|SMSCSIO_TYPE_PAGED},
+	{0x2e, SMSCSIO_TYPE_LPC|SMSCSIO_TYPE_FLAT|SMSCSIO_TYPE_PAGED},
+	{0x4e, SMSCSIO_TYPE_LPC|SMSCSIO_TYPE_FLAT|SMSCSIO_TYPE_PAGED},
+	{0,0}
+};
+
+/* Globals */
+
+static struct smsc_ircc_cb *dev_self[] = { NULL, NULL};
+
+static int ircc_irq=255;
+static int ircc_dma=255;
+static int ircc_fir=0;
+static int ircc_sir=0;
+static int ircc_cfg=0;
+static int ircc_transceiver=0;
+
+static unsigned short	dev_count=0;
+
+static inline void register_bank(int iobase, int bank)
+{
+        outb(((inb(iobase+IRCC_MASTER) & 0xf0) | (bank & 0x07)),
+               iobase+IRCC_MASTER);
+}
+
+
+/*******************************************************************************
+ *
+ *
+ * SMSC-ircc stuff
+ *
+ *
+ *******************************************************************************/
+
+/*
+ * Function smsc_ircc_init ()
+ *
+ *    Initialize chip. Just try to find out how many chips we are dealing with
+ *    and where they are
+ */
+static int __init smsc_ircc_init(void)
+{
+	int ret=-ENODEV;
+
+	IRDA_DEBUG(1, "%s\n", __FUNCTION__);
+
+	dev_count=0;
+ 
+	if ((ircc_fir>0)&&(ircc_sir>0)) {
+		IRDA_MESSAGE(" Overriding FIR address 0x%04x\n", ircc_fir);
+		IRDA_MESSAGE(" Overriding SIR address 0x%04x\n", ircc_sir);
+
+		if (smsc_ircc_open(ircc_fir, ircc_sir, ircc_dma, ircc_irq) == 0)
+			return 0;
+
+		return -ENODEV;
+	}
+
+	/* try user provided configuration register base address */
+	if (ircc_cfg>0) {
+	        IRDA_MESSAGE(" Overriding configuration address 0x%04x\n",
+			     ircc_cfg);
+		if (!smsc_superio_fdc(ircc_cfg))
+			ret = 0;
+		if (!smsc_superio_lpc(ircc_cfg))
+			ret = 0;
+	}
+	
+	if(smsc_ircc_look_for_chips()>0) ret = 0;
+	
+	return ret;
+}
+
+/*
+ * Function smsc_ircc_open (firbase, sirbase, dma, irq)
+ *
+ *    Try to open driver instance
+ *
+ */
+static int __init smsc_ircc_open(unsigned int fir_base, unsigned int sir_base, u8 dma, u8 irq)
+{
+	struct smsc_ircc_cb *self;
+	struct net_device *dev;
+	int err;
+	
+	IRDA_DEBUG(1, "%s\n", __FUNCTION__);
+
+	err = smsc_ircc_present(fir_base, sir_base);
+	if(err) 
+		goto err_out;
+		
+	err = -ENOMEM;
+	if (dev_count > DIM(dev_self)) {
+	        IRDA_WARNING("%s(), too many devices!\n", __FUNCTION__);
+		goto err_out1;
+	}
+
+	/*
+	 *  Allocate new instance of the driver
+	 */
+	dev = alloc_irdadev(sizeof(struct smsc_ircc_cb));
+	if (!dev) {
+		IRDA_WARNING("%s() can't allocate net device\n", __FUNCTION__);
+		goto err_out1;
+	}
+
+	SET_MODULE_OWNER(dev);
+
+	dev->hard_start_xmit = smsc_ircc_hard_xmit_sir;
+#if SMSC_IRCC2_C_NET_TIMEOUT
+	dev->tx_timeout	     = smsc_ircc_timeout;
+	dev->watchdog_timeo  = HZ*2;  /* Allow enough time for speed change */
+#endif
+	dev->open            = smsc_ircc_net_open;
+	dev->stop            = smsc_ircc_net_close;
+	dev->do_ioctl        = smsc_ircc_net_ioctl;
+	dev->get_stats	     = smsc_ircc_net_get_stats;
+	
+	self = dev->priv;
+	self->netdev = dev;
+
+	/* Make ifconfig display some details */
+	dev->base_addr = self->io.fir_base = fir_base;
+	dev->irq = self->io.irq = irq;
+
+	/* Need to store self somewhere */
+	dev_self[dev_count++] = self;
+	spin_lock_init(&self->lock);
+
+	self->rx_buff.truesize = SMSC_IRCC2_RX_BUFF_TRUESIZE; 
+	self->tx_buff.truesize = SMSC_IRCC2_TX_BUFF_TRUESIZE;
+
+	self->rx_buff.head =
+		dma_alloc_coherent(NULL, self->rx_buff.truesize,
+				   &self->rx_buff_dma, GFP_KERNEL);
+	if (self->rx_buff.head == NULL) {
+		IRDA_ERROR("%s, Can't allocate memory for receive buffer!\n",
+			   driver_name);
+		goto err_out2;
+	}
+
+	self->tx_buff.head =
+		dma_alloc_coherent(NULL, self->tx_buff.truesize,
+				   &self->tx_buff_dma, GFP_KERNEL);
+	if (self->tx_buff.head == NULL) {
+		IRDA_ERROR("%s, Can't allocate memory for transmit buffer!\n",
+			   driver_name);
+		goto err_out3;
+	}
+
+	memset(self->rx_buff.head, 0, self->rx_buff.truesize);
+	memset(self->tx_buff.head, 0, self->tx_buff.truesize);
+
+	self->rx_buff.in_frame = FALSE;
+	self->rx_buff.state = OUTSIDE_FRAME;
+	self->tx_buff.data = self->tx_buff.head;
+	self->rx_buff.data = self->rx_buff.head;
+	   
+	smsc_ircc_setup_io(self, fir_base, sir_base, dma, irq);
+
+	smsc_ircc_setup_qos(self);
+
+	smsc_ircc_init_chip(self);
+	
+	if(ircc_transceiver > 0  && 
+	   ircc_transceiver < SMSC_IRCC2_C_NUMBER_OF_TRANSCEIVERS)
+		self->transceiver = ircc_transceiver;
+	else
+		smsc_ircc_probe_transceiver(self);
+
+	err = register_netdev(self->netdev);
+	if(err) {
+		IRDA_ERROR("%s, Network device registration failed!\n",
+			   driver_name);
+		goto err_out4;
+	}
+
+	self->pmdev = pm_register(PM_SYS_DEV, PM_SYS_IRDA, smsc_ircc_pmproc);
+	if (self->pmdev)
+		self->pmdev->data = self;
+
+	IRDA_MESSAGE("IrDA: Registered device %s\n", dev->name);
+
+	return 0;
+ err_out4:
+	dma_free_coherent(NULL, self->tx_buff.truesize,
+			  self->tx_buff.head, self->tx_buff_dma);
+ err_out3:
+	dma_free_coherent(NULL, self->rx_buff.truesize,
+			  self->rx_buff.head, self->rx_buff_dma);
+ err_out2:
+	free_netdev(self->netdev);
+	dev_self[--dev_count] = NULL;
+ err_out1:
+	release_region(fir_base, SMSC_IRCC2_FIR_CHIP_IO_EXTENT);
+	release_region(sir_base, SMSC_IRCC2_SIR_CHIP_IO_EXTENT);
+ err_out:
+	return err;
+}
+
+/*
+ * Function smsc_ircc_present(fir_base, sir_base)
+ *
+ *    Check the smsc-ircc chip presence
+ *
+ */
+static int smsc_ircc_present(unsigned int fir_base, unsigned int sir_base)
+{
+	unsigned char low, high, chip, config, dma, irq, version;
+
+	if (!request_region(fir_base, SMSC_IRCC2_FIR_CHIP_IO_EXTENT,
+			    driver_name)) {
+		IRDA_WARNING("%s: can't get fir_base of 0x%03x\n",
+			     __FUNCTION__, fir_base);
+		goto out1;
+	}
+
+	if (!request_region(sir_base, SMSC_IRCC2_SIR_CHIP_IO_EXTENT,
+			    driver_name)) {
+		IRDA_WARNING("%s: can't get sir_base of 0x%03x\n",
+			     __FUNCTION__, sir_base);
+		goto out2;
+	}
+
+	register_bank(fir_base, 3);
+
+	high    = inb(fir_base+IRCC_ID_HIGH);
+	low     = inb(fir_base+IRCC_ID_LOW);
+	chip    = inb(fir_base+IRCC_CHIP_ID);
+	version = inb(fir_base+IRCC_VERSION);
+	config  = inb(fir_base+IRCC_INTERFACE);
+	dma     = config & IRCC_INTERFACE_DMA_MASK;
+	irq     = (config & IRCC_INTERFACE_IRQ_MASK) >> 4;
+
+	if (high != 0x10 || low != 0xb8 || (chip != 0xf1 && chip != 0xf2)) { 
+	        IRDA_WARNING("%s(), addr 0x%04x - no device found!\n",
+			     __FUNCTION__, fir_base);
+		goto out3;
+	}
+	IRDA_MESSAGE("SMsC IrDA Controller found\n IrCC version %d.%d, "
+		     "firport 0x%03x, sirport 0x%03x dma=%d, irq=%d\n",
+		     chip & 0x0f, version, fir_base, sir_base, dma, irq);
+
+	return 0;
+ out3:
+	release_region(sir_base, SMSC_IRCC2_SIR_CHIP_IO_EXTENT);
+ out2:
+	release_region(fir_base, SMSC_IRCC2_FIR_CHIP_IO_EXTENT);
+ out1:
+	return -ENODEV;
+}
+
+/*
+ * Function smsc_ircc_setup_io(self, fir_base, sir_base, dma, irq)
+ *
+ *    Setup I/O
+ *
+ */
+static void smsc_ircc_setup_io(struct smsc_ircc_cb *self, 
+			       unsigned int fir_base, unsigned int sir_base, 
+			       u8 dma, u8 irq)
+{
+	unsigned char config, chip_dma, chip_irq;
+
+	register_bank(fir_base, 3);
+	config  = inb(fir_base+IRCC_INTERFACE);
+	chip_dma     = config & IRCC_INTERFACE_DMA_MASK;
+	chip_irq     = (config & IRCC_INTERFACE_IRQ_MASK) >> 4;
+
+	self->io.fir_base  = fir_base;
+	self->io.sir_base  = sir_base;
+	self->io.fir_ext   = SMSC_IRCC2_FIR_CHIP_IO_EXTENT;
+	self->io.sir_ext   = SMSC_IRCC2_SIR_CHIP_IO_EXTENT;
+	self->io.fifo_size = SMSC_IRCC2_FIFO_SIZE;
+	self->io.speed = SMSC_IRCC2_C_IRDA_FALLBACK_SPEED;
+
+	if (irq < 255) {
+		if (irq != chip_irq)
+			IRDA_MESSAGE("%s, Overriding IRQ - chip says %d, using %d\n",
+				     driver_name, chip_irq, irq);
+		self->io.irq = irq;
+	}
+	else
+		self->io.irq = chip_irq;
+	
+	if (dma < 255) {
+		if (dma != chip_dma)
+			IRDA_MESSAGE("%s, Overriding DMA - chip says %d, using %d\n",
+				     driver_name, chip_dma, dma);
+		self->io.dma = dma;
+	}
+	else
+		self->io.dma = chip_dma;
+
+}
+
+/*
+ * Function smsc_ircc_setup_qos(self)
+ *
+ *    Setup qos
+ *
+ */
+static void smsc_ircc_setup_qos(struct smsc_ircc_cb *self)
+{
+	/* Initialize QoS for this device */
+	irda_init_max_qos_capabilies(&self->qos);
+	
+	self->qos.baud_rate.bits = IR_9600|IR_19200|IR_38400|IR_57600|
+		IR_115200|IR_576000|IR_1152000|(IR_4000000 << 8);
+
+	self->qos.min_turn_time.bits = SMSC_IRCC2_MIN_TURN_TIME;
+	self->qos.window_size.bits = SMSC_IRCC2_WINDOW_SIZE;
+	irda_qos_bits_to_value(&self->qos);
+}
+
+/*
+ * Function smsc_ircc_init_chip(self)
+ *
+ *    Init chip
+ *
+ */
+static void smsc_ircc_init_chip(struct smsc_ircc_cb *self)
+{
+	int iobase, ir_mode, ctrl, fast; 
+	
+	IRDA_ASSERT( self != NULL, return; );
+	iobase = self->io.fir_base;
+
+	ir_mode = IRCC_CFGA_IRDA_SIR_A;
+	ctrl = 0;
+	fast = 0;
+
+	register_bank(iobase, 0);
+	outb(IRCC_MASTER_RESET, iobase+IRCC_MASTER);
+	outb(0x00, iobase+IRCC_MASTER);
+
+	register_bank(iobase, 1);
+	outb(((inb(iobase+IRCC_SCE_CFGA) & 0x87) | ir_mode), 
+	     iobase+IRCC_SCE_CFGA);
+
+#ifdef smsc_669 /* Uses pin 88/89 for Rx/Tx */
+	outb(((inb(iobase+IRCC_SCE_CFGB) & 0x3f) | IRCC_CFGB_MUX_COM), 
+	     iobase+IRCC_SCE_CFGB);
+#else	
+	outb(((inb(iobase+IRCC_SCE_CFGB) & 0x3f) | IRCC_CFGB_MUX_IR),
+	     iobase+IRCC_SCE_CFGB);
+#endif	
+	(void) inb(iobase+IRCC_FIFO_THRESHOLD);
+	outb(SMSC_IRCC2_FIFO_THRESHOLD, iobase+IRCC_FIFO_THRESHOLD);
+	
+	register_bank(iobase, 4);
+	outb((inb(iobase+IRCC_CONTROL) & 0x30) | ctrl, iobase+IRCC_CONTROL);
+	
+	register_bank(iobase, 0);
+	outb(fast, iobase+IRCC_LCR_A);
+
+	smsc_ircc_set_sir_speed(self, SMSC_IRCC2_C_IRDA_FALLBACK_SPEED);
+	
+	/* Power on device */
+	outb(0x00, iobase+IRCC_MASTER);
+}
+
+/*
+ * Function smsc_ircc_net_ioctl (dev, rq, cmd)
+ *
+ *    Process IOCTL commands for this device
+ *
+ */
+static int smsc_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+	struct if_irda_req *irq = (struct if_irda_req *) rq;
+	struct smsc_ircc_cb *self;
+	unsigned long flags;
+	int ret = 0;
+
+	IRDA_ASSERT(dev != NULL, return -1;);
+
+	self = dev->priv;
+
+	IRDA_ASSERT(self != NULL, return -1;);
+
+	IRDA_DEBUG(2, "%s(), %s, (cmd=0x%X)\n", __FUNCTION__, dev->name, cmd);
+	
+	switch (cmd) {
+	case SIOCSBANDWIDTH: /* Set bandwidth */
+		if (!capable(CAP_NET_ADMIN))
+			ret = -EPERM;
+                else {
+			/* Make sure we are the only one touching
+			 * self->io.speed and the hardware - Jean II */
+			spin_lock_irqsave(&self->lock, flags);
+			smsc_ircc_change_speed(self, irq->ifr_baudrate);
+			spin_unlock_irqrestore(&self->lock, flags);
+		}
+		break;
+	case SIOCSMEDIABUSY: /* Set media busy */
+		if (!capable(CAP_NET_ADMIN)) {
+			ret = -EPERM;
+			break;
+		}
+
+		irda_device_set_media_busy(self->netdev, TRUE);
+		break;
+	case SIOCGRECEIVING: /* Check if we are receiving right now */
+		irq->ifr_receiving = smsc_ircc_is_receiving(self);
+		break;
+	#if 0
+	case SIOCSDTRRTS:
+		if (!capable(CAP_NET_ADMIN)) {
+			ret = -EPERM;
+			break;
+		}
+		smsc_ircc_sir_set_dtr_rts(dev, irq->ifr_dtr, irq->ifr_rts);
+		break;
+	#endif
+	default:
+		ret = -EOPNOTSUPP;
+	}
+	
+	return ret;
+}
+
+static struct net_device_stats *smsc_ircc_net_get_stats(struct net_device *dev)
+{
+	struct smsc_ircc_cb *self = (struct smsc_ircc_cb *) dev->priv;
+	
+	return &self->stats;
+}
+
+#if SMSC_IRCC2_C_NET_TIMEOUT
+/*
+ * Function smsc_ircc_timeout (struct net_device *dev)
+ *
+ *    The networking timeout management.
+ *
+ */
+
+static void smsc_ircc_timeout(struct net_device *dev)
+{
+	struct smsc_ircc_cb *self;
+	unsigned long flags;
+
+	self = (struct smsc_ircc_cb *) dev->priv;
+	
+	IRDA_WARNING("%s: transmit timed out, changing speed to: %d\n",
+		     dev->name, self->io.speed);
+	spin_lock_irqsave(&self->lock, flags);
+	smsc_ircc_sir_start(self);
+	smsc_ircc_change_speed(self, self->io.speed);
+	dev->trans_start = jiffies;
+	netif_wake_queue(dev);
+	spin_unlock_irqrestore(&self->lock, flags);
+}
+#endif
+
+/*
+ * Function smsc_ircc_hard_xmit_sir (struct sk_buff *skb, struct net_device *dev)
+ *
+ *    Transmits the current frame until FIFO is full, then
+ *    waits until the next transmit interrupt, and continues until the
+ *    frame is transmitted.
+ */
+int smsc_ircc_hard_xmit_sir(struct sk_buff *skb, struct net_device *dev)
+{
+	struct smsc_ircc_cb *self;
+	unsigned long flags;
+	int iobase;
+	s32 speed;
+
+	IRDA_DEBUG(1, "%s\n", __FUNCTION__);
+
+	IRDA_ASSERT(dev != NULL, return 0;);
+	
+	self = (struct smsc_ircc_cb *) dev->priv;
+	IRDA_ASSERT(self != NULL, return 0;);
+
+	iobase = self->io.sir_base;
+
+	netif_stop_queue(dev);
+	
+	/* Make sure test of self->io.speed & speed change are atomic */
+	spin_lock_irqsave(&self->lock, flags);
+
+	/* Check if we need to change the speed */
+	speed = irda_get_next_speed(skb);
+	if ((speed != self->io.speed) && (speed != -1)) {
+		/* Check for empty frame */
+		if (!skb->len) {
+			/*
+			 * We send frames one by one in SIR mode (no
+			 * pipelining), so at this point, if we were sending
+			 * a previous frame, we just received the interrupt
+			 * telling us it is finished (UART_IIR_THRI).
+			 * Therefore, waiting for the transmitter to really
+			 * finish draining the fifo won't take too long.
+			 * And the interrupt handler is not expected to run.
+			 * - Jean II */
+			smsc_ircc_sir_wait_hw_transmitter_finish(self);
+			smsc_ircc_change_speed(self, speed);
+			spin_unlock_irqrestore(&self->lock, flags);
+			dev_kfree_skb(skb);
+			return 0;
+		} else {
+			self->new_speed = speed;
+		}
+	}
+
+	/* Init tx buffer */
+	self->tx_buff.data = self->tx_buff.head;
+
+	/* Copy skb to tx_buff while wrapping, stuffing and making CRC */
+	self->tx_buff.len = async_wrap_skb(skb, self->tx_buff.data, 
+					   self->tx_buff.truesize);
+	
+	self->stats.tx_bytes += self->tx_buff.len;
+
+	/* Turn on transmit finished interrupt. Will fire immediately!  */
+	outb(UART_IER_THRI, iobase+UART_IER); 
+
+	spin_unlock_irqrestore(&self->lock, flags);
+
+	dev_kfree_skb(skb);
+	
+	return 0;
+}
+
+/*
+ * Function smsc_ircc_set_fir_speed (self, baud)
+ *
+ *    Change the speed of the device
+ *
+ */
+static void smsc_ircc_set_fir_speed(struct smsc_ircc_cb *self, u32 speed)
+{
+	int fir_base, ir_mode, ctrl, fast;
+
+	IRDA_ASSERT(self != NULL, return;);
+	fir_base = self->io.fir_base;
+
+	self->io.speed = speed;
+
+	switch(speed) {
+	default:
+	case 576000:		
+		ir_mode = IRCC_CFGA_IRDA_HDLC;
+		ctrl = IRCC_CRC;
+		fast = 0;
+		IRDA_DEBUG(0, "%s(), handling baud of 576000\n", __FUNCTION__);
+		break;
+	case 1152000:
+		ir_mode = IRCC_CFGA_IRDA_HDLC;
+		ctrl = IRCC_1152 | IRCC_CRC;
+		fast = IRCC_LCR_A_FAST | IRCC_LCR_A_GP_DATA;
+		IRDA_DEBUG(0, "%s(), handling baud of 1152000\n",
+			   __FUNCTION__);
+		break;
+	case 4000000:
+		ir_mode = IRCC_CFGA_IRDA_4PPM;
+		ctrl = IRCC_CRC;
+		fast = IRCC_LCR_A_FAST;
+		IRDA_DEBUG(0, "%s(), handling baud of 4000000\n",
+			   __FUNCTION__);
+		break;
+	}
+	#if 0
+	Now in tranceiver!
+	/* This causes an interrupt */
+	register_bank(fir_base, 0);
+	outb((inb(fir_base+IRCC_LCR_A) &  0xbf) | fast, fir_base+IRCC_LCR_A);
+	#endif
+	
+	register_bank(fir_base, 1);
+	outb(((inb(fir_base+IRCC_SCE_CFGA) & IRCC_SCE_CFGA_BLOCK_CTRL_BITS_MASK) | ir_mode), fir_base+IRCC_SCE_CFGA);
+	
+	register_bank(fir_base, 4);
+	outb((inb(fir_base+IRCC_CONTROL) & 0x30) | ctrl, fir_base+IRCC_CONTROL);
+}
+
+/*
+ * Function smsc_ircc_fir_start(self)
+ *
+ *    Change the speed of the device
+ *
+ */
+static void smsc_ircc_fir_start(struct smsc_ircc_cb *self)
+{
+	struct net_device *dev;
+	int fir_base;
+
+	IRDA_DEBUG(1, "%s\n", __FUNCTION__);
+
+	IRDA_ASSERT(self != NULL, return;);
+	dev = self->netdev;
+	IRDA_ASSERT(dev != NULL, return;);
+
+	fir_base = self->io.fir_base;
+
+	/* Reset everything */
+
+	/* Install FIR transmit handler */
+	dev->hard_start_xmit = smsc_ircc_hard_xmit_fir;	
+
+	/* Clear FIFO */
+	outb(inb(fir_base+IRCC_LCR_A)|IRCC_LCR_A_FIFO_RESET, fir_base+IRCC_LCR_A);
+
+	/* Enable interrupt */
+	/*outb(IRCC_IER_ACTIVE_FRAME|IRCC_IER_EOM, fir_base+IRCC_IER);*/
+
+	register_bank(fir_base, 1);
+
+	/* Select the TX/RX interface */	
+#ifdef SMSC_669 /* Uses pin 88/89 for Rx/Tx */
+	outb(((inb(fir_base+IRCC_SCE_CFGB) & 0x3f) | IRCC_CFGB_MUX_COM), 
+	     fir_base+IRCC_SCE_CFGB);
+#else	
+	outb(((inb(fir_base+IRCC_SCE_CFGB) & 0x3f) | IRCC_CFGB_MUX_IR),
+	     fir_base+IRCC_SCE_CFGB);
+#endif	
+	(void) inb(fir_base+IRCC_FIFO_THRESHOLD);
+
+	/* Enable SCE interrupts */
+	outb(0, fir_base+IRCC_MASTER);
+	register_bank(fir_base, 0);
+	outb(IRCC_IER_ACTIVE_FRAME|IRCC_IER_EOM, fir_base+IRCC_IER);
+	outb(IRCC_MASTER_INT_EN, fir_base+IRCC_MASTER);
+}
+
+/*
+ * Function smsc_ircc_fir_stop(self, baud)
+ *
+ *    Change the speed of the device
+ *
+ */
+static void smsc_ircc_fir_stop(struct smsc_ircc_cb *self)
+{
+	int fir_base;
+
+	IRDA_DEBUG(1, "%s\n", __FUNCTION__);
+	
+	IRDA_ASSERT(self != NULL, return;);
+
+	fir_base = self->io.fir_base;
+	register_bank(fir_base, 0);
+	/*outb(IRCC_MASTER_RESET, fir_base+IRCC_MASTER);*/	
+	outb(inb(fir_base+IRCC_LCR_B) & IRCC_LCR_B_SIP_ENABLE, fir_base+IRCC_LCR_B);
+}
+
+
+/*
+ * Function smsc_ircc_change_speed(self, baud)
+ *
+ *    Change the speed of the device
+ *
+ * This function *must* be called with spinlock held, because it may
+ * be called from the irq handler. - Jean II
+ */
+static void smsc_ircc_change_speed(void *priv, u32 speed)
+{
+	struct smsc_ircc_cb *self = (struct smsc_ircc_cb *) priv;
+	struct net_device *dev;
+	int iobase;
+	int last_speed_was_sir;
+	
+	IRDA_DEBUG(0, "%s() changing speed to: %d\n", __FUNCTION__, speed);
+
+	IRDA_ASSERT(self != NULL, return;);
+	dev = self->netdev;
+	iobase = self->io.fir_base;
+
+	last_speed_was_sir = self->io.speed <= SMSC_IRCC2_MAX_SIR_SPEED;
+
+	#if 0
+	/* Temp Hack */
+	speed= 1152000;
+	self->io.speed = speed;
+	last_speed_was_sir = 0;
+	smsc_ircc_fir_start(self);	
+	#endif
+	
+	if(self->io.speed == 0)
+		smsc_ircc_sir_start(self);
+
+	#if 0
+	if(!last_speed_was_sir) speed = self->io.speed;
+	#endif
+
+	if(self->io.speed != speed) smsc_ircc_set_transceiver_for_speed(self, speed);
+
+	self->io.speed = speed;
+	
+	if(speed <= SMSC_IRCC2_MAX_SIR_SPEED) {
+		if(!last_speed_was_sir) {
+			smsc_ircc_fir_stop(self);
+			smsc_ircc_sir_start(self);
+		}
+		smsc_ircc_set_sir_speed(self, speed); 
+	}
+	else {
+		if(last_speed_was_sir) {
+			#if SMSC_IRCC2_C_SIR_STOP		
+			smsc_ircc_sir_stop(self);
+			#endif
+			smsc_ircc_fir_start(self);
+		}
+		smsc_ircc_set_fir_speed(self, speed);
+
+		#if 0
+		self->tx_buff.len = 10;
+		self->tx_buff.data = self->tx_buff.head;
+		
+		smsc_ircc_dma_xmit(self, iobase, 4000);
+		#endif
+		/* Be ready for incoming frames */
+		smsc_ircc_dma_receive(self, iobase);
+	}
+	
+	netif_wake_queue(dev);
+}
+
+/*
+ * Function smsc_ircc_set_sir_speed (self, speed)
+ *
+ *    Set speed of IrDA port to specified baudrate
+ *
+ */
+void smsc_ircc_set_sir_speed(void *priv, __u32 speed)
+{
+	struct smsc_ircc_cb *self = (struct smsc_ircc_cb *) priv;
+	int iobase; 
+	int fcr;    /* FIFO control reg */
+	int lcr;    /* Line control reg */
+	int divisor;
+
+	IRDA_DEBUG(0, "%s(), Setting speed to: %d\n", __FUNCTION__, speed);
+
+	IRDA_ASSERT(self != NULL, return;);
+	iobase = self->io.sir_base;
+	
+	/* Update accounting for new speed */
+	self->io.speed = speed;
+
+	/* Turn off interrupts */
+	outb(0, iobase+UART_IER); 
+
+	divisor = SMSC_IRCC2_MAX_SIR_SPEED/speed;
+	
+	fcr = UART_FCR_ENABLE_FIFO;
+
+	/* 
+	 * Use trigger level 1 to avoid 3 ms. timeout delay at 9600 bps, and
+	 * almost 1,7 ms at 19200 bps. At speeds above that we can just forget
+	 * about this timeout since it will always be fast enough. 
+	 */
+	if (self->io.speed < 38400)
+		fcr |= UART_FCR_TRIGGER_1;
+	else 
+		fcr |= UART_FCR_TRIGGER_14;
+        
+	/* IrDA ports use 8N1 */
+	lcr = UART_LCR_WLEN8;
+	
+	outb(UART_LCR_DLAB | lcr, iobase+UART_LCR); /* Set DLAB */
+	outb(divisor & 0xff,      iobase+UART_DLL); /* Set speed */
+	outb(divisor >> 8,	  iobase+UART_DLM);
+	outb(lcr,		  iobase+UART_LCR); /* Set 8N1	*/
+	outb(fcr,		  iobase+UART_FCR); /* Enable FIFO's */
+
+	/* Turn on interrups */
+	outb(UART_IER_RLSI|UART_IER_RDI|UART_IER_THRI, iobase+UART_IER);
+
+	IRDA_DEBUG(2, "%s() speed changed to: %d\n", __FUNCTION__, speed);
+}
+
+
+/*
+ * Function smsc_ircc_hard_xmit_fir (skb, dev)
+ *
+ *    Transmit the frame!
+ *
+ */
+static int smsc_ircc_hard_xmit_fir(struct sk_buff *skb, struct net_device *dev)
+{
+	struct smsc_ircc_cb *self;
+	unsigned long flags;
+	s32 speed;
+	int iobase;
+	int mtt;
+
+	IRDA_ASSERT(dev != NULL, return 0;);
+	self = (struct smsc_ircc_cb *) dev->priv;
+	IRDA_ASSERT(self != NULL, return 0;);
+
+	iobase = self->io.fir_base;
+
+	netif_stop_queue(dev);
+
+	/* Make sure test of self->io.speed & speed change are atomic */
+	spin_lock_irqsave(&self->lock, flags);
+
+	/* Check if we need to change the speed after this frame */
+	speed = irda_get_next_speed(skb);
+	if ((speed != self->io.speed) && (speed != -1)) {
+		/* Check for empty frame */
+		if (!skb->len) {
+			/* Note : you should make sure that speed changes
+			 * are not going to corrupt any outgoing frame.
+			 * Look at nsc-ircc for the gory details - Jean II */
+			smsc_ircc_change_speed(self, speed); 
+			spin_unlock_irqrestore(&self->lock, flags);
+			dev_kfree_skb(skb);
+			return 0;
+		} else
+			self->new_speed = speed;
+	}
+	
+	memcpy(self->tx_buff.head, skb->data, skb->len);
+
+	self->tx_buff.len = skb->len;
+	self->tx_buff.data = self->tx_buff.head;
+	
+	mtt = irda_get_mtt(skb);	
+	if (mtt) {
+		int bofs;
+
+		/* 
+		 * Compute how many BOFs (STA or PA's) we need to waste the
+		 * min turn time given the speed of the link.
+		 */
+		bofs = mtt * (self->io.speed / 1000) / 8000;
+		if (bofs > 4095)
+			bofs = 4095;
+
+		smsc_ircc_dma_xmit(self, iobase, bofs);
+	} else {
+		/* Transmit frame */
+		smsc_ircc_dma_xmit(self, iobase, 0);
+	}
+	spin_unlock_irqrestore(&self->lock, flags);
+	dev_kfree_skb(skb);
+
+	return 0;
+}
+
+/*
+ * Function smsc_ircc_dma_xmit (self, iobase)
+ *
+ *    Transmit data using DMA
+ *
+ */
+static void smsc_ircc_dma_xmit(struct smsc_ircc_cb *self, int iobase, int bofs)
+{
+	u8 ctrl;
+
+	IRDA_DEBUG(3, "%s\n", __FUNCTION__);
+#if 1
+	/* Disable Rx */
+	register_bank(iobase, 0);
+	outb(0x00, iobase+IRCC_LCR_B);
+#endif
+	register_bank(iobase, 1);
+	outb(inb(iobase+IRCC_SCE_CFGB) & ~IRCC_CFGB_DMA_ENABLE, 
+	     iobase+IRCC_SCE_CFGB);
+
+	self->io.direction = IO_XMIT;
+
+	/* Set BOF additional count for generating the min turn time */
+	register_bank(iobase, 4);
+	outb(bofs & 0xff, iobase+IRCC_BOF_COUNT_LO);
+	ctrl = inb(iobase+IRCC_CONTROL) & 0xf0;
+	outb(ctrl | ((bofs >> 8) & 0x0f), iobase+IRCC_BOF_COUNT_HI);
+
+	/* Set max Tx frame size */
+	outb(self->tx_buff.len >> 8, iobase+IRCC_TX_SIZE_HI);
+	outb(self->tx_buff.len & 0xff, iobase+IRCC_TX_SIZE_LO);
+
+	/*outb(UART_MCR_OUT2, self->io.sir_base + UART_MCR);*/
+	
+	/* Enable burst mode chip Tx DMA */
+	register_bank(iobase, 1);
+	outb(inb(iobase+IRCC_SCE_CFGB) | IRCC_CFGB_DMA_ENABLE |
+	     IRCC_CFGB_DMA_BURST, iobase+IRCC_SCE_CFGB);
+
+	/* Setup DMA controller (must be done after enabling chip DMA) */
+	irda_setup_dma(self->io.dma, self->tx_buff_dma, self->tx_buff.len,
+		       DMA_TX_MODE);
+
+	/* Enable interrupt */
+
+	register_bank(iobase, 0);
+	outb(IRCC_IER_ACTIVE_FRAME | IRCC_IER_EOM, iobase+IRCC_IER);
+	outb(IRCC_MASTER_INT_EN, iobase+IRCC_MASTER);
+	
+	/* Enable transmit */
+	outb(IRCC_LCR_B_SCE_TRANSMIT | IRCC_LCR_B_SIP_ENABLE, iobase+IRCC_LCR_B);
+}
+
+/*
+ * Function smsc_ircc_dma_xmit_complete (self)
+ *
+ *    The transfer of a frame in finished. This function will only be called 
+ *    by the interrupt handler
+ *
+ */
+static void smsc_ircc_dma_xmit_complete(struct smsc_ircc_cb *self, int iobase)
+{
+	IRDA_DEBUG(3, "%s\n", __FUNCTION__);
+#if 0
+	/* Disable Tx */
+	register_bank(iobase, 0);
+	outb(0x00, iobase+IRCC_LCR_B);
+#endif
+	register_bank(self->io.fir_base, 1);
+	outb(inb(self->io.fir_base+IRCC_SCE_CFGB) & ~IRCC_CFGB_DMA_ENABLE,
+	     self->io.fir_base+IRCC_SCE_CFGB);
+
+	/* Check for underrun! */
+	register_bank(iobase, 0);
+	if (inb(iobase+IRCC_LSR) & IRCC_LSR_UNDERRUN) {
+		self->stats.tx_errors++;
+		self->stats.tx_fifo_errors++;
+
+		/* Reset error condition */
+		register_bank(iobase, 0);
+		outb(IRCC_MASTER_ERROR_RESET, iobase+IRCC_MASTER);
+		outb(0x00, iobase+IRCC_MASTER);
+	} else {
+		self->stats.tx_packets++;
+		self->stats.tx_bytes +=  self->tx_buff.len;
+	}
+
+	/* Check if it's time to change the speed */
+	if (self->new_speed) {
+		smsc_ircc_change_speed(self, self->new_speed);		
+		self->new_speed = 0;
+	}
+
+	netif_wake_queue(self->netdev);
+}
+
+/*
+ * Function smsc_ircc_dma_receive(self)
+ *
+ *    Get ready for receiving a frame. The device will initiate a DMA
+ *    if it starts to receive a frame.
+ *
+ */
+static int smsc_ircc_dma_receive(struct smsc_ircc_cb *self, int iobase) 
+{
+#if 0
+	/* Turn off chip DMA */
+	register_bank(iobase, 1);
+	outb(inb(iobase+IRCC_SCE_CFGB) & ~IRCC_CFGB_DMA_ENABLE, 
+	     iobase+IRCC_SCE_CFGB);
+#endif
+	
+	/* Disable Tx */
+	register_bank(iobase, 0);
+	outb(0x00, iobase+IRCC_LCR_B);
+
+	/* Turn off chip DMA */
+	register_bank(iobase, 1);
+	outb(inb(iobase+IRCC_SCE_CFGB) & ~IRCC_CFGB_DMA_ENABLE, 
+	     iobase+IRCC_SCE_CFGB);
+
+	self->io.direction = IO_RECV;
+	self->rx_buff.data = self->rx_buff.head;
+
+	/* Set max Rx frame size */
+	register_bank(iobase, 4);
+	outb((2050 >> 8) & 0x0f, iobase+IRCC_RX_SIZE_HI);
+	outb(2050 & 0xff, iobase+IRCC_RX_SIZE_LO);
+
+	/* Setup DMA controller */
+	irda_setup_dma(self->io.dma, self->rx_buff_dma, self->rx_buff.truesize,
+		       DMA_RX_MODE);
+
+	/* Enable burst mode chip Rx DMA */
+	register_bank(iobase, 1);
+	outb(inb(iobase+IRCC_SCE_CFGB) | IRCC_CFGB_DMA_ENABLE | 
+	     IRCC_CFGB_DMA_BURST, iobase+IRCC_SCE_CFGB);
+
+	/* Enable interrupt */
+	register_bank(iobase, 0);
+	outb(IRCC_IER_ACTIVE_FRAME | IRCC_IER_EOM, iobase+IRCC_IER);
+	outb(IRCC_MASTER_INT_EN, iobase+IRCC_MASTER);
+
+
+	/* Enable receiver */
+	register_bank(iobase, 0);
+	outb(IRCC_LCR_B_SCE_RECEIVE | IRCC_LCR_B_SIP_ENABLE, 
+	     iobase+IRCC_LCR_B);
+	
+	return 0;
+}
+
+/*
+ * Function smsc_ircc_dma_receive_complete(self, iobase)
+ *
+ *    Finished with receiving frames
+ *
+ */
+static void smsc_ircc_dma_receive_complete(struct smsc_ircc_cb *self, int iobase)
+{
+	struct sk_buff *skb;
+	int len, msgcnt, lsr;
+	
+	register_bank(iobase, 0);
+	
+	IRDA_DEBUG(3, "%s\n", __FUNCTION__);
+#if 0
+	/* Disable Rx */
+	register_bank(iobase, 0);
+	outb(0x00, iobase+IRCC_LCR_B);
+#endif
+	register_bank(iobase, 0);
+	outb(inb(iobase+IRCC_LSAR) & ~IRCC_LSAR_ADDRESS_MASK, iobase+IRCC_LSAR);
+	lsr= inb(iobase+IRCC_LSR);
+	msgcnt = inb(iobase+IRCC_LCR_B) & 0x08;
+
+	IRDA_DEBUG(2, "%s: dma count = %d\n", __FUNCTION__,
+		   get_dma_residue(self->io.dma));
+
+	len = self->rx_buff.truesize - get_dma_residue(self->io.dma);
+
+	/* Look for errors 
+	 */	
+
+	if(lsr & (IRCC_LSR_FRAME_ERROR | IRCC_LSR_CRC_ERROR | IRCC_LSR_SIZE_ERROR)) {
+		self->stats.rx_errors++;
+		if(lsr & IRCC_LSR_FRAME_ERROR) self->stats.rx_frame_errors++;
+		if(lsr & IRCC_LSR_CRC_ERROR) self->stats.rx_crc_errors++;
+		if(lsr & IRCC_LSR_SIZE_ERROR) self->stats.rx_length_errors++;
+		if(lsr & (IRCC_LSR_UNDERRUN | IRCC_LSR_OVERRUN)) self->stats.rx_length_errors++;
+		return;
+	}
+	/* Remove CRC */
+	if (self->io.speed < 4000000)
+		len -= 2;
+	else
+		len -= 4;
+
+	if ((len < 2) || (len > 2050)) {
+		IRDA_WARNING("%s(), bogus len=%d\n", __FUNCTION__, len);
+		return;
+	}
+	IRDA_DEBUG(2, "%s: msgcnt = %d, len=%d\n", __FUNCTION__, msgcnt, len);
+
+	skb = dev_alloc_skb(len+1);
+	if (!skb)  {
+		IRDA_WARNING("%s(), memory squeeze, dropping frame.\n",
+			     __FUNCTION__);
+		return;
+	}			
+	/* Make sure IP header gets aligned */
+	skb_reserve(skb, 1); 
+
+	memcpy(skb_put(skb, len), self->rx_buff.data, len);
+	self->stats.rx_packets++;
+	self->stats.rx_bytes += len;
+
+	skb->dev = self->netdev;
+	skb->mac.raw  = skb->data;
+	skb->protocol = htons(ETH_P_IRDA);
+	netif_rx(skb);
+}
+
+/*
+ * Function smsc_ircc_sir_receive (self)
+ *
+ *    Receive one frame from the infrared port
+ *
+ */
+static void smsc_ircc_sir_receive(struct smsc_ircc_cb *self) 
+{
+	int boguscount = 0;
+	int iobase;
+
+	IRDA_ASSERT(self != NULL, return;);
+
+	iobase = self->io.sir_base;
+
+	/*  
+	 * Receive all characters in Rx FIFO, unwrap and unstuff them. 
+         * async_unwrap_char will deliver all found frames  
+	 */
+	do {
+		async_unwrap_char(self->netdev, &self->stats, &self->rx_buff, 
+				  inb(iobase+UART_RX));
+
+		/* Make sure we don't stay here to long */
+		if (boguscount++ > 32) {
+			IRDA_DEBUG(2, "%s(), breaking!\n", __FUNCTION__);
+			break;
+		}
+	} while (inb(iobase+UART_LSR) & UART_LSR_DR);	
+}
+
+
+/*
+ * Function smsc_ircc_interrupt (irq, dev_id, regs)
+ *
+ *    An interrupt from the chip has arrived. Time to do some work
+ *
+ */
+static irqreturn_t smsc_ircc_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct net_device *dev = (struct net_device *) dev_id;
+	struct smsc_ircc_cb *self;
+	int iobase, iir, lcra, lsr;
+	irqreturn_t ret = IRQ_NONE;
+
+	if (dev == NULL) {
+		printk(KERN_WARNING "%s: irq %d for unknown device.\n", 
+		       driver_name, irq);
+		goto irq_ret;
+	}
+	self = (struct smsc_ircc_cb *) dev->priv;
+	IRDA_ASSERT(self != NULL, return IRQ_NONE;);
+
+	/* Serialise the interrupt handler in various CPUs, stop Tx path */
+	spin_lock(&self->lock);	
+
+	/* Check if we should use the SIR interrupt handler */
+	if (self->io.speed <=  SMSC_IRCC2_MAX_SIR_SPEED) {
+		ret = smsc_ircc_interrupt_sir(dev);
+		goto irq_ret_unlock;
+	}
+
+	iobase = self->io.fir_base;
+
+	register_bank(iobase, 0);
+	iir = inb(iobase+IRCC_IIR);
+	if (iir == 0) 
+		goto irq_ret_unlock;
+	ret = IRQ_HANDLED;
+
+	/* Disable interrupts */
+	outb(0, iobase+IRCC_IER);
+	lcra = inb(iobase+IRCC_LCR_A);
+	lsr = inb(iobase+IRCC_LSR);
+	
+	IRDA_DEBUG(2, "%s(), iir = 0x%02x\n", __FUNCTION__, iir);
+
+	if (iir & IRCC_IIR_EOM) {
+		if (self->io.direction == IO_RECV)
+			smsc_ircc_dma_receive_complete(self, iobase);
+		else
+			smsc_ircc_dma_xmit_complete(self, iobase);
+		
+		smsc_ircc_dma_receive(self, iobase);
+	}
+
+	if (iir & IRCC_IIR_ACTIVE_FRAME) {
+		/*printk(KERN_WARNING "%s(): Active Frame\n", __FUNCTION__);*/
+	}
+
+	/* Enable interrupts again */
+
+	register_bank(iobase, 0);
+	outb(IRCC_IER_ACTIVE_FRAME|IRCC_IER_EOM, iobase+IRCC_IER);
+
+ irq_ret_unlock:
+	spin_unlock(&self->lock);
+ irq_ret:
+	return ret;
+}
+
+/*
+ * Function irport_interrupt_sir (irq, dev_id, regs)
+ *
+ *    Interrupt handler for SIR modes
+ */
+static irqreturn_t smsc_ircc_interrupt_sir(struct net_device *dev)
+{
+	struct smsc_ircc_cb *self = dev->priv;
+	int boguscount = 0;
+	int iobase;
+	int iir, lsr;
+
+	/* Already locked comming here in smsc_ircc_interrupt() */
+	/*spin_lock(&self->lock);*/
+
+	iobase = self->io.sir_base;
+
+	iir = inb(iobase+UART_IIR) & UART_IIR_ID;
+	if (iir == 0)
+		return IRQ_NONE;
+	while (iir) {
+		/* Clear interrupt */
+		lsr = inb(iobase+UART_LSR);
+
+		IRDA_DEBUG(4, "%s(), iir=%02x, lsr=%02x, iobase=%#x\n", 
+			    __FUNCTION__, iir, lsr, iobase);
+
+		switch (iir) {
+		case UART_IIR_RLSI:
+			IRDA_DEBUG(2, "%s(), RLSI\n", __FUNCTION__);
+			break;
+		case UART_IIR_RDI:
+			/* Receive interrupt */
+			smsc_ircc_sir_receive(self);
+			break;
+		case UART_IIR_THRI:
+			if (lsr & UART_LSR_THRE)
+				/* Transmitter ready for data */
+				smsc_ircc_sir_write_wakeup(self);
+			break;
+		default:
+			IRDA_DEBUG(0, "%s(), unhandled IIR=%#x\n",
+				   __FUNCTION__, iir);
+			break;
+		} 
+		
+		/* Make sure we don't stay here to long */
+		if (boguscount++ > 100)
+			break;
+
+ 	        iir = inb(iobase + UART_IIR) & UART_IIR_ID;
+	}
+	/*spin_unlock(&self->lock);*/
+	return IRQ_HANDLED;
+}
+
+
+#if 0 /* unused */
+/*
+ * Function ircc_is_receiving (self)
+ *
+ *    Return TRUE is we are currently receiving a frame
+ *
+ */
+static int ircc_is_receiving(struct smsc_ircc_cb *self)
+{
+	int status = FALSE;
+	/* int iobase; */
+
+	IRDA_DEBUG(1, "%s\n", __FUNCTION__);
+
+	IRDA_ASSERT(self != NULL, return FALSE;);
+
+	IRDA_DEBUG(0, "%s: dma count = %d\n", __FUNCTION__,
+		   get_dma_residue(self->io.dma));
+
+	status = (self->rx_buff.state != OUTSIDE_FRAME);
+	
+	return status;
+}
+#endif /* unused */
+
+
+/*
+ * Function smsc_ircc_net_open (dev)
+ *
+ *    Start the device
+ *
+ */
+static int smsc_ircc_net_open(struct net_device *dev)
+{
+	struct smsc_ircc_cb *self;
+	int iobase;
+	char hwname[16];
+	unsigned long flags;
+
+	IRDA_DEBUG(1, "%s\n", __FUNCTION__);
+	
+	IRDA_ASSERT(dev != NULL, return -1;);
+	self = (struct smsc_ircc_cb *) dev->priv;
+	IRDA_ASSERT(self != NULL, return 0;);
+	
+	iobase = self->io.fir_base;
+
+	if (request_irq(self->io.irq, smsc_ircc_interrupt, 0, dev->name, 
+			(void *) dev)) {
+		IRDA_DEBUG(0, "%s(), unable to allocate irq=%d\n",
+			   __FUNCTION__, self->io.irq);
+		return -EAGAIN;
+	}
+
+	spin_lock_irqsave(&self->lock, flags);
+	/*smsc_ircc_sir_start(self);*/
+	self->io.speed = 0;
+	smsc_ircc_change_speed(self, SMSC_IRCC2_C_IRDA_FALLBACK_SPEED);
+	spin_unlock_irqrestore(&self->lock, flags);
+	
+	/* Give self a hardware name */
+	/* It would be cool to offer the chip revision here - Jean II */
+	sprintf(hwname, "SMSC @ 0x%03x", self->io.fir_base);
+
+	/* 
+	 * Open new IrLAP layer instance, now that everything should be
+	 * initialized properly 
+	 */
+	self->irlap = irlap_open(dev, &self->qos, hwname);
+
+	/*
+	 * Always allocate the DMA channel after the IRQ,
+	 * and clean up on failure.
+	 */
+	if (request_dma(self->io.dma, dev->name)) {
+		smsc_ircc_net_close(dev);
+
+		IRDA_WARNING("%s(), unable to allocate DMA=%d\n",
+			     __FUNCTION__, self->io.dma);
+		return -EAGAIN;
+	}
+	
+	netif_start_queue(dev);
+
+	return 0;
+}
+
+/*
+ * Function smsc_ircc_net_close (dev)
+ *
+ *    Stop the device
+ *
+ */
+static int smsc_ircc_net_close(struct net_device *dev)
+{
+	struct smsc_ircc_cb *self;
+	int iobase;
+
+	IRDA_DEBUG(1, "%s\n", __FUNCTION__);
+	
+	IRDA_ASSERT(dev != NULL, return -1;);
+	self = (struct smsc_ircc_cb *) dev->priv;	
+	IRDA_ASSERT(self != NULL, return 0;);
+	
+	iobase = self->io.fir_base;
+
+	/* Stop device */
+	netif_stop_queue(dev);
+	
+	/* Stop and remove instance of IrLAP */
+	if (self->irlap)
+		irlap_close(self->irlap);
+	self->irlap = NULL;
+
+	free_irq(self->io.irq, dev);
+
+	disable_dma(self->io.dma);
+
+	free_dma(self->io.dma);
+
+	return 0;
+}
+
+
+static void smsc_ircc_suspend(struct smsc_ircc_cb *self)
+{
+	IRDA_MESSAGE("%s, Suspending\n", driver_name);
+
+	if (self->io.suspended)
+		return;
+
+	smsc_ircc_net_close(self->netdev);
+
+	self->io.suspended = 1;
+}
+
+static void smsc_ircc_wakeup(struct smsc_ircc_cb *self)
+{
+	if (!self->io.suspended)
+		return;
+
+	/* The code was doing a "cli()" here, but this can't be right.
+	 * If you need protection, do it in net_open with a spinlock
+	 * or give a good reason. - Jean II */
+
+	smsc_ircc_net_open(self->netdev);
+	
+	IRDA_MESSAGE("%s, Waking up\n", driver_name);
+}
+
+static int smsc_ircc_pmproc(struct pm_dev *dev, pm_request_t rqst, void *data)
+{
+        struct smsc_ircc_cb *self = (struct smsc_ircc_cb*) dev->data;
+        if (self) {
+                switch (rqst) {
+                case PM_SUSPEND:
+                        smsc_ircc_suspend(self);
+                        break;
+                case PM_RESUME:
+                        smsc_ircc_wakeup(self);
+                        break;
+                }
+        }
+	return 0;
+}
+
+/*
+ * Function smsc_ircc_close (self)
+ *
+ *    Close driver instance
+ *
+ */
+static int __exit smsc_ircc_close(struct smsc_ircc_cb *self)
+{
+	int iobase;
+	unsigned long flags;
+
+	IRDA_DEBUG(1, "%s\n", __FUNCTION__);
+
+	IRDA_ASSERT(self != NULL, return -1;);
+
+	iobase = self->io.fir_base;
+
+	if (self->pmdev)
+		pm_unregister(self->pmdev);
+
+	/* Remove netdevice */
+	unregister_netdev(self->netdev);
+
+	/* Make sure the irq handler is not exectuting */
+	spin_lock_irqsave(&self->lock, flags);
+
+	/* Stop interrupts */
+	register_bank(iobase, 0);
+	outb(0, iobase+IRCC_IER);
+	outb(IRCC_MASTER_RESET, iobase+IRCC_MASTER);
+	outb(0x00, iobase+IRCC_MASTER);
+#if 0
+	/* Reset to SIR mode */
+	register_bank(iobase, 1);
+        outb(IRCC_CFGA_IRDA_SIR_A|IRCC_CFGA_TX_POLARITY, iobase+IRCC_SCE_CFGA);
+        outb(IRCC_CFGB_IR, iobase+IRCC_SCE_CFGB);
+#endif
+	spin_unlock_irqrestore(&self->lock, flags);
+
+	/* Release the PORTS that this driver is using */
+	IRDA_DEBUG(0, "%s(), releasing 0x%03x\n",  __FUNCTION__,
+		   self->io.fir_base);
+
+	release_region(self->io.fir_base, self->io.fir_ext);
+
+	IRDA_DEBUG(0, "%s(), releasing 0x%03x\n", __FUNCTION__, 
+		   self->io.sir_base);
+
+	release_region(self->io.sir_base, self->io.sir_ext);
+
+	if (self->tx_buff.head)
+		dma_free_coherent(NULL, self->tx_buff.truesize,
+				  self->tx_buff.head, self->tx_buff_dma);
+	
+	if (self->rx_buff.head)
+		dma_free_coherent(NULL, self->rx_buff.truesize,
+				  self->rx_buff.head, self->rx_buff_dma);
+
+	free_netdev(self->netdev);
+
+	return 0;
+}
+
+static void __exit smsc_ircc_cleanup(void)
+{
+	int i;
+
+	IRDA_DEBUG(1, "%s\n", __FUNCTION__);
+
+	for (i=0; i < 2; i++) {
+		if (dev_self[i])
+			smsc_ircc_close(dev_self[i]);
+	}
+}
+
+/*
+ *	Start SIR operations
+ *
+ * This function *must* be called with spinlock held, because it may
+ * be called from the irq handler (via smsc_ircc_change_speed()). - Jean II
+ */
+void smsc_ircc_sir_start(struct smsc_ircc_cb *self)
+{
+	struct net_device *dev;
+	int fir_base, sir_base;
+
+	IRDA_DEBUG(3, "%s\n", __FUNCTION__);
+
+	IRDA_ASSERT(self != NULL, return;);	
+	dev= self->netdev;
+	IRDA_ASSERT(dev != NULL, return;);		
+	dev->hard_start_xmit = &smsc_ircc_hard_xmit_sir;
+
+	fir_base = self->io.fir_base;
+	sir_base = self->io.sir_base;
+
+	/* Reset everything */
+	outb(IRCC_MASTER_RESET, fir_base+IRCC_MASTER);
+
+	#if SMSC_IRCC2_C_SIR_STOP
+	/*smsc_ircc_sir_stop(self);*/
+	#endif
+
+	register_bank(fir_base, 1);
+	outb(((inb(fir_base+IRCC_SCE_CFGA) & IRCC_SCE_CFGA_BLOCK_CTRL_BITS_MASK) | IRCC_CFGA_IRDA_SIR_A), fir_base+IRCC_SCE_CFGA);
+
+	/* Initialize UART */
+	outb(UART_LCR_WLEN8, sir_base+UART_LCR);  /* Reset DLAB */
+	outb((UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2), sir_base+UART_MCR);
+	
+	/* Turn on interrups */
+	outb(UART_IER_RLSI | UART_IER_RDI |UART_IER_THRI, sir_base+UART_IER);
+
+	IRDA_DEBUG(3, "%s() - exit\n", __FUNCTION__);
+
+	outb(0x00, fir_base+IRCC_MASTER);
+}
+
+#if SMSC_IRCC2_C_SIR_STOP
+void smsc_ircc_sir_stop(struct smsc_ircc_cb *self)
+{
+	int iobase;
+
+	IRDA_DEBUG(3, "%s\n", __FUNCTION__);
+	iobase = self->io.sir_base;
+
+	/* Reset UART */
+	outb(0, iobase+UART_MCR);
+	
+	/* Turn off interrupts */
+	outb(0, iobase+UART_IER);
+}
+#endif
+
+/*
+ * Function smsc_sir_write_wakeup (self)
+ *
+ *    Called by the SIR interrupt handler when there's room for more data.
+ *    If we have more packets to send, we send them here.
+ *
+ */
+static void smsc_ircc_sir_write_wakeup(struct smsc_ircc_cb *self)
+{
+	int actual = 0;
+	int iobase;
+	int fcr;
+
+	IRDA_ASSERT(self != NULL, return;);
+
+	IRDA_DEBUG(4, "%s\n", __FUNCTION__);
+
+	iobase = self->io.sir_base;
+
+	/* Finished with frame?  */
+	if (self->tx_buff.len > 0)  {
+		/* Write data left in transmit buffer */
+		actual = smsc_ircc_sir_write(iobase, self->io.fifo_size, 
+				      self->tx_buff.data, self->tx_buff.len);
+		self->tx_buff.data += actual;
+		self->tx_buff.len  -= actual;
+	} else {
+	
+	/*if (self->tx_buff.len ==0)  {*/
+		
+		/* 
+		 *  Now serial buffer is almost free & we can start 
+		 *  transmission of another packet. But first we must check
+		 *  if we need to change the speed of the hardware
+		 */
+		if (self->new_speed) {
+			IRDA_DEBUG(5, "%s(), Changing speed to %d.\n",
+				   __FUNCTION__, self->new_speed);
+			smsc_ircc_sir_wait_hw_transmitter_finish(self);
+			smsc_ircc_change_speed(self, self->new_speed);
+			self->new_speed = 0;
+		} else {
+			/* Tell network layer that we want more frames */
+			netif_wake_queue(self->netdev);
+		}
+		self->stats.tx_packets++;
+
+		if(self->io.speed <= 115200) {
+		/* 
+		 * Reset Rx FIFO to make sure that all reflected transmit data
+		 * is discarded. This is needed for half duplex operation
+		 */
+		fcr = UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR;
+		if (self->io.speed < 38400)
+			fcr |= UART_FCR_TRIGGER_1;
+		else 
+			fcr |= UART_FCR_TRIGGER_14;
+
+		outb(fcr, iobase+UART_FCR);
+
+		/* Turn on receive interrupts */
+		outb(UART_IER_RDI, iobase+UART_IER);
+		}
+	}
+}
+
+/*
+ * Function smsc_ircc_sir_write (iobase, fifo_size, buf, len)
+ *
+ *    Fill Tx FIFO with transmit data
+ *
+ */
+static int smsc_ircc_sir_write(int iobase, int fifo_size, __u8 *buf, int len)
+{
+	int actual = 0;
+	
+	/* Tx FIFO should be empty! */
+	if (!(inb(iobase+UART_LSR) & UART_LSR_THRE)) {
+		IRDA_WARNING("%s(), failed, fifo not empty!\n", __FUNCTION__);
+		return 0;
+	}
+        
+	/* Fill FIFO with current frame */
+	while ((fifo_size-- > 0) && (actual < len)) {
+		/* Transmit next byte */
+		outb(buf[actual], iobase+UART_TX);
+		actual++;
+	}
+	return actual;
+}
+
+/*
+ * Function smsc_ircc_is_receiving (self)
+ *
+ *    Returns true is we are currently receiving data
+ *
+ */
+static int smsc_ircc_is_receiving(struct smsc_ircc_cb *self)
+{
+	return (self->rx_buff.state != OUTSIDE_FRAME);
+}
+
+
+/*
+ * Function smsc_ircc_probe_transceiver(self)
+ *
+ *    Tries to find the used Transceiver
+ *
+ */
+static void smsc_ircc_probe_transceiver(struct smsc_ircc_cb *self)
+{
+	unsigned int	i;
+	
+	IRDA_ASSERT(self != NULL, return;);
+	
+	for(i=0; smsc_transceivers[i].name!=NULL; i++) 
+		if((*smsc_transceivers[i].probe)(self->io.fir_base)) {
+			IRDA_MESSAGE(" %s transceiver found\n",
+				     smsc_transceivers[i].name);
+			self->transceiver= i+1;
+			return;
+		}
+	IRDA_MESSAGE("No transceiver found. Defaulting to %s\n",
+		     smsc_transceivers[SMSC_IRCC2_C_DEFAULT_TRANSCEIVER].name);
+			
+	self->transceiver= SMSC_IRCC2_C_DEFAULT_TRANSCEIVER;
+}
+
+
+/*
+ * Function smsc_ircc_set_transceiver_for_speed(self, speed)
+ *
+ *    Set the transceiver according to the speed
+ *
+ */
+static void smsc_ircc_set_transceiver_for_speed(struct smsc_ircc_cb *self, u32 speed)
+{
+	unsigned int trx;
+	
+	trx = self->transceiver;
+	if(trx>0) (*smsc_transceivers[trx-1].set_for_speed)(self->io.fir_base, speed);
+}
+
+/*
+ * Function smsc_ircc_wait_hw_transmitter_finish ()
+ *
+ *    Wait for the real end of HW transmission
+ *
+ * The UART is a strict FIFO, and we get called only when we have finished
+ * pushing data to the FIFO, so the maximum amount of time we must wait
+ * is only for the FIFO to drain out.
+ *
+ * We use a simple calibrated loop. We may need to adjust the loop
+ * delay (udelay) to balance I/O traffic and latency. And we also need to
+ * adjust the maximum timeout.
+ * It would probably be better to wait for the proper interrupt,
+ * but it doesn't seem to be available.
+ *
+ * We can't use jiffies or kernel timers because :
+ * 1) We are called from the interrupt handler, which disable softirqs,
+ * so jiffies won't be increased
+ * 2) Jiffies granularity is usually very coarse (10ms), and we don't
+ * want to wait that long to detect stuck hardware.
+ * Jean II
+ */
+
+static void smsc_ircc_sir_wait_hw_transmitter_finish(struct smsc_ircc_cb *self)
+{
+	int iobase;
+	int count = SMSC_IRCC2_HW_TRANSMITTER_TIMEOUT_US;
+	
+	iobase = self->io.sir_base;
+	
+	/* Calibrated busy loop */
+	while((count-- > 0) && !(inb(iobase+UART_LSR) & UART_LSR_TEMT))
+		udelay(1);
+
+	if(count == 0)
+		IRDA_DEBUG(0, "%s(): stuck transmitter\n", __FUNCTION__);
+}
+
+
+/* PROBING
+ *
+ *
+ */
+
+static int __init smsc_ircc_look_for_chips(void)
+{
+	smsc_chip_address_t *address;
+	char	*type;
+	unsigned int cfg_base, found;
+	
+	found = 0;
+	address = possible_addresses;
+	
+	while(address->cfg_base){
+		cfg_base = address->cfg_base;
+		
+		/*printk(KERN_WARNING "%s(): probing: 0x%02x for: 0x%02x\n", __FUNCTION__, cfg_base, address->type);*/
+		
+		if( address->type & SMSCSIO_TYPE_FDC){
+			type = "FDC";
+			if((address->type) & SMSCSIO_TYPE_FLAT) {
+				if(!smsc_superio_flat(fdc_chips_flat,cfg_base, type)) found++;
+			}
+			if((address->type) & SMSCSIO_TYPE_PAGED) {
+				if(!smsc_superio_paged(fdc_chips_paged,cfg_base, type)) found++;		
+			}			
+		}
+		if( address->type & SMSCSIO_TYPE_LPC){
+			type = "LPC";
+			if((address->type) & SMSCSIO_TYPE_FLAT) {
+				if(!smsc_superio_flat(lpc_chips_flat,cfg_base,type)) found++;
+			}
+			if((address->type) & SMSCSIO_TYPE_PAGED) {
+				if(!smsc_superio_paged(lpc_chips_paged,cfg_base,"LPC")) found++;		
+			}			
+		}
+		address++;
+	}
+	return found;
+} 
+
+/*
+ * Function smsc_superio_flat (chip, base, type)
+ *
+ *    Try to get configuration of a smc SuperIO chip with flat register model
+ *
+ */
+static int __init smsc_superio_flat(const smsc_chip_t *chips, unsigned short cfgbase, char *type)
+{
+	unsigned short firbase, sirbase;
+	u8 mode, dma, irq;
+	int ret = -ENODEV;
+
+	IRDA_DEBUG(1, "%s\n", __FUNCTION__);
+
+	if (smsc_ircc_probe(cfgbase, SMSCSIOFLAT_DEVICEID_REG, chips, type)==NULL)
+		return ret;
+
+	outb(SMSCSIOFLAT_UARTMODE0C_REG, cfgbase);
+	mode = inb(cfgbase+1);
+	
+	/*printk(KERN_WARNING "%s(): mode: 0x%02x\n", __FUNCTION__, mode);*/
+	
+	if(!(mode & SMSCSIOFLAT_UART2MODE_VAL_IRDA))
+		IRDA_WARNING("%s(): IrDA not enabled\n", __FUNCTION__);
+
+	outb(SMSCSIOFLAT_UART2BASEADDR_REG, cfgbase);
+	sirbase = inb(cfgbase+1) << 2;
+
+   	/* FIR iobase */
+	outb(SMSCSIOFLAT_FIRBASEADDR_REG, cfgbase);
+	firbase = inb(cfgbase+1) << 3;
+
+	/* DMA */
+	outb(SMSCSIOFLAT_FIRDMASELECT_REG, cfgbase);
+	dma = inb(cfgbase+1) & SMSCSIOFLAT_FIRDMASELECT_MASK;
+	
+	/* IRQ */
+	outb(SMSCSIOFLAT_UARTIRQSELECT_REG, cfgbase);
+	irq = inb(cfgbase+1) & SMSCSIOFLAT_UART2IRQSELECT_MASK;
+
+	IRDA_MESSAGE("%s(): fir: 0x%02x, sir: 0x%02x, dma: %02d, irq: %d, mode: 0x%02x\n", __FUNCTION__, firbase, sirbase, dma, irq, mode);
+
+	if (firbase) {
+		if (smsc_ircc_open(firbase, sirbase, dma, irq) == 0)
+			ret=0; 
+	}
+	
+	/* Exit configuration */
+	outb(SMSCSIO_CFGEXITKEY, cfgbase);
+
+	return ret;
+}
+
+/*
+ * Function smsc_superio_paged (chip, base, type)
+ *
+ *    Try  to get configuration of a smc SuperIO chip with paged register model
+ *
+ */
+static int __init smsc_superio_paged(const smsc_chip_t *chips, unsigned short cfg_base, char *type)
+{
+	unsigned short fir_io, sir_io;
+	int ret = -ENODEV;
+	
+	IRDA_DEBUG(1, "%s\n", __FUNCTION__);
+
+	if (smsc_ircc_probe(cfg_base,0x20,chips,type)==NULL)
+		return ret;
+	
+	/* Select logical device (UART2) */
+	outb(0x07, cfg_base);
+	outb(0x05, cfg_base + 1);
+		
+	/* SIR iobase */
+	outb(0x60, cfg_base);
+	sir_io  = inb(cfg_base + 1) << 8;
+	outb(0x61, cfg_base);
+	sir_io |= inb(cfg_base + 1);
+		
+	/* Read FIR base */
+	outb(0x62, cfg_base);
+	fir_io = inb(cfg_base + 1) << 8;
+	outb(0x63, cfg_base);
+	fir_io |= inb(cfg_base + 1);
+	outb(0x2b, cfg_base); /* ??? */
+
+	if (fir_io) {
+		if (smsc_ircc_open(fir_io, sir_io, ircc_dma, ircc_irq) == 0)
+			ret=0; 
+	}
+	
+	/* Exit configuration */
+	outb(SMSCSIO_CFGEXITKEY, cfg_base);
+
+	return ret;
+}
+
+
+static int __init smsc_access(unsigned short cfg_base,unsigned char reg)
+{
+	IRDA_DEBUG(1, "%s\n", __FUNCTION__);
+
+	outb(reg, cfg_base);
+
+	if (inb(cfg_base)!=reg)
+		return -1;
+
+	return 0;
+}
+
+static const smsc_chip_t * __init smsc_ircc_probe(unsigned short cfg_base,u8 reg,const smsc_chip_t *chip,char *type)
+{
+	u8 devid,xdevid,rev; 
+
+	IRDA_DEBUG(1, "%s\n", __FUNCTION__);
+
+	/* Leave configuration */
+
+	outb(SMSCSIO_CFGEXITKEY, cfg_base);
+
+	if (inb(cfg_base) == SMSCSIO_CFGEXITKEY)	/* not a smc superio chip */
+		return NULL;
+
+	outb(reg, cfg_base);
+
+	xdevid=inb(cfg_base+1);
+
+	/* Enter configuration */
+
+	outb(SMSCSIO_CFGACCESSKEY, cfg_base);
+
+	#if 0
+	if (smsc_access(cfg_base,0x55))	/* send second key and check */
+		return NULL;
+	#endif
+	
+	/* probe device ID */
+
+	if (smsc_access(cfg_base,reg))
+		return NULL;
+
+	devid=inb(cfg_base+1);
+	
+	if (devid==0)			/* typical value for unused port */
+		return NULL;
+
+	if (devid==0xff)		/* typical value for unused port */
+		return NULL;
+
+	/* probe revision ID */
+
+	if (smsc_access(cfg_base,reg+1))
+		return NULL;
+
+	rev=inb(cfg_base+1);
+
+	if (rev>=128)			/* i think this will make no sense */
+		return NULL;
+
+	if (devid==xdevid)		/* protection against false positives */        
+		return NULL;
+
+	/* Check for expected device ID; are there others? */
+
+	while(chip->devid!=devid) {
+
+		chip++;
+
+		if (chip->name==NULL)
+			return NULL;
+	}
+
+	IRDA_MESSAGE("found SMC SuperIO Chip (devid=0x%02x rev=%02X base=0x%04x): %s%s\n",devid,rev,cfg_base,type,chip->name);
+
+	if (chip->rev>rev){
+		IRDA_MESSAGE("Revision higher than expected\n");	
+		return NULL;
+	}
+	
+	if (chip->flags&NoIRDA)
+		IRDA_MESSAGE("chipset does not support IRDA\n");
+
+	return chip;
+}
+
+static int __init smsc_superio_fdc(unsigned short cfg_base)
+{
+	int ret = -1;
+
+	if (!request_region(cfg_base, 2, driver_name)) {
+		IRDA_WARNING("%s: can't get cfg_base of 0x%03x\n",
+			     __FUNCTION__, cfg_base);
+	} else {
+		if (!smsc_superio_flat(fdc_chips_flat,cfg_base,"FDC")
+		    ||!smsc_superio_paged(fdc_chips_paged,cfg_base,"FDC"))
+			ret =  0;
+
+		release_region(cfg_base, 2);
+	}
+
+	return ret;
+}
+
+static int __init smsc_superio_lpc(unsigned short cfg_base)
+{
+	int ret = -1;
+
+	if (!request_region(cfg_base, 2, driver_name)) {
+		IRDA_WARNING("%s: can't get cfg_base of 0x%03x\n",
+			     __FUNCTION__, cfg_base);
+	} else {
+		if (!smsc_superio_flat(lpc_chips_flat,cfg_base,"LPC")
+		    ||!smsc_superio_paged(lpc_chips_paged,cfg_base,"LPC"))
+			ret = 0;
+		release_region(cfg_base, 2);
+	}
+	return ret;
+}
+
+/************************************************
+ *
+ * Transceivers specific functions
+ *
+ ************************************************/
+
+
+/*
+ * Function smsc_ircc_set_transceiver_smsc_ircc_atc(fir_base, speed)
+ *
+ *    Program transceiver through smsc-ircc ATC circuitry
+ *
+ */
+
+static void smsc_ircc_set_transceiver_smsc_ircc_atc(int fir_base, u32 speed)
+{
+	unsigned long jiffies_now, jiffies_timeout;
+	u8	val;
+	
+	jiffies_now= jiffies;
+	jiffies_timeout= jiffies+SMSC_IRCC2_ATC_PROGRAMMING_TIMEOUT_JIFFIES;
+	
+	/* ATC */
+	register_bank(fir_base, 4);
+	outb((inb(fir_base+IRCC_ATC) & IRCC_ATC_MASK) |IRCC_ATC_nPROGREADY|IRCC_ATC_ENABLE, fir_base+IRCC_ATC);
+	while((val=(inb(fir_base+IRCC_ATC) & IRCC_ATC_nPROGREADY)) && !time_after(jiffies, jiffies_timeout));
+	if(val)
+		IRDA_WARNING("%s(): ATC: 0x%02x\n", __FUNCTION__,
+			     inb(fir_base+IRCC_ATC));
+}
+
+/*
+ * Function smsc_ircc_probe_transceiver_smsc_ircc_atc(fir_base)
+ *
+ *    Probe transceiver smsc-ircc ATC circuitry
+ *
+ */
+
+static int smsc_ircc_probe_transceiver_smsc_ircc_atc(int fir_base)
+{
+	return 0;
+}
+
+/*
+ * Function smsc_ircc_set_transceiver_smsc_ircc_fast_pin_select(self, speed)
+ *
+ *    Set transceiver 
+ *
+ */
+
+static void smsc_ircc_set_transceiver_smsc_ircc_fast_pin_select(int fir_base, u32 speed)
+{
+	u8	fast_mode;
+	
+	switch(speed)
+	{
+		default:
+		case 576000 :
+		fast_mode = 0; 
+		break;
+		case 1152000 :
+		case 4000000 :
+		fast_mode = IRCC_LCR_A_FAST;
+		break;
+		
+	}
+	register_bank(fir_base, 0);
+	outb((inb(fir_base+IRCC_LCR_A) &  0xbf) | fast_mode, fir_base+IRCC_LCR_A);
+}
+
+/*
+ * Function smsc_ircc_probe_transceiver_smsc_ircc_fast_pin_select(fir_base)
+ *
+ *    Probe transceiver 
+ *
+ */
+
+static int smsc_ircc_probe_transceiver_smsc_ircc_fast_pin_select(int fir_base)
+{
+	return 0;
+}
+
+/*
+ * Function smsc_ircc_set_transceiver_toshiba_sat1800(fir_base, speed)
+ *
+ *    Set transceiver 
+ *
+ */
+
+static void smsc_ircc_set_transceiver_toshiba_sat1800(int fir_base, u32 speed)
+{
+	u8	fast_mode;
+	
+	switch(speed)
+	{
+		default:
+		case 576000 :
+		fast_mode = 0; 
+		break;
+		case 1152000 :
+		case 4000000 :
+		fast_mode = /*IRCC_LCR_A_FAST |*/ IRCC_LCR_A_GP_DATA;
+		break;
+		
+	}
+	/* This causes an interrupt */
+	register_bank(fir_base, 0);
+	outb((inb(fir_base+IRCC_LCR_A) &  0xbf) | fast_mode, fir_base+IRCC_LCR_A);
+}
+
+/*
+ * Function smsc_ircc_probe_transceiver_toshiba_sat1800(fir_base)
+ *
+ *    Probe transceiver 
+ *
+ */
+
+static int smsc_ircc_probe_transceiver_toshiba_sat1800(int fir_base)
+{
+	return 0;
+}
+
+
+module_init(smsc_ircc_init);
+module_exit(smsc_ircc_cleanup);
+
+MODULE_AUTHOR("Daniele Peri <peri@csai.unipa.it>");
+MODULE_DESCRIPTION("SMC IrCC SIR/FIR controller driver");
+MODULE_LICENSE("GPL");
+
+module_param(ircc_dma, int, 0);
+MODULE_PARM_DESC(ircc_dma, "DMA channel");
+module_param(ircc_irq, int, 0);
+MODULE_PARM_DESC(ircc_irq, "IRQ line");
+module_param(ircc_fir, int, 0);
+MODULE_PARM_DESC(ircc_fir, "FIR Base Address");
+module_param(ircc_sir, int, 0);
+MODULE_PARM_DESC(ircc_sir, "SIR Base Address");
+module_param(ircc_cfg, int, 0);
+MODULE_PARM_DESC(ircc_cfg, "Configuration register base address");
+module_param(ircc_transceiver, int, 0);
+MODULE_PARM_DESC(ircc_transceiver, "Transceiver type");
diff --git a/drivers/net/irda/smsc-ircc2.h b/drivers/net/irda/smsc-ircc2.h
new file mode 100644
index 0000000..458611c
--- /dev/null
+++ b/drivers/net/irda/smsc-ircc2.h
@@ -0,0 +1,194 @@
+/*********************************************************************
+ * $Id: smsc-ircc2.h,v 1.12.2.1 2002/10/27 10:52:37 dip Exp $               
+ *
+ * Description:   Definitions for the SMC IrCC chipset
+ * Status:        Experimental.
+ * Author:        Daniele Peri (peri@csai.unipa.it)
+ *
+ *     Copyright (c) 2002      Daniele Peri
+ *     All Rights Reserved.
+ *
+ * Based on smc-ircc.h:
+ * 
+ *     Copyright (c) 1999-2000, Dag Brattli <dagb@cs.uit.no>
+ *     Copyright (c) 1998-1999, Thomas Davis (tadavis@jps.net>
+ *     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
+ *
+ ********************************************************************/
+
+#ifndef SMSC_IRCC2_H
+#define SMSC_IRCC2_H
+
+/* DMA modes needed */
+#define DMA_TX_MODE                0x08    /* Mem to I/O, ++, demand. */
+#define DMA_RX_MODE                0x04    /* I/O to mem, ++, demand. */
+
+/* Master Control Register */
+#define IRCC_MASTER                0x07
+#define   IRCC_MASTER_POWERDOWN	   0x80
+#define   IRCC_MASTER_RESET        0x40
+#define   IRCC_MASTER_INT_EN       0x20
+#define   IRCC_MASTER_ERROR_RESET  0x10
+
+/* Register block 0 */
+
+/* Interrupt Identification */
+#define IRCC_IIR					0x01
+#define   IRCC_IIR_ACTIVE_FRAME		0x80
+#define   IRCC_IIR_EOM				0x40
+#define   IRCC_IIR_RAW_MODE			0x20
+#define   IRCC_IIR_FIFO				0x10
+
+/* Interrupt Enable */
+#define IRCC_IER					0x02
+#define   IRCC_IER_ACTIVE_FRAME		0x80
+#define   IRCC_IER_EOM				0x40
+#define   IRCC_IER_RAW_MODE			0x20
+#define   IRCC_IER_FIFO				0x10
+
+/* Line Status Register */
+#define IRCC_LSR					0x03
+#define   IRCC_LSR_UNDERRUN			0x80
+#define   IRCC_LSR_OVERRUN			0x40
+#define   IRCC_LSR_FRAME_ERROR		0x20
+#define   IRCC_LSR_SIZE_ERROR		0x10
+#define   IRCC_LSR_CRC_ERROR		0x80
+#define   IRCC_LSR_FRAME_ABORT		0x40
+
+/* Line Status Address Register */
+#define IRCC_LSAR					0x03
+#define IRCC_LSAR_ADDRESS_MASK		0x07
+
+/* Line Control Register A */
+#define IRCC_LCR_A                 0x04
+#define   IRCC_LCR_A_FIFO_RESET    0x80
+#define   IRCC_LCR_A_FAST          0x40
+#define   IRCC_LCR_A_GP_DATA       0x20
+#define   IRCC_LCR_A_RAW_TX        0x10
+#define   IRCC_LCR_A_RAW_RX        0x08
+#define   IRCC_LCR_A_ABORT         0x04
+#define   IRCC_LCR_A_DATA_DONE     0x02
+
+/* Line Control Register B */
+#define IRCC_LCR_B                 0x05
+#define   IRCC_LCR_B_SCE_DISABLED  0x00
+#define   IRCC_LCR_B_SCE_TRANSMIT  0x40
+#define   IRCC_LCR_B_SCE_RECEIVE   0x80
+#define   IRCC_LCR_B_SCE_UNDEFINED 0xc0
+#define   IRCC_LCR_B_SIP_ENABLE	   0x20
+#define   IRCC_LCR_B_BRICK_WALL    0x10
+
+/* Bus Status Register */
+#define IRCC_BSR                   0x06
+#define   IRCC_BSR_NOT_EMPTY	   0x80
+#define   IRCC_BSR_FIFO_FULL	   0x40
+#define   IRCC_BSR_TIMEOUT	   0x20
+
+/* Register block 1 */
+
+#define IRCC_FIFO_THRESHOLD			0x02
+
+#define IRCC_SCE_CFGA				0x00
+#define   IRCC_CFGA_AUX_IR			0x80
+#define   IRCC_CFGA_HALF_DUPLEX		0x04
+#define   IRCC_CFGA_TX_POLARITY		0x02
+#define   IRCC_CFGA_RX_POLARITY		0x01
+
+#define   IRCC_CFGA_COM				0x00
+#define		IRCC_SCE_CFGA_BLOCK_CTRL_BITS_MASK	0x87
+#define   	IRCC_CFGA_IRDA_SIR_A	0x08
+#define   	IRCC_CFGA_ASK_SIR		0x10
+#define   	IRCC_CFGA_IRDA_SIR_B	0x18
+#define   	IRCC_CFGA_IRDA_HDLC		0x20
+#define		IRCC_CFGA_IRDA_4PPM		0x28
+#define		IRCC_CFGA_CONSUMER		0x30
+#define		IRCC_CFGA_RAW_IR		0x38
+#define     IRCC_CFGA_OTHER			0x40
+
+#define IRCC_IR_HDLC               0x04
+#define IRCC_IR_4PPM               0x01
+#define IRCC_IR_CONSUMER           0x02
+
+#define IRCC_SCE_CFGB	           0x01
+#define IRCC_CFGB_LOOPBACK         0x20
+#define IRCC_CFGB_LPBCK_TX_CRC	   0x10
+#define IRCC_CFGB_NOWAIT	   0x08
+#define IRCC_CFGB_STRING_MOVE	   0x04
+#define IRCC_CFGB_DMA_BURST 	   0x02
+#define IRCC_CFGB_DMA_ENABLE	   0x01
+
+#define IRCC_CFGB_MUX_COM          0x00
+#define IRCC_CFGB_MUX_IR           0x40
+#define IRCC_CFGB_MUX_AUX          0x80
+#define IRCC_CFGB_MUX_INACTIVE	   0xc0
+
+/* Register block 3 - Identification Registers! */
+#define IRCC_ID_HIGH	           0x00   /* 0x10 */
+#define IRCC_ID_LOW	           0x01   /* 0xB8 */
+#define IRCC_CHIP_ID 	           0x02   /* 0xF1 */
+#define IRCC_VERSION	           0x03   /* 0x01 */
+#define IRCC_INTERFACE	           0x04   /* low 4 = DMA, high 4 = IRQ */
+#define 	IRCC_INTERFACE_DMA_MASK	0x0F   /* low 4 = DMA, high 4 = IRQ */
+#define 	IRCC_INTERFACE_IRQ_MASK	0xF0   /* low 4 = DMA, high 4 = IRQ */
+
+/* Register block 4 - IrDA */
+#define IRCC_CONTROL               0x00
+#define IRCC_BOF_COUNT_LO          0x01 /* Low byte */
+#define IRCC_BOF_COUNT_HI          0x00 /* High nibble (bit 0-3) */
+#define IRCC_BRICKWALL_CNT_LO      0x02 /* Low byte */
+#define IRCC_BRICKWALL_CNT_HI      0x03 /* High nibble (bit 4-7) */
+#define IRCC_TX_SIZE_LO            0x04 /* Low byte */
+#define IRCC_TX_SIZE_HI            0x03 /* High nibble (bit 0-3) */
+#define IRCC_RX_SIZE_HI            0x05 /* High nibble (bit 0-3) */
+#define IRCC_RX_SIZE_LO            0x06 /* Low byte */
+
+#define IRCC_1152                  0x80
+#define IRCC_CRC                   0x40
+
+/* Register block 5 - IrDA */
+#define IRCC_ATC					0x00
+#define 	IRCC_ATC_nPROGREADY		0x80
+#define 	IRCC_ATC_SPEED			0x40
+#define 	IRCC_ATC_ENABLE			0x20
+#define 	IRCC_ATC_MASK			0xE0
+
+
+#define IRCC_IRHALFDUPLEX_TIMEOUT	0x01
+
+#define IRCC_SCE_TX_DELAY_TIMER		0x02
+
+/*
+ * Other definitions
+ */
+
+#define SMSC_IRCC2_MAX_SIR_SPEED		115200
+#define SMSC_IRCC2_FIR_CHIP_IO_EXTENT 	8
+#define SMSC_IRCC2_SIR_CHIP_IO_EXTENT 	8
+#define SMSC_IRCC2_FIFO_SIZE			16
+#define SMSC_IRCC2_FIFO_THRESHOLD		64
+/* Max DMA buffer size needed = (data_size + 6) * (window_size) + 6; */
+#define SMSC_IRCC2_RX_BUFF_TRUESIZE		14384
+#define SMSC_IRCC2_TX_BUFF_TRUESIZE		14384
+#define SMSC_IRCC2_MIN_TURN_TIME		0x07
+#define SMSC_IRCC2_WINDOW_SIZE			0x07
+/* Maximum wait for hw transmitter to finish */
+#define SMSC_IRCC2_HW_TRANSMITTER_TIMEOUT_US	1000	/* 1 ms */
+/* Maximum wait for ATC transceiver programming to finish */
+#define SMSC_IRCC2_ATC_PROGRAMMING_TIMEOUT_JIFFIES 1
+#endif /* SMSC_IRCC2_H */
diff --git a/drivers/net/irda/smsc-sio.h b/drivers/net/irda/smsc-sio.h
new file mode 100644
index 0000000..59e20e6
--- /dev/null
+++ b/drivers/net/irda/smsc-sio.h
@@ -0,0 +1,100 @@
+#ifndef SMSC_SIO_H
+#define SMSC_SIO_H
+
+/******************************************
+ Keys. They should work with every SMsC SIO
+ ******************************************/
+
+#define SMSCSIO_CFGACCESSKEY		0x55
+#define SMSCSIO_CFGEXITKEY			0xaa
+
+/*****************************
+ * Generic SIO Flat (!?)     *
+ *****************************/
+ 
+/* Register 0x0d */
+#define SMSCSIOFLAT_DEVICEID_REG				0x0d
+
+/* Register 0x0c */
+#define SMSCSIOFLAT_UARTMODE0C_REG				0x0c
+#define 	SMSCSIOFLAT_UART2MODE_MASK			0x38
+#define 	SMSCSIOFLAT_UART2MODE_VAL_COM		0x00
+#define 	SMSCSIOFLAT_UART2MODE_VAL_IRDA		0x08
+#define 	SMSCSIOFLAT_UART2MODE_VAL_ASKIR		0x10
+
+/* Register 0x25 */
+#define SMSCSIOFLAT_UART2BASEADDR_REG			0x25
+
+/* Register 0x2b */
+#define SMSCSIOFLAT_FIRBASEADDR_REG				0x2b
+
+/* Register 0x2c */
+#define SMSCSIOFLAT_FIRDMASELECT_REG			0x2c
+#define 	SMSCSIOFLAT_FIRDMASELECT_MASK		0x0f
+
+/* Register 0x28 */
+#define SMSCSIOFLAT_UARTIRQSELECT_REG			0x28
+#define 	SMSCSIOFLAT_UART2IRQSELECT_MASK		0x0f
+#define 	SMSCSIOFLAT_UART1IRQSELECT_MASK		0xf0
+#define 	SMSCSIOFLAT_UARTIRQSELECT_VAL_NONE	0x00
+
+
+/*********************
+ * LPC47N227         *
+ *********************/
+
+#define LPC47N227_CFGACCESSKEY		0x55
+#define LPC47N227_CFGEXITKEY		0xaa
+
+/* Register 0x00 */
+#define LPC47N227_FDCPOWERVALIDCONF_REG		0x00
+#define 	LPC47N227_FDCPOWER_MASK			0x08
+#define 	LPC47N227_VALID_MASK				0x80
+
+/* Register 0x02 */
+#define LPC47N227_UART12POWER_REG				0x02
+#define 	LPC47N227_UART1POWERDOWN_MASK		0x08
+#define 	LPC47N227_UART2POWERDOWN_MASK		0x80
+
+/* Register 0x07 */
+#define LPC47N227_APMBOOTDRIVE_REG				0x07
+#define 	LPC47N227_PARPORT2AUTOPWRDOWN_MASK	0x10 /* auto power down on if set */
+#define 	LPC47N227_UART2AUTOPWRDOWN_MASK	0x20 /* auto power down on if set */
+#define 	LPC47N227_UART1AUTOPWRDOWN_MASK	0x40 /* auto power down on if set */
+
+/* Register 0x0c */
+#define LPC47N227_UARTMODE0C_REG				0x0c
+#define 	LPC47N227_UART2MODE_MASK			0x38
+#define 	LPC47N227_UART2MODE_VAL_COM		0x00
+#define 	LPC47N227_UART2MODE_VAL_IRDA		0x08
+#define 	LPC47N227_UART2MODE_VAL_ASKIR		0x10
+
+/* Register 0x0d */
+#define LPC47N227_DEVICEID_REG					0x0d
+#define 	LPC47N227_DEVICEID_DEFVAL			0x5a
+
+/* Register 0x0e */
+#define LPC47N227_REVISIONID_REG				0x0e
+
+/* Register 0x25 */
+#define LPC47N227_UART2BASEADDR_REG			0x25
+
+/* Register 0x28 */
+#define LPC47N227_UARTIRQSELECT_REG			0x28
+#define 	LPC47N227_UART2IRQSELECT_MASK		0x0f
+#define 	LPC47N227_UART1IRQSELECT_MASK		0xf0
+#define 	LPC47N227_UARTIRQSELECT_VAL_NONE	0x00
+
+/* Register 0x2b */
+#define LPC47N227_FIRBASEADDR_REG				0x2b
+
+/* Register 0x2c */
+#define LPC47N227_FIRDMASELECT_REG				0x2c
+#define 	LPC47N227_FIRDMASELECT_MASK		0x0f
+#define 	LPC47N227_FIRDMASELECT_VAL_DMA1	0x01 /* 47n227 has three dma channels */
+#define 	LPC47N227_FIRDMASELECT_VAL_DMA2	0x02
+#define 	LPC47N227_FIRDMASELECT_VAL_DMA3	0x03
+#define 	LPC47N227_FIRDMASELECT_VAL_NONE	0x0f
+
+
+#endif
diff --git a/drivers/net/irda/stir4200.c b/drivers/net/irda/stir4200.c
new file mode 100644
index 0000000..83c605e
--- /dev/null
+++ b/drivers/net/irda/stir4200.c
@@ -0,0 +1,1184 @@
+/*****************************************************************************
+*
+* Filename:      stir4200.c
+* Version:       0.4
+* Description:   Irda SigmaTel USB Dongle
+* Status:        Experimental
+* Author:        Stephen Hemminger <shemminger@osdl.org>
+*
+*  	Based on earlier driver by Paul Stewart <stewart@parc.com>
+*
+*	Copyright (C) 2000, Roman Weissgaerber <weissg@vienna.at>
+*	Copyright (C) 2001, Dag Brattli <dag@brattli.net>
+*	Copyright (C) 2001, Jean Tourrilhes <jt@hpl.hp.com>
+*	Copyright (C) 2004, Stephen Hemminger <shemminger@osdl.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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*
+*****************************************************************************/
+
+/*
+ * This dongle does no framing, and requires polling to receive the
+ * data.  The STIr4200 has bulk in and out endpoints just like
+ * usr-irda devices, but the data it sends and receives is raw; like
+ * irtty, it needs to call the wrap and unwrap functions to add and
+ * remove SOF/BOF and escape characters to/from the frame.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/time.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/usb.h>
+#include <linux/crc32.h>
+#include <net/irda/irda.h>
+#include <net/irda/irlap.h>
+#include <net/irda/irda_device.h>
+#include <net/irda/wrapper.h>
+#include <net/irda/crc.h>
+#include <asm/byteorder.h>
+#include <asm/unaligned.h>
+
+MODULE_AUTHOR("Stephen Hemminger <shemminger@osdl.org>");
+MODULE_DESCRIPTION("IrDA-USB Dongle Driver for SigmaTel STIr4200");
+MODULE_LICENSE("GPL");
+
+static int qos_mtt_bits = 0x07;	/* 1 ms or more */
+module_param(qos_mtt_bits, int, 0);
+MODULE_PARM_DESC(qos_mtt_bits, "Minimum Turn Time");
+
+static int rx_sensitivity = 1;	/* FIR 0..4, SIR 0..6 */
+module_param(rx_sensitivity, int, 0);
+MODULE_PARM_DESC(rx_sensitivity, "Set Receiver sensitivity (0-6, 0 is most sensitive)");
+
+static int tx_power = 0;	/* 0 = highest ... 3 = lowest */
+module_param(tx_power, int, 0);
+MODULE_PARM_DESC(tx_power, "Set Transmitter power (0-3, 0 is highest power)");
+
+#define STIR_IRDA_HEADER  	4
+#define CTRL_TIMEOUT		100	   /* milliseconds */
+#define TRANSMIT_TIMEOUT	200	   /* milliseconds */
+#define STIR_FIFO_SIZE		4096
+#define FIFO_REGS_SIZE		3
+
+enum FirChars {
+	FIR_CE   = 0x7d,
+	FIR_XBOF = 0x7f,
+	FIR_EOF  = 0x7e,
+};
+
+enum StirRequests {
+	REQ_WRITE_REG =		0x00,
+	REQ_READ_REG =		0x01,
+	REQ_READ_ROM =		0x02,
+	REQ_WRITE_SINGLE =	0x03,
+};
+
+/* Register offsets */
+enum StirRegs {
+	REG_RSVD=0,
+	REG_MODE,
+	REG_PDCLK,
+	REG_CTRL1,
+	REG_CTRL2,
+	REG_FIFOCTL,
+	REG_FIFOLSB,
+	REG_FIFOMSB,
+	REG_DPLL,
+	REG_IRDIG,
+	REG_TEST=15,
+};
+
+enum StirModeMask {
+	MODE_FIR = 0x80,
+	MODE_SIR = 0x20,
+	MODE_ASK = 0x10,
+	MODE_FASTRX = 0x08,
+	MODE_FFRSTEN = 0x04,
+	MODE_NRESET = 0x02,
+	MODE_2400 = 0x01,
+};
+
+enum StirPdclkMask {
+	PDCLK_4000000 = 0x02,
+	PDCLK_115200 = 0x09,
+	PDCLK_57600 = 0x13,
+	PDCLK_38400 = 0x1D,
+	PDCLK_19200 = 0x3B,
+	PDCLK_9600 = 0x77,
+	PDCLK_2400 = 0xDF,
+};
+
+enum StirCtrl1Mask {
+	CTRL1_SDMODE = 0x80,
+	CTRL1_RXSLOW = 0x40,
+	CTRL1_TXPWD = 0x10,
+	CTRL1_RXPWD = 0x08,
+	CTRL1_SRESET = 0x01,
+};
+
+enum StirCtrl2Mask {
+	CTRL2_SPWIDTH = 0x08,
+	CTRL2_REVID = 0x03,
+};
+
+enum StirFifoCtlMask {
+	FIFOCTL_EOF = 0x80,
+	FIFOCTL_UNDER = 0x40,
+	FIFOCTL_OVER = 0x20,
+	FIFOCTL_DIR = 0x10,
+	FIFOCTL_CLR = 0x08,
+	FIFOCTL_EMPTY = 0x04,
+	FIFOCTL_RXERR = 0x02,
+	FIFOCTL_TXERR = 0x01,
+};
+
+enum StirDiagMask {
+	IRDIG_RXHIGH = 0x80,
+	IRDIG_RXLOW = 0x40,
+};
+
+enum StirTestMask {
+	TEST_PLLDOWN = 0x80,
+	TEST_LOOPIR = 0x40,
+	TEST_LOOPUSB = 0x20,
+	TEST_TSTENA = 0x10,
+	TEST_TSTOSC = 0x0F,
+};
+
+struct stir_cb {
+        struct usb_device *usbdev;      /* init: probe_irda */
+        struct net_device *netdev;      /* network layer */
+        struct irlap_cb   *irlap;       /* The link layer we are binded to */
+        struct net_device_stats stats;	/* network statistics */
+        struct qos_info   qos;
+	unsigned 	  speed;	/* Current speed */
+
+	wait_queue_head_t thr_wait;	/* transmit thread wakeup */
+	struct completion thr_exited;
+	pid_t		  thr_pid;
+
+	struct sk_buff	  *tx_pending;
+	void		  *io_buf;	/* transmit/receive buffer */
+	__u8		  *fifo_status;
+
+	iobuff_t  	  rx_buff;	/* receive unwrap state machine */
+	struct timeval	  rx_time;
+	int		  receiving;
+	struct urb	 *rx_urb;
+};
+
+
+/* These are the currently known USB ids */
+static struct usb_device_id dongles[] = {
+    /* SigmaTel, Inc,  STIr4200 IrDA/USB Bridge */
+    { USB_DEVICE(0x066f, 0x4200) },
+    { }
+};
+
+MODULE_DEVICE_TABLE(usb, dongles);
+
+/* Send control message to set dongle register */
+static int write_reg(struct stir_cb *stir, __u16 reg, __u8 value)
+{
+	struct usb_device *dev = stir->usbdev;
+
+	pr_debug("%s: write reg %d = 0x%x\n",
+		 stir->netdev->name, reg, value);
+	return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+			       REQ_WRITE_SINGLE,
+			       USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_DEVICE,
+			       value, reg, NULL, 0,
+			       CTRL_TIMEOUT);
+}
+
+/* Send control message to read multiple registers */
+static inline int read_reg(struct stir_cb *stir, __u16 reg,
+		    __u8 *data, __u16 count)
+{
+	struct usb_device *dev = stir->usbdev;
+
+	return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+			       REQ_READ_REG,
+			       USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+			       0, reg, data, count,
+			       CTRL_TIMEOUT);
+}
+
+static inline int isfir(u32 speed)
+{
+	return (speed == 4000000);
+}
+
+/*
+ * Prepare a FIR IrDA frame for transmission to the USB dongle.  The
+ * FIR transmit frame is documented in the datasheet.  It consists of
+ * a two byte 0x55 0xAA sequence, two little-endian length bytes, a
+ * sequence of exactly 16 XBOF bytes of 0x7E, two BOF bytes of 0x7E,
+ * then the data escaped as follows:
+ *
+ *    0x7D -> 0x7D 0x5D
+ *    0x7E -> 0x7D 0x5E
+ *    0x7F -> 0x7D 0x5F
+ *
+ * Then, 4 bytes of little endian (stuffed) FCS follow, then two
+ * trailing EOF bytes of 0x7E.
+ */
+static inline __u8 *stuff_fir(__u8 *p, __u8 c)
+{
+	switch(c) {
+	case 0x7d:
+	case 0x7e:
+	case 0x7f:
+		*p++ = 0x7d;
+		c ^= IRDA_TRANS;
+		/* fall through */
+	default:
+		*p++ = c;
+	}
+	return p;
+}
+
+/* Take raw data in skb and put it wrapped into buf */
+static unsigned wrap_fir_skb(const struct sk_buff *skb, __u8 *buf)
+{
+	__u8 *ptr = buf;
+	__u32 fcs = ~(crc32_le(~0, skb->data, skb->len));
+	__u16 wraplen;
+	int i;
+
+	/* Header */
+	buf[0] = 0x55;
+	buf[1] = 0xAA;
+
+	ptr = buf + STIR_IRDA_HEADER;
+	memset(ptr, 0x7f, 16);
+	ptr += 16;
+
+	/* BOF */
+	*ptr++  = 0x7e;
+	*ptr++  = 0x7e;
+
+	/* Address / Control / Information */
+	for (i = 0; i < skb->len; i++)
+		ptr = stuff_fir(ptr, skb->data[i]);
+
+	/* FCS */
+	ptr = stuff_fir(ptr, fcs & 0xff);
+	ptr = stuff_fir(ptr, (fcs >> 8) & 0xff);
+	ptr = stuff_fir(ptr, (fcs >> 16) & 0xff);
+	ptr = stuff_fir(ptr, (fcs >> 24) & 0xff);
+
+	/* EOFs */
+	*ptr++ = 0x7e;
+	*ptr++ = 0x7e;
+
+	/* Total length, minus the header */
+	wraplen = (ptr - buf) - STIR_IRDA_HEADER;
+	buf[2] = wraplen & 0xff;
+	buf[3] = (wraplen >> 8) & 0xff;
+
+	return wraplen + STIR_IRDA_HEADER;
+}
+
+static unsigned wrap_sir_skb(struct sk_buff *skb, __u8 *buf)
+{
+	__u16 wraplen;
+
+	wraplen = async_wrap_skb(skb, buf + STIR_IRDA_HEADER,
+				 STIR_FIFO_SIZE - STIR_IRDA_HEADER);
+	buf[0] = 0x55;
+	buf[1] = 0xAA;
+	buf[2] = wraplen & 0xff;
+	buf[3] = (wraplen >> 8) & 0xff;
+
+	return wraplen + STIR_IRDA_HEADER;
+}
+
+/*
+ * Frame is fully formed in the rx_buff so check crc
+ * and pass up to irlap
+ * setup for next receive
+ */
+static void fir_eof(struct stir_cb *stir)
+{
+	iobuff_t *rx_buff = &stir->rx_buff;
+	int len = rx_buff->len - 4;
+	struct sk_buff *skb, *nskb;
+	__u32 fcs;
+
+	if (unlikely(len <= 0)) {
+		pr_debug("%s: short frame len %d\n",
+			 stir->netdev->name, len);
+
+		++stir->stats.rx_errors;
+		++stir->stats.rx_length_errors;
+		return;
+	}
+
+	fcs = ~(crc32_le(~0, rx_buff->data, len));
+	if (fcs != le32_to_cpu(get_unaligned((u32 *)(rx_buff->data+len)))) {
+		pr_debug("crc error calc 0x%x len %d\n", fcs, len);
+		stir->stats.rx_errors++;
+		stir->stats.rx_crc_errors++;
+		return;
+	}
+
+	/* if frame is short then just copy it */
+	if (len < IRDA_RX_COPY_THRESHOLD) {
+		nskb = dev_alloc_skb(len + 1);
+		if (unlikely(!nskb)) {
+			++stir->stats.rx_dropped;
+			return;
+		}
+		skb_reserve(nskb, 1);
+		skb = nskb;
+		memcpy(nskb->data, rx_buff->data, len);
+	} else {
+		nskb = dev_alloc_skb(rx_buff->truesize);
+		if (unlikely(!nskb)) {
+			++stir->stats.rx_dropped;
+			return;
+		}
+		skb_reserve(nskb, 1);
+		skb = rx_buff->skb;
+		rx_buff->skb = nskb;
+		rx_buff->head = nskb->data;
+	}
+
+	skb_put(skb, len);
+
+	skb->mac.raw  = skb->data;
+	skb->protocol = htons(ETH_P_IRDA);
+	skb->dev = stir->netdev;
+
+	netif_rx(skb);
+
+	stir->stats.rx_packets++;
+	stir->stats.rx_bytes += len;
+
+	rx_buff->data = rx_buff->head;
+	rx_buff->len = 0;
+}
+
+/* Unwrap FIR stuffed data and bump it to IrLAP */
+static void stir_fir_chars(struct stir_cb *stir,
+			    const __u8 *bytes, int len)
+{
+	iobuff_t *rx_buff = &stir->rx_buff;
+	int	i;
+
+	for (i = 0; i < len; i++) {
+		__u8	byte = bytes[i];
+
+		switch(rx_buff->state) {
+		case OUTSIDE_FRAME:
+			/* ignore garbage till start of frame */
+			if (unlikely(byte != FIR_EOF))
+				continue;
+			/* Now receiving frame */
+			rx_buff->state = BEGIN_FRAME;
+
+			/* Time to initialize receive buffer */
+			rx_buff->data = rx_buff->head;
+			rx_buff->len = 0;
+			continue;
+
+		case LINK_ESCAPE:
+			if (byte == FIR_EOF) {
+				pr_debug("%s: got EOF after escape\n",
+					 stir->netdev->name);
+				goto frame_error;
+			}
+			rx_buff->state = INSIDE_FRAME;
+			byte ^= IRDA_TRANS;
+			break;
+
+		case BEGIN_FRAME:
+			/* ignore multiple BOF/EOF */
+			if (byte == FIR_EOF)
+				continue;
+			rx_buff->state = INSIDE_FRAME;
+			rx_buff->in_frame = TRUE;
+
+			/* fall through */
+		case INSIDE_FRAME:
+			switch(byte) {
+			case FIR_CE:
+				rx_buff->state = LINK_ESCAPE;
+				continue;
+			case FIR_XBOF:
+				/* 0x7f is not used in this framing */
+				pr_debug("%s: got XBOF without escape\n",
+					 stir->netdev->name);
+				goto frame_error;
+			case FIR_EOF:
+				rx_buff->state = OUTSIDE_FRAME;
+				rx_buff->in_frame = FALSE;
+				fir_eof(stir);
+				continue;
+			}
+			break;
+		}
+
+		/* add byte to rx buffer */
+		if (unlikely(rx_buff->len >= rx_buff->truesize)) {
+			pr_debug("%s: fir frame exceeds %d\n",
+				 stir->netdev->name, rx_buff->truesize);
+			++stir->stats.rx_over_errors;
+			goto error_recovery;
+		}
+
+		rx_buff->data[rx_buff->len++] = byte;
+		continue;
+
+	frame_error:
+		++stir->stats.rx_frame_errors;
+
+	error_recovery:
+		++stir->stats.rx_errors;
+		rx_buff->state = OUTSIDE_FRAME;
+		rx_buff->in_frame = FALSE;
+	}
+}
+
+/* Unwrap SIR stuffed data and bump it up to IrLAP */
+static void stir_sir_chars(struct stir_cb *stir,
+			    const __u8 *bytes, int len)
+{
+	int i;
+
+	for (i = 0; i < len; i++)
+		async_unwrap_char(stir->netdev, &stir->stats,
+				  &stir->rx_buff, bytes[i]);
+}
+
+static inline void unwrap_chars(struct stir_cb *stir,
+				const __u8 *bytes, int length)
+{
+	if (isfir(stir->speed))
+		stir_fir_chars(stir, bytes, length);
+	else
+		stir_sir_chars(stir, bytes, length);
+}
+
+/* Mode parameters for each speed */
+static const struct {
+	unsigned speed;
+	__u8 pdclk;
+} stir_modes[] = {
+        { 2400,    PDCLK_2400 },
+        { 9600,    PDCLK_9600 },
+        { 19200,   PDCLK_19200 },
+        { 38400,   PDCLK_38400 },
+        { 57600,   PDCLK_57600 },
+        { 115200,  PDCLK_115200 },
+        { 4000000, PDCLK_4000000 },
+};
+
+
+/*
+ * Setup chip for speed.
+ *  Called at startup to initialize the chip
+ *  and on speed changes.
+ *
+ * Note: Write multiple registers doesn't appear to work
+ */
+static int change_speed(struct stir_cb *stir, unsigned speed)
+{
+	int i, err;
+	__u8 mode;
+
+	for (i = 0; i < ARRAY_SIZE(stir_modes); ++i) {
+		if (speed == stir_modes[i].speed)
+			goto found;
+	}
+
+	warn("%s: invalid speed %d", stir->netdev->name, speed);
+	return -EINVAL;
+
+ found:
+	pr_debug("speed change from %d to %d\n", stir->speed, speed);
+
+	/* Reset modulator */
+	err = write_reg(stir, REG_CTRL1, CTRL1_SRESET);
+	if (err)
+		goto out;
+
+	/* Undocumented magic to tweak the DPLL */
+	err = write_reg(stir, REG_DPLL, 0x15);
+	if (err)
+		goto out;
+
+	/* Set clock */
+	err = write_reg(stir, REG_PDCLK, stir_modes[i].pdclk);
+	if (err)
+		goto out;
+
+	mode = MODE_NRESET | MODE_FASTRX;
+	if (isfir(speed))
+		mode |= MODE_FIR | MODE_FFRSTEN;
+	else
+		mode |= MODE_SIR;
+
+	if (speed == 2400)
+		mode |= MODE_2400;
+
+	err = write_reg(stir, REG_MODE, mode);
+	if (err)
+		goto out;
+
+	/* This resets TEMIC style transceiver if any. */
+	err = write_reg(stir, REG_CTRL1,
+			CTRL1_SDMODE | (tx_power & 3) << 1);
+	if (err)
+		goto out;
+
+	err = write_reg(stir, REG_CTRL1, (tx_power & 3) << 1);
+	if (err)
+		goto out;
+
+	/* Reset sensitivity */
+	err = write_reg(stir, REG_CTRL2, (rx_sensitivity & 7) << 5);
+ out:
+	stir->speed = speed;
+	return err;
+}
+
+/*
+ * Called from net/core when new frame is available.
+ */
+static int stir_hard_xmit(struct sk_buff *skb, struct net_device *netdev)
+{
+	struct stir_cb *stir = netdev_priv(netdev);
+
+	netif_stop_queue(netdev);
+
+	/* the IRDA wrapping routines don't deal with non linear skb */
+	SKB_LINEAR_ASSERT(skb);
+
+	skb = xchg(&stir->tx_pending, skb);
+	wake_up(&stir->thr_wait);
+	
+	/* this should never happen unless stop/wakeup problem */
+	if (unlikely(skb)) {
+		WARN_ON(1);
+		dev_kfree_skb(skb);
+	}
+
+	return 0;
+}
+
+/*
+ * Wait for the transmit FIFO to have space for next data
+ *
+ * If space < 0 then wait till FIFO completely drains.
+ * FYI: can take up to 13 seconds at 2400baud.
+ */
+static int fifo_txwait(struct stir_cb *stir, int space)
+{
+	int err;
+	unsigned long count, status;
+
+	/* Read FIFO status and count */
+	for(;;) {
+		err = read_reg(stir, REG_FIFOCTL, stir->fifo_status, 
+				   FIFO_REGS_SIZE);
+		if (unlikely(err != FIFO_REGS_SIZE)) {
+			warn("%s: FIFO register read error: %d", 
+			     stir->netdev->name, err);
+
+			return err;
+		}
+
+		status = stir->fifo_status[0];
+		count = (unsigned)(stir->fifo_status[2] & 0x1f) << 8 
+			| stir->fifo_status[1];
+
+		pr_debug("fifo status 0x%lx count %lu\n", status, count);
+
+		/* error when receive/transmit fifo gets confused */
+		if (status & FIFOCTL_RXERR) {
+			stir->stats.rx_fifo_errors++;
+			stir->stats.rx_errors++;
+			break;
+		}
+
+		if (status & FIFOCTL_TXERR) {
+			stir->stats.tx_fifo_errors++;
+			stir->stats.tx_errors++;
+			break;
+		}
+
+		/* is fifo receiving already, or empty */
+		if (!(status & FIFOCTL_DIR)
+		    || (status & FIFOCTL_EMPTY))
+			return 0;
+
+		if (signal_pending(current))
+			return -EINTR;
+
+		/* shutting down? */
+		if (!netif_running(stir->netdev)
+		    || !netif_device_present(stir->netdev))
+			return -ESHUTDOWN;
+
+		/* only waiting for some space */
+		if (space >= 0 && STIR_FIFO_SIZE - 4 > space + count)
+			return 0;
+
+		/* estimate transfer time for remaining chars */
+		msleep((count * 8000) / stir->speed);
+	}
+			
+	err = write_reg(stir, REG_FIFOCTL, FIFOCTL_CLR);
+	if (err) 
+		return err;
+	err = write_reg(stir, REG_FIFOCTL, 0);
+	if (err)
+		return err;
+
+	return 0;
+}
+
+
+/* Wait for turnaround delay before starting transmit.  */
+static void turnaround_delay(const struct stir_cb *stir, long us)
+{
+	long ticks;
+	struct timeval now;
+
+	if (us <= 0)
+		return;
+
+	do_gettimeofday(&now);
+	if (now.tv_sec - stir->rx_time.tv_sec > 0)
+		us -= USEC_PER_SEC;
+	us -= now.tv_usec - stir->rx_time.tv_usec;
+	if (us < 10)
+		return;
+
+	ticks = us / (1000000 / HZ);
+	if (ticks > 0) {
+		current->state = TASK_INTERRUPTIBLE;
+		schedule_timeout(1 + ticks);
+	} else
+		udelay(us);
+}
+
+/*
+ * Start receiver by submitting a request to the receive pipe.
+ * If nothing is available it will return after rx_interval.
+ */
+static int receive_start(struct stir_cb *stir)
+{
+	/* reset state */
+	stir->receiving = 1;
+
+	stir->rx_buff.in_frame = FALSE;
+	stir->rx_buff.state = OUTSIDE_FRAME;
+
+	stir->rx_urb->status = 0;
+	return usb_submit_urb(stir->rx_urb, GFP_KERNEL);
+}
+
+/* Stop all pending receive Urb's */
+static void receive_stop(struct stir_cb *stir)
+{
+	stir->receiving = 0;
+	usb_kill_urb(stir->rx_urb);
+
+	if (stir->rx_buff.in_frame) 
+		stir->stats.collisions++;
+}
+/*
+ * Wrap data in socket buffer and send it.
+ */
+static void stir_send(struct stir_cb *stir, struct sk_buff *skb)
+{
+	unsigned wraplen;
+	int first_frame = 0;
+
+	/* if receiving, need to turnaround */
+	if (stir->receiving) {
+		receive_stop(stir);
+		turnaround_delay(stir, irda_get_mtt(skb));
+		first_frame = 1;
+	}
+
+	if (isfir(stir->speed))
+		wraplen = wrap_fir_skb(skb, stir->io_buf);
+	else
+		wraplen = wrap_sir_skb(skb, stir->io_buf);
+		
+	/* check for space available in fifo */
+	if (!first_frame)
+		fifo_txwait(stir, wraplen);
+
+	stir->stats.tx_packets++;
+	stir->stats.tx_bytes += skb->len;
+	stir->netdev->trans_start = jiffies;
+	pr_debug("send %d (%d)\n", skb->len, wraplen);
+
+	if (usb_bulk_msg(stir->usbdev, usb_sndbulkpipe(stir->usbdev, 1),
+			 stir->io_buf, wraplen,
+			 NULL, TRANSMIT_TIMEOUT))
+		stir->stats.tx_errors++;
+}
+
+/*
+ * Transmit state machine thread
+ */
+static int stir_transmit_thread(void *arg)
+{
+	struct stir_cb *stir = arg;
+	struct net_device *dev = stir->netdev;
+	struct sk_buff *skb;
+
+	daemonize("%s", dev->name);
+	allow_signal(SIGTERM);
+
+	while (netif_running(dev)
+	       && netif_device_present(dev)
+	       && !signal_pending(current))
+	{
+#ifdef CONFIG_PM
+		/* if suspending, then power off and wait */
+		if (unlikely(current->flags & PF_FREEZE)) {
+			if (stir->receiving)
+				receive_stop(stir);
+			else
+				fifo_txwait(stir, -1);
+
+			write_reg(stir, REG_CTRL1, CTRL1_TXPWD|CTRL1_RXPWD);
+
+			refrigerator(PF_FREEZE);
+
+			if (change_speed(stir, stir->speed))
+				break;
+		}
+#endif
+
+		/* if something to send? */
+		skb = xchg(&stir->tx_pending, NULL);
+		if (skb) {
+			unsigned new_speed = irda_get_next_speed(skb);
+			netif_wake_queue(dev);
+
+			if (skb->len > 0)
+				stir_send(stir, skb);
+			dev_kfree_skb(skb);
+
+			if ((new_speed != -1) && (stir->speed != new_speed)) {
+				if (fifo_txwait(stir, -1) ||
+				    change_speed(stir, new_speed))
+					break;
+			}
+			continue;
+		}
+
+		/* nothing to send? start receiving */
+		if (!stir->receiving 
+		    && irda_device_txqueue_empty(dev)) {
+			/* Wait otherwise chip gets confused. */
+			if (fifo_txwait(stir, -1))
+				break;
+
+			if (unlikely(receive_start(stir))) {
+				if (net_ratelimit())
+					info("%s: receive usb submit failed",
+					     stir->netdev->name);
+				stir->receiving = 0;
+				msleep(10);
+				continue;
+			}
+		}
+
+		/* sleep if nothing to send */
+		wait_event_interruptible(stir->thr_wait, stir->tx_pending);
+	}
+
+	complete_and_exit (&stir->thr_exited, 0);
+}
+
+
+/*
+ * USB bulk receive completion callback.
+ * Wakes up every ms (usb round trip) with wrapped 
+ * data.
+ */
+static void stir_rcv_irq(struct urb *urb, struct pt_regs *regs)
+{
+	struct stir_cb *stir = urb->context;
+	int err;
+
+	/* in process of stopping, just drop data */
+	if (!netif_running(stir->netdev))
+		return;
+
+	/* unlink, shutdown, unplug, other nasties */
+	if (urb->status != 0) 
+		return;
+
+	if (urb->actual_length > 0) {
+		pr_debug("receive %d\n", urb->actual_length);
+		unwrap_chars(stir, urb->transfer_buffer,
+			     urb->actual_length);
+		
+		stir->netdev->last_rx = jiffies;
+		do_gettimeofday(&stir->rx_time);
+	}
+
+	/* kernel thread is stopping receiver don't resubmit */
+	if (!stir->receiving)
+		return;
+
+	/* resubmit existing urb */
+	err = usb_submit_urb(urb, GFP_ATOMIC);
+
+	/* in case of error, the kernel thread will restart us */
+	if (err) {
+		warn("%s: usb receive submit error: %d",
+			stir->netdev->name, err);
+		stir->receiving = 0;
+		wake_up(&stir->thr_wait);
+	}
+}
+
+/*
+ * Function stir_net_open (dev)
+ *
+ *    Network device is taken up. Usually this is done by "ifconfig irda0 up"
+ */
+static int stir_net_open(struct net_device *netdev)
+{
+	struct stir_cb *stir = netdev_priv(netdev);
+	int err;
+	char hwname[16];
+
+	err = usb_clear_halt(stir->usbdev, usb_sndbulkpipe(stir->usbdev, 1));
+	if (err)
+		goto err_out1;
+	err = usb_clear_halt(stir->usbdev, usb_rcvbulkpipe(stir->usbdev, 2));
+	if (err)
+		goto err_out1;
+
+	err = change_speed(stir, 9600);
+	if (err)
+		goto err_out1;
+
+	err = -ENOMEM;
+
+	/* Initialize for SIR/FIR to copy data directly into skb.  */
+	stir->receiving = 0;
+	stir->rx_buff.truesize = IRDA_SKB_MAX_MTU;
+	stir->rx_buff.skb = dev_alloc_skb(IRDA_SKB_MAX_MTU);
+	if (!stir->rx_buff.skb) 
+		goto err_out1;
+
+	skb_reserve(stir->rx_buff.skb, 1);
+	stir->rx_buff.head = stir->rx_buff.skb->data;
+	do_gettimeofday(&stir->rx_time);
+
+	stir->rx_urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!stir->rx_urb) 
+		goto err_out2;
+
+	stir->io_buf = kmalloc(STIR_FIFO_SIZE, GFP_KERNEL);
+	if (!stir->io_buf)
+		goto err_out3;
+
+	usb_fill_bulk_urb(stir->rx_urb, stir->usbdev,
+			  usb_rcvbulkpipe(stir->usbdev, 2),
+			  stir->io_buf, STIR_FIFO_SIZE,
+			  stir_rcv_irq, stir);
+
+	stir->fifo_status = kmalloc(FIFO_REGS_SIZE, GFP_KERNEL);
+	if (!stir->fifo_status) 
+		goto err_out4;
+		
+	/*
+	 * Now that everything should be initialized properly,
+	 * Open new IrLAP layer instance to take care of us...
+	 * Note : will send immediately a speed change...
+	 */
+	sprintf(hwname, "usb#%d", stir->usbdev->devnum);
+	stir->irlap = irlap_open(netdev, &stir->qos, hwname);
+	if (!stir->irlap) {
+		err("stir4200: irlap_open failed");
+		goto err_out5;
+	}
+
+	/** Start kernel thread for transmit.  */
+	stir->thr_pid = kernel_thread(stir_transmit_thread, stir,
+				      CLONE_FS|CLONE_FILES);
+	if (stir->thr_pid < 0) {
+		err = stir->thr_pid;
+		err("stir4200: unable to start kernel thread");
+		goto err_out6;
+	}
+
+	netif_start_queue(netdev);
+
+	return 0;
+
+ err_out6:
+	irlap_close(stir->irlap);
+ err_out5:
+	kfree(stir->fifo_status);
+ err_out4:
+	kfree(stir->io_buf);
+ err_out3:
+	usb_free_urb(stir->rx_urb);
+ err_out2:
+	kfree_skb(stir->rx_buff.skb);
+ err_out1:
+	return err;
+}
+
+/*
+ * Function stir_net_close (stir)
+ *
+ *    Network device is taken down. Usually this is done by
+ *    "ifconfig irda0 down"
+ */
+static int stir_net_close(struct net_device *netdev)
+{
+	struct stir_cb *stir = netdev_priv(netdev);
+
+	/* Stop transmit processing */
+	netif_stop_queue(netdev);
+
+	/* Kill transmit thread */
+	kill_proc(stir->thr_pid, SIGTERM, 1);
+	wait_for_completion(&stir->thr_exited);
+	kfree(stir->fifo_status);
+
+	/* Mop up receive urb's */
+	usb_kill_urb(stir->rx_urb);
+	
+	kfree(stir->io_buf);
+	usb_free_urb(stir->rx_urb);
+	kfree_skb(stir->rx_buff.skb);
+
+	/* Stop and remove instance of IrLAP */
+	if (stir->irlap)
+		irlap_close(stir->irlap);
+
+	stir->irlap = NULL;
+
+	return 0;
+}
+
+/*
+ * IOCTLs : Extra out-of-band network commands...
+ */
+static int stir_net_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd)
+{
+	struct if_irda_req *irq = (struct if_irda_req *) rq;
+	struct stir_cb *stir = netdev_priv(netdev);
+	int ret = 0;
+
+	switch (cmd) {
+	case SIOCSBANDWIDTH: /* Set bandwidth */
+		if (!capable(CAP_NET_ADMIN))
+			return -EPERM;
+
+		/* Check if the device is still there */
+		if (netif_device_present(stir->netdev))
+			ret = change_speed(stir, irq->ifr_baudrate);
+		break;
+
+	case SIOCSMEDIABUSY: /* Set media busy */
+		if (!capable(CAP_NET_ADMIN))
+			return -EPERM;
+
+		/* Check if the IrDA stack is still there */
+		if (netif_running(stir->netdev))
+			irda_device_set_media_busy(stir->netdev, TRUE);
+		break;
+
+	case SIOCGRECEIVING:
+		/* Only approximately true */
+		irq->ifr_receiving = stir->receiving;
+		break;
+
+	default:
+		ret = -EOPNOTSUPP;
+	}
+
+	return ret;
+}
+
+/*
+ * Get device stats (for /proc/net/dev and ifconfig)
+ */
+static struct net_device_stats *stir_net_get_stats(struct net_device *netdev)
+{
+	struct stir_cb *stir = netdev_priv(netdev);
+	return &stir->stats;
+}
+
+/*
+ * This routine is called by the USB subsystem for each new device
+ * in the system. We need to check if the device is ours, and in
+ * this case start handling it.
+ * Note : it might be worth protecting this function by a global
+ * spinlock... Or not, because maybe USB already deal with that...
+ */
+static int stir_probe(struct usb_interface *intf,
+		      const struct usb_device_id *id)
+{
+	struct usb_device *dev = interface_to_usbdev(intf);
+	struct stir_cb *stir = NULL;
+	struct net_device *net;
+	int ret = -ENOMEM;
+
+	/* Allocate network device container. */
+	net = alloc_irdadev(sizeof(*stir));
+	if(!net)
+		goto err_out1;
+
+	SET_MODULE_OWNER(net);
+	SET_NETDEV_DEV(net, &intf->dev);
+	stir = netdev_priv(net);
+	stir->netdev = net;
+	stir->usbdev = dev;
+
+	ret = usb_reset_configuration(dev);
+	if (ret != 0) {
+		err("stir4200: usb reset configuration failed");
+		goto err_out2;
+	}
+
+	printk(KERN_INFO "SigmaTel STIr4200 IRDA/USB found at address %d, "
+		"Vendor: %x, Product: %x\n",
+	       dev->devnum, le16_to_cpu(dev->descriptor.idVendor),
+	       le16_to_cpu(dev->descriptor.idProduct));
+
+	/* Initialize QoS for this device */
+	irda_init_max_qos_capabilies(&stir->qos);
+
+	/* That's the Rx capability. */
+	stir->qos.baud_rate.bits       &= IR_2400 | IR_9600 | IR_19200 |
+					 IR_38400 | IR_57600 | IR_115200 |
+					 (IR_4000000 << 8);
+	stir->qos.min_turn_time.bits   &= qos_mtt_bits;
+	irda_qos_bits_to_value(&stir->qos);
+
+	init_completion (&stir->thr_exited);
+	init_waitqueue_head (&stir->thr_wait);
+
+	/* Override the network functions we need to use */
+	net->hard_start_xmit = stir_hard_xmit;
+	net->open            = stir_net_open;
+	net->stop            = stir_net_close;
+	net->get_stats	     = stir_net_get_stats;
+	net->do_ioctl        = stir_net_ioctl;
+
+	ret = register_netdev(net);
+	if (ret != 0)
+		goto err_out2;
+
+	info("IrDA: Registered SigmaTel device %s", net->name);
+
+	usb_set_intfdata(intf, stir);
+
+	return 0;
+
+err_out2:
+	free_netdev(net);
+err_out1:
+	return ret;
+}
+
+/*
+ * The current device is removed, the USB layer tell us to shut it down...
+ */
+static void stir_disconnect(struct usb_interface *intf)
+{
+	struct stir_cb *stir = usb_get_intfdata(intf);
+
+	if (!stir)
+		return;
+
+	unregister_netdev(stir->netdev);
+	free_netdev(stir->netdev);
+
+	usb_set_intfdata(intf, NULL);
+}
+
+#ifdef CONFIG_PM
+/* Power management suspend, so power off the transmitter/receiver */
+static int stir_suspend(struct usb_interface *intf, u32 state)
+{
+	struct stir_cb *stir = usb_get_intfdata(intf);
+
+	netif_device_detach(stir->netdev);
+	return 0;
+}
+
+/* Coming out of suspend, so reset hardware */
+static int stir_resume(struct usb_interface *intf)
+{
+	struct stir_cb *stir = usb_get_intfdata(intf);
+
+	netif_device_attach(stir->netdev);
+
+	/* receiver restarted when send thread wakes up */
+	return 0;
+}
+#endif
+
+/*
+ * USB device callbacks
+ */
+static struct usb_driver irda_driver = {
+	.owner		= THIS_MODULE,
+	.name		= "stir4200",
+	.probe		= stir_probe,
+	.disconnect	= stir_disconnect,
+	.id_table	= dongles,
+#ifdef CONFIG_PM
+	.suspend	= stir_suspend,
+	.resume		= stir_resume,
+#endif
+};
+
+/*
+ * Module insertion
+ */
+static int __init stir_init(void)
+{
+	return usb_register(&irda_driver);
+}
+module_init(stir_init);
+
+/*
+ * Module removal
+ */
+static void __exit stir_cleanup(void)
+{
+	/* Deregister the driver and remove all pending instances */
+	usb_deregister(&irda_driver);
+}
+module_exit(stir_cleanup);
diff --git a/drivers/net/irda/tekram-sir.c b/drivers/net/irda/tekram-sir.c
new file mode 100644
index 0000000..0dd6bc7
--- /dev/null
+++ b/drivers/net/irda/tekram-sir.c
@@ -0,0 +1,232 @@
+/*********************************************************************
+ *                
+ * 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"
+
+static int tekram_delay = 150;		/* default is 150 ms */
+module_param(tekram_delay, int, 0);
+MODULE_PARM_DESC(tekram_delay, "tekram dongle write complete delay");
+
+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,
+};
+
+static int __init tekram_sir_init(void)
+{
+	if (tekram_delay < 1  ||  tekram_delay > 500)
+		tekram_delay = 200;
+	IRDA_DEBUG(1, "%s - using %d ms delay\n",
+		tekram.driver_name, tekram_delay);
+	return irda_register_dongle(&tekram);
+}
+
+static void __exit tekram_sir_cleanup(void)
+{
+	irda_unregister_dongle(&tekram);
+}
+
+static int tekram_open(struct sir_dev *dev)
+{
+	struct qos_info *qos = &dev->qos;
+
+	IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
+
+	sirdev_set_dtr_rts(dev, TRUE, TRUE);
+
+	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);
+
+	/* irda thread waits 50 msec for power settling */
+
+	return 0;
+}
+
+static int tekram_close(struct sir_dev *dev)
+{
+	IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
+
+	/* Power off dongle */
+	sirdev_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 state = dev->fsm.substate;
+	unsigned delay = 0;
+	u8 byte;
+	static int ret = 0;
+	
+	IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
+
+	switch(state) {
+	case SIRDEV_STATE_DONGLE_SPEED:
+
+		switch (speed) {
+		default:
+			speed = 9600;
+			ret = -EINVAL;
+			/* 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 */
+		sirdev_set_dtr_rts(dev, TRUE, FALSE);
+	
+		/* Wait at least 7us */
+		udelay(14);
+
+		/* Write control byte */
+		sirdev_raw_write(dev, &byte, 1);
+		
+		dev->speed = speed;
+
+		state = TEKRAM_STATE_WAIT_SPEED;
+		delay = tekram_delay;
+		break;
+
+	case TEKRAM_STATE_WAIT_SPEED:
+		/* Set DTR, Set RTS */
+		sirdev_set_dtr_rts(dev, TRUE, TRUE);
+		udelay(50);
+		break;
+
+	default:
+		IRDA_ERROR("%s - undefined state %d\n", __FUNCTION__, state);
+		ret = -EINVAL;
+		break;
+	}
+
+	dev->fsm.substate = state;
+	return (delay > 0) ? delay : ret;
+}
+
+/*
+ * 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
+ */
+
+static int tekram_reset(struct sir_dev *dev)
+{
+	IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
+
+	/* Clear DTR, Set RTS */
+	sirdev_set_dtr_rts(dev, FALSE, TRUE); 
+
+	/* Should sleep 1 ms */
+	msleep(1);
+
+	/* Set DTR, Set RTS */
+	sirdev_set_dtr_rts(dev, TRUE, TRUE);
+	
+	/* Wait at least 50 us */
+	udelay(75);
+
+	dev->speed = 9600;
+
+	return 0;
+}
+
+MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>");
+MODULE_DESCRIPTION("Tekram IrMate IR-210B dongle driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("irda-dongle-0"); /* IRDA_TEKRAM_DONGLE */
+		
+module_init(tekram_sir_init);
+module_exit(tekram_sir_cleanup);
diff --git a/drivers/net/irda/tekram.c b/drivers/net/irda/tekram.c
new file mode 100644
index 0000000..8f62582
--- /dev/null
+++ b/drivers/net/irda/tekram.c
@@ -0,0 +1,282 @@
+/*********************************************************************
+ *                
+ * Filename:      tekram.c
+ * Version:       1.2
+ * 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:   Fri Dec 17 09:13:09 1999
+ * Modified by:   Dag Brattli <dagb@cs.uit.no>
+ * 
+ *     Copyright (c) 1998-1999 Dag Brattli, 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/tty.h>
+#include <linux/init.h>
+
+#include <net/irda/irda.h>
+#include <net/irda/irda_device.h>
+
+static void tekram_open(dongle_t *self, struct qos_info *qos);
+static void tekram_close(dongle_t *self);
+static int  tekram_change_speed(struct irda_task *task);
+static int  tekram_reset(struct irda_task *task);
+
+#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_reg dongle = {
+	.type = IRDA_TEKRAM_DONGLE,
+	.open  = tekram_open,
+	.close = tekram_close,
+	.reset = tekram_reset,
+	.change_speed = tekram_change_speed,
+	.owner = THIS_MODULE,
+};
+
+static int __init tekram_init(void)
+{
+	return irda_device_register_dongle(&dongle);
+}
+
+static void __exit tekram_cleanup(void)
+{
+	irda_device_unregister_dongle(&dongle);
+}
+
+static void tekram_open(dongle_t *self, struct qos_info *qos)
+{
+	IRDA_DEBUG(2, "%s()\n", __FUNCTION__ );
+
+	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);
+}
+
+static void tekram_close(dongle_t *self)
+{
+	IRDA_DEBUG(2, "%s()\n", __FUNCTION__ );
+
+	/* Power off dongle */
+	self->set_dtr_rts(self->dev, FALSE, FALSE);
+
+	if (self->reset_task)
+		irda_task_delete(self->reset_task);
+	if (self->speed_task)
+		irda_task_delete(self->speed_task);
+}
+
+/*
+ * 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)
+ *    5. clear RTS (return to NORMAL Operation)
+ *    6. wait at least 50 us, new setting (baud rate, etc) takes effect here 
+ *       after
+ */
+static int tekram_change_speed(struct irda_task *task)
+{
+	dongle_t *self = (dongle_t *) task->instance;
+	__u32 speed = (__u32) task->param;
+	__u8 byte;
+	int ret = 0;
+	
+	IRDA_DEBUG(2, "%s()\n", __FUNCTION__ );
+
+	IRDA_ASSERT(task != NULL, return -1;);
+
+	if (self->speed_task && self->speed_task != task) {
+		IRDA_DEBUG(0, "%s(), busy!\n", __FUNCTION__ );
+		return msecs_to_jiffies(10);
+	} else
+		self->speed_task = task;
+
+	switch (speed) {
+	default:
+	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;
+	}
+
+	switch (task->state) {
+	case IRDA_TASK_INIT:
+	case IRDA_TASK_CHILD_INIT:		
+		/* 
+		 * Need to reset the dongle and go to 9600 bps before
+                 * programming 
+		 */
+		if (irda_task_execute(self, tekram_reset, NULL, task, 
+				      (void *) speed))
+		{
+			/* Dongle need more time to reset */
+			irda_task_next_state(task, IRDA_TASK_CHILD_WAIT);
+
+			/* Give reset 1 sec to finish */
+			ret = msecs_to_jiffies(1000);
+		} else
+			irda_task_next_state(task, IRDA_TASK_CHILD_DONE);
+		break;
+	case IRDA_TASK_CHILD_WAIT:
+		IRDA_WARNING("%s(), resetting dongle timed out!\n",
+			     __FUNCTION__);
+		ret = -1;
+		break;
+	case IRDA_TASK_CHILD_DONE:
+		/* Set DTR, Clear RTS */
+		self->set_dtr_rts(self->dev, TRUE, FALSE);
+	
+		/* Wait at least 7us */
+		udelay(14);
+
+		/* Write control byte */
+		self->write(self->dev, &byte, 1);
+		
+		irda_task_next_state(task, IRDA_TASK_WAIT);
+
+		/* Wait at least 100 ms */
+		ret = msecs_to_jiffies(150);
+		break;
+	case IRDA_TASK_WAIT:
+		/* Set DTR, Set RTS */
+		self->set_dtr_rts(self->dev, TRUE, TRUE);
+
+		irda_task_next_state(task, IRDA_TASK_DONE);
+		self->speed_task = NULL;
+		break;
+	default:
+		IRDA_ERROR("%s(), unknown state %d\n",
+			   __FUNCTION__, task->state);
+		irda_task_next_state(task, IRDA_TASK_DONE);
+		self->speed_task = NULL;
+		ret = -1;
+		break;
+	}
+	return ret;
+}
+
+/*
+ * 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
+ */
+int tekram_reset(struct irda_task *task)
+{
+	dongle_t *self = (dongle_t *) task->instance;
+	int ret = 0;
+
+	IRDA_DEBUG(2, "%s()\n", __FUNCTION__ );
+
+	IRDA_ASSERT(task != NULL, return -1;);
+
+	if (self->reset_task && self->reset_task != task) {
+		IRDA_DEBUG(0, "%s(), busy!\n", __FUNCTION__ );
+		return msecs_to_jiffies(10);
+	} else
+		self->reset_task = task;
+	
+	/* Power off dongle */
+	//self->set_dtr_rts(self->dev, FALSE, FALSE);
+	self->set_dtr_rts(self->dev, TRUE, TRUE);
+
+	switch (task->state) {
+	case IRDA_TASK_INIT:
+		irda_task_next_state(task, IRDA_TASK_WAIT1);
+
+		/* Sleep 50 ms */
+		ret = msecs_to_jiffies(50);
+		break;
+	case IRDA_TASK_WAIT1:
+		/* Clear DTR, Set RTS */
+		self->set_dtr_rts(self->dev, FALSE, TRUE); 
+
+		irda_task_next_state(task, IRDA_TASK_WAIT2);
+		
+		/* Should sleep 1 ms */
+		ret = msecs_to_jiffies(1);
+		break;
+	case IRDA_TASK_WAIT2:
+		/* Set DTR, Set RTS */
+		self->set_dtr_rts(self->dev, TRUE, TRUE);
+	
+		/* Wait at least 50 us */
+		udelay(75);
+
+		irda_task_next_state(task, IRDA_TASK_DONE);
+		self->reset_task = NULL;
+		break;
+	default:
+		IRDA_ERROR("%s(), unknown state %d\n",
+			   __FUNCTION__, task->state);
+		irda_task_next_state(task, IRDA_TASK_DONE);		
+		self->reset_task = NULL;
+		ret = -1;
+	}
+	return ret;
+}
+
+MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>");
+MODULE_DESCRIPTION("Tekram IrMate IR-210B dongle driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("irda-dongle-0"); /* IRDA_TEKRAM_DONGLE */
+		
+/*
+ * Function init_module (void)
+ *
+ *    Initialize Tekram module
+ *
+ */
+module_init(tekram_init);
+
+/*
+ * Function cleanup_module (void)
+ *
+ *    Cleanup Tekram module
+ *
+ */
+module_exit(tekram_cleanup);
diff --git a/drivers/net/irda/via-ircc.c b/drivers/net/irda/via-ircc.c
new file mode 100644
index 0000000..8bafb45
--- /dev/null
+++ b/drivers/net/irda/via-ircc.c
@@ -0,0 +1,1676 @@
+/********************************************************************
+ Filename:      via-ircc.c
+ Version:       1.0 
+ Description:   Driver for the VIA VT8231/VT8233 IrDA chipsets
+ Author:        VIA Technologies,inc
+ Date  :	08/06/2003
+
+Copyright (c) 1998-2003 VIA Technologies, Inc.
+
+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, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTIES OR REPRESENTATIONS; 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.
+
+F01 Oct/02/02: Modify code for V0.11(move out back to back transfer)
+F02 Oct/28/02: Add SB device ID for 3147 and 3177.
+ Comment :
+       jul/09/2002 : only implement two kind of dongle currently.
+       Oct/02/2002 : work on VT8231 and VT8233 .
+       Aug/06/2003 : change driver format to pci driver .
+
+2004-02-16: <sda@bdit.de>
+- Removed unneeded 'legacy' pci stuff.
+- Make sure SIR mode is set (hw_init()) before calling mode-dependant stuff.
+- On speed change from core, don't send SIR frame with new speed. 
+  Use current speed and change speeds later.
+- Make module-param dongle_id actually work.
+- New dongle_id 17 (0x11): TDFS4500. Single-ended SIR only. 
+  Tested with home-grown PCB on EPIA boards.
+- Code cleanup.
+       
+ ********************************************************************/
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/rtnetlink.h>
+#include <linux/pci.h>
+#include <linux/dma-mapping.h>
+
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/byteorder.h>
+
+#include <linux/pm.h>
+
+#include <net/irda/wrapper.h>
+#include <net/irda/irda.h>
+#include <net/irda/irda_device.h>
+
+#include "via-ircc.h"
+
+#define VIA_MODULE_NAME "via-ircc"
+#define CHIP_IO_EXTENT 0x40
+
+static char *driver_name = VIA_MODULE_NAME;
+
+/* Module parameters */
+static int qos_mtt_bits = 0x07;	/* 1 ms or more */
+static int dongle_id = 0;	/* default: probe */
+
+/* We can't guess the type of connected dongle, user *must* supply it. */
+module_param(dongle_id, int, 0);
+
+/* FIXME : we should not need this, because instances should be automatically
+ * managed by the PCI layer. Especially that we seem to only be using the
+ * first entry. Jean II */
+/* Max 4 instances for now */
+static struct via_ircc_cb *dev_self[] = { NULL, NULL, NULL, NULL };
+
+/* Some prototypes */
+static int via_ircc_open(int i, chipio_t * info, unsigned int id);
+static int via_ircc_close(struct via_ircc_cb *self);
+static int via_ircc_dma_receive(struct via_ircc_cb *self);
+static int via_ircc_dma_receive_complete(struct via_ircc_cb *self,
+					 int iobase);
+static int via_ircc_hard_xmit_sir(struct sk_buff *skb,
+				  struct net_device *dev);
+static int via_ircc_hard_xmit_fir(struct sk_buff *skb,
+				  struct net_device *dev);
+static void via_hw_init(struct via_ircc_cb *self);
+static void via_ircc_change_speed(struct via_ircc_cb *self, __u32 baud);
+static irqreturn_t via_ircc_interrupt(int irq, void *dev_id,
+				      struct pt_regs *regs);
+static int via_ircc_is_receiving(struct via_ircc_cb *self);
+static int via_ircc_read_dongle_id(int iobase);
+
+static int via_ircc_net_open(struct net_device *dev);
+static int via_ircc_net_close(struct net_device *dev);
+static int via_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq,
+			      int cmd);
+static struct net_device_stats *via_ircc_net_get_stats(struct net_device
+						       *dev);
+static void via_ircc_change_dongle_speed(int iobase, int speed,
+					 int dongle_id);
+static int RxTimerHandler(struct via_ircc_cb *self, int iobase);
+static void hwreset(struct via_ircc_cb *self);
+static int via_ircc_dma_xmit(struct via_ircc_cb *self, u16 iobase);
+static int upload_rxdata(struct via_ircc_cb *self, int iobase);
+static int __devinit via_init_one (struct pci_dev *pcidev, const struct pci_device_id *id);
+static void __devexit via_remove_one (struct pci_dev *pdev);
+
+/* FIXME : Should use udelay() instead, even if we are x86 only - Jean II */
+static void iodelay(int udelay)
+{
+	u8 data;
+	int i;
+
+	for (i = 0; i < udelay; i++) {
+		data = inb(0x80);
+	}
+}
+
+static struct pci_device_id via_pci_tbl[] = {
+	{ PCI_VENDOR_ID_VIA, 0x8231, PCI_ANY_ID, PCI_ANY_ID,0,0,0 },
+	{ PCI_VENDOR_ID_VIA, 0x3109, PCI_ANY_ID, PCI_ANY_ID,0,0,1 },
+	{ PCI_VENDOR_ID_VIA, 0x3074, PCI_ANY_ID, PCI_ANY_ID,0,0,2 },
+	{ PCI_VENDOR_ID_VIA, 0x3147, PCI_ANY_ID, PCI_ANY_ID,0,0,3 },
+	{ PCI_VENDOR_ID_VIA, 0x3177, PCI_ANY_ID, PCI_ANY_ID,0,0,4 },
+	{ 0, }
+};
+
+MODULE_DEVICE_TABLE(pci,via_pci_tbl);
+
+
+static struct pci_driver via_driver = {
+	.name		= VIA_MODULE_NAME,
+	.id_table	= via_pci_tbl,
+	.probe		= via_init_one,
+	.remove		= __devexit_p(via_remove_one),
+};
+
+
+/*
+ * Function via_ircc_init ()
+ *
+ *    Initialize chip. Just find out chip type and resource.
+ */
+static int __init via_ircc_init(void)
+{
+	int rc;
+
+	IRDA_DEBUG(3, "%s()\n", __FUNCTION__);
+
+	rc = pci_register_driver(&via_driver);
+	if (rc < 0) {
+		IRDA_DEBUG(0, "%s(): error rc = %d, returning  -ENODEV...\n",
+			   __FUNCTION__, rc);
+		return -ENODEV;
+	}
+	return 0;
+}
+
+static int __devinit via_init_one (struct pci_dev *pcidev, const struct pci_device_id *id)
+{
+	int rc;
+        u8 temp,oldPCI_40,oldPCI_44,bTmp,bTmp1;
+	u16 Chipset,FirDRQ1,FirDRQ0,FirIRQ,FirIOBase;
+	chipio_t info;
+
+	IRDA_DEBUG(2, "%s(): Device ID=(0X%X)\n", __FUNCTION__, id->device);
+
+	rc = pci_enable_device (pcidev);
+	if (rc) {
+		IRDA_DEBUG(0, "%s(): error rc = %d\n", __FUNCTION__, rc);
+		return -ENODEV;
+	}
+
+	// South Bridge exist
+        if ( ReadLPCReg(0x20) != 0x3C )
+		Chipset=0x3096;
+	else
+		Chipset=0x3076;
+
+	if (Chipset==0x3076) {
+		IRDA_DEBUG(2, "%s(): Chipset = 3076\n", __FUNCTION__);
+
+		WriteLPCReg(7,0x0c );
+		temp=ReadLPCReg(0x30);//check if BIOS Enable Fir
+		if((temp&0x01)==1) {   // BIOS close or no FIR
+			WriteLPCReg(0x1d, 0x82 );
+			WriteLPCReg(0x23,0x18);
+			temp=ReadLPCReg(0xF0);
+			if((temp&0x01)==0) {
+				temp=(ReadLPCReg(0x74)&0x03);    //DMA
+				FirDRQ0=temp + 4;
+				temp=(ReadLPCReg(0x74)&0x0C) >> 2;
+				FirDRQ1=temp + 4;
+			} else {
+				temp=(ReadLPCReg(0x74)&0x0C) >> 2;    //DMA
+				FirDRQ0=temp + 4;
+				FirDRQ1=FirDRQ0;
+			}
+			FirIRQ=(ReadLPCReg(0x70)&0x0f);		//IRQ
+			FirIOBase=ReadLPCReg(0x60 ) << 8;	//IO Space :high byte
+			FirIOBase=FirIOBase| ReadLPCReg(0x61) ;	//low byte
+			FirIOBase=FirIOBase  ;
+			info.fir_base=FirIOBase;
+			info.irq=FirIRQ;
+			info.dma=FirDRQ1;
+			info.dma2=FirDRQ0;
+			pci_read_config_byte(pcidev,0x40,&bTmp);
+			pci_write_config_byte(pcidev,0x40,((bTmp | 0x08) & 0xfe));
+			pci_read_config_byte(pcidev,0x42,&bTmp);
+			pci_write_config_byte(pcidev,0x42,(bTmp | 0xf0));
+			pci_write_config_byte(pcidev,0x5a,0xc0);
+			WriteLPCReg(0x28, 0x70 );
+			if (via_ircc_open(0, &info,0x3076) == 0)
+				rc=0;
+		} else
+			rc = -ENODEV; //IR not turn on	 
+	} else { //Not VT1211
+		IRDA_DEBUG(2, "%s(): Chipset = 3096\n", __FUNCTION__);
+
+		pci_read_config_byte(pcidev,0x67,&bTmp);//check if BIOS Enable Fir
+		if((bTmp&0x01)==1) {  // BIOS enable FIR
+			//Enable Double DMA clock
+			pci_read_config_byte(pcidev,0x42,&oldPCI_40);
+			pci_write_config_byte(pcidev,0x42,oldPCI_40 | 0x80);
+			pci_read_config_byte(pcidev,0x40,&oldPCI_40);
+			pci_write_config_byte(pcidev,0x40,oldPCI_40 & 0xf7);
+			pci_read_config_byte(pcidev,0x44,&oldPCI_44);
+			pci_write_config_byte(pcidev,0x44,0x4e);
+  //---------- read configuration from Function0 of south bridge
+			if((bTmp&0x02)==0) {
+				pci_read_config_byte(pcidev,0x44,&bTmp1); //DMA
+				FirDRQ0 = (bTmp1 & 0x30) >> 4;
+				pci_read_config_byte(pcidev,0x44,&bTmp1);
+				FirDRQ1 = (bTmp1 & 0xc0) >> 6;
+			} else  {
+				pci_read_config_byte(pcidev,0x44,&bTmp1);    //DMA
+				FirDRQ0 = (bTmp1 & 0x30) >> 4 ;
+				FirDRQ1=0;
+			}
+			pci_read_config_byte(pcidev,0x47,&bTmp1);  //IRQ
+			FirIRQ = bTmp1 & 0x0f;
+
+			pci_read_config_byte(pcidev,0x69,&bTmp);
+			FirIOBase = bTmp << 8;//hight byte
+			pci_read_config_byte(pcidev,0x68,&bTmp);
+			FirIOBase = (FirIOBase | bTmp ) & 0xfff0;
+  //-------------------------
+			info.fir_base=FirIOBase;
+			info.irq=FirIRQ;
+			info.dma=FirDRQ1;
+			info.dma2=FirDRQ0;
+			if (via_ircc_open(0, &info,0x3096) == 0)
+				rc=0;
+		} else
+			rc = -ENODEV; //IR not turn on !!!!!
+	}//Not VT1211
+
+	IRDA_DEBUG(2, "%s(): End - rc = %d\n", __FUNCTION__, rc);
+	return rc;
+}
+
+/*
+ * Function via_ircc_clean ()
+ *
+ *    Close all configured chips
+ *
+ */
+static void via_ircc_clean(void)
+{
+	int i;
+
+	IRDA_DEBUG(3, "%s()\n", __FUNCTION__);
+
+	for (i=0; i < 4; i++) {
+		if (dev_self[i])
+			via_ircc_close(dev_self[i]);
+	}
+}
+
+static void __devexit via_remove_one (struct pci_dev *pdev)
+{
+	IRDA_DEBUG(3, "%s()\n", __FUNCTION__);
+
+	/* FIXME : This is ugly. We should use pci_get_drvdata(pdev);
+	 * to get our driver instance and call directly via_ircc_close().
+	 * See vlsi_ir for details...
+	 * Jean II */
+	via_ircc_clean();
+
+	/* FIXME : This should be in via_ircc_close(), because here we may
+	 * theoritically disable still configured devices :-( - Jean II */
+	pci_disable_device(pdev);
+}
+
+static void __exit via_ircc_cleanup(void)
+{
+	IRDA_DEBUG(3, "%s()\n", __FUNCTION__);
+
+	/* FIXME : This should be redundant, as pci_unregister_driver()
+	 * should call via_remove_one() on each device.
+	 * Jean II */
+	via_ircc_clean();
+
+	/* Cleanup all instances of the driver */
+	pci_unregister_driver (&via_driver); 
+}
+
+/*
+ * Function via_ircc_open (iobase, irq)
+ *
+ *    Open driver instance
+ *
+ */
+static __devinit int via_ircc_open(int i, chipio_t * info, unsigned int id)
+{
+	struct net_device *dev;
+	struct via_ircc_cb *self;
+	int err;
+
+	IRDA_DEBUG(3, "%s()\n", __FUNCTION__);
+
+	/* Allocate new instance of the driver */
+	dev = alloc_irdadev(sizeof(struct via_ircc_cb));
+	if (dev == NULL) 
+		return -ENOMEM;
+
+	self = dev->priv;
+	self->netdev = dev;
+	spin_lock_init(&self->lock);
+
+	/* FIXME : We should store our driver instance in the PCI layer,
+	 * using pci_set_drvdata(), not in this array.
+	 * See vlsi_ir for details... - Jean II */
+	/* FIXME : 'i' is always 0 (see via_init_one()) :-( - Jean II */
+	/* Need to store self somewhere */
+	dev_self[i] = self;
+	self->index = i;
+	/* Initialize Resource */
+	self->io.cfg_base = info->cfg_base;
+	self->io.fir_base = info->fir_base;
+	self->io.irq = info->irq;
+	self->io.fir_ext = CHIP_IO_EXTENT;
+	self->io.dma = info->dma;
+	self->io.dma2 = info->dma2;
+	self->io.fifo_size = 32;
+	self->chip_id = id;
+	self->st_fifo.len = 0;
+	self->RxDataReady = 0;
+
+	/* Reserve the ioports that we need */
+	if (!request_region(self->io.fir_base, self->io.fir_ext, driver_name)) {
+		IRDA_DEBUG(0, "%s(), can't get iobase of 0x%03x\n",
+			   __FUNCTION__, self->io.fir_base);
+		err = -ENODEV;
+		goto err_out1;
+	}
+	
+	/* Initialize QoS for this device */
+	irda_init_max_qos_capabilies(&self->qos);
+
+	/* Check if user has supplied the dongle id or not */
+	if (!dongle_id)
+		dongle_id = via_ircc_read_dongle_id(self->io.fir_base);
+	self->io.dongle_id = dongle_id;
+
+	/* The only value we must override it the baudrate */
+	/* Maximum speeds and capabilities are dongle-dependant. */
+	switch( self->io.dongle_id ){
+	case 0x0d:
+		self->qos.baud_rate.bits =
+		    IR_9600 | IR_19200 | IR_38400 | IR_57600 | IR_115200 |
+		    IR_576000 | IR_1152000 | (IR_4000000 << 8);
+		break;
+	default:
+		self->qos.baud_rate.bits =
+		    IR_9600 | IR_19200 | IR_38400 | IR_57600 | IR_115200;
+		break;
+	}
+
+	/* Following was used for testing:
+	 *
+	 *   self->qos.baud_rate.bits = IR_9600;
+	 *
+	 * Is is no good, as it prohibits (error-prone) speed-changes.
+	 */
+
+	self->qos.min_turn_time.bits = qos_mtt_bits;
+	irda_qos_bits_to_value(&self->qos);
+
+	/* Max DMA buffer size needed = (data_size + 6) * (window_size) + 6; */
+	self->rx_buff.truesize = 14384 + 2048;
+	self->tx_buff.truesize = 14384 + 2048;
+
+	/* Allocate memory if needed */
+	self->rx_buff.head =
+		dma_alloc_coherent(NULL, self->rx_buff.truesize,
+				   &self->rx_buff_dma, GFP_KERNEL);
+	if (self->rx_buff.head == NULL) {
+		err = -ENOMEM;
+		goto err_out2;
+	}
+	memset(self->rx_buff.head, 0, self->rx_buff.truesize);
+
+	self->tx_buff.head =
+		dma_alloc_coherent(NULL, self->tx_buff.truesize,
+				   &self->tx_buff_dma, GFP_KERNEL);
+	if (self->tx_buff.head == NULL) {
+		err = -ENOMEM;
+		goto err_out3;
+	}
+	memset(self->tx_buff.head, 0, self->tx_buff.truesize);
+
+	self->rx_buff.in_frame = FALSE;
+	self->rx_buff.state = OUTSIDE_FRAME;
+	self->tx_buff.data = self->tx_buff.head;
+	self->rx_buff.data = self->rx_buff.head;
+
+	/* Reset Tx queue info */
+	self->tx_fifo.len = self->tx_fifo.ptr = self->tx_fifo.free = 0;
+	self->tx_fifo.tail = self->tx_buff.head;
+
+	/* Keep track of module usage */
+	SET_MODULE_OWNER(dev);
+
+	/* Override the network functions we need to use */
+	dev->hard_start_xmit = via_ircc_hard_xmit_sir;
+	dev->open = via_ircc_net_open;
+	dev->stop = via_ircc_net_close;
+	dev->do_ioctl = via_ircc_net_ioctl;
+	dev->get_stats = via_ircc_net_get_stats;
+
+	err = register_netdev(dev);
+	if (err)
+		goto err_out4;
+
+	IRDA_MESSAGE("IrDA: Registered device %s (via-ircc)\n", dev->name);
+
+	/* Initialise the hardware..
+	*/
+	self->io.speed = 9600;
+	via_hw_init(self);
+	return 0;
+ err_out4:
+	dma_free_coherent(NULL, self->tx_buff.truesize,
+			  self->tx_buff.head, self->tx_buff_dma);
+ err_out3:
+	dma_free_coherent(NULL, self->rx_buff.truesize,
+			  self->rx_buff.head, self->rx_buff_dma);
+ err_out2:
+	release_region(self->io.fir_base, self->io.fir_ext);
+ err_out1:
+	free_netdev(dev);
+	dev_self[i] = NULL;
+	return err;
+}
+
+/*
+ * Function via_ircc_close (self)
+ *
+ *    Close driver instance
+ *
+ */
+static int via_ircc_close(struct via_ircc_cb *self)
+{
+	int iobase;
+
+	IRDA_DEBUG(3, "%s()\n", __FUNCTION__);
+
+	IRDA_ASSERT(self != NULL, return -1;);
+
+	iobase = self->io.fir_base;
+
+	ResetChip(iobase, 5);	//hardware reset.
+	/* Remove netdevice */
+	unregister_netdev(self->netdev);
+
+	/* Release the PORT that this driver is using */
+	IRDA_DEBUG(2, "%s(), Releasing Region %03x\n",
+		   __FUNCTION__, self->io.fir_base);
+	release_region(self->io.fir_base, self->io.fir_ext);
+	if (self->tx_buff.head)
+		dma_free_coherent(NULL, self->tx_buff.truesize,
+				  self->tx_buff.head, self->tx_buff_dma);
+	if (self->rx_buff.head)
+		dma_free_coherent(NULL, self->rx_buff.truesize,
+				  self->rx_buff.head, self->rx_buff_dma);
+	dev_self[self->index] = NULL;
+
+	free_netdev(self->netdev);
+
+	return 0;
+}
+
+/*
+ * Function via_hw_init(self)
+ *
+ *    Returns non-negative on success.
+ *
+ * Formerly via_ircc_setup 
+ */
+static void via_hw_init(struct via_ircc_cb *self)
+{
+	int iobase = self->io.fir_base;
+
+	IRDA_DEBUG(3, "%s()\n", __FUNCTION__);
+
+	SetMaxRxPacketSize(iobase, 0x0fff);	//set to max:4095
+	// FIFO Init
+	EnRXFIFOReadyInt(iobase, OFF);
+	EnRXFIFOHalfLevelInt(iobase, OFF);
+	EnTXFIFOHalfLevelInt(iobase, OFF);
+	EnTXFIFOUnderrunEOMInt(iobase, ON);
+	EnTXFIFOReadyInt(iobase, OFF);
+	InvertTX(iobase, OFF);
+	InvertRX(iobase, OFF);
+
+	if (ReadLPCReg(0x20) == 0x3c)
+		WriteLPCReg(0xF0, 0);	// for VT1211
+	/* Int Init */
+	EnRXSpecInt(iobase, ON);
+
+	/* The following is basically hwreset */
+	/* If this is the case, why not just call hwreset() ? Jean II */
+	ResetChip(iobase, 5);
+	EnableDMA(iobase, OFF);
+	EnableTX(iobase, OFF);
+	EnableRX(iobase, OFF);
+	EnRXDMA(iobase, OFF);
+	EnTXDMA(iobase, OFF);
+	RXStart(iobase, OFF);
+	TXStart(iobase, OFF);
+	InitCard(iobase);
+	CommonInit(iobase);
+	SIRFilter(iobase, ON);
+	SetSIR(iobase, ON);
+	CRC16(iobase, ON);
+	EnTXCRC(iobase, 0);
+	WriteReg(iobase, I_ST_CT_0, 0x00);
+	SetBaudRate(iobase, 9600);
+	SetPulseWidth(iobase, 12);
+	SetSendPreambleCount(iobase, 0);
+
+	self->io.speed = 9600;
+	self->st_fifo.len = 0;
+
+	via_ircc_change_dongle_speed(iobase, self->io.speed,
+				     self->io.dongle_id);
+
+	WriteReg(iobase, I_ST_CT_0, 0x80);
+}
+
+/*
+ * Function via_ircc_read_dongle_id (void)
+ *
+ */
+static int via_ircc_read_dongle_id(int iobase)
+{
+	int dongle_id = 9;	/* Default to IBM */
+
+	IRDA_ERROR("via-ircc: dongle probing not supported, please specify dongle_id module parameter.\n");
+	return dongle_id;
+}
+
+/*
+ * Function via_ircc_change_dongle_speed (iobase, speed, dongle_id)
+ *    Change speed of the attach dongle
+ *    only implement two type of dongle currently.
+ */
+static void via_ircc_change_dongle_speed(int iobase, int speed,
+					 int dongle_id)
+{
+	u8 mode = 0;
+
+	/* speed is unused, as we use IsSIROn()/IsMIROn() */
+	speed = speed;
+
+	IRDA_DEBUG(1, "%s(): change_dongle_speed to %d for 0x%x, %d\n",
+		   __FUNCTION__, speed, iobase, dongle_id);
+
+	switch (dongle_id) {
+
+		/* Note: The dongle_id's listed here are derived from
+		 * nsc-ircc.c */ 
+
+	case 0x08:		/* HP HSDL-2300, HP HSDL-3600/HSDL-3610 */
+		UseOneRX(iobase, ON);	// use one RX pin   RX1,RX2
+		InvertTX(iobase, OFF);
+		InvertRX(iobase, OFF);
+
+		EnRX2(iobase, ON);	//sir to rx2
+		EnGPIOtoRX2(iobase, OFF);
+
+		if (IsSIROn(iobase)) {	//sir
+			// Mode select Off
+			SlowIRRXLowActive(iobase, ON);
+			udelay(1000);
+			SlowIRRXLowActive(iobase, OFF);
+		} else {
+			if (IsMIROn(iobase)) {	//mir
+				// Mode select On
+				SlowIRRXLowActive(iobase, OFF);
+				udelay(20);
+			} else {	// fir
+				if (IsFIROn(iobase)) {	//fir
+					// Mode select On
+					SlowIRRXLowActive(iobase, OFF);
+					udelay(20);
+				}
+			}
+		}
+		break;
+
+	case 0x09:		/* IBM31T1100 or Temic TFDS6000/TFDS6500 */
+		UseOneRX(iobase, ON);	//use ONE RX....RX1
+		InvertTX(iobase, OFF);
+		InvertRX(iobase, OFF);	// invert RX pin
+
+		EnRX2(iobase, ON);
+		EnGPIOtoRX2(iobase, OFF);
+		if (IsSIROn(iobase)) {	//sir
+			// Mode select On
+			SlowIRRXLowActive(iobase, ON);
+			udelay(20);
+			// Mode select Off
+			SlowIRRXLowActive(iobase, OFF);
+		}
+		if (IsMIROn(iobase)) {	//mir
+			// Mode select On
+			SlowIRRXLowActive(iobase, OFF);
+			udelay(20);
+			// Mode select Off
+			SlowIRRXLowActive(iobase, ON);
+		} else {	// fir
+			if (IsFIROn(iobase)) {	//fir
+				// Mode select On
+				SlowIRRXLowActive(iobase, OFF);
+				// TX On
+				WriteTX(iobase, ON);
+				udelay(20);
+				// Mode select OFF
+				SlowIRRXLowActive(iobase, ON);
+				udelay(20);
+				// TX Off
+				WriteTX(iobase, OFF);
+			}
+		}
+		break;
+
+	case 0x0d:
+		UseOneRX(iobase, OFF);	// use two RX pin   RX1,RX2
+		InvertTX(iobase, OFF);
+		InvertRX(iobase, OFF);
+		SlowIRRXLowActive(iobase, OFF);
+		if (IsSIROn(iobase)) {	//sir
+			EnGPIOtoRX2(iobase, OFF);
+			WriteGIO(iobase, OFF);
+			EnRX2(iobase, OFF);	//sir to rx2
+		} else {	// fir mir
+			EnGPIOtoRX2(iobase, OFF);
+			WriteGIO(iobase, OFF);
+			EnRX2(iobase, OFF);	//fir to rx
+		}
+		break;
+
+	case 0x11:		/* Temic TFDS4500 */
+
+		IRDA_DEBUG(2, "%s: Temic TFDS4500: One RX pin, TX normal, RX inverted.\n", __FUNCTION__);
+
+		UseOneRX(iobase, ON);	//use ONE RX....RX1
+		InvertTX(iobase, OFF);
+		InvertRX(iobase, ON);	// invert RX pin
+	
+		EnRX2(iobase, ON);	//sir to rx2
+		EnGPIOtoRX2(iobase, OFF);
+
+		if( IsSIROn(iobase) ){	//sir
+
+			// Mode select On
+			SlowIRRXLowActive(iobase, ON);
+			udelay(20);
+			// Mode select Off
+			SlowIRRXLowActive(iobase, OFF);
+
+		} else{
+			IRDA_DEBUG(0, "%s: Warning: TFDS4500 not running in SIR mode !\n", __FUNCTION__);
+		}
+		break;
+
+	case 0x0ff:		/* Vishay */
+		if (IsSIROn(iobase))
+			mode = 0;
+		else if (IsMIROn(iobase))
+			mode = 1;
+		else if (IsFIROn(iobase))
+			mode = 2;
+		else if (IsVFIROn(iobase))
+			mode = 5;	//VFIR-16
+		SI_SetMode(iobase, mode);
+		break;
+
+	default:
+		IRDA_ERROR("%s: Error: dongle_id %d unsupported !\n",
+			   __FUNCTION__, dongle_id);
+	}
+}
+
+/*
+ * Function via_ircc_change_speed (self, baud)
+ *
+ *    Change the speed of the device
+ *
+ */
+static void via_ircc_change_speed(struct via_ircc_cb *self, __u32 speed)
+{
+	struct net_device *dev = self->netdev;
+	u16 iobase;
+	u8 value = 0, bTmp;
+
+	iobase = self->io.fir_base;
+	/* Update accounting for new speed */
+	self->io.speed = speed;
+	IRDA_DEBUG(1, "%s: change_speed to %d bps.\n", __FUNCTION__, speed);
+
+	WriteReg(iobase, I_ST_CT_0, 0x0);
+
+	/* Controller mode sellection */
+	switch (speed) {
+	case 2400:
+	case 9600:
+	case 19200:
+	case 38400:
+	case 57600:
+	case 115200:
+		value = (115200/speed)-1;
+		SetSIR(iobase, ON);
+		CRC16(iobase, ON);
+		break;
+	case 576000:
+		/* FIXME: this can't be right, as it's the same as 115200,
+		 * and 576000 is MIR, not SIR. */
+		value = 0;
+		SetSIR(iobase, ON);
+		CRC16(iobase, ON);
+		break;
+	case 1152000:
+		value = 0;
+		SetMIR(iobase, ON);
+		/* FIXME: CRC ??? */
+		break;
+	case 4000000:
+		value = 0;
+		SetFIR(iobase, ON);
+		SetPulseWidth(iobase, 0);
+		SetSendPreambleCount(iobase, 14);
+		CRC16(iobase, OFF);
+		EnTXCRC(iobase, ON);
+		break;
+	case 16000000:
+		value = 0;
+		SetVFIR(iobase, ON);
+		/* FIXME: CRC ??? */
+		break;
+	default:
+		value = 0;
+		break;
+	}
+
+	/* Set baudrate to 0x19[2..7] */
+	bTmp = (ReadReg(iobase, I_CF_H_1) & 0x03);
+	bTmp |= value << 2;
+	WriteReg(iobase, I_CF_H_1, bTmp);
+
+	/* Some dongles may need to be informed about speed changes. */
+	via_ircc_change_dongle_speed(iobase, speed, self->io.dongle_id);
+
+	/* Set FIFO size to 64 */
+	SetFIFO(iobase, 64);
+
+	/* Enable IR */
+	WriteReg(iobase, I_ST_CT_0, 0x80);
+
+	// EnTXFIFOHalfLevelInt(iobase,ON);
+
+	/* Enable some interrupts so we can receive frames */
+	//EnAllInt(iobase,ON);
+
+	if (IsSIROn(iobase)) {
+		SIRFilter(iobase, ON);
+		SIRRecvAny(iobase, ON);
+	} else {
+		SIRFilter(iobase, OFF);
+		SIRRecvAny(iobase, OFF);
+	}
+
+	if (speed > 115200) {
+		/* Install FIR xmit handler */
+		dev->hard_start_xmit = via_ircc_hard_xmit_fir;
+		via_ircc_dma_receive(self);
+	} else {
+		/* Install SIR xmit handler */
+		dev->hard_start_xmit = via_ircc_hard_xmit_sir;
+	}
+	netif_wake_queue(dev);
+}
+
+/*
+ * Function via_ircc_hard_xmit (skb, dev)
+ *
+ *    Transmit the frame!
+ *
+ */
+static int via_ircc_hard_xmit_sir(struct sk_buff *skb,
+				  struct net_device *dev)
+{
+	struct via_ircc_cb *self;
+	unsigned long flags;
+	u16 iobase;
+	__u32 speed;
+
+	self = (struct via_ircc_cb *) dev->priv;
+	IRDA_ASSERT(self != NULL, return 0;);
+	iobase = self->io.fir_base;
+
+	netif_stop_queue(dev);
+	/* Check if we need to change the speed */
+	speed = irda_get_next_speed(skb);
+	if ((speed != self->io.speed) && (speed != -1)) {
+		/* Check for empty frame */
+		if (!skb->len) {
+			via_ircc_change_speed(self, speed);
+			dev->trans_start = jiffies;
+			dev_kfree_skb(skb);
+			return 0;
+		} else
+			self->new_speed = speed;
+	}
+	InitCard(iobase);
+	CommonInit(iobase);
+	SIRFilter(iobase, ON);
+	SetSIR(iobase, ON);
+	CRC16(iobase, ON);
+	EnTXCRC(iobase, 0);
+	WriteReg(iobase, I_ST_CT_0, 0x00);
+
+	spin_lock_irqsave(&self->lock, flags);
+	self->tx_buff.data = self->tx_buff.head;
+	self->tx_buff.len =
+	    async_wrap_skb(skb, self->tx_buff.data,
+			   self->tx_buff.truesize);
+
+	self->stats.tx_bytes += self->tx_buff.len;
+	/* Send this frame with old speed */
+	SetBaudRate(iobase, self->io.speed);
+	SetPulseWidth(iobase, 12);
+	SetSendPreambleCount(iobase, 0);
+	WriteReg(iobase, I_ST_CT_0, 0x80);
+
+	EnableTX(iobase, ON);
+	EnableRX(iobase, OFF);
+
+	ResetChip(iobase, 0);
+	ResetChip(iobase, 1);
+	ResetChip(iobase, 2);
+	ResetChip(iobase, 3);
+	ResetChip(iobase, 4);
+
+	EnAllInt(iobase, ON);
+	EnTXDMA(iobase, ON);
+	EnRXDMA(iobase, OFF);
+
+	irda_setup_dma(self->io.dma, self->tx_buff_dma, self->tx_buff.len,
+		       DMA_TX_MODE);
+
+	SetSendByte(iobase, self->tx_buff.len);
+	RXStart(iobase, OFF);
+	TXStart(iobase, ON);
+
+	dev->trans_start = jiffies;
+	spin_unlock_irqrestore(&self->lock, flags);
+	dev_kfree_skb(skb);
+	return 0;
+}
+
+static int via_ircc_hard_xmit_fir(struct sk_buff *skb,
+				  struct net_device *dev)
+{
+	struct via_ircc_cb *self;
+	u16 iobase;
+	__u32 speed;
+	unsigned long flags;
+
+	self = (struct via_ircc_cb *) dev->priv;
+	iobase = self->io.fir_base;
+
+	if (self->st_fifo.len)
+		return 0;
+	if (self->chip_id == 0x3076)
+		iodelay(1500);
+	else
+		udelay(1500);
+	netif_stop_queue(dev);
+	speed = irda_get_next_speed(skb);
+	if ((speed != self->io.speed) && (speed != -1)) {
+		if (!skb->len) {
+			via_ircc_change_speed(self, speed);
+			dev->trans_start = jiffies;
+			dev_kfree_skb(skb);
+			return 0;
+		} else
+			self->new_speed = speed;
+	}
+	spin_lock_irqsave(&self->lock, flags);
+	self->tx_fifo.queue[self->tx_fifo.free].start = self->tx_fifo.tail;
+	self->tx_fifo.queue[self->tx_fifo.free].len = skb->len;
+
+	self->tx_fifo.tail += skb->len;
+	self->stats.tx_bytes += skb->len;
+	memcpy(self->tx_fifo.queue[self->tx_fifo.free].start, skb->data,
+	       skb->len);
+	self->tx_fifo.len++;
+	self->tx_fifo.free++;
+//F01   if (self->tx_fifo.len == 1) {
+	via_ircc_dma_xmit(self, iobase);
+//F01   }
+//F01   if (self->tx_fifo.free < (MAX_TX_WINDOW -1 )) netif_wake_queue(self->netdev);
+	dev->trans_start = jiffies;
+	dev_kfree_skb(skb);
+	spin_unlock_irqrestore(&self->lock, flags);
+	return 0;
+
+}
+
+static int via_ircc_dma_xmit(struct via_ircc_cb *self, u16 iobase)
+{
+	EnTXDMA(iobase, OFF);
+	self->io.direction = IO_XMIT;
+	EnPhys(iobase, ON);
+	EnableTX(iobase, ON);
+	EnableRX(iobase, OFF);
+	ResetChip(iobase, 0);
+	ResetChip(iobase, 1);
+	ResetChip(iobase, 2);
+	ResetChip(iobase, 3);
+	ResetChip(iobase, 4);
+	EnAllInt(iobase, ON);
+	EnTXDMA(iobase, ON);
+	EnRXDMA(iobase, OFF);
+	irda_setup_dma(self->io.dma,
+		       ((u8 *)self->tx_fifo.queue[self->tx_fifo.ptr].start -
+			self->tx_buff.head) + self->tx_buff_dma,
+		       self->tx_fifo.queue[self->tx_fifo.ptr].len, DMA_TX_MODE);
+	IRDA_DEBUG(1, "%s: tx_fifo.ptr=%x,len=%x,tx_fifo.len=%x..\n",
+		   __FUNCTION__, self->tx_fifo.ptr,
+		   self->tx_fifo.queue[self->tx_fifo.ptr].len,
+		   self->tx_fifo.len);
+
+	SetSendByte(iobase, self->tx_fifo.queue[self->tx_fifo.ptr].len);
+	RXStart(iobase, OFF);
+	TXStart(iobase, ON);
+	return 0;
+
+}
+
+/*
+ * Function via_ircc_dma_xmit_complete (self)
+ *
+ *    The transfer of a frame in finished. This function will only be called 
+ *    by the interrupt handler
+ *
+ */
+static int via_ircc_dma_xmit_complete(struct via_ircc_cb *self)
+{
+	int iobase;
+	int ret = TRUE;
+	u8 Tx_status;
+
+	IRDA_DEBUG(3, "%s()\n", __FUNCTION__);
+
+	iobase = self->io.fir_base;
+	/* Disable DMA */
+//      DisableDmaChannel(self->io.dma);
+	/* Check for underrrun! */
+	/* Clear bit, by writing 1 into it */
+	Tx_status = GetTXStatus(iobase);
+	if (Tx_status & 0x08) {
+		self->stats.tx_errors++;
+		self->stats.tx_fifo_errors++;
+		hwreset(self);
+// how to clear underrrun ?
+	} else {
+		self->stats.tx_packets++;
+		ResetChip(iobase, 3);
+		ResetChip(iobase, 4);
+	}
+	/* Check if we need to change the speed */
+	if (self->new_speed) {
+		via_ircc_change_speed(self, self->new_speed);
+		self->new_speed = 0;
+	}
+
+	/* Finished with this frame, so prepare for next */
+	if (IsFIROn(iobase)) {
+		if (self->tx_fifo.len) {
+			self->tx_fifo.len--;
+			self->tx_fifo.ptr++;
+		}
+	}
+	IRDA_DEBUG(1,
+		   "%s: tx_fifo.len=%x ,tx_fifo.ptr=%x,tx_fifo.free=%x...\n",
+		   __FUNCTION__,
+		   self->tx_fifo.len, self->tx_fifo.ptr, self->tx_fifo.free);
+/* F01_S
+	// Any frames to be sent back-to-back? 
+	if (self->tx_fifo.len) {
+		// Not finished yet! 
+	  	via_ircc_dma_xmit(self, iobase);
+		ret = FALSE;
+	} else { 
+F01_E*/
+	// Reset Tx FIFO info 
+	self->tx_fifo.len = self->tx_fifo.ptr = self->tx_fifo.free = 0;
+	self->tx_fifo.tail = self->tx_buff.head;
+//F01   }
+
+	// Make sure we have room for more frames 
+//F01   if (self->tx_fifo.free < (MAX_TX_WINDOW -1 )) {
+	// Not busy transmitting anymore 
+	// Tell the network layer, that we can accept more frames 
+	netif_wake_queue(self->netdev);
+//F01   }
+	return ret;
+}
+
+/*
+ * Function via_ircc_dma_receive (self)
+ *
+ *    Set configuration for receive a frame.
+ *
+ */
+static int via_ircc_dma_receive(struct via_ircc_cb *self)
+{
+	int iobase;
+
+	iobase = self->io.fir_base;
+
+	IRDA_DEBUG(3, "%s()\n", __FUNCTION__);
+
+	self->tx_fifo.len = self->tx_fifo.ptr = self->tx_fifo.free = 0;
+	self->tx_fifo.tail = self->tx_buff.head;
+	self->RxDataReady = 0;
+	self->io.direction = IO_RECV;
+	self->rx_buff.data = self->rx_buff.head;
+	self->st_fifo.len = self->st_fifo.pending_bytes = 0;
+	self->st_fifo.tail = self->st_fifo.head = 0;
+
+	EnPhys(iobase, ON);
+	EnableTX(iobase, OFF);
+	EnableRX(iobase, ON);
+
+	ResetChip(iobase, 0);
+	ResetChip(iobase, 1);
+	ResetChip(iobase, 2);
+	ResetChip(iobase, 3);
+	ResetChip(iobase, 4);
+
+	EnAllInt(iobase, ON);
+	EnTXDMA(iobase, OFF);
+	EnRXDMA(iobase, ON);
+	irda_setup_dma(self->io.dma2, self->rx_buff_dma,
+		  self->rx_buff.truesize, DMA_RX_MODE);
+	TXStart(iobase, OFF);
+	RXStart(iobase, ON);
+
+	return 0;
+}
+
+/*
+ * Function via_ircc_dma_receive_complete (self)
+ *
+ *    Controller Finished with receiving frames,
+ *    and this routine is call by ISR
+ *    
+ */
+static int via_ircc_dma_receive_complete(struct via_ircc_cb *self,
+					 int iobase)
+{
+	struct st_fifo *st_fifo;
+	struct sk_buff *skb;
+	int len, i;
+	u8 status = 0;
+
+	iobase = self->io.fir_base;
+	st_fifo = &self->st_fifo;
+
+	if (self->io.speed < 4000000) {	//Speed below FIR
+		len = GetRecvByte(iobase, self);
+		skb = dev_alloc_skb(len + 1);
+		if (skb == NULL)
+			return FALSE;
+		// Make sure IP header gets aligned 
+		skb_reserve(skb, 1);
+		skb_put(skb, len - 2);
+		if (self->chip_id == 0x3076) {
+			for (i = 0; i < len - 2; i++)
+				skb->data[i] = self->rx_buff.data[i * 2];
+		} else {
+			if (self->chip_id == 0x3096) {
+				for (i = 0; i < len - 2; i++)
+					skb->data[i] =
+					    self->rx_buff.data[i];
+			}
+		}
+		// Move to next frame 
+		self->rx_buff.data += len;
+		self->stats.rx_bytes += len;
+		self->stats.rx_packets++;
+		skb->dev = self->netdev;
+		skb->mac.raw = skb->data;
+		skb->protocol = htons(ETH_P_IRDA);
+		netif_rx(skb);
+		return TRUE;
+	}
+
+	else {			//FIR mode
+		len = GetRecvByte(iobase, self);
+		if (len == 0)
+			return TRUE;	//interrupt only, data maybe move by RxT  
+		if (((len - 4) < 2) || ((len - 4) > 2048)) {
+			IRDA_DEBUG(1, "%s(): Trouble:len=%x,CurCount=%x,LastCount=%x..\n",
+				   __FUNCTION__, len, RxCurCount(iobase, self),
+				   self->RxLastCount);
+			hwreset(self);
+			return FALSE;
+		}
+		IRDA_DEBUG(2, "%s(): fifo.len=%x,len=%x,CurCount=%x..\n",
+			   __FUNCTION__,
+			   st_fifo->len, len - 4, RxCurCount(iobase, self));
+
+		st_fifo->entries[st_fifo->tail].status = status;
+		st_fifo->entries[st_fifo->tail].len = len;
+		st_fifo->pending_bytes += len;
+		st_fifo->tail++;
+		st_fifo->len++;
+		if (st_fifo->tail > MAX_RX_WINDOW)
+			st_fifo->tail = 0;
+		self->RxDataReady = 0;
+
+		// It maybe have MAX_RX_WINDOW package receive by
+		// receive_complete before Timer IRQ
+/* F01_S
+          if (st_fifo->len < (MAX_RX_WINDOW+2 )) { 
+		  RXStart(iobase,ON);
+	  	  SetTimer(iobase,4);
+	  }
+	  else	  { 
+F01_E */
+		EnableRX(iobase, OFF);
+		EnRXDMA(iobase, OFF);
+		RXStart(iobase, OFF);
+//F01_S
+		// Put this entry back in fifo 
+		if (st_fifo->head > MAX_RX_WINDOW)
+			st_fifo->head = 0;
+		status = st_fifo->entries[st_fifo->head].status;
+		len = st_fifo->entries[st_fifo->head].len;
+		st_fifo->head++;
+		st_fifo->len--;
+
+		skb = dev_alloc_skb(len + 1 - 4);
+		/*
+		 * if frame size,data ptr,or skb ptr are wrong ,the get next
+		 * entry.
+		 */
+		if ((skb == NULL) || (skb->data == NULL)
+		    || (self->rx_buff.data == NULL) || (len < 6)) {
+			self->stats.rx_dropped++;
+			return TRUE;
+		}
+		skb_reserve(skb, 1);
+		skb_put(skb, len - 4);
+
+		memcpy(skb->data, self->rx_buff.data, len - 4);
+		IRDA_DEBUG(2, "%s(): len=%x.rx_buff=%p\n", __FUNCTION__,
+			   len - 4, self->rx_buff.data);
+
+		// Move to next frame 
+		self->rx_buff.data += len;
+		self->stats.rx_bytes += len;
+		self->stats.rx_packets++;
+		skb->dev = self->netdev;
+		skb->mac.raw = skb->data;
+		skb->protocol = htons(ETH_P_IRDA);
+		netif_rx(skb);
+
+//F01_E
+	}			//FIR
+	return TRUE;
+
+}
+
+/*
+ * if frame is received , but no INT ,then use this routine to upload frame.
+ */
+static int upload_rxdata(struct via_ircc_cb *self, int iobase)
+{
+	struct sk_buff *skb;
+	int len;
+	struct st_fifo *st_fifo;
+	st_fifo = &self->st_fifo;
+
+	len = GetRecvByte(iobase, self);
+
+	IRDA_DEBUG(2, "%s(): len=%x\n", __FUNCTION__, len);
+
+	skb = dev_alloc_skb(len + 1);
+	if ((skb == NULL) || ((len - 4) < 2)) {
+		self->stats.rx_dropped++;
+		return FALSE;
+	}
+	skb_reserve(skb, 1);
+	skb_put(skb, len - 4 + 1);
+	memcpy(skb->data, self->rx_buff.data, len - 4 + 1);
+	st_fifo->tail++;
+	st_fifo->len++;
+	if (st_fifo->tail > MAX_RX_WINDOW)
+		st_fifo->tail = 0;
+	// Move to next frame 
+	self->rx_buff.data += len;
+	self->stats.rx_bytes += len;
+	self->stats.rx_packets++;
+	skb->dev = self->netdev;
+	skb->mac.raw = skb->data;
+	skb->protocol = htons(ETH_P_IRDA);
+	netif_rx(skb);
+	if (st_fifo->len < (MAX_RX_WINDOW + 2)) {
+		RXStart(iobase, ON);
+	} else {
+		EnableRX(iobase, OFF);
+		EnRXDMA(iobase, OFF);
+		RXStart(iobase, OFF);
+	}
+	return TRUE;
+}
+
+/*
+ * Implement back to back receive , use this routine to upload data.
+ */
+
+static int RxTimerHandler(struct via_ircc_cb *self, int iobase)
+{
+	struct st_fifo *st_fifo;
+	struct sk_buff *skb;
+	int len;
+	u8 status;
+
+	st_fifo = &self->st_fifo;
+
+	if (CkRxRecv(iobase, self)) {
+		// if still receiving ,then return ,don't upload frame 
+		self->RetryCount = 0;
+		SetTimer(iobase, 20);
+		self->RxDataReady++;
+		return FALSE;
+	} else
+		self->RetryCount++;
+
+	if ((self->RetryCount >= 1) ||
+	    ((st_fifo->pending_bytes + 2048) > self->rx_buff.truesize)
+	    || (st_fifo->len >= (MAX_RX_WINDOW))) {
+		while (st_fifo->len > 0) {	//upload frame
+			// Put this entry back in fifo 
+			if (st_fifo->head > MAX_RX_WINDOW)
+				st_fifo->head = 0;
+			status = st_fifo->entries[st_fifo->head].status;
+			len = st_fifo->entries[st_fifo->head].len;
+			st_fifo->head++;
+			st_fifo->len--;
+
+			skb = dev_alloc_skb(len + 1 - 4);
+			/*
+			 * if frame size, data ptr, or skb ptr are wrong,
+			 * then get next entry.
+			 */
+			if ((skb == NULL) || (skb->data == NULL)
+			    || (self->rx_buff.data == NULL) || (len < 6)) {
+				self->stats.rx_dropped++;
+				continue;
+			}
+			skb_reserve(skb, 1);
+			skb_put(skb, len - 4);
+			memcpy(skb->data, self->rx_buff.data, len - 4);
+
+			IRDA_DEBUG(2, "%s(): len=%x.head=%x\n", __FUNCTION__,
+				   len - 4, st_fifo->head);
+
+			// Move to next frame 
+			self->rx_buff.data += len;
+			self->stats.rx_bytes += len;
+			self->stats.rx_packets++;
+			skb->dev = self->netdev;
+			skb->mac.raw = skb->data;
+			skb->protocol = htons(ETH_P_IRDA);
+			netif_rx(skb);
+		}		//while
+		self->RetryCount = 0;
+
+		IRDA_DEBUG(2,
+			   "%s(): End of upload HostStatus=%x,RxStatus=%x\n",
+			   __FUNCTION__,
+			   GetHostStatus(iobase), GetRXStatus(iobase));
+
+		/*
+		 * if frame is receive complete at this routine ,then upload
+		 * frame.
+		 */
+		if ((GetRXStatus(iobase) & 0x10)
+		    && (RxCurCount(iobase, self) != self->RxLastCount)) {
+			upload_rxdata(self, iobase);
+			if (irda_device_txqueue_empty(self->netdev))
+				via_ircc_dma_receive(self);
+		}
+	}			// timer detect complete
+	else
+		SetTimer(iobase, 4);
+	return TRUE;
+
+}
+
+
+
+/*
+ * Function via_ircc_interrupt (irq, dev_id, regs)
+ *
+ *    An interrupt from the chip has arrived. Time to do some work
+ *
+ */
+static irqreturn_t via_ircc_interrupt(int irq, void *dev_id,
+				      struct pt_regs *regs)
+{
+	struct net_device *dev = (struct net_device *) dev_id;
+	struct via_ircc_cb *self;
+	int iobase;
+	u8 iHostIntType, iRxIntType, iTxIntType;
+
+	if (!dev) {
+		IRDA_WARNING("%s: irq %d for unknown device.\n", driver_name,
+			     irq);
+		return IRQ_NONE;
+	}
+	self = (struct via_ircc_cb *) dev->priv;
+	iobase = self->io.fir_base;
+	spin_lock(&self->lock);
+	iHostIntType = GetHostStatus(iobase);
+
+	IRDA_DEBUG(4, "%s(): iHostIntType %02x:  %s %s %s  %02x\n",
+		   __FUNCTION__, iHostIntType,
+		   (iHostIntType & 0x40) ? "Timer" : "",
+		   (iHostIntType & 0x20) ? "Tx" : "",
+		   (iHostIntType & 0x10) ? "Rx" : "",
+		   (iHostIntType & 0x0e) >> 1);
+
+	if ((iHostIntType & 0x40) != 0) {	//Timer Event
+		self->EventFlag.TimeOut++;
+		ClearTimerInt(iobase, 1);
+		if (self->io.direction == IO_XMIT) {
+			via_ircc_dma_xmit(self, iobase);
+		}
+		if (self->io.direction == IO_RECV) {
+			/*
+			 * frame ready hold too long, must reset.
+			 */
+			if (self->RxDataReady > 30) {
+				hwreset(self);
+				if (irda_device_txqueue_empty(self->netdev)) {
+					via_ircc_dma_receive(self);
+				}
+			} else {	// call this to upload frame.
+				RxTimerHandler(self, iobase);
+			}
+		}		//RECV
+	}			//Timer Event
+	if ((iHostIntType & 0x20) != 0) {	//Tx Event
+		iTxIntType = GetTXStatus(iobase);
+
+		IRDA_DEBUG(4, "%s(): iTxIntType %02x:  %s %s %s %s\n",
+			   __FUNCTION__, iTxIntType,
+			   (iTxIntType & 0x08) ? "FIFO underr." : "",
+			   (iTxIntType & 0x04) ? "EOM" : "",
+			   (iTxIntType & 0x02) ? "FIFO ready" : "",
+			   (iTxIntType & 0x01) ? "Early EOM" : "");
+
+		if (iTxIntType & 0x4) {
+			self->EventFlag.EOMessage++;	// read and will auto clean
+			if (via_ircc_dma_xmit_complete(self)) {
+				if (irda_device_txqueue_empty
+				    (self->netdev)) {
+					via_ircc_dma_receive(self);
+				}
+			} else {
+				self->EventFlag.Unknown++;
+			}
+		}		//EOP
+	}			//Tx Event
+	//----------------------------------------
+	if ((iHostIntType & 0x10) != 0) {	//Rx Event
+		/* Check if DMA has finished */
+		iRxIntType = GetRXStatus(iobase);
+
+		IRDA_DEBUG(4, "%s(): iRxIntType %02x:  %s %s %s %s %s %s %s\n",
+			   __FUNCTION__, iRxIntType,
+			   (iRxIntType & 0x80) ? "PHY err."	: "",
+			   (iRxIntType & 0x40) ? "CRC err"	: "",
+			   (iRxIntType & 0x20) ? "FIFO overr."	: "",
+			   (iRxIntType & 0x10) ? "EOF"		: "",
+			   (iRxIntType & 0x08) ? "RxData"	: "",
+			   (iRxIntType & 0x02) ? "RxMaxLen"	: "",
+			   (iRxIntType & 0x01) ? "SIR bad"	: "");
+		if (!iRxIntType)
+			IRDA_DEBUG(3, "%s(): RxIRQ =0\n", __FUNCTION__);
+
+		if (iRxIntType & 0x10) {
+			if (via_ircc_dma_receive_complete(self, iobase)) {
+//F01       if(!(IsFIROn(iobase)))  via_ircc_dma_receive(self);
+				via_ircc_dma_receive(self);
+			}
+		}		// No ERR     
+		else {		//ERR
+			IRDA_DEBUG(4, "%s(): RxIRQ ERR:iRxIntType=%x,HostIntType=%x,CurCount=%x,RxLastCount=%x_____\n",
+				   __FUNCTION__, iRxIntType, iHostIntType,
+				   RxCurCount(iobase, self),
+				   self->RxLastCount);
+
+			if (iRxIntType & 0x20) {	//FIFO OverRun ERR
+				ResetChip(iobase, 0);
+				ResetChip(iobase, 1);
+			} else {	//PHY,CRC ERR
+
+				if (iRxIntType != 0x08)
+					hwreset(self);	//F01
+			}
+			via_ircc_dma_receive(self);
+		}		//ERR
+
+	}			//Rx Event
+	spin_unlock(&self->lock);
+	return IRQ_RETVAL(iHostIntType);
+}
+
+static void hwreset(struct via_ircc_cb *self)
+{
+	int iobase;
+	iobase = self->io.fir_base;
+
+	IRDA_DEBUG(3, "%s()\n", __FUNCTION__);
+
+	ResetChip(iobase, 5);
+	EnableDMA(iobase, OFF);
+	EnableTX(iobase, OFF);
+	EnableRX(iobase, OFF);
+	EnRXDMA(iobase, OFF);
+	EnTXDMA(iobase, OFF);
+	RXStart(iobase, OFF);
+	TXStart(iobase, OFF);
+	InitCard(iobase);
+	CommonInit(iobase);
+	SIRFilter(iobase, ON);
+	SetSIR(iobase, ON);
+	CRC16(iobase, ON);
+	EnTXCRC(iobase, 0);
+	WriteReg(iobase, I_ST_CT_0, 0x00);
+	SetBaudRate(iobase, 9600);
+	SetPulseWidth(iobase, 12);
+	SetSendPreambleCount(iobase, 0);
+	WriteReg(iobase, I_ST_CT_0, 0x80);
+
+	/* Restore speed. */
+	via_ircc_change_speed(self, self->io.speed);
+
+	self->st_fifo.len = 0;
+}
+
+/*
+ * Function via_ircc_is_receiving (self)
+ *
+ *    Return TRUE is we are currently receiving a frame
+ *
+ */
+static int via_ircc_is_receiving(struct via_ircc_cb *self)
+{
+	int status = FALSE;
+	int iobase;
+
+	IRDA_ASSERT(self != NULL, return FALSE;);
+
+	iobase = self->io.fir_base;
+	if (CkRxRecv(iobase, self))
+		status = TRUE;
+
+	IRDA_DEBUG(2, "%s(): status=%x....\n", __FUNCTION__, status);
+
+	return status;
+}
+
+
+/*
+ * Function via_ircc_net_open (dev)
+ *
+ *    Start the device
+ *
+ */
+static int via_ircc_net_open(struct net_device *dev)
+{
+	struct via_ircc_cb *self;
+	int iobase;
+	char hwname[32];
+
+	IRDA_DEBUG(3, "%s()\n", __FUNCTION__);
+
+	IRDA_ASSERT(dev != NULL, return -1;);
+	self = (struct via_ircc_cb *) dev->priv;
+	self->stats.rx_packets = 0;
+	IRDA_ASSERT(self != NULL, return 0;);
+	iobase = self->io.fir_base;
+	if (request_irq(self->io.irq, via_ircc_interrupt, 0, dev->name, dev)) {
+		IRDA_WARNING("%s, unable to allocate irq=%d\n", driver_name,
+			     self->io.irq);
+		return -EAGAIN;
+	}
+	/*
+	 * Always allocate the DMA channel after the IRQ, and clean up on 
+	 * failure.
+	 */
+	if (request_dma(self->io.dma, dev->name)) {
+		IRDA_WARNING("%s, unable to allocate dma=%d\n", driver_name,
+			     self->io.dma);
+		free_irq(self->io.irq, self);
+		return -EAGAIN;
+	}
+	if (self->io.dma2 != self->io.dma) {
+		if (request_dma(self->io.dma2, dev->name)) {
+			IRDA_WARNING("%s, unable to allocate dma2=%d\n",
+				     driver_name, self->io.dma2);
+			free_irq(self->io.irq, self);
+			return -EAGAIN;
+		}
+	}
+
+
+	/* turn on interrupts */
+	EnAllInt(iobase, ON);
+	EnInternalLoop(iobase, OFF);
+	EnExternalLoop(iobase, OFF);
+
+	/* */
+	via_ircc_dma_receive(self);
+
+	/* Ready to play! */
+	netif_start_queue(dev);
+
+	/* 
+	 * Open new IrLAP layer instance, now that everything should be
+	 * initialized properly 
+	 */
+	sprintf(hwname, "VIA @ 0x%x", iobase);
+	self->irlap = irlap_open(dev, &self->qos, hwname);
+
+	self->RxLastCount = 0;
+
+	return 0;
+}
+
+/*
+ * Function via_ircc_net_close (dev)
+ *
+ *    Stop the device
+ *
+ */
+static int via_ircc_net_close(struct net_device *dev)
+{
+	struct via_ircc_cb *self;
+	int iobase;
+
+	IRDA_DEBUG(3, "%s()\n", __FUNCTION__);
+
+	IRDA_ASSERT(dev != NULL, return -1;);
+	self = (struct via_ircc_cb *) dev->priv;
+	IRDA_ASSERT(self != NULL, return 0;);
+
+	/* Stop device */
+	netif_stop_queue(dev);
+	/* Stop and remove instance of IrLAP */
+	if (self->irlap)
+		irlap_close(self->irlap);
+	self->irlap = NULL;
+	iobase = self->io.fir_base;
+	EnTXDMA(iobase, OFF);
+	EnRXDMA(iobase, OFF);
+	DisableDmaChannel(self->io.dma);
+
+	/* Disable interrupts */
+	EnAllInt(iobase, OFF);
+	free_irq(self->io.irq, dev);
+	free_dma(self->io.dma);
+
+	return 0;
+}
+
+/*
+ * Function via_ircc_net_ioctl (dev, rq, cmd)
+ *
+ *    Process IOCTL commands for this device
+ *
+ */
+static int via_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq,
+			      int cmd)
+{
+	struct if_irda_req *irq = (struct if_irda_req *) rq;
+	struct via_ircc_cb *self;
+	unsigned long flags;
+	int ret = 0;
+
+	IRDA_ASSERT(dev != NULL, return -1;);
+	self = dev->priv;
+	IRDA_ASSERT(self != NULL, return -1;);
+	IRDA_DEBUG(1, "%s(), %s, (cmd=0x%X)\n", __FUNCTION__, dev->name,
+		   cmd);
+	/* Disable interrupts & save flags */
+	spin_lock_irqsave(&self->lock, flags);
+	switch (cmd) {
+	case SIOCSBANDWIDTH:	/* Set bandwidth */
+		if (!capable(CAP_NET_ADMIN)) {
+			ret = -EPERM;
+			goto out;
+		}
+		via_ircc_change_speed(self, irq->ifr_baudrate);
+		break;
+	case SIOCSMEDIABUSY:	/* Set media busy */
+		if (!capable(CAP_NET_ADMIN)) {
+			ret = -EPERM;
+			goto out;
+		}
+		irda_device_set_media_busy(self->netdev, TRUE);
+		break;
+	case SIOCGRECEIVING:	/* Check if we are receiving right now */
+		irq->ifr_receiving = via_ircc_is_receiving(self);
+		break;
+	default:
+		ret = -EOPNOTSUPP;
+	}
+      out:
+	spin_unlock_irqrestore(&self->lock, flags);
+	return ret;
+}
+
+static struct net_device_stats *via_ircc_net_get_stats(struct net_device
+						       *dev)
+{
+	struct via_ircc_cb *self = (struct via_ircc_cb *) dev->priv;
+
+	return &self->stats;
+}
+
+MODULE_AUTHOR("VIA Technologies,inc");
+MODULE_DESCRIPTION("VIA IrDA Device Driver");
+MODULE_LICENSE("GPL");
+
+module_init(via_ircc_init);
+module_exit(via_ircc_cleanup);
diff --git a/drivers/net/irda/via-ircc.h b/drivers/net/irda/via-ircc.h
new file mode 100644
index 0000000..204b1b3
--- /dev/null
+++ b/drivers/net/irda/via-ircc.h
@@ -0,0 +1,853 @@
+/*********************************************************************
+ *                
+ * Filename:      via-ircc.h
+ * Version:       1.0
+ * Description:   Driver for the VIA VT8231/VT8233 IrDA chipsets
+ * Author:        VIA Technologies, inc
+ * Date  :	  08/06/2003
+
+Copyright (c) 1998-2003 VIA Technologies, Inc.
+
+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, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTIES OR REPRESENTATIONS; 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.
+
+ * Comment:
+ * jul/08/2002 : Rx buffer length should use Rx ring ptr.	
+ * Oct/28/2002 : Add SB id for 3147 and 3177.	
+ * jul/09/2002 : only implement two kind of dongle currently.
+ * Oct/02/2002 : work on VT8231 and VT8233 .
+ * Aug/06/2003 : change driver format to pci driver .
+ ********************************************************************/
+#ifndef via_IRCC_H
+#define via_IRCC_H
+#include <linux/time.h>
+#include <linux/spinlock.h>
+#include <linux/pm.h>
+#include <linux/types.h>
+#include <asm/io.h>
+
+#define MAX_TX_WINDOW 7
+#define MAX_RX_WINDOW 7
+
+struct st_fifo_entry {
+	int status;
+	int len;
+};
+
+struct st_fifo {
+	struct st_fifo_entry entries[MAX_RX_WINDOW + 2];
+	int pending_bytes;
+	int head;
+	int tail;
+	int len;
+};
+
+struct frame_cb {
+	void *start;		/* Start of frame in DMA mem */
+	int len;		/* Lenght of frame in DMA mem */
+};
+
+struct tx_fifo {
+	struct frame_cb queue[MAX_TX_WINDOW + 2];	/* Info about frames in queue */
+	int ptr;		/* Currently being sent */
+	int len;		/* Lenght of queue */
+	int free;		/* Next free slot */
+	void *tail;		/* Next free start in DMA mem */
+};
+
+
+struct eventflag		// for keeping track of Interrupt Events
+{
+	//--------tx part
+	unsigned char TxFIFOUnderRun;
+	unsigned char EOMessage;
+	unsigned char TxFIFOReady;
+	unsigned char EarlyEOM;
+	//--------rx part
+	unsigned char PHYErr;
+	unsigned char CRCErr;
+	unsigned char RxFIFOOverRun;
+	unsigned char EOPacket;
+	unsigned char RxAvail;
+	unsigned char TooLargePacket;
+	unsigned char SIRBad;
+	//--------unknown
+	unsigned char Unknown;
+	//----------
+	unsigned char TimeOut;
+	unsigned char RxDMATC;
+	unsigned char TxDMATC;
+};
+
+/* Private data for each instance */
+struct via_ircc_cb {
+	struct st_fifo st_fifo;	/* Info about received frames */
+	struct tx_fifo tx_fifo;	/* Info about frames to be transmitted */
+
+	struct net_device *netdev;	/* Yes! we are some kind of netdevice */
+	struct net_device_stats stats;
+
+	struct irlap_cb *irlap;	/* The link layer we are binded to */
+	struct qos_info qos;	/* QoS capabilities for this device */
+
+	chipio_t io;		/* IrDA controller information */
+	iobuff_t tx_buff;	/* Transmit buffer */
+	iobuff_t rx_buff;	/* Receive buffer */
+	dma_addr_t tx_buff_dma;
+	dma_addr_t rx_buff_dma;
+
+	__u8 ier;		/* Interrupt enable register */
+
+	struct timeval stamp;
+	struct timeval now;
+
+	spinlock_t lock;	/* For serializing operations */
+
+	__u32 flags;		/* Interface flags */
+	__u32 new_speed;
+	int index;		/* Instance index */
+
+	struct eventflag EventFlag;
+	struct pm_dev *dev;
+	unsigned int chip_id;	/* to remember chip id */
+	unsigned int RetryCount;
+	unsigned int RxDataReady;
+	unsigned int RxLastCount;
+};
+
+
+//---------I=Infrared,  H=Host, M=Misc, T=Tx, R=Rx, ST=Status,
+//         CF=Config, CT=Control, L=Low, H=High, C=Count
+#define  I_CF_L_0  		0x10
+#define  I_CF_H_0		0x11
+#define  I_SIR_BOF		0x12
+#define  I_SIR_EOF		0x13
+#define  I_ST_CT_0		0x15
+#define  I_ST_L_1		0x16
+#define  I_ST_H_1		0x17
+#define  I_CF_L_1		0x18
+#define  I_CF_H_1		0x19
+#define  I_CF_L_2		0x1a
+#define  I_CF_H_2		0x1b
+#define  I_CF_3		0x1e
+#define  H_CT			0x20
+#define  H_ST			0x21
+#define  M_CT			0x22
+#define  TX_CT_1		0x23
+#define  TX_CT_2		0x24
+#define  TX_ST			0x25
+#define  RX_CT			0x26
+#define  RX_ST			0x27
+#define  RESET			0x28
+#define  P_ADDR		0x29
+#define  RX_C_L		0x2a
+#define  RX_C_H		0x2b
+#define  RX_P_L		0x2c
+#define  RX_P_H		0x2d
+#define  TX_C_L		0x2e
+#define  TX_C_H		0x2f
+#define  TIMER         	0x32
+#define  I_CF_4         	0x33
+#define  I_T_C_L		0x34
+#define  I_T_C_H		0x35
+#define  VERSION		0x3f
+//-------------------------------
+#define StartAddr 	0x10	// the first register address
+#define EndAddr 	0x3f	// the last register address
+#define GetBit(val,bit)  val = (unsigned char) ((val>>bit) & 0x1)
+			// Returns the bit
+#define SetBit(val,bit)  val= (unsigned char ) (val | (0x1 << bit))
+			// Sets bit to 1
+#define ResetBit(val,bit) val= (unsigned char ) (val & ~(0x1 << bit))
+			// Sets bit to 0
+
+#define OFF   0
+#define ON   1
+#define DMA_TX_MODE   0x08
+#define DMA_RX_MODE   0x04
+
+#define DMA1   0
+#define DMA2   0xc0
+#define MASK1   DMA1+0x0a
+#define MASK2   DMA2+0x14
+
+#define Clk_bit 0x40
+#define Tx_bit 0x01
+#define Rd_Valid 0x08
+#define RxBit 0x08
+
+static void DisableDmaChannel(unsigned int channel)
+{
+	switch (channel) {	// 8 Bit DMA channels DMAC1
+	case 0:
+		outb(4, MASK1);	//mask channel 0
+		break;
+	case 1:
+		outb(5, MASK1);	//Mask channel 1
+		break;
+	case 2:
+		outb(6, MASK1);	//Mask channel 2
+		break;
+	case 3:
+		outb(7, MASK1);	//Mask channel 3
+		break;
+	case 5:
+		outb(5, MASK2);	//Mask channel 5
+		break;
+	case 6:
+		outb(6, MASK2);	//Mask channel 6
+		break;
+	case 7:
+		outb(7, MASK2);	//Mask channel 7
+		break;
+	default:
+		break;
+	};			//Switch
+}
+
+static unsigned char ReadLPCReg(int iRegNum)
+{
+	unsigned char iVal;
+
+	outb(0x87, 0x2e);
+	outb(0x87, 0x2e);
+	outb(iRegNum, 0x2e);
+	iVal = inb(0x2f);
+	outb(0xaa, 0x2e);
+
+	return iVal;
+}
+
+static void WriteLPCReg(int iRegNum, unsigned char iVal)
+{
+
+	outb(0x87, 0x2e);
+	outb(0x87, 0x2e);
+	outb(iRegNum, 0x2e);
+	outb(iVal, 0x2f);
+	outb(0xAA, 0x2e);
+}
+
+static __u8 ReadReg(unsigned int BaseAddr, int iRegNum)
+{
+	return ((__u8) inb(BaseAddr + iRegNum));
+}
+
+static void WriteReg(unsigned int BaseAddr, int iRegNum, unsigned char iVal)
+{
+	outb(iVal, BaseAddr + iRegNum);
+}
+
+static int WriteRegBit(unsigned int BaseAddr, unsigned char RegNum,
+		unsigned char BitPos, unsigned char value)
+{
+	__u8 Rtemp, Wtemp;
+
+	if (BitPos > 7) {
+		return -1;
+	}
+	if ((RegNum < StartAddr) || (RegNum > EndAddr))
+		return -1;
+	Rtemp = ReadReg(BaseAddr, RegNum);
+	if (value == 0)
+		Wtemp = ResetBit(Rtemp, BitPos);
+	else {
+		if (value == 1)
+			Wtemp = SetBit(Rtemp, BitPos);
+		else
+			return -1;
+	}
+	WriteReg(BaseAddr, RegNum, Wtemp);
+	return 0;
+}
+
+static __u8 CheckRegBit(unsigned int BaseAddr, unsigned char RegNum,
+		 unsigned char BitPos)
+{
+	__u8 temp;
+
+	if (BitPos > 7)
+		return 0xff;
+	if ((RegNum < StartAddr) || (RegNum > EndAddr)) {
+//     printf("what is the register %x!\n",RegNum);
+	}
+	temp = ReadReg(BaseAddr, RegNum);
+	return GetBit(temp, BitPos);
+}
+
+static void SetMaxRxPacketSize(__u16 iobase, __u16 size)
+{
+	__u16 low, high;
+	if ((size & 0xe000) == 0) {
+		low = size & 0x00ff;
+		high = (size & 0x1f00) >> 8;
+		WriteReg(iobase, I_CF_L_2, low);
+		WriteReg(iobase, I_CF_H_2, high);
+
+	}
+
+}
+
+//for both Rx and Tx
+
+static void SetFIFO(__u16 iobase, __u16 value)
+{
+	switch (value) {
+	case 128:
+		WriteRegBit(iobase, 0x11, 0, 0);
+		WriteRegBit(iobase, 0x11, 7, 1);
+		break;
+	case 64:
+		WriteRegBit(iobase, 0x11, 0, 0);
+		WriteRegBit(iobase, 0x11, 7, 0);
+		break;
+	case 32:
+		WriteRegBit(iobase, 0x11, 0, 1);
+		WriteRegBit(iobase, 0x11, 7, 0);
+		break;
+	default:
+		WriteRegBit(iobase, 0x11, 0, 0);
+		WriteRegBit(iobase, 0x11, 7, 0);
+	}
+
+}
+
+#define CRC16(BaseAddr,val)         WriteRegBit(BaseAddr,I_CF_L_0,7,val)	//0 for 32 CRC
+/*
+#define SetVFIR(BaseAddr,val)       WriteRegBit(BaseAddr,I_CF_H_0,5,val)
+#define SetFIR(BaseAddr,val)        WriteRegBit(BaseAddr,I_CF_L_0,6,val)
+#define SetMIR(BaseAddr,val)        WriteRegBit(BaseAddr,I_CF_L_0,5,val)
+#define SetSIR(BaseAddr,val)        WriteRegBit(BaseAddr,I_CF_L_0,4,val)
+*/
+#define SIRFilter(BaseAddr,val)     WriteRegBit(BaseAddr,I_CF_L_0,3,val)
+#define Filter(BaseAddr,val)        WriteRegBit(BaseAddr,I_CF_L_0,2,val)
+#define InvertTX(BaseAddr,val)      WriteRegBit(BaseAddr,I_CF_L_0,1,val)
+#define InvertRX(BaseAddr,val)      WriteRegBit(BaseAddr,I_CF_L_0,0,val)
+//****************************I_CF_H_0
+#define EnableTX(BaseAddr,val)      WriteRegBit(BaseAddr,I_CF_H_0,4,val)
+#define EnableRX(BaseAddr,val)      WriteRegBit(BaseAddr,I_CF_H_0,3,val)
+#define EnableDMA(BaseAddr,val)     WriteRegBit(BaseAddr,I_CF_H_0,2,val)
+#define SIRRecvAny(BaseAddr,val)    WriteRegBit(BaseAddr,I_CF_H_0,1,val)
+#define DiableTrans(BaseAddr,val)   WriteRegBit(BaseAddr,I_CF_H_0,0,val)
+//***************************I_SIR_BOF,I_SIR_EOF
+#define SetSIRBOF(BaseAddr,val)     WriteReg(BaseAddr,I_SIR_BOF,val)
+#define SetSIREOF(BaseAddr,val)     WriteReg(BaseAddr,I_SIR_EOF,val)
+#define GetSIRBOF(BaseAddr)        ReadReg(BaseAddr,I_SIR_BOF)
+#define GetSIREOF(BaseAddr)        ReadReg(BaseAddr,I_SIR_EOF)
+//*******************I_ST_CT_0
+#define EnPhys(BaseAddr,val)   WriteRegBit(BaseAddr,I_ST_CT_0,7,val)
+#define IsModeError(BaseAddr) CheckRegBit(BaseAddr,I_ST_CT_0,6)	//RO
+#define IsVFIROn(BaseAddr)     CheckRegBit(BaseAddr,0x14,0)	//RO for VT1211 only
+#define IsFIROn(BaseAddr)     CheckRegBit(BaseAddr,I_ST_CT_0,5)	//RO
+#define IsMIROn(BaseAddr)     CheckRegBit(BaseAddr,I_ST_CT_0,4)	//RO
+#define IsSIROn(BaseAddr)     CheckRegBit(BaseAddr,I_ST_CT_0,3)	//RO
+#define IsEnableTX(BaseAddr)  CheckRegBit(BaseAddr,I_ST_CT_0,2)	//RO
+#define IsEnableRX(BaseAddr)  CheckRegBit(BaseAddr,I_ST_CT_0,1)	//RO
+#define Is16CRC(BaseAddr)     CheckRegBit(BaseAddr,I_ST_CT_0,0)	//RO
+//***************************I_CF_3
+#define DisableAdjacentPulseWidth(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_3,5,val)	//1 disable
+#define DisablePulseWidthAdjust(BaseAddr,val)   WriteRegBit(BaseAddr,I_CF_3,4,val)	//1 disable
+#define UseOneRX(BaseAddr,val)                  WriteRegBit(BaseAddr,I_CF_3,1,val)	//0 use two RX
+#define SlowIRRXLowActive(BaseAddr,val)         WriteRegBit(BaseAddr,I_CF_3,0,val)	//0 show RX high=1 in SIR
+//***************************H_CT
+#define EnAllInt(BaseAddr,val)   WriteRegBit(BaseAddr,H_CT,7,val)
+#define TXStart(BaseAddr,val)    WriteRegBit(BaseAddr,H_CT,6,val)
+#define RXStart(BaseAddr,val)    WriteRegBit(BaseAddr,H_CT,5,val)
+#define ClearRXInt(BaseAddr,val)   WriteRegBit(BaseAddr,H_CT,4,val)	// 1 clear
+//*****************H_ST
+#define IsRXInt(BaseAddr)           CheckRegBit(BaseAddr,H_ST,4)
+#define GetIntIndentify(BaseAddr)   ((ReadReg(BaseAddr,H_ST)&0xf1) >>1)
+#define IsHostBusy(BaseAddr)        CheckRegBit(BaseAddr,H_ST,0)
+#define GetHostStatus(BaseAddr)     ReadReg(BaseAddr,H_ST)	//RO
+//**************************M_CT
+#define EnTXDMA(BaseAddr,val)         WriteRegBit(BaseAddr,M_CT,7,val)
+#define EnRXDMA(BaseAddr,val)         WriteRegBit(BaseAddr,M_CT,6,val)
+#define SwapDMA(BaseAddr,val)         WriteRegBit(BaseAddr,M_CT,5,val)
+#define EnInternalLoop(BaseAddr,val)  WriteRegBit(BaseAddr,M_CT,4,val)
+#define EnExternalLoop(BaseAddr,val)  WriteRegBit(BaseAddr,M_CT,3,val)
+//**************************TX_CT_1
+#define EnTXFIFOHalfLevelInt(BaseAddr,val)   WriteRegBit(BaseAddr,TX_CT_1,4,val)	//half empty int (1 half)
+#define EnTXFIFOUnderrunEOMInt(BaseAddr,val) WriteRegBit(BaseAddr,TX_CT_1,5,val)
+#define EnTXFIFOReadyInt(BaseAddr,val)       WriteRegBit(BaseAddr,TX_CT_1,6,val)	//int when reach it threshold (setting by bit 4)
+//**************************TX_CT_2
+#define ForceUnderrun(BaseAddr,val)   WriteRegBit(BaseAddr,TX_CT_2,7,val)	// force an underrun int
+#define EnTXCRC(BaseAddr,val)         WriteRegBit(BaseAddr,TX_CT_2,6,val)	//1 for FIR,MIR...0 (not SIR)
+#define ForceBADCRC(BaseAddr,val)     WriteRegBit(BaseAddr,TX_CT_2,5,val)	//force an bad CRC
+#define SendSIP(BaseAddr,val)         WriteRegBit(BaseAddr,TX_CT_2,4,val)	//send indication pulse for prevent SIR disturb
+#define ClearEnTX(BaseAddr,val)       WriteRegBit(BaseAddr,TX_CT_2,3,val)	// opposite to EnTX
+//*****************TX_ST
+#define GetTXStatus(BaseAddr) 	ReadReg(BaseAddr,TX_ST)	//RO
+//**************************RX_CT
+#define EnRXSpecInt(BaseAddr,val)           WriteRegBit(BaseAddr,RX_CT,0,val)
+#define EnRXFIFOReadyInt(BaseAddr,val)      WriteRegBit(BaseAddr,RX_CT,1,val)	//enable int when reach it threshold (setting by bit 7)
+#define EnRXFIFOHalfLevelInt(BaseAddr,val)  WriteRegBit(BaseAddr,RX_CT,7,val)	//enable int when (1) half full...or (0) just not full
+//*****************RX_ST
+#define GetRXStatus(BaseAddr) 	ReadReg(BaseAddr,RX_ST)	//RO
+//***********************P_ADDR
+#define SetPacketAddr(BaseAddr,addr)        WriteReg(BaseAddr,P_ADDR,addr)
+//***********************I_CF_4
+#define EnGPIOtoRX2(BaseAddr,val)	WriteRegBit(BaseAddr,I_CF_4,7,val)
+#define EnTimerInt(BaseAddr,val)		WriteRegBit(BaseAddr,I_CF_4,1,val)
+#define ClearTimerInt(BaseAddr,val)	WriteRegBit(BaseAddr,I_CF_4,0,val)
+//***********************I_T_C_L
+#define WriteGIO(BaseAddr,val)	    WriteRegBit(BaseAddr,I_T_C_L,7,val)
+#define ReadGIO(BaseAddr)		    CheckRegBit(BaseAddr,I_T_C_L,7)
+#define ReadRX(BaseAddr)		    CheckRegBit(BaseAddr,I_T_C_L,3)	//RO
+#define WriteTX(BaseAddr,val)		WriteRegBit(BaseAddr,I_T_C_L,0,val)
+//***********************I_T_C_H
+#define EnRX2(BaseAddr,val)		    WriteRegBit(BaseAddr,I_T_C_H,7,val)
+#define ReadRX2(BaseAddr)           CheckRegBit(BaseAddr,I_T_C_H,7)
+//**********************Version
+#define GetFIRVersion(BaseAddr)		ReadReg(BaseAddr,VERSION)
+
+
+static void SetTimer(__u16 iobase, __u8 count)
+{
+	EnTimerInt(iobase, OFF);
+	WriteReg(iobase, TIMER, count);
+	EnTimerInt(iobase, ON);
+}
+
+
+static void SetSendByte(__u16 iobase, __u32 count)
+{
+	__u32 low, high;
+
+	if ((count & 0xf000) == 0) {
+		low = count & 0x00ff;
+		high = (count & 0x0f00) >> 8;
+		WriteReg(iobase, TX_C_L, low);
+		WriteReg(iobase, TX_C_H, high);
+	}
+}
+
+static void ResetChip(__u16 iobase, __u8 type)
+{
+	__u8 value;
+
+	value = (type + 2) << 4;
+	WriteReg(iobase, RESET, type);
+}
+
+static int CkRxRecv(__u16 iobase, struct via_ircc_cb *self)
+{
+	__u8 low, high;
+	__u16 wTmp = 0, wTmp1 = 0, wTmp_new = 0;
+
+	low = ReadReg(iobase, RX_C_L);
+	high = ReadReg(iobase, RX_C_H);
+	wTmp1 = high;
+	wTmp = (wTmp1 << 8) | low;
+	udelay(10);
+	low = ReadReg(iobase, RX_C_L);
+	high = ReadReg(iobase, RX_C_H);
+	wTmp1 = high;
+	wTmp_new = (wTmp1 << 8) | low;
+	if (wTmp_new != wTmp)
+		return 1;
+	else
+		return 0;
+
+}
+
+static __u16 RxCurCount(__u16 iobase, struct via_ircc_cb * self)
+{
+	__u8 low, high;
+	__u16 wTmp = 0, wTmp1 = 0;
+
+	low = ReadReg(iobase, RX_P_L);
+	high = ReadReg(iobase, RX_P_H);
+	wTmp1 = high;
+	wTmp = (wTmp1 << 8) | low;
+	return wTmp;
+}
+
+/* This Routine can only use in recevie_complete
+ * for it will update last count.
+ */
+
+static __u16 GetRecvByte(__u16 iobase, struct via_ircc_cb * self)
+{
+	__u8 low, high;
+	__u16 wTmp, wTmp1, ret;
+
+	low = ReadReg(iobase, RX_P_L);
+	high = ReadReg(iobase, RX_P_H);
+	wTmp1 = high;
+	wTmp = (wTmp1 << 8) | low;
+
+
+	if (wTmp >= self->RxLastCount)
+		ret = wTmp - self->RxLastCount;
+	else
+		ret = (0x8000 - self->RxLastCount) + wTmp;
+	self->RxLastCount = wTmp;
+
+/* RX_P is more actually the RX_C
+ low=ReadReg(iobase,RX_C_L);
+ high=ReadReg(iobase,RX_C_H);
+
+ if(!(high&0xe000)) {
+	 temp=(high<<8)+low;
+	 return temp;
+ }
+ else return 0;
+*/
+	return ret;
+}
+
+static void Sdelay(__u16 scale)
+{
+	__u8 bTmp;
+	int i, j;
+
+	for (j = 0; j < scale; j++) {
+		for (i = 0; i < 0x20; i++) {
+			bTmp = inb(0xeb);
+			outb(bTmp, 0xeb);
+		}
+	}
+}
+
+static void Tdelay(__u16 scale)
+{
+	__u8 bTmp;
+	int i, j;
+
+	for (j = 0; j < scale; j++) {
+		for (i = 0; i < 0x50; i++) {
+			bTmp = inb(0xeb);
+			outb(bTmp, 0xeb);
+		}
+	}
+}
+
+
+static void ActClk(__u16 iobase, __u8 value)
+{
+	__u8 bTmp;
+	bTmp = ReadReg(iobase, 0x34);
+	if (value)
+		WriteReg(iobase, 0x34, bTmp | Clk_bit);
+	else
+		WriteReg(iobase, 0x34, bTmp & ~Clk_bit);
+}
+
+static void ClkTx(__u16 iobase, __u8 Clk, __u8 Tx)
+{
+	__u8 bTmp;
+
+	bTmp = ReadReg(iobase, 0x34);
+	if (Clk == 0)
+		bTmp &= ~Clk_bit;
+	else {
+		if (Clk == 1)
+			bTmp |= Clk_bit;
+	}
+	WriteReg(iobase, 0x34, bTmp);
+	Sdelay(1);
+	if (Tx == 0)
+		bTmp &= ~Tx_bit;
+	else {
+		if (Tx == 1)
+			bTmp |= Tx_bit;
+	}
+	WriteReg(iobase, 0x34, bTmp);
+}
+
+static void Wr_Byte(__u16 iobase, __u8 data)
+{
+	__u8 bData = data;
+//      __u8 btmp;
+	int i;
+
+	ClkTx(iobase, 0, 1);
+
+	Tdelay(2);
+	ActClk(iobase, 1);
+	Tdelay(1);
+
+	for (i = 0; i < 8; i++) {	//LDN
+
+		if ((bData >> i) & 0x01) {
+			ClkTx(iobase, 0, 1);	//bit data = 1;
+		} else {
+			ClkTx(iobase, 0, 0);	//bit data = 1;
+		}
+		Tdelay(2);
+		Sdelay(1);
+		ActClk(iobase, 1);	//clk hi
+		Tdelay(1);
+	}
+}
+
+static __u8 Rd_Indx(__u16 iobase, __u8 addr, __u8 index)
+{
+	__u8 data = 0, bTmp, data_bit;
+	int i;
+
+	bTmp = addr | (index << 1) | 0;
+	ClkTx(iobase, 0, 0);
+	Tdelay(2);
+	ActClk(iobase, 1);
+	udelay(1);
+	Wr_Byte(iobase, bTmp);
+	Sdelay(1);
+	ClkTx(iobase, 0, 0);
+	Tdelay(2);
+	for (i = 0; i < 10; i++) {
+		ActClk(iobase, 1);
+		Tdelay(1);
+		ActClk(iobase, 0);
+		Tdelay(1);
+		ClkTx(iobase, 0, 1);
+		Tdelay(1);
+		bTmp = ReadReg(iobase, 0x34);
+		if (!(bTmp & Rd_Valid))
+			break;
+	}
+	if (!(bTmp & Rd_Valid)) {
+		for (i = 0; i < 8; i++) {
+			ActClk(iobase, 1);
+			Tdelay(1);
+			ActClk(iobase, 0);
+			bTmp = ReadReg(iobase, 0x34);
+			data_bit = 1 << i;
+			if (bTmp & RxBit)
+				data |= data_bit;
+			else
+				data &= ~data_bit;
+			Tdelay(2);
+		}
+	} else {
+		for (i = 0; i < 2; i++) {
+			ActClk(iobase, 1);
+			Tdelay(1);
+			ActClk(iobase, 0);
+			Tdelay(2);
+		}
+		bTmp = ReadReg(iobase, 0x34);
+	}
+	for (i = 0; i < 1; i++) {
+		ActClk(iobase, 1);
+		Tdelay(1);
+		ActClk(iobase, 0);
+		Tdelay(2);
+	}
+	ClkTx(iobase, 0, 0);
+	Tdelay(1);
+	for (i = 0; i < 3; i++) {
+		ActClk(iobase, 1);
+		Tdelay(1);
+		ActClk(iobase, 0);
+		Tdelay(2);
+	}
+	return data;
+}
+
+static void Wr_Indx(__u16 iobase, __u8 addr, __u8 index, __u8 data)
+{
+	int i;
+	__u8 bTmp;
+
+	ClkTx(iobase, 0, 0);
+	udelay(2);
+	ActClk(iobase, 1);
+	udelay(1);
+	bTmp = addr | (index << 1) | 1;
+	Wr_Byte(iobase, bTmp);
+	Wr_Byte(iobase, data);
+	for (i = 0; i < 2; i++) {
+		ClkTx(iobase, 0, 0);
+		Tdelay(2);
+		ActClk(iobase, 1);
+		Tdelay(1);
+	}
+	ActClk(iobase, 0);
+}
+
+static void ResetDongle(__u16 iobase)
+{
+	int i;
+	ClkTx(iobase, 0, 0);
+	Tdelay(1);
+	for (i = 0; i < 30; i++) {
+		ActClk(iobase, 1);
+		Tdelay(1);
+		ActClk(iobase, 0);
+		Tdelay(1);
+	}
+	ActClk(iobase, 0);
+}
+
+static void SetSITmode(__u16 iobase)
+{
+
+	__u8 bTmp;
+
+	bTmp = ReadLPCReg(0x28);
+	WriteLPCReg(0x28, bTmp | 0x10);	//select ITMOFF
+	bTmp = ReadReg(iobase, 0x35);
+	WriteReg(iobase, 0x35, bTmp | 0x40);	// Driver ITMOFF
+	WriteReg(iobase, 0x28, bTmp | 0x80);	// enable All interrupt
+}
+
+static void SI_SetMode(__u16 iobase, int mode)
+{
+	//__u32 dTmp;
+	__u8 bTmp;
+
+	WriteLPCReg(0x28, 0x70);	// S/W Reset
+	SetSITmode(iobase);
+	ResetDongle(iobase);
+	udelay(10);
+	Wr_Indx(iobase, 0x40, 0x0, 0x17);	//RX ,APEN enable,Normal power
+	Wr_Indx(iobase, 0x40, 0x1, mode);	//Set Mode
+	Wr_Indx(iobase, 0x40, 0x2, 0xff);	//Set power to FIR VFIR > 1m
+	bTmp = Rd_Indx(iobase, 0x40, 1);
+}
+
+static void InitCard(__u16 iobase)
+{
+	ResetChip(iobase, 5);
+	WriteReg(iobase, I_ST_CT_0, 0x00);	// open CHIP on
+	SetSIRBOF(iobase, 0xc0);	// hardware default value
+	SetSIREOF(iobase, 0xc1);
+}
+
+static void CommonInit(__u16 iobase)
+{
+//  EnTXCRC(iobase,0);
+	SwapDMA(iobase, OFF);
+	SetMaxRxPacketSize(iobase, 0x0fff);	//set to max:4095
+	EnRXFIFOReadyInt(iobase, OFF);
+	EnRXFIFOHalfLevelInt(iobase, OFF);
+	EnTXFIFOHalfLevelInt(iobase, OFF);
+	EnTXFIFOUnderrunEOMInt(iobase, ON);
+//  EnTXFIFOReadyInt(iobase,ON);
+	InvertTX(iobase, OFF);
+	InvertRX(iobase, OFF);
+//  WriteLPCReg(0xF0,0); //(if VT1211 then do this)
+	if (IsSIROn(iobase)) {
+		SIRFilter(iobase, ON);
+		SIRRecvAny(iobase, ON);
+	} else {
+		SIRFilter(iobase, OFF);
+		SIRRecvAny(iobase, OFF);
+	}
+	EnRXSpecInt(iobase, ON);
+	WriteReg(iobase, I_ST_CT_0, 0x80);
+	EnableDMA(iobase, ON);
+}
+
+static void SetBaudRate(__u16 iobase, __u32 rate)
+{
+	__u8 value = 11, temp;
+
+	if (IsSIROn(iobase)) {
+		switch (rate) {
+		case (__u32) (2400L):
+			value = 47;
+			break;
+		case (__u32) (9600L):
+			value = 11;
+			break;
+		case (__u32) (19200L):
+			value = 5;
+			break;
+		case (__u32) (38400L):
+			value = 2;
+			break;
+		case (__u32) (57600L):
+			value = 1;
+			break;
+		case (__u32) (115200L):
+			value = 0;
+			break;
+		default:
+			break;
+		};
+	} else if (IsMIROn(iobase)) {
+		value = 0;	// will automatically be fixed in 1.152M
+	} else if (IsFIROn(iobase)) {
+		value = 0;	// will automatically be fixed in 4M
+	}
+	temp = (ReadReg(iobase, I_CF_H_1) & 0x03);
+	temp |= value << 2;
+	WriteReg(iobase, I_CF_H_1, temp);
+}
+
+static void SetPulseWidth(__u16 iobase, __u8 width)
+{
+	__u8 temp, temp1, temp2;
+
+	temp = (ReadReg(iobase, I_CF_L_1) & 0x1f);
+	temp1 = (ReadReg(iobase, I_CF_H_1) & 0xfc);
+	temp2 = (width & 0x07) << 5;
+	temp |= temp2;
+	temp2 = (width & 0x18) >> 3;
+	temp1 |= temp2;
+	WriteReg(iobase, I_CF_L_1, temp);
+	WriteReg(iobase, I_CF_H_1, temp1);
+}
+
+static void SetSendPreambleCount(__u16 iobase, __u8 count)
+{
+	__u8 temp;
+
+	temp = ReadReg(iobase, I_CF_L_1) & 0xe0;
+	temp |= count;
+	WriteReg(iobase, I_CF_L_1, temp);
+
+}
+
+static void SetVFIR(__u16 BaseAddr, __u8 val)
+{
+	__u8 tmp;
+
+	tmp = ReadReg(BaseAddr, I_CF_L_0);
+	WriteReg(BaseAddr, I_CF_L_0, tmp & 0x8f);
+	WriteRegBit(BaseAddr, I_CF_H_0, 5, val);
+}
+
+static void SetFIR(__u16 BaseAddr, __u8 val)
+{
+	__u8 tmp;
+
+	WriteRegBit(BaseAddr, I_CF_H_0, 5, 0);
+	tmp = ReadReg(BaseAddr, I_CF_L_0);
+	WriteReg(BaseAddr, I_CF_L_0, tmp & 0x8f);
+	WriteRegBit(BaseAddr, I_CF_L_0, 6, val);
+}
+
+static void SetMIR(__u16 BaseAddr, __u8 val)
+{
+	__u8 tmp;
+
+	WriteRegBit(BaseAddr, I_CF_H_0, 5, 0);
+	tmp = ReadReg(BaseAddr, I_CF_L_0);
+	WriteReg(BaseAddr, I_CF_L_0, tmp & 0x8f);
+	WriteRegBit(BaseAddr, I_CF_L_0, 5, val);
+}
+
+static void SetSIR(__u16 BaseAddr, __u8 val)
+{
+	__u8 tmp;
+
+	WriteRegBit(BaseAddr, I_CF_H_0, 5, 0);
+	tmp = ReadReg(BaseAddr, I_CF_L_0);
+	WriteReg(BaseAddr, I_CF_L_0, tmp & 0x8f);
+	WriteRegBit(BaseAddr, I_CF_L_0, 4, val);
+}
+
+#endif				/* via_IRCC_H */
diff --git a/drivers/net/irda/vlsi_ir.c b/drivers/net/irda/vlsi_ir.c
new file mode 100644
index 0000000..35fad81
--- /dev/null
+++ b/drivers/net/irda/vlsi_ir.c
@@ -0,0 +1,1912 @@
+/*********************************************************************
+ *
+ *	vlsi_ir.c:	VLSI82C147 PCI IrDA controller driver for Linux
+ *
+ *	Copyright (c) 2001-2003 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.
+ *
+ *	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/config.h>
+#include <linux/module.h>
+ 
+#define DRIVER_NAME 		"vlsi_ir"
+#define DRIVER_VERSION		"v0.5"
+#define DRIVER_DESCRIPTION	"IrDA SIR/MIR/FIR driver for VLSI 82C147"
+#define DRIVER_AUTHOR		"Martin Diehl <info@mdiehl.de>"
+
+MODULE_DESCRIPTION(DRIVER_DESCRIPTION);
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_LICENSE("GPL");
+
+/********************************************************/
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/delay.h>
+#include <linux/time.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/smp_lock.h>
+#include <asm/uaccess.h>
+#include <asm/byteorder.h>
+
+#include <net/irda/irda.h>
+#include <net/irda/irda_device.h>
+#include <net/irda/wrapper.h>
+#include <net/irda/crc.h>
+
+#include "vlsi_ir.h"
+
+/********************************************************/
+
+static /* const */ char drivername[] = DRIVER_NAME;
+
+static struct pci_device_id vlsi_irda_table [] = {
+	{
+		.class =        PCI_CLASS_WIRELESS_IRDA << 8,
+		.class_mask =	PCI_CLASS_SUBCLASS_MASK << 8, 
+		.vendor =       PCI_VENDOR_ID_VLSI,
+		.device =       PCI_DEVICE_ID_VLSI_82C147,
+		.subvendor = 	PCI_ANY_ID,
+		.subdevice =	PCI_ANY_ID,
+	},
+	{ /* all zeroes */ }
+};
+
+MODULE_DEVICE_TABLE(pci, vlsi_irda_table);
+
+/********************************************************/
+
+/*	clksrc: which clock source to be used
+ *		0: auto - try PLL, fallback to 40MHz XCLK
+ *		1: on-chip 48MHz PLL
+ *		2: external 48MHz XCLK
+ *		3: external 40MHz XCLK (HP OB-800)
+ */
+
+static int clksrc = 0;			/* default is 0(auto) */
+module_param(clksrc, int, 0);
+MODULE_PARM_DESC(clksrc, "clock input source selection");
+
+/*	ringsize: size of the tx and rx descriptor rings
+ *		independent for tx and rx
+ *		specify as ringsize=tx[,rx]
+ *		allowed values: 4, 8, 16, 32, 64
+ *		Due to the IrDA 1.x max. allowed window size=7,
+ *		there should be no gain when using rings larger than 8
+ */
+
+static int ringsize[] = {8,8};		/* default is tx=8 / rx=8 */
+module_param_array(ringsize, int, NULL, 0);
+MODULE_PARM_DESC(ringsize, "TX, RX ring descriptor size");
+
+/*	sirpulse: tuning of the SIR pulse width within IrPHY 1.3 limits
+ *		0: very short, 1.5us (exception: 6us at 2.4 kbaud)
+ *		1: nominal 3/16 bittime width
+ *	note: IrDA compliant peer devices should be happy regardless
+ *		which one is used. Primary goal is to save some power
+ *		on the sender's side - at 9.6kbaud for example the short
+ *		pulse width saves more than 90% of the transmitted IR power.
+ */
+
+static int sirpulse = 1;		/* default is 3/16 bittime */
+module_param(sirpulse, int, 0);
+MODULE_PARM_DESC(sirpulse, "SIR pulse width tuning");
+
+/*	qos_mtt_bits: encoded min-turn-time value we require the peer device
+ *		 to use before transmitting to us. "Type 1" (per-station)
+ *		 bitfield according to IrLAP definition (section 6.6.8)
+ *		 Don't know which transceiver is used by my OB800 - the
+ *		 pretty common HP HDLS-1100 requires 1 msec - so lets use this.
+ */
+
+static int qos_mtt_bits = 0x07;		/* default is 1 ms or more */
+module_param(qos_mtt_bits, int, 0);
+MODULE_PARM_DESC(qos_mtt_bits, "IrLAP bitfield representing min-turn-time");
+
+/********************************************************/
+
+static void vlsi_reg_debug(unsigned iobase, const char *s)
+{
+	int	i;
+
+	printk(KERN_DEBUG "%s: ", s);
+	for (i = 0; i < 0x20; i++)
+		printk("%02x", (unsigned)inb((iobase+i)));
+	printk("\n");
+}
+
+static void vlsi_ring_debug(struct vlsi_ring *r)
+{
+	struct ring_descr *rd;
+	unsigned i;
+
+	printk(KERN_DEBUG "%s - ring %p / size %u / mask 0x%04x / len %u / dir %d / hw %p\n",
+		__FUNCTION__, r, r->size, r->mask, r->len, r->dir, r->rd[0].hw);
+	printk(KERN_DEBUG "%s - head = %d / tail = %d\n", __FUNCTION__,
+		atomic_read(&r->head) & r->mask, atomic_read(&r->tail) & r->mask);
+	for (i = 0; i < r->size; i++) {
+		rd = &r->rd[i];
+		printk(KERN_DEBUG "%s - ring descr %u: ", __FUNCTION__, i);
+		printk("skb=%p data=%p hw=%p\n", rd->skb, rd->buf, rd->hw);
+		printk(KERN_DEBUG "%s - hw: status=%02x count=%u addr=0x%08x\n",
+			__FUNCTION__, (unsigned) rd_get_status(rd),
+			(unsigned) rd_get_count(rd), (unsigned) rd_get_addr(rd));
+	}
+}
+
+/********************************************************/
+
+/* needed regardless of CONFIG_PROC_FS */
+static struct proc_dir_entry *vlsi_proc_root = NULL;
+
+#ifdef CONFIG_PROC_FS
+
+static void vlsi_proc_pdev(struct seq_file *seq, struct pci_dev *pdev)
+{
+	unsigned iobase = pci_resource_start(pdev, 0);
+	unsigned i;
+
+	seq_printf(seq, "\n%s (vid/did: %04x/%04x)\n",
+		   PCIDEV_NAME(pdev), (int)pdev->vendor, (int)pdev->device);
+	seq_printf(seq, "pci-power-state: %u\n", (unsigned) pdev->current_state);
+	seq_printf(seq, "resources: irq=%u / io=0x%04x / dma_mask=0x%016Lx\n",
+		   pdev->irq, (unsigned)pci_resource_start(pdev, 0), (unsigned long long)pdev->dma_mask);
+	seq_printf(seq, "hw registers: ");
+	for (i = 0; i < 0x20; i++)
+		seq_printf(seq, "%02x", (unsigned)inb((iobase+i)));
+	seq_printf(seq, "\n");
+}
+		
+static void vlsi_proc_ndev(struct seq_file *seq, struct net_device *ndev)
+{
+	vlsi_irda_dev_t *idev = ndev->priv;
+	u8 byte;
+	u16 word;
+	unsigned delta1, delta2;
+	struct timeval now;
+	unsigned iobase = ndev->base_addr;
+
+	seq_printf(seq, "\n%s link state: %s / %s / %s / %s\n", ndev->name,
+		netif_device_present(ndev) ? "attached" : "detached", 
+		netif_running(ndev) ? "running" : "not running",
+		netif_carrier_ok(ndev) ? "carrier ok" : "no carrier",
+		netif_queue_stopped(ndev) ? "queue stopped" : "queue running");
+
+	if (!netif_running(ndev))
+		return;
+
+	seq_printf(seq, "\nhw-state:\n");
+	pci_read_config_byte(idev->pdev, VLSI_PCI_IRMISC, &byte);
+	seq_printf(seq, "IRMISC:%s%s%s uart%s",
+		(byte&IRMISC_IRRAIL) ? " irrail" : "",
+		(byte&IRMISC_IRPD) ? " irpd" : "",
+		(byte&IRMISC_UARTTST) ? " uarttest" : "",
+		(byte&IRMISC_UARTEN) ? "@" : " disabled\n");
+	if (byte&IRMISC_UARTEN) {
+		seq_printf(seq, "0x%s\n",
+			(byte&2) ? ((byte&1) ? "3e8" : "2e8")
+				 : ((byte&1) ? "3f8" : "2f8"));
+	}
+	pci_read_config_byte(idev->pdev, VLSI_PCI_CLKCTL, &byte);
+	seq_printf(seq, "CLKCTL: PLL %s%s%s / clock %s / wakeup %s\n",
+		(byte&CLKCTL_PD_INV) ? "powered" : "down",
+		(byte&CLKCTL_LOCK) ? " locked" : "",
+		(byte&CLKCTL_EXTCLK) ? ((byte&CLKCTL_XCKSEL)?" / 40 MHz XCLK":" / 48 MHz XCLK") : "",
+		(byte&CLKCTL_CLKSTP) ? "stopped" : "running",
+		(byte&CLKCTL_WAKE) ? "enabled" : "disabled");
+	pci_read_config_byte(idev->pdev, VLSI_PCI_MSTRPAGE, &byte);
+	seq_printf(seq, "MSTRPAGE: 0x%02x\n", (unsigned)byte);
+
+	byte = inb(iobase+VLSI_PIO_IRINTR);
+	seq_printf(seq, "IRINTR:%s%s%s%s%s%s%s%s\n",
+		(byte&IRINTR_ACTEN) ? " ACTEN" : "",
+		(byte&IRINTR_RPKTEN) ? " RPKTEN" : "",
+		(byte&IRINTR_TPKTEN) ? " TPKTEN" : "",
+		(byte&IRINTR_OE_EN) ? " OE_EN" : "",
+		(byte&IRINTR_ACTIVITY) ? " ACTIVITY" : "",
+		(byte&IRINTR_RPKTINT) ? " RPKTINT" : "",
+		(byte&IRINTR_TPKTINT) ? " TPKTINT" : "",
+		(byte&IRINTR_OE_INT) ? " OE_INT" : "");
+	word = inw(iobase+VLSI_PIO_RINGPTR);
+	seq_printf(seq, "RINGPTR: rx=%u / tx=%u\n", RINGPTR_GET_RX(word), RINGPTR_GET_TX(word));
+	word = inw(iobase+VLSI_PIO_RINGBASE);
+	seq_printf(seq, "RINGBASE: busmap=0x%08x\n",
+		((unsigned)word << 10)|(MSTRPAGE_VALUE<<24));
+	word = inw(iobase+VLSI_PIO_RINGSIZE);
+	seq_printf(seq, "RINGSIZE: rx=%u / tx=%u\n", RINGSIZE_TO_RXSIZE(word),
+		RINGSIZE_TO_TXSIZE(word));
+
+	word = inw(iobase+VLSI_PIO_IRCFG);
+	seq_printf(seq, "IRCFG:%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
+		(word&IRCFG_LOOP) ? " LOOP" : "",
+		(word&IRCFG_ENTX) ? " ENTX" : "",
+		(word&IRCFG_ENRX) ? " ENRX" : "",
+		(word&IRCFG_MSTR) ? " MSTR" : "",
+		(word&IRCFG_RXANY) ? " RXANY" : "",
+		(word&IRCFG_CRC16) ? " CRC16" : "",
+		(word&IRCFG_FIR) ? " FIR" : "",
+		(word&IRCFG_MIR) ? " MIR" : "",
+		(word&IRCFG_SIR) ? " SIR" : "",
+		(word&IRCFG_SIRFILT) ? " SIRFILT" : "",
+		(word&IRCFG_SIRTEST) ? " SIRTEST" : "",
+		(word&IRCFG_TXPOL) ? " TXPOL" : "",
+		(word&IRCFG_RXPOL) ? " RXPOL" : "");
+	word = inw(iobase+VLSI_PIO_IRENABLE);
+	seq_printf(seq, "IRENABLE:%s%s%s%s%s%s%s%s\n",
+		(word&IRENABLE_PHYANDCLOCK) ? " PHYANDCLOCK" : "",
+		(word&IRENABLE_CFGER) ? " CFGERR" : "",
+		(word&IRENABLE_FIR_ON) ? " FIR_ON" : "",
+		(word&IRENABLE_MIR_ON) ? " MIR_ON" : "",
+		(word&IRENABLE_SIR_ON) ? " SIR_ON" : "",
+		(word&IRENABLE_ENTXST) ? " ENTXST" : "",
+		(word&IRENABLE_ENRXST) ? " ENRXST" : "",
+		(word&IRENABLE_CRC16_ON) ? " CRC16_ON" : "");
+	word = inw(iobase+VLSI_PIO_PHYCTL);
+	seq_printf(seq, "PHYCTL: baud-divisor=%u / pulsewidth=%u / preamble=%u\n",
+		(unsigned)PHYCTL_TO_BAUD(word),
+		(unsigned)PHYCTL_TO_PLSWID(word),
+		(unsigned)PHYCTL_TO_PREAMB(word));
+	word = inw(iobase+VLSI_PIO_NPHYCTL);
+	seq_printf(seq, "NPHYCTL: baud-divisor=%u / pulsewidth=%u / preamble=%u\n",
+		(unsigned)PHYCTL_TO_BAUD(word),
+		(unsigned)PHYCTL_TO_PLSWID(word),
+		(unsigned)PHYCTL_TO_PREAMB(word));
+	word = inw(iobase+VLSI_PIO_MAXPKT);
+	seq_printf(seq, "MAXPKT: max. rx packet size = %u\n", word);
+	word = inw(iobase+VLSI_PIO_RCVBCNT) & RCVBCNT_MASK;
+	seq_printf(seq, "RCVBCNT: rx-fifo filling level = %u\n", word);
+
+	seq_printf(seq, "\nsw-state:\n");
+	seq_printf(seq, "IrPHY setup: %d baud - %s encoding\n", idev->baud, 
+		(idev->mode==IFF_SIR)?"SIR":((idev->mode==IFF_MIR)?"MIR":"FIR"));
+	do_gettimeofday(&now);
+	if (now.tv_usec >= idev->last_rx.tv_usec) {
+		delta2 = now.tv_usec - idev->last_rx.tv_usec;
+		delta1 = 0;
+	}
+	else {
+		delta2 = 1000000 + now.tv_usec - idev->last_rx.tv_usec;
+		delta1 = 1;
+	}
+	seq_printf(seq, "last rx: %lu.%06u sec\n",
+		now.tv_sec - idev->last_rx.tv_sec - delta1, delta2);	
+
+	seq_printf(seq, "RX: packets=%lu / bytes=%lu / errors=%lu / dropped=%lu",
+		idev->stats.rx_packets, idev->stats.rx_bytes, idev->stats.rx_errors,
+		idev->stats.rx_dropped);
+	seq_printf(seq, " / overrun=%lu / length=%lu / frame=%lu / crc=%lu\n",
+		idev->stats.rx_over_errors, idev->stats.rx_length_errors,
+		idev->stats.rx_frame_errors, idev->stats.rx_crc_errors);
+	seq_printf(seq, "TX: packets=%lu / bytes=%lu / errors=%lu / dropped=%lu / fifo=%lu\n",
+		idev->stats.tx_packets, idev->stats.tx_bytes, idev->stats.tx_errors,
+		idev->stats.tx_dropped, idev->stats.tx_fifo_errors);
+
+}
+		
+static void vlsi_proc_ring(struct seq_file *seq, struct vlsi_ring *r)
+{
+	struct ring_descr *rd;
+	unsigned i, j;
+	int h, t;
+
+	seq_printf(seq, "size %u / mask 0x%04x / len %u / dir %d / hw %p\n",
+		r->size, r->mask, r->len, r->dir, r->rd[0].hw);
+	h = atomic_read(&r->head) & r->mask;
+	t = atomic_read(&r->tail) & r->mask;
+	seq_printf(seq, "head = %d / tail = %d ", h, t);
+	if (h == t)
+		seq_printf(seq, "(empty)\n");
+	else {
+		if (((t+1)&r->mask) == h)
+			seq_printf(seq, "(full)\n");
+		else
+			seq_printf(seq, "(level = %d)\n", ((unsigned)(t-h) & r->mask)); 
+		rd = &r->rd[h];
+		j = (unsigned) rd_get_count(rd);
+		seq_printf(seq, "current: rd = %d / status = %02x / len = %u\n",
+				h, (unsigned)rd_get_status(rd), j);
+		if (j > 0) {
+			seq_printf(seq, "   data:");
+			if (j > 20)
+				j = 20;
+			for (i = 0; i < j; i++)
+				seq_printf(seq, " %02x", (unsigned)((unsigned char *)rd->buf)[i]);
+			seq_printf(seq, "\n");
+		}
+	}
+	for (i = 0; i < r->size; i++) {
+		rd = &r->rd[i];
+		seq_printf(seq, "> ring descr %u: ", i);
+		seq_printf(seq, "skb=%p data=%p hw=%p\n", rd->skb, rd->buf, rd->hw);
+		seq_printf(seq, "  hw: status=%02x count=%u busaddr=0x%08x\n",
+			(unsigned) rd_get_status(rd),
+			(unsigned) rd_get_count(rd), (unsigned) rd_get_addr(rd));
+	}
+}
+
+static int vlsi_seq_show(struct seq_file *seq, void *v)
+{
+	struct net_device *ndev = seq->private;
+	vlsi_irda_dev_t *idev = ndev->priv;
+	unsigned long flags;
+
+	seq_printf(seq, "\n%s %s\n\n", DRIVER_NAME, DRIVER_VERSION);
+	seq_printf(seq, "clksrc: %s\n", 
+		(clksrc>=2) ? ((clksrc==3)?"40MHz XCLK":"48MHz XCLK")
+			    : ((clksrc==1)?"48MHz PLL":"autodetect"));
+	seq_printf(seq, "ringsize: tx=%d / rx=%d\n",
+		ringsize[0], ringsize[1]);
+	seq_printf(seq, "sirpulse: %s\n", (sirpulse)?"3/16 bittime":"short");
+	seq_printf(seq, "qos_mtt_bits: 0x%02x\n", (unsigned)qos_mtt_bits);
+
+	spin_lock_irqsave(&idev->lock, flags);
+	if (idev->pdev != NULL) {
+		vlsi_proc_pdev(seq, idev->pdev);
+
+		if (idev->pdev->current_state == 0)
+			vlsi_proc_ndev(seq, ndev);
+		else
+			seq_printf(seq, "\nPCI controller down - resume_ok = %d\n",
+				idev->resume_ok);
+		if (netif_running(ndev) && idev->rx_ring && idev->tx_ring) {
+			seq_printf(seq, "\n--------- RX ring -----------\n\n");
+			vlsi_proc_ring(seq, idev->rx_ring);
+			seq_printf(seq, "\n--------- TX ring -----------\n\n");
+			vlsi_proc_ring(seq, idev->tx_ring);
+		}
+	}
+	seq_printf(seq, "\n");
+	spin_unlock_irqrestore(&idev->lock, flags);
+
+	return 0;
+}
+
+static int vlsi_seq_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, vlsi_seq_show, PDE(inode)->data);
+}
+
+static struct file_operations vlsi_proc_fops = {
+	.owner	 = THIS_MODULE,
+	.open    = vlsi_seq_open,
+	.read    = seq_read,
+	.llseek  = seq_lseek,
+	.release = single_release,
+};
+
+#define VLSI_PROC_FOPS		(&vlsi_proc_fops)
+
+#else	/* CONFIG_PROC_FS */
+#define VLSI_PROC_FOPS		NULL
+#endif
+
+/********************************************************/
+
+static struct vlsi_ring *vlsi_alloc_ring(struct pci_dev *pdev, struct ring_descr_hw *hwmap,
+						unsigned size, unsigned len, int dir)
+{
+	struct vlsi_ring *r;
+	struct ring_descr *rd;
+	unsigned	i, j;
+	dma_addr_t	busaddr;
+
+	if (!size  ||  ((size-1)&size)!=0)	/* must be >0 and power of 2 */
+		return NULL;
+
+	r = kmalloc(sizeof(*r) + size * sizeof(struct ring_descr), GFP_KERNEL);
+	if (!r)
+		return NULL;
+	memset(r, 0, sizeof(*r));
+
+	r->pdev = pdev;
+	r->dir = dir;
+	r->len = len;
+	r->rd = (struct ring_descr *)(r+1);
+	r->mask = size - 1;
+	r->size = size;
+	atomic_set(&r->head, 0);
+	atomic_set(&r->tail, 0);
+
+	for (i = 0; i < size; i++) {
+		rd = r->rd + i;
+		memset(rd, 0, sizeof(*rd));
+		rd->hw = hwmap + i;
+		rd->buf = kmalloc(len, GFP_KERNEL|GFP_DMA);
+		if (rd->buf == NULL
+		    ||  !(busaddr = pci_map_single(pdev, rd->buf, len, dir))) {
+			if (rd->buf) {
+				IRDA_ERROR("%s: failed to create PCI-MAP for %p",
+					   __FUNCTION__, rd->buf);
+				kfree(rd->buf);
+				rd->buf = NULL;
+			}
+			for (j = 0; j < i; j++) {
+				rd = r->rd + j;
+				busaddr = rd_get_addr(rd);
+				rd_set_addr_status(rd, 0, 0);
+				if (busaddr)
+					pci_unmap_single(pdev, busaddr, len, dir);
+				kfree(rd->buf);
+				rd->buf = NULL;
+			}
+			kfree(r);
+			return NULL;
+		}
+		rd_set_addr_status(rd, busaddr, 0);
+		/* initially, the dma buffer is owned by the CPU */
+		rd->skb = NULL;
+	}
+	return r;
+}
+
+static int vlsi_free_ring(struct vlsi_ring *r)
+{
+	struct ring_descr *rd;
+	unsigned	i;
+	dma_addr_t	busaddr;
+
+	for (i = 0; i < r->size; i++) {
+		rd = r->rd + i;
+		if (rd->skb)
+			dev_kfree_skb_any(rd->skb);
+		busaddr = rd_get_addr(rd);
+		rd_set_addr_status(rd, 0, 0);
+		if (busaddr)
+			pci_unmap_single(r->pdev, busaddr, r->len, r->dir);
+		if (rd->buf)
+			kfree(rd->buf);
+	}
+	kfree(r);
+	return 0;
+}
+
+static int vlsi_create_hwif(vlsi_irda_dev_t *idev)
+{
+	char 			*ringarea;
+	struct ring_descr_hw	*hwmap;
+
+	idev->virtaddr = NULL;
+	idev->busaddr = 0;
+
+	ringarea = pci_alloc_consistent(idev->pdev, HW_RING_AREA_SIZE, &idev->busaddr);
+	if (!ringarea) {
+		IRDA_ERROR("%s: insufficient memory for descriptor rings\n",
+			   __FUNCTION__);
+		goto out;
+	}
+	memset(ringarea, 0, HW_RING_AREA_SIZE);
+
+	hwmap = (struct ring_descr_hw *)ringarea;
+	idev->rx_ring = vlsi_alloc_ring(idev->pdev, hwmap, ringsize[1],
+					XFER_BUF_SIZE, PCI_DMA_FROMDEVICE);
+	if (idev->rx_ring == NULL)
+		goto out_unmap;
+
+	hwmap += MAX_RING_DESCR;
+	idev->tx_ring = vlsi_alloc_ring(idev->pdev, hwmap, ringsize[0],
+					XFER_BUF_SIZE, PCI_DMA_TODEVICE);
+	if (idev->tx_ring == NULL)
+		goto out_free_rx;
+
+	idev->virtaddr = ringarea;
+	return 0;
+
+out_free_rx:
+	vlsi_free_ring(idev->rx_ring);
+out_unmap:
+	idev->rx_ring = idev->tx_ring = NULL;
+	pci_free_consistent(idev->pdev, HW_RING_AREA_SIZE, ringarea, idev->busaddr);
+	idev->busaddr = 0;
+out:
+	return -ENOMEM;
+}
+
+static int vlsi_destroy_hwif(vlsi_irda_dev_t *idev)
+{
+	vlsi_free_ring(idev->rx_ring);
+	vlsi_free_ring(idev->tx_ring);
+	idev->rx_ring = idev->tx_ring = NULL;
+
+	if (idev->busaddr)
+		pci_free_consistent(idev->pdev,HW_RING_AREA_SIZE,idev->virtaddr,idev->busaddr);
+
+	idev->virtaddr = NULL;
+	idev->busaddr = 0;
+
+	return 0;
+}
+
+/********************************************************/
+
+static int vlsi_process_rx(struct vlsi_ring *r, struct ring_descr *rd)
+{
+	u16		status;
+	int		crclen, len = 0;
+	struct sk_buff	*skb;
+	int		ret = 0;
+	struct net_device *ndev = (struct net_device *)pci_get_drvdata(r->pdev);
+	vlsi_irda_dev_t *idev = ndev->priv;
+
+	pci_dma_sync_single_for_cpu(r->pdev, rd_get_addr(rd), r->len, r->dir);
+	/* dma buffer now owned by the CPU */
+	status = rd_get_status(rd);
+	if (status & RD_RX_ERROR) {
+		if (status & RD_RX_OVER)  
+			ret |= VLSI_RX_OVER;
+		if (status & RD_RX_LENGTH)  
+			ret |= VLSI_RX_LENGTH;
+		if (status & RD_RX_PHYERR)  
+			ret |= VLSI_RX_FRAME;
+		if (status & RD_RX_CRCERR)  
+			ret |= VLSI_RX_CRC;
+		goto done;
+	}
+
+	len = rd_get_count(rd);
+	crclen = (idev->mode==IFF_FIR) ? sizeof(u32) : sizeof(u16);
+	len -= crclen;		/* remove trailing CRC */
+	if (len <= 0) {
+		IRDA_DEBUG(0, "%s: strange frame (len=%d)\n", __FUNCTION__, len);
+		ret |= VLSI_RX_DROP;
+		goto done;
+	}
+
+	if (idev->mode == IFF_SIR) {	/* hw checks CRC in MIR, FIR mode */
+
+		/* rd->buf is a streaming PCI_DMA_FROMDEVICE map. Doing the
+		 * endian-adjustment there just in place will dirty a cache line
+		 * which belongs to the map and thus we must be sure it will
+		 * get flushed before giving the buffer back to hardware.
+		 * vlsi_fill_rx() will do this anyway - but here we rely on.
+		 */
+		le16_to_cpus(rd->buf+len);
+		if (irda_calc_crc16(INIT_FCS,rd->buf,len+crclen) != GOOD_FCS) {
+			IRDA_DEBUG(0, "%s: crc error\n", __FUNCTION__);
+			ret |= VLSI_RX_CRC;
+			goto done;
+		}
+	}
+
+	if (!rd->skb) {
+		IRDA_WARNING("%s: rx packet lost\n", __FUNCTION__);
+		ret |= VLSI_RX_DROP;
+		goto done;
+	}
+
+	skb = rd->skb;
+	rd->skb = NULL;
+	skb->dev = ndev;
+	memcpy(skb_put(skb,len), rd->buf, len);
+	skb->mac.raw = skb->data;
+	if (in_interrupt())
+		netif_rx(skb);
+	else
+		netif_rx_ni(skb);
+	ndev->last_rx = jiffies;
+
+done:
+	rd_set_status(rd, 0);
+	rd_set_count(rd, 0);
+	/* buffer still owned by CPU */
+
+	return (ret) ? -ret : len;
+}
+
+static void vlsi_fill_rx(struct vlsi_ring *r)
+{
+	struct ring_descr *rd;
+
+	for (rd = ring_last(r); rd != NULL; rd = ring_put(r)) {
+		if (rd_is_active(rd)) {
+			IRDA_WARNING("%s: driver bug: rx descr race with hw\n",
+				     __FUNCTION__);
+			vlsi_ring_debug(r);
+			break;
+		}
+		if (!rd->skb) {
+			rd->skb = dev_alloc_skb(IRLAP_SKB_ALLOCSIZE);
+			if (rd->skb) {
+				skb_reserve(rd->skb,1);
+				rd->skb->protocol = htons(ETH_P_IRDA);
+			}
+			else
+				break;	/* probably not worth logging? */
+		}
+		/* give dma buffer back to busmaster */
+		pci_dma_sync_single_for_device(r->pdev, rd_get_addr(rd), r->len, r->dir);
+		rd_activate(rd);
+	}
+}
+
+static void vlsi_rx_interrupt(struct net_device *ndev)
+{
+	vlsi_irda_dev_t *idev = ndev->priv;
+	struct vlsi_ring *r = idev->rx_ring;
+	struct ring_descr *rd;
+	int ret;
+
+	for (rd = ring_first(r); rd != NULL; rd = ring_get(r)) {
+
+		if (rd_is_active(rd))
+			break;
+
+		ret = vlsi_process_rx(r, rd);
+
+		if (ret < 0) {
+			ret = -ret;
+			idev->stats.rx_errors++;
+			if (ret & VLSI_RX_DROP)  
+				idev->stats.rx_dropped++;
+			if (ret & VLSI_RX_OVER)  
+				idev->stats.rx_over_errors++;
+			if (ret & VLSI_RX_LENGTH)  
+				idev->stats.rx_length_errors++;
+			if (ret & VLSI_RX_FRAME)  
+				idev->stats.rx_frame_errors++;
+			if (ret & VLSI_RX_CRC)  
+				idev->stats.rx_crc_errors++;
+		}
+		else if (ret > 0) {
+			idev->stats.rx_packets++;
+			idev->stats.rx_bytes += ret;
+		}
+	}
+
+	do_gettimeofday(&idev->last_rx); /* remember "now" for later mtt delay */
+
+	vlsi_fill_rx(r);
+
+	if (ring_first(r) == NULL) {
+		/* we are in big trouble, if this should ever happen */
+		IRDA_ERROR("%s: rx ring exhausted!\n", __FUNCTION__);
+		vlsi_ring_debug(r);
+	}
+	else
+		outw(0, ndev->base_addr+VLSI_PIO_PROMPT);
+}
+
+/* caller must have stopped the controller from busmastering */
+
+static void vlsi_unarm_rx(vlsi_irda_dev_t *idev)
+{
+	struct vlsi_ring *r = idev->rx_ring;
+	struct ring_descr *rd;
+	int ret;
+
+	for (rd = ring_first(r); rd != NULL; rd = ring_get(r)) {
+
+		ret = 0;
+		if (rd_is_active(rd)) {
+			rd_set_status(rd, 0);
+			if (rd_get_count(rd)) {
+				IRDA_DEBUG(0, "%s - dropping rx packet\n", __FUNCTION__);
+				ret = -VLSI_RX_DROP;
+			}
+			rd_set_count(rd, 0);
+			pci_dma_sync_single_for_cpu(r->pdev, rd_get_addr(rd), r->len, r->dir);
+			if (rd->skb) {
+				dev_kfree_skb_any(rd->skb);
+				rd->skb = NULL;
+			}
+		}
+		else
+			ret = vlsi_process_rx(r, rd);
+
+		if (ret < 0) {
+			ret = -ret;
+			idev->stats.rx_errors++;
+			if (ret & VLSI_RX_DROP)  
+				idev->stats.rx_dropped++;
+			if (ret & VLSI_RX_OVER)  
+				idev->stats.rx_over_errors++;
+			if (ret & VLSI_RX_LENGTH)  
+				idev->stats.rx_length_errors++;
+			if (ret & VLSI_RX_FRAME)  
+				idev->stats.rx_frame_errors++;
+			if (ret & VLSI_RX_CRC)  
+				idev->stats.rx_crc_errors++;
+		}
+		else if (ret > 0) {
+			idev->stats.rx_packets++;
+			idev->stats.rx_bytes += ret;
+		}
+	}
+}
+
+/********************************************************/
+
+static int vlsi_process_tx(struct vlsi_ring *r, struct ring_descr *rd)
+{
+	u16		status;
+	int		len;
+	int		ret;
+
+	pci_dma_sync_single_for_cpu(r->pdev, rd_get_addr(rd), r->len, r->dir);
+	/* dma buffer now owned by the CPU */
+	status = rd_get_status(rd);
+	if (status & RD_TX_UNDRN)
+		ret = VLSI_TX_FIFO;
+	else
+		ret = 0;
+	rd_set_status(rd, 0);
+
+	if (rd->skb) {
+		len = rd->skb->len;
+		dev_kfree_skb_any(rd->skb);
+		rd->skb = NULL;
+	}
+	else	/* tx-skb already freed? - should never happen */
+		len = rd_get_count(rd);		/* incorrect for SIR! (due to wrapping) */
+
+	rd_set_count(rd, 0);
+	/* dma buffer still owned by the CPU */
+
+	return (ret) ? -ret : len;
+}
+
+static int vlsi_set_baud(vlsi_irda_dev_t *idev, unsigned iobase)
+{
+	u16 nphyctl;
+	u16 config;
+	unsigned mode;
+	int	ret;
+	int	baudrate;
+	int	fifocnt;
+
+	baudrate = idev->new_baud;
+	IRDA_DEBUG(2, "%s: %d -> %d\n", __FUNCTION__, idev->baud, idev->new_baud);
+	if (baudrate == 4000000) {
+		mode = IFF_FIR;
+		config = IRCFG_FIR;
+		nphyctl = PHYCTL_FIR;
+	}
+	else if (baudrate == 1152000) {
+		mode = IFF_MIR;
+		config = IRCFG_MIR | IRCFG_CRC16;
+		nphyctl = PHYCTL_MIR(clksrc==3);
+	}
+	else {
+		mode = IFF_SIR;
+		config = IRCFG_SIR | IRCFG_SIRFILT  | IRCFG_RXANY;
+		switch(baudrate) {
+			default:
+				IRDA_WARNING("%s: undefined baudrate %d - fallback to 9600!\n",
+					     __FUNCTION__, baudrate);
+				baudrate = 9600;
+				/* fallthru */
+			case 2400:
+			case 9600:
+			case 19200:
+			case 38400:
+			case 57600:
+			case 115200:
+				nphyctl = PHYCTL_SIR(baudrate,sirpulse,clksrc==3);
+				break;
+		}
+	}
+	config |= IRCFG_MSTR | IRCFG_ENRX;
+
+	fifocnt = inw(iobase+VLSI_PIO_RCVBCNT) & RCVBCNT_MASK;
+	if (fifocnt != 0) {
+		IRDA_DEBUG(0, "%s: rx fifo not empty(%d)\n", __FUNCTION__, fifocnt);
+	}
+
+	outw(0, iobase+VLSI_PIO_IRENABLE);
+	outw(config, iobase+VLSI_PIO_IRCFG);
+	outw(nphyctl, iobase+VLSI_PIO_NPHYCTL);
+	wmb();
+	outw(IRENABLE_PHYANDCLOCK, iobase+VLSI_PIO_IRENABLE);
+	mb();
+
+	udelay(1);	/* chip applies IRCFG on next rising edge of its 8MHz clock */
+
+	/* read back settings for validation */
+
+	config = inw(iobase+VLSI_PIO_IRENABLE) & IRENABLE_MASK;
+
+	if (mode == IFF_FIR)
+		config ^= IRENABLE_FIR_ON;
+	else if (mode == IFF_MIR)
+		config ^= (IRENABLE_MIR_ON|IRENABLE_CRC16_ON);
+	else
+		config ^= IRENABLE_SIR_ON;
+
+	if (config != (IRENABLE_PHYANDCLOCK|IRENABLE_ENRXST)) {
+		IRDA_WARNING("%s: failed to set %s mode!\n", __FUNCTION__,
+			(mode==IFF_SIR)?"SIR":((mode==IFF_MIR)?"MIR":"FIR"));
+		ret = -1;
+	}
+	else {
+		if (inw(iobase+VLSI_PIO_PHYCTL) != nphyctl) {
+			IRDA_WARNING("%s: failed to apply baudrate %d\n",
+				     __FUNCTION__, baudrate);
+			ret = -1;
+		}
+		else {
+			idev->mode = mode;
+			idev->baud = baudrate;
+			idev->new_baud = 0;
+			ret = 0;
+		}
+	}
+
+	if (ret)
+		vlsi_reg_debug(iobase,__FUNCTION__);
+
+	return ret;
+}
+
+static int vlsi_hard_start_xmit(struct sk_buff *skb, struct net_device *ndev)
+{
+	vlsi_irda_dev_t *idev = ndev->priv;
+	struct vlsi_ring	*r = idev->tx_ring;
+	struct ring_descr *rd;
+	unsigned long flags;
+	unsigned iobase = ndev->base_addr;
+	u8 status;
+	u16 config;
+	int mtt;
+	int len, speed;
+	struct timeval  now, ready;
+	char *msg = NULL;
+
+	speed = irda_get_next_speed(skb);
+	spin_lock_irqsave(&idev->lock, flags);
+	if (speed != -1  &&  speed != idev->baud) {
+		netif_stop_queue(ndev);
+		idev->new_baud = speed;
+		status = RD_TX_CLRENTX;  /* stop tx-ring after this frame */
+	}
+	else
+		status = 0;
+
+	if (skb->len == 0) {
+		/* handle zero packets - should be speed change */
+		if (status == 0) {
+			msg = "bogus zero-length packet";
+			goto drop_unlock;
+		}
+
+		/* due to the completely asynch tx operation we might have
+		 * IrLAP racing with the hardware here, f.e. if the controller
+		 * is just sending the last packet with current speed while
+		 * the LAP is already switching the speed using synchronous
+		 * len=0 packet. Immediate execution would lead to hw lockup
+		 * requiring a powercycle to reset. Good candidate to trigger
+		 * this is the final UA:RSP packet after receiving a DISC:CMD
+		 * when getting the LAP down.
+		 * Note that we are not protected by the queue_stop approach
+		 * because the final UA:RSP arrives _without_ request to apply
+		 * new-speed-after-this-packet - hence the driver doesn't know
+		 * this was the last packet and doesn't stop the queue. So the
+		 * forced switch to default speed from LAP gets through as fast
+		 * as only some 10 usec later while the UA:RSP is still processed
+		 * by the hardware and we would get screwed.
+		 */
+
+		if (ring_first(idev->tx_ring) == NULL) {
+			/* no race - tx-ring already empty */
+			vlsi_set_baud(idev, iobase);
+			netif_wake_queue(ndev);
+		}
+		else
+			;
+			/* keep the speed change pending like it would
+			 * for any len>0 packet. tx completion interrupt
+			 * will apply it when the tx ring becomes empty.
+			 */
+		spin_unlock_irqrestore(&idev->lock, flags);
+		dev_kfree_skb_any(skb);
+		return 0;
+	}
+
+	/* sanity checks - simply drop the packet */
+
+	rd = ring_last(r);
+	if (!rd) {
+		msg = "ring full, but queue wasn't stopped";
+		goto drop_unlock;
+	}
+
+	if (rd_is_active(rd)) {
+		msg = "entry still owned by hw";
+		goto drop_unlock;
+	}
+
+	if (!rd->buf) {
+		msg = "tx ring entry without pci buffer";
+		goto drop_unlock;
+	}
+
+	if (rd->skb) {
+		msg = "ring entry with old skb still attached";
+		goto drop_unlock;
+	}
+
+	/* no need for serialization or interrupt disable during mtt */
+	spin_unlock_irqrestore(&idev->lock, flags);
+
+	if ((mtt = irda_get_mtt(skb)) > 0) {
+	
+		ready.tv_usec = idev->last_rx.tv_usec + mtt;
+		ready.tv_sec = idev->last_rx.tv_sec;
+		if (ready.tv_usec >= 1000000) {
+			ready.tv_usec -= 1000000;
+			ready.tv_sec++;		/* IrLAP 1.1: mtt always < 1 sec */
+		}
+		for(;;) {
+			do_gettimeofday(&now);
+			if (now.tv_sec > ready.tv_sec
+			    ||  (now.tv_sec==ready.tv_sec && now.tv_usec>=ready.tv_usec))
+			    	break;
+			udelay(100);
+			/* must not sleep here - we are called under xmit_lock! */
+		}
+	}
+
+	/* tx buffer already owned by CPU due to pci_dma_sync_single_for_cpu()
+	 * after subsequent tx-completion
+	 */
+
+	if (idev->mode == IFF_SIR) {
+		status |= RD_TX_DISCRC;		/* no hw-crc creation */
+		len = async_wrap_skb(skb, rd->buf, r->len);
+
+		/* Some rare worst case situation in SIR mode might lead to
+		 * potential buffer overflow. The wrapper detects this, returns
+		 * with a shortened frame (without FCS/EOF) but doesn't provide
+		 * any error indication about the invalid packet which we are
+		 * going to transmit.
+		 * Therefore we log if the buffer got filled to the point, where the
+		 * wrapper would abort, i.e. when there are less than 5 bytes left to
+		 * allow appending the FCS/EOF.
+		 */
+
+		if (len >= r->len-5)
+			 IRDA_WARNING("%s: possible buffer overflow with SIR wrapping!\n",
+				      __FUNCTION__);
+	}
+	else {
+		/* hw deals with MIR/FIR mode wrapping */
+		status |= RD_TX_PULSE;		/* send 2 us highspeed indication pulse */
+		len = skb->len;
+		if (len > r->len) {
+			msg = "frame exceeds tx buffer length";
+			goto drop;
+		}
+		else
+			memcpy(rd->buf, skb->data, len);
+	}
+
+	rd->skb = skb;			/* remember skb for tx-complete stats */
+
+	rd_set_count(rd, len);
+	rd_set_status(rd, status);	/* not yet active! */
+
+	/* give dma buffer back to busmaster-hw (flush caches to make
+	 * CPU-driven changes visible from the pci bus).
+	 */
+
+	pci_dma_sync_single_for_device(r->pdev, rd_get_addr(rd), r->len, r->dir);
+
+/*	Switching to TX mode here races with the controller
+ *	which may stop TX at any time when fetching an inactive descriptor
+ *	or one with CLR_ENTX set. So we switch on TX only, if TX was not running
+ *	_after_ the new descriptor was activated on the ring. This ensures
+ *	we will either find TX already stopped or we can be sure, there
+ *	will be a TX-complete interrupt even if the chip stopped doing
+ *	TX just after we found it still running. The ISR will then find
+ *	the non-empty ring and restart TX processing. The enclosing
+ *	spinlock provides the correct serialization to prevent race with isr.
+ */
+
+	spin_lock_irqsave(&idev->lock,flags);
+
+	rd_activate(rd);
+
+	if (!(inw(iobase+VLSI_PIO_IRENABLE) & IRENABLE_ENTXST)) {
+		int fifocnt;
+
+		fifocnt = inw(ndev->base_addr+VLSI_PIO_RCVBCNT) & RCVBCNT_MASK;
+		if (fifocnt != 0) {
+			IRDA_DEBUG(0, "%s: rx fifo not empty(%d)\n", __FUNCTION__, fifocnt);
+		}
+
+		config = inw(iobase+VLSI_PIO_IRCFG);
+		mb();
+		outw(config | IRCFG_ENTX, iobase+VLSI_PIO_IRCFG);
+		wmb();
+		outw(0, iobase+VLSI_PIO_PROMPT);
+	}
+	ndev->trans_start = jiffies;
+
+	if (ring_put(r) == NULL) {
+		netif_stop_queue(ndev);
+		IRDA_DEBUG(3, "%s: tx ring full - queue stopped\n", __FUNCTION__);
+	}
+	spin_unlock_irqrestore(&idev->lock, flags);
+
+	return 0;
+
+drop_unlock:
+	spin_unlock_irqrestore(&idev->lock, flags);
+drop:
+	IRDA_WARNING("%s: dropping packet - %s\n", __FUNCTION__, msg);
+	dev_kfree_skb_any(skb);
+	idev->stats.tx_errors++;
+	idev->stats.tx_dropped++;
+	/* Don't even think about returning NET_XMIT_DROP (=1) here!
+	 * In fact any retval!=0 causes the packet scheduler to requeue the
+	 * packet for later retry of transmission - which isn't exactly
+	 * what we want after we've just called dev_kfree_skb_any ;-)
+	 */
+	return 0;
+}
+
+static void vlsi_tx_interrupt(struct net_device *ndev)
+{
+	vlsi_irda_dev_t *idev = ndev->priv;
+	struct vlsi_ring	*r = idev->tx_ring;
+	struct ring_descr	*rd;
+	unsigned	iobase;
+	int	ret;
+	u16	config;
+
+	for (rd = ring_first(r); rd != NULL; rd = ring_get(r)) {
+
+		if (rd_is_active(rd))
+			break;
+
+		ret = vlsi_process_tx(r, rd);
+
+		if (ret < 0) {
+			ret = -ret;
+			idev->stats.tx_errors++;
+			if (ret & VLSI_TX_DROP)
+				idev->stats.tx_dropped++;
+			if (ret & VLSI_TX_FIFO)
+				idev->stats.tx_fifo_errors++;
+		}
+		else if (ret > 0){
+			idev->stats.tx_packets++;
+			idev->stats.tx_bytes += ret;
+		}
+	}
+
+	iobase = ndev->base_addr;
+
+	if (idev->new_baud  &&  rd == NULL)	/* tx ring empty and speed change pending */
+		vlsi_set_baud(idev, iobase);
+
+	config = inw(iobase+VLSI_PIO_IRCFG);
+	if (rd == NULL)			/* tx ring empty: re-enable rx */
+		outw((config & ~IRCFG_ENTX) | IRCFG_ENRX, iobase+VLSI_PIO_IRCFG);
+
+	else if (!(inw(iobase+VLSI_PIO_IRENABLE) & IRENABLE_ENTXST)) {
+		int fifocnt;
+
+		fifocnt = inw(iobase+VLSI_PIO_RCVBCNT) & RCVBCNT_MASK;
+		if (fifocnt != 0) {
+			IRDA_DEBUG(0, "%s: rx fifo not empty(%d)\n",
+				__FUNCTION__, fifocnt);
+		}
+		outw(config | IRCFG_ENTX, iobase+VLSI_PIO_IRCFG);
+	}
+
+	outw(0, iobase+VLSI_PIO_PROMPT);
+
+	if (netif_queue_stopped(ndev)  &&  !idev->new_baud) {
+		netif_wake_queue(ndev);
+		IRDA_DEBUG(3, "%s: queue awoken\n", __FUNCTION__);
+	}
+}
+
+/* caller must have stopped the controller from busmastering */
+
+static void vlsi_unarm_tx(vlsi_irda_dev_t *idev)
+{
+	struct vlsi_ring *r = idev->tx_ring;
+	struct ring_descr *rd;
+	int ret;
+
+	for (rd = ring_first(r); rd != NULL; rd = ring_get(r)) {
+
+		ret = 0;
+		if (rd_is_active(rd)) {
+			rd_set_status(rd, 0);
+			rd_set_count(rd, 0);
+			pci_dma_sync_single_for_cpu(r->pdev, rd_get_addr(rd), r->len, r->dir);
+			if (rd->skb) {
+				dev_kfree_skb_any(rd->skb);
+				rd->skb = NULL;
+			}
+			IRDA_DEBUG(0, "%s - dropping tx packet\n", __FUNCTION__);
+			ret = -VLSI_TX_DROP;
+		}
+		else
+			ret = vlsi_process_tx(r, rd);
+
+		if (ret < 0) {
+			ret = -ret;
+			idev->stats.tx_errors++;
+			if (ret & VLSI_TX_DROP)
+				idev->stats.tx_dropped++;
+			if (ret & VLSI_TX_FIFO)
+				idev->stats.tx_fifo_errors++;
+		}
+		else if (ret > 0){
+			idev->stats.tx_packets++;
+			idev->stats.tx_bytes += ret;
+		}
+	}
+
+}
+
+/********************************************************/
+
+static int vlsi_start_clock(struct pci_dev *pdev)
+{
+	u8	clkctl, lock;
+	int	i, count;
+
+	if (clksrc < 2) { /* auto or PLL: try PLL */
+		clkctl = CLKCTL_PD_INV | CLKCTL_CLKSTP;
+		pci_write_config_byte(pdev, VLSI_PCI_CLKCTL, clkctl);
+
+		/* procedure to detect PLL lock synchronisation:
+		 * after 0.5 msec initial delay we expect to find 3 PLL lock
+		 * indications within 10 msec for successful PLL detection.
+		 */
+		udelay(500);
+		count = 0;
+		for (i = 500; i <= 10000; i += 50) { /* max 10 msec */
+			pci_read_config_byte(pdev, VLSI_PCI_CLKCTL, &lock);
+			if (lock&CLKCTL_LOCK) {
+				if (++count >= 3)
+					break;
+			}
+			udelay(50);
+		}
+		if (count < 3) {
+			if (clksrc == 1) { /* explicitly asked for PLL hence bail out */
+				IRDA_ERROR("%s: no PLL or failed to lock!\n",
+					   __FUNCTION__);
+				clkctl = CLKCTL_CLKSTP;
+				pci_write_config_byte(pdev, VLSI_PCI_CLKCTL, clkctl);
+				return -1;
+			}
+			else			/* was: clksrc=0(auto) */
+				clksrc = 3;	/* fallback to 40MHz XCLK (OB800) */
+
+			IRDA_DEBUG(0, "%s: PLL not locked, fallback to clksrc=%d\n",
+				__FUNCTION__, clksrc);
+		}
+		else
+			clksrc = 1;	/* got successful PLL lock */
+	}
+
+	if (clksrc != 1) {
+		/* we get here if either no PLL detected in auto-mode or
+		   an external clock source was explicitly specified */
+
+		clkctl = CLKCTL_EXTCLK | CLKCTL_CLKSTP;
+		if (clksrc == 3)
+			clkctl |= CLKCTL_XCKSEL;	
+		pci_write_config_byte(pdev, VLSI_PCI_CLKCTL, clkctl);
+
+		/* no way to test for working XCLK */
+	}
+	else
+		pci_read_config_byte(pdev, VLSI_PCI_CLKCTL, &clkctl);
+
+	/* ok, now going to connect the chip with the clock source */
+
+	clkctl &= ~CLKCTL_CLKSTP;
+	pci_write_config_byte(pdev, VLSI_PCI_CLKCTL, clkctl);
+
+	return 0;
+}
+
+static void vlsi_stop_clock(struct pci_dev *pdev)
+{
+	u8	clkctl;
+
+	/* disconnect chip from clock source */
+	pci_read_config_byte(pdev, VLSI_PCI_CLKCTL, &clkctl);
+	clkctl |= CLKCTL_CLKSTP;
+	pci_write_config_byte(pdev, VLSI_PCI_CLKCTL, clkctl);
+
+	/* disable all clock sources */
+	clkctl &= ~(CLKCTL_EXTCLK | CLKCTL_PD_INV);
+	pci_write_config_byte(pdev, VLSI_PCI_CLKCTL, clkctl);
+}
+
+/********************************************************/
+
+/* writing all-zero to the VLSI PCI IO register area seems to prevent
+ * some occasional situations where the hardware fails (symptoms are 
+ * what appears as stalled tx/rx state machines, i.e. everything ok for
+ * receive or transmit but hw makes no progress or is unable to access
+ * the bus memory locations).
+ * Best place to call this is immediately after/before the internal clock
+ * gets started/stopped.
+ */
+
+static inline void vlsi_clear_regs(unsigned iobase)
+{
+	unsigned	i;
+	const unsigned	chip_io_extent = 32;
+
+	for (i = 0; i < chip_io_extent; i += sizeof(u16))
+		outw(0, iobase + i);
+}
+
+static int vlsi_init_chip(struct pci_dev *pdev)
+{
+	struct net_device *ndev = pci_get_drvdata(pdev);
+	vlsi_irda_dev_t *idev = ndev->priv;
+	unsigned	iobase;
+	u16 ptr;
+
+	/* start the clock and clean the registers */
+
+	if (vlsi_start_clock(pdev)) {
+		IRDA_ERROR("%s: no valid clock source\n", __FUNCTION__);
+		return -1;
+	}
+	iobase = ndev->base_addr;
+	vlsi_clear_regs(iobase);
+
+	outb(IRINTR_INT_MASK, iobase+VLSI_PIO_IRINTR); /* w/c pending IRQ, disable all INT */
+
+	outw(0, iobase+VLSI_PIO_IRENABLE);	/* disable IrPHY-interface */
+
+	/* disable everything, particularly IRCFG_MSTR - (also resetting the RING_PTR) */
+
+	outw(0, iobase+VLSI_PIO_IRCFG);
+	wmb();
+
+	outw(MAX_PACKET_LENGTH, iobase+VLSI_PIO_MAXPKT);  /* max possible value=0x0fff */
+
+	outw(BUS_TO_RINGBASE(idev->busaddr), iobase+VLSI_PIO_RINGBASE);
+
+	outw(TX_RX_TO_RINGSIZE(idev->tx_ring->size, idev->rx_ring->size),
+		iobase+VLSI_PIO_RINGSIZE);	
+
+	ptr = inw(iobase+VLSI_PIO_RINGPTR);
+	atomic_set(&idev->rx_ring->head, RINGPTR_GET_RX(ptr));
+	atomic_set(&idev->rx_ring->tail, RINGPTR_GET_RX(ptr));
+	atomic_set(&idev->tx_ring->head, RINGPTR_GET_TX(ptr));
+	atomic_set(&idev->tx_ring->tail, RINGPTR_GET_TX(ptr));
+
+	vlsi_set_baud(idev, iobase);	/* idev->new_baud used as provided by caller */
+
+	outb(IRINTR_INT_MASK, iobase+VLSI_PIO_IRINTR);	/* just in case - w/c pending IRQ's */
+	wmb();
+
+	/* DO NOT BLINDLY ENABLE IRINTR_ACTEN!
+	 * basically every received pulse fires an ACTIVITY-INT
+	 * leading to >>1000 INT's per second instead of few 10
+	 */
+
+	outb(IRINTR_RPKTEN|IRINTR_TPKTEN, iobase+VLSI_PIO_IRINTR);
+
+	return 0;
+}
+
+static int vlsi_start_hw(vlsi_irda_dev_t *idev)
+{
+	struct pci_dev *pdev = idev->pdev;
+	struct net_device *ndev = pci_get_drvdata(pdev);
+	unsigned iobase = ndev->base_addr;
+	u8 byte;
+
+	/* we don't use the legacy UART, disable its address decoding */
+
+	pci_read_config_byte(pdev, VLSI_PCI_IRMISC, &byte);
+	byte &= ~(IRMISC_UARTEN | IRMISC_UARTTST);
+	pci_write_config_byte(pdev, VLSI_PCI_IRMISC, byte);
+
+	/* enable PCI busmaster access to our 16MB page */
+
+	pci_write_config_byte(pdev, VLSI_PCI_MSTRPAGE, MSTRPAGE_VALUE);
+	pci_set_master(pdev);
+
+	if (vlsi_init_chip(pdev) < 0) {
+		pci_disable_device(pdev);
+		return -1;
+	}
+
+	vlsi_fill_rx(idev->rx_ring);
+
+	do_gettimeofday(&idev->last_rx);	/* first mtt may start from now on */
+
+	outw(0, iobase+VLSI_PIO_PROMPT);	/* kick hw state machine */
+
+	return 0;
+}
+
+static int vlsi_stop_hw(vlsi_irda_dev_t *idev)
+{
+	struct pci_dev *pdev = idev->pdev;
+	struct net_device *ndev = pci_get_drvdata(pdev);
+	unsigned iobase = ndev->base_addr;
+	unsigned long flags;
+
+	spin_lock_irqsave(&idev->lock,flags);
+	outw(0, iobase+VLSI_PIO_IRENABLE);
+	outw(0, iobase+VLSI_PIO_IRCFG);			/* disable everything */
+
+	/* disable and w/c irqs */
+	outb(0, iobase+VLSI_PIO_IRINTR);
+	wmb();
+	outb(IRINTR_INT_MASK, iobase+VLSI_PIO_IRINTR);
+	spin_unlock_irqrestore(&idev->lock,flags);
+
+	vlsi_unarm_tx(idev);
+	vlsi_unarm_rx(idev);
+
+	vlsi_clear_regs(iobase);
+	vlsi_stop_clock(pdev);
+
+	pci_disable_device(pdev);
+
+	return 0;
+}
+
+/**************************************************************/
+
+static struct net_device_stats * vlsi_get_stats(struct net_device *ndev)
+{
+	vlsi_irda_dev_t *idev = ndev->priv;
+
+	return &idev->stats;
+}
+
+static void vlsi_tx_timeout(struct net_device *ndev)
+{
+	vlsi_irda_dev_t *idev = ndev->priv;
+
+
+	vlsi_reg_debug(ndev->base_addr, __FUNCTION__);
+	vlsi_ring_debug(idev->tx_ring);
+
+	if (netif_running(ndev))
+		netif_stop_queue(ndev);
+
+	vlsi_stop_hw(idev);
+
+	/* now simply restart the whole thing */
+
+	if (!idev->new_baud)
+		idev->new_baud = idev->baud;		/* keep current baudrate */
+
+	if (vlsi_start_hw(idev))
+		IRDA_ERROR("%s: failed to restart hw - %s(%s) unusable!\n",
+			   __FUNCTION__, PCIDEV_NAME(idev->pdev), ndev->name);
+	else
+		netif_start_queue(ndev);
+}
+
+static int vlsi_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd)
+{
+	vlsi_irda_dev_t *idev = ndev->priv;
+	struct if_irda_req *irq = (struct if_irda_req *) rq;
+	unsigned long flags;
+	u16 fifocnt;
+	int ret = 0;
+
+	switch (cmd) {
+		case SIOCSBANDWIDTH:
+			if (!capable(CAP_NET_ADMIN)) {
+				ret = -EPERM;
+				break;
+			}
+			spin_lock_irqsave(&idev->lock, flags);
+			idev->new_baud = irq->ifr_baudrate;
+			/* when called from userland there might be a minor race window here
+			 * if the stack tries to change speed concurrently - which would be
+			 * pretty strange anyway with the userland having full control...
+			 */
+			vlsi_set_baud(idev, ndev->base_addr);
+			spin_unlock_irqrestore(&idev->lock, flags);
+			break;
+		case SIOCSMEDIABUSY:
+			if (!capable(CAP_NET_ADMIN)) {
+				ret = -EPERM;
+				break;
+			}
+			irda_device_set_media_busy(ndev, TRUE);
+			break;
+		case SIOCGRECEIVING:
+			/* the best we can do: check whether there are any bytes in rx fifo.
+			 * The trustable window (in case some data arrives just afterwards)
+			 * may be as short as 1usec or so at 4Mbps.
+			 */
+			fifocnt = inw(ndev->base_addr+VLSI_PIO_RCVBCNT) & RCVBCNT_MASK;
+			irq->ifr_receiving = (fifocnt!=0) ? 1 : 0;
+			break;
+		default:
+			IRDA_WARNING("%s: notsupp - cmd=%04x\n",
+				     __FUNCTION__, cmd);
+			ret = -EOPNOTSUPP;
+	}	
+	
+	return ret;
+}
+
+/********************************************************/
+
+static irqreturn_t vlsi_interrupt(int irq, void *dev_instance,
+					struct pt_regs *regs)
+{
+	struct net_device *ndev = dev_instance;
+	vlsi_irda_dev_t *idev = ndev->priv;
+	unsigned	iobase;
+	u8		irintr;
+	int 		boguscount = 5;
+	unsigned long	flags;
+	int		handled = 0;
+
+	iobase = ndev->base_addr;
+	spin_lock_irqsave(&idev->lock,flags);
+	do {
+		irintr = inb(iobase+VLSI_PIO_IRINTR);
+		mb();
+		outb(irintr, iobase+VLSI_PIO_IRINTR);	/* acknowledge asap */
+
+		if (!(irintr&=IRINTR_INT_MASK))		/* not our INT - probably shared */
+			break;
+
+		handled = 1;
+
+		if (unlikely(!(irintr & ~IRINTR_ACTIVITY)))
+			break;				/* nothing todo if only activity */
+
+		if (irintr&IRINTR_RPKTINT)
+			vlsi_rx_interrupt(ndev);
+
+		if (irintr&IRINTR_TPKTINT)
+			vlsi_tx_interrupt(ndev);
+
+	} while (--boguscount > 0);
+	spin_unlock_irqrestore(&idev->lock,flags);
+
+	if (boguscount <= 0)
+		IRDA_MESSAGE("%s: too much work in interrupt!\n",
+			     __FUNCTION__);
+	return IRQ_RETVAL(handled);
+}
+
+/********************************************************/
+
+static int vlsi_open(struct net_device *ndev)
+{
+	vlsi_irda_dev_t *idev = ndev->priv;
+	int	err = -EAGAIN;
+	char	hwname[32];
+
+	if (pci_request_regions(idev->pdev, drivername)) {
+		IRDA_WARNING("%s: io resource busy\n", __FUNCTION__);
+		goto errout;
+	}
+	ndev->base_addr = pci_resource_start(idev->pdev,0);
+	ndev->irq = idev->pdev->irq;
+
+	/* under some rare occasions the chip apparently comes up with
+	 * IRQ's pending. We better w/c pending IRQ and disable them all
+	 */
+
+	outb(IRINTR_INT_MASK, ndev->base_addr+VLSI_PIO_IRINTR);
+
+	if (request_irq(ndev->irq, vlsi_interrupt, SA_SHIRQ,
+			drivername, ndev)) {
+		IRDA_WARNING("%s: couldn't get IRQ: %d\n",
+			     __FUNCTION__, ndev->irq);
+		goto errout_io;
+	}
+
+	if ((err = vlsi_create_hwif(idev)) != 0)
+		goto errout_irq;
+
+	sprintf(hwname, "VLSI-FIR @ 0x%04x", (unsigned)ndev->base_addr);
+	idev->irlap = irlap_open(ndev,&idev->qos,hwname);
+	if (!idev->irlap)
+		goto errout_free_ring;
+
+	do_gettimeofday(&idev->last_rx);  /* first mtt may start from now on */
+
+	idev->new_baud = 9600;		/* start with IrPHY using 9600(SIR) mode */
+
+	if ((err = vlsi_start_hw(idev)) != 0)
+		goto errout_close_irlap;
+
+	netif_start_queue(ndev);
+
+	IRDA_MESSAGE("%s: device %s operational\n", __FUNCTION__, ndev->name);
+
+	return 0;
+
+errout_close_irlap:
+	irlap_close(idev->irlap);
+errout_free_ring:
+	vlsi_destroy_hwif(idev);
+errout_irq:
+	free_irq(ndev->irq,ndev);
+errout_io:
+	pci_release_regions(idev->pdev);
+errout:
+	return err;
+}
+
+static int vlsi_close(struct net_device *ndev)
+{
+	vlsi_irda_dev_t *idev = ndev->priv;
+
+	netif_stop_queue(ndev);
+
+	if (idev->irlap)
+		irlap_close(idev->irlap);
+	idev->irlap = NULL;
+
+	vlsi_stop_hw(idev);
+
+	vlsi_destroy_hwif(idev);
+
+	free_irq(ndev->irq,ndev);
+
+	pci_release_regions(idev->pdev);
+
+	IRDA_MESSAGE("%s: device %s stopped\n", __FUNCTION__, ndev->name);
+
+	return 0;
+}
+
+static int vlsi_irda_init(struct net_device *ndev)
+{
+	vlsi_irda_dev_t *idev = ndev->priv;
+	struct pci_dev *pdev = idev->pdev;
+
+	SET_MODULE_OWNER(ndev);
+
+	ndev->irq = pdev->irq;
+	ndev->base_addr = pci_resource_start(pdev,0);
+
+	/* PCI busmastering
+	 * see include file for details why we need these 2 masks, in this order!
+	 */
+
+	if (pci_set_dma_mask(pdev,DMA_MASK_USED_BY_HW)
+	    || pci_set_dma_mask(pdev,DMA_MASK_MSTRPAGE)) {
+		IRDA_ERROR("%s: aborting due to PCI BM-DMA address limitations\n", __FUNCTION__);
+		return -1;
+	}
+
+	irda_init_max_qos_capabilies(&idev->qos);
+
+	/* the VLSI82C147 does not support 576000! */
+
+	idev->qos.baud_rate.bits = IR_2400 | IR_9600
+		| IR_19200 | IR_38400 | IR_57600 | IR_115200
+		| IR_1152000 | (IR_4000000 << 8);
+
+	idev->qos.min_turn_time.bits = qos_mtt_bits;
+
+	irda_qos_bits_to_value(&idev->qos);
+
+	/* currently no public media definitions for IrDA */
+
+	ndev->flags |= IFF_PORTSEL | IFF_AUTOMEDIA;
+	ndev->if_port = IF_PORT_UNKNOWN;
+ 
+	ndev->open	      = vlsi_open;
+	ndev->stop	      = vlsi_close;
+	ndev->get_stats	      = vlsi_get_stats;
+	ndev->hard_start_xmit = vlsi_hard_start_xmit;
+	ndev->do_ioctl	      = vlsi_ioctl;
+	ndev->tx_timeout      = vlsi_tx_timeout;
+	ndev->watchdog_timeo  = 500*HZ/1000;	/* max. allowed turn time for IrLAP */
+
+	SET_NETDEV_DEV(ndev, &pdev->dev);
+
+	return 0;
+}	
+
+/**************************************************************/
+
+static int __devinit
+vlsi_irda_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+	struct net_device	*ndev;
+	vlsi_irda_dev_t		*idev;
+
+	if (pci_enable_device(pdev))
+		goto out;
+	else
+		pdev->current_state = 0; /* hw must be running now */
+
+	IRDA_MESSAGE("%s: IrDA PCI controller %s detected\n",
+		     drivername, PCIDEV_NAME(pdev));
+
+	if ( !pci_resource_start(pdev,0)
+	     || !(pci_resource_flags(pdev,0) & IORESOURCE_IO) ) {
+		IRDA_ERROR("%s: bar 0 invalid", __FUNCTION__);
+		goto out_disable;
+	}
+
+	ndev = alloc_irdadev(sizeof(*idev));
+	if (ndev==NULL) {
+		IRDA_ERROR("%s: Unable to allocate device memory.\n",
+			   __FUNCTION__);
+		goto out_disable;
+	}
+
+	idev = ndev->priv;
+
+	spin_lock_init(&idev->lock);
+	init_MUTEX(&idev->sem);
+	down(&idev->sem);
+	idev->pdev = pdev;
+
+	if (vlsi_irda_init(ndev) < 0)
+		goto out_freedev;
+
+	if (register_netdev(ndev) < 0) {
+		IRDA_ERROR("%s: register_netdev failed\n", __FUNCTION__);
+		goto out_freedev;
+	}
+
+	if (vlsi_proc_root != NULL) {
+		struct proc_dir_entry *ent;
+
+		ent = create_proc_entry(ndev->name, S_IFREG|S_IRUGO, vlsi_proc_root);
+		if (!ent) {
+			IRDA_WARNING("%s: failed to create proc entry\n",
+				     __FUNCTION__);
+		} else {
+			ent->data = ndev;
+			ent->proc_fops = VLSI_PROC_FOPS;
+			ent->size = 0;
+		}
+		idev->proc_entry = ent;
+	}
+	IRDA_MESSAGE("%s: registered device %s\n", drivername, ndev->name);
+
+	pci_set_drvdata(pdev, ndev);
+	up(&idev->sem);
+
+	return 0;
+
+out_freedev:
+	up(&idev->sem);
+	free_netdev(ndev);
+out_disable:
+	pci_disable_device(pdev);
+out:
+	pci_set_drvdata(pdev, NULL);
+	return -ENODEV;
+}
+
+static void __devexit vlsi_irda_remove(struct pci_dev *pdev)
+{
+	struct net_device *ndev = pci_get_drvdata(pdev);
+	vlsi_irda_dev_t *idev;
+
+	if (!ndev) {
+		IRDA_ERROR("%s: lost netdevice?\n", drivername);
+		return;
+	}
+
+	unregister_netdev(ndev);
+
+	idev = ndev->priv;
+	down(&idev->sem);
+	if (idev->proc_entry) {
+		remove_proc_entry(ndev->name, vlsi_proc_root);
+		idev->proc_entry = NULL;
+	}
+	up(&idev->sem);
+
+	free_netdev(ndev);
+
+	pci_set_drvdata(pdev, NULL);
+
+	IRDA_MESSAGE("%s: %s removed\n", drivername, PCIDEV_NAME(pdev));
+}
+
+#ifdef CONFIG_PM
+
+/* The Controller doesn't provide PCI PM capabilities as defined by PCI specs.
+ * Some of the Linux PCI-PM code however depends on this, for example in
+ * pci_set_power_state(). So we have to take care to perform the required
+ * operations on our own (particularly reflecting the pdev->current_state)
+ * otherwise we might get cheated by pci-pm.
+ */
+
+
+static int vlsi_irda_suspend(struct pci_dev *pdev, u32 state)
+{
+	struct net_device *ndev = pci_get_drvdata(pdev);
+	vlsi_irda_dev_t *idev;
+
+	if (state < 1 || state > 3 ) {
+		IRDA_ERROR("%s - %s: invalid pm state request: %u\n",
+			   __FUNCTION__, PCIDEV_NAME(pdev), state);
+		return 0;
+	}
+	if (!ndev) {
+		IRDA_ERROR("%s - %s: no netdevice \n",
+			   __FUNCTION__, PCIDEV_NAME(pdev));
+		return 0;
+	}
+	idev = ndev->priv;	
+	down(&idev->sem);
+	if (pdev->current_state != 0) {			/* already suspended */
+		if (state > pdev->current_state) {	/* simply go deeper */
+			pci_set_power_state(pdev,state);
+			pdev->current_state = state;
+		}
+		else
+			IRDA_ERROR("%s - %s: invalid suspend request %u -> %u\n", __FUNCTION__, PCIDEV_NAME(pdev), pdev->current_state, state);
+		up(&idev->sem);
+		return 0;
+	}
+
+	if (netif_running(ndev)) {
+		netif_device_detach(ndev);
+		vlsi_stop_hw(idev);
+		pci_save_state(pdev);
+		if (!idev->new_baud)
+			/* remember speed settings to restore on resume */
+			idev->new_baud = idev->baud;
+	}
+
+	pci_set_power_state(pdev,state);
+	pdev->current_state = state;
+	idev->resume_ok = 1;
+	up(&idev->sem);
+	return 0;
+}
+
+static int vlsi_irda_resume(struct pci_dev *pdev)
+{
+	struct net_device *ndev = pci_get_drvdata(pdev);
+	vlsi_irda_dev_t	*idev;
+
+	if (!ndev) {
+		IRDA_ERROR("%s - %s: no netdevice \n",
+			   __FUNCTION__, PCIDEV_NAME(pdev));
+		return 0;
+	}
+	idev = ndev->priv;	
+	down(&idev->sem);
+	if (pdev->current_state == 0) {
+		up(&idev->sem);
+		IRDA_WARNING("%s - %s: already resumed\n",
+			     __FUNCTION__, PCIDEV_NAME(pdev));
+		return 0;
+	}
+	
+	pci_set_power_state(pdev, 0);
+	pdev->current_state = 0;
+
+	if (!idev->resume_ok) {
+		/* should be obsolete now - but used to happen due to:
+		 * - pci layer initially setting pdev->current_state = 4 (unknown)
+		 * - pci layer did not walk the save_state-tree (might be APM problem)
+		 *   so we could not refuse to suspend from undefined state
+		 * - vlsi_irda_suspend detected invalid state and refused to save
+		 *   configuration for resume - but was too late to stop suspending
+		 * - vlsi_irda_resume got screwed when trying to resume from garbage
+		 *
+		 * now we explicitly set pdev->current_state = 0 after enabling the
+		 * device and independently resume_ok should catch any garbage config.
+		 */
+		IRDA_WARNING("%s - hm, nothing to resume?\n", __FUNCTION__);
+		up(&idev->sem);
+		return 0;
+	}
+
+	if (netif_running(ndev)) {
+		pci_restore_state(pdev);
+		vlsi_start_hw(idev);
+		netif_device_attach(ndev);
+	}
+	idev->resume_ok = 0;
+	up(&idev->sem);
+	return 0;
+}
+
+#endif /* CONFIG_PM */
+
+/*********************************************************/
+
+static struct pci_driver vlsi_irda_driver = {
+	.name		= drivername,
+	.id_table	= vlsi_irda_table,
+	.probe		= vlsi_irda_probe,
+	.remove		= __devexit_p(vlsi_irda_remove),
+#ifdef CONFIG_PM
+	.suspend	= vlsi_irda_suspend,
+	.resume		= vlsi_irda_resume,
+#endif
+};
+
+#define PROC_DIR ("driver/" DRIVER_NAME)
+
+static int __init vlsi_mod_init(void)
+{
+	int	i, ret;
+
+	if (clksrc < 0  ||  clksrc > 3) {
+		IRDA_ERROR("%s: invalid clksrc=%d\n", drivername, clksrc);
+		return -1;
+	}
+
+	for (i = 0; i < 2; i++) {
+		switch(ringsize[i]) {
+			case 4:
+			case 8:
+			case 16:
+			case 32:
+			case 64:
+				break;
+			default:
+				IRDA_WARNING("%s: invalid %s ringsize %d, using default=8", drivername, (i)?"rx":"tx", ringsize[i]);
+				ringsize[i] = 8;
+				break;
+		}
+	} 
+
+	sirpulse = !!sirpulse;
+
+	/* create_proc_entry returns NULL if !CONFIG_PROC_FS.
+	 * Failure to create the procfs entry is handled like running
+	 * without procfs - it's not required for the driver to work.
+	 */
+	vlsi_proc_root = create_proc_entry(PROC_DIR, S_IFDIR, NULL);
+	if (vlsi_proc_root) {
+		/* protect registered procdir against module removal.
+		 * Because we are in the module init path there's no race
+		 * window after create_proc_entry (and no barrier needed).
+		 */
+		vlsi_proc_root->owner = THIS_MODULE;
+	}
+
+	ret = pci_module_init(&vlsi_irda_driver);
+
+	if (ret && vlsi_proc_root)
+		remove_proc_entry(PROC_DIR, NULL);
+	return ret;
+
+}
+
+static void __exit vlsi_mod_exit(void)
+{
+	pci_unregister_driver(&vlsi_irda_driver);
+	if (vlsi_proc_root)
+		remove_proc_entry(PROC_DIR, NULL);
+}
+
+module_init(vlsi_mod_init);
+module_exit(vlsi_mod_exit);
diff --git a/drivers/net/irda/vlsi_ir.h b/drivers/net/irda/vlsi_ir.h
new file mode 100644
index 0000000..414694a
--- /dev/null
+++ b/drivers/net/irda/vlsi_ir.h
@@ -0,0 +1,798 @@
+
+/*********************************************************************
+ *
+ *	vlsi_ir.h:	VLSI82C147 PCI IrDA controller driver for Linux
+ *
+ *	Version:	0.5
+ *
+ *	Copyright (c) 2001-2003 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.
+ *
+ *	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
+ *
+ ********************************************************************/
+
+#ifndef IRDA_VLSI_FIR_H
+#define IRDA_VLSI_FIR_H
+
+/* ================================================================
+ * compatibility stuff
+ */
+
+/* definitions not present in pci_ids.h */
+
+#ifndef PCI_CLASS_WIRELESS_IRDA
+#define PCI_CLASS_WIRELESS_IRDA		0x0d00
+#endif
+
+#ifndef PCI_CLASS_SUBCLASS_MASK
+#define PCI_CLASS_SUBCLASS_MASK		0xffff
+#endif
+
+/* in recent 2.5 interrupt handlers have non-void return value */
+#ifndef IRQ_RETVAL
+typedef void irqreturn_t;
+#define IRQ_NONE
+#define IRQ_HANDLED
+#define IRQ_RETVAL(x)
+#endif
+
+/* some stuff need to check kernelversion. Not all 2.5 stuff was present
+ * in early 2.5.x - the test is merely to separate 2.4 from 2.5
+ */
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+
+/* PDE() introduced in 2.5.4 */
+#ifdef CONFIG_PROC_FS
+#define PDE(inode) ((inode)->u.generic_ip)
+#endif
+
+/* irda crc16 calculation exported in 2.5.42 */
+#define irda_calc_crc16(fcs,buf,len)	(GOOD_FCS)
+
+/* we use this for unified pci device name access */
+#define PCIDEV_NAME(pdev)	((pdev)->name)
+
+#else /* 2.5 or later */
+
+/* recent 2.5/2.6 stores pci device names at varying places ;-) */
+#ifdef CONFIG_PCI_NAMES
+/* human readable name */
+#define PCIDEV_NAME(pdev)	((pdev)->pretty_name)
+#else
+/* whatever we get from the associated struct device - bus:slot:dev.fn id */
+#define PCIDEV_NAME(pdev)	(pci_name(pdev))
+#endif
+
+#endif
+
+/* ================================================================ */
+
+/* non-standard PCI registers */
+
+enum vlsi_pci_regs {
+	VLSI_PCI_CLKCTL		= 0x40,		/* chip clock input control */
+	VLSI_PCI_MSTRPAGE	= 0x41,		/* addr [31:24] for all busmaster cycles */
+	VLSI_PCI_IRMISC		= 0x42		/* mainly legacy UART related */
+};
+
+/* ------------------------------------------ */
+
+/* VLSI_PCI_CLKCTL: Clock Control Register (u8, rw) */
+
+/* Three possible clock sources: either on-chip 48MHz PLL or
+ * external clock applied to EXTCLK pin. External clock may
+ * be either 48MHz or 40MHz, which is indicated by XCKSEL.
+ * CLKSTP controls whether the selected clock source gets
+ * connected to the IrDA block.
+ *
+ * On my HP OB-800 the BIOS sets external 40MHz clock as source
+ * when IrDA enabled and I've never detected any PLL lock success.
+ * Apparently the 14.3...MHz OSC input required for the PLL to work
+ * is not connected and the 40MHz EXTCLK is provided externally.
+ * At least this is what makes the driver working for me.
+ */
+
+enum vlsi_pci_clkctl {
+
+	/* PLL control */
+
+	CLKCTL_PD_INV		= 0x04,		/* PD#: inverted power down signal,
+						 * i.e. PLL is powered, if PD_INV set */
+	CLKCTL_LOCK		= 0x40,		/* (ro) set, if PLL is locked */
+
+	/* clock source selection */
+
+	CLKCTL_EXTCLK		= 0x20,		/* set to select external clock input, not PLL */
+	CLKCTL_XCKSEL		= 0x10,		/* set to indicate EXTCLK is 40MHz, not 48MHz */
+
+	/* IrDA block control */
+
+	CLKCTL_CLKSTP		= 0x80,		/* set to disconnect from selected clock source */
+	CLKCTL_WAKE		= 0x08		/* set to enable wakeup feature: whenever IR activity
+						 * is detected, PD_INV gets set(?) and CLKSTP cleared */
+};
+
+/* ------------------------------------------ */
+
+/* VLSI_PCI_MSTRPAGE: Master Page Register (u8, rw) and busmastering stuff */
+
+#define DMA_MASK_USED_BY_HW	0xffffffff
+#define DMA_MASK_MSTRPAGE	0x00ffffff
+#define MSTRPAGE_VALUE		(DMA_MASK_MSTRPAGE >> 24)
+
+	/* PCI busmastering is somewhat special for this guy - in short:
+	 *
+	 * We select to operate using fixed MSTRPAGE=0, use ISA DMA
+	 * address restrictions to make the PCI BM api aware of this,
+	 * but ensure the hardware is dealing with real 32bit access.
+	 *
+	 * In detail:
+	 * The chip executes normal 32bit busmaster cycles, i.e.
+	 * drives all 32 address lines. These addresses however are
+	 * composed of [0:23] taken from various busaddr-pointers
+	 * and [24:31] taken from the MSTRPAGE register in the VLSI82C147
+	 * config space. Therefore _all_ busmastering must be
+	 * targeted to/from one single 16MB (busaddr-) superpage!
+	 * The point is to make sure all the allocations for memory
+	 * locations with busmaster access (ring descriptors, buffers)
+	 * are indeed bus-mappable to the same 16MB range (for x86 this
+	 * means they must reside in the same 16MB physical memory address
+	 * range). The only constraint we have which supports "several objects
+	 * mappable to common 16MB range" paradigma, is the old ISA DMA
+	 * restriction to the first 16MB of physical address range.
+	 * Hence the approach here is to enable PCI busmaster support using
+	 * the correct 32bit dma-mask used by the chip. Afterwards the device's
+	 * dma-mask gets restricted to 24bit, which must be honoured somehow by
+	 * all allocations for memory areas to be exposed to the chip ...
+	 *
+	 * Note:
+	 * Don't be surprised to get "Setting latency timer..." messages every
+	 * time when PCI busmastering is enabled for the chip.
+	 * The chip has its PCI latency timer RO fixed at 0 - which is not a
+	 * problem here, because it is never requesting _burst_ transactions.
+	 */
+
+/* ------------------------------------------ */
+
+/* VLSI_PCIIRMISC: IR Miscellaneous Register (u8, rw) */
+
+/* legacy UART emulation - not used by this driver - would require:
+ * (see below for some register-value definitions)
+ *
+ *	- IRMISC_UARTEN must be set to enable UART address decoding
+ *	- IRMISC_UARTSEL configured
+ *	- IRCFG_MASTER must be cleared
+ *	- IRCFG_SIR must be set
+ *	- IRENABLE_PHYANDCLOCK must be asserted 0->1 (and hence IRENABLE_SIR_ON)
+ */
+
+enum vlsi_pci_irmisc {
+
+	/* IR transceiver control */
+
+	IRMISC_IRRAIL		= 0x40,		/* (ro?) IR rail power indication (and control?)
+						 * 0=3.3V / 1=5V. Probably set during power-on?
+						 * unclear - not touched by driver */
+	IRMISC_IRPD		= 0x08,		/* transceiver power down, if set */
+
+	/* legacy UART control */
+
+	IRMISC_UARTTST		= 0x80,		/* UART test mode - "always write 0" */
+	IRMISC_UARTEN		= 0x04,		/* enable UART address decoding */
+
+	/* bits [1:0] IRMISC_UARTSEL to select legacy UART address */
+
+	IRMISC_UARTSEL_3f8	= 0x00,
+	IRMISC_UARTSEL_2f8	= 0x01,
+	IRMISC_UARTSEL_3e8	= 0x02,
+	IRMISC_UARTSEL_2e8	= 0x03
+};
+
+/* ================================================================ */
+
+/* registers mapped to 32 byte PCI IO space */
+
+/* note: better access all registers at the indicated u8/u16 size
+ *	 although some of them contain only 1 byte of information.
+ *	 some of them (particaluarly PROMPT and IRCFG) ignore
+ *	 access when using the wrong addressing mode!
+ */
+
+enum vlsi_pio_regs {
+	VLSI_PIO_IRINTR		= 0x00,		/* interrupt enable/request (u8, rw) */
+	VLSI_PIO_RINGPTR	= 0x02,		/* rx/tx ring pointer (u16, ro) */
+	VLSI_PIO_RINGBASE	= 0x04,		/* [23:10] of ring address (u16, rw) */
+	VLSI_PIO_RINGSIZE	= 0x06,		/* rx/tx ring size (u16, rw) */
+	VLSI_PIO_PROMPT		= 0x08, 	/* triggers ring processing (u16, wo) */
+	/* 0x0a-0x0f: reserved / duplicated UART regs */
+	VLSI_PIO_IRCFG		= 0x10,		/* configuration select (u16, rw) */
+	VLSI_PIO_SIRFLAG	= 0x12,		/* BOF/EOF for filtered SIR (u16, ro) */
+	VLSI_PIO_IRENABLE	= 0x14,		/* enable and status register (u16, rw/ro) */
+	VLSI_PIO_PHYCTL		= 0x16,		/* physical layer current status (u16, ro) */
+	VLSI_PIO_NPHYCTL	= 0x18,		/* next physical layer select (u16, rw) */
+	VLSI_PIO_MAXPKT		= 0x1a,		/* [11:0] max len for packet receive (u16, rw) */
+	VLSI_PIO_RCVBCNT	= 0x1c		/* current receive-FIFO byte count (u16, ro) */
+	/* 0x1e-0x1f: reserved / duplicated UART regs */
+};
+
+/* ------------------------------------------ */
+
+/* VLSI_PIO_IRINTR: Interrupt Register (u8, rw) */
+
+/* enable-bits:
+ *		1 = enable / 0 = disable
+ * interrupt condition bits:
+ * 		set according to corresponding interrupt source
+ *		(regardless of the state of the enable bits)
+ *		enable bit status indicates whether interrupt gets raised
+ *		write-to-clear
+ * note: RPKTINT and TPKTINT behave different in legacy UART mode (which we don't use :-)
+ */
+
+enum vlsi_pio_irintr {
+	IRINTR_ACTEN	= 0x80,	/* activity interrupt enable */
+	IRINTR_ACTIVITY	= 0x40,	/* activity monitor (traffic detected) */
+	IRINTR_RPKTEN	= 0x20,	/* receive packet interrupt enable*/
+	IRINTR_RPKTINT	= 0x10,	/* rx-packet transfered from fifo to memory finished */
+	IRINTR_TPKTEN	= 0x08,	/* transmit packet interrupt enable */
+	IRINTR_TPKTINT	= 0x04,	/* last bit of tx-packet+crc shifted to ir-pulser */
+	IRINTR_OE_EN	= 0x02,	/* UART rx fifo overrun error interrupt enable */
+	IRINTR_OE_INT	= 0x01	/* UART rx fifo overrun error (read LSR to clear) */
+};
+
+/* we use this mask to check whether the (shared PCI) interrupt is ours */
+
+#define IRINTR_INT_MASK		(IRINTR_ACTIVITY|IRINTR_RPKTINT|IRINTR_TPKTINT)
+
+/* ------------------------------------------ */
+
+/* VLSI_PIO_RINGPTR: Ring Pointer Read-Back Register (u16, ro) */
+
+/* _both_ ring pointers are indices relative to the _entire_ rx,tx-ring!
+ * i.e. the referenced descriptor is located
+ * at RINGBASE + PTR * sizeof(descr) for rx and tx
+ * therefore, the tx-pointer has offset MAX_RING_DESCR
+ */
+
+#define MAX_RING_DESCR		64	/* tx, rx rings may contain up to 64 descr each */
+
+#define RINGPTR_RX_MASK		(MAX_RING_DESCR-1)
+#define RINGPTR_TX_MASK		((MAX_RING_DESCR-1)<<8)
+
+#define RINGPTR_GET_RX(p)	((p)&RINGPTR_RX_MASK)
+#define RINGPTR_GET_TX(p)	(((p)&RINGPTR_TX_MASK)>>8)
+
+/* ------------------------------------------ */
+
+/* VLSI_PIO_RINGBASE: Ring Pointer Base Address Register (u16, ro) */
+
+/* Contains [23:10] part of the ring base (bus-) address
+ * which must be 1k-alinged. [31:24] is taken from
+ * VLSI_PCI_MSTRPAGE above.
+ * The controller initiates non-burst PCI BM cycles to
+ * fetch and update the descriptors in the ring.
+ * Once fetched, the descriptor remains cached onchip
+ * until it gets closed and updated due to the ring
+ * processing state machine.
+ * The entire ring area is split in rx and tx areas with each
+ * area consisting of 64 descriptors of 8 bytes each.
+ * The rx(tx) ring is located at ringbase+0 (ringbase+64*8).
+ */
+
+#define BUS_TO_RINGBASE(p)	(((p)>>10)&0x3fff)
+
+/* ------------------------------------------ */
+
+/* VLSI_PIO_RINGSIZE: Ring Size Register (u16, rw) */
+
+/* bit mask to indicate the ring size to be used for rx and tx.
+ * 	possible values		encoded bits
+ *		 4		   0000
+ *		 8		   0001
+ *		16		   0011
+ *		32		   0111
+ *		64		   1111
+ * located at [15:12] for tx and [11:8] for rx ([7:0] unused)
+ *
+ * note: probably a good idea to have IRCFG_MSTR cleared when writing
+ *	 this so the state machines are stopped and the RINGPTR is reset!
+ */
+
+#define SIZE_TO_BITS(num)		((((num)-1)>>2)&0x0f)
+#define TX_RX_TO_RINGSIZE(tx,rx)	((SIZE_TO_BITS(tx)<<12)|(SIZE_TO_BITS(rx)<<8))
+#define RINGSIZE_TO_RXSIZE(rs)		((((rs)&0x0f00)>>6)+4)
+#define RINGSIZE_TO_TXSIZE(rs)		((((rs)&0xf000)>>10)+4)
+
+
+/* ------------------------------------------ */
+
+/* VLSI_PIO_PROMPT: Ring Prompting Register (u16, write-to-start) */
+
+/* writing any value kicks the ring processing state machines
+ * for both tx, rx rings as follows:
+ * 	- active rings (currently owning an active descriptor)
+ *	  ignore the prompt and continue
+ *	- idle rings fetch the next descr from the ring and start
+ *	  their processing
+ */
+
+/* ------------------------------------------ */
+
+/* VLSI_PIO_IRCFG: IR Config Register (u16, rw) */
+
+/* notes:
+ *	- not more than one SIR/MIR/FIR bit must be set at any time
+ *	- SIR, MIR, FIR and CRC16 select the configuration which will
+ *	  be applied on next 0->1 transition of IRENABLE_PHYANDCLOCK (see below).
+ *	- besides allowing the PCI interface to execute busmaster cycles
+ *	  and therefore the ring SM to operate, the MSTR bit has side-effects:
+ *	  when MSTR is cleared, the RINGPTR's get reset and the legacy UART mode
+ *	  (in contrast to busmaster access mode) gets enabled.
+ *	- clearing ENRX or setting ENTX while data is received may stall the
+ *	  receive fifo until ENRX reenabled _and_ another packet arrives
+ *	- SIRFILT means the chip performs the required unwrapping of hardware
+ *	  headers (XBOF's, BOF/EOF) and un-escaping in the _receive_ direction.
+ *	  Only the resulting IrLAP payload is copied to the receive buffers -
+ *	  but with the 16bit FCS still encluded. Question remains, whether it
+ *	  was already checked or we should do it before passing the packet to IrLAP?
+ */
+
+enum vlsi_pio_ircfg {
+	IRCFG_LOOP	= 0x4000,	/* enable loopback test mode */
+	IRCFG_ENTX	= 0x1000,	/* transmit enable */
+	IRCFG_ENRX	= 0x0800,	/* receive enable */
+	IRCFG_MSTR	= 0x0400,	/* master enable */
+	IRCFG_RXANY	= 0x0200,	/* receive any packet */
+	IRCFG_CRC16	= 0x0080,	/* 16bit (not 32bit) CRC select for MIR/FIR */
+	IRCFG_FIR	= 0x0040,	/* FIR 4PPM encoding mode enable */
+	IRCFG_MIR	= 0x0020,	/* MIR HDLC encoding mode enable */
+	IRCFG_SIR	= 0x0010,	/* SIR encoding mode enable */
+	IRCFG_SIRFILT	= 0x0008,	/* enable SIR decode filter (receiver unwrapping) */
+	IRCFG_SIRTEST	= 0x0004,	/* allow SIR decode filter when not in SIR mode */
+	IRCFG_TXPOL	= 0x0002,	/* invert tx polarity when set */
+	IRCFG_RXPOL	= 0x0001	/* invert rx polarity when set */
+};
+
+/* ------------------------------------------ */
+
+/* VLSI_PIO_SIRFLAG: SIR Flag Register (u16, ro) */
+
+/* register contains hardcoded BOF=0xc0 at [7:0] and EOF=0xc1 at [15:8]
+ * which is used for unwrapping received frames in SIR decode-filter mode
+ */
+
+/* ------------------------------------------ */
+
+/* VLSI_PIO_IRENABLE: IR Enable Register (u16, rw/ro) */
+
+/* notes:
+ *	- IREN acts as gate for latching the configured IR mode information
+ *	  from IRCFG and IRPHYCTL when IREN=reset and applying them when
+ *	  IREN gets set afterwards.
+ *	- ENTXST reflects IRCFG_ENTX
+ *	- ENRXST = IRCFG_ENRX && (!IRCFG_ENTX || IRCFG_LOOP)
+ */
+
+enum vlsi_pio_irenable {
+	IRENABLE_PHYANDCLOCK	= 0x8000,  /* enable IR phy and gate the mode config (rw) */
+	IRENABLE_CFGER		= 0x4000,  /* mode configuration error (ro) */
+	IRENABLE_FIR_ON		= 0x2000,  /* FIR on status (ro) */
+	IRENABLE_MIR_ON		= 0x1000,  /* MIR on status (ro) */
+	IRENABLE_SIR_ON		= 0x0800,  /* SIR on status (ro) */
+	IRENABLE_ENTXST		= 0x0400,  /* transmit enable status (ro) */
+	IRENABLE_ENRXST		= 0x0200,  /* Receive enable status (ro) */
+	IRENABLE_CRC16_ON	= 0x0100   /* 16bit (not 32bit) CRC enabled status (ro) */
+};
+
+#define	  IRENABLE_MASK	    0xff00  /* Read mask */
+
+/* ------------------------------------------ */
+
+/* VLSI_PIO_PHYCTL: IR Physical Layer Current Control Register (u16, ro) */
+
+/* read-back of the currently applied physical layer status.
+ * applied from VLSI_PIO_NPHYCTL at rising edge of IRENABLE_PHYANDCLOCK
+ * contents identical to VLSI_PIO_NPHYCTL (see below)
+ */
+
+/* ------------------------------------------ */
+
+/* VLSI_PIO_NPHYCTL: IR Physical Layer Next Control Register (u16, rw) */
+
+/* latched during IRENABLE_PHYANDCLOCK=0 and applied at 0-1 transition
+ *
+ * consists of BAUD[15:10], PLSWID[9:5] and PREAMB[4:0] bits defined as follows:
+ *
+ * SIR-mode:	BAUD = (115.2kHz / baudrate) - 1
+ *		PLSWID = (pulsetime * freq / (BAUD+1)) - 1
+ *			where pulsetime is the requested IrPHY pulse width
+ *			and freq is 8(16)MHz for 40(48)MHz primary input clock
+ *		PREAMB: don't care for SIR
+ *
+ *		The nominal SIR pulse width is 3/16 bit time so we have PLSWID=12
+ *		fixed for all SIR speeds at 40MHz input clock (PLSWID=24 at 48MHz).
+ *		IrPHY also allows shorter pulses down to the nominal pulse duration
+ *		at 115.2kbaud (minus some tolerance) which is 1.41 usec.
+ *		Using the expression PLSWID = 12/(BAUD+1)-1 (multiplied by two for 48MHz)
+ *		we get the minimum acceptable PLSWID values according to the VLSI
+ *		specification, which provides 1.5 usec pulse width for all speeds (except
+ *		for 2.4kbaud getting 6usec). This is fine with IrPHY v1.3 specs and
+ *		reduces the transceiver power which drains the battery. At 9.6kbaud for
+ *		example this amounts to more than 90% battery power saving!
+ *
+ * MIR-mode:	BAUD = 0
+ *		PLSWID = 9(10) for 40(48) MHz input clock
+ *			to get nominal MIR pulse width
+ *		PREAMB = 1
+ *
+ * FIR-mode:	BAUD = 0
+ *		PLSWID: don't care
+ *		PREAMB = 15
+ */
+
+#define PHYCTL_BAUD_SHIFT	10
+#define PHYCTL_BAUD_MASK	0xfc00
+#define PHYCTL_PLSWID_SHIFT	5
+#define PHYCTL_PLSWID_MASK	0x03e0
+#define PHYCTL_PREAMB_SHIFT	0
+#define PHYCTL_PREAMB_MASK	0x001f
+
+#define PHYCTL_TO_BAUD(bwp)	(((bwp)&PHYCTL_BAUD_MASK)>>PHYCTL_BAUD_SHIFT)
+#define PHYCTL_TO_PLSWID(bwp)	(((bwp)&PHYCTL_PLSWID_MASK)>>PHYCTL_PLSWID_SHIFT)
+#define PHYCTL_TO_PREAMB(bwp)	(((bwp)&PHYCTL_PREAMB_MASK)>>PHYCTL_PREAMB_SHIFT)
+
+#define BWP_TO_PHYCTL(b,w,p)	((((b)<<PHYCTL_BAUD_SHIFT)&PHYCTL_BAUD_MASK) \
+				 | (((w)<<PHYCTL_PLSWID_SHIFT)&PHYCTL_PLSWID_MASK) \
+				 | (((p)<<PHYCTL_PREAMB_SHIFT)&PHYCTL_PREAMB_MASK))
+
+#define BAUD_BITS(br)		((115200/(br))-1)
+
+static inline unsigned
+calc_width_bits(unsigned baudrate, unsigned widthselect, unsigned clockselect)
+{
+	unsigned	tmp;
+
+	if (widthselect)	/* nominal 3/16 puls width */
+		return (clockselect) ? 12 : 24;
+
+	tmp = ((clockselect) ? 12 : 24) / (BAUD_BITS(baudrate)+1);
+
+	/* intermediate result of integer division needed here */
+
+	return (tmp>0) ? (tmp-1) : 0;
+}
+
+#define PHYCTL_SIR(br,ws,cs)	BWP_TO_PHYCTL(BAUD_BITS(br),calc_width_bits((br),(ws),(cs)),0)
+#define PHYCTL_MIR(cs)		BWP_TO_PHYCTL(0,((cs)?9:10),1)
+#define PHYCTL_FIR		BWP_TO_PHYCTL(0,0,15)
+
+/* quite ugly, I know. But implementing these calculations here avoids
+ * having magic numbers in the code and allows some playing with pulsewidths
+ * without risk to violate the standards.
+ * FWIW, here is the table for reference:
+ *
+ * baudrate	BAUD	min-PLSWID	nom-PLSWID	PREAMB
+ *     2400	  47	   0(0)		   12(24)	   0
+ *     9600	  11	   0(0)		   12(24)	   0
+ *    19200	   5	   1(2)		   12(24)	   0
+ *    38400	   2	   3(6)	           12(24)	   0
+ *    57600	   1	   5(10)	   12(24)	   0
+ *   115200	   0	  11(22)	   12(24)	   0
+ *	MIR	   0	    -		    9(10)	   1
+ *	FIR	   0        -               0		  15
+ *
+ * note: x(y) means x-value for 40MHz / y-value for 48MHz primary input clock
+ */
+
+/* ------------------------------------------ */
+
+
+/* VLSI_PIO_MAXPKT: Maximum Packet Length register (u16, rw) */
+
+/* maximum acceptable length for received packets */
+
+/* hw imposed limitation - register uses only [11:0] */
+#define MAX_PACKET_LENGTH	0x0fff
+
+/* IrLAP I-field (apparently not defined elsewhere) */
+#define IRDA_MTU		2048
+
+/* complete packet consists of A(1)+C(1)+I(<=IRDA_MTU) */
+#define IRLAP_SKB_ALLOCSIZE	(1+1+IRDA_MTU)
+
+/* the buffers we use to exchange frames with the hardware need to be
+ * larger than IRLAP_SKB_ALLOCSIZE because we may have up to 4 bytes FCS
+ * appended and, in SIR mode, a lot of frame wrapping bytes. The worst
+ * case appears to be a SIR packet with I-size==IRDA_MTU and all bytes
+ * requiring to be escaped to provide transparency. Furthermore, the peer
+ * might ask for quite a number of additional XBOFs:
+ *	up to 115+48 XBOFS		 163
+ *	regular BOF			   1
+ *	A-field				   1
+ *	C-field				   1
+ *	I-field, IRDA_MTU, all escaped	4096
+ *	FCS (16 bit at SIR, escaped)	   4
+ *	EOF				   1
+ * AFAICS nothing in IrLAP guarantees A/C field not to need escaping
+ * (f.e. 0xc0/0xc1 - i.e. BOF/EOF - are legal values there) so in the
+ * worst case we have 4269 bytes total frame size.
+ * However, the VLSI uses 12 bits only for all buffer length values,
+ * which limits the maximum useable buffer size <= 4095.
+ * Note this is not a limitation in the receive case because we use
+ * the SIR filtering mode where the hw unwraps the frame and only the
+ * bare packet+fcs is stored into the buffer - in contrast to the SIR
+ * tx case where we have to pass frame-wrapped packets to the hw.
+ * If this would ever become an issue in real life, the only workaround
+ * I see would be using the legacy UART emulation in SIR mode.
+ */
+
+#define XFER_BUF_SIZE		MAX_PACKET_LENGTH
+
+/* ------------------------------------------ */
+
+/* VLSI_PIO_RCVBCNT: Receive Byte Count Register (u16, ro) */
+
+/* receive packet counter gets incremented on every non-filtered
+ * byte which was put in the receive fifo and reset for each
+ * new packet. Used to decide whether we are just in the middle
+ * of receiving
+ */
+
+/* better apply the [11:0] mask when reading, as some docs say the
+ * reserved [15:12] would return 1 when reading - which is wrong AFAICS
+ */
+#define RCVBCNT_MASK	0x0fff
+
+/******************************************************************/
+
+/* descriptors for rx/tx ring
+ *
+ * accessed by hardware - don't change!
+ *
+ * the descriptor is owned by hardware, when the ACTIVE status bit
+ * is set and nothing (besides reading status to test the bit)
+ * shall be done. The bit gets cleared by hw, when the descriptor
+ * gets closed. Premature reaping of descriptors owned be the chip
+ * can be achieved by disabling IRCFG_MSTR
+ *
+ * Attention: Writing addr overwrites status!
+ *
+ * ### FIXME: depends on endianess (but there ain't no non-i586 ob800 ;-)
+ */
+
+struct ring_descr_hw {
+	volatile u16	rd_count;	/* tx/rx count [11:0] */
+	u16		reserved;
+	union {
+		u32	addr;		/* [23:0] of the buffer's busaddress */
+		struct {
+			u8		addr_res[3];
+			volatile u8	status;		/* descriptor status */
+		} rd_s __attribute__((packed));
+	} rd_u __attribute((packed));
+} __attribute__ ((packed));
+
+#define rd_addr		rd_u.addr
+#define rd_status	rd_u.rd_s.status
+
+/* ring descriptor status bits */
+
+#define RD_ACTIVE		0x80	/* descriptor owned by hw (both TX,RX) */
+
+/* TX ring descriptor status */
+
+#define	RD_TX_DISCRC		0x40	/* do not send CRC (for SIR) */
+#define	RD_TX_BADCRC		0x20	/* force a bad CRC */
+#define	RD_TX_PULSE		0x10	/* send indication pulse after this frame (MIR/FIR) */
+#define	RD_TX_FRCEUND		0x08	/* force underrun */
+#define	RD_TX_CLRENTX		0x04	/* clear ENTX after this frame */
+#define	RD_TX_UNDRN		0x01	/* TX fifo underrun (probably PCI problem) */
+
+/* RX ring descriptor status */
+
+#define RD_RX_PHYERR		0x40	/* physical encoding error */
+#define RD_RX_CRCERR		0x20	/* CRC error (MIR/FIR) */
+#define RD_RX_LENGTH		0x10	/* frame exceeds buffer length */
+#define RD_RX_OVER		0x08	/* RX fifo overrun (probably PCI problem) */
+#define RD_RX_SIRBAD		0x04	/* EOF missing: BOF follows BOF (SIR, filtered) */
+
+#define RD_RX_ERROR		0x7c	/* any error in received frame */
+
+/* the memory required to hold the 2 descriptor rings */
+#define HW_RING_AREA_SIZE	(2 * MAX_RING_DESCR * sizeof(struct ring_descr_hw))
+
+/******************************************************************/
+
+/* sw-ring descriptors consists of a bus-mapped transfer buffer with
+ * associated skb and a pointer to the hw entry descriptor
+ */
+
+struct ring_descr {
+	struct ring_descr_hw	*hw;
+	struct sk_buff		*skb;
+	void			*buf;
+};
+
+/* wrappers for operations on hw-exposed ring descriptors
+ * access to the hw-part of the descriptors must use these.
+ */
+
+static inline int rd_is_active(struct ring_descr *rd)
+{
+	return ((rd->hw->rd_status & RD_ACTIVE) != 0);
+}
+
+static inline void rd_activate(struct ring_descr *rd)
+{
+	rd->hw->rd_status |= RD_ACTIVE;
+}
+
+static inline void rd_set_status(struct ring_descr *rd, u8 s)
+{
+	rd->hw->rd_status = s;	 /* may pass ownership to the hardware */
+}
+
+static inline void rd_set_addr_status(struct ring_descr *rd, dma_addr_t a, u8 s)
+{
+	/* order is important for two reasons:
+	 *  - overlayed: writing addr overwrites status
+	 *  - we want to write status last so we have valid address in
+	 *    case status has RD_ACTIVE set
+	 */
+
+	if ((a & ~DMA_MASK_MSTRPAGE)>>24 != MSTRPAGE_VALUE) {
+		IRDA_ERROR("%s: pci busaddr inconsistency!\n", __FUNCTION__);
+		dump_stack();
+		return;
+	}
+
+	a &= DMA_MASK_MSTRPAGE;  /* clear highbyte to make sure we won't write
+				  * to status - just in case MSTRPAGE_VALUE!=0
+				  */
+	rd->hw->rd_addr = cpu_to_le32(a);
+	wmb();
+	rd_set_status(rd, s);	 /* may pass ownership to the hardware */
+}
+
+static inline void rd_set_count(struct ring_descr *rd, u16 c)
+{
+	rd->hw->rd_count = cpu_to_le16(c);
+}
+
+static inline u8 rd_get_status(struct ring_descr *rd)
+{
+	return rd->hw->rd_status;
+}
+
+static inline dma_addr_t rd_get_addr(struct ring_descr *rd)
+{
+	dma_addr_t	a;
+
+	a = le32_to_cpu(rd->hw->rd_addr);
+	return (a & DMA_MASK_MSTRPAGE) | (MSTRPAGE_VALUE << 24);
+}
+
+static inline u16 rd_get_count(struct ring_descr *rd)
+{
+	return le16_to_cpu(rd->hw->rd_count);
+}
+
+/******************************************************************/
+
+/* sw descriptor rings for rx, tx:
+ *
+ * operations follow producer-consumer paradigm, with the hw
+ * in the middle doing the processing.
+ * ring size must be power of two.
+ *
+ * producer advances r->tail after inserting for processing
+ * consumer advances r->head after removing processed rd
+ * ring is empty if head==tail / full if (tail+1)==head
+ */
+
+struct vlsi_ring {
+	struct pci_dev		*pdev;
+	int			dir;
+	unsigned		len;
+	unsigned		size;
+	unsigned		mask;
+	atomic_t		head, tail;
+	struct ring_descr	*rd;
+};
+
+/* ring processing helpers */
+
+static inline struct ring_descr *ring_last(struct vlsi_ring *r)
+{
+	int t;
+
+	t = atomic_read(&r->tail) & r->mask;
+	return (((t+1) & r->mask) == (atomic_read(&r->head) & r->mask)) ? NULL : &r->rd[t];
+}
+
+static inline struct ring_descr *ring_put(struct vlsi_ring *r)
+{
+	atomic_inc(&r->tail);
+	return ring_last(r);
+}
+
+static inline struct ring_descr *ring_first(struct vlsi_ring *r)
+{
+	int h;
+
+	h = atomic_read(&r->head) & r->mask;
+	return (h == (atomic_read(&r->tail) & r->mask)) ? NULL : &r->rd[h];
+}
+
+static inline struct ring_descr *ring_get(struct vlsi_ring *r)
+{
+	atomic_inc(&r->head);
+	return ring_first(r);
+}
+
+/******************************************************************/
+
+/* our private compound VLSI-PCI-IRDA device information */
+
+typedef struct vlsi_irda_dev {
+	struct pci_dev		*pdev;
+	struct net_device_stats	stats;
+
+	struct irlap_cb		*irlap;
+
+	struct qos_info		qos;
+
+	unsigned		mode;
+	int			baud, new_baud;
+
+	dma_addr_t		busaddr;
+	void			*virtaddr;
+	struct vlsi_ring	*tx_ring, *rx_ring;
+
+	struct timeval		last_rx;
+
+	spinlock_t		lock;
+	struct semaphore	sem;
+
+	u8			resume_ok;	
+	struct proc_dir_entry	*proc_entry;
+
+} vlsi_irda_dev_t;
+
+/********************************************************/
+
+/* the remapped error flags we use for returning from frame
+ * post-processing in vlsi_process_tx/rx() after it was completed
+ * by the hardware. These functions either return the >=0 number
+ * of transfered bytes in case of success or the negative (-)
+ * of the or'ed error flags.
+ */
+
+#define VLSI_TX_DROP		0x0001
+#define VLSI_TX_FIFO		0x0002
+
+#define VLSI_RX_DROP		0x0100
+#define VLSI_RX_OVER		0x0200
+#define VLSI_RX_LENGTH  	0x0400
+#define VLSI_RX_FRAME		0x0800
+#define VLSI_RX_CRC		0x1000
+
+/********************************************************/
+
+#endif /* IRDA_VLSI_FIR_H */
+
diff --git a/drivers/net/irda/w83977af.h b/drivers/net/irda/w83977af.h
new file mode 100644
index 0000000..04476c2e
--- /dev/null
+++ b/drivers/net/irda/w83977af.h
@@ -0,0 +1,53 @@
+#ifndef W83977AF_H
+#define W83977AF_H
+
+#define W977_EFIO_BASE 0x370
+#define W977_EFIO2_BASE 0x3f0
+#define W977_DEVICE_IR 0x06
+
+
+/*
+ * Enter extended function mode
+ */
+static inline void w977_efm_enter(unsigned int efio)
+{
+        outb(0x87, efio);
+        outb(0x87, efio);
+}
+
+/*
+ * Select a device to configure 
+ */
+
+static inline void w977_select_device(__u8 devnum, unsigned int efio)
+{
+	outb(0x07, efio);
+	outb(devnum, efio+1);
+} 
+
+/* 
+ * Write a byte to a register
+ */
+static inline void w977_write_reg(__u8 reg, __u8 value, unsigned int efio)
+{
+	outb(reg, efio);
+	outb(value, efio+1);
+}
+
+/*
+ * read a byte from a register
+ */
+static inline __u8 w977_read_reg(__u8 reg, unsigned int efio)
+{
+	outb(reg, efio);
+	return inb(efio+1);
+}
+
+/*
+ * Exit extended function mode
+ */
+static inline void w977_efm_exit(unsigned int efio)
+{
+	outb(0xAA, efio);
+}
+#endif
diff --git a/drivers/net/irda/w83977af_ir.c b/drivers/net/irda/w83977af_ir.c
new file mode 100644
index 0000000..0ea65c4
--- /dev/null
+++ b/drivers/net/irda/w83977af_ir.c
@@ -0,0 +1,1379 @@
+/*********************************************************************
+ *                
+ * Filename:      w83977af_ir.c
+ * Version:       1.0
+ * Description:   FIR driver for the Winbond W83977AF Super I/O chip
+ * Status:        Experimental.
+ * Author:        Paul VanderSpek
+ * Created at:    Wed Nov  4 11:46:16 1998
+ * Modified at:   Fri Jan 28 12:10:59 2000
+ * Modified by:   Dag Brattli <dagb@cs.uit.no>
+ * 
+ *     Copyright (c) 1998-2000 Dag Brattli <dagb@cs.uit.no>
+ *     Copyright (c) 1998-1999 Rebel.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.
+ *  
+ *     Neither Paul VanderSpek nor Rebel.com admit liability nor provide
+ *     warranty for any of this software. This material is provided "AS-IS"
+ *     and at no charge.
+ *     
+ *     If you find bugs in this file, its very likely that the same bug
+ *     will also be in pc87108.c since the implementations are quite
+ *     similar.
+ *
+ *     Notice that all functions that needs to access the chip in _any_
+ *     way, must save BSR register on entry, and restore it on exit. 
+ *     It is _very_ important to follow this policy!
+ *
+ *         __u8 bank;
+ *     
+ *         bank = inb( iobase+BSR);
+ *  
+ *         do_your_stuff_here();
+ *
+ *         outb( bank, iobase+BSR);
+ *
+ ********************************************************************/
+
+#include <linux/module.h>
+#include <linux/config.h> 
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/rtnetlink.h>
+#include <linux/dma-mapping.h>
+
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/byteorder.h>
+
+#include <net/irda/irda.h>
+#include <net/irda/wrapper.h>
+#include <net/irda/irda_device.h>
+#include "w83977af.h"
+#include "w83977af_ir.h"
+
+#ifdef  CONFIG_ARCH_NETWINDER            /* Adjust to NetWinder differences */
+#undef  CONFIG_NETWINDER_TX_DMA_PROBLEMS /* Not needed */
+#define CONFIG_NETWINDER_RX_DMA_PROBLEMS /* Must have this one! */
+#endif
+#undef  CONFIG_USE_INTERNAL_TIMER  /* Just cannot make that timer work */
+#define CONFIG_USE_W977_PNP        /* Currently needed */
+#define PIO_MAX_SPEED       115200 
+
+static char *driver_name = "w83977af_ir";
+static int  qos_mtt_bits = 0x07;   /* 1 ms or more */
+
+#define CHIP_IO_EXTENT 8
+
+static unsigned int io[] = { 0x180, ~0, ~0, ~0 };
+#ifdef CONFIG_ARCH_NETWINDER             /* Adjust to NetWinder differences */
+static unsigned int irq[] = { 6, 0, 0, 0 };
+#else
+static unsigned int irq[] = { 11, 0, 0, 0 };
+#endif
+static unsigned int dma[] = { 1, 0, 0, 0 };
+static unsigned int efbase[] = { W977_EFIO_BASE, W977_EFIO2_BASE };
+static unsigned int efio = W977_EFIO_BASE;
+
+static struct w83977af_ir *dev_self[] = { NULL, NULL, NULL, NULL};
+
+/* Some prototypes */
+static int  w83977af_open(int i, unsigned int iobase, unsigned int irq, 
+                          unsigned int dma);
+static int  w83977af_close(struct w83977af_ir *self);
+static int  w83977af_probe(int iobase, int irq, int dma);
+static int  w83977af_dma_receive(struct w83977af_ir *self); 
+static int  w83977af_dma_receive_complete(struct w83977af_ir *self);
+static int  w83977af_hard_xmit(struct sk_buff *skb, struct net_device *dev);
+static int  w83977af_pio_write(int iobase, __u8 *buf, int len, int fifo_size);
+static void w83977af_dma_write(struct w83977af_ir *self, int iobase);
+static void w83977af_change_speed(struct w83977af_ir *self, __u32 speed);
+static int  w83977af_is_receiving(struct w83977af_ir *self);
+
+static int  w83977af_net_open(struct net_device *dev);
+static int  w83977af_net_close(struct net_device *dev);
+static int  w83977af_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+static struct net_device_stats *w83977af_net_get_stats(struct net_device *dev);
+
+/*
+ * Function w83977af_init ()
+ *
+ *    Initialize chip. Just try to find out how many chips we are dealing with
+ *    and where they are
+ */
+static int __init w83977af_init(void)
+{
+        int i;
+
+	IRDA_DEBUG(0, "%s()\n", __FUNCTION__ );
+
+	for (i=0; (io[i] < 2000) && (i < 4); i++) { 
+		if (w83977af_open(i, io[i], irq[i], dma[i]) == 0)
+			return 0;
+	}
+	return -ENODEV;
+}
+
+/*
+ * Function w83977af_cleanup ()
+ *
+ *    Close all configured chips
+ *
+ */
+static void __exit w83977af_cleanup(void)
+{
+	int i;
+
+        IRDA_DEBUG(4, "%s()\n", __FUNCTION__ );
+
+	for (i=0; i < 4; i++) {
+		if (dev_self[i])
+			w83977af_close(dev_self[i]);
+	}
+}
+
+/*
+ * Function w83977af_open (iobase, irq)
+ *
+ *    Open driver instance
+ *
+ */
+int w83977af_open(int i, unsigned int iobase, unsigned int irq, 
+		  unsigned int dma)
+{
+	struct net_device *dev;
+        struct w83977af_ir *self;
+	int err;
+
+	IRDA_DEBUG(0, "%s()\n", __FUNCTION__ );
+
+	/* Lock the port that we need */
+	if (!request_region(iobase, CHIP_IO_EXTENT, driver_name)) {
+		IRDA_DEBUG(0, "%s(), can't get iobase of 0x%03x\n",
+		      __FUNCTION__ , iobase);
+		return -ENODEV;
+	}
+
+	if (w83977af_probe(iobase, irq, dma) == -1) {
+		err = -1;
+		goto err_out;
+	}
+	/*
+	 *  Allocate new instance of the driver
+	 */
+	dev = alloc_irdadev(sizeof(struct w83977af_ir));
+	if (dev == NULL) {
+		printk( KERN_ERR "IrDA: Can't allocate memory for "
+			"IrDA control block!\n");
+		err = -ENOMEM;
+		goto err_out;
+	}
+
+	self = dev->priv;
+	spin_lock_init(&self->lock);
+   
+
+	/* Initialize IO */
+	self->io.fir_base   = iobase;
+        self->io.irq       = irq;
+        self->io.fir_ext   = CHIP_IO_EXTENT;
+        self->io.dma       = dma;
+        self->io.fifo_size = 32;
+
+	/* Initialize QoS for this device */
+	irda_init_max_qos_capabilies(&self->qos);
+	
+	/* The only value we must override it the baudrate */
+
+	/* FIXME: The HP HDLS-1100 does not support 1152000! */
+	self->qos.baud_rate.bits = IR_9600|IR_19200|IR_38400|IR_57600|
+		IR_115200|IR_576000|IR_1152000|(IR_4000000 << 8);
+
+	/* The HP HDLS-1100 needs 1 ms according to the specs */
+	self->qos.min_turn_time.bits = qos_mtt_bits;
+	irda_qos_bits_to_value(&self->qos);
+	
+	/* Max DMA buffer size needed = (data_size + 6) * (window_size) + 6; */
+	self->rx_buff.truesize = 14384; 
+	self->tx_buff.truesize = 4000;
+	
+	/* Allocate memory if needed */
+	self->rx_buff.head =
+		dma_alloc_coherent(NULL, self->rx_buff.truesize,
+				   &self->rx_buff_dma, GFP_KERNEL);
+	if (self->rx_buff.head == NULL) {
+		err = -ENOMEM;
+		goto err_out1;
+	}
+
+	memset(self->rx_buff.head, 0, self->rx_buff.truesize);
+	
+	self->tx_buff.head =
+		dma_alloc_coherent(NULL, self->tx_buff.truesize,
+				   &self->tx_buff_dma, GFP_KERNEL);
+	if (self->tx_buff.head == NULL) {
+		err = -ENOMEM;
+		goto err_out2;
+	}
+	memset(self->tx_buff.head, 0, self->tx_buff.truesize);
+
+	self->rx_buff.in_frame = FALSE;
+	self->rx_buff.state = OUTSIDE_FRAME;
+	self->tx_buff.data = self->tx_buff.head;
+	self->rx_buff.data = self->rx_buff.head;
+	self->netdev = dev;
+
+	/* Keep track of module usage */
+	SET_MODULE_OWNER(dev);
+
+	/* Override the network functions we need to use */
+	dev->hard_start_xmit = w83977af_hard_xmit;
+	dev->open            = w83977af_net_open;
+	dev->stop            = w83977af_net_close;
+	dev->do_ioctl        = w83977af_net_ioctl;
+	dev->get_stats	     = w83977af_net_get_stats;
+
+	err = register_netdev(dev);
+	if (err) {
+		IRDA_ERROR("%s(), register_netdevice() failed!\n", __FUNCTION__);
+		goto err_out3;
+	}
+	IRDA_MESSAGE("IrDA: Registered device %s\n", dev->name);
+
+	/* Need to store self somewhere */
+	dev_self[i] = self;
+	
+	return 0;
+err_out3:
+	dma_free_coherent(NULL, self->tx_buff.truesize,
+			  self->tx_buff.head, self->tx_buff_dma);
+err_out2:	
+	dma_free_coherent(NULL, self->rx_buff.truesize,
+			  self->rx_buff.head, self->rx_buff_dma);
+err_out1:
+	free_netdev(dev);
+err_out:
+	release_region(iobase, CHIP_IO_EXTENT);
+	return err;
+}
+
+/*
+ * Function w83977af_close (self)
+ *
+ *    Close driver instance
+ *
+ */
+static int w83977af_close(struct w83977af_ir *self)
+{
+	int iobase;
+
+	IRDA_DEBUG(0, "%s()\n", __FUNCTION__ );
+
+        iobase = self->io.fir_base;
+
+#ifdef CONFIG_USE_W977_PNP
+	/* enter PnP configuration mode */
+	w977_efm_enter(efio);
+
+	w977_select_device(W977_DEVICE_IR, efio);
+
+	/* Deactivate device */
+	w977_write_reg(0x30, 0x00, efio);
+
+	w977_efm_exit(efio);
+#endif /* CONFIG_USE_W977_PNP */
+
+	/* Remove netdevice */
+	unregister_netdev(self->netdev);
+
+	/* Release the PORT that this driver is using */
+	IRDA_DEBUG(0 , "%s(), Releasing Region %03x\n", 
+	      __FUNCTION__ , self->io.fir_base);
+	release_region(self->io.fir_base, self->io.fir_ext);
+
+	if (self->tx_buff.head)
+		dma_free_coherent(NULL, self->tx_buff.truesize,
+				  self->tx_buff.head, self->tx_buff_dma);
+	
+	if (self->rx_buff.head)
+		dma_free_coherent(NULL, self->rx_buff.truesize,
+				  self->rx_buff.head, self->rx_buff_dma);
+
+	free_netdev(self->netdev);
+
+	return 0;
+}
+
+int w83977af_probe( int iobase, int irq, int dma)
+{
+  	int version;
+	int i;
+  	
+ 	for (i=0; i < 2; i++) {
+ 		IRDA_DEBUG( 0, "%s()\n", __FUNCTION__ );
+#ifdef CONFIG_USE_W977_PNP
+ 		/* Enter PnP configuration mode */
+		w977_efm_enter(efbase[i]);
+  
+ 		w977_select_device(W977_DEVICE_IR, efbase[i]);
+  
+ 		/* Configure PnP port, IRQ, and DMA channel */
+ 		w977_write_reg(0x60, (iobase >> 8) & 0xff, efbase[i]);
+ 		w977_write_reg(0x61, (iobase) & 0xff, efbase[i]);
+  
+ 		w977_write_reg(0x70, irq, efbase[i]);
+#ifdef CONFIG_ARCH_NETWINDER
+		/* Netwinder uses 1 higher than Linux */
+ 		w977_write_reg(0x74, dma+1, efbase[i]);
+#else
+ 		w977_write_reg(0x74, dma, efbase[i]);   
+#endif /*CONFIG_ARCH_NETWINDER */
+ 		w977_write_reg(0x75, 0x04, efbase[i]);  /* Disable Tx DMA */
+  	
+ 		/* Set append hardware CRC, enable IR bank selection */	
+ 		w977_write_reg(0xf0, APEDCRC|ENBNKSEL, efbase[i]);
+  
+ 		/* Activate device */
+ 		w977_write_reg(0x30, 0x01, efbase[i]);
+  
+ 		w977_efm_exit(efbase[i]);
+#endif /* CONFIG_USE_W977_PNP */
+  		/* Disable Advanced mode */
+  		switch_bank(iobase, SET2);
+  		outb(iobase+2, 0x00);  
+ 
+ 		/* Turn on UART (global) interrupts */
+ 		switch_bank(iobase, SET0);
+  		outb(HCR_EN_IRQ, iobase+HCR);
+  	
+  		/* Switch to advanced mode */
+  		switch_bank(iobase, SET2);
+  		outb(inb(iobase+ADCR1) | ADCR1_ADV_SL, iobase+ADCR1);
+  
+  		/* Set default IR-mode */
+  		switch_bank(iobase, SET0);
+  		outb(HCR_SIR, iobase+HCR);
+  
+  		/* Read the Advanced IR ID */
+  		switch_bank(iobase, SET3);
+  		version = inb(iobase+AUID);
+  	
+  		/* Should be 0x1? */
+  		if (0x10 == (version & 0xf0)) {
+ 			efio = efbase[i];
+ 
+ 			/* Set FIFO size to 32 */
+ 			switch_bank(iobase, SET2);
+ 			outb(ADCR2_RXFS32|ADCR2_TXFS32, iobase+ADCR2);	
+ 	
+ 			/* Set FIFO threshold to TX17, RX16 */
+ 			switch_bank(iobase, SET0);	
+ 			outb(UFR_RXTL|UFR_TXTL|UFR_TXF_RST|UFR_RXF_RST|
+			     UFR_EN_FIFO,iobase+UFR);
+ 
+ 			/* Receiver frame length */
+ 			switch_bank(iobase, SET4);
+			outb(2048 & 0xff, iobase+6);
+			outb((2048 >> 8) & 0x1f, iobase+7);
+
+			/* 
+			 * Init HP HSDL-1100 transceiver. 
+			 * 
+			 * Set IRX_MSL since we have 2 * receive paths IRRX, 
+			 * and IRRXH. Clear IRSL0D since we want IRSL0 * to 
+			 * be a input pin used for IRRXH 
+			 *
+			 *   IRRX  pin 37 connected to receiver 
+			 *   IRTX  pin 38 connected to transmitter
+			 *   FIRRX pin 39 connected to receiver      (IRSL0) 
+			 *   CIRRX pin 40 connected to pin 37
+			 */
+			switch_bank(iobase, SET7);
+			outb(0x40, iobase+7);
+			
+			IRDA_MESSAGE("W83977AF (IR) driver loaded. "
+				     "Version: 0x%02x\n", version);
+			
+			return 0;
+		} else {
+			/* Try next extented function register address */
+			IRDA_DEBUG( 0, "%s(), Wrong chip version", __FUNCTION__ );
+		}
+  	}   	
+	return -1;
+}
+
+void w83977af_change_speed(struct w83977af_ir *self, __u32 speed)
+{
+	int ir_mode = HCR_SIR;
+	int iobase; 
+	__u8 set;
+
+	iobase = self->io.fir_base;
+
+	/* Update accounting for new speed */
+	self->io.speed = speed;
+
+	/* Save current bank */
+	set = inb(iobase+SSR);
+
+	/* Disable interrupts */
+	switch_bank(iobase, SET0);
+	outb(0, iobase+ICR);
+
+	/* Select Set 2 */
+	switch_bank(iobase, SET2);
+	outb(0x00, iobase+ABHL);
+
+	switch (speed) {
+	case 9600:   outb(0x0c, iobase+ABLL); break;
+	case 19200:  outb(0x06, iobase+ABLL); break;
+	case 38400:  outb(0x03, iobase+ABLL); break;
+	case 57600:  outb(0x02, iobase+ABLL); break;
+	case 115200: outb(0x01, iobase+ABLL); break;
+	case 576000:
+		ir_mode = HCR_MIR_576;
+		IRDA_DEBUG(0, "%s(), handling baud of 576000\n", __FUNCTION__ );
+		break;
+	case 1152000:
+		ir_mode = HCR_MIR_1152;
+		IRDA_DEBUG(0, "%s(), handling baud of 1152000\n", __FUNCTION__ );
+		break;
+	case 4000000:
+		ir_mode = HCR_FIR;
+		IRDA_DEBUG(0, "%s(), handling baud of 4000000\n", __FUNCTION__ );
+		break;
+	default:
+		ir_mode = HCR_FIR;
+		IRDA_DEBUG(0, "%s(), unknown baud rate of %d\n", __FUNCTION__ , speed);
+		break;
+	}
+
+	/* Set speed mode */
+	switch_bank(iobase, SET0);
+	outb(ir_mode, iobase+HCR);
+
+	/* set FIFO size to 32 */
+	switch_bank(iobase, SET2);
+	outb(ADCR2_RXFS32|ADCR2_TXFS32, iobase+ADCR2);	
+	
+	/* set FIFO threshold to TX17, RX16 */
+	switch_bank(iobase, SET0);
+	outb(0x00, iobase+UFR);        /* Reset */
+	outb(UFR_EN_FIFO, iobase+UFR); /* First we must enable FIFO */
+	outb(0xa7, iobase+UFR);
+
+	netif_wake_queue(self->netdev);
+		
+	/* Enable some interrupts so we can receive frames */
+	switch_bank(iobase, SET0);
+	if (speed > PIO_MAX_SPEED) {
+		outb(ICR_EFSFI, iobase+ICR);
+		w83977af_dma_receive(self);
+	} else
+		outb(ICR_ERBRI, iobase+ICR);
+    	
+	/* Restore SSR */
+	outb(set, iobase+SSR);
+}
+
+/*
+ * Function w83977af_hard_xmit (skb, dev)
+ *
+ *    Sets up a DMA transfer to send the current frame.
+ *
+ */
+int w83977af_hard_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct w83977af_ir *self;
+	__s32 speed;
+	int iobase;
+	__u8 set;
+	int mtt;
+	
+	self = (struct w83977af_ir *) dev->priv;
+
+	iobase = self->io.fir_base;
+
+	IRDA_DEBUG(4, "%s(%ld), skb->len=%d\n", __FUNCTION__ , jiffies, 
+		   (int) skb->len);
+	
+	/* Lock transmit buffer */
+	netif_stop_queue(dev);
+	
+	/* Check if we need to change the speed */
+	speed = irda_get_next_speed(skb);
+	if ((speed != self->io.speed) && (speed != -1)) {
+		/* Check for empty frame */
+		if (!skb->len) {
+			w83977af_change_speed(self, speed); 
+			dev->trans_start = jiffies;
+			dev_kfree_skb(skb);
+			return 0;
+		} else
+			self->new_speed = speed;
+	}
+
+	/* Save current set */
+	set = inb(iobase+SSR);
+	
+	/* Decide if we should use PIO or DMA transfer */
+	if (self->io.speed > PIO_MAX_SPEED) {
+		self->tx_buff.data = self->tx_buff.head;
+		memcpy(self->tx_buff.data, skb->data, skb->len);
+		self->tx_buff.len = skb->len;
+		
+		mtt = irda_get_mtt(skb);
+#ifdef CONFIG_USE_INTERNAL_TIMER
+	        if (mtt > 50) {
+			/* Adjust for timer resolution */
+			mtt /= 1000+1;
+
+			/* Setup timer */
+			switch_bank(iobase, SET4);
+			outb(mtt & 0xff, iobase+TMRL);
+			outb((mtt >> 8) & 0x0f, iobase+TMRH);
+			
+			/* Start timer */
+			outb(IR_MSL_EN_TMR, iobase+IR_MSL);
+			self->io.direction = IO_XMIT;
+			
+			/* Enable timer interrupt */
+			switch_bank(iobase, SET0);
+			outb(ICR_ETMRI, iobase+ICR);
+		} else {
+#endif
+			IRDA_DEBUG(4, "%s(%ld), mtt=%d\n", __FUNCTION__ , jiffies, mtt);
+			if (mtt)
+				udelay(mtt);
+
+			/* Enable DMA interrupt */
+			switch_bank(iobase, SET0);
+	 		outb(ICR_EDMAI, iobase+ICR);
+	     		w83977af_dma_write(self, iobase);
+#ifdef CONFIG_USE_INTERNAL_TIMER
+		}
+#endif
+	} else {
+		self->tx_buff.data = self->tx_buff.head;
+		self->tx_buff.len = async_wrap_skb(skb, self->tx_buff.data, 
+						   self->tx_buff.truesize);
+		
+		/* Add interrupt on tx low level (will fire immediately) */
+		switch_bank(iobase, SET0);
+		outb(ICR_ETXTHI, iobase+ICR);
+	}
+	dev->trans_start = jiffies;
+	dev_kfree_skb(skb);
+
+	/* Restore set register */
+	outb(set, iobase+SSR);
+
+	return 0;
+}
+
+/*
+ * Function w83977af_dma_write (self, iobase)
+ *
+ *    Send frame using DMA
+ *
+ */
+static void w83977af_dma_write(struct w83977af_ir *self, int iobase)
+{
+	__u8 set;
+#ifdef CONFIG_NETWINDER_TX_DMA_PROBLEMS
+	unsigned long flags;
+	__u8 hcr;
+#endif
+        IRDA_DEBUG(4, "%s(), len=%d\n", __FUNCTION__ , self->tx_buff.len);
+
+	/* Save current set */
+	set = inb(iobase+SSR);
+
+	/* Disable DMA */
+	switch_bank(iobase, SET0);
+	outb(inb(iobase+HCR) & ~HCR_EN_DMA, iobase+HCR);
+
+	/* Choose transmit DMA channel  */ 
+	switch_bank(iobase, SET2);
+	outb(ADCR1_D_CHSW|/*ADCR1_DMA_F|*/ADCR1_ADV_SL, iobase+ADCR1);
+#ifdef CONFIG_NETWINDER_TX_DMA_PROBLEMS
+	spin_lock_irqsave(&self->lock, flags);
+
+	disable_dma(self->io.dma);
+	clear_dma_ff(self->io.dma);
+	set_dma_mode(self->io.dma, DMA_MODE_READ);
+	set_dma_addr(self->io.dma, self->tx_buff_dma);
+	set_dma_count(self->io.dma, self->tx_buff.len);
+#else
+	irda_setup_dma(self->io.dma, self->tx_buff_dma, self->tx_buff.len,
+		       DMA_MODE_WRITE);	
+#endif
+	self->io.direction = IO_XMIT;
+	
+	/* Enable DMA */
+ 	switch_bank(iobase, SET0);
+#ifdef CONFIG_NETWINDER_TX_DMA_PROBLEMS
+	hcr = inb(iobase+HCR);
+	outb(hcr | HCR_EN_DMA, iobase+HCR);
+	enable_dma(self->io.dma);
+	spin_unlock_irqrestore(&self->lock, flags);
+#else	
+	outb(inb(iobase+HCR) | HCR_EN_DMA | HCR_TX_WT, iobase+HCR);
+#endif
+
+	/* Restore set register */
+	outb(set, iobase+SSR);
+}
+
+/*
+ * Function w83977af_pio_write (iobase, buf, len, fifo_size)
+ *
+ *    
+ *
+ */
+static int w83977af_pio_write(int iobase, __u8 *buf, int len, int fifo_size)
+{
+	int actual = 0;
+	__u8 set;
+	
+	IRDA_DEBUG(4, "%s()\n", __FUNCTION__ );
+
+	/* Save current bank */
+	set = inb(iobase+SSR);
+
+	switch_bank(iobase, SET0);
+	if (!(inb_p(iobase+USR) & USR_TSRE)) {
+		IRDA_DEBUG(4,
+			   "%s(), warning, FIFO not empty yet!\n", __FUNCTION__  );
+
+		fifo_size -= 17;
+		IRDA_DEBUG(4, "%s(), %d bytes left in tx fifo\n", 
+			   __FUNCTION__ , fifo_size);
+	}
+
+	/* Fill FIFO with current frame */
+	while ((fifo_size-- > 0) && (actual < len)) {
+		/* Transmit next byte */
+		outb(buf[actual++], iobase+TBR);
+	}
+        
+	IRDA_DEBUG(4, "%s(), fifo_size %d ; %d sent of %d\n", 
+		   __FUNCTION__ , fifo_size, actual, len);
+
+	/* Restore bank */
+	outb(set, iobase+SSR);
+
+	return actual;
+}
+
+/*
+ * Function w83977af_dma_xmit_complete (self)
+ *
+ *    The transfer of a frame in finished. So do the necessary things
+ *
+ *    
+ */
+static void w83977af_dma_xmit_complete(struct w83977af_ir *self)
+{
+	int iobase;
+	__u8 set;
+
+	IRDA_DEBUG(4, "%s(%ld)\n", __FUNCTION__ , jiffies);
+
+	IRDA_ASSERT(self != NULL, return;);
+
+	iobase = self->io.fir_base;
+
+	/* Save current set */
+	set = inb(iobase+SSR);
+
+	/* Disable DMA */
+	switch_bank(iobase, SET0);
+	outb(inb(iobase+HCR) & ~HCR_EN_DMA, iobase+HCR);
+	
+	/* Check for underrrun! */
+	if (inb(iobase+AUDR) & AUDR_UNDR) {
+		IRDA_DEBUG(0, "%s(), Transmit underrun!\n", __FUNCTION__ );
+		
+		self->stats.tx_errors++;
+		self->stats.tx_fifo_errors++;
+
+		/* Clear bit, by writing 1 to it */
+		outb(AUDR_UNDR, iobase+AUDR);
+	} else
+		self->stats.tx_packets++;
+
+	
+	if (self->new_speed) {
+		w83977af_change_speed(self, self->new_speed);
+		self->new_speed = 0;
+	}
+
+	/* Unlock tx_buff and request another frame */
+	/* Tell the network layer, that we want more frames */
+	netif_wake_queue(self->netdev);
+	
+	/* Restore set */
+	outb(set, iobase+SSR);
+}
+
+/*
+ * Function w83977af_dma_receive (self)
+ *
+ *    Get ready for receiving a frame. The device will initiate a DMA
+ *    if it starts to receive a frame.
+ *
+ */
+int w83977af_dma_receive(struct w83977af_ir *self) 
+{
+	int iobase;
+	__u8 set;
+#ifdef CONFIG_NETWINDER_RX_DMA_PROBLEMS
+	unsigned long flags;
+	__u8 hcr;
+#endif
+	IRDA_ASSERT(self != NULL, return -1;);
+
+	IRDA_DEBUG(4, "%s\n", __FUNCTION__ );
+
+	iobase= self->io.fir_base;
+
+	/* Save current set */
+	set = inb(iobase+SSR);
+
+	/* Disable DMA */
+	switch_bank(iobase, SET0);
+	outb(inb(iobase+HCR) & ~HCR_EN_DMA, iobase+HCR);
+
+	/* Choose DMA Rx, DMA Fairness, and Advanced mode */
+	switch_bank(iobase, SET2);
+	outb((inb(iobase+ADCR1) & ~ADCR1_D_CHSW)/*|ADCR1_DMA_F*/|ADCR1_ADV_SL,
+	     iobase+ADCR1);
+
+	self->io.direction = IO_RECV;
+	self->rx_buff.data = self->rx_buff.head;
+
+#ifdef CONFIG_NETWINDER_RX_DMA_PROBLEMS
+	spin_lock_irqsave(&self->lock, flags);
+
+	disable_dma(self->io.dma);
+	clear_dma_ff(self->io.dma);
+	set_dma_mode(self->io.dma, DMA_MODE_READ);
+	set_dma_addr(self->io.dma, self->rx_buff_dma);
+	set_dma_count(self->io.dma, self->rx_buff.truesize);
+#else
+	irda_setup_dma(self->io.dma, self->rx_buff_dma, self->rx_buff.truesize,
+		       DMA_MODE_READ);
+#endif
+	/* 
+	 * Reset Rx FIFO. This will also flush the ST_FIFO, it's very 
+	 * important that we don't reset the Tx FIFO since it might not
+	 * be finished transmitting yet
+	 */
+	switch_bank(iobase, SET0);
+	outb(UFR_RXTL|UFR_TXTL|UFR_RXF_RST|UFR_EN_FIFO, iobase+UFR);
+	self->st_fifo.len = self->st_fifo.tail = self->st_fifo.head = 0;
+	
+	/* Enable DMA */
+	switch_bank(iobase, SET0);
+#ifdef CONFIG_NETWINDER_RX_DMA_PROBLEMS
+	hcr = inb(iobase+HCR);
+	outb(hcr | HCR_EN_DMA, iobase+HCR);
+	enable_dma(self->io.dma);
+	spin_unlock_irqrestore(&self->lock, flags);
+#else	
+	outb(inb(iobase+HCR) | HCR_EN_DMA, iobase+HCR);
+#endif
+	/* Restore set */
+	outb(set, iobase+SSR);
+
+	return 0;
+}
+
+/*
+ * Function w83977af_receive_complete (self)
+ *
+ *    Finished with receiving a frame
+ *
+ */
+int w83977af_dma_receive_complete(struct w83977af_ir *self)
+{
+	struct sk_buff *skb;
+	struct st_fifo *st_fifo;
+	int len;
+	int iobase;
+	__u8 set;
+	__u8 status;
+
+	IRDA_DEBUG(4, "%s\n", __FUNCTION__ );
+
+	st_fifo = &self->st_fifo;
+
+	iobase = self->io.fir_base;
+
+	/* Save current set */
+	set = inb(iobase+SSR);
+	
+	iobase = self->io.fir_base;
+
+	/* Read status FIFO */
+	switch_bank(iobase, SET5);
+	while ((status = inb(iobase+FS_FO)) & FS_FO_FSFDR) {
+		st_fifo->entries[st_fifo->tail].status = status;
+		
+		st_fifo->entries[st_fifo->tail].len  = inb(iobase+RFLFL);
+		st_fifo->entries[st_fifo->tail].len |= inb(iobase+RFLFH) << 8;
+		
+		st_fifo->tail++;
+		st_fifo->len++;
+	}
+	
+	while (st_fifo->len) {
+		/* Get first entry */
+		status = st_fifo->entries[st_fifo->head].status;
+		len    = st_fifo->entries[st_fifo->head].len;
+		st_fifo->head++;
+		st_fifo->len--;
+
+		/* Check for errors */
+		if (status & FS_FO_ERR_MSK) {
+			if (status & FS_FO_LST_FR) {
+				/* Add number of lost frames to stats */
+				self->stats.rx_errors += len;	
+			} else {
+				/* Skip frame */
+				self->stats.rx_errors++;
+				
+				self->rx_buff.data += len;
+				
+				if (status & FS_FO_MX_LEX)
+					self->stats.rx_length_errors++;
+				
+				if (status & FS_FO_PHY_ERR) 
+					self->stats.rx_frame_errors++;
+				
+				if (status & FS_FO_CRC_ERR) 
+					self->stats.rx_crc_errors++;
+			}
+			/* The errors below can be reported in both cases */
+			if (status & FS_FO_RX_OV)
+				self->stats.rx_fifo_errors++;
+			
+			if (status & FS_FO_FSF_OV)
+				self->stats.rx_fifo_errors++;
+			
+		} else {
+			/* Check if we have transferred all data to memory */
+			switch_bank(iobase, SET0);
+			if (inb(iobase+USR) & USR_RDR) {
+#ifdef CONFIG_USE_INTERNAL_TIMER
+				/* Put this entry back in fifo */
+				st_fifo->head--;
+				st_fifo->len++;
+				st_fifo->entries[st_fifo->head].status = status;
+				st_fifo->entries[st_fifo->head].len = len;
+				
+				/* Restore set register */
+				outb(set, iobase+SSR);
+			
+				return FALSE; 	/* I'll be back! */
+#else
+				udelay(80); /* Should be enough!? */
+#endif
+			}
+						
+			skb = dev_alloc_skb(len+1);
+			if (skb == NULL)  {
+				printk(KERN_INFO
+				       "%s(), memory squeeze, dropping frame.\n", __FUNCTION__);
+				/* Restore set register */
+				outb(set, iobase+SSR);
+
+				return FALSE;
+			}
+			
+			/*  Align to 20 bytes */
+			skb_reserve(skb, 1); 
+			
+			/* Copy frame without CRC */
+			if (self->io.speed < 4000000) {
+				skb_put(skb, len-2);
+				memcpy(skb->data, self->rx_buff.data, len-2);
+			} else {
+				skb_put(skb, len-4);
+				memcpy(skb->data, self->rx_buff.data, len-4);
+			}
+
+			/* Move to next frame */
+			self->rx_buff.data += len;
+			self->stats.rx_packets++;
+			
+			skb->dev = self->netdev;
+			skb->mac.raw  = skb->data;
+			skb->protocol = htons(ETH_P_IRDA);
+			netif_rx(skb);
+			self->netdev->last_rx = jiffies;
+		}
+	}
+	/* Restore set register */
+	outb(set, iobase+SSR);
+
+	return TRUE;
+}
+
+/*
+ * Function pc87108_pio_receive (self)
+ *
+ *    Receive all data in receiver FIFO
+ *
+ */
+static void w83977af_pio_receive(struct w83977af_ir *self) 
+{
+	__u8 byte = 0x00;
+	int iobase;
+
+	IRDA_DEBUG(4, "%s()\n", __FUNCTION__ );
+
+	IRDA_ASSERT(self != NULL, return;);
+	
+	iobase = self->io.fir_base;
+	
+	/*  Receive all characters in Rx FIFO */
+	do {
+		byte = inb(iobase+RBR);
+		async_unwrap_char(self->netdev, &self->stats, &self->rx_buff, 
+				  byte);
+	} while (inb(iobase+USR) & USR_RDR); /* Data available */	
+}
+
+/*
+ * Function w83977af_sir_interrupt (self, eir)
+ *
+ *    Handle SIR interrupt
+ *
+ */
+static __u8 w83977af_sir_interrupt(struct w83977af_ir *self, int isr)
+{
+	int actual;
+	__u8 new_icr = 0;
+	__u8 set;
+	int iobase;
+
+	IRDA_DEBUG(4, "%s(), isr=%#x\n", __FUNCTION__ , isr);
+	
+	iobase = self->io.fir_base;
+	/* Transmit FIFO low on data */
+	if (isr & ISR_TXTH_I) {
+		/* Write data left in transmit buffer */
+		actual = w83977af_pio_write(self->io.fir_base, 
+					    self->tx_buff.data, 
+					    self->tx_buff.len, 
+					    self->io.fifo_size);
+
+		self->tx_buff.data += actual;
+		self->tx_buff.len  -= actual;
+		
+		self->io.direction = IO_XMIT;
+
+		/* Check if finished */
+		if (self->tx_buff.len > 0) {
+			new_icr |= ICR_ETXTHI;
+		} else {
+			set = inb(iobase+SSR);
+			switch_bank(iobase, SET0);
+			outb(AUDR_SFEND, iobase+AUDR);
+			outb(set, iobase+SSR); 
+
+			self->stats.tx_packets++;
+
+			/* Feed me more packets */
+			netif_wake_queue(self->netdev);
+			new_icr |= ICR_ETBREI;
+		}
+	}
+	/* Check if transmission has completed */
+	if (isr & ISR_TXEMP_I) {		
+		/* Check if we need to change the speed? */
+		if (self->new_speed) {
+			IRDA_DEBUG(2,
+				   "%s(), Changing speed!\n", __FUNCTION__ );
+			w83977af_change_speed(self, self->new_speed);
+			self->new_speed = 0;
+		}
+
+		/* Turn around and get ready to receive some data */
+		self->io.direction = IO_RECV;
+		new_icr |= ICR_ERBRI;
+	}
+
+	/* Rx FIFO threshold or timeout */
+	if (isr & ISR_RXTH_I) {
+		w83977af_pio_receive(self);
+
+		/* Keep receiving */
+		new_icr |= ICR_ERBRI;
+	}
+	return new_icr;
+}
+
+/*
+ * Function pc87108_fir_interrupt (self, eir)
+ *
+ *    Handle MIR/FIR interrupt
+ *
+ */
+static __u8 w83977af_fir_interrupt(struct w83977af_ir *self, int isr)
+{
+	__u8 new_icr = 0;
+	__u8 set;
+	int iobase;
+
+	iobase = self->io.fir_base;
+	set = inb(iobase+SSR);
+	
+	/* End of frame detected in FIFO */
+	if (isr & (ISR_FEND_I|ISR_FSF_I)) {
+		if (w83977af_dma_receive_complete(self)) {
+			
+			/* Wait for next status FIFO interrupt */
+			new_icr |= ICR_EFSFI;
+		} else {
+			/* DMA not finished yet */
+
+			/* Set timer value, resolution 1 ms */
+			switch_bank(iobase, SET4);
+			outb(0x01, iobase+TMRL); /* 1 ms */
+			outb(0x00, iobase+TMRH);
+
+			/* Start timer */
+			outb(IR_MSL_EN_TMR, iobase+IR_MSL);
+
+			new_icr |= ICR_ETMRI;
+		}
+	}
+	/* Timer finished */
+	if (isr & ISR_TMR_I) {
+		/* Disable timer */
+		switch_bank(iobase, SET4);
+		outb(0, iobase+IR_MSL);
+
+		/* Clear timer event */
+		/* switch_bank(iobase, SET0); */
+/* 		outb(ASCR_CTE, iobase+ASCR); */
+
+		/* Check if this is a TX timer interrupt */
+		if (self->io.direction == IO_XMIT) {
+			w83977af_dma_write(self, iobase);
+
+			new_icr |= ICR_EDMAI;
+		} else {
+			/* Check if DMA has now finished */
+			w83977af_dma_receive_complete(self);
+
+			new_icr |= ICR_EFSFI;
+		}
+	}	
+	/* Finished with DMA */
+	if (isr & ISR_DMA_I) {
+		w83977af_dma_xmit_complete(self);
+
+		/* Check if there are more frames to be transmitted */
+		/* if (irda_device_txqueue_empty(self)) { */
+		
+		/* Prepare for receive 
+		 * 
+		 * ** Netwinder Tx DMA likes that we do this anyway **
+		 */
+		w83977af_dma_receive(self);
+		new_icr = ICR_EFSFI;
+	       /* } */
+	}
+	
+	/* Restore set */
+	outb(set, iobase+SSR);
+
+	return new_icr;
+}
+
+/*
+ * Function w83977af_interrupt (irq, dev_id, regs)
+ *
+ *    An interrupt from the chip has arrived. Time to do some work
+ *
+ */
+static irqreturn_t w83977af_interrupt(int irq, void *dev_id,
+					struct pt_regs *regs)
+{
+	struct net_device *dev = (struct net_device *) dev_id;
+	struct w83977af_ir *self;
+	__u8 set, icr, isr;
+	int iobase;
+
+	if (!dev) {
+		printk(KERN_WARNING "%s: irq %d for unknown device.\n", 
+			driver_name, irq);
+		return IRQ_NONE;
+	}
+	self = (struct w83977af_ir *) dev->priv;
+
+	iobase = self->io.fir_base;
+
+	/* Save current bank */
+	set = inb(iobase+SSR);
+	switch_bank(iobase, SET0);
+	
+	icr = inb(iobase+ICR); 
+	isr = inb(iobase+ISR) & icr; /* Mask out the interesting ones */ 
+
+	outb(0, iobase+ICR); /* Disable interrupts */
+	
+	if (isr) {
+		/* Dispatch interrupt handler for the current speed */
+		if (self->io.speed > PIO_MAX_SPEED )
+			icr = w83977af_fir_interrupt(self, isr);
+		else
+			icr = w83977af_sir_interrupt(self, isr);
+	}
+
+	outb(icr, iobase+ICR);    /* Restore (new) interrupts */
+	outb(set, iobase+SSR);    /* Restore bank register */
+	return IRQ_RETVAL(isr);
+}
+
+/*
+ * Function w83977af_is_receiving (self)
+ *
+ *    Return TRUE is we are currently receiving a frame
+ *
+ */
+static int w83977af_is_receiving(struct w83977af_ir *self)
+{
+	int status = FALSE;
+	int iobase;
+	__u8 set;
+
+	IRDA_ASSERT(self != NULL, return FALSE;);
+
+	if (self->io.speed > 115200) {
+		iobase = self->io.fir_base;
+
+		/* Check if rx FIFO is not empty */
+		set = inb(iobase+SSR);
+		switch_bank(iobase, SET2);
+		if ((inb(iobase+RXFDTH) & 0x3f) != 0) {
+			/* We are receiving something */
+			status =  TRUE;
+		}
+		outb(set, iobase+SSR);
+	} else 
+		status = (self->rx_buff.state != OUTSIDE_FRAME);
+	
+	return status;
+}
+
+/*
+ * Function w83977af_net_open (dev)
+ *
+ *    Start the device
+ *
+ */
+static int w83977af_net_open(struct net_device *dev)
+{
+	struct w83977af_ir *self;
+	int iobase;
+	char hwname[32];
+	__u8 set;
+	
+	IRDA_DEBUG(0, "%s()\n", __FUNCTION__ );
+	
+	IRDA_ASSERT(dev != NULL, return -1;);
+	self = (struct w83977af_ir *) dev->priv;
+	
+	IRDA_ASSERT(self != NULL, return 0;);
+	
+	iobase = self->io.fir_base;
+
+	if (request_irq(self->io.irq, w83977af_interrupt, 0, dev->name, 
+			(void *) dev)) {
+		return -EAGAIN;
+	}
+	/*
+	 * Always allocate the DMA channel after the IRQ,
+	 * and clean up on failure.
+	 */
+	if (request_dma(self->io.dma, dev->name)) {
+		free_irq(self->io.irq, self);
+		return -EAGAIN;
+	}
+		
+	/* Save current set */
+	set = inb(iobase+SSR);
+
+ 	/* Enable some interrupts so we can receive frames again */
+ 	switch_bank(iobase, SET0);
+ 	if (self->io.speed > 115200) {
+ 		outb(ICR_EFSFI, iobase+ICR);
+ 		w83977af_dma_receive(self);
+ 	} else
+ 		outb(ICR_ERBRI, iobase+ICR);
+
+	/* Restore bank register */
+	outb(set, iobase+SSR);
+
+	/* Ready to play! */
+	netif_start_queue(dev);
+	
+	/* Give self a hardware name */
+	sprintf(hwname, "w83977af @ 0x%03x", self->io.fir_base);
+
+	/* 
+	 * Open new IrLAP layer instance, now that everything should be
+	 * initialized properly 
+	 */
+	self->irlap = irlap_open(dev, &self->qos, hwname);
+
+	return 0;
+}
+
+/*
+ * Function w83977af_net_close (dev)
+ *
+ *    Stop the device
+ *
+ */
+static int w83977af_net_close(struct net_device *dev)
+{
+	struct w83977af_ir *self;
+	int iobase;
+	__u8 set;
+
+	IRDA_DEBUG(0, "%s()\n", __FUNCTION__ );
+
+	IRDA_ASSERT(dev != NULL, return -1;);
+	
+	self = (struct w83977af_ir *) dev->priv;
+	
+	IRDA_ASSERT(self != NULL, return 0;);
+	
+	iobase = self->io.fir_base;
+
+	/* Stop device */
+	netif_stop_queue(dev);
+	
+	/* Stop and remove instance of IrLAP */
+	if (self->irlap)
+		irlap_close(self->irlap);
+	self->irlap = NULL;
+
+	disable_dma(self->io.dma);
+
+	/* Save current set */
+	set = inb(iobase+SSR);
+	
+	/* Disable interrupts */
+	switch_bank(iobase, SET0);
+	outb(0, iobase+ICR); 
+
+	free_irq(self->io.irq, dev);
+	free_dma(self->io.dma);
+
+	/* Restore bank register */
+	outb(set, iobase+SSR);
+
+	return 0;
+}
+
+/*
+ * Function w83977af_net_ioctl (dev, rq, cmd)
+ *
+ *    Process IOCTL commands for this device
+ *
+ */
+static int w83977af_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+	struct if_irda_req *irq = (struct if_irda_req *) rq;
+	struct w83977af_ir *self;
+	unsigned long flags;
+	int ret = 0;
+
+	IRDA_ASSERT(dev != NULL, return -1;);
+
+	self = dev->priv;
+
+	IRDA_ASSERT(self != NULL, return -1;);
+
+	IRDA_DEBUG(2, "%s(), %s, (cmd=0x%X)\n", __FUNCTION__ , dev->name, cmd);
+	
+	spin_lock_irqsave(&self->lock, flags);
+
+	switch (cmd) {
+	case SIOCSBANDWIDTH: /* Set bandwidth */
+		if (!capable(CAP_NET_ADMIN)) {
+			ret = -EPERM;
+			goto out;
+		}
+		w83977af_change_speed(self, irq->ifr_baudrate);
+		break;
+	case SIOCSMEDIABUSY: /* Set media busy */
+		if (!capable(CAP_NET_ADMIN)) {
+			ret = -EPERM;
+			goto out;
+		}
+		irda_device_set_media_busy(self->netdev, TRUE);
+		break;
+	case SIOCGRECEIVING: /* Check if we are receiving right now */
+		irq->ifr_receiving = w83977af_is_receiving(self);
+		break;
+	default:
+		ret = -EOPNOTSUPP;
+	}
+out:
+	spin_unlock_irqrestore(&self->lock, flags);
+	return ret;
+}
+
+static struct net_device_stats *w83977af_net_get_stats(struct net_device *dev)
+{
+	struct w83977af_ir *self = (struct w83977af_ir *) dev->priv;
+	
+	return &self->stats;
+}
+
+MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>");
+MODULE_DESCRIPTION("Winbond W83977AF IrDA Device Driver");
+MODULE_LICENSE("GPL");
+
+
+module_param(qos_mtt_bits, int, 0);
+MODULE_PARM_DESC(qos_mtt_bits, "Mimimum Turn Time");
+module_param_array(io, int, NULL, 0);
+MODULE_PARM_DESC(io, "Base I/O addresses");
+module_param_array(irq, int, NULL, 0);
+MODULE_PARM_DESC(irq, "IRQ lines");
+
+/*
+ * Function init_module (void)
+ *
+ *    
+ *
+ */
+module_init(w83977af_init);
+
+/*
+ * Function cleanup_module (void)
+ *
+ *    
+ *
+ */
+module_exit(w83977af_cleanup);
diff --git a/drivers/net/irda/w83977af_ir.h b/drivers/net/irda/w83977af_ir.h
new file mode 100644
index 0000000..0b7661d
--- /dev/null
+++ b/drivers/net/irda/w83977af_ir.h
@@ -0,0 +1,199 @@
+/*********************************************************************
+ *                
+ * Filename:      w83977af_ir.h
+ * Version:       
+ * Description:   
+ * Status:        Experimental.
+ * Author:        Paul VanderSpek
+ * Created at:    Thu Nov 19 13:55:34 1998
+ * Modified at:   Tue Jan 11 13:08:19 2000
+ * Modified by:   Dag Brattli <dagb@cs.uit.no>
+ * 
+ *     Copyright (c) 1998-2000 Dag Brattli, 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.
+ *     
+ ********************************************************************/
+
+#ifndef W83977AF_IR_H
+#define W83977AF_IR_H
+
+#include <asm/io.h>
+#include <linux/types.h>
+
+/* Flags for configuration register CRF0 */
+#define ENBNKSEL	0x01
+#define APEDCRC		0x02
+#define TXW4C           0x04
+#define RXW4C           0x08
+
+/* Bank 0 */
+#define RBR             0x00 /* Receiver buffer register */
+#define TBR             0x00 /* Transmitter buffer register */
+
+#define ICR		0x01 /* Interrupt configuration register */
+#define ICR_ERBRI       0x01 /* Receiver buffer register interrupt */
+#define ICR_ETBREI      0x02 /* Transeiver empty interrupt */
+#define ICR_EUSRI	0x04//* IR status interrupt */
+#define ICR_EHSRI       0x04
+#define ICR_ETXURI      0x04 /* Tx underrun */
+#define ICR_EDMAI	0x10 /* DMA interrupt */
+#define ICR_ETXTHI      0x20 /* Transmitter threshold interrupt */
+#define ICR_EFSFI       0x40 /* Frame status FIFO interrupt */
+#define ICR_ETMRI       0x80 /* Timer interrupt */
+
+#define UFR		0x02 /* FIFO control register */
+#define UFR_EN_FIFO     0x01 /* Enable FIFO's */
+#define UFR_RXF_RST     0x02 /* Reset Rx FIFO */
+#define UFR_TXF_RST     0x04 /* Reset Tx FIFO */
+#define UFR_RXTL	0x80 /* Rx FIFO threshold (set to 16) */
+#define UFR_TXTL	0x20 /* Tx FIFO threshold (set to 17) */
+
+#define ISR		0x02 /* Interrupt status register */
+#define ISR_RXTH_I	0x01 /* Receive threshold interrupt */
+#define ISR_TXEMP_I     0x02 /* Transmitter empty interrupt */
+#define ISR_FEND_I	0x04
+#define ISR_DMA_I	0x10
+#define ISR_TXTH_I	0x20 /* Transmitter threshold interrupt */
+#define ISR_FSF_I       0x40
+#define ISR_TMR_I       0x80 /* Timer interrupt */
+
+#define UCR             0x03 /* Uart control register */
+#define UCR_DLS8        0x03 /* 8N1 */
+
+#define SSR 	        0x03 /* Sets select register */
+#define SET0 	        UCR_DLS8        /* Make sure we keep 8N1 */
+#define SET1	        (0x80|UCR_DLS8) /* Make sure we keep 8N1 */
+#define SET2	        0xE0
+#define SET3	        0xE4
+#define SET4	        0xE8
+#define SET5	        0xEC
+#define SET6	        0xF0
+#define SET7	        0xF4
+
+#define HCR		0x04
+#define HCR_MODE_MASK	~(0xD0)
+#define HCR_SIR         0x60
+#define HCR_MIR_576  	0x20	
+#define HCR_MIR_1152	0x80
+#define HCR_FIR		0xA0
+#define HCR_EN_DMA	0x04
+#define HCR_EN_IRQ	0x08
+#define HCR_TX_WT	0x08
+
+#define USR             0x05 /* IR status register */
+#define USR_RDR         0x01 /* Receive data ready */
+#define USR_TSRE        0x40 /* Transmitter empty? */
+
+#define AUDR            0x07
+#define AUDR_SFEND      0x08 /* Set a frame end */
+#define AUDR_RXBSY      0x20 /* Rx busy */
+#define AUDR_UNDR       0x40 /* Transeiver underrun */
+
+/* Set 2 */
+#define ABLL            0x00 /* Advanced baud rate divisor latch (low byte) */
+#define ABHL            0x01 /* Advanced baud rate divisor latch (high byte) */
+
+#define ADCR1		0x02
+#define ADCR1_ADV_SL	0x01	
+#define ADCR1_D_CHSW	0x08	/* the specs are wrong. its bit 3, not 4 */
+#define ADCR1_DMA_F	0x02
+
+#define ADCR2		0x04
+#define ADCR2_TXFS32	0x01
+#define ADCR2_RXFS32	0x04
+
+#define RXFDTH          0x07
+
+/* Set 3 */
+#define AUID		0x00
+
+/* Set 4 */
+#define TMRL            0x00 /* Timer value register (low byte) */
+#define TMRH            0x01 /* Timer value register (high byte) */
+
+#define IR_MSL          0x02 /* Infrared mode select */
+#define IR_MSL_EN_TMR   0x01 /* Enable timer */
+
+#define TFRLL		0x04 /* Transmitter frame length (low byte) */
+#define TFRLH		0x05 /* Transmitter frame length (high byte) */
+#define RFRLL		0x06 /* Receiver frame length (low byte) */
+#define RFRLH		0x07 /* Receiver frame length (high byte) */
+
+/* Set 5 */
+
+#define FS_FO           0x05 /* Frame status FIFO */
+#define FS_FO_FSFDR     0x80 /* Frame status FIFO data ready */
+#define FS_FO_LST_FR    0x40 /* Frame lost */
+#define FS_FO_MX_LEX    0x10 /* Max frame len exceeded */
+#define FS_FO_PHY_ERR   0x08 /* Physical layer error */
+#define FS_FO_CRC_ERR   0x04 
+#define FS_FO_RX_OV     0x02 /* Receive overrun */
+#define FS_FO_FSF_OV    0x01 /* Frame status FIFO overrun */
+#define FS_FO_ERR_MSK   0x5f /* Error mask */
+
+#define RFLFL           0x06
+#define RFLFH           0x07
+
+/* Set 6 */
+#define IR_CFG2		0x00
+#define IR_CFG2_DIS_CRC	0x02
+
+/* Set 7 */
+#define IRM_CR		0x07 /* Infrared module control register */
+#define IRM_CR_IRX_MSL	0x40
+#define IRM_CR_AF_MNT   0x80 /* Automatic format */
+
+/* For storing entries in the status FIFO */
+struct st_fifo_entry {
+	int status;
+	int len;
+};
+
+struct st_fifo {
+	struct st_fifo_entry entries[10];
+	int head;
+	int tail;
+	int len;
+};
+
+/* Private data for each instance */
+struct w83977af_ir {
+	struct st_fifo st_fifo;
+
+	int tx_buff_offsets[10]; /* Offsets between frames in tx_buff */
+	int tx_len;          /* Number of frames in tx_buff */
+
+	struct net_device *netdev; /* Yes! we are some kind of netdevice */
+	struct net_device_stats stats;
+	
+	struct irlap_cb    *irlap; /* The link layer we are binded to */
+	struct qos_info     qos;   /* QoS capabilities for this device */
+	
+	chipio_t io;               /* IrDA controller information */
+	iobuff_t tx_buff;          /* Transmit buffer */
+	iobuff_t rx_buff;          /* Receive buffer */
+	dma_addr_t tx_buff_dma;
+	dma_addr_t rx_buff_dma;
+
+	/* Note : currently locking is *very* incomplete, but this
+	 * will get you started. Check in nsc-ircc.c for a proper
+	 * locking strategy. - Jean II */
+	spinlock_t lock;           /* For serializing operations */
+	
+	__u32 new_speed;
+};
+
+static inline void switch_bank( int iobase, int set)
+{
+	outb(set, iobase+SSR);
+}
+
+#endif