diff -urN linux-2.6.10-rc3-mm1/drivers/char/Kconfig linux-2.6.10-rc3-no2/drivers/char/Kconfig
--- linux-2.6.10-rc3-mm1/drivers/char/Kconfig	2004-12-22 17:08:10.000000000 -0500
+++ linux-2.6.10-rc3-no2/drivers/char/Kconfig	2004-12-22 19:37:08.967108274 -0500
@@ -600,6 +600,8 @@
 
 source "drivers/char/watchdog/Kconfig"
 
+source "drivers/char/lirc/Kconfig"
+
 config DS1620
 	tristate "NetWinder thermometer support"
 	depends on ARCH_NETWINDER
diff -urN linux-2.6.10-rc3-mm1/drivers/char/Kconfig.orig linux-2.6.10-rc3-no2/drivers/char/Kconfig.orig
--- linux-2.6.10-rc3-mm1/drivers/char/Kconfig.orig	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.10-rc3-no2/drivers/char/Kconfig.orig	2004-12-22 17:08:10.000000000 -0500
@@ -0,0 +1,1011 @@
+#
+# Character device configuration
+#
+
+menu "Character devices"
+
+config VT
+	bool "Virtual terminal" if EMBEDDED
+	select INPUT
+	default y if !VIOCONS
+	---help---
+	  If you say Y here, you will get support for terminal devices with
+	  display and keyboard devices. These are called "virtual" because you
+	  can run several virtual terminals (also called virtual consoles) on
+	  one physical terminal. This is rather useful, for example one
+	  virtual terminal can collect system messages and warnings, another
+	  one can be used for a text-mode user session, and a third could run
+	  an X session, all in parallel. Switching between virtual terminals
+	  is done with certain key combinations, usually Alt-<function key>.
+
+	  The setterm command ("man setterm") can be used to change the
+	  properties (such as colors or beeping) of a virtual terminal. The
+	  man page console_codes(4) ("man console_codes") contains the special
+	  character sequences that can be used to change those properties
+	  directly. The fonts used on virtual terminals can be changed with
+	  the setfont ("man setfont") command and the key bindings are defined
+	  with the loadkeys ("man loadkeys") command.
+
+	  You need at least one virtual terminal device in order to make use
+	  of your keyboard and monitor. Therefore, only people configuring an
+	  embedded system would want to say N here in order to save some
+	  memory; the only way to log into such a system is then via a serial
+	  or network connection.
+
+	  If unsure, say Y, or else you won't be able to do much with your new
+	  shiny Linux system :-)
+
+config VT_CONSOLE
+	bool "Support for console on virtual terminal" if EMBEDDED
+	depends on VT
+	default y
+	---help---
+	  The system console is the device which receives all kernel messages
+	  and warnings and which allows logins in single user mode. If you
+	  answer Y here, a virtual terminal (the device used to interact with
+	  a physical terminal) can be used as system console. This is the most
+	  common mode of operations, so you should say Y here unless you want
+	  the kernel messages be output only to a serial port (in which case
+	  you should say Y to "Console on serial port", below).
+
+	  If you do say Y here, by default the currently visible virtual
+	  terminal (/dev/tty0) will be used as system console. You can change
+	  that with a kernel command line option such as "console=tty3" which
+	  would use the third virtual terminal as system console. (Try "man
+	  bootparam" or see the documentation of your boot loader (lilo or
+	  loadlin) about how to pass options to the kernel at boot time.)
+
+	  If unsure, say Y.
+
+config HW_CONSOLE
+	bool
+	depends on VT && !S390 && !USERMODE
+	default y
+
+config SERIAL_NONSTANDARD
+	bool "Non-standard serial port support"
+	---help---
+	  Say Y here if you have any non-standard serial boards -- boards
+	  which aren't supported using the standard "dumb" serial driver.
+	  This includes intelligent serial boards such as Cyclades,
+	  Digiboards, etc. These are usually used for systems that need many
+	  serial ports because they serve many terminals or dial-in
+	  connections.
+
+	  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 non-standard serial boards.
+
+	  Most people can say N here.
+
+config COMPUTONE
+	tristate "Computone IntelliPort Plus serial support"
+	depends on SERIAL_NONSTANDARD && BROKEN_ON_SMP
+	---help---
+	  This driver supports the entire family of Intelliport II/Plus
+	  controllers with the exception of the MicroChannel controllers and
+	  products previous to the Intelliport II. These are multiport cards,
+	  which give you many serial ports. You would need something like this
+	  to connect more than two modems to your Linux box, for instance in
+	  order to become a dial-in server. If you have a card like that, say
+	  Y here and read <file:Documentation/computone.txt>.
+
+	  To compile this driver as modules, choose M here: the
+	  modules will be called ip2 and ip2main.
+
+config ROCKETPORT
+	tristate "Comtrol RocketPort support"
+	depends on SERIAL_NONSTANDARD
+	help
+	  This driver supports Comtrol RocketPort and RocketModem PCI boards.   
+          These boards provide 2, 4, 8, 16, or 32 high-speed serial ports or
+          modems.  For information about the RocketPort/RocketModem  boards
+          and this driver read <file:Documentation/rocket.txt>.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called rocket.
+
+	  If you want to compile this driver into the kernel, say Y here.  If
+          you don't have a Comtrol RocketPort/RocketModem card installed, say N.
+
+config CYCLADES
+	tristate "Cyclades async mux support"
+	depends on SERIAL_NONSTANDARD
+	---help---
+	  This driver supports Cyclades Z and Y multiserial boards.
+	  You would need something like this to connect more than two modems to
+	  your Linux box, for instance in order to become a dial-in server.
+
+	  For information about the Cyclades-Z card, read
+	  <file:drivers/char/README.cycladesZ>.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called cyclades.
+
+	  If you haven't heard about it, it's safe to say N.
+
+config CYZ_INTR
+	bool "Cyclades-Z interrupt mode operation (EXPERIMENTAL)"
+	depends on EXPERIMENTAL && CYCLADES
+	help
+	  The Cyclades-Z family of multiport cards allows 2 (two) driver op
+	  modes: polling and interrupt. In polling mode, the driver will check
+	  the status of the Cyclades-Z ports every certain amount of time
+	  (which is called polling cycle and is configurable). In interrupt
+	  mode, it will use an interrupt line (IRQ) in order to check the
+	  status of the Cyclades-Z ports. The default op mode is polling. If
+	  unsure, say N.
+
+config DIGIEPCA
+	tristate "Digiboard Intelligent Async Support"
+	depends on SERIAL_NONSTANDARD && BROKEN_ON_SMP
+	---help---
+	  This is a driver for Digi International's Xx, Xeve, and Xem series
+	  of cards which provide multiple serial ports. You would need
+	  something like this to connect more than two modems to your Linux
+	  box, for instance in order to become a dial-in server. This driver
+	  supports the original PC (ISA) boards as well as PCI, and EISA. If
+	  you have a card like this, say Y here and read the file
+	  <file:Documentation/digiepca.txt>.
+
+	  NOTE: There is another, separate driver for the Digiboard PC boards:
+	  "Digiboard PC/Xx Support" below. You should (and can) only select
+	  one of the two drivers.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called epca.
+
+config DIGI
+	tristate "Digiboard PC/Xx Support"
+	depends on SERIAL_NONSTANDARD && DIGIEPCA=n && BROKEN_ON_SMP
+	help
+	  This is a driver for the Digiboard PC/Xe, PC/Xi, and PC/Xeve cards
+	  that give you many serial ports. You would need something like this
+	  to connect more than two modems to your Linux box, for instance in
+	  order to become a dial-in server. If you have a card like that, say
+	  Y here and read the file <file:Documentation/digiboard.txt>.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called pcxx.
+
+config ESPSERIAL
+	tristate "Hayes ESP serial port support"
+	depends on SERIAL_NONSTANDARD && ISA && BROKEN_ON_SMP
+	help
+	  This is a driver which supports Hayes ESP serial ports.  Both single
+	  port cards and multiport cards are supported.  Make sure to read
+	  <file:Documentation/hayes-esp.txt>.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called esp.
+
+	  If unsure, say N.
+
+config MOXA_INTELLIO
+	tristate "Moxa Intellio support"
+	depends on SERIAL_NONSTANDARD && BROKEN_ON_SMP
+	help
+	  Say Y here if you have a Moxa Intellio multiport serial card.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called moxa.
+
+config MOXA_SMARTIO
+	tristate "Moxa SmartIO support"
+	depends on SERIAL_NONSTANDARD && BROKEN_ON_SMP
+	help
+	  Say Y here if you have a Moxa SmartIO multiport serial card.
+
+	  This driver can also be built as a module ( = code which can be
+	  inserted in and removed from the running kernel whenever you want).
+	  The module will be called mxser. If you want to do that, say M
+	  here.
+
+config ISI
+	tristate "Multi-Tech multiport card support (EXPERIMENTAL)"
+	depends on SERIAL_NONSTANDARD && PCI && EXPERIMENTAL && BROKEN_ON_SMP && m
+	help
+	  This is a driver for the Multi-Tech cards which provide several
+	  serial ports.  The driver is experimental and can currently only be
+	  built as a module. The module will be called isicom.
+	  If you want to do that, choose M here.
+
+config SYNCLINK
+	tristate "Microgate SyncLink card support"
+	depends on SERIAL_NONSTANDARD && PCI
+	help
+	  Provides support for the SyncLink ISA and PCI multiprotocol serial
+	  adapters. These adapters support asynchronous and HDLC bit
+	  synchronous communication up to 10Mbps (PCI adapter).
+
+	  This driver can only be built as a module ( = code which can be
+	  inserted in and removed from the running kernel whenever you want).
+	  The module will be called synclink.  If you want to do that, say M
+	  here.
+
+config SYNCLINKMP
+	tristate "SyncLink Multiport support"
+	depends on SERIAL_NONSTANDARD
+	help
+	  Enable support for the SyncLink Multiport (2 or 4 ports)
+	  serial adapter, running asynchronous and HDLC communications up
+	  to 2.048Mbps. Each ports is independently selectable for
+	  RS-232, V.35, RS-449, RS-530, and X.21
+
+	  This driver may be built as a module ( = code which can be
+	  inserted in and removed from the running kernel whenever you want).
+	  The module will be called synclinkmp.  If you want to do that, say M
+	  here.
+
+config N_HDLC
+	tristate "HDLC line discipline support"
+	depends on SERIAL_NONSTANDARD
+	help
+	  Allows synchronous HDLC communications with tty device drivers that
+	  support synchronous HDLC such as the Microgate SyncLink adapter.
+
+	  This driver can only be built as a module ( = code which can be
+	  inserted in and removed from the running kernel whenever you want).
+	  The module will be called n_hdlc. If you want to do that, say M
+	  here.
+
+config RISCOM8
+	tristate "SDL RISCom/8 card support"
+	depends on SERIAL_NONSTANDARD && BROKEN_ON_SMP
+	help
+	  This is a driver for the SDL Communications RISCom/8 multiport card,
+	  which gives you many serial ports. You would need something like
+	  this to connect more than two modems to your Linux box, for instance
+	  in order to become a dial-in server. If you have a card like that,
+	  say Y here and read the file <file:Documentation/riscom8.txt>.
+
+	  Also it's possible to say M here and compile this driver as kernel
+	  loadable module; the module will be called riscom8.
+
+config SPECIALIX
+	tristate "Specialix IO8+ card support"
+	depends on SERIAL_NONSTANDARD && BROKEN_ON_SMP
+	help
+	  This is a driver for the Specialix IO8+ multiport card (both the
+	  ISA and the PCI version) which gives you many serial ports. You
+	  would need something like this to connect more than two modems to
+	  your Linux box, for instance in order to become a dial-in server.
+
+	  If you have a card like that, say Y here and read the file
+	  <file:Documentation/specialix.txt>. Also it's possible to say M here
+	  and compile this driver as kernel loadable module which will be
+	  called specialix.
+
+config SPECIALIX_RTSCTS
+	bool "Specialix DTR/RTS pin is RTS"
+	depends on SPECIALIX
+	help
+	  The Specialix IO8+ card can only support either RTS or DTR. If you
+	  say N here, the driver will use the pin as "DTR" when the tty is in
+	  software handshake mode.  If you say Y here or hardware handshake is
+	  on, it will always be RTS.  Read the file
+	  <file:Documentation/specialix.txt> for more information.
+
+config SX
+	tristate "Specialix SX (and SI) card support"
+	depends on SERIAL_NONSTANDARD && BROKEN_ON_SMP
+	help
+	  This is a driver for the SX and SI multiport serial cards.
+	  Please read the file <file:Documentation/sx.txt> for details.
+
+	  This driver can only be built as a module ( = code which can be
+	  inserted in and removed from the running kernel whenever you want).
+	  The module will be called sx. If you want to do that, say M here.
+
+config RIO
+	tristate "Specialix RIO system support"
+	depends on SERIAL_NONSTANDARD && BROKEN_ON_SMP
+	help
+	  This is a driver for the Specialix RIO, a smart serial card which
+	  drives an outboard box that can support up to 128 ports.  Product
+	  information is at <http://www.perle.com/support/documentation.html#multiport>.
+	  There are both ISA and PCI versions.
+
+config RIO_OLDPCI
+	bool "Support really old RIO/PCI cards"
+	depends on RIO
+	help
+	  Older RIO PCI cards need some initialization-time configuration to
+	  determine the IRQ and some control addresses.  If you have a RIO and
+	  this doesn't seem to work, try setting this to Y.
+
+config STALDRV
+	bool "Stallion multiport serial support"
+	depends on SERIAL_NONSTANDARD
+	help
+	  Stallion cards give you many serial ports.  You would need something
+	  like this to connect more than two modems to your Linux box, for
+	  instance in order to become a dial-in server.  If you say Y here,
+	  you will be asked for your specific card model in the next
+	  questions.  Make sure to read <file:Documentation/stallion.txt> in
+	  this case.  If you have never heard about all this, it's safe to
+	  say N.
+
+config STALLION
+	tristate "Stallion EasyIO or EC8/32 support"
+	depends on STALDRV && BROKEN_ON_SMP
+	help
+	  If you have an EasyIO or EasyConnection 8/32 multiport Stallion
+	  card, then this is for you; say Y.  Make sure to read
+	  <file:Documentation/stallion.txt>.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called stallion.
+
+config ISTALLION
+	tristate "Stallion EC8/64, ONboard, Brumby support"
+	depends on STALDRV && BROKEN_ON_SMP
+	help
+	  If you have an EasyConnection 8/64, ONboard, Brumby or Stallion
+	  serial multiport card, say Y here. Make sure to read
+	  <file:Documentation/stallion.txt>.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called istallion.
+
+config SERIAL_TX3912
+	bool "TX3912/PR31700 serial port support"
+	depends on SERIAL_NONSTANDARD && MIPS && BROKEN_ON_SMP
+	help
+	  The TX3912 is a Toshiba RISC processor based o the MIPS 3900 core;
+	  see <http://www.toshiba.com/taec/components/Generic/risc/tx3912.htm>.
+	  Say Y here to enable kernel support for the on-board serial port.
+
+config SERIAL_TX3912_CONSOLE
+	bool "Console on TX3912/PR31700 serial port"
+	depends on SERIAL_TX3912
+	help
+	  The TX3912 is a Toshiba RISC processor based o the MIPS 3900 core;
+	  see <http://www.toshiba.com/taec/components/Generic/risc/tx3912.htm>.
+	  Say Y here to direct console I/O to the on-board serial port.
+
+config AU1000_UART
+	bool "Enable Au1000 UART Support"
+	depends on SERIAL_NONSTANDARD && MIPS
+	help
+	  If you have an Alchemy AU1000 processor (MIPS based) and you want
+	  to use serial ports, say Y.  Otherwise, say N.
+
+config AU1000_SERIAL_CONSOLE
+	bool "Enable Au1000 serial console"
+	depends on AU1000_UART
+	help
+	  If you have an Alchemy AU1000 processor (MIPS based) and you want
+	  to use a console on a serial port, say Y.  Otherwise, say N.
+
+config QTRONIX_KEYBOARD
+	bool "Enable Qtronix 990P Keyboard Support"
+	depends on IT8712
+	help
+	  Images of Qtronix keyboards are at
+	  <http://www.qtronix.com/keyboard.html>.
+
+config IT8172_CIR
+	bool
+	depends on QTRONIX_KEYBOARD
+	default y
+
+config IT8172_SCR0
+	bool "Enable Smart Card Reader 0 Support "
+	depends on IT8712
+	help
+	  Say Y here to support smart-card reader 0 (SCR0) on the Integrated
+	  Technology Express, Inc. ITE8172 SBC.  Vendor page at
+	  <http://www.ite.com.tw/ia/brief_it8172bsp.htm>; picture of the
+	  board at <http://www.mvista.com/partners/semiconductor/ite.html>.
+
+config IT8172_SCR1
+	bool "Enable Smart Card Reader 1 Support "
+	depends on IT8712
+	help
+	  Say Y here to support smart-card reader 1 (SCR1) on the Integrated
+	  Technology Express, Inc. ITE8172 SBC.  Vendor page at
+	  <http://www.ite.com.tw/ia/brief_it8172bsp.htm>; picture of the
+	  board at <http://www.mvista.com/partners/semiconductor/ite.html>.
+
+config A2232
+	tristate "Commodore A2232 serial support (EXPERIMENTAL)"
+	depends on EXPERIMENTAL && ZORRO && BROKEN_ON_SMP
+	---help---
+	  This option supports the 2232 7-port serial card shipped with the
+	  Amiga 2000 and other Zorro-bus machines, dating from 1989.  At
+	  a max of 19,200 bps, the ports are served by a 6551 ACIA UART chip
+	  each, plus a 8520 CIA, and a master 6502 CPU and buffer as well. The
+	  ports were connected with 8 pin DIN connectors on the card bracket,
+	  for which 8 pin to DB25 adapters were supplied. The card also had
+	  jumpers internally to toggle various pinning configurations.
+
+	  This driver can be built as a module; but then "generic_serial"
+	  will also be built as a module. This has to be loaded before
+	  "ser_a2232". If you want to do this, answer M here.
+
+config SGI_SNSC
+	bool "SGI Altix system controller communication support"
+	depends on (IA64_SGI_SN2 || IA64_GENERIC)
+	help
+	  If you have an SGI Altix and you want to enable system
+	  controller communication from user space (you want this!),
+	  say Y.  Otherwise, say N.
+
+source "drivers/serial/Kconfig"
+
+config UNIX98_PTYS
+	bool "Unix98 PTY support" if EMBEDDED
+	default y
+	---help---
+	  A pseudo terminal (PTY) is a software device consisting of two
+	  halves: a master and a slave. The slave device behaves identical to
+	  a physical terminal; the master device is used by a process to
+	  read data from and write data to the slave, thereby emulating a
+	  terminal. Typical programs for the master side are telnet servers
+	  and xterms.
+
+	  Linux has traditionally used the BSD-like names /dev/ptyxx for
+	  masters and /dev/ttyxx for slaves of pseudo terminals. This scheme
+	  has a number of problems. The GNU C library glibc 2.1 and later,
+	  however, supports the Unix98 naming standard: in order to acquire a
+	  pseudo terminal, a process opens /dev/ptmx; the number of the pseudo
+	  terminal is then made available to the process and the pseudo
+	  terminal slave can be accessed as /dev/pts/<number>. What was
+	  traditionally /dev/ttyp2 will then be /dev/pts/2, for example.
+
+	  All modern Linux systems use the Unix98 ptys.  Say Y unless
+	  you're on an embedded system and want to conserve memory.
+
+config LEGACY_PTYS
+	bool "Legacy (BSD) PTY support"
+	default y
+	---help---
+	  A pseudo terminal (PTY) is a software device consisting of two
+	  halves: a master and a slave. The slave device behaves identical to
+	  a physical terminal; the master device is used by a process to
+	  read data from and write data to the slave, thereby emulating a
+	  terminal. Typical programs for the master side are telnet servers
+	  and xterms.
+
+	  Linux has traditionally used the BSD-like names /dev/ptyxx
+	  for masters and /dev/ttyxx for slaves of pseudo
+	  terminals. This scheme has a number of problems, including
+	  security.  This option enables these legacy devices; on most
+	  systems, it is safe to say N.
+
+
+config LEGACY_PTY_COUNT
+	int "Maximum number of legacy PTY in use"
+	depends on LEGACY_PTYS
+	range 1 256
+	default "256"
+	---help---
+	  The maximum number of legacy PTYs that can be used at any one time.
+	  The default is 256, and should be more than enough.  Embedded
+	  systems may want to reduce this to save memory.
+
+	  When not in use, each legacy PTY occupies 12 bytes on 32-bit
+	  architectures and 24 bytes on 64-bit architectures.
+
+config PRINTER
+	tristate "Parallel printer support"
+	depends on PARPORT
+	---help---
+	  If you intend to attach a printer to the parallel port of your Linux
+	  box (as opposed to using a serial printer; if the connector at the
+	  printer has 9 or 25 holes ["female"], then it's serial), say Y.
+	  Also read the Printing-HOWTO, available from
+	  <http://www.tldp.org/docs.html#howto>.
+
+	  It is possible to share one parallel port among several devices
+	  (e.g. printer and ZIP drive) and it is safe to compile the
+	  corresponding drivers into the kernel.
+
+	  To compile this driver as a module, choose M here and read
+	  <file:Documentation/parport.txt>.  The module will be called lp.
+
+	  If you have several parallel ports, you can specify which ports to
+	  use with the "lp" kernel command line option.  (Try "man bootparam"
+	  or see the documentation of your boot loader (lilo or loadlin) about
+	  how to pass options to the kernel at boot time.)  The syntax of the
+	  "lp" command line option can be found in <file:drivers/char/lp.c>.
+
+	  If you have more than 8 printers, you need to increase the LP_NO
+	  macro in lp.c and the PARPORT_MAX macro in parport.h.
+
+config LP_CONSOLE
+	bool "Support for console on line printer"
+	depends on PRINTER
+	---help---
+	  If you want kernel messages to be printed out as they occur, you
+	  can have a console on the printer. This option adds support for
+	  doing that; to actually get it to happen you need to pass the
+	  option "console=lp0" to the kernel at boot time.
+
+	  If the printer is out of paper (or off, or unplugged, or too
+	  busy..) the kernel will stall until the printer is ready again.
+	  By defining CONSOLE_LP_STRICT to 0 (at your own risk) you
+	  can make the kernel continue when this happens,
+	  but it'll lose the kernel messages.
+
+	  If unsure, say N.
+
+config PPDEV
+	tristate "Support for user-space parallel port device drivers"
+	depends on PARPORT
+	---help---
+	  Saying Y to this adds support for /dev/parport device nodes.  This
+	  is needed for programs that want portable access to the parallel
+	  port, for instance deviceid (which displays Plug-and-Play device
+	  IDs).
+
+	  This is the parallel port equivalent of SCSI generic support (sg).
+	  It is safe to say N to this -- it is not needed for normal printing
+	  or parallel port CD-ROM/disk support.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ppdev.
+
+	  If unsure, say N.
+
+config TIPAR
+	tristate "Texas Instruments parallel link cable support"
+	depends on PARPORT
+	---help---
+	  If you own a Texas Instruments graphing calculator and use a
+	  parallel link cable, then you might be interested in this driver.
+
+	  If you enable this driver, you will be able to communicate with
+	  your calculator through a set of device nodes under /dev. The
+	  main advantage of this driver is that you don't have to be root
+	  to use this precise link cable (depending on the permissions on
+	  the device nodes, though).
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called tipar.
+
+	  If you don't know what a parallel link cable is or what a Texas
+	  Instruments graphing calculator is, then you probably don't need this
+	  driver.
+
+	  If unsure, say N.
+
+config HVC_CONSOLE
+	bool "pSeries Hypervisor Virtual Console support"
+	depends on PPC_PSERIES
+	help
+	  pSeries machines when partitioned support a hypervisor virtual
+	  console. This driver allows each pSeries partition to have a console
+	  which is accessed via the HMC.
+
+config HVCS
+	tristate "IBM Hypervisor Virtual Console Server support"
+	depends on PPC_PSERIES
+	help
+	  Partitionable IBM Power5 ppc64 machines allow hosting of
+	  firmware virtual consoles from one Linux partition by
+	  another Linux partition.  This driver allows console data
+	  from Linux partitions to be accessed through TTY device
+	  interfaces in the device tree of a Linux partition running
+	  this driver.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called hvcs.ko.  Additionally, this module
+	  will depend on arch specific APIs exported from hvcserver.ko
+	  which will also be compiled when this driver is built as a
+	  module.
+
+source "drivers/char/ipmi/Kconfig"
+
+source "drivers/char/watchdog/Kconfig"
+
+config DS1620
+	tristate "NetWinder thermometer support"
+	depends on ARCH_NETWINDER
+	help
+	  Say Y here to include support for the thermal management hardware
+	  found in the NetWinder. This driver allows the user to control the
+	  temperature set points and to read the current temperature.
+
+	  It is also possible to say M here to build it as a module (ds1620)
+	  It is recommended to be used on a NetWinder, but it is not a
+	  necessity.
+
+config NWBUTTON
+	tristate "NetWinder Button"
+	depends on ARCH_NETWINDER
+	---help---
+	  If you say Y here and create a character device node /dev/nwbutton
+	  with major and minor numbers 10 and 158 ("man mknod"), then every
+	  time the orange button is pressed a number of times, the number of
+	  times the button was pressed will be written to that device.
+
+	  This is most useful for applications, as yet unwritten, which
+	  perform actions based on how many times the button is pressed in a
+	  row.
+
+	  Do not hold the button down for too long, as the driver does not
+	  alter the behaviour of the hardware reset circuitry attached to the
+	  button; it will still execute a hard reset if the button is held
+	  down for longer than approximately five seconds.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called nwbutton.
+
+	  Most people will answer Y to this question and "Reboot Using Button"
+	  below to be able to initiate a system shutdown from the button.
+
+config NWBUTTON_REBOOT
+	bool "Reboot Using Button"
+	depends on NWBUTTON
+	help
+	  If you say Y here, then you will be able to initiate a system
+	  shutdown and reboot by pressing the orange button a number of times.
+	  The number of presses to initiate the shutdown is two by default,
+	  but this can be altered by modifying the value of NUM_PRESSES_REBOOT
+	  in nwbutton.h and recompiling the driver or, if you compile the
+	  driver as a module, you can specify the number of presses at load
+	  time with "insmod button reboot_count=<something>".
+
+config NWFLASH
+	tristate "NetWinder flash support"
+	depends on ARCH_NETWINDER
+	---help---
+	  If you say Y here and create a character device /dev/flash with
+	  major 10 and minor 160 you can manipulate the flash ROM containing
+	  the NetWinder firmware. Be careful as accidentally overwriting the
+	  flash contents can render your computer unbootable. On no account
+	  allow random users access to this device. :-)
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called nwflash.
+
+	  If you're not sure, say N.
+
+config HW_RANDOM
+	tristate "Intel/AMD/VIA HW Random Number Generator support"
+	depends on (X86 || IA64) && PCI
+	---help---
+	  This driver provides kernel-side support for the Random Number
+	  Generator hardware found on Intel i8xx-based motherboards,
+	  AMD 76x-based motherboards, and Via Nehemiah CPUs.
+
+	  Provides a character driver, used to read() entropy data.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called hw_random.
+
+	  If unsure, say N.
+
+config NVRAM
+	tristate "/dev/nvram support"
+	depends on ATARI || X86 || X86_64 || ARM || GENERIC_NVRAM
+	---help---
+	  If you say Y here and create a character special file /dev/nvram
+	  with major number 10 and minor number 144 using mknod ("man mknod"),
+	  you get read and write access to the extra bytes of non-volatile
+	  memory in the real time clock (RTC), which is contained in every PC
+	  and most Ataris.  The actual number of bytes varies, depending on the
+	  nvram in the system, but is usually 114 (128-14 for the RTC).
+
+	  This memory is conventionally called "CMOS RAM" on PCs and "NVRAM"
+	  on Ataris. /dev/nvram may be used to view settings there, or to
+	  change them (with some utility). It could also be used to frequently
+	  save a few bits of very important data that may not be lost over
+	  power-off and for which writing to disk is too insecure. Note
+	  however that most NVRAM space in a PC belongs to the BIOS and you
+	  should NEVER idly tamper with it. See Ralf Brown's interrupt list
+	  for a guide to the use of CMOS bytes by your BIOS.
+
+	  On Atari machines, /dev/nvram is always configured and does not need
+	  to be selected.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called nvram.
+
+config RTC
+	tristate "Enhanced Real Time Clock Support"
+	depends on !PPC32 && !PARISC && !IA64 && !M68K
+	---help---
+	  If you say Y here and create a character special file /dev/rtc with
+	  major number 10 and minor number 135 using mknod ("man mknod"), you
+	  will get access to the real time clock (or hardware clock) built
+	  into your computer.
+
+	  Every PC has such a clock built in. It can be used to generate
+	  signals from as low as 1Hz up to 8192Hz, and can also be used
+	  as a 24 hour alarm. It reports status information via the file
+	  /proc/driver/rtc and its behaviour is set by various ioctls on
+	  /dev/rtc.
+
+	  If you run Linux on a multiprocessor machine and said Y to
+	  "Symmetric Multi Processing" above, you should say Y here to read
+	  and set the RTC in an SMP compatible fashion.
+
+	  If you think you have a use for such a device (such as periodic data
+	  sampling), then say Y here, and read <file:Documentation/rtc.txt>
+	  for details.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called rtc.
+
+config SGI_DS1286
+	tristate "SGI DS1286 RTC support"
+	depends on SGI_IP22
+	help
+	  If you say Y here and create a character special file /dev/rtc with
+	  major number 10 and minor number 135 using mknod ("man mknod"), you
+	  will get access to the real time clock built into your computer.
+	  Every SGI has such a clock built in. It reports status information
+	  via the file /proc/rtc and its behaviour is set by various ioctls on
+	  /dev/rtc.
+
+config SGI_IP27_RTC
+	bool "SGI M48T35 RTC support"
+	depends on SGI_IP27
+	help
+	  If you say Y here and create a character special file /dev/rtc with
+	  major number 10 and minor number 135 using mknod ("man mknod"), you
+	  will get access to the real time clock built into your computer.
+	  Every SGI has such a clock built in. It reports status information
+	  via the file /proc/rtc and its behaviour is set by various ioctls on
+	  /dev/rtc.
+
+config GEN_RTC
+	tristate "Generic /dev/rtc emulation"
+	depends on RTC!=y && !IA64 && !ARM
+	---help---
+	  If you say Y here and create a character special file /dev/rtc with
+	  major number 10 and minor number 135 using mknod ("man mknod"), you
+	  will get access to the real time clock (or hardware clock) built
+	  into your computer.
+
+	  It reports status information via the file /proc/driver/rtc and its
+	  behaviour is set by various ioctls on /dev/rtc. If you enable the
+	  "extended RTC operation" below it will also provide an emulation
+	  for RTC_UIE which is required by some programs and may improve
+	  precision in some cases.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called genrtc.
+
+config GEN_RTC_X
+	bool "Extended RTC operation"
+	depends on GEN_RTC
+	help
+	  Provides an emulation for RTC_UIE which is required by some programs
+	  and may improve precision of the generic RTC support in some cases.
+
+config EFI_RTC
+	bool "EFI Real Time Clock Services"
+	depends on IA64
+
+config DS1302
+	tristate "DS1302 RTC support"
+	depends on M32R && (PLAT_M32700UT || PLAT_OPSPUT)
+	help
+	  If you say Y here and create a character special file /dev/rtc with
+	  major number 121 and minor number 0 using mknod ("man mknod"), you
+	  will get access to the real time clock (or hardware clock) built
+	  into your computer.
+
+config S3C2410_RTC
+	bool "S3C2410 RTC Driver"
+	depends on ARCH_S3C2410
+	help
+	  RTC (Realtime Clock) driver for the clock inbuilt into the
+	  Samsung S3C2410. This can provide periodic interrupt rates
+	  from 1Hz to 64Hz for user programs, and wakeup from Alarm.
+
+config COBALT_LCD
+	bool "Support for Cobalt LCD"
+	depends on MIPS_COBALT
+	help
+	  This option enables support for the LCD display and buttons found
+	  on Cobalt systems through a misc device.
+
+config DTLK
+	tristate "Double Talk PC internal speech card support"
+	help
+	  This driver is for the DoubleTalk PC, a speech synthesizer
+	  manufactured by RC Systems (<http://www.rcsys.com/>).  It is also
+	  called the `internal DoubleTalk'.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called dtlk.
+
+config R3964
+	tristate "Siemens R3964 line discipline"
+	---help---
+	  This driver allows synchronous communication with devices using the
+	  Siemens R3964 packet protocol. Unless you are dealing with special
+	  hardware like PLCs, you are unlikely to need this.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called n_r3964.
+
+	  If unsure, say N.
+
+config APPLICOM
+	tristate "Applicom intelligent fieldbus card support"
+	depends on PCI
+	---help---
+	  This driver provides the kernel-side support for the intelligent
+	  fieldbus cards made by Applicom International. More information
+	  about these cards can be found on the WWW at the address
+	  <http://www.applicom-int.com/>, or by email from David Woodhouse
+	  <dwmw2@infradead.org>.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called applicom.
+
+	  If unsure, say N.
+
+config SONYPI
+	tristate "Sony Vaio Programmable I/O Control Device support (EXPERIMENTAL)"
+	depends on EXPERIMENTAL && X86 && PCI && INPUT && !64BIT
+	---help---
+	  This driver enables access to the Sony Programmable I/O Control
+	  Device which can be found in many (all ?) Sony Vaio laptops.
+
+	  If you have one of those laptops, read
+	  <file:Documentation/sonypi.txt>, and say Y or M here.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called sonypi.
+
+
+menu "Ftape, the floppy tape device driver"
+
+config FTAPE
+	tristate "Ftape (QIC-80/Travan) support"
+	depends on BROKEN_ON_SMP && (ALPHA || X86)
+	---help---
+	  If you have a tape drive that is connected to your floppy
+	  controller, say Y here.
+
+	  Some tape drives (like the Seagate "Tape Store 3200" or the Iomega
+	  "Ditto 3200" or the Exabyte "Eagle TR-3") come with a "high speed"
+	  controller of their own. These drives (and their companion
+	  controllers) are also supported if you say Y here.
+
+	  If you have a special controller (such as the CMS FC-10, FC-20,
+	  Mountain Mach-II, or any controller that is based on the Intel 82078
+	  FDC like the high speed controllers by Seagate and Exabyte and
+	  Iomega's "Ditto Dash") you must configure it by selecting the
+	  appropriate entries from the "Floppy tape controllers" sub-menu
+	  below and possibly modify the default values for the IRQ and DMA
+	  channel and the IO base in ftape's configuration menu.
+
+	  If you want to use your floppy tape drive on a PCI-bus based system,
+	  please read the file <file:drivers/char/ftape/README.PCI>.
+
+	  The ftape kernel driver is also available as a runtime loadable
+	  module. To compile this driver as a module, choose M here: the
+	  module will be called ftape.
+
+	  Note that the Ftape-HOWTO is out of date (sorry) and documents the
+	  older version 2.08 of this software but still contains useful
+	  information.  There is a web page with more recent documentation at
+	  <http://www.instmath.rwth-aachen.de/~heine/ftape/>.  This page
+	  always contains the latest release of the ftape driver and useful
+	  information (backup software, ftape related patches and
+	  documentation, FAQ).  Note that the file system interface has
+	  changed quite a bit compared to previous versions of ftape.  Please
+	  read <file:Documentation/ftape.txt>.
+
+source "drivers/char/ftape/Kconfig"
+
+endmenu
+
+source "drivers/char/agp/Kconfig"
+
+source "drivers/char/drm/Kconfig"
+
+source "drivers/char/pcmcia/Kconfig"
+
+config MWAVE
+	tristate "ACP Modem (Mwave) support"
+	depends on X86
+	select SERIAL_8250
+	---help---
+	  The ACP modem (Mwave) for Linux is a WinModem. It is composed of a
+	  kernel driver and a user level application. Together these components
+	  support direct attachment to public switched telephone networks (PSTNs)
+	  and support selected world wide countries.
+
+	  This version of the ACP Modem driver supports the IBM Thinkpad 600E,
+	  600, and 770 that include on board ACP modem hardware.
+
+	  The modem also supports the standard communications port interface
+	  (ttySx) and is compatible with the Hayes AT Command Set.
+
+	  The user level application needed to use this driver can be found at
+	  the IBM Linux Technology Center (LTC) web site:
+	  <http://www.ibm.com/linux/ltc/>.
+
+	  If you own one of the above IBM Thinkpads which has the Mwave chipset
+	  in it, say Y.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called mwave.
+
+config SCx200_GPIO
+	tristate "NatSemi SCx200 GPIO Support"
+	depends on SCx200
+	help
+	  Give userspace access to the GPIO pins on the National
+	  Semiconductor SCx200 processors.
+
+	  If compiled as a module, it will be called scx200_gpio.
+
+config RAW_DRIVER
+	tristate "RAW driver (/dev/raw/rawN) (OBSOLETE)"
+	help
+	  The raw driver permits block devices to be bound to /dev/raw/rawN. 
+	  Once bound, I/O against /dev/raw/rawN uses efficient zero-copy I/O. 
+	  See the raw(8) manpage for more details.
+
+          The raw driver is deprecated and may be removed from 2.7
+          kernels.  Applications should simply open the device (eg /dev/hda1)
+          with the O_DIRECT flag.
+
+config HPET
+	bool "HPET - High Precision Event Timer" if (X86 || IA64)
+	default n
+	depends on ACPI
+	help
+	  If you say Y here, you will have a miscdevice named "/dev/hpet/".  Each
+	  open selects one of the timers supported by the HPET.  The timers are
+	  non-periodioc and/or periodic.
+
+config HPET_RTC_IRQ
+	bool "HPET Control RTC IRQ" if !HPET_EMULATE_RTC
+	default n
+	depends on HPET
+	help
+	  If you say Y here, you will disable RTC_IRQ in drivers/char/rtc.c. It
+	  is assumed the platform called hpet_alloc with the RTC IRQ values for
+	  the HPET timers.
+
+config HPET_MMAP
+	bool "Allow mmap of HPET"
+	default y
+	depends on HPET
+	help
+	  If you say Y here, user applications will be able to mmap
+	  the HPET registers.
+
+	  In some hardware implementations, the page containing HPET
+	  registers may also contain other things that shouldn't be
+	  exposed to the user.  If this applies to your hardware,
+	  say N here.
+
+config MAX_RAW_DEVS
+	int "Maximum number of RAW devices to support (1-8192)"
+	depends on RAW_DRIVER
+	default "256"
+	help
+	  The maximum number of RAW devices that are supported.
+	  Default is 256. Increase this number in case you need lots of
+	  raw devices.
+
+config HANGCHECK_TIMER
+	tristate "Hangcheck timer"
+	depends on X86_64 || X86
+	help
+	  The hangcheck-timer module detects when the system has gone
+	  out to lunch past a certain margin.  It can reboot the system
+	  or merely print a warning.
+
+config MMTIMER
+	tristate "MMTIMER Memory mapped RTC for SGI Altix"
+	depends on IA64_GENERIC || IA64_SGI_SN2
+	default y
+	help
+	  The mmtimer device allows direct userspace access to the
+	  Altix system timer.
+
+endmenu
+
diff -urN linux-2.6.10-rc3-mm1/drivers/char/Makefile linux-2.6.10-rc3-no2/drivers/char/Makefile
--- linux-2.6.10-rc3-mm1/drivers/char/Makefile	2004-12-22 17:08:10.000000000 -0500
+++ linux-2.6.10-rc3-no2/drivers/char/Makefile	2004-12-22 19:37:08.969107996 -0500
@@ -7,7 +7,7 @@
 #
 FONTMAPFILE = cp437.uni
 
-obj-y	 += mem.o random.o tty_io.o n_tty.o tty_ioctl.o
+obj-y	 += mem.o random.o tty_io.o n_tty.o tty_ioctl.o lirc/
 
 obj-$(CONFIG_LEGACY_PTYS)	+= pty.o
 obj-$(CONFIG_UNIX98_PTYS)	+= pty.o
diff -urN linux-2.6.10-rc3-mm1/drivers/char/Makefile.orig linux-2.6.10-rc3-no2/drivers/char/Makefile.orig
--- linux-2.6.10-rc3-mm1/drivers/char/Makefile.orig	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.10-rc3-no2/drivers/char/Makefile.orig	2004-12-22 17:08:10.000000000 -0500
@@ -0,0 +1,119 @@
+#
+# Makefile for the kernel character device drivers.
+#
+
+#
+# This file contains the font map for the default (hardware) font
+#
+FONTMAPFILE = cp437.uni
+
+obj-y	 += mem.o random.o tty_io.o n_tty.o tty_ioctl.o
+
+obj-$(CONFIG_LEGACY_PTYS)	+= pty.o
+obj-$(CONFIG_UNIX98_PTYS)	+= pty.o
+obj-y				+= misc.o
+obj-$(CONFIG_VT)		+= vt_ioctl.o vc_screen.o consolemap.o \
+				   consolemap_deftbl.o selection.o keyboard.o
+obj-$(CONFIG_HW_CONSOLE)	+= vt.o defkeymap.o
+obj-$(CONFIG_MAGIC_SYSRQ)	+= sysrq.o
+obj-$(CONFIG_ESPSERIAL)		+= esp.o
+obj-$(CONFIG_MVME147_SCC)	+= generic_serial.o vme_scc.o
+obj-$(CONFIG_MVME162_SCC)	+= generic_serial.o vme_scc.o
+obj-$(CONFIG_BVME6000_SCC)	+= generic_serial.o vme_scc.o
+obj-$(CONFIG_SERIAL_TX3912)	+= generic_serial.o serial_tx3912.o
+obj-$(CONFIG_ROCKETPORT)	+= rocket.o
+obj-$(CONFIG_SERIAL167)		+= serial167.o
+obj-$(CONFIG_CYCLADES)		+= cyclades.o
+obj-$(CONFIG_STALLION)		+= stallion.o
+obj-$(CONFIG_ISTALLION)		+= istallion.o
+obj-$(CONFIG_DIGI)		+= pcxx.o
+obj-$(CONFIG_DIGIEPCA)		+= epca.o
+obj-$(CONFIG_SPECIALIX)		+= specialix.o
+obj-$(CONFIG_MOXA_INTELLIO)	+= moxa.o
+obj-$(CONFIG_A2232)		+= ser_a2232.o generic_serial.o
+obj-$(CONFIG_ATARI_DSP56K)	+= dsp56k.o
+obj-$(CONFIG_MOXA_SMARTIO)	+= mxser.o
+obj-$(CONFIG_COMPUTONE)		+= ip2.o ip2main.o
+obj-$(CONFIG_RISCOM8)		+= riscom8.o
+obj-$(CONFIG_ISI)		+= isicom.o
+obj-$(CONFIG_SYNCLINK)		+= synclink.o
+obj-$(CONFIG_SYNCLINKMP)	+= synclinkmp.o
+obj-$(CONFIG_N_HDLC)		+= n_hdlc.o
+obj-$(CONFIG_AMIGA_BUILTIN_SERIAL) += amiserial.o
+obj-$(CONFIG_SX)		+= sx.o generic_serial.o
+obj-$(CONFIG_RIO)		+= rio/ generic_serial.o
+obj-$(CONFIG_HVC_CONSOLE)	+= hvc_console.o hvsi.o
+obj-$(CONFIG_RAW_DRIVER)	+= raw.o
+obj-$(CONFIG_SGI_SNSC)		+= snsc.o
+obj-$(CONFIG_MMTIMER)		+= mmtimer.o
+obj-$(CONFIG_VIOCONS) += viocons.o
+obj-$(CONFIG_VIOTAPE)		+= viotape.o
+obj-$(CONFIG_HVCS)		+= hvcs.o
+
+obj-$(CONFIG_PRINTER) += lp.o
+obj-$(CONFIG_TIPAR) += tipar.o
+
+obj-$(CONFIG_DTLK) += dtlk.o
+obj-$(CONFIG_R3964) += n_r3964.o
+obj-$(CONFIG_APPLICOM) += applicom.o
+obj-$(CONFIG_SONYPI) += sonypi.o
+obj-$(CONFIG_RTC) += rtc.o
+obj-$(CONFIG_HPET) += hpet.o
+obj-$(CONFIG_GEN_RTC) += genrtc.o
+obj-$(CONFIG_EFI_RTC) += efirtc.o
+obj-$(CONFIG_SGI_DS1286) += ds1286.o
+obj-$(CONFIG_SGI_IP27_RTC) += ip27-rtc.o
+obj-$(CONFIG_DS1302) += ds1302.o
+obj-$(CONFIG_S3C2410_RTC) += s3c2410-rtc.o
+ifeq ($(CONFIG_GENERIC_NVRAM),y)
+  obj-$(CONFIG_NVRAM) += generic_nvram.o
+else
+  obj-$(CONFIG_NVRAM) += nvram.o
+endif
+obj-$(CONFIG_TOSHIBA) += toshiba.o
+obj-$(CONFIG_I8K) += i8k.o
+obj-$(CONFIG_DS1620) += ds1620.o
+obj-$(CONFIG_HW_RANDOM) += hw_random.o
+obj-$(CONFIG_QIC02_TAPE) += tpqic02.o
+obj-$(CONFIG_FTAPE) += ftape/
+obj-$(CONFIG_COBALT_LCD) += lcd.o
+obj-$(CONFIG_PPDEV) += ppdev.o
+obj-$(CONFIG_NWBUTTON) += nwbutton.o
+obj-$(CONFIG_NWFLASH) += nwflash.o
+obj-$(CONFIG_SCx200_GPIO) += scx200_gpio.o
+
+obj-$(CONFIG_WATCHDOG)	+= watchdog/
+obj-$(CONFIG_MWAVE) += mwave/
+obj-$(CONFIG_AGP) += agp/
+obj-$(CONFIG_DRM) += drm/
+obj-$(CONFIG_PCMCIA) += pcmcia/
+obj-$(CONFIG_IPMI_HANDLER) += ipmi/
+
+obj-$(CONFIG_HANGCHECK_TIMER) += hangcheck-timer.o
+
+# Files generated that shall be removed upon make clean
+clean-files := consolemap_deftbl.c defkeymap.c qtronixmap.c
+
+quiet_cmd_conmk = CONMK   $@
+      cmd_conmk = scripts/conmakehash $< > $@
+
+$(obj)/consolemap_deftbl.c: $(src)/$(FONTMAPFILE)
+	$(call cmd,conmk)
+
+$(obj)/defkeymap.o:  $(obj)/defkeymap.c
+
+$(obj)/qtronixmap.o: $(obj)/qtronixmap.c
+
+# Uncomment if you're changing the keymap and have an appropriate
+# loadkeys version for the map. By default, we'll use the shipped
+# versions.
+# GENERATE_KEYMAP := 1
+
+ifdef GENERATE_KEYMAP
+
+$(obj)/defkeymap.c $(obj)/qtronixmap.c: $(obj)/%.c: $(src)/%.map
+	loadkeys --mktable $< > $@.tmp
+	sed -e 's/^static *//' $@.tmp > $@
+	rm $@.tmp
+
+endif
diff -urN linux-2.6.10-rc3-mm1/drivers/char/lirc/Kconfig linux-2.6.10-rc3-no2/drivers/char/lirc/Kconfig
--- linux-2.6.10-rc3-mm1/drivers/char/lirc/Kconfig	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.10-rc3-no2/drivers/char/lirc/Kconfig	2004-12-22 19:37:08.937112445 -0500
@@ -0,0 +1,205 @@
+# LIRC http://lirc.sf.net/
+
+menu "Linux InfraRed Controller"
+
+config LIRC_SUPPORT
+	tristate "Linux InfraRed Controller"
+
+	config LIRC_MAX_DEV
+		int "Maximum LIRC devices"
+		default "2"
+		depends on LIRC_SUPPORT
+
+	config LIRC_I2C
+		tristate "I2C Driver"
+		depends on LIRC_SUPPORT && VIDEO_BT848 && I2C && I2C_ALGOBIT
+		help
+		  Say Y here if you need support for the following cards:
+
+		  Pixelview IR
+		  Hauppauage IR
+		  PV951 IR
+		  TV-Box IR
+		  KNC ONE IR
+
+		  If these dont make sense to you, then dont use the module.
+
+	config LIRC_GPIO
+		tristate "GPIO Driver"
+		depends on LIRC_SUPPORT && VIDEO_BT848
+
+	config LIRC_BT829
+		tristate "BT829 Driver"
+		depends on LIRC_SUPPORT
+
+	config LIRC_IT87
+		tristate "IT87 Driver"
+		depends on LIRC_SUPPORT
+
+	config LIRC_SASEM
+		tristate "SASEM Driver"
+		depends on LIRC_SUPPORT
+
+	config LIRC_ATIUSB
+		tristate "ATI USB Driver"
+		depends on LIRC_SUPPORT && USB
+
+	config LIRC_IGORPLUGUSB
+		tristate "IGORPLUG USB Driver"
+		depends on LIRC_SUPPORT && USB
+
+	config LIRC_MCEUSB
+		tristate "MCE USB Driver"
+		depends on LIRC_SUPPORT && USB
+
+	config LIRC_PARALLEL
+		tristate "Parallel Driver"
+		depends on LIRC_SUPPORT && !SMP && PARPORT
+
+		choice
+			prompt "Parallel Port"
+			depends on LIRC_PARALLEL
+			config LIRC_PARALLEL_LPT1
+				bool "LPT1 (0x378, 7)"
+			config LIRC_PARALLEL_LPT2
+				bool "LPT2 (0x278, 5)"
+			config LIRC_PARALLEL_LPT3
+				bool "COM3 (0x3bc, none)"
+			config LIRC_PARALLEL_OTHER
+				bool "Other (custom values)"
+		endchoice
+
+		config LIRC_PORT_PARALLEL
+			hex "I/O Port"
+				default "0x378" if LIRC_PARALLEL_LPT1
+				default "0x278" if LIRC_PARALLEL_LPT2
+				default "0x3bc" if LIRC_PARALLEL_LPT3
+			depends on LIRC_PARALLEL
+
+		config LIRC_IRQ_PARALLEL
+			hex "IRQ"
+				default "7" if LIRC_PARALLEL_LPT1
+				default "5" if LIRC_PARALLEL_LPT2
+			depends on LIRC_PARALLEL
+
+		config LIRC_TIMER
+			int "Timer"
+			default "65535"
+			depends on LIRC_PARALLEL
+
+	config LIRC_SERIAL
+		tristate "Serial Driver"
+		depends on LIRC_SUPPORT && SERIAL_8250
+
+		choice
+			prompt "Serial Receiver Type"
+			depends on LIRC_SERIAL
+
+			config LIRC_HOMEBREW
+				bool "Homebrew"
+
+			config LIRC_SERIAL_ANIMAX
+				bool "Animax"
+
+			config LIRC_SERIAL_IRDEO
+				bool "IRdeo"
+
+			config LIRC_SERIAL_IRDEO_REMOTE
+				bool "IRdeo Remote"
+
+		endchoice
+
+		config LIRC_SERIAL_TRANSMITTER
+			bool "With transmitter diode"
+			depends on LIRC_SERIAL && !LIRC_SERIAL_ANIMAX
+
+		config LIRC_SERIAL_SOFTCARRIER
+			bool "With software carrier"
+			depends on LIRC_SERIAL_TRANSMITTER
+
+		config LIRC_SERIAL_IGOR
+			bool "Igor Ceska's variation"
+			depends on LIRC_SERIAL
+
+		choice
+			prompt "Serial Port"
+			depends on LIRC_SERIAL
+			config LIRC_SERIAL_COM1
+				bool "COM1 (0x3f8, 4)"
+			config LIRC_SERIAL_COM2
+				bool "COM2 (0x2f8, 3)"
+			config LIRC_SERIAL_COM3
+				bool "COM3 (0x3e8, 4)"
+			config LIRC_SERIAL_COM4
+				bool "COM4 (0x2e8, 3)"
+			config LIRC_SERIAL_OTHER
+				bool "Other (custom values)"
+		endchoice
+
+		config LIRC_PORT_SERIAL
+			hex "I/O Port"
+				default "0x3f8" if LIRC_SERIAL_COM1
+				default "0x2f8" if LIRC_SERIAL_COM2
+				default "0x3e8" if LIRC_SERIAL_COM3
+				default "0x2e8" if LIRC_SERIAL_COM4
+			depends on LIRC_SERIAL
+
+		config LIRC_IRQ_SERIAL
+			hex "IRQ"
+				default "4" if LIRC_SERIAL_COM1 || LIRC_SERIAL_COM3
+				default "3" if LIRC_SERIAL_COM2 || LIRC_SERIAL_COM4
+			depends on LIRC_SERIAL
+
+	config LIRC_SIR
+		tristate "SIR Driver"
+		depends on LIRC_SUPPORT
+
+		config LIRC_ON_SA1100
+			bool "LIRC driver for StrongARM SA1100 embedded microprocessor"
+			depends on LIRC_SIR
+
+		choice
+			prompt "SIR Type"
+			depends on LIRC_SIR && !LIRC_ON_SA1100
+
+			config LIRC_SIR_IRDA
+				bool "SIR IrDA (built-in IR ports)"
+
+			config LIRC_SIR_TEKRAM
+				bool "Tekram Irmate 210 (16x50 UART compatible serial port)"
+
+			config LIRC_SIR_ACTISYS_ACT200L
+				bool "Actisys Act200L SIR driver support"
+
+		endchoice
+
+		choice
+			prompt "Serial Port"
+			depends on LIRC_SIR
+			config LIRC_SIR_COM1
+				bool "COM1 (0x3f8, 4)"
+			config LIRC_SIR_COM2
+				bool "COM2 (0x2f8, 3)"
+			config LIRC_SIR_COM3
+				bool "COM3 (0x3e8, 4)"
+			config LIRC_SIR_COM4
+				bool "COM4 (0x2e8, 3)"
+			config LIRC_SIR_OTHER
+				bool "Other (custom values)"
+		endchoice
+
+		config LIRC_PORT_SIR
+			hex "I/O Port"
+				default "0x3f8" if LIRC_SIR_COM1
+				default "0x2f8" if LIRC_SIR_COM2
+				default "0x3e8" if LIRC_SIR_COM3
+				default "0x2e8" if LIRC_SIR_COM4
+			depends on LIRC_SIR
+
+		config LIRC_IRQ_SIR
+			hex "IRQ"
+				default "4" if LIRC_SIR_COM1 || LIRC_SIR_COM3
+				default "3" if LIRC_SIR_COM2 || LIRC_SIR_COM4
+			depends on LIRC_SIR
+
+endmenu
diff -urN linux-2.6.10-rc3-mm1/drivers/char/lirc/Makefile linux-2.6.10-rc3-no2/drivers/char/lirc/Makefile
--- linux-2.6.10-rc3-mm1/drivers/char/lirc/Makefile	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.10-rc3-no2/drivers/char/lirc/Makefile	2004-12-22 19:37:08.938112306 -0500
@@ -0,0 +1,16 @@
+#
+# Makefile for the lirc drivers
+#
+
+obj-$(CONFIG_LIRC_SUPPORT) += lirc_dev.o
+obj-$(CONFIG_LIRC_GPIO) += lirc_gpio.o
+obj-$(CONFIG_LIRC_BT829) += lirc_bt829.o
+obj-$(CONFIG_LIRC_IT87) += lirc_it87.o
+obj-$(CONFIG_LIRC_SASEM) += lirc_sasem.o
+obj-$(CONFIG_LIRC_PARALLEL) += lirc_parallel.o
+obj-$(CONFIG_LIRC_SERIAL) += lirc_serial.o
+obj-$(CONFIG_LIRC_SIR) += lirc_sir.o
+obj-$(CONFIG_LIRC_ATIUSB) += lirc_atiusb.o
+obj-$(CONFIG_LIRC_IGORPLUGUSB) += lirc_igorplugusb.o
+obj-$(CONFIG_LIRC_MCEUSB) += lirc_mceusb.o
+obj-$(CONFIG_LIRC_I2C) += lirc_i2c.o
diff -urN linux-2.6.10-rc3-mm1/drivers/char/lirc/lirc_atiusb.c linux-2.6.10-rc3-no2/drivers/char/lirc/lirc_atiusb.c
--- linux-2.6.10-rc3-mm1/drivers/char/lirc/lirc_atiusb.c	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.10-rc3-no2/drivers/char/lirc/lirc_atiusb.c	2004-12-22 19:35:16.549733131 -0500
@@ -0,0 +1,1278 @@
+/* lirc_atiusb - USB remote support for LIRC
+ * (currently only supports X10 USB remotes)
+ * (supports ATI Remote Wonder and ATI Remote Wonder II, too)
+ *
+ * Copyright (C) 2003-2004 Paul Miller <pmiller9@users.sourceforge.net>
+ *
+ * This driver was derived from:
+ *   Vladimir Dergachev <volodya@minspring.com>'s 2002
+ *      "USB ATI Remote support" (input device)
+ *   Adrian Dewhurst <sailor-lk@sailorfrag.net>'s 2002
+ *      "USB StreamZap remote driver" (LIRC)
+ *   Artur Lipowski <alipowski@kki.net.pl>'s 2002
+ *      "lirc_dev" and "lirc_gpio" LIRC modules
+ *
+ * $Id: lirc_atiusb.c,v 1.44 2004/12/01 01:27:54 pmiller9 Exp $
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/version.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 0)
+#error "*******************************************************"
+#error "Sorry, this driver needs kernel version 2.4.0 or higher"
+#error "*******************************************************"
+#endif
+
+#include <linux/config.h>
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/kmod.h>
+#include <linux/smp_lock.h>
+#include <linux/completion.h>
+#include <asm/uaccess.h>
+#include <linux/usb.h>
+#include <linux/poll.h>
+#include <linux/wait.h>
+#include <linux/list.h>
+
+#include "linux/lirc.h"
+#include "linux/kcompat.h"
+#include "drivers/lirc_dev/lirc_dev.h"
+
+#define DRIVER_VERSION		"0.4"
+#define DRIVER_AUTHOR		"Paul Miller <pmiller9@users.sourceforge.net>"
+#define DRIVER_DESC		"USB remote driver for LIRC"
+#define DRIVER_NAME		"lirc_atiusb"
+
+#define CODE_LENGTH		5
+#define CODE_LENGTH_ATI2	3
+#define CODE_MIN_LENGTH		3
+#define USB_BUFLEN		(CODE_LENGTH*4)
+
+#define RW2_MODENAV_KEYCODE	0x3F
+#define RW2_NULL_MODE		0xFF
+/* Fake (virtual) keycode indicating compass mouse usage */
+#define RW2_MOUSE_KEYCODE	0xFF
+#define RW2_PRESSRELEASE_KEYCODE	0xFE
+
+#define RW2_PRESS_CODE		1
+#define RW2_HOLD_CODE		2
+#define RW2_RELEASE_CODE	0
+
+/* module parameters */
+#ifdef CONFIG_USB_DEBUG
+	static int debug = 1;
+#else
+	static int debug = 0;
+#endif
+#define dprintk(fmt, args...)                                 \
+	do{                                                   \
+		if(debug) printk(KERN_DEBUG fmt, ## args);    \
+	}while(0)
+
+static int mask = 0xFFFF;	// channel acceptance bit mask
+static int unique = 0;		// enable channel-specific codes
+static int repeat = 10;		// repeat time in 1/100 sec
+static int emit_updown = 0;	// send seperate press/release codes (rw2)
+static int emit_modekeys = 0;	// send keycodes for aux1-aux4, pc, and mouse (rw2)
+static unsigned long repeat_jiffies; // repeat timeout
+static int mdeadzone = 0;	// mouse sensitivity >= 0
+static int mgradient = 375;	// 1000*gradient from cardinal direction
+
+/* get hi and low bytes of a 16-bits int */
+#define HI(a)			((unsigned char)((a) >> 8))
+#define LO(a)			((unsigned char)((a) & 0xff))
+
+/* lock irctl structure */
+#define IRLOCK			down_interruptible(&ir->lock)
+#define IRUNLOCK		up(&ir->lock)
+
+/* general constants */
+#define SUCCESS			0
+#define SEND_FLAG_IN_PROGRESS	1
+#define SEND_FLAG_COMPLETE	2
+#define FREE_ALL		0xFF
+
+/* endpoints */
+#define EP_KEYS			0
+#define EP_MOUSE		1
+#define EP_MOUSE_ADDR		0x81
+#define EP_KEYS_ADDR		0x82
+
+#define VENDOR_ATI1		0x0bc7
+#define VENDOR_ATI2		0x0471
+
+static struct usb_device_id usb_remote_table [] = {
+	{ USB_DEVICE(VENDOR_ATI1, 0x0002) },	/* X10 USB Firecracker Interface */
+	{ USB_DEVICE(VENDOR_ATI1, 0x0003) },	/* X10 VGA Video Sender */
+	{ USB_DEVICE(VENDOR_ATI1, 0x0004) },	/* ATI Wireless Remote Receiver */
+	{ USB_DEVICE(VENDOR_ATI1, 0x0005) },	/* NVIDIA Wireless Remote Receiver */
+	{ USB_DEVICE(VENDOR_ATI1, 0x0006) },	/* ATI Wireless Remote Receiver */
+	{ USB_DEVICE(VENDOR_ATI1, 0x0007) },	/* X10 USB Wireless Transceiver */
+	{ USB_DEVICE(VENDOR_ATI1, 0x0008) },	/* X10 USB Wireless Transceiver */
+	{ USB_DEVICE(VENDOR_ATI1, 0x0009) },	/* X10 USB Wireless Transceiver */
+	{ USB_DEVICE(VENDOR_ATI1, 0x000A) },	/* X10 USB Wireless Transceiver */
+	{ USB_DEVICE(VENDOR_ATI1, 0x000B) },	/* X10 USB Transceiver */
+	{ USB_DEVICE(VENDOR_ATI1, 0x000C) },	/* X10 USB Transceiver */
+	{ USB_DEVICE(VENDOR_ATI1, 0x000D) },	/* X10 USB Transceiver */
+	{ USB_DEVICE(VENDOR_ATI1, 0x000E) },	/* X10 USB Transceiver */
+	{ USB_DEVICE(VENDOR_ATI1, 0x000F) },	/* X10 USB Transceiver */
+
+	{ USB_DEVICE(VENDOR_ATI2, 0x0602) },	/* ATI Remote Wonder 2: Input Device */
+	{ USB_DEVICE(VENDOR_ATI2, 0x0603) },	/* ATI Remote Wonder 2: Controller (???) */
+
+	{ }					/* Terminating entry */
+};
+
+
+
+
+
+
+struct in_endpt {
+	/* inner link in list of endpoints for the remote specified by ir */
+	struct list_head iep_list_link;
+	struct irctl *ir;
+	struct urb *urb;
+	struct usb_endpoint_descriptor *ep;
+	int type;
+
+	/* buffers and dma */
+	unsigned char *buf;
+	unsigned int len;
+#ifdef KERNEL_2_5
+	dma_addr_t dma;
+#endif
+
+	/* handle repeats */
+	unsigned char old[CODE_LENGTH];
+	unsigned long old_jiffies;
+};
+
+struct out_endpt {
+	struct irctl *ir;
+	struct urb *urb;
+	struct usb_endpoint_descriptor *ep;
+
+	/* buffers and dma */
+	unsigned char *buf;
+#ifdef KERNEL_2_5
+	dma_addr_t dma;
+#endif
+
+	/* handle sending (init strings) */
+	int send_flags;
+	wait_queue_head_t wait;
+};
+
+
+/* data structure for each usb remote */
+struct irctl {
+	/* inner link in list of all remotes managed by this module */
+	struct list_head remote_list_link;
+	/* Number of usb interfaces associated with this device */
+	int dev_refcount;
+
+	/* usb */
+	struct usb_device *usbdev;
+	/* Head link to list of all inbound endpoints in this remote */
+	struct list_head iep_listhead;
+	struct out_endpt *out_init;
+	int devnum;
+
+	/* remote type based on usb_device_id tables */
+	enum {
+		ATI1_COMPATIBLE,
+		ATI2_COMPATIBLE
+	} remote_type;
+
+	/* rw2 current mode (mirror's remote's state) */
+	int mode;
+
+	/* lirc */
+	struct lirc_plugin *p;
+	int connected;
+
+	/* locking */
+	struct semaphore lock;
+};
+
+/* list of all registered devices via the remote_list_link in irctl */
+static struct list_head remote_list;
+
+/* Convenience macros to retrieve a pointer to the surrounding struct from
+ * the given list_head reference within, pointed at by link. */
+#define get_iep_from_link(link)  list_entry((link), struct in_endpt, iep_list_link);
+#define get_irctl_from_link(link)  list_entry((link), struct irctl, remote_list_link);
+
+/* init strings */
+static char init1[] = {0x01, 0x00, 0x20, 0x14};
+static char init2[] = {0x01, 0x00, 0x20, 0x14, 0x20, 0x20, 0x20};
+
+/* send packet - used to initialize remote */
+static void send_packet(struct out_endpt *oep, u16 cmd, unsigned char *data)
+{
+	struct irctl *ir = oep->ir;
+	DECLARE_WAITQUEUE(wait, current);
+	int timeout = HZ; /* 1 second */
+	unsigned char buf[USB_BUFLEN];
+
+	dprintk(DRIVER_NAME "[%d]: send called (%#x)\n", ir->devnum, cmd);
+
+	IRLOCK;
+	oep->urb->transfer_buffer_length = LO(cmd) + 1;
+	oep->urb->dev = oep->ir->usbdev;
+	oep->send_flags = SEND_FLAG_IN_PROGRESS;
+
+	memcpy(buf+1, data, LO(cmd));
+	buf[0] = HI(cmd);
+	memcpy(oep->buf, buf, LO(cmd)+1);
+
+	set_current_state(TASK_INTERRUPTIBLE);
+	add_wait_queue(&oep->wait, &wait);
+
+#ifdef KERNEL_2_5
+	if (usb_submit_urb(oep->urb, SLAB_ATOMIC)) {
+#else
+	if (usb_submit_urb(oep->urb)) {
+#endif
+		set_current_state(TASK_RUNNING);
+		remove_wait_queue(&oep->wait, &wait);
+		IRUNLOCK;
+		return;
+	}
+	IRUNLOCK;
+
+	while (timeout && (oep->urb->status == -EINPROGRESS)
+		&& !(oep->send_flags & SEND_FLAG_COMPLETE)) {
+		timeout = schedule_timeout(timeout);
+		rmb();
+	}
+
+	dprintk(DRIVER_NAME "[%d]: send complete (%#x)\n", ir->devnum, cmd);
+
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&oep->wait, &wait);
+	usb_unlink_urb(oep->urb);
+}
+
+static int unregister_from_lirc(struct irctl *ir)
+{
+	struct lirc_plugin *p = ir->p;
+	int devnum;
+	int rtn;
+
+	devnum = ir->devnum;
+	dprintk(DRIVER_NAME "[%d]: unregister from lirc called\n", devnum);
+
+	if ((rtn = lirc_unregister_plugin(p->minor)) > 0) {
+		printk(DRIVER_NAME "[%d]: error in lirc_unregister minor: %d\n"
+			"Trying again...\n", devnum, p->minor);
+		if (rtn == -EBUSY) {
+			printk(DRIVER_NAME
+				"[%d]: device is opened, will unregister"
+				" on close\n", devnum);
+			return -EAGAIN;
+		}
+		set_current_state(TASK_INTERRUPTIBLE);
+		schedule_timeout(HZ);
+
+		if ((rtn = lirc_unregister_plugin(p->minor)) > 0) {
+			printk(DRIVER_NAME "[%d]: lirc_unregister failed\n",
+			devnum);
+		}
+	}
+
+	if (rtn != SUCCESS) {
+		printk(DRIVER_NAME "[%d]: didn't free resources\n", devnum);
+		return -EAGAIN;
+	}
+
+	printk(DRIVER_NAME "[%d]: usb remote disconnected\n", devnum);
+	return SUCCESS;
+}
+
+
+static int set_use_inc(void *data)
+{
+	struct irctl *ir = data;
+	struct list_head *pos, *n;
+	struct in_endpt *iep;
+
+	if (!ir) {
+		printk(DRIVER_NAME "[?]: set_use_inc called with no context\n");
+		return -EIO;
+	}
+	dprintk(DRIVER_NAME "[%d]: set use inc\n", ir->devnum);
+
+	MOD_INC_USE_COUNT;
+
+	if (!ir->connected) {
+		if (!ir->usbdev)
+			return -ENOENT;
+
+		IRLOCK;
+		/* Iterate through the inbound endpoints */
+		list_for_each_safe(pos, n, &ir->iep_listhead) {
+			/* extract the current in_endpt */
+			iep = get_iep_from_link(pos);
+			iep->urb->dev = ir->usbdev;
+#ifdef KERNEL_2_5
+			if (usb_submit_urb(iep->urb, SLAB_ATOMIC)) {
+#else
+			if (usb_submit_urb(iep->urb)) {
+#endif
+				printk(DRIVER_NAME "[%d]: open result = -EIO error "
+					"submitting urb\n", ir->devnum);
+				IRUNLOCK;
+				MOD_DEC_USE_COUNT;
+				return -EIO;
+			}
+		}
+		ir->connected = 1;
+		IRUNLOCK;
+	}
+
+	return SUCCESS;
+}
+
+static void set_use_dec(void *data)
+{
+	struct irctl *ir = data;
+	struct list_head *pos, *n;
+	struct in_endpt *iep;
+
+	if (!ir) {
+		printk(DRIVER_NAME "[?]: set_use_dec called with no context\n");
+		return;
+	}
+	dprintk(DRIVER_NAME "[%d]: set use dec\n", ir->devnum);
+
+	if (ir->connected) {
+		IRLOCK;
+		/* Free inbound usb urbs */
+		list_for_each_safe(pos, n, &ir->iep_listhead) {
+			iep = get_iep_from_link(pos);
+			usb_unlink_urb(iep->urb);
+		}
+		ir->connected = 0;
+		IRUNLOCK;
+	}
+	MOD_DEC_USE_COUNT;
+}
+
+static void print_data(struct in_endpt *iep, char *buf, int len)
+{
+	char codes[USB_BUFLEN*3 + 1];
+	int i;
+
+	if (len <= 0)
+		return;
+
+	for (i = 0; i < len && i < USB_BUFLEN; i++) {
+		snprintf(codes+i*3, 4, "%02x ", buf[i] & 0xFF);
+	}
+	printk(DRIVER_NAME "[%d]: data received %s (ep=0x%x length=%d)\n",
+		iep->ir->devnum, codes, iep->ep->bEndpointAddress, len);
+}
+
+static int code_check(struct in_endpt *iep, int len)
+{
+	struct irctl *ir = iep->ir;
+	int i, chan;
+
+	/* ATI RW1: some remotes emit both 4 and 5 byte length codes. */
+	/* ATI RW2: emit 3 byte codes */
+	if (len < CODE_MIN_LENGTH || len > CODE_LENGTH)
+		return -1;
+
+	switch (ir->remote_type) {
+
+	case ATI1_COMPATIBLE:
+
+		// *** channel not tested with 4/5-byte Dutch remotes ***
+		chan = ((iep->buf[len-1]>>4) & 0x0F);
+
+		/* strip channel code */
+		if (!unique) {
+			iep->buf[len-1] &= 0x0F;
+			iep->buf[len-3] -= (chan<<4);
+		}
+		break;
+
+	default:
+		chan = 0;
+	}
+
+	if ( !((1U<<chan) & mask) ) {
+		dprintk(DRIVER_NAME "[%d]: ignore channel %d\n", ir->devnum, chan+1);
+		return -1;
+	}
+	dprintk(DRIVER_NAME "[%d]: accept channel %d\n", ir->devnum, chan+1);
+
+	if (ir->remote_type == ATI1_COMPATIBLE) {
+		/* check for repeats */
+		if (memcmp(iep->old, iep->buf, len) == 0) {
+			if (iep->old_jiffies + repeat_jiffies > jiffies) {
+				return -1;
+			}
+		} else {
+			for (i = len; i < CODE_LENGTH; i++) iep->buf[i] = 0;
+			memcpy(iep->old, iep->buf, CODE_LENGTH);
+		}
+		iep->old_jiffies = jiffies;
+	}
+
+	return SUCCESS;
+}
+
+/*
+ * Since the ATI Remote Wonder II has quite a different structure from the
+ * prior version, this function was seperated out to clarify the sanitization
+ * process.
+ *
+ * Here is a summary of the main differences:
+ *
+ * a. The rw2 has no sense of a transmission channel.  But, it does have an
+ *    auxilliary mode state, which is set by the mode buttons Aux1 through
+ *    Aux4 and "PC".  These map respectively to 0-4 in the first byte of the
+ *    recv buffer.  Any subsequent button press sends this mode number as its
+ *    "channel code."  Annoyingly enough, the mode setting buttons all send
+ *    the same key code (0x3f), and can only be distinguished via their mode
+ *    byte.
+ *
+ *    Because of this, old-style "unique"-parameter-enabled channel squashing
+ *    kills the functionality of the aux1-aux4 and PC buttons.  However, to
+ *    not do so would cause each remote key to send a different code depending
+ *    on the active aux.  Further complicating matters, using the mouse norb
+ *    also sends an identical code as would pushing the active aux button.  To
+ *    handle this we need a seperate parameter, like rw2modes, with the
+ *    following values and meanings:
+ *
+ *    	0: Don't squash any channel info
+ *    	1: Only squash channel data for non-mode setting keys
+ *    	2: Ignore aux keypresses, but don't squash channel
+ *    	3: Ignore aux keypresses and squash channel data
+ *
+ *    Option 1 may seem useless since the mouse sends the same code, but one
+ *    need only ignore in userspace any press of a mode-setting code that only
+ *    reaffirms the current mode.  The 3rd party lirccd should be able to
+ *    handle this easily enough, but lircd doesn't keep the state necessary
+ *    for this.  TODO We could work around this in the driver by emitting a
+ *    single 02 (press) code for a mode key only if that mode is not currently
+ *    active.
+ *
+ *    Option 2 would be useful for those wanting super configurability,
+ *    offering the ability to program 5 times the number actions based on the
+ *    current mode.
+ *
+ * b. The rw2 has its own built in repeat handling; the keys endpoint
+ *    encodes this in the second byte as 1 for press, 2 for hold, and 0 for
+ *    release.  This is generally much more responsive than lirc's built-in
+ *    timeout handling.
+ *
+ *    The problem is that the remote can send the release-recieve pair
+ *    (0,1) while one is still holding down the same button if the
+ *    transmission is momentarilly interrupted.  (It seems that the receiver
+ *    manages this count instead of the remote.)  By default, this information
+ *    is squashed to 2.
+ *
+ *    In order to expose the built-in repeat code, set the emit_updown
+ *    parameter as described below.
+ *
+ * c. The mouse norb is much more sensitive than on the rw1.  It emulates
+ *    a joystick-like controller with the second byte representing the x-axis
+ *    and the third, the y-axis.  Treated as signed integers, these axes range
+ *    approximately as follows:
+ *
+ *    	x: (left) -46 ... 46 (right) (0xd2..0x2e)
+ *    	y: (up)   -46 ... 46 (down)  (0xd2..0x2e)
+ *
+ *    NB these values do not correspond to the pressure with which the mouse
+ *    norb is pushed in a given direction, but rather seems to indicate the
+ *    duration for which a given direction is held.
+ *
+ *    These are normalized to 8 cardinal directions for easy configuration via
+ *    lircd.conf.  The normalization can be fined tuned with the mdeadzone and
+ *    mgradient parameters as described below.
+ *
+ * d. The interrupt rate of the mouse vs. the normal keys is different.
+ *
+ * 	mouse: ~27Hz (37ms between interrupts)
+ * 	keys:  ~10Hz (100ms between interrupts)
+ *
+ *    This means that the normal gap mechanism for lircd won't work as
+ *    expected; is emit_updown>0 if you can get away with it.
+ */
+static int process_ati2_input(struct in_endpt *iep, int len) {
+	struct irctl *ir = iep->ir;
+	int mode;
+	char *buf = iep->buf;
+
+	if (len != CODE_LENGTH_ATI2) {
+		dprintk(DRIVER_NAME
+			"[%d]: Huh?  Abnormal length (%d) buffer recieved.\n",
+			ir->devnum, len);
+		return -1;
+	}
+
+	mode = buf[0];
+
+	/* Squash the mode indicator if unique wasn't set non-zero */
+	if (!unique) buf[0] = 0;
+
+	if (iep->ep->bEndpointAddress == EP_KEYS_ADDR) {
+		/* ignore mouse navigation indicator key and mode-set (aux) keys */
+		if (buf[2] == RW2_MODENAV_KEYCODE) {
+			if (emit_modekeys >= 2) { /* emit raw */
+				buf[0] = mode;
+			} else if (emit_modekeys == 1) { /* translate */
+				buf[0] = mode;
+				if (ir->mode != mode) {
+					buf[1] = 0x03;
+					ir->mode = mode;
+					return SUCCESS;
+				}
+			} else {
+				dprintk(DRIVER_NAME "[%d]: ignore dummy code 0x%x (ep=0x%x)\n",
+					ir->devnum, buf[2], iep->ep->bEndpointAddress);
+				return -1;
+			}
+		}
+
+		if (buf[1] != 2) {
+			/* handle press/release codes */
+			if (emit_updown == 0) /* ignore */
+				return -1;
+			else if(emit_updown == 1) /* normalize keycode */
+				buf[2] = RW2_PRESSRELEASE_KEYCODE;
+			/* else emit raw */
+		}
+
+	} else {
+		int x = (signed char)buf[1];
+		int y = (signed char)buf[2];
+		int code = 0x00;
+		int dir_ew, dir_ns;
+
+		buf[2] = RW2_MOUSE_KEYCODE;
+
+		/* sensitivity threshold (use L2norm^2) */
+		if (mdeadzone > (x*x+y*y)) {
+			buf[1] = 0x00;
+			return SUCCESS;
+		}
+
+/* Nybble encoding: xy, 2 is -1 (S or W); 1 (N or E) */
+#define MOUSE_N		0x01
+#define MOUSE_NE	0x11
+#define MOUSE_E		0x10
+#define MOUSE_SE	0x12
+#define MOUSE_S		0x02
+#define MOUSE_SW	0x22
+#define MOUSE_W		0x20
+#define MOUSE_NW	0x21
+
+		/* cardinal leanings: positive x -> E, positive y -> S */
+		dir_ew = (x > 0) ? MOUSE_E : MOUSE_W;
+		dir_ns = (y > 0) ? MOUSE_S : MOUSE_N;
+
+		/* convert coordintes(angle) into compass direction */
+		if (x == 0) {
+			code = dir_ns;
+		} else if (y == 0) {
+			code = dir_ew;
+		} else {
+			if (abs(1000*y/x) > mgradient)
+				code = dir_ns;
+			if (abs(1000*x/y) > mgradient)
+				code |= dir_ew;
+		}
+
+		buf[1] = code;
+		dprintk(DRIVER_NAME "[%d]: mouse compass=0x%x %s%s (%d,%d)\n",
+			ir->devnum, code,
+			(code & MOUSE_S ? "S" : (code & MOUSE_N ? "N" : "")),
+			(code & MOUSE_E ? "E" : (code & MOUSE_W ? "W" : "")),
+			x, y);
+	}
+
+	return SUCCESS;
+}
+
+
+#ifdef KERNEL_2_5
+static void usb_remote_recv(struct urb *urb, struct pt_regs *regs)
+#else
+static void usb_remote_recv(struct urb *urb)
+#endif
+{
+	struct in_endpt *iep;
+	int len;
+
+	if (!urb)
+		return;
+	if (!(iep = urb->context)) {
+#ifdef KERNEL_2_5
+		urb->transfer_flags |= URB_ASYNC_UNLINK;
+#endif
+		usb_unlink_urb(urb);
+		return;
+	}
+	if (!iep->ir->usbdev)
+		return;
+
+	len = urb->actual_length;
+	if (debug)
+		print_data(iep,urb->transfer_buffer,len);
+
+	switch (urb->status) {
+
+	/* success */
+	case SUCCESS:
+
+		if(iep->ir->remote_type == ATI2_COMPATIBLE) {
+			if (process_ati2_input(iep, len) < 0) break;
+		} else {
+			if (code_check(iep, len) < 0) break;
+		}
+
+		lirc_buffer_write_1(iep->ir->p->rbuf, iep->buf);
+		wake_up(&iep->ir->p->rbuf->wait_poll);
+		break;
+
+	/* unlink */
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+#ifdef KERNEL_2_5
+		urb->transfer_flags |= URB_ASYNC_UNLINK;
+#endif
+		usb_unlink_urb(urb);
+		return;
+
+	case -EPIPE:
+	default:
+		break;
+	}
+
+	/* resubmit urb */
+#ifdef KERNEL_2_5
+	usb_submit_urb(urb, SLAB_ATOMIC);
+#endif
+}
+
+#ifdef KERNEL_2_5
+static void usb_remote_send(struct urb *urb, struct pt_regs *regs)
+#else
+static void usb_remote_send(struct urb *urb)
+#endif
+{
+	struct out_endpt *oep;
+
+	if (!urb)
+		return;
+	if (!(oep = urb->context)) {
+		usb_unlink_urb(urb);
+		return;
+	}
+	if (!oep->ir->usbdev)
+		return;
+
+	dprintk(DRIVER_NAME "[%d]: usb out called\n", oep->ir->devnum);
+
+	if (urb->status)
+		return;
+
+	oep->send_flags |= SEND_FLAG_COMPLETE;
+	wmb();
+	if (waitqueue_active(&oep->wait))
+		wake_up(&oep->wait);
+}
+
+
+/***************************************************************************
+ * Initialization and removal
+ ***************************************************************************/
+
+/*
+ * Free iep according to mem_failure which specifies a checkpoint into the
+ * initialization sequence for rollback recovery.
+ */
+static void free_in_endpt(struct in_endpt *iep, int mem_failure)
+{
+	struct irctl *ir;
+	dprintk(DRIVER_NAME ": free_in_endpt(%p, %d)\n", iep, mem_failure);
+	if (!iep) return;
+
+	ir = iep->ir;
+	if (!ir) {
+		dprintk(DRIVER_NAME ": free_in_endpt: WARNING! null ir\n");
+		return;
+	}
+	IRLOCK;
+	switch (mem_failure) {
+	case FREE_ALL:
+	case 5:
+		list_del(&iep->iep_list_link);
+		dprintk(DRIVER_NAME "[%d]: free_in_endpt removing ep=0x%0x from list\n", ir->devnum, iep->ep->bEndpointAddress);
+	case 4:
+		if (iep->urb) {
+			usb_unlink_urb(iep->urb);
+			usb_free_urb(iep->urb);
+			iep->urb = 0;
+		} else {
+			dprintk(DRIVER_NAME "[%d]: free_in_endpt null urb!\n", ir->devnum);
+		}
+	case 3:
+#ifdef KERNEL_2_5
+		usb_buffer_free(iep->ir->usbdev, iep->len, iep->buf, iep->dma);
+#else
+		kfree(iep->buf);
+#endif
+		iep->buf = 0;
+	case 2:
+		kfree(iep);
+	}
+	IRUNLOCK;
+}
+
+/*
+ * Construct a new inbound endpoint for this remote, and add it to the list of
+ * in_epts in ir.
+ */
+static struct in_endpt *new_in_endpt(struct irctl *ir, struct usb_endpoint_descriptor *ep)
+{
+	struct usb_device *dev = ir->usbdev;
+	struct in_endpt *iep;
+	int pipe, maxp, len, addr;
+	int mem_failure;
+
+	addr = ep->bEndpointAddress;
+	pipe = usb_rcvintpipe(dev, addr);
+	maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
+
+	len = (maxp > USB_BUFLEN) ? USB_BUFLEN : maxp;
+	len -= (len % CODE_LENGTH);
+
+	dprintk(DRIVER_NAME "[%d]: acceptable inbound endpoint (0x%x) found (maxp=%d len=%d)\n", ir->devnum, addr, maxp, len);
+
+	mem_failure = 0;
+	if ( !(iep = kmalloc(sizeof(*iep), GFP_KERNEL)) ) {
+		mem_failure = 1;
+	} else {
+		memset(iep, 0, sizeof(*iep));
+		iep->ir = ir;
+		iep->ep = ep;
+		iep->len = len;
+
+#ifdef KERNEL_2_5
+		if ( !(iep->buf = usb_buffer_alloc(dev, len, SLAB_ATOMIC, &iep->dma)) ) {
+			mem_failure = 2;
+		} else if ( !(iep->urb = usb_alloc_urb(0, GFP_KERNEL)) ) {
+			mem_failure = 3;
+		}
+#else
+		if ( !(iep->buf = kmalloc(len, GFP_KERNEL)) ) {
+			mem_failure = 2;
+		} else if ( !(iep->urb = usb_alloc_urb(0)) ) {
+			mem_failure = 3;
+		}
+#endif
+	}
+	if (mem_failure) {
+		free_in_endpt(iep, mem_failure);
+		printk(DRIVER_NAME "[%d]: ep=0x%x out of memory (code=%d)\n", ir->devnum, addr, mem_failure);
+		return NULL;
+	}
+	list_add_tail(&iep->iep_list_link, &ir->iep_listhead);
+	dprintk(DRIVER_NAME "[%d]: adding ep=0x%0x to list\n", ir->devnum, iep->ep->bEndpointAddress);
+	return iep;
+}
+
+static void free_out_endpt(struct out_endpt *oep, int mem_failure)
+{
+	struct irctl *ir;
+	dprintk(DRIVER_NAME ": free_out_endpt(%p, %d)\n", oep, mem_failure);
+	if (!oep) return;
+
+	wake_up_all(&oep->wait);
+
+	ir = oep->ir;
+	if (!ir) {
+		dprintk(DRIVER_NAME ": free_out_endpt: WARNING! null ir\n");
+		return;
+	}
+	IRLOCK;
+	switch (mem_failure) {
+	case FREE_ALL:
+	case 4:
+		if (oep->urb) {
+			usb_unlink_urb(oep->urb);
+			usb_free_urb(oep->urb);
+			oep->urb = 0;
+		} else {
+			dprintk(DRIVER_NAME "[%d]: free_out_endpt: null urb!\n", ir->devnum);
+		}
+	case 3:
+#ifdef KERNEL_2_5
+		usb_buffer_free(oep->ir->usbdev, USB_BUFLEN, oep->buf, oep->dma);
+#else
+		kfree(oep->buf);
+#endif
+		oep->buf = 0;
+	case 2:
+		kfree(oep);
+	}
+	IRUNLOCK;
+}
+
+static struct out_endpt *new_out_endpt(struct irctl *ir, struct usb_endpoint_descriptor *ep)
+{
+	struct usb_device *dev = ir->usbdev;
+	struct out_endpt *oep;
+	int mem_failure;
+
+	dprintk(DRIVER_NAME "[%d]: acceptable outbound endpoint (0x%x) found\n", ir->devnum, ep->bEndpointAddress);
+
+	mem_failure = 0;
+	if ( !(oep = kmalloc(sizeof(*oep), GFP_KERNEL)) ) {
+		mem_failure = 1;
+	} else {
+		memset(oep, 0, sizeof(*oep));
+		oep->ir = ir;
+		oep->ep = ep;
+		init_waitqueue_head(&oep->wait);
+
+#ifdef KERNEL_2_5
+		if ( !(oep->buf = usb_buffer_alloc(dev, USB_BUFLEN, SLAB_ATOMIC, &oep->dma)) ) {
+			mem_failure = 2;
+		} else if ( !(oep->urb = usb_alloc_urb(0, GFP_KERNEL)) ) {
+			mem_failure = 3;
+		}
+#else
+		if ( !(oep->buf = kmalloc(USB_BUFLEN, GFP_KERNEL)) ) {
+			mem_failure = 2;
+		} else if ( !(oep->urb = usb_alloc_urb(0)) ) {
+			mem_failure = 3;
+		}
+#endif
+	}
+	if (mem_failure) {
+		free_out_endpt(oep, mem_failure);
+		printk(DRIVER_NAME "[%d]: ep=0x%x out of memory (code=%d)\n", ir->devnum, ep->bEndpointAddress, mem_failure);
+		return NULL;
+	}
+	return oep;
+}
+
+static void free_irctl(struct irctl *ir, int mem_failure)
+{
+	struct list_head *pos, *n;
+	struct in_endpt *in;
+	dprintk(DRIVER_NAME ": free_irctl(%p, %d)\n", ir, mem_failure);
+
+	if (!ir) return;
+
+	list_for_each_safe(pos, n, &ir->iep_listhead) {
+		in = get_iep_from_link(pos);
+		free_in_endpt(in, FREE_ALL);
+	}
+	if (ir->out_init) {
+		free_out_endpt(ir->out_init, FREE_ALL);
+		ir->out_init = NULL;
+	}
+
+	IRLOCK;
+	switch (mem_failure) {
+	case FREE_ALL:
+	case 6:
+	    	if (!--ir->dev_refcount) {
+			list_del(&ir->remote_list_link);
+			dprintk(DRIVER_NAME "[%d]: free_irctl: removing remote from list\n",
+				ir->devnum);
+		} else {
+			dprintk(DRIVER_NAME "[%d]: free_irctl: refcount at %d,"
+				"aborting free_irctl\n", ir->devnum, ir->dev_refcount);
+			IRUNLOCK;
+			return;
+		}
+	case 5:
+	case 4:
+	case 3:
+		if (ir->p) {
+			switch (mem_failure) {
+			case 5: lirc_buffer_free(ir->p->rbuf);
+			case 4: kfree(ir->p->rbuf);
+			case 3: kfree(ir->p);
+			}
+		} else {
+			printk(DRIVER_NAME "[%d]: ir->p is a null pointer!\n", ir->devnum);
+		}
+	case 2:
+		IRUNLOCK;
+		kfree(ir);
+		return;
+	}
+	IRUNLOCK;
+}
+
+static struct irctl *new_irctl(struct usb_device *dev)
+{
+	struct irctl *ir;
+	struct lirc_plugin *plugin;
+	struct lirc_buffer *rbuf;
+	int type, devnum;
+	int mem_failure;
+
+	devnum = dev->devnum;
+
+	/* determine remote type */
+	switch (dev->descriptor.idVendor) {
+	case VENDOR_ATI1:
+		type = ATI1_COMPATIBLE;
+		break;
+	case VENDOR_ATI2:
+		type = ATI2_COMPATIBLE;
+		break;
+	default:
+		dprintk(DRIVER_NAME "[%d]: unknown type\n", devnum);
+		return NULL;
+	}
+	dprintk(DRIVER_NAME "[%d]: remote type = %d\n", devnum, type);
+
+	/* allocate kernel memory */
+	mem_failure = 0;
+	if ( !(ir = kmalloc(sizeof(*ir), GFP_KERNEL)) ) {
+		mem_failure = 1;
+	} else {
+		memset(ir, 0, sizeof(*ir));
+		/* add this infrared remote struct to remote_list, keeping track of
+		 * the number of drivers registered. */
+		dprintk(DRIVER_NAME "[%d]: adding remote to list\n", devnum);
+		list_add_tail(&ir->remote_list_link, &remote_list);
+		ir->dev_refcount=1;
+
+		if (!(plugin = kmalloc(sizeof(*plugin), GFP_KERNEL))) {
+			mem_failure = 2;
+		} else if (!(rbuf = kmalloc(sizeof(*rbuf), GFP_KERNEL))) {
+			mem_failure = 3;
+		} else if (lirc_buffer_init(rbuf, CODE_LENGTH, USB_BUFLEN/CODE_LENGTH)) {
+			mem_failure = 4;
+		} else {
+			memset(plugin, 0, sizeof(*plugin));
+			strcpy(plugin->name, DRIVER_NAME " ");
+			plugin->minor = -1;
+			plugin->code_length = CODE_LENGTH*8;
+			plugin->features = LIRC_CAN_REC_LIRCCODE;
+			plugin->data = ir;
+			plugin->rbuf = rbuf;
+			plugin->set_use_inc = &set_use_inc;
+			plugin->set_use_dec = &set_use_dec;
+			ir->usbdev = dev;
+			ir->p = plugin;
+			ir->remote_type = type;
+			ir->devnum = devnum;
+			ir->mode = RW2_NULL_MODE;
+
+			init_MUTEX(&ir->lock);
+			INIT_LIST_HEAD(&ir->iep_listhead);
+		}
+	}
+	if (mem_failure) {
+		free_irctl(ir, mem_failure);
+		printk(DRIVER_NAME "[%d]: out of memory (code=%d)\n", devnum, mem_failure);
+		return NULL;
+	}
+	return ir;
+}
+
+
+/*
+ * Scan the global list of remotes to see if the device listed is one of them.
+ * If it is, the corresponding irctl is returned, with its dev_refcount
+ * incremented.  Otherwise, returns null.
+ */
+static struct irctl *get_prior_reg_ir(struct usb_device *dev) {
+	struct list_head *pos;
+	struct irctl *ir = NULL;
+
+	dprintk(DRIVER_NAME "[%d]: scanning remote_list...\n", dev->devnum);
+	list_for_each(pos, &remote_list) {
+		ir = get_irctl_from_link(pos);
+		if (ir->usbdev != dev) {
+		    dprintk(DRIVER_NAME "[%d]: device %d isn't it...", dev->devnum, ir->devnum);
+		    ir = NULL;
+		} else {
+		    dprintk(DRIVER_NAME "[%d]: prior instance found.\n", dev->devnum);
+		    ir->dev_refcount++;
+		    break;
+		}
+	}
+	return ir;
+}
+
+/* If the USB interface has an out endpoint for control (eg, the first Remote
+ * Wonder) send the appropriate initialization packets. */
+static void send_outbound_init(struct irctl *ir) {
+	if (ir->out_init) {
+		struct out_endpt *oep = ir->out_init;
+		dprintk(DRIVER_NAME "[%d]: usb_remote_probe: initializing outbound ep\n", ir->devnum);
+		usb_fill_int_urb(oep->urb, ir->usbdev,
+			usb_sndintpipe(ir->usbdev, oep->ep->bEndpointAddress), oep->buf,
+			USB_BUFLEN, usb_remote_send, oep, oep->ep->bInterval);
+
+		send_packet(oep, 0x8004, init1);
+		send_packet(oep, 0x8007, init2);
+	}
+}
+
+/* Log driver and usb info */
+static void log_usb_dev_info(struct usb_device *dev) {
+	char buf[63], name[128]="";
+	if (dev->descriptor.iManufacturer
+		&& usb_string(dev, dev->descriptor.iManufacturer, buf, 63) > 0)
+		strncpy(name, buf, 128);
+	if (dev->descriptor.iProduct
+		&& usb_string(dev, dev->descriptor.iProduct, buf, 63) > 0)
+		snprintf(name, 128, "%s %s", name, buf);
+	printk(DRIVER_NAME "[%d]: %s on usb%d:%d\n", dev->devnum, name,
+	       dev->bus->busnum, dev->devnum);
+}
+
+
+#ifdef KERNEL_2_5
+static int usb_remote_probe(struct usb_interface *intf,
+				const struct usb_device_id *id)
+{
+	struct usb_device *dev = interface_to_usbdev(intf);
+	struct usb_host_interface *idesc;
+#else
+static void *usb_remote_probe(struct usb_device *dev, unsigned int ifnum,
+				const struct usb_device_id *id)
+{
+	struct usb_interface *intf = &dev->actconfig->interface[ifnum];
+	struct usb_interface_descriptor *idesc;
+#endif
+	struct usb_endpoint_descriptor *ep;
+	struct in_endpt *iep;
+	struct irctl *ir;
+	int i, type;
+
+	dprintk(DRIVER_NAME "[%d]: usb_remote_probe: dev:%p, intf:%p, id:%p)\n",
+		dev->devnum, dev, intf, id);
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,4)
+	idesc = intf->cur_altsetting;
+#else
+	idesc = &intf->altsetting[intf->act_altsetting];
+#endif
+
+	/* Check if a usb remote has already been registered for this device */
+	ir = get_prior_reg_ir(dev);
+
+	if ( !ir && !(ir = new_irctl(dev)) ) {
+#ifdef KERNEL_2_5
+		return -ENOMEM;
+#else
+		return NULL;
+#endif
+	}
+	type = ir->remote_type;
+
+	// step through the endpoints to find first in and first out endpoint
+	// of type interrupt transfer
+#ifdef KERNEL_2_5
+	for (i = 0; i < idesc->desc.bNumEndpoints; ++i) {
+		ep = &idesc->endpoint[i].desc;
+#else
+	for (i = 0; i < idesc->bNumEndpoints; ++i) {
+		ep = &idesc->endpoint[i];
+#endif
+		dprintk(DRIVER_NAME "[%d]: processing endpoint %d\n", dev->devnum, i);
+		if ( ((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN)
+			&& ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT)) {
+
+			if ((iep = new_in_endpt(ir,ep))) {
+				usb_fill_int_urb(iep->urb, dev,
+					usb_rcvintpipe(dev,iep->ep->bEndpointAddress), iep->buf,
+					iep->len, usb_remote_recv, iep, iep->ep->bInterval);
+			}
+		}
+
+		if ( ((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT)
+			&& ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT)
+			&& (ir->out_init == NULL)) {
+
+			ir->out_init = new_out_endpt(ir,ep);
+		}
+	}
+	if (list_empty(&ir->iep_listhead)) {
+		printk(DRIVER_NAME "[%d]: inbound endpoint not found\n", ir->devnum);
+		free_irctl(ir, FREE_ALL);
+#ifdef KERNEL_2_5
+		return -ENODEV;
+#else
+		return NULL;
+#endif
+	}
+	if (ir->dev_refcount == 1) {
+		if ((ir->p->minor = lirc_register_plugin(ir->p)) < 0) {
+			free_irctl(ir, FREE_ALL);
+#ifdef KERNEL_2_5
+			return -ENODEV;
+#else
+			return NULL;
+#endif
+		}
+
+		/* Note new driver registration in kernel logs */
+		log_usb_dev_info(dev);
+
+		/* outbound data (initialization) */
+		send_outbound_init(ir);
+	}
+
+#ifdef KERNEL_2_5
+	usb_set_intfdata(intf, ir);
+	return SUCCESS;
+#else
+	return ir;
+#endif
+}
+
+#ifdef KERNEL_2_5
+static void usb_remote_disconnect(struct usb_interface *intf)
+{
+//	struct usb_device *dev = interface_to_usbdev(intf);
+	struct irctl *ir = usb_get_intfdata(intf);
+	usb_set_intfdata(intf, NULL);
+#else
+static void usb_remote_disconnect(struct usb_device *dev, void *ptr)
+{
+	struct irctl *ir = ptr;
+#endif
+
+	dprintk(DRIVER_NAME ": disconnecting remote %d:\n", (ir? ir->devnum: -1));
+	if (!ir || !ir->p)
+		return;
+
+	if (ir->usbdev) {
+		/* Only unregister once */
+		ir->usbdev = NULL;
+		unregister_from_lirc(ir);
+	}
+
+	/* This also removes the current remote from remote_list */
+	free_irctl(ir, FREE_ALL);
+}
+
+static struct usb_driver usb_remote_driver = {
+	.owner =	THIS_MODULE,
+	.name =		DRIVER_NAME,
+	.probe =	usb_remote_probe,
+	.disconnect =	usb_remote_disconnect,
+	.id_table =	usb_remote_table
+};
+
+static int __init usb_remote_init(void)
+{
+	int i;
+
+	INIT_LIST_HEAD(&remote_list);
+
+	printk("\n" DRIVER_NAME ": " DRIVER_DESC " v" DRIVER_VERSION "\n");
+	printk(DRIVER_NAME ": " DRIVER_AUTHOR "\n");
+	dprintk(DRIVER_NAME ": debug mode enabled: $Id: lirc_atiusb.c,v 1.44 2004/12/01 01:27:54 pmiller9 Exp $\n");
+
+	request_module("lirc_dev");
+
+	repeat_jiffies = repeat*HZ/100;
+
+	if ((i = usb_register(&usb_remote_driver)) < 0) {
+		printk(DRIVER_NAME ": usb register failed, result = %d\n", i);
+		return -ENODEV;
+	}
+
+	return SUCCESS;
+}
+
+static void __exit usb_remote_exit(void)
+{
+	usb_deregister(&usb_remote_driver);
+}
+
+module_init(usb_remote_init);
+module_exit(usb_remote_exit);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(usb, usb_remote_table);
+
+module_param(debug, bool, 0644);
+MODULE_PARM_DESC(debug, "Debug enabled or not (default: 0)");
+
+module_param(mask, int, 0644);
+MODULE_PARM_DESC(mask, "Set channel acceptance bit mask (default: 0xFFFF)");
+
+module_param(unique, bool, 0644);
+MODULE_PARM_DESC(unique, "Enable channel-specific codes (default: 0)");
+
+module_param(repeat, int, 0644);
+MODULE_PARM_DESC(repeat, "Repeat timeout (1/100 sec) (default: 10)");
+
+module_param(mdeadzone, int, 0644);
+MODULE_PARM_DESC(mdeadzone, "rw2 mouse sensitivity threshold (default: 0)");
+
+/*
+ * Enabling this will cause the built-in Remote Wonder II repeate coding to
+ * not be squashed.  The second byte of the keys output will then be:
+ * 
+ * 	1 initial press (button down)
+ * 	2 holding (button remains pressed)
+ * 	0 release (button up)
+ *
+ * By default, the driver emits 2 for both 1 and 2, and emits nothing for 0.
+ * This is good for people having trouble getting their rw2 to send a good
+ * consistent signal to the receiver.
+ *
+ * However, if you have no troubles with the driver outputting up-down pairs
+ * at random points while you're still holding a button, then you can enable
+ * this parameter to get finer grain repeat control out of your remote:
+ * 
+ * 	1 Emit a single (per-channel) virtual code for all up/down events
+ * 	2 Emit the actual rw2 output
+ *
+ * 1 is easier to write lircd configs for; 2 allows full control.
+ */
+module_param(emit_updown, int, 0644);
+MODULE_PARM_DESC(emit_updown, "emit press/release codes (rw2): 0:don't (default), 1:emit 2 codes only, 2:code for each button");
+
+module_param(emit_modekeys, int, 0644);
+MODULE_PARM_DESC(emit_modekeys, "emit keycodes for aux1-aux4, pc, and mouse (rw2): 0:don't (default), 1:emit translated codes: one for mode switch, one for same mode, 2:raw codes");
+
+module_param(mgradient, int, 0644);
+MODULE_PARM_DESC(mgradient, "rw2 mouse: 1000*gradient from E to NE (default: 500 => .5 => ~27 degrees)");
+
+EXPORT_NO_SYMBOLS;
diff -urN linux-2.6.10-rc3-mm1/drivers/char/lirc/lirc_bt829.c linux-2.6.10-rc3-no2/drivers/char/lirc/lirc_bt829.c
--- linux-2.6.10-rc3-mm1/drivers/char/lirc/lirc_bt829.c	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.10-rc3-no2/drivers/char/lirc/lirc_bt829.c	2004-12-22 19:35:16.552732714 -0500
@@ -0,0 +1,394 @@
+/*
+ * Remote control driver for the TV-card based on bt829
+ *
+ *  by Leonid Froenchenko <lfroen@galileo.co.il>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include <linux/version.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 0)
+#error "This driver needs kernel version 2.4.0 or higher"
+#endif
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/threads.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+
+#include "linux/kcompat.h"
+#include "drivers/lirc_dev/lirc_dev.h"
+
+static int poll_main(void);
+static int atir_init_start(void);
+
+static void write_index(unsigned char index,unsigned int value);
+static unsigned int read_index(unsigned char index);
+
+static void do_i2c_start(void);
+static void do_i2c_stop(void);
+
+static void seems_wr_byte(unsigned char al);
+static unsigned char seems_rd_byte(void);
+
+static unsigned int read_index(unsigned char al);
+static void write_index(unsigned char ah,unsigned int edx);
+
+static void cycle_delay(int cycle);
+
+static void do_set_bits(unsigned char bl);
+static unsigned char do_get_bits(void);
+
+#define DATA_PCI_OFF 0x7FFC00
+#define WAIT_CYCLE   20
+
+static int debug = 0;
+#define dprintk(fmt, args...)                                 \
+	do{                                                   \
+		if(debug) printk(KERN_DEBUG fmt, ## args);    \
+	}while(0)
+
+static int atir_minor;
+static unsigned long pci_addr_phys, pci_addr_lin;
+
+static struct lirc_plugin atir_plugin;
+
+static int do_pci_probe(void)
+{
+	struct pci_dev *my_dev;
+#ifndef KERNEL_2_5
+	/* unnecessary with recent kernels */
+	if ( !pci_present() ) {
+		printk(KERN_ERR "ATIR: no pci in this kernel\n");
+	}
+#endif
+	my_dev = (struct pci_dev *)pci_find_device(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_264VT,NULL);
+	if ( my_dev ) {
+		printk(KERN_ERR "ATIR: Using device: %s\n",
+		       pci_pretty_name(my_dev));
+		pci_addr_phys = 0;
+		if ( my_dev->resource[0].flags & IORESOURCE_MEM ) {
+			pci_addr_phys = my_dev->resource[0].start;
+			printk(KERN_INFO "ATIR memory at 0x%08X \n",(unsigned int)pci_addr_phys);
+		}
+		if ( pci_addr_phys == 0 ) {
+			printk(KERN_ERR "ATIR no memory resource ?\n");
+			return 0;
+		}
+	} else {
+		printk(KERN_ERR "ATIR: pci_prob failed\n");
+		return 0;
+	}
+	return 1;
+}
+
+static int atir_add_to_buf (void* data, struct lirc_buffer* buf)
+{
+	unsigned char key;
+	int status;
+	status = poll_main();
+	key = (status >> 8) & 0xFF;
+	if( status & 0xFF )
+	{
+	//    printk(KERN_INFO "ATIR reading key %02X\n",*key);
+		lirc_buffer_write_1( buf, &key );
+		return 0;
+	}
+	return -ENODATA;
+}
+
+static int atir_set_use_inc(void* data)
+{
+	MOD_INC_USE_COUNT;
+	dprintk("ATIR driver is opened\n");
+	return 0;
+}
+
+static void atir_set_use_dec(void* data)
+{
+	MOD_DEC_USE_COUNT;
+	dprintk("ATIR driver is closed\n");
+}
+
+int init_module(void)
+{
+	if ( !do_pci_probe() ) {
+		return 1;
+	}
+
+	if ( !atir_init_start() ) {
+		return 1;
+	}
+
+	strcpy(atir_plugin.name,"ATIR");
+	atir_plugin.minor       = -1;
+	atir_plugin.code_length = 8;
+	atir_plugin.sample_rate = 10;
+	atir_plugin.data        = 0;
+	atir_plugin.add_to_buf  = atir_add_to_buf;
+	atir_plugin.set_use_inc = atir_set_use_inc;
+	atir_plugin.set_use_dec = atir_set_use_dec;
+
+	atir_minor = lirc_register_plugin(&atir_plugin);
+	dprintk("ATIR driver is registered on minor %d\n",atir_minor);
+
+	return 0;
+}
+
+
+void cleanup_module(void)
+{
+	lirc_unregister_plugin(atir_minor);
+}
+
+
+static int atir_init_start(void)
+{
+	pci_addr_lin = (unsigned long)ioremap(pci_addr_phys + DATA_PCI_OFF,0x400);
+	if ( pci_addr_lin == 0 ) {
+		printk(KERN_INFO "atir: pci mem must be mapped\n");
+		return 0;
+	}
+	return 1;
+}
+
+static void cycle_delay(int cycle)
+{
+	udelay(WAIT_CYCLE*cycle);
+}
+
+
+static int poll_main()
+{
+	unsigned char status_high, status_low;
+	
+	do_i2c_start();
+
+	seems_wr_byte(0xAA);
+	seems_wr_byte(0x01);
+
+	do_i2c_start();
+
+	seems_wr_byte(0xAB);
+
+	status_low = seems_rd_byte();
+	status_high = seems_rd_byte();
+
+	do_i2c_stop();
+
+	return (status_high << 8) | status_low;
+}
+
+static void do_i2c_start(void)
+{
+	do_set_bits(3);
+	cycle_delay(4);
+
+	do_set_bits(1);
+	cycle_delay(7);
+
+	do_set_bits(0);
+	cycle_delay(2);
+}
+
+static void do_i2c_stop(void)
+{
+	unsigned char bits;
+	bits =  do_get_bits() & 0xFD;
+	do_set_bits(bits);
+	cycle_delay(1);
+
+	bits |= 1;
+	do_set_bits(bits);
+	cycle_delay(2);
+
+	bits |= 2;
+	do_set_bits(bits);
+	bits = 3;
+	do_set_bits(bits);
+	cycle_delay(2);
+}
+
+static void seems_wr_byte(unsigned char value)
+{
+	int i;
+	unsigned char reg;
+    
+	reg = do_get_bits();
+	for(i = 0;i < 8;i++) {
+		if ( value & 0x80 ) {
+			reg |= 0x02;
+		} else {
+			reg &= 0xFD;
+		}
+		do_set_bits(reg);
+		cycle_delay(1);
+
+		reg |= 1;
+		do_set_bits(reg);
+		cycle_delay(1);
+
+		reg &= 0xFE;
+		do_set_bits(reg);
+		cycle_delay(1);
+		value <<= 1;
+	}
+	cycle_delay(2);
+
+	reg |= 2;
+	do_set_bits(reg);
+
+	reg |= 1;
+	do_set_bits(reg);
+
+	cycle_delay(1);
+	do_get_bits();
+
+	reg &= 0xFE;
+	do_set_bits(reg);
+	cycle_delay(3);
+}
+
+static unsigned char seems_rd_byte(void)
+{
+	int i;
+	int rd_byte;
+	unsigned char bits_2, bits_1;
+
+	bits_1 = do_get_bits() | 2;
+	do_set_bits(bits_1);
+
+	rd_byte = 0;
+	for(i = 0;i < 8;i++) {
+		bits_1 &= 0xFE;
+		do_set_bits(bits_1);
+		cycle_delay(2);
+
+		bits_1 |= 1;
+		do_set_bits(bits_1);
+		cycle_delay(1);
+
+		if ( (bits_2 = do_get_bits()) & 2 ) {
+			rd_byte |= 1;
+		}
+		rd_byte <<= 1;
+	}
+
+	bits_1 = 0;
+	if ( bits_2 == 0 ) {
+		bits_1 |= 2;
+	}
+	do_set_bits(bits_1);
+	cycle_delay(2);
+
+	bits_1 |= 1;
+	do_set_bits(bits_1);
+	cycle_delay(3);
+
+	bits_1 &= 0xFE;
+	do_set_bits(bits_1);
+	cycle_delay(2);
+
+	rd_byte >>= 1;
+	rd_byte &= 0xFF;
+	return rd_byte;
+}
+
+static void do_set_bits(unsigned char new_bits)
+{
+	int reg_val;
+	reg_val = read_index(0x34);
+	if ( new_bits & 2 ) {
+		reg_val &= 0xFFFFFFDF;
+		reg_val |= 1;
+	} else {
+		reg_val &= 0xFFFFFFFE;
+		reg_val |= 0x20;
+	}
+	reg_val |= 0x10;
+	write_index(0x34,reg_val);
+
+	reg_val = read_index(0x31);
+	if ( new_bits & 1 ) {
+		reg_val |= 0x1000000;
+	} else {
+		reg_val &= 0xFEFFFFFF;
+	}
+	reg_val |= 0x8000000;
+	write_index(0x31,reg_val);
+}
+
+static unsigned char do_get_bits(void)
+{
+	unsigned char bits;
+	int reg_val;
+
+	reg_val = read_index(0x34);
+	reg_val |= 0x10;
+	reg_val &= 0xFFFFFFDF;
+	write_index(0x34,reg_val);
+
+	reg_val = read_index(0x34);
+	bits = 0;
+	if ( reg_val & 8 ) {
+		bits |= 2;
+	} else {
+		bits &= 0xFD;
+	}
+	reg_val = read_index(0x31);
+	if ( reg_val & 0x1000000 ) {
+		bits |= 1;	
+	} else {
+		bits &= 0xFE;
+	}
+	return bits;
+}
+
+static unsigned int read_index(unsigned char index)
+{
+	unsigned int addr, value;
+	//  addr = pci_addr_lin + DATA_PCI_OFF + ((index & 0xFF) << 2);
+	addr = pci_addr_lin + ((index & 0xFF) << 2);
+	value = readl(addr);
+	return value;
+}
+
+static void write_index(unsigned char index,unsigned int reg_val)
+{
+	unsigned int addr;
+	addr = pci_addr_lin + ((index & 0xFF) << 2);
+	writel(reg_val,addr);
+}
+
+MODULE_AUTHOR("Froenchenko Leonid");
+MODULE_DESCRIPTION("IR remote driver for bt829 based TV cards");
+MODULE_LICENSE("GPL");
+
+module_param(debug, bool, 0644);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
+
+EXPORT_NO_SYMBOLS;
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff -urN linux-2.6.10-rc3-mm1/drivers/char/lirc/lirc_dev.c linux-2.6.10-rc3-no2/drivers/char/lirc/lirc_dev.c
--- linux-2.6.10-rc3-mm1/drivers/char/lirc/lirc_dev.c	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.10-rc3-no2/drivers/char/lirc/lirc_dev.c	2004-12-22 19:35:16.555732298 -0500
@@ -0,0 +1,843 @@
+/*
+ * LIRC base driver
+ * 
+ * (L) by Artur Lipowski <alipowski@interia.pl>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * $Id: lirc_dev.c,v 1.36 2004/09/05 16:48:48 lirc Exp $
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+ 
+#include <linux/version.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 2, 18)
+#error "**********************************************************"
+#error " Sorry, this driver needs kernel version 2.2.18 or higher "
+#error "**********************************************************"
+#endif
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/ioctl.h>
+#include <linux/fs.h>
+#include <linux/poll.h>
+#include <linux/smp_lock.h>
+#include <asm/uaccess.h>
+#include <asm/semaphore.h>
+#include <asm/errno.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+#include <linux/wrapper.h>
+#endif
+#define __KERNEL_SYSCALLS__
+#include <linux/unistd.h>
+/* DevFS header */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+#include <linux/devfs_fs_kernel.h>
+#endif
+/* SysFS header */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+#include <linux/device.h>
+#endif
+
+#include "linux/kcompat.h"
+#include "linux/lirc.h"
+#include "lirc_dev.h"
+
+static int debug = 0;
+#define dprintk(fmt, args...)                                 \
+	do{                                                   \
+		if(debug) printk(KERN_DEBUG fmt, ## args);    \
+	}while(0)
+
+#define IRCTL_DEV_NAME    "BaseRemoteCtl"
+#define SUCCESS           0
+#define NOPLUG            -1
+#define LOGHEAD           "lirc_dev (%s[%d]): "
+
+struct irctl
+{
+	struct lirc_plugin p;
+	int open;
+
+	struct lirc_buffer *buf;
+
+	int tpid;
+	struct semaphore *t_notify;
+	struct semaphore *t_notify2;
+	int shutdown;
+	long jiffies_to_wait;
+
+#ifdef LIRC_HAVE_DEVFS_24
+	devfs_handle_t devfs_handle;
+#endif
+};
+
+DECLARE_MUTEX(plugin_lock);
+
+static struct irctl irctls[MAX_IRCTL_DEVICES];
+static struct file_operations fops;
+
+/* Only used for sysfs but defined to void otherwise */
+static struct class_simple *lirc_class;
+
+/*  helper function
+ *  initializes the irctl structure
+ */
+static inline void init_irctl(struct irctl *ir)
+{
+	memset(&ir->p, 0, sizeof(struct lirc_plugin));
+	ir->p.minor = NOPLUG;
+
+	ir->tpid = -1;
+	ir->t_notify = NULL;
+	ir->t_notify2 = NULL;
+	ir->shutdown = 0;
+	ir->jiffies_to_wait = 0;
+
+	ir->open = 0;
+}
+
+
+/*  helper function
+ *  reads key codes from plugin and puts them into buffer
+ *  buffer free space is checked and locking performed
+ *  returns 0 on success
+ */
+static inline int add_to_buf(struct irctl *ir)
+{
+	if (lirc_buffer_full(ir->buf)) {
+		dprintk(LOGHEAD "buffer overflow\n",
+			ir->p.name, ir->p.minor);
+		return -EOVERFLOW;
+	}
+
+	if(ir->p.add_to_buf) {
+		int res = -ENODATA;
+		int got_data = 0;
+		
+		/* service the device as long as it is returning
+		 * data and we have space
+		 */
+		while( !lirc_buffer_full(ir->buf) )
+		{
+			res = ir->p.add_to_buf( ir->p.data, ir->buf );
+			if( res == SUCCESS )
+				got_data++;
+			else
+				break;
+		}
+		
+		if( res == -ENODEV )
+		{
+			ir->shutdown = 1;
+		}
+		return (got_data ? SUCCESS : res);
+	}
+	
+	return SUCCESS;
+}
+
+/* main function of the polling thread
+ */
+static int lirc_thread(void *irctl)
+{
+	struct irctl *ir = irctl;
+	
+	/* This thread doesn't need any user-level access,
+	 * so get rid of all our resources
+	 */
+	daemonize("lirc_dev");
+	
+	if (ir->t_notify != NULL) {
+		up(ir->t_notify);
+	}
+	
+	dprintk(LOGHEAD "poll thread started\n", ir->p.name, ir->p.minor);
+	
+	do {
+		if (ir->open) {
+			if (ir->jiffies_to_wait) {
+				current->state = TASK_INTERRUPTIBLE;
+				schedule_timeout(ir->jiffies_to_wait);
+			} else {
+				interruptible_sleep_on(ir->p.get_queue(ir->p.data));
+			}
+			if (ir->shutdown) {
+				break;
+			}
+			if (!add_to_buf(ir)) {
+				wake_up_interruptible(&ir->buf->wait_poll);
+			}
+		} else {
+			/* if device not opened so we can sleep half a second */
+			current->state = TASK_INTERRUPTIBLE;
+			schedule_timeout(HZ/2);
+		}
+	} while (!ir->shutdown);
+	
+	if (ir->t_notify2 != NULL) {
+		down(ir->t_notify2);
+	}
+
+	ir->tpid = -1;
+	if (ir->t_notify != NULL) {
+		up(ir->t_notify);
+	}
+	
+	dprintk(LOGHEAD "poll thread ended\n", ir->p.name, ir->p.minor);
+	
+	return 0;
+}
+
+/*
+ *
+ */
+int lirc_register_plugin(struct lirc_plugin *p)
+{
+	struct irctl *ir;
+	int minor;
+	int bytes_in_key;
+	int err;
+#ifdef LIRC_HAVE_DEVFS_24
+	char name[16];
+#endif
+	DECLARE_MUTEX_LOCKED(tn);
+
+	if (!p) {
+		printk("lirc_dev: lirc_register_plugin:"
+		       "plugin pointer must be not NULL!\n");
+		err = -EBADRQC;
+		goto out;
+	}
+
+	if (MAX_IRCTL_DEVICES <= p->minor) {
+		printk("lirc_dev: lirc_register_plugin:"
+		       "\" minor\" must be between 0 and %d (%d)!\n",
+		       MAX_IRCTL_DEVICES-1, p->minor);
+		err = -EBADRQC;
+		goto out;
+	}
+
+	if (1 > p->code_length || (BUFLEN*8) < p->code_length) {
+		printk("lirc_dev: lirc_register_plugin:"
+		       "code length in bits for minor (%d) "
+		       "must be less than %d!\n",
+		       p->minor, BUFLEN*8);
+		err = -EBADRQC;
+		goto out;
+	}
+
+	printk("lirc_dev: lirc_register_plugin:"
+	       "sample_rate: %d\n",p->sample_rate);
+	if (p->sample_rate) {
+		if (2 > p->sample_rate || HZ < p->sample_rate) {
+			printk("lirc_dev: lirc_register_plugin:"
+			       "sample_rate must be between 2 and %d!\n", HZ);
+			err = -EBADRQC;
+			goto out;
+		}
+		if (!p->add_to_buf) {
+			printk("lirc_dev: lirc_register_plugin:"
+			       "add_to_buf cannot be NULL when "
+			       "sample_rate is set\n");
+			err = -EBADRQC;
+			goto out;
+		}
+	} else if (!(p->fops && p->fops->read)
+		   && !p->get_queue && !p->rbuf) {
+		printk("lirc_dev: lirc_register_plugin:"
+		       "fops->read, get_queue and rbuf "
+		       "cannot all be NULL!\n");
+		err = -EBADRQC;
+		goto out;
+	} else if (!p->get_queue && !p->rbuf) {
+		if (!(p->fops && p->fops->read && p->fops->poll) 
+		    || (!p->fops->ioctl && !p->ioctl)) {
+			printk("lirc_dev: lirc_register_plugin:"
+			       "neither read, poll nor ioctl can be NULL!\n");
+			err = -EBADRQC;
+			goto out;
+		}
+	}
+
+	down_interruptible(&plugin_lock);
+
+	minor = p->minor;
+
+	if (0 > minor) {
+		/* find first free slot for plugin */
+		for (minor=0; minor<MAX_IRCTL_DEVICES; minor++)
+			if (irctls[minor].p.minor == NOPLUG)
+				break;
+		if (MAX_IRCTL_DEVICES == minor) {
+			printk("lirc_dev: lirc_register_plugin: "
+			       "no free slots for plugins!\n");
+			err = -ENOMEM;
+			goto out_lock;
+		}
+	} else if (irctls[minor].p.minor != NOPLUG) {
+		printk("lirc_dev: lirc_register_plugin:"
+		       "minor (%d) just registered!\n", minor);
+		err = -EBUSY;
+		goto out_lock;
+	}
+
+	ir = &irctls[minor];
+
+	if (p->sample_rate) {
+		ir->jiffies_to_wait = HZ / p->sample_rate;
+	} else {
+                /* it means - wait for externeal event in task queue */
+		ir->jiffies_to_wait = 0;
+	} 
+
+	/* some safety check 8-) */
+	p->name[sizeof(p->name)-1] = '\0';
+
+	bytes_in_key = p->code_length/8 + (p->code_length%8 ? 1 : 0);
+	
+	if (p->rbuf) {
+		ir->buf = p->rbuf;
+	} else {
+		ir->buf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
+		if(!ir->buf) {
+			err = -ENOMEM;
+			goto out_lock;
+		}
+		if(lirc_buffer_init
+		   (ir->buf, bytes_in_key, BUFLEN/bytes_in_key) != 0) {
+			kfree(ir->buf);
+			err = -ENOMEM;
+			goto out_lock;
+		}
+	}
+
+	if (p->features==0)
+		p->features = (p->code_length > 8) ?
+			LIRC_CAN_REC_LIRCCODE : LIRC_CAN_REC_CODE;
+
+	ir->p = *p;
+	ir->p.minor = minor;
+
+#if defined(LIRC_HAVE_DEVFS_24)
+	sprintf (name, DEV_LIRC "/%d", ir->p.minor);
+	ir->devfs_handle = devfs_register(NULL, name, DEVFS_FL_DEFAULT,
+					  IRCTL_DEV_MAJOR, ir->p.minor,
+					  S_IFCHR | S_IRUSR | S_IWUSR,
+					  &fops, NULL);
+#elif defined(LIRC_HAVE_DEVFS_26)
+	devfs_mk_cdev(MKDEV(IRCTL_DEV_MAJOR, ir->p.minor),
+			S_IFCHR|S_IRUSR|S_IWUSR,
+			DEV_LIRC "/%u", ir->p.minor);
+#endif
+	class_simple_device_add(lirc_class, MKDEV(IRCTL_DEV_MAJOR, ir->p.minor),
+				NULL, "lirc%u", ir->p.minor);
+
+	if(p->sample_rate || p->get_queue) {
+		/* try to fire up polling thread */
+		ir->t_notify = &tn;
+		ir->tpid = kernel_thread(lirc_thread, (void*)ir, 0);
+		if (ir->tpid < 0) {
+			printk("lirc_dev: lirc_register_plugin:"
+			       "cannot run poll thread for minor = %d\n",
+			       p->minor);
+			err = -ECHILD;
+			goto out_sysfs;
+		}
+		down(&tn);
+		ir->t_notify = NULL;
+	}
+	up(&plugin_lock);
+
+/*
+ * Recent kernels should handle this autmatically by increasing/decreasing
+ * use count when a dependant module is loaded/unloaded.
+ */
+#ifndef KERNEL_2_5
+	MOD_INC_USE_COUNT;
+#endif
+	dprintk("lirc_dev: plugin %s registered at minor number = %d\n",
+		ir->p.name, ir->p.minor);
+	return minor;
+	
+out_sysfs:
+	class_simple_device_remove(MKDEV(IRCTL_DEV_MAJOR, ir->p.minor));
+#ifdef LIRC_HAVE_DEVFS_24
+	devfs_unregister(ir->devfs_handle);
+#endif
+#ifdef LIRC_HAVE_DEVFS_26
+	devfs_remove(DEV_LIRC "/%i", ir->p.minor);
+#endif
+out_lock:
+	up(&plugin_lock);
+out:
+	return err;
+}
+
+/*
+ *
+ */
+int lirc_unregister_plugin(int minor)
+{
+	struct irctl *ir;
+	DECLARE_MUTEX_LOCKED(tn);
+	DECLARE_MUTEX_LOCKED(tn2);
+
+	if (minor < 0 || minor >= MAX_IRCTL_DEVICES) {
+		printk("lirc_dev: lirc_unregister_plugin:"
+		       "\" minor\" must be between 0 and %d!\n",
+		       MAX_IRCTL_DEVICES-1);
+		return -EBADRQC;
+	}
+
+	ir = &irctls[minor];
+
+	down_interruptible(&plugin_lock);
+
+	if (ir->p.minor != minor) {
+		printk("lirc_dev: lirc_unregister_plugin:"
+		       "minor (%d) device not registered!", minor);
+		up(&plugin_lock);
+		return -ENOENT;
+	}
+
+	if (ir->open) {
+		printk("lirc_dev: lirc_unregister_plugin:"
+		       "plugin %s[%d] in use!", ir->p.name, ir->p.minor);
+		up(&plugin_lock);
+		return -EBUSY;
+	}
+
+	/* end up polling thread */
+	if (ir->tpid >= 0) {
+		ir->t_notify = &tn;
+		ir->t_notify2 = &tn2;
+		ir->shutdown = 1;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+		{
+			struct task_struct *p;
+			
+			p = find_task_by_pid(ir->tpid);
+			wake_up_process(p);
+		}
+#else
+		/* 2.2.x does not export wake_up_process() */
+		wake_up_interruptible(ir->p.get_queue(ir->p.data));
+#endif
+		up(&tn2);
+		down(&tn);
+		ir->t_notify = NULL;
+		ir->t_notify2 = NULL;
+	}
+
+	dprintk("lirc_dev: plugin %s unregistered from minor number = %d\n",
+		ir->p.name, ir->p.minor);
+
+#ifdef LIRC_HAVE_DEVFS_24
+	devfs_unregister(ir->devfs_handle);
+#endif
+#ifdef LIRC_HAVE_DEVFS_26
+	devfs_remove(DEV_LIRC "/%u", ir->p.minor);
+#endif
+	class_simple_device_remove(MKDEV(IRCTL_DEV_MAJOR, ir->p.minor));
+
+	if (ir->buf != ir->p.rbuf){
+		lirc_buffer_free(ir->buf);
+		kfree(ir->buf);
+	}
+	ir->buf = NULL;
+	init_irctl(ir);
+	up(&plugin_lock);
+
+/*
+ * Recent kernels should handle this autmatically by increasing/decreasing
+ * use count when a dependant module is loaded/unloaded.
+ */
+#ifndef KERNEL_2_5
+	MOD_DEC_USE_COUNT;
+#endif
+
+	return SUCCESS;
+}
+
+/*
+ *
+ */
+static int irctl_open(struct inode *inode, struct file *file)
+{
+	struct irctl *ir;
+	int retval;
+	
+	if (MINOR(inode->i_rdev) >= MAX_IRCTL_DEVICES) {
+		dprintk("lirc_dev [%d]: open result = -ENODEV\n",
+			MINOR(inode->i_rdev));
+		return -ENODEV;
+	}
+
+	ir = &irctls[MINOR(inode->i_rdev)];
+
+	dprintk(LOGHEAD "open called\n", ir->p.name, ir->p.minor);
+
+	/* if the plugin has an open function use it instead */
+	if(ir->p.fops && ir->p.fops->open)
+		return ir->p.fops->open(inode, file);
+
+	down_interruptible(&plugin_lock);
+
+	if (ir->p.minor == NOPLUG) {
+		up(&plugin_lock);
+		dprintk(LOGHEAD "open result = -ENODEV\n",
+			ir->p.name, ir->p.minor);
+		return -ENODEV;
+	}
+
+	if (ir->open) {
+		up(&plugin_lock);
+		dprintk(LOGHEAD "open result = -EBUSY\n",
+			ir->p.name, ir->p.minor);
+		return -EBUSY;
+	}
+
+	/* there is no need for locking here because ir->open is 0 
+         * and lirc_thread isn't using buffer
+	 * plugins which use irq's should allocate them on set_use_inc,
+	 * so there should be no problem with those either.
+         */
+	ir->buf->head = ir->buf->tail;
+	ir->buf->fill = 0;
+
+	++ir->open;
+	retval = ir->p.set_use_inc(ir->p.data);
+
+	up(&plugin_lock);
+
+	if (retval != SUCCESS) {
+		--ir->open;
+		return retval;
+	}
+
+	dprintk(LOGHEAD "open result = %d\n", ir->p.name, ir->p.minor, SUCCESS);
+
+	return SUCCESS;
+}
+
+/*
+ *
+ */
+static int irctl_close(struct inode *inode, struct file *file)
+{
+	struct irctl *ir = &irctls[MINOR(inode->i_rdev)];
+
+	dprintk(LOGHEAD "close called\n", ir->p.name, ir->p.minor);
+ 
+	/* if the plugin has a close function use it instead */
+	if(ir->p.fops && ir->p.fops->release)
+		return ir->p.fops->release(inode, file);
+
+	down_interruptible(&plugin_lock);
+
+	--ir->open;
+	ir->p.set_use_dec(ir->p.data);
+
+	up(&plugin_lock);
+
+	return SUCCESS;
+}
+
+/*
+ *
+ */
+static unsigned int irctl_poll(struct file *file, poll_table *wait)
+{
+	struct irctl *ir = &irctls[MINOR(file->f_dentry->d_inode->i_rdev)];
+
+	dprintk(LOGHEAD "poll called\n", ir->p.name, ir->p.minor);
+
+	/* if the plugin has a poll function use it instead */
+	if(ir->p.fops && ir->p.fops->poll)
+		return ir->p.fops->poll(file, wait);
+
+	poll_wait(file, &ir->buf->wait_poll, wait);
+
+	dprintk(LOGHEAD "poll result = %s\n",
+		ir->p.name, ir->p.minor, 
+		lirc_buffer_empty(ir->buf) ? "0" : "POLLIN|POLLRDNORM");
+
+	return lirc_buffer_empty(ir->buf) ? 0 : (POLLIN|POLLRDNORM);
+}
+
+/*
+ *
+ */
+static int irctl_ioctl(struct inode *inode, struct file *file,
+                       unsigned int cmd, unsigned long arg)
+{
+	unsigned long mode;
+	int result;
+	struct irctl *ir = &irctls[MINOR(inode->i_rdev)];
+
+	dprintk(LOGHEAD "ioctl called (%u)\n",
+		ir->p.name, ir->p.minor, cmd);
+
+	/* if the plugin has a ioctl function use it instead */
+	if(ir->p.fops && ir->p.fops->ioctl)
+		return ir->p.fops->ioctl(inode, file, cmd, arg);
+
+	if (ir->p.minor == NOPLUG) {
+		dprintk(LOGHEAD "ioctl result = -ENODEV\n",
+			ir->p.name, ir->p.minor);
+		return -ENODEV;
+	}
+
+	/* Give the plugin a chance to handle the ioctl */
+	if(ir->p.ioctl){
+		result = ir->p.ioctl(inode, file, cmd, arg);
+		if (result != -ENOIOCTLCMD)
+			return result;
+	}
+	/* The plugin can't handle cmd */
+	result = SUCCESS;
+
+	switch(cmd)
+	{
+	case LIRC_GET_FEATURES:
+		result = put_user(ir->p.features, (unsigned long*)arg);
+		break;
+	case LIRC_GET_REC_MODE:
+		if(!(ir->p.features&LIRC_CAN_REC_MASK))
+			return(-ENOSYS);
+		
+		result = put_user(LIRC_REC2MODE
+				  (ir->p.features&LIRC_CAN_REC_MASK),
+				  (unsigned long*)arg);
+		break;
+	case LIRC_SET_REC_MODE:
+		if(!(ir->p.features&LIRC_CAN_REC_MASK))
+			return(-ENOSYS);
+
+		result = get_user(mode, (unsigned long*)arg);
+		if(!result && !(LIRC_MODE2REC(mode) & ir->p.features)) {
+			result = -EINVAL;
+		}
+		/* FIXME: We should actually set the mode somehow 
+		 * but for now, lirc_serial doesn't support mode changin
+		 * eighter */
+		break;
+	case LIRC_GET_LENGTH:
+		result = put_user((unsigned long)ir->p.code_length, 
+				  (unsigned long *)arg);
+		break;
+	default:
+		result = -ENOIOCTLCMD;
+	}
+
+	dprintk(LOGHEAD "ioctl result = %d\n",
+		ir->p.name, ir->p.minor, result);
+
+	return result;
+}
+
+/*
+ *
+ */
+static ssize_t irctl_read(struct file *file,
+			  char *buffer,   
+			  size_t length, 
+			  loff_t *ppos)     
+{
+	struct irctl *ir = &irctls[MINOR(file->f_dentry->d_inode->i_rdev)];
+	unsigned char buf[ir->buf->chunk_size];
+	int ret=0, written=0;
+	DECLARE_WAITQUEUE(wait, current);
+
+	dprintk(LOGHEAD "read called\n", ir->p.name, ir->p.minor);
+
+	/* if the plugin has a specific read function use it instead */
+	if(ir->p.fops && ir->p.fops->read)
+		return ir->p.fops->read(file, buffer, length, ppos);
+
+	if (length % ir->buf->chunk_size) {
+		dprintk(LOGHEAD "read result = -EINVAL\n",
+			ir->p.name, ir->p.minor);
+		return -EINVAL;
+	}
+
+	/* we add ourselves to the task queue before buffer check 
+         * to avoid losing scan code (in case when queue is awaken somewhere 
+	 * beetwen while condition checking and scheduling)
+	 */
+	add_wait_queue(&ir->buf->wait_poll, &wait);
+	current->state = TASK_INTERRUPTIBLE;
+
+	/* while we did't provide 'length' bytes, device is opened in blocking
+	 * mode and 'copy_to_user' is happy, wait for data.
+	 */
+	while (written < length && ret == 0) { 
+		if (lirc_buffer_empty(ir->buf)) {
+			/* According to the read(2) man page, 'written' can be
+			 * returned as less than 'length', instead of blocking
+			 * again, returning -EWOULDBLOCK, or returning
+			 * -ERESTARTSYS */
+			if (written) break;
+			if (file->f_flags & O_NONBLOCK) {
+				dprintk(LOGHEAD "read result = -EWOULDBLOCK\n", 
+						ir->p.name, ir->p.minor);
+				remove_wait_queue(&ir->buf->wait_poll, &wait);
+				current->state = TASK_RUNNING;
+				return -EWOULDBLOCK;
+			}
+			if (signal_pending(current)) {
+				dprintk(LOGHEAD "read result = -ERESTARTSYS\n", 
+						ir->p.name, ir->p.minor);
+				remove_wait_queue(&ir->buf->wait_poll, &wait);
+				current->state = TASK_RUNNING;
+				return -ERESTARTSYS;
+			}
+			schedule();
+			current->state = TASK_INTERRUPTIBLE;
+		} else {
+			lirc_buffer_read_1(ir->buf, buf);
+			ret = copy_to_user((void *)buffer+written, buf,
+					   ir->buf->chunk_size);
+			written += ir->buf->chunk_size;
+		}
+	}
+
+	remove_wait_queue(&ir->buf->wait_poll, &wait);
+	current->state = TASK_RUNNING;
+
+	dprintk(LOGHEAD "read result = %s (%d)\n",
+		ir->p.name, ir->p.minor, ret ? "-EFAULT" : "OK", ret);
+
+	return ret ? -EFAULT : written;
+}
+
+static ssize_t irctl_write(struct file *file, const char *buffer,
+			   size_t length, loff_t * ppos)
+{
+	struct irctl *ir = &irctls[MINOR(file->f_dentry->d_inode->i_rdev)];
+
+	dprintk(LOGHEAD "read called\n", ir->p.name, ir->p.minor);
+
+	/* if the plugin has a specific read function use it instead */
+	if(ir->p.fops && ir->p.fops->write)
+		return ir->p.fops->write(file, buffer, length, ppos);
+
+	return -EINVAL;
+}
+
+
+static struct file_operations fops = {
+	read:    irctl_read, 
+	write:   irctl_write,
+	poll:    irctl_poll,
+	ioctl:   irctl_ioctl,
+	open:    irctl_open,
+	release: irctl_close
+};
+
+
+
+EXPORT_SYMBOL(lirc_register_plugin);
+EXPORT_SYMBOL(lirc_unregister_plugin);
+
+/*
+ *
+ */
+static int lirc_dev_init(void)
+{  	
+	int i;
+
+	for (i=0; i < MAX_IRCTL_DEVICES; ++i) {
+		init_irctl(&irctls[i]);	
+	}
+
+	if(register_chrdev(IRCTL_DEV_MAJOR, IRCTL_DEV_NAME, &fops)) {
+		printk(KERN_ERR "lirc_dev: register_chrdev failed\n");
+		goto out;
+	}
+
+	lirc_class = class_simple_create(THIS_MODULE, "lirc");
+	if(IS_ERR(lirc_class)) {
+		printk(KERN_ERR "lirc_dev: class_simple_create failed\n");
+		goto out_unregister;
+	}
+
+	printk("lirc_dev: IR Remote Control driver registered, at major %d \n", 
+	       IRCTL_DEV_MAJOR);
+
+	return SUCCESS;
+
+out_unregister:
+	if(unregister_chrdev(IRCTL_DEV_MAJOR, IRCTL_DEV_NAME))
+		printk(KERN_ERR "lirc_dev: unregister_chrdev failed!\n");
+out:
+	return -1;
+}
+
+/* ---------------------------------------------------------------------- */
+
+/* For now dont try to use it as a static version !  */
+
+#ifdef MODULE
+
+/*
+ *
+ */
+int init_module(void)
+{
+	return lirc_dev_init();
+}
+
+/*
+ *
+ */
+void cleanup_module(void)
+{
+	int ret;
+
+	ret = unregister_chrdev(IRCTL_DEV_MAJOR, IRCTL_DEV_NAME);
+	class_simple_destroy(lirc_class);
+
+	if(ret)
+		printk("lirc_dev: error in module_unregister_chrdev: %d\n", ret);
+	else
+		dprintk("lirc_dev: module successfully unloaded\n");
+}
+
+MODULE_DESCRIPTION("LIRC base driver module");
+MODULE_AUTHOR("Artur Lipowski");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_CHARDEV_MAJOR(IRCTL_DEV_MAJOR);
+
+module_param(debug, bool, 0644);
+MODULE_PARM_DESC(debug, "Enable debugging messages");
+
+#endif /* MODULE */
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff -urN linux-2.6.10-rc3-mm1/drivers/char/lirc/lirc_dev.h linux-2.6.10-rc3-no2/drivers/char/lirc/lirc_dev.h
--- linux-2.6.10-rc3-mm1/drivers/char/lirc/lirc_dev.h	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.10-rc3-no2/drivers/char/lirc/lirc_dev.h	2004-12-22 19:35:16.557732020 -0500
@@ -0,0 +1,238 @@
+/*
+ * LIRC base driver
+ * 
+ * (L) by Artur Lipowski <alipowski@interia.pl>
+ *        This code is licensed under GNU GPL
+ *
+ * $Id: lirc_dev.h,v 1.14 2004/08/07 10:06:08 lirc Exp $
+ *
+ */
+
+#ifndef _LINUX_LIRC_DEV_H
+#define _LINUX_LIRC_DEV_H
+
+#define MAX_IRCTL_DEVICES 2
+#define BUFLEN            16
+
+//#define LIRC_BUFF_POWER_OF_2
+#ifdef LIRC_BUFF_POWER_OF_2
+#define mod(n, div) ((n) & ((div) -1))
+#else
+#define mod(n, div) ((n) % (div))
+#endif
+#include <linux/slab.h>
+#include <linux/fs.h>
+struct lirc_buffer
+{
+        wait_queue_head_t wait_poll;
+	spinlock_t lock;
+
+	unsigned char *data;
+	unsigned int chunk_size;
+	unsigned int size; /* in chunks */
+	unsigned int fill; /* in chunks */
+	int head, tail;    /* in chunks */
+	/* Using chunks instead of bytes pretends to simplify boundary checking 
+	 * And should allow for some performance fine tunning later */
+};
+static inline int lirc_buffer_init(struct lirc_buffer *buf,
+				    unsigned int chunk_size,
+				    unsigned int size)
+{
+	/* Adjusting size to the next power of 2 would allow for
+	 * inconditional LIRC_BUFF_POWER_OF_2 optimization */
+	init_waitqueue_head(&buf->wait_poll);
+	spin_lock_init(&buf->lock);
+	buf->head = buf->tail = buf->fill = 0;
+	buf->chunk_size = chunk_size;
+	buf->size = size;
+	buf->data = kmalloc(size*chunk_size, GFP_KERNEL);
+	if (buf->data == NULL)
+		return -1;
+	memset(buf->data, 0, size*chunk_size);
+	return 0;
+}
+static inline void lirc_buffer_free(struct lirc_buffer *buf)
+{
+	kfree(buf->data);
+	buf->data = NULL;
+	buf->head = buf->tail = buf->fill = 0;
+	buf->chunk_size = 0;
+	buf->size = 0;
+}
+static inline int  lirc_buffer_full(struct lirc_buffer *buf)
+{
+	return (buf->fill >= buf->size);
+}
+static inline int  lirc_buffer_empty(struct lirc_buffer *buf)
+{
+	return !(buf->fill);
+}
+static inline int  lirc_buffer_available(struct lirc_buffer *buf)
+{
+    return (buf->size - buf->fill);
+}
+static inline void lirc_buffer_lock(struct lirc_buffer *buf, unsigned long *flags)
+{
+	spin_lock_irqsave(&buf->lock, *flags);
+}
+static inline void lirc_buffer_unlock(struct lirc_buffer *buf, unsigned long *flags)
+{
+	spin_unlock_irqrestore(&buf->lock, *flags);
+}
+static inline void _lirc_buffer_remove_1(struct lirc_buffer *buf)
+{
+	buf->head = mod(buf->head+1, buf->size);
+	buf->fill -= 1;
+}
+static inline void lirc_buffer_remove_1(struct lirc_buffer *buf)
+{
+	unsigned long flags;
+	lirc_buffer_lock(buf, &flags);
+	_lirc_buffer_remove_1(buf);
+	lirc_buffer_unlock(buf, &flags);
+}
+static inline void _lirc_buffer_read_1(struct lirc_buffer *buf,
+				     unsigned char *dest)
+{
+	memcpy(dest, &buf->data[buf->head*buf->chunk_size], buf->chunk_size);
+	buf->head = mod(buf->head+1, buf->size);
+	buf->fill -= 1;
+}
+static inline void lirc_buffer_read_1(struct lirc_buffer *buf,
+				      unsigned char *dest)
+{
+	unsigned long flags;
+	lirc_buffer_lock(buf, &flags);
+	_lirc_buffer_read_1(buf, dest);
+	lirc_buffer_unlock(buf, &flags);
+}
+static inline void _lirc_buffer_write_1(struct lirc_buffer *buf,
+				      unsigned char *orig)
+{
+	memcpy(&buf->data[buf->tail*buf->chunk_size], orig, buf->chunk_size);
+	buf->tail = mod(buf->tail+1, buf->size);
+	buf->fill++;
+}
+static inline void lirc_buffer_write_1(struct lirc_buffer *buf,
+				       unsigned char *orig)
+{
+	unsigned long flags;
+	lirc_buffer_lock(buf, &flags);
+	_lirc_buffer_write_1(buf, orig);
+	lirc_buffer_unlock(buf, &flags);
+}
+static inline void _lirc_buffer_write_n(struct lirc_buffer *buf,
+					unsigned char* orig, int count)
+{
+	memcpy(&buf->data[buf->tail*buf->chunk_size], orig,
+	       count*buf->chunk_size);
+	buf->tail = mod(buf->tail+count, buf->size);
+	buf->fill += count;
+}
+static inline void lirc_buffer_write_n(struct lirc_buffer *buf,
+				       unsigned char* orig, int count)
+{
+	unsigned long flags;
+	int space1;
+	lirc_buffer_lock(buf,&flags);
+	if( buf->head > buf->tail ) space1 = buf->head - buf->tail;
+	else space1 = buf->size - buf->tail;
+	
+	if( count > space1 )
+	{
+		_lirc_buffer_write_n(buf, orig, space1);
+		_lirc_buffer_write_n(buf, orig+(space1*buf->chunk_size),
+				     count-space1);
+	}
+	else
+	{
+		_lirc_buffer_write_n(buf, orig, count);
+	}
+	lirc_buffer_unlock(buf, &flags);
+}
+
+struct lirc_plugin
+{
+	char name[40];
+	int minor;
+	int code_length;
+	int sample_rate;
+	unsigned long features;
+	void* data;
+	int (*add_to_buf) (void* data, struct lirc_buffer* buf);
+	wait_queue_head_t* (*get_queue) (void* data);
+	struct lirc_buffer *rbuf;
+	int (*set_use_inc) (void* data);
+	void (*set_use_dec) (void* data);
+	int (*ioctl) (struct inode *,struct file *,unsigned int,
+		      unsigned long);
+	struct file_operations *fops;
+};
+/* name:
+ * this string will be used for logs
+ *
+ * minor:
+ * indicates minor device (/dev/lirc) number for registered plugin
+ * if caller fills it with negative value, then the first free minor 
+ * number will be used (if available)
+ *
+ * code_length:
+ * length of the remote control key code expressed in bits
+ *
+ * sample_rate:
+ * sample_rate equal to 0 means that no polling will be performed and
+ * add_to_buf will be triggered by external events (through task queue
+ * returned by get_queue)
+ *
+ * data:
+ * it may point to any plugin data and this pointer will be passed to
+ * all callback functions
+ *
+ * add_to_buf:
+ * add_to_buf will be called after specified period of the time or
+ * triggered by the external event, this behavior depends on value of
+ * the sample_rate this function will be called in user context. This
+ * routine should return 0 if data was added to the buffer and
+ * -ENODATA if none was available. This should add some number of bits
+ * evenly divisible by code_length to the buffer
+ *
+ * get_queue:
+ * this callback should return a pointer to the task queue which will
+ * be used for external event waiting
+ *
+ * rbuf:
+ * if not NULL, it will be used as a read buffer, you will have to
+ * write to the buffer by other means, like irq's (see also
+ * lirc_serial.c).
+ *
+ * set_use_inc:
+ * set_use_inc will be called after device is opened
+ *
+ * set_use_dec:
+ * set_use_dec will be called after device is closed
+ *
+ * ioctl:
+ * Some ioctl's can be directly handled by lirc_dev but will be
+ * forwared here if not NULL and only handled if it returns
+ * -ENOIOCTLCMD (see also lirc_serial.c).
+ *
+ * fops:
+ * file_operations for drivers which don't fit the current plugin model.
+ */
+
+
+/* following functions can be called ONLY from user context
+ *
+ * returns negative value on error or minor number 
+ * of the registered device if success
+ * contens of the structure pointed by p is copied
+ */
+extern int lirc_register_plugin(struct lirc_plugin *p);
+
+/* returns negative value on error or 0 if success
+*/
+extern int lirc_unregister_plugin(int minor);
+
+
+#endif
diff -urN linux-2.6.10-rc3-mm1/drivers/char/lirc/lirc_gpio.c linux-2.6.10-rc3-no2/drivers/char/lirc/lirc_gpio.c
--- linux-2.6.10-rc3-mm1/drivers/char/lirc/lirc_gpio.c	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.10-rc3-no2/drivers/char/lirc/lirc_gpio.c	2004-12-22 19:35:16.560731603 -0500
@@ -0,0 +1,589 @@
+/*
+ * Remote control driver for the TV-card
+ * key codes are obtained from GPIO port
+ * 
+ * (L) by Artur Lipowski <alipowski@interia.pl>
+ *     patch for the AverMedia by Santiago Garcia Mantinan <manty@i.am>
+ *                            and Christoph Bartelmus <lirc@bartelmus.de>
+ *     patch for the BestBuy by Miguel Angel Alvarez <maacruz@navegalia.com>
+ *     patch for the Winfast TV2000 by Juan Toledo
+ *     <toledo@users.sourceforge.net>
+ *     patch for the I-O Data GV-BCTV5/PCI by Jens C. Rasmussen
+ *     <jens.rasmussen@ieee.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * $Id: lirc_gpio.c,v 1.40 2004/08/07 10:06:08 lirc Exp $
+ *
+ */
+
+#include <linux/version.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 2, 4)
+#error "*******************************************************"
+#error "Sorry, this driver needs kernel version 2.2.4 or higher"
+#error "*******************************************************"
+#endif
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/kmod.h>
+#include <linux/sched.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+#include <linux/wrapper.h>
+#endif
+#include <linux/errno.h>
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
+#include "../drivers/char/bttv.h"
+#include "../drivers/char/bttvp.h"
+#else
+#include "../drivers/media/video/bttv.h"
+#include "../drivers/media/video/bttvp.h"
+#endif
+
+#if BTTV_VERSION_CODE < KERNEL_VERSION(0,7,45)
+#error "*******************************************************"
+#error " Sorry, this driver needs bttv version 0.7.45 or       "
+#error " higher. If you are using the bttv package, copy it to "
+#error " the kernel                                            "
+#error "*******************************************************"
+#endif
+
+#include "linux/kcompat.h"
+#include "drivers/lirc_dev/lirc_dev.h"
+
+/* insmod parameters */
+static int debug = 0;
+static int card = 0;
+static int minor = -1;
+static int bttv_id = BTTV_UNKNOWN;
+static unsigned long gpio_mask = 0;
+static unsigned long gpio_enable = 0;
+static unsigned long gpio_lock_mask = 0;
+static unsigned long gpio_xor_mask = 0;
+static int soft_gap = 0;
+static int sample_rate = 10;
+
+#define dprintk(fmt, args...)                                 \
+	do{                                                   \
+		if(debug) printk(KERN_DEBUG fmt, ## args);    \
+	}while(0)
+
+struct rcv_info {
+	int bttv_id;
+	int card_id;
+	unsigned long gpio_mask;
+	unsigned long gpio_enable;
+	unsigned long gpio_lock_mask;
+	unsigned long gpio_xor_mask;
+	int soft_gap;
+	int sample_rate;
+	unsigned char code_length;
+};
+
+static struct rcv_info rcv_infos[] = {
+	{BTTV_UNKNOWN,                0,          0,          0,         0,          0,   0,  1,  0},
+	{BTTV_PXELVWPLTVPAK,          0, 0x00003e00,          0, 0x0010000,          0,   0, 15, 32},
+	{BTTV_PXELVWPLTVPRO,          0, 0x00001f00,          0, 0x0008000,          0, 500, 12, 32},
+	{BTTV_PV_BT878P_9B,           0, 0x00001f00,          0, 0x0008000,          0, 500, 12, 32},
+	{BTTV_PV_BT878P_PLUS,         0, 0x00001f00,          0, 0x0008000,          0, 500, 12, 32},
+	{BTTV_AVERMEDIA,              0, 0x00f88000,          0, 0x0010000, 0x00010000,   0, 10, 32},
+	{BTTV_AVPHONE98,     0x00011461, 0x003b8000, 0x00004000, 0x0800000, 0x00800000,   0, 10,  0}, /*mapped to Capture98*/
+	{BTTV_AVERMEDIA98,   0x00021461, 0x003b8000, 0x00004000, 0x0800000, 0x00800000,   0, 10,  0}, /*mapped to Capture98*/
+	{BTTV_AVPHONE98,     0x00031461, 0x00f88000,          0, 0x0010000, 0x00010000,   0, 10, 32}, /*mapped to Phone98*/
+	/* is this one correct? */
+	{BTTV_AVERMEDIA98,   0x00041461, 0x00f88000,          0, 0x0010000, 0x00010000,   0, 10, 32}, /*mapped to Phone98*/
+	/* work-around for VDOMATE */
+	{BTTV_AVERMEDIA98,   0x03001461, 0x00f88000,          0, 0x0010000, 0x00010000,   0, 10, 32}, /*mapped to Phone98*/
+	/* reported by Danijel Korzinek, AVerTV GOw/FM */
+	{BTTV_AVERMEDIA98,   0x00000000, 0x00f88000,          0, 0x0010000, 0x00010000,   0, 10, 32}, /*mapped to Phone98*/
+	{BTTV_CHRONOS_VS2,            0, 0x000000f8,          0, 0x0000100,          0,   0, 20,  0},
+	/* CPH031 and CPH033 cards (?) */
+	/* MIRO was just a work-around */
+	{BTTV_MIRO,                   0, 0x00001f00,          0, 0x0004000,          0,   0, 10, 32},
+	{BTTV_DYNALINK,               0, 0x00001f00,          0, 0x0004000,          0,   0, 10, 32},
+	{BTTV_WINVIEW_601,            0, 0x00001f00,          0, 0x0004000,          0,   0,  0, 32},
+#ifdef BTTV_KWORLD
+	{BTTV_KWORLD,                 0, 0x00007f00,          0, 0x0004000,          0,   0, 12, 32},
+#endif
+	/* just a guess */
+	{BTTV_MAGICTVIEW061,          0, 0x0028e000,          0, 0x0020000,          0,   0, 20, 32},
+ 	{BTTV_MAGICTVIEW063,          0, 0x0028e000,          0, 0x0020000,          0,   0, 20, 32},
+ 	{BTTV_PHOEBE_TVMAS,           0, 0x0028e000,          0, 0x0020000,          0,   0, 20, 32},
+#ifdef BTTV_BESTBUY_EASYTV2
+        {BTTV_BESTBUY_EASYTV,         0, 0x00007F00,          0, 0x0004000,          0,   0, 10,  8},
+        {BTTV_BESTBUY_EASYTV2,        0, 0x00007F00,          0, 0x0008000,          0,   0, 10,  8},
+#endif
+	/* lock_mask probably also 0x100, or maybe it is 0x0 for all others !?! */
+	{BTTV_FLYVIDEO,               0, 0x000000f8,          0,         0,          0,   0,  0, 42},
+ 	{BTTV_FLYVIDEO_98,            0, 0x000000f8,          0, 0x0000100,          0,   0,  0, 42},
+ 	{BTTV_TYPHOON_TVIEW,          0, 0x000000f8,          0, 0x0000100,          0,   0,  0, 42},
+#ifdef BTTV_FLYVIDEO_98FM
+	/* smorar@alfonzo.smuts.uct.ac.za */
+	{BTTV_FLYVIDEO_98FM,          0, 0x000000f8,          0, 0x0000100,          0,   0,  0, 42},
+#endif
+	/* The Leadtek WinFast TV 2000 XP card (id 0x6606107d) uses an
+	 * extra gpio bit compared to the original TV 2000 card (id
+	 * 0x217d6606); as the bttv-0.7.100 driver does not
+	 * distinguish between the two cards, we enable the extra bit
+	 * based on the card id: */
+	{BTTV_WINFAST2000,   0x6606107d, 0x000008f8,          0, 0x0000100,          0,   0,  0, 32},
+	/* default: */
+	{BTTV_WINFAST2000,            0, 0x000000f8,          0, 0x0000100,          0,   0,  0, 32},
+#ifdef BTTV_GVBCTV5PCI
+	{BTTV_GVBCTV5PCI,             0, 0x00f0b000,          0,         0,          0,   0, 20,  8},
+#endif
+};
+
+static unsigned char code_length = 0;
+static unsigned char code_bytes = 1;
+
+#define MAX_BYTES 8
+
+#define SUCCESS 0
+#define LOGHEAD "lirc_gpio (%d): "
+
+/* how many bits GPIO value can be shifted right before processing
+ * it is computed from the value of gpio_mask_parameter
+ */
+static unsigned char gpio_pre_shift = 0;
+
+
+static inline int reverse(int data, int bits)
+{
+	int i;
+	int c;
+	
+	for (c=0,i=0; i<bits; i++) {
+		c |= (((data & (1<<i)) ? 1:0)) << (bits-1-i);
+	}
+
+	return c;
+}
+
+static int build_key(unsigned long gpio_val, unsigned char codes[MAX_BYTES])
+{
+	unsigned long mask = gpio_mask;
+	unsigned char shift = 0;
+
+	dprintk(LOGHEAD "gpio_val is %lx\n",card,(unsigned long) gpio_val);
+	
+	gpio_val ^= gpio_xor_mask;
+	
+	if (gpio_lock_mask && (gpio_val & gpio_lock_mask)) {
+		return -EBUSY;
+	}
+	
+	switch (bttv_id)
+	{
+	case BTTV_AVERMEDIA98:
+		if (bttv_write_gpio(card, gpio_enable, gpio_enable)) {
+			dprintk(LOGHEAD "cannot write to GPIO\n", card);
+			return -EIO;
+		}
+		if (bttv_read_gpio(card, &gpio_val)) {
+			dprintk(LOGHEAD "cannot read GPIO\n", card);
+			return -EIO;
+		}
+		if (bttv_write_gpio(card, gpio_enable, 0)) {
+			dprintk(LOGHEAD "cannot write to GPIO\n", card);
+			return -EIO;
+		}
+		break;
+	default:
+		break;
+	}
+	
+	/* extract bits from "raw" GPIO value using gpio_mask */
+	codes[0] = 0;
+	gpio_val >>= gpio_pre_shift;
+	while (mask) {
+		if (mask & 1u) {
+			codes[0] |= (gpio_val & 1u) << shift++;
+		}
+		mask >>= 1;
+		gpio_val >>= 1;
+	}
+	
+	dprintk(LOGHEAD "code is %lx\n",card,(unsigned long) codes[0]);
+	switch (bttv_id)
+	{
+	case BTTV_AVERMEDIA:
+		codes[2] = (codes[0]<<2)&0xff;
+		codes[3] = (~codes[2])&0xff;
+		codes[0] = 0x02;
+		codes[1] = 0xFD;
+		break;
+	case BTTV_AVPHONE98:
+		codes[2] = ((codes[0]&(~0x1))<<2)&0xff;
+		codes[3] = (~codes[2])&0xff;
+		if (codes[0]&0x1) {
+			codes[0] = 0xc0;
+			codes[1] = 0x3f;
+		} else {
+			codes[0] = 0x40;
+			codes[1] = 0xbf;
+		}
+		break;
+	case BTTV_AVERMEDIA98:
+		break;
+	case BTTV_FLYVIDEO:
+	case BTTV_FLYVIDEO_98:
+	case BTTV_TYPHOON_TVIEW:
+#ifdef BTTV_FLYVIDEO_98FM
+	case BTTV_FLYVIDEO_98FM:
+#endif
+		codes[4]=codes[0]<<3;
+		codes[5]=((~codes[4])&0xff);
+		
+		codes[0]=0x00;
+		codes[1]=0x1A;
+		codes[2]=0x1F;
+		codes[3]=0x2F;
+		break;
+        case BTTV_MAGICTVIEW061:
+        case BTTV_MAGICTVIEW063:
+	case BTTV_PHOEBE_TVMAS:
+		codes[0] = (codes[0]&0x01)
+			|((codes[0]&0x02)<<1)
+			|((codes[0]&0x04)<<2)
+			|((codes[0]&0x08)>>2)
+			|((codes[0]&0x10)>>1);
+		/* FALLTHROUGH */
+	case BTTV_MIRO:
+	case BTTV_DYNALINK:
+	case BTTV_PXELVWPLTVPAK:
+	case BTTV_PXELVWPLTVPRO:
+	case BTTV_PV_BT878P_9B:
+	case BTTV_PV_BT878P_PLUS:
+#ifdef BTTV_KWORLD
+	case BTTV_KWORLD:
+#endif
+		codes[2] = reverse(codes[0],8);
+		codes[3] = (~codes[2])&0xff;
+		codes[0] = 0x61;
+		codes[1] = 0xD6;
+		break;
+#if 0
+		/* derived from e-tech config file */
+		/* 26 + 16 bits */
+		/* won't apply it until it's confirmed with a fly98 */
+ 	case BTTV_FLYVIDEO_98:
+	case BTTV_FLYVIDEO_98FM:
+		codes[4]=codes[0]<<3;
+		codes[5]=(~codes[4])&0xff;
+		
+		codes[0]=0x00;
+		codes[1]=0x1A;
+		codes[2]=0x1F;
+		codes[3]=0x2F;
+		break;
+#endif
+	case BTTV_WINFAST2000:
+		/* shift extra bit */
+		codes[0] = (codes[0]&0x1f) | ((codes[0]&0x20) << 1);
+	case BTTV_WINVIEW_601:
+		codes[2] = reverse(codes[0],8);
+		codes[3] = (~codes[2])&0xff;
+		codes[0] = 0xC0;
+		codes[1] = 0x3F;
+		break;
+	default:
+		break;
+	}
+
+	return SUCCESS;
+}
+
+/* add_to_buf - copy a code to the buffer */
+static int add_to_buf(void* data, struct lirc_buffer* buf)
+{
+	static unsigned long next_time = 0;
+	static unsigned char prev_codes[MAX_BYTES];
+	unsigned long code = 0;
+	unsigned char cur_codes[MAX_BYTES];
+    
+	if (bttv_read_gpio(card, &code)) {
+		dprintk(LOGHEAD "cannot read GPIO\n", card);
+		return -EIO;
+	}
+	
+	if (build_key(code, cur_codes)) {
+		return -EFAULT;
+	}
+	
+	if (soft_gap) {
+		if (!memcmp(prev_codes, cur_codes, code_bytes) &&
+			jiffies < next_time) {
+			return -EAGAIN;
+		}
+		next_time = jiffies + soft_gap;
+	}
+	memcpy( prev_codes, cur_codes, code_bytes );
+		
+	lirc_buffer_write_1( buf, cur_codes );
+		
+	return SUCCESS;
+}
+
+static int set_use_inc(void* data)
+{
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static void set_use_dec(void* data)
+{
+	MOD_DEC_USE_COUNT;
+}
+
+static wait_queue_head_t* get_queue(void* data)
+{
+	return bttv_get_gpio_queue(card);
+}
+
+static struct lirc_plugin plugin = {
+	.name		= "lirc_gpio  ",
+	.add_to_buf	= add_to_buf,
+	.get_queue	= get_queue,
+	.set_use_inc	= set_use_inc,
+	.set_use_dec	= set_use_dec,
+};
+
+/*
+ *
+ */
+static int gpio_remote_init(void)
+{  	
+	int ret;
+	unsigned int mask;
+
+	/* "normalize" gpio_mask
+	 * this means shift it right until first bit is set
+	 */
+	while (!(gpio_mask & 1u)) {
+		gpio_pre_shift++;
+		gpio_mask >>= 1;
+	}
+
+	if (code_length) {
+		plugin.code_length = code_length;
+	} else {
+		/* calculate scan code length in bits if needed */
+		plugin.code_length = 1;
+		mask = gpio_mask >> 1;
+		while (mask) {
+			if (mask & 1u) {
+				plugin.code_length++;
+			}
+			mask >>= 1;
+		}
+	}
+
+	code_bytes = (plugin.code_length/8) + (plugin.code_length%8 ? 1 : 0);
+	if (MAX_BYTES < code_bytes) {
+		printk (LOGHEAD "scan code too long (%d bytes)\n",
+			minor, code_bytes);
+		return -EBADRQC;
+	}
+
+	if (gpio_enable) {
+		if(bttv_gpio_enable(card, gpio_enable, gpio_enable)) {
+			printk(LOGHEAD "gpio_enable failure\n", minor);
+			return -EIO;
+		}
+	}
+
+
+	/* translate ms to jiffies */
+	soft_gap = (soft_gap*HZ) / 1000;
+
+	plugin.minor = minor;
+	plugin.sample_rate = sample_rate;
+
+	ret = lirc_register_plugin(&plugin);
+	
+	if (0 > ret) {
+		printk (LOGHEAD "device registration failed with %d\n",
+			minor, ret);
+		return ret;
+	}
+	
+	minor = ret;
+	printk(LOGHEAD "driver registered\n", minor);
+
+	return SUCCESS;
+}
+
+#ifdef MODULE
+/*
+ *
+ */
+int init_module(void)
+{
+	int type,cardid,card_type;
+
+	if (MAX_IRCTL_DEVICES < minor) {
+		printk("lirc_gpio: parameter minor (%d) must be less than %d!\n",
+		       minor, MAX_IRCTL_DEVICES-1);
+		return -EBADRQC;
+	}
+	
+	request_module("bttv");
+
+	/* if gpio_mask not zero then use module parameters 
+	 * instead of autodetecting TV card
+	 */
+	if (gpio_mask) {
+		if (sample_rate!=0 &&
+		    (2 > sample_rate || HZ < sample_rate)) {
+			printk(LOGHEAD "parameter sample_rate "
+			       "must be between 2 and %d!\n", minor, HZ);
+			return -EBADRQC;
+		}
+
+		if (sample_rate!=0 && soft_gap && 
+		    ((2000/sample_rate) > soft_gap || 1000 < soft_gap)) {
+			printk(LOGHEAD "parameter soft_gap "
+			       "must be between %d and 1000!\n",
+			       minor, 2000/sample_rate);
+			return -EBADRQC;
+		}
+	} else {
+		if(bttv_get_cardinfo(card,&type,&cardid)==-1) {
+			printk(LOGHEAD "could not get card type\n", minor);
+			return -EBADRQC;
+		}
+		printk(LOGHEAD "card type 0x%x, id 0x%x\n",minor,
+		       type,cardid);
+
+		if (type == BTTV_UNKNOWN) {
+			printk(LOGHEAD "cannot detect TV card nr %d!\n",
+			       minor, card);
+			return -EBADRQC;
+		}
+		for (card_type = 1;
+		     card_type < sizeof(rcv_infos)/sizeof(struct rcv_info); 
+		     card_type++) {
+			if (rcv_infos[card_type].bttv_id == type &&
+			    (rcv_infos[card_type].card_id == 0 ||
+			     rcv_infos[card_type].card_id == cardid)) {
+				bttv_id = rcv_infos[card_type].bttv_id;
+				gpio_mask = rcv_infos[card_type].gpio_mask;
+				gpio_enable = rcv_infos[card_type].gpio_enable;
+				gpio_lock_mask = rcv_infos[card_type].gpio_lock_mask;
+				gpio_xor_mask = rcv_infos[card_type].gpio_xor_mask;
+				soft_gap = rcv_infos[card_type].soft_gap;
+				sample_rate = rcv_infos[card_type].sample_rate;
+				code_length = rcv_infos[card_type].code_length;
+				break;
+			}
+		}
+		if (type==BTTV_AVPHONE98 && cardid==0x00011461)	{
+			bttv_id = BTTV_AVERMEDIA98;
+		}
+		if (type==BTTV_AVERMEDIA98 && cardid==0x00041461) {
+			bttv_id = BTTV_AVPHONE98;
+		}
+		if (type==BTTV_AVERMEDIA98 && cardid==0x03001461) {
+			bttv_id = BTTV_AVPHONE98;
+		}
+		if (type==BTTV_AVERMEDIA98 && cardid==0x00000000) {
+			bttv_id = BTTV_AVPHONE98;
+		}
+		if (card_type == sizeof(rcv_infos)/sizeof(struct rcv_info)) {
+			printk(LOGHEAD "TV card type 0x%x not supported!\n",
+			       minor, type);
+			return -EBADRQC;
+		}
+	}
+
+	request_module("lirc_dev");
+
+	return gpio_remote_init();
+}
+
+/*
+ *
+ */
+void cleanup_module(void)
+{
+	int ret;
+
+	ret = lirc_unregister_plugin(minor);
+ 
+	if (0 > ret) {
+		printk(LOGHEAD "error in lirc_unregister_minor: %d\n"
+		       "Trying again...\n",
+		       minor, ret);
+
+		current->state = TASK_INTERRUPTIBLE;
+		schedule_timeout(HZ);
+
+		ret = lirc_unregister_plugin(minor);
+ 
+		if (0 > ret) {
+			printk(LOGHEAD "error in lirc_unregister_minor: %d!!!\n",
+			       minor, ret);
+			return;
+		}
+	}
+
+	dprintk(LOGHEAD "module successfully unloaded\n", minor);
+}
+
+/* Dont try to use it as a static version !  */
+MODULE_DESCRIPTION("Driver module for remote control (data from bt848 GPIO port)");
+MODULE_AUTHOR("Artur Lipowski");
+MODULE_LICENSE("GPL");
+
+module_param(minor, int, 0444);
+MODULE_PARM_DESC(minor, "Preferred minor device number");
+
+module_param(card, int, 0444);
+MODULE_PARM_DESC(card, "TV card number to attach to");
+
+module_param(gpio_mask, long, 0444);
+MODULE_PARM_DESC(gpio_mask, "gpio_mask");
+
+module_param(gpio_lock_mask, long, 0444);
+MODULE_PARM_DESC(gpio_lock_mask, "gpio_lock_mask");
+
+module_param(gpio_xor_mask, long, 0444);
+MODULE_PARM_DESC(gpio_xor_mask, "gpio_xor_mask");
+
+module_param(soft_gap, int, 0444);
+MODULE_PARM_DESC(soft_gap, "Time between keypresses (in ms)");
+
+module_param(sample_rate, int, 0444);
+MODULE_PARM_DESC(sample_rate, "Sample rate (between 2 and HZ)");
+
+module_param(bttv_id, int, 0444);
+MODULE_PARM_DESC(bttv_id, "BTTV card type");
+
+module_param(debug, bool, 0644);
+MODULE_PARM_DESC(debug, "Enable debugging messages");
+
+EXPORT_NO_SYMBOLS;
+
+#endif /* MODULE */
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff -urN linux-2.6.10-rc3-mm1/drivers/char/lirc/lirc_i2c.c linux-2.6.10-rc3-no2/drivers/char/lirc/lirc_i2c.c
--- linux-2.6.10-rc3-mm1/drivers/char/lirc/lirc_i2c.c	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.10-rc3-no2/drivers/char/lirc/lirc_i2c.c	2004-12-22 19:35:16.563731186 -0500
@@ -0,0 +1,546 @@
+/*      $Id: lirc_i2c.c,v 1.27 2004/12/01 18:53:12 lirc Exp $      */
+
+/*
+ * i2c IR lirc plugin for Hauppauge and Pixelview cards - new 2.3.x i2c stack
+ *
+ * Copyright (c) 2000 Gerd Knorr <kraxel@goldbach.in-berlin.de>
+ * modified for PixelView (BT878P+W/FM) by
+ *      Michal Kochanowicz <mkochano@pld.org.pl>
+ *      Christoph Bartelmus <lirc@bartelmus.de>
+ * modified for KNC ONE TV Station/Anubis Typhoon TView Tuner by
+ *      Ulrich Mueller <ulrich.mueller42@web.de>
+ * modified for Asus TV-Box and Creative/VisionTek BreakOut-Box by
+ *      Stefan Jahn <stefan@lkcc.org>
+ *
+ * parts are cut&pasted from the old lirc_haup.c driver
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <linux/version.h>
+#if LINUX_VERSION_CODE < 0x020200
+#error "--- Sorry, this driver needs kernel version 2.2.0 or higher. ---"
+#endif
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <linux/module.h>
+#include <linux/kmod.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+
+#ifndef I2C_CLIENT_END
+#error "********************************************************"
+#error " Sorry, this driver needs the new I2C stack.            "
+#error " You can get it at http://www2.lm-sensors.nu/~lm78/.    "
+#error "********************************************************"
+#endif
+
+#include <linux/i2c-algo-bit.h>
+
+#include <asm/semaphore.h>
+
+#include "linux/kcompat.h"
+#include "drivers/lirc_dev/lirc_dev.h"
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
+#include "../drivers/char/bttv.h"
+#else
+#include "../drivers/media/video/bttv.h"
+#endif
+
+struct IR {
+	struct lirc_plugin l;
+	struct i2c_client  c;
+	int nextkey;
+	unsigned char b[3];
+	unsigned char bits;
+	unsigned char flag;
+};
+
+/* ----------------------------------------------------------------------- */
+
+#define DEVICE_NAME "lirc_i2c"
+
+/* ----------------------------------------------------------------------- */
+/* insmod parameters                                                       */
+
+static int debug   = 0;    /* debug output */
+static int minor   = -1;   /* minor number */
+
+#define dprintk(fmt, args...)                                           \
+	do{                                                             \
+		if(debug) printk(KERN_DEBUG DEVICE_NAME ": " fmt,       \
+				 ## args);                              \
+	}while(0)
+
+/* ----------------------------------------------------------------------- */
+
+static inline int reverse(int data, int bits)
+{
+	int i;
+	int c;
+	
+	for (c=0,i=0; i<bits; i++) {
+		c |= (((data & (1<<i)) ? 1:0)) << (bits-1-i);
+	}
+
+	return c;
+}
+
+static int add_to_buf_pcf8574(void* data, struct lirc_buffer* buf)
+{
+	struct IR *ir = data;
+	int rc;
+	unsigned char all, mask;
+	unsigned char key;
+
+	/* compute all valid bits (key code + pressed/release flag) */
+	all = ir->bits | ir->flag;
+
+	/* save IR writable mask bits */
+	mask = i2c_smbus_read_byte(&ir->c) & ~all;
+
+	/* send bit mask */
+	rc = i2c_smbus_write_byte(&ir->c, (0xff & all) | mask);
+
+	/* receive scan code */
+	rc = i2c_smbus_read_byte(&ir->c);
+
+	if (rc == -1) {
+		dprintk("%s read error\n", ir->c.name);
+		return -EIO;
+	}
+
+	/* drop duplicate polls */
+	if (ir->b[0] == (rc & all)) {
+		return -ENODATA;
+	}
+	ir->b[0] = rc & all;
+
+	dprintk("%s key 0x%02X %s\n", ir->c.name, rc & ir->bits,
+		(rc & ir->flag) ? "released" : "pressed");
+
+	if (rc & ir->flag) {
+		/* ignore released buttons */
+		return -ENODATA;
+	}
+
+	/* set valid key code */
+	key  = rc & ir->bits;
+	lirc_buffer_write_1( buf, &key );
+	return 0;
+}
+
+static int add_to_buf_haup(void* data, struct lirc_buffer* buf)
+{
+	struct IR *ir = data;
+	unsigned char keybuf[3];
+	__u16 code;
+	unsigned char codes[2];
+
+	/* poll IR chip */
+	if (3 == i2c_master_recv(&ir->c,keybuf,3)) {
+		ir->b[0] = keybuf[0];
+		ir->b[1] = keybuf[1];
+		ir->b[2] = keybuf[2];
+		dprintk("key (0x%02x/0x%02x)\n", ir->b[0], ir->b[1]);
+	} else {
+		dprintk("read error\n");
+		/* keep last successfull read buffer */
+	}
+
+	/* key pressed ? */
+	if ((ir->b[0] & 0x80) == 0)
+		return -ENODATA;
+	
+	/* look what we have */
+	code = (((__u16)ir->b[0]&0x7f)<<6) | (ir->b[1]>>2);
+	
+	codes[0] = (code >> 8) & 0xff;
+	codes[1] = code & 0xff;
+
+	/* return it */
+	lirc_buffer_write_1( buf, codes );
+	return 0;
+}
+
+static int add_to_buf_pixelview(void* data, struct lirc_buffer* buf)
+{
+	struct IR *ir = data;
+	unsigned char key;
+	
+	/* poll IR chip */
+	if (1 != i2c_master_recv(&ir->c,&key,1)) {
+		dprintk("read error\n");
+		return -1;
+	}
+	dprintk("key %02x\n", key);
+
+	/* return it */
+	lirc_buffer_write_1( buf, &key );
+	return 0;
+}
+
+static int add_to_buf_pv951(void* data, struct lirc_buffer* buf)
+{
+	struct IR *ir = data;
+	unsigned char key;
+	unsigned char codes[4];
+	
+	/* poll IR chip */
+	if (1 != i2c_master_recv(&ir->c,&key,1)) {
+		dprintk("read error\n");
+		return -ENODATA;
+	}
+	/* ignore 0xaa */
+	if (key==0xaa)
+		return -ENODATA;
+	dprintk("key %02x\n", key);
+
+	codes[0] = 0x61;
+	codes[1] = 0xD6;
+	codes[2] = reverse(key,8);
+	codes[3] = (~codes[2])&0xff;
+	
+	lirc_buffer_write_1( buf, codes );
+	return 0;
+}
+
+static int add_to_buf_knc1(void *data, struct lirc_buffer* buf)
+{
+	static unsigned char last_key = 0xFF;
+	struct IR *ir = data;
+	unsigned char key;
+	
+	/* poll IR chip */
+	if (1 != i2c_master_recv(&ir->c,&key,1)) {
+		dprintk("read error\n");
+		return -ENODATA;
+	}
+	
+	/* it seems that 0xFE indicates that a button is still hold
+	   down, while 0xFF indicates that no button is hold
+	   down. 0xFE sequences are sometimes interrupted by 0xFF */
+	
+	dprintk("key %02x\n", key);
+	
+	if( key == 0xFF )
+		return -ENODATA;
+	
+	if ( key == 0xFE )
+		key = last_key;
+
+	last_key = key;
+	lirc_buffer_write_1( buf, &key );
+
+	return 0;
+}
+
+static int set_use_inc(void* data)
+{
+	struct IR *ir = data;
+
+	/* lock bttv in memory while /dev/lirc is in use  */
+	/* this is completely broken code. lirc_unregister_plugin()
+	   must be possible even when the device is open */
+#ifdef KERNEL_2_5
+	i2c_use_client(&ir->c);
+#else
+	if (ir->c.adapter->inc_use) 
+		ir->c.adapter->inc_use(ir->c.adapter);
+#endif
+
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static void set_use_dec(void* data)
+{
+	struct IR *ir = data;
+
+#ifdef KERNEL_2_5
+	i2c_release_client(&ir->c);
+#else
+	if (ir->c.adapter->dec_use) 
+		ir->c.adapter->dec_use(ir->c.adapter);
+#endif
+	MOD_DEC_USE_COUNT;
+}
+
+static struct lirc_plugin lirc_template = {
+	name:        "lirc_i2c",
+	set_use_inc: set_use_inc,
+	set_use_dec: set_use_dec
+};
+
+/* ----------------------------------------------------------------------- */
+
+static int ir_attach(struct i2c_adapter *adap, int addr,
+		      unsigned short flags, int kind);
+static int ir_detach(struct i2c_client *client);
+static int ir_probe(struct i2c_adapter *adap);
+static int ir_command(struct i2c_client *client, unsigned int cmd, void *arg);
+
+static struct i2c_driver driver = {
+        name:           "i2c ir driver",
+        id:             I2C_DRIVERID_EXP3, /* FIXME */
+        flags:          I2C_DF_NOTIFY,
+        attach_adapter: ir_probe,
+        detach_client:  ir_detach,
+        command:        ir_command,
+};
+
+static struct i2c_client client_template = 
+{
+        name:   "unset",
+        driver: &driver
+};
+
+static int ir_attach(struct i2c_adapter *adap, int addr,
+		     unsigned short flags, int kind)
+{
+        struct IR *ir;
+	
+        client_template.adapter = adap;
+        client_template.addr = addr;
+	
+        if (NULL == (ir = kmalloc(sizeof(struct IR),GFP_KERNEL)))
+                return -ENOMEM;
+        memcpy(&ir->l,&lirc_template,sizeof(struct lirc_plugin));
+        memcpy(&ir->c,&client_template,sizeof(struct i2c_client));
+	
+	ir->c.adapter = adap;
+	ir->c.addr    = addr;
+#ifdef KERNEL_2_5
+	i2c_set_clientdata(&ir->c, ir);
+#else
+	ir->c.data    = ir;
+#endif
+	ir->l.data    = ir;
+	ir->l.minor   = minor;
+	ir->l.sample_rate = 10;
+	ir->nextkey   = -1;
+
+	switch(addr)
+	{
+	case 0x64:
+		strcpy(ir->c.name,"Pixelview IR");
+		ir->l.code_length = 8;
+		ir->l.add_to_buf=add_to_buf_pixelview;
+		break;
+	case 0x4b:
+		strcpy(ir->c.name,"PV951 IR");
+		ir->l.code_length = 32;
+		ir->l.add_to_buf=add_to_buf_pv951;
+		break;
+	case 0x18:
+	case 0x1a:
+		strcpy(ir->c.name,"Hauppauge IR");
+		ir->l.code_length = 13;
+		ir->l.add_to_buf=add_to_buf_haup;
+		break;
+	case 0x30:
+		strcpy(ir->c.name,"KNC ONE IR");
+		ir->l.code_length = 8;
+		ir->l.add_to_buf=add_to_buf_knc1;
+		break;
+	case 0x21:
+	case 0x23:
+		strcpy(ir->c.name,"TV-Box IR");
+		ir->l.code_length = 8;
+		ir->l.add_to_buf=add_to_buf_pcf8574;
+		ir->bits = flags & 0xff;
+		ir->flag = (flags >> 8) & 0xff;
+		break;
+		
+	default:
+		/* shouldn't happen */
+		printk("lirc_i2c: Huh? unknown i2c address (0x%02x)?\n",addr);
+		kfree(ir);
+		return -1;
+	}
+	printk("lirc_i2c: chip found @ 0x%02x (%s)\n",addr,ir->c.name);
+	
+	/* register device */
+	i2c_attach_client(&ir->c);
+	ir->l.minor = lirc_register_plugin(&ir->l);
+#ifdef KERNEL_2_5
+	i2c_use_client(&ir->c);
+#else
+	if (ir->c.adapter->inc_use) 
+		ir->c.adapter->inc_use(ir->c.adapter);
+#endif
+	
+	return 0;
+}
+
+static int ir_detach(struct i2c_client *client)
+{
+#ifdef KERNEL_2_5
+	struct IR *ir = i2c_get_clientdata(client);
+#else
+        struct IR *ir = client->data;
+#endif
+	
+	/* unregister device */
+#ifdef KERNEL_2_5
+	i2c_release_client(&ir->c);
+#else
+	if (ir->c.adapter->dec_use) 
+		ir->c.adapter->dec_use(ir->c.adapter);
+#endif
+	lirc_unregister_plugin(ir->l.minor);
+	i2c_detach_client(&ir->c);
+
+	/* free memory */
+	kfree(ir);
+	return 0;
+}
+
+static int ir_probe(struct i2c_adapter *adap) {
+	
+	/* The external IR receiver is at i2c address 0x34 (0x35 for
+	   reads).  Future Hauppauge cards will have an internal
+	   receiver at 0x30 (0x31 for reads).  In theory, both can be
+	   fitted, and Hauppauge suggest an external overrides an
+	   internal. 
+	   
+	   That's why we probe 0x1a (~0x34) first. CB 
+	*/
+	
+	static const int probe[] = { 0x1a, 0x18, 0x4b, 0x64, 0x30, -1};
+	struct i2c_client c; char buf; int i,rc;
+
+	if (adap->id == (I2C_ALGO_BIT | I2C_HW_B_BT848)) {
+		memset(&c,0,sizeof(c));
+		c.adapter = adap;
+		for (i = 0; -1 != probe[i]; i++) {
+			c.addr = probe[i];
+			rc = i2c_master_recv(&c,&buf,1);
+			dprintk("probe 0x%02x @ %s: %s\n",
+				probe[i], adap->name, 
+				(1 == rc) ? "yes" : "no");
+			if (1 == rc)
+			{
+				ir_attach(adap,probe[i],0,0);
+			}
+		}
+	}
+
+	/* Asus TV-Box and Creative/VisionTek BreakOut-Box (PCF8574) */
+	else if (adap->id == (I2C_ALGO_BIT | I2C_HW_B_RIVA)) {
+		/* addresses to probe;
+		   leave 0x24 and 0x25 because SAA7113H possibly uses it 
+		   0x21 and 0x22 possibly used by SAA7108E 
+		   Asus:      0x21 is a correct address (channel 1 of PCF8574)
+		   Creative:  0x23 is a correct address (channel 3 of PCF8574)
+		   VisionTek: 0x23 is a correct address (channel 3 of PCF8574)
+		*/
+		static const int pcf_probe[] = { 0x20, 0x21, 0x22, 0x23,
+						 0x24, 0x25, 0x26, 0x27, -1 };
+		int ret1, ret2, ret3, ret4;
+		unsigned char bits = 0, flag = 0;
+
+		memset(&c,0,sizeof(c));
+		c.adapter = adap;
+		for (i = 0; -1 != pcf_probe[i]; i++) {
+			c.addr = pcf_probe[i];
+			ret1 = i2c_smbus_write_byte(&c, 0xff);
+			ret2 = i2c_smbus_read_byte(&c);
+			ret3 = i2c_smbus_write_byte(&c, 0x00);
+			ret4 = i2c_smbus_read_byte(&c);
+
+			/* ensure that the writable bitmask works correctly */
+			rc = 0;
+			if (ret1 != -1 && ret2 != -1 && 
+			    ret3 != -1 && ret4 != -1) {
+				/* in the Asus TV-Box: bit 1-0 */
+				if (((ret2 & 0x03) == 0x03) && 
+				    ((ret4 & 0x03) == 0x00)) {
+					bits = (unsigned char) ~0x07;
+					flag = 0x04;
+					rc = 1;
+				}
+				/* in the Creative/VisionTek BreakOut-Box: bit 7-6 */
+				if (((ret2 & 0xc0) == 0xc0) && 
+				    ((ret4 & 0xc0) == 0x00)) {
+					bits = (unsigned char) ~0xe0;
+					flag = 0x20;
+					rc = 1;
+				}
+			}
+			dprintk("probe 0x%02x @ %s: %s\n",
+				c.addr, adap->name, rc ? "yes" : "no");
+			if (rc)
+				ir_attach(adap,pcf_probe[i],bits|(flag<<8),0);
+		}
+	}
+		
+	return 0;
+}
+
+static int ir_command(struct i2c_client *client,unsigned int cmd, void *arg)
+{
+	/* nothing */
+	return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+#ifdef MODULE
+
+int init_module(void)
+{
+	request_module("bttv");
+	request_module("rivatv");
+	request_module("ivtv");
+	i2c_add_driver(&driver);
+	return 0;
+}
+
+void cleanup_module(void)
+{
+	i2c_del_driver(&driver);
+}
+
+MODULE_DESCRIPTION("Infrared receiver driver for Hauppauge and Pixelview cards (i2c stack)");
+MODULE_AUTHOR("Gerd Knorr, Michal Kochanowicz, Christoph Bartelmus, Ulrich Mueller, Stefan Jahn");
+MODULE_LICENSE("GPL");
+
+module_param(minor, int, 0444);
+MODULE_PARM_DESC(minor, "Preferred minor device number");
+
+module_param(debug, bool, 0644);
+MODULE_PARM_DESC(debug, "Enable debugging messages");
+
+EXPORT_NO_SYMBOLS;
+
+#endif /* MODULE */
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff -urN linux-2.6.10-rc3-mm1/drivers/char/lirc/lirc_igorplugusb.c linux-2.6.10-rc3-no2/drivers/char/lirc/lirc_igorplugusb.c
--- linux-2.6.10-rc3-mm1/drivers/char/lirc/lirc_igorplugusb.c	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.10-rc3-no2/drivers/char/lirc/lirc_igorplugusb.c	2004-12-22 19:35:16.567730631 -0500
@@ -0,0 +1,700 @@
+/* lirc_igorplugusb - USB remote support for LIRC
+ *
+ * Supports the standard homebrew IgorPlugUSB receiver with Igor's firmware.
+ * See http://www.cesko.host.sk/IgorPlugUSB/IgorPlug-USB%20(AVR)_eng.htm 
+ * 
+ * The device can only record bursts of up to 36 pulses/spaces.
+ * Works fine with RC5. Longer commands lead to device buffer overrun.
+ * (Maybe a better firmware or a microcontroller with more ram can help?)
+ *
+ * Version 0.1  [beta status]
+ *
+ * Copyright (C) 2004 Jan M. Hochstein <hochstein@algo.informatik.tu-darmstadt.de>
+ *
+ * This driver was derived from:
+ *   Paul Miller <pmiller9@users.sourceforge.net>
+ *      "lirc_atiusb" module
+ *   Vladimir Dergachev <volodya@minspring.com>'s 2002
+ *      "USB ATI Remote support" (input device)
+ *   Adrian Dewhurst <sailor-lk@sailorfrag.net>'s 2002
+ *      "USB StreamZap remote driver" (LIRC)
+ *   Artur Lipowski <alipowski@kki.net.pl>'s 2002
+ *      "lirc_dev" and "lirc_gpio" LIRC modules
+ *
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/version.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 0)
+#error "*******************************************************"
+#error "Sorry, this driver needs kernel version 2.4.0 or higher"
+#error "*******************************************************"
+#endif
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/kmod.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/ioctl.h>
+#include <linux/fs.h>
+#include <linux/usb.h>
+#include <linux/poll.h>
+#include <linux/smp_lock.h>
+#include <linux/time.h>
+#include "linux/kcompat.h"
+#include "linux/lirc.h"
+#include "drivers/lirc_dev/lirc_dev.h"
+
+#if !defined(KERNEL_2_5)
+#        define USB_CTRL_GET_TIMEOUT    5
+#endif
+
+/* lock irctl structure */
+#define IRLOCK			down_interruptible(&ir->lock)
+#define IRUNLOCK		up(&ir->lock)
+
+/* module identification */
+#define DRIVER_VERSION		"0.1"
+#define DRIVER_AUTHOR		\
+        "Jan M. Hochstein <hochstein@algo.informatik.tu-darmstadt.de>"
+#define DRIVER_DESC		"USB remote driver for LIRC"
+#define DRIVER_NAME		"lirc_igorplugusb"
+
+/* debugging support */
+#ifdef CONFIG_USB_DEBUG
+        static int debug = 1;
+#else
+        static int debug = 0;
+#endif
+
+#define dprintk(fmt, args...)                                 \
+	do{                                                   \
+		if(debug) printk(KERN_DEBUG fmt, ## args);    \
+	}while(0)
+
+/* general constants */
+#define SUCCESS                 0
+
+/* One mode2 pulse/space has 4 bytes. */
+#define CODE_LENGTH             sizeof(lirc_t)
+
+/* Igor's firmware cannot record bursts longer than 36. */
+#define DEVICE_BUFLEN           36
+
+/** Header at the beginning of the device's buffer:
+        unsigned char data_length
+        unsigned char data_start    (!=0 means ring-buffer overrun)
+        unsigned char counter       (incremented by each burst)
+**/
+#define DEVICE_HEADERLEN        3
+
+/* This is for the gap */
+#define ADDITIONAL_LIRC_BYTES   2
+
+/* times to poll per second */
+#define SAMPLE_RATE             10
+
+
+/**** Igor's USB Request Codes */
+
+#define SET_INFRABUFFER_EMPTY   1
+/** 
+ * Params: none
+ * Answer: empty
+ *
+**/
+
+#define GET_INFRACODE           2
+/** 
+ * Params: 
+ *   wValue: offset to begin reading infra buffer
+ *
+ * Answer: infra data
+ *
+**/
+
+#define SET_DATAPORT_DIRECTION  3
+/** 
+ * Params: 
+ *   wValue: (byte) 1 bit for each data port pin (0=in, 1=out)
+ *
+ * Answer: empty
+ *
+**/
+
+#define GET_DATAPORT_DIRECTION  4
+/** 
+ * Params: none
+ *
+ * Answer: (byte) 1 bit for each data port pin (0=in, 1=out)
+ *
+**/
+
+#define SET_OUT_DATAPORT        5
+/** 
+ * Params: 
+ *   wValue: byte to write to output data port
+ *
+ * Answer: empty
+ *
+**/
+
+#define GET_OUT_DATAPORT        6
+/** 
+ * Params: none
+ *
+ * Answer: least significant 3 bits read from output data port
+ *
+**/
+
+#define GET_IN_DATAPORT         7
+/** 
+ * Params: none
+ *
+ * Answer: least significant 3 bits read from input data port
+ *
+**/
+
+#define READ_EEPROM             8
+/** 
+ * Params: 
+ *   wValue: offset to begin reading EEPROM
+ *
+ * Answer: EEPROM bytes
+ *
+**/
+
+#define WRITE_EEPROM            9
+/** 
+ * Params: 
+ *   wValue: offset to EEPROM byte
+ *   wIndex: byte to write
+ *
+ * Answer: empty
+ *
+**/
+
+#define SEND_RS232              10
+/** 
+ * Params: 
+ *   wValue: byte to send
+ *
+ * Answer: empty
+ *
+**/
+
+#define RECV_RS232              11
+/** 
+ * Params: none
+ *
+ * Answer: byte received
+ *
+**/
+
+#define SET_RS232_BAUD          12
+/** 
+ * Params: 
+ *   wValue: byte to write to UART bit rate register (UBRR)
+ *
+ * Answer: empty
+ *
+**/
+
+#define GET_RS232_BAUD          13
+/** 
+ * Params: none
+ *
+ * Answer: byte read from UART bit rate register (UBRR)
+ *
+**/
+
+
+/* data structure for each usb remote */
+struct irctl {
+
+	/* usb */
+	struct usb_device *usbdev;
+	struct urb *urb_in;
+	int devnum;
+
+	unsigned char *buf_in;
+	unsigned int len_in;
+        int in_space;
+	struct timeval last_time;
+
+#if defined(KERNEL_2_5)
+	dma_addr_t dma_in;
+#endif
+
+	/* lirc */
+	struct lirc_plugin *p;
+	int connected;
+
+	/* handle sending (init strings) */
+	int send_flags;
+	wait_queue_head_t wait_out;
+
+	struct semaphore lock;
+};
+
+static int unregister_from_lirc(struct irctl *ir)
+{
+	struct lirc_plugin *p = ir->p;
+	int devnum;
+	int rtn;
+
+	if(!ir->p)
+        	return -EINVAL;
+        
+	devnum = ir->devnum;
+	dprintk(DRIVER_NAME "[%d]: unregister from lirc called\n", devnum);
+
+	if ((rtn = lirc_unregister_plugin(p->minor)) > 0) {
+		printk(DRIVER_NAME "[%d]: error in lirc_unregister minor: %d\n"
+			"Trying again...\n", devnum, p->minor);
+		if (rtn == -EBUSY) {
+			printk(DRIVER_NAME
+				"[%d]: device is opened, will unregister"
+				" on close\n", devnum);
+			return -EAGAIN;
+		}
+		set_current_state(TASK_INTERRUPTIBLE);
+		schedule_timeout(HZ);
+
+		if ((rtn = lirc_unregister_plugin(p->minor)) > 0) {
+			printk(DRIVER_NAME "[%d]: lirc_unregister failed\n",
+			devnum);
+		}
+	}
+
+	if (rtn != SUCCESS) {
+		dprintk(DRIVER_NAME "[%d]: didn't free resources\n", devnum);
+		return -EAGAIN;
+	}
+
+	printk(DRIVER_NAME "[%d]: usb remote disconnected\n", devnum);
+
+	lirc_buffer_free(p->rbuf);
+	kfree(p->rbuf);
+	kfree(p);
+	kfree(ir);
+        ir->p = NULL;
+	return SUCCESS;
+}
+
+static int set_use_inc(void *data)
+{
+	struct irctl *ir = data;
+
+	if (!ir) {
+		printk(DRIVER_NAME "[?]: set_use_inc called with no context\n");
+		return -EIO;
+	}
+	dprintk(DRIVER_NAME "[%d]: set use inc\n", ir->devnum);
+
+	MOD_INC_USE_COUNT;
+
+	if (!ir->connected) {
+		if (!ir->usbdev)
+			return -ENODEV;
+
+		ir->connected = 1;
+	}
+
+	return SUCCESS;
+}
+
+static void set_use_dec(void *data)
+{
+	struct irctl *ir = data;
+
+	if (!ir) {
+		printk(DRIVER_NAME "[?]: set_use_dec called with no context\n");
+		return;
+	}
+	dprintk(DRIVER_NAME "[%d]: set use dec\n", ir->devnum);
+
+	if (ir->connected) {
+		IRLOCK;
+		ir->connected = 0;
+                unregister_from_lirc(ir);
+		IRUNLOCK;
+	}
+	MOD_DEC_USE_COUNT;
+}
+
+
+/** 
+ * Called in user context.
+ * return 0 if data was added to the buffer and
+ * -ENODATA if none was available. This should add some number of bits
+ * evenly divisible by code_length to the buffer
+**/
+static int usb_remote_poll(void* data, struct lirc_buffer* buf)
+{
+	int ret;
+	struct irctl *ir = (struct irctl *)data;
+
+	if(!ir->usbdev)  /* Has the device been removed? */
+		return -ENODEV;
+
+	memset(ir->buf_in, 0, ir->len_in);
+  
+	if((ret = usb_control_msg(
+        	ir->usbdev, usb_rcvctrlpipe(ir->usbdev, 0), 
+		GET_INFRACODE, USB_TYPE_VENDOR|USB_DIR_IN,
+		0/* offset */, /*unused*/0, 
+		ir->buf_in, ir->len_in, 
+		/*timeout*/HZ * USB_CTRL_GET_TIMEOUT)) > 0)
+	{
+		int i = DEVICE_HEADERLEN;
+		lirc_t code,timediff;
+                struct timeval now;
+
+		if(ret <= 1)  /* ACK packet has 1 byte --> ignore */
+			return -ENODATA;
+
+		dprintk(DRIVER_NAME ": Got %d bytes. Header: %02x %02x %02x\n", 
+                	ret, ir->buf_in[0], ir->buf_in[1], ir->buf_in[2]);
+      
+		if(ir->buf_in[2] != 0) {
+			printk(DRIVER_NAME "[%d]: Device buffer overrun.\n", 
+                        	ir->devnum);
+			i = DEVICE_HEADERLEN + ir->buf_in[2];  /* start at earliest byte */
+			/* where are we now? space, gap or pulse? */
+		}
+      
+		do_gettimeofday(&now);
+		timediff = now.tv_sec - ir->last_time.tv_sec;
+		if(timediff+1 > PULSE_MASK/1000000)
+			timediff = PULSE_MASK;
+		else {
+			timediff *= 1000000;
+			timediff += now.tv_usec - ir->last_time.tv_usec;
+		}
+		ir->last_time.tv_sec = now.tv_sec;
+		ir->last_time.tv_usec = now.tv_usec;
+
+		/* create leading gap  */
+		code = timediff;
+  		lirc_buffer_write_n(buf, (unsigned char*)&code, 1);
+		ir->in_space = 1;   /* next comes a pulse */
+
+		/* MODE2: pulse/space (PULSE_BIT) in 1us units */
+
+		while(i < ret) {
+			/* 1 Igor-tick = 85.333333 us */
+			code = (unsigned int)ir->buf_in[i] * 85 
+				+ (unsigned int)ir->buf_in[i]/3;
+			if(ir->in_space)
+				code |= PULSE_BIT;
+			lirc_buffer_write_n(buf, (unsigned char*)&code, 1);  
+			/* 1 chunk = CODE_LENGTH bytes */
+			ir->in_space ^= 1;
+			++ i;
+		}
+
+		if((ret = usb_control_msg(
+                	ir->usbdev, usb_rcvctrlpipe(ir->usbdev, 0), 
+			SET_INFRABUFFER_EMPTY, USB_TYPE_VENDOR|USB_DIR_IN,
+			/*unused*/0, /*unused*/0, 
+			/*dummy*/ir->buf_in, /*dummy*/ir->len_in, 
+                        /*timeout*/HZ * USB_CTRL_GET_TIMEOUT)) < 0)
+		{
+			printk(DRIVER_NAME "[%d]: SET_INFRABUFFER_EMPTY: error %d\n", 
+				ir->devnum, ret);
+		}
+		return SUCCESS;
+	}
+	else {
+		printk(DRIVER_NAME "[%d]: GET_INFRACODE: error %d\n", 
+                	ir->devnum, ret);
+	}
+
+	return -ENODATA;
+}
+
+
+
+#if defined(KERNEL_2_5)
+static int usb_remote_probe(struct usb_interface *intf,
+				const struct usb_device_id *id)
+{
+	struct usb_device *dev = NULL;
+	struct usb_host_interface *idesc = NULL;
+	struct usb_host_endpoint *ep_ctl2;
+#else
+static void *usb_remote_probe(struct usb_device *dev, unsigned int ifnum,
+				const struct usb_device_id *id)
+{
+	struct usb_interface *intf;
+	struct usb_interface_descriptor *idesc;
+	struct usb_endpoint_descriptor *ep_ctl2;
+#endif
+	struct irctl *ir = NULL;
+	struct lirc_plugin *plugin = NULL;
+	struct lirc_buffer *rbuf = NULL;
+	int devnum, pipe, maxp, bytes_in_key;
+	int minor = 0;
+	char buf[63], name[128]="";
+	int mem_failure = 0;
+	int ret;
+
+	dprintk(DRIVER_NAME ": usb probe called.\n");
+
+#if defined(KERNEL_2_5)
+	dev = interface_to_usbdev(intf);
+
+#  if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 5)
+	idesc = &intf->altsetting[intf->act_altsetting];  /* in 2.6.4 */
+#  else
+	idesc = intf->cur_altsetting;  /* in 2.6.6 */
+#  endif
+
+	if (idesc->desc.bNumEndpoints != 1)
+		return -ENODEV;
+	ep_ctl2 = idesc->endpoint;
+	if (((ep_ctl2->desc.bEndpointAddress & USB_ENDPOINT_DIR_MASK) != USB_DIR_IN)
+		|| (ep_ctl2->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+		!= USB_ENDPOINT_XFER_CONTROL)
+		return -ENODEV;
+	pipe = usb_rcvctrlpipe(dev, ep_ctl2->desc.bEndpointAddress);
+#else
+	intf = &dev->actconfig->interface[ifnum];
+	idesc = &intf->altsetting[intf->act_altsetting];
+	if (idesc->bNumEndpoints != 1)
+		return NULL;
+	ep_ctl2 = idesc->endpoint;
+	if (((ep_ctl2->bEndpointAddress & USB_ENDPOINT_DIR_MASK) != USB_DIR_IN)
+		|| (ep_ctl2->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+		!= USB_ENDPOINT_XFER_CONTROL)
+		return NULL;
+	pipe = usb_rcvctrlpipe(dev, ep_ctl2->bEndpointAddress);
+#endif
+	devnum = dev->devnum;
+	maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
+
+	bytes_in_key = CODE_LENGTH;
+
+	dprintk(DRIVER_NAME "[%d]: bytes_in_key=%d maxp=%d\n",
+		devnum, bytes_in_key, maxp);
+
+
+	/* allocate kernel memory */
+	mem_failure = 0;
+	if (!(ir = kmalloc(sizeof(struct irctl), GFP_KERNEL))) {
+		mem_failure = 1;
+	} else {
+		memset(ir, 0, sizeof(struct irctl));
+
+		if (!(plugin = kmalloc(sizeof(struct lirc_plugin), GFP_KERNEL))) {
+			mem_failure = 2;
+		} else if (!(rbuf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL))) {
+			mem_failure = 3;
+		} else if (lirc_buffer_init(rbuf, bytes_in_key, 
+                		DEVICE_BUFLEN+ADDITIONAL_LIRC_BYTES)) {
+			mem_failure = 4;
+#if defined(KERNEL_2_5)
+		} else if (!(ir->buf_in = usb_buffer_alloc(dev, 
+				DEVICE_BUFLEN+DEVICE_HEADERLEN, 
+                                SLAB_ATOMIC, &ir->dma_in))) {
+			mem_failure = 5;
+#else
+		} else if (!(ir->buf_in = kmalloc(
+				DEVICE_BUFLEN+DEVICE_HEADERLEN, GFP_KERNEL))) {
+			mem_failure = 5;
+#endif
+		} else {
+
+			memset(plugin, 0, sizeof(struct lirc_plugin));
+
+			strcpy(plugin->name, DRIVER_NAME " ");
+			plugin->minor = -1;
+			plugin->code_length = bytes_in_key*8; /* in bits */
+			plugin->features = LIRC_CAN_REC_MODE2;
+			plugin->data = ir;
+			plugin->rbuf = rbuf;
+			plugin->set_use_inc = &set_use_inc;
+			plugin->set_use_dec = &set_use_dec;
+			plugin->sample_rate = SAMPLE_RATE;    /* per second */
+			plugin->add_to_buf = &usb_remote_poll;
+
+			init_MUTEX(&ir->lock);
+			init_waitqueue_head(&ir->wait_out);
+
+			if ((minor = lirc_register_plugin(plugin)) < 0) {
+				mem_failure = 9;
+			}
+		}
+	}
+
+	/* free allocated memory in case of failure */
+	switch (mem_failure) {
+	case 9:
+#if defined(KERNEL_2_5)
+		usb_buffer_free(dev, DEVICE_BUFLEN+DEVICE_HEADERLEN, 
+                	ir->buf_in, ir->dma_in);
+#else
+		kfree(ir->buf_in);
+#endif
+	case 5:
+		lirc_buffer_free(rbuf);
+	case 4:
+		kfree(rbuf);
+	case 3:
+		kfree(plugin);
+	case 2:
+		kfree(ir);
+	case 1:
+		printk(DRIVER_NAME "[%d]: out of memory (code=%d)\n",
+			devnum, mem_failure);
+#if defined(KERNEL_2_5)
+		return -ENOMEM;
+#else
+		return NULL;
+#endif
+	}
+
+	plugin->minor = minor;
+	ir->p = plugin;
+	ir->devnum = devnum;
+	ir->usbdev = dev;
+	ir->len_in = DEVICE_BUFLEN+DEVICE_HEADERLEN;
+	ir->connected = 0;
+	ir->in_space = 1; /* First mode2 event is a space. */
+	do_gettimeofday(&ir->last_time);
+
+	if (dev->descriptor.iManufacturer
+		&& usb_string(dev, dev->descriptor.iManufacturer, buf, 63) > 0)
+		strncpy(name, buf, 128);
+	if (dev->descriptor.iProduct
+		&& usb_string(dev, dev->descriptor.iProduct, buf, 63) > 0)
+		snprintf(name, 128, "%s %s", name, buf);
+	printk(DRIVER_NAME "[%d]: %s on usb%d:%d\n", devnum, name,
+	       dev->bus->busnum, devnum);
+
+	/* clear device buffer */
+	if ((ret = usb_control_msg(ir->usbdev, usb_rcvctrlpipe(ir->usbdev, 0), 
+		SET_INFRABUFFER_EMPTY, USB_TYPE_VENDOR|USB_DIR_IN,
+		/*unused*/0, /*unused*/0, 
+		/*dummy*/ir->buf_in, /*dummy*/ir->len_in, 
+		/*timeout*/HZ * USB_CTRL_GET_TIMEOUT)) < 0)
+	{
+		printk(DRIVER_NAME "[%d]: SET_INFRABUFFER_EMPTY: error %d\n", 
+			devnum, ret);
+	}
+
+#if defined(KERNEL_2_5)
+	usb_set_intfdata(intf, ir);
+	return SUCCESS;
+#else
+	return ir;
+#endif
+}
+
+
+#if defined(KERNEL_2_5)
+static void usb_remote_disconnect(struct usb_interface *intf)
+{
+	struct usb_device *dev = interface_to_usbdev(intf);
+	struct irctl *ir = usb_get_intfdata(intf);
+	usb_set_intfdata(intf, NULL);
+#else
+static void usb_remote_disconnect(struct usb_device *dev, void *ptr)
+{
+	struct irctl *ir = ptr;
+#endif
+
+	if (!ir || !ir->p)
+		return;
+
+	ir->usbdev = NULL;
+	wake_up_all(&ir->wait_out);
+
+	IRLOCK;
+#if defined(KERNEL_2_5)
+	usb_buffer_free(dev, ir->len_in, ir->buf_in, ir->dma_in);
+#else
+	kfree(ir->buf_in);
+#endif
+	IRUNLOCK;
+
+	unregister_from_lirc(ir);
+}
+
+static struct usb_device_id usb_remote_id_table [] = {
+	{ USB_DEVICE(0x03eb, 0x0002) },	/* Igor Plug USB (Atmel's Manufact. ID) */
+	{ }				/* Terminating entry */
+};
+
+static struct usb_driver usb_remote_driver = {
+	.owner =	THIS_MODULE,
+	.name =		DRIVER_NAME,
+	.probe =	usb_remote_probe,
+	.disconnect =	usb_remote_disconnect,
+	.id_table =	usb_remote_id_table
+};
+
+static int __init usb_remote_init(void)
+{
+	int i;
+
+	printk("\n" DRIVER_NAME ": " DRIVER_DESC " v" DRIVER_VERSION "\n");
+	printk(DRIVER_NAME ": " DRIVER_AUTHOR "\n");
+	dprintk(DRIVER_NAME ": debug mode enabled\n");
+
+	request_module("lirc_dev");
+
+	if ((i = usb_register(&usb_remote_driver)) < 0) {
+		printk(DRIVER_NAME ": usb register failed, result = %d\n", i);
+		return -ENODEV;
+	}
+
+	return SUCCESS;
+}
+
+static void __exit usb_remote_exit(void)
+{
+	usb_deregister(&usb_remote_driver);
+}
+
+module_init(usb_remote_init);
+module_exit(usb_remote_exit);
+
+#if defined(KERNEL_2_5)
+#include <linux/vermagic.h>
+MODULE_INFO(vermagic, VERMAGIC_STRING);
+#endif
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(usb, usb_remote_id_table);
+
+EXPORT_NO_SYMBOLS;
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff -urN linux-2.6.10-rc3-mm1/drivers/char/lirc/lirc_it87.c linux-2.6.10-rc3-no2/drivers/char/lirc/lirc_it87.c
--- linux-2.6.10-rc3-mm1/drivers/char/lirc/lirc_it87.c	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.10-rc3-no2/drivers/char/lirc/lirc_it87.c	2004-12-22 19:35:16.570730214 -0500
@@ -0,0 +1,947 @@
+/*
+ * LIRC driver for ITE IT8712/IT8705 CIR port
+ *
+ * Copyright (C) 2001 Hans-Günter Lütke Uphues <hg_lu@web.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ *
+ * ITE IT8705 and IT8712(not tested) CIR-port support for lirc based
+ * via cut and paste from lirc_sir.c (C) 2000 Milan Pikula
+ *
+ * Attention: Sendmode only tested with debugging logs 
+ *
+ * 2001/02/27 Christoph Bartelmus <lirc@bartelmus.de> :
+ *   reimplemented read function
+ */
+
+
+#include <linux/version.h>
+#include <linux/module.h>
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+ 
+#include <linux/config.h>
+
+
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/serial_reg.h>
+#include <linux/time.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+#include <linux/mm.h>
+#include <linux/delay.h>
+#include <linux/poll.h>
+#include <asm/system.h>
+#include <asm/segment.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/fcntl.h>
+
+#include <linux/timer.h>
+
+#include "linux/lirc.h"
+#include "drivers/lirc_dev/lirc_dev.h"
+#include "linux/kcompat.h"
+
+#include "lirc_it87.h"
+
+static unsigned long it87_bits_in_byte_out = 0;
+static unsigned long it87_send_counter = 0;
+static unsigned char it87_RXEN_mask = IT87_CIR_RCR_RXEN;
+
+#define RBUF_LEN 1024
+#define WBUF_LEN 1024
+
+#define LIRC_DRIVER_NAME "lirc_it87"
+
+/* timeout for sequences in jiffies (=5/100s) */
+/* must be longer than TIME_CONST */
+#define IT87_TIMEOUT	(HZ*5/100)
+
+/* insmod parameters */
+static int debug = 0;
+#define dprintk(fmt, args...)                                     \
+	do{                                                       \
+		if(debug) printk(KERN_DEBUG LIRC_DRIVER_NAME ": " \
+                                 fmt, ## args);                   \
+	}while(0)
+
+static int io = IT87_CIR_DEFAULT_IOBASE;
+static int irq = IT87_CIR_DEFAULT_IRQ;
+static unsigned char it87_freq = 38; /* kHz */
+/* receiver demodulator default: off */
+static int it87_enable_demodulator = 0;
+
+static spinlock_t timer_lock = SPIN_LOCK_UNLOCKED;
+static struct timer_list timerlist;
+/* time of last signal change detected */
+static struct timeval last_tv = {0, 0};
+/* time of last UART data ready interrupt */
+static struct timeval last_intr_tv = {0, 0};
+static int last_value = 0;
+
+static DECLARE_WAIT_QUEUE_HEAD(lirc_read_queue);
+
+static spinlock_t hardware_lock = SPIN_LOCK_UNLOCKED;
+static spinlock_t dev_lock = SPIN_LOCK_UNLOCKED;
+
+static lirc_t rx_buf[RBUF_LEN]; unsigned int rx_tail = 0, rx_head = 0;
+static lirc_t tx_buf[WBUF_LEN];
+
+/* SECTION: Prototypes */
+
+/* Communication with user-space */
+static int lirc_open(struct inode * inode,
+		     struct file * file);
+static int lirc_close(struct inode * inode,
+		      struct file *file);
+static unsigned int lirc_poll(struct file * file,
+			      poll_table * wait);
+static ssize_t lirc_read(struct file * file,
+			 char * buf,
+			 size_t count,
+			 loff_t * ppos);
+static ssize_t lirc_write(struct file * file,
+			  const char * buf,
+			  size_t n,
+			  loff_t * pos);
+static int lirc_ioctl(struct inode *node,
+		      struct file *filep,
+		      unsigned int cmd,
+		      unsigned long arg);
+static void add_read_queue(int flag,
+			   unsigned long val);
+#ifdef MODULE
+static int init_chrdev(void);
+static void drop_chrdev(void);
+#endif
+	/* Hardware */
+static irqreturn_t it87_interrupt(int irq, void * dev_id,
+				 struct pt_regs * regs);
+static void send_space(unsigned long len);
+static void send_pulse(unsigned long len);
+static void init_send(void);
+static void terminate_send(unsigned long len);
+static int init_hardware(void);
+static void drop_hardware(void);
+	/* Initialisation */
+static int init_port(void);
+static void drop_port(void);
+int init_module(void);
+void cleanup_module(void);
+
+
+/* SECTION: Communication with user-space */
+
+static int lirc_open(struct inode * inode,
+		     struct file * file)
+{
+	spin_lock(&dev_lock);
+	if (MOD_IN_USE) {
+		spin_unlock(&dev_lock);
+		return -EBUSY;
+	}
+	MOD_INC_USE_COUNT;
+	spin_unlock(&dev_lock);
+	return 0;
+}
+
+
+static int lirc_close(struct inode * inode,
+		      struct file *file)
+{
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+
+static unsigned int lirc_poll(struct file * file,
+			      poll_table * wait)
+{
+	poll_wait(file, &lirc_read_queue, wait);
+	if (rx_head != rx_tail)
+		return POLLIN | POLLRDNORM;
+	return 0;
+}
+
+
+static ssize_t lirc_read(struct file * file,
+			 char * buf,
+			 size_t count,
+			 loff_t * ppos)
+{
+	int n=0;
+	int retval=0;
+	
+	while(n<count)
+	{
+		if(file->f_flags & O_NONBLOCK &&
+		   rx_head==rx_tail)
+		{
+			retval = -EAGAIN;
+			break;
+		}
+		retval=wait_event_interruptible(lirc_read_queue,
+						rx_head!=rx_tail);
+		if(retval)
+		{
+			break;
+		}
+		
+		retval=verify_area(VERIFY_WRITE,(void *) buf+n,
+				   sizeof(lirc_t));
+		if (retval)
+		{
+			return retval;
+		}
+		copy_to_user((void *) buf+n,(void *) (rx_buf+rx_head),
+			     sizeof(lirc_t));
+		rx_head=(rx_head+1)&(RBUF_LEN-1);
+		n+=sizeof(lirc_t);
+	}
+	if(n)
+	{
+		return n;
+	}
+	return retval;
+}
+
+
+static ssize_t lirc_write(struct file * file,
+			  const char * buf,
+			  size_t n,
+			  loff_t * pos)
+{
+	int i;
+	int retval;
+
+        if(n%sizeof(lirc_t) || (n/sizeof(lirc_t)) > WBUF_LEN)
+		return(-EINVAL);
+	retval = verify_area(VERIFY_READ, buf, n);
+	if (retval)
+		return retval;
+	copy_from_user(tx_buf, buf, n);
+	i = 0;
+	n/=sizeof(lirc_t);
+	init_send();
+	while (1) {
+		if (i >= n)
+			break;
+		if (tx_buf[i])
+			send_pulse(tx_buf[i]);
+		i++;
+		if (i >= n)
+			break;
+		if (tx_buf[i])
+			send_space(tx_buf[i]);
+		i++;
+	}
+	terminate_send(tx_buf[i-1]);
+	return n;
+}
+
+
+static int lirc_ioctl(struct inode *node,
+		      struct file *filep,
+		      unsigned int cmd,
+		      unsigned long arg)
+{
+	int retval = 0;
+	unsigned long value = 0;
+	unsigned int ivalue;
+
+	if (cmd == LIRC_GET_FEATURES)
+		value = LIRC_CAN_SEND_PULSE |
+			LIRC_CAN_SET_SEND_CARRIER |
+			LIRC_CAN_REC_MODE2;
+	else if (cmd == LIRC_GET_SEND_MODE)
+		value = LIRC_MODE_PULSE;
+	else if (cmd == LIRC_GET_REC_MODE)
+		value = LIRC_MODE_MODE2;
+	
+	switch (cmd) {
+	case LIRC_GET_FEATURES:
+	case LIRC_GET_SEND_MODE:
+	case LIRC_GET_REC_MODE:
+		retval = put_user(value, (unsigned long *) arg);
+		break;
+
+	case LIRC_SET_SEND_MODE:
+	case LIRC_SET_REC_MODE:
+		retval = get_user(value, (unsigned long *) arg);
+		break;
+
+	case LIRC_SET_SEND_CARRIER:
+		retval=get_user(ivalue,(unsigned int *) arg);
+		if(retval) return(retval);
+		ivalue /= 1000;
+		if (ivalue > IT87_CIR_FREQ_MAX ||
+		    ivalue < IT87_CIR_FREQ_MIN) return(-EINVAL);
+
+		it87_freq = ivalue;
+		{
+			unsigned long hw_flags;
+
+			spin_lock_irqsave(&hardware_lock, hw_flags);
+			outb(((inb(io + IT87_CIR_TCR2) & IT87_CIR_TCR2_TXMPW) |
+			      (it87_freq - IT87_CIR_FREQ_MIN) << 3),
+			     io + IT87_CIR_TCR2);
+			spin_unlock_irqrestore(&hardware_lock, hw_flags);
+			dprintk("demodulation frequency: %d kHz\n",
+				it87_freq);
+		}
+
+		break;
+
+	default:
+		retval = -ENOIOCTLCMD;
+	}
+	
+	if (retval)
+		return retval;
+	
+	if (cmd == LIRC_SET_REC_MODE) {
+		if (value != LIRC_MODE_MODE2)
+			retval = -ENOSYS;
+	} else if (cmd == LIRC_SET_SEND_MODE) {
+		if (value != LIRC_MODE_PULSE)
+			retval = -ENOSYS;
+	}
+	return retval;
+}
+
+static void add_read_queue(int flag,
+			   unsigned long val)
+{
+	unsigned int new_rx_tail;
+	lirc_t newval;
+
+	dprintk("add flag %d with val %lu\n", flag,val);
+	
+	newval = val & PULSE_MASK;
+
+	/* statistically pulses are ~TIME_CONST/2 too long: we could
+	   maybe make this more exactly but this is good enough */
+	if(flag) /* pulse */ {
+		if(newval>TIME_CONST/2) {
+			newval-=TIME_CONST/2;
+		}
+		else /* should not ever happen */ {
+			newval=1;
+		}
+		newval|=PULSE_BIT;
+	}
+	else {
+		newval+=TIME_CONST/2;
+	}
+	new_rx_tail = (rx_tail + 1) & (RBUF_LEN - 1);
+	if (new_rx_tail == rx_head) {
+		dprintk("Buffer overrun.\n");
+		return;
+	}
+	rx_buf[rx_tail] = newval;
+	rx_tail = new_rx_tail;
+	wake_up_interruptible(&lirc_read_queue);
+}
+
+
+static struct file_operations lirc_fops = {
+	read:    lirc_read,
+	write:   lirc_write,
+	poll:    lirc_poll,
+	ioctl:   lirc_ioctl,
+	open:    lirc_open,
+	release: lirc_close,
+};
+
+static int set_use_inc(void* data)
+{
+#if WE_DONT_USE_LOCAL_OPEN_CLOSE
+	MOD_INC_USE_COUNT;
+#endif
+       return 0;
+}
+
+static void set_use_dec(void* data)
+{
+#if WE_DONT_USE_LOCAL_OPEN_CLOSE
+	MOD_DEC_USE_COUNT;
+#endif
+}
+static struct lirc_plugin plugin = {
+       name:           LIRC_DRIVER_NAME,
+       minor:          -1,
+       code_length:    1,
+       sample_rate:    0,
+       data:           NULL,
+       add_to_buf:     NULL,
+       get_queue:      NULL,
+       set_use_inc:    set_use_inc,
+       set_use_dec:    set_use_dec,
+       fops:           &lirc_fops,
+};
+
+
+#ifdef MODULE
+static int init_chrdev(void)
+{
+	plugin.minor = lirc_register_plugin(&plugin);
+	
+	if (plugin.minor < 0) {
+		printk(KERN_ERR LIRC_DRIVER_NAME ": init_chrdev() failed.\n");
+		return -EIO;
+	}
+	return 0;
+}
+
+
+static void drop_chrdev(void)
+{
+	lirc_unregister_plugin(plugin.minor);
+}
+#endif
+
+
+/* SECTION: Hardware */
+static long delta(struct timeval * tv1,
+		  struct timeval * tv2)
+{
+	unsigned long deltv;
+	
+	deltv = tv2->tv_sec - tv1->tv_sec;
+	if (deltv > 15)
+		deltv = 0xFFFFFF;
+	else
+		deltv = deltv*1000000 +
+			tv2->tv_usec -
+			tv1->tv_usec;
+	return deltv;
+}
+
+
+static void it87_timeout(unsigned long data) 
+{
+	/* if last received signal was a pulse, but receiving stopped
+	   within the 9 bit frame, we need to finish this pulse and
+	   simulate a signal change to from pulse to space. Otherwise
+	   upper layers will receive two sequences next time. */
+	
+	unsigned long flags;
+	unsigned long pulse_end;
+	
+	/* avoid interference with interrupt */
+ 	spin_lock_irqsave(&timer_lock, flags);
+	if (last_value) {
+		/* determine 'virtual' pulse end: */
+	 	pulse_end = delta(&last_tv, &last_intr_tv);
+		dprintk("timeout add %d for %lu usec\n", 
+			last_value, pulse_end);
+		add_read_queue(last_value,
+			       pulse_end);
+		last_value = 0;
+		last_tv=last_intr_tv;
+	}
+	spin_unlock_irqrestore(&timer_lock, flags);		
+}
+
+
+static irqreturn_t it87_interrupt(int irq,
+				 void * dev_id,
+				 struct pt_regs * regs)
+{
+	unsigned char data;
+	struct timeval curr_tv;
+	static unsigned long deltv;
+	unsigned long deltintrtv;
+	unsigned long flags, hw_flags;
+	int iir, lsr;
+	int fifo = 0;
+
+	iir = inb(io + IT87_CIR_IIR);
+
+	switch (iir & IT87_CIR_IIR_IID) {
+	case 0x4:
+	case 0x6:
+		lsr = inb(io + IT87_CIR_RSR) & (IT87_CIR_RSR_RXFTO |
+						    IT87_CIR_RSR_RXFBC);
+		fifo = lsr & IT87_CIR_RSR_RXFBC;
+		dprintk("iir: 0x%x fifo: 0x%x\n", iir, lsr);
+	
+		/* avoid interference with timer */
+		spin_lock_irqsave(&timer_lock, flags);
+		spin_lock_irqsave(&hardware_lock, hw_flags);
+		do {
+			del_timer(&timerlist);
+			data = inb(io + IT87_CIR_DR);
+
+			dprintk("data=%.2x\n", data);
+			do_gettimeofday(&curr_tv);
+			deltv = delta(&last_tv, &curr_tv);
+			deltintrtv = delta(&last_intr_tv, &curr_tv);
+
+			dprintk("t %lu , d %d\n", deltintrtv, (int)data);
+			
+			/* if nothing came in last 2 cycles,
+			   it was gap */
+			if (deltintrtv > TIME_CONST * 2) {
+				if (last_value) {
+					dprintk("GAP\n");
+
+					/* simulate signal change */
+					add_read_queue(last_value,
+						       deltv-
+						       deltintrtv);
+					last_value = 0;
+					last_tv.tv_sec = last_intr_tv.tv_sec;
+					last_tv.tv_usec = last_intr_tv.tv_usec;
+					deltv = deltintrtv;
+				}
+			}
+			data = 1;
+			if (data ^ last_value) {
+				/* deltintrtv > 2*TIME_CONST,
+				   remember ? */
+				/* the other case is timeout */
+				add_read_queue(last_value,
+					       deltv-TIME_CONST);
+				last_value = data;
+				last_tv = curr_tv;
+				if(last_tv.tv_usec>=TIME_CONST) {
+					last_tv.tv_usec-=TIME_CONST;
+				}
+				else {
+					last_tv.tv_sec--;
+					last_tv.tv_usec+=1000000-
+						TIME_CONST;
+				}
+			}
+			last_intr_tv = curr_tv;
+			if (data) {
+				/* start timer for end of sequence detection */
+				timerlist.expires = jiffies + IT87_TIMEOUT;
+				add_timer(&timerlist);
+			}
+			outb((inb(io + IT87_CIR_RCR) & ~IT87_CIR_RCR_RXEN) |
+			     IT87_CIR_RCR_RXACT,
+			     io + IT87_CIR_RCR);
+			if (it87_RXEN_mask) {
+				outb(inb(io + IT87_CIR_RCR) | IT87_CIR_RCR_RXEN, 
+				     io + IT87_CIR_RCR);
+			}
+			fifo--;
+		}
+		while (fifo != 0);
+		spin_unlock_irqrestore(&hardware_lock, hw_flags);
+		spin_unlock_irqrestore(&timer_lock, flags);
+		
+		return IRQ_RETVAL(IRQ_HANDLED);
+
+	default:
+		/* not our irq */
+		dprintk("unknown IRQ (shouldn't happen) !!\n");
+		return IRQ_RETVAL(IRQ_NONE);
+	}
+}
+
+
+static void send_it87(unsigned long len,
+		      unsigned long stime,
+		      unsigned char send_byte,
+		      unsigned int count_bits)
+{
+        long count = len / stime;
+	long time_left = 0;
+	static unsigned char byte_out = 0;
+
+	dprintk("%s: len=%ld, sb=%d\n", __FUNCTION__, len, send_byte);
+
+	time_left = (long)len - (long)count * (long)stime;
+	count += ((2 * time_left) / stime);
+	while (count) {
+		long i=0;
+		for (i=0; i<count_bits; i++) {
+			byte_out = (byte_out << 1) | (send_byte & 1);
+			it87_bits_in_byte_out++;
+		}
+		if (it87_bits_in_byte_out == 8) {
+			dprintk("out=0x%x, tsr_txfbc: 0x%x\n",
+				byte_out,
+				inb(io + IT87_CIR_TSR) &
+				IT87_CIR_TSR_TXFBC);
+
+			while ((inb(io + IT87_CIR_TSR) &
+				IT87_CIR_TSR_TXFBC) >= IT87_CIR_FIFO_SIZE);
+			{
+				unsigned long hw_flags;
+
+				spin_lock_irqsave(&hardware_lock, hw_flags);
+				outb(byte_out, io + IT87_CIR_DR);
+				spin_unlock_irqrestore(&hardware_lock, hw_flags);
+			}
+			it87_bits_in_byte_out = 0;
+			it87_send_counter++;
+			byte_out = 0;
+		}
+		count--;
+	}
+}
+
+
+/*
+maybe: exchange space and pulse because
+it8705 only modulates 0-bits
+*/
+
+
+static void send_space(unsigned long len)
+{
+	send_it87(len,
+		  TIME_CONST,
+		  IT87_CIR_SPACE,
+		  IT87_CIR_BAUDRATE_DIVISOR);
+}
+
+static void send_pulse(unsigned long len)
+{
+	send_it87(len,
+		  TIME_CONST,
+		  IT87_CIR_PULSE,
+		  IT87_CIR_BAUDRATE_DIVISOR);
+}
+
+
+static void init_send()
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&hardware_lock, flags);
+	/* RXEN=0: receiver disable */
+	it87_RXEN_mask = 0;
+	outb(inb(io + IT87_CIR_RCR) & ~IT87_CIR_RCR_RXEN,
+	     io + IT87_CIR_RCR);
+	spin_unlock_irqrestore(&hardware_lock, flags);
+	it87_bits_in_byte_out = 0;
+	it87_send_counter = 0;
+}
+
+
+static void terminate_send(unsigned long len)
+{
+	unsigned long flags;
+	unsigned long last = 0;
+
+	last = it87_send_counter;
+	/* make sure all necessary data has been sent */
+	while (last == it87_send_counter)
+		send_space(len);
+	/* wait until all data sent */
+	while ((inb(io + IT87_CIR_TSR) & IT87_CIR_TSR_TXFBC) != 0);
+	/* then reenable receiver */
+	spin_lock_irqsave(&hardware_lock, flags);
+	it87_RXEN_mask = IT87_CIR_RCR_RXEN;
+	outb(inb(io + IT87_CIR_RCR) | IT87_CIR_RCR_RXEN,
+	     io + IT87_CIR_RCR);
+	spin_unlock_irqrestore(&hardware_lock, flags);
+}
+
+
+static int init_hardware(void)
+{
+	unsigned long flags;
+	unsigned char it87_rcr = 0;
+	
+	spin_lock_irqsave(&hardware_lock, flags);
+	/* init cir-port */
+	/* enable r/w-access to Baudrate-Register */
+	outb(IT87_CIR_IER_BR, io + IT87_CIR_IER);
+	outb(IT87_CIR_BAUDRATE_DIVISOR % 0x100, io+IT87_CIR_BDLR);
+	outb(IT87_CIR_BAUDRATE_DIVISOR / 0x100, io+IT87_CIR_BDHR);
+	/* Baudrate Register off, define IRQs: Input only */
+	outb(IT87_CIR_IER_IEC | IT87_CIR_IER_RDAIE, io + IT87_CIR_IER);
+	/* RX: HCFS=0, RXDCR = 001b (35,6..40,3 kHz), RXEN=1 */
+	it87_rcr = (IT87_CIR_RCR_RXEN & it87_RXEN_mask) | 0x1;
+	if (it87_enable_demodulator)
+		it87_rcr |= IT87_CIR_RCR_RXEND;
+	outb(it87_rcr, io + IT87_CIR_RCR);
+	/* TX: 38kHz, 13,3us (pulse-width */
+	outb(((it87_freq - IT87_CIR_FREQ_MIN) << 3) | 0x06,
+	     io + IT87_CIR_TCR2);
+	spin_unlock_irqrestore(&hardware_lock, flags);
+	return 0;
+}
+
+
+static void drop_hardware(void)
+{
+	unsigned long flags;
+	
+	spin_lock_irqsave(&hardware_lock, flags);
+	disable_irq(irq);
+	/* receiver disable */
+	it87_RXEN_mask = 0;
+	outb(0x1, io + IT87_CIR_RCR);
+	/* turn off irqs */
+	outb(0, io + IT87_CIR_IER);
+	/* fifo clear */
+        outb(IT87_CIR_TCR1_FIFOCLR, io+IT87_CIR_TCR1);
+        /* reset */
+        outb(IT87_CIR_IER_RESET, io+IT87_CIR_IER);
+	enable_irq(irq);
+	spin_unlock_irqrestore(&hardware_lock, flags);
+}
+
+
+static unsigned char it87_read(unsigned char port)
+{
+	outb(port, IT87_ADRPORT);
+	return inb(IT87_DATAPORT);
+}
+
+
+static void it87_write(unsigned char port,
+		       unsigned char data)
+{
+	outb(port, IT87_ADRPORT);
+	outb(data, IT87_DATAPORT);
+}
+
+
+/* SECTION: Initialisation */
+
+static int init_port(void)
+{
+	int retval = 0;
+	
+	unsigned char init_bytes[4] = {IT87_INIT};
+	unsigned char it87_chipid = 0;
+	unsigned char ldn = 0;
+	unsigned int  it87_io = 0;
+	unsigned int  it87_irq = 0;
+	
+	/* Enter MB PnP Mode */
+	outb(init_bytes[0], IT87_ADRPORT);
+	outb(init_bytes[1], IT87_ADRPORT);
+	outb(init_bytes[2], IT87_ADRPORT);
+	outb(init_bytes[3], IT87_ADRPORT);
+	
+	/* 8712 or 8705 ? */
+	it87_chipid = it87_read(IT87_CHIP_ID1);
+	if (it87_chipid != 0x87) {
+		retval = -ENXIO;
+		return retval;
+	}
+	it87_chipid = it87_read(IT87_CHIP_ID2);
+	if ((it87_chipid != 0x12) && (it87_chipid != 0x05)) {
+		printk(KERN_INFO LIRC_DRIVER_NAME
+		       ": no IT8705/12 found, exiting..\n");
+		retval = -ENXIO;
+		return retval;
+	}
+	printk(KERN_INFO LIRC_DRIVER_NAME
+	       ": found IT87%.2x.\n",
+	       it87_chipid);
+
+	/* get I/O-Port and IRQ */
+	if (it87_chipid == 0x12)
+		ldn = IT8712_CIR_LDN;
+	else
+		ldn = IT8705_CIR_LDN;
+	it87_write(IT87_LDN, ldn);
+	
+	it87_io = it87_read(IT87_CIR_BASE_MSB) * 256 +
+		it87_read(IT87_CIR_BASE_LSB);
+	if (it87_io == 0) {
+		if (io == 0)
+			io = IT87_CIR_DEFAULT_IOBASE;
+		printk(KERN_INFO LIRC_DRIVER_NAME
+		       ": set default io 0x%x\n",
+		       io);
+		it87_write(IT87_CIR_BASE_MSB, io / 0x100);
+		it87_write(IT87_CIR_BASE_LSB, io % 0x100);
+	}
+	else
+		io = it87_io;
+	
+	it87_irq = it87_read(IT87_CIR_IRQ);
+	if (it87_irq == 0) {
+		if (irq == 0)
+			irq = IT87_CIR_DEFAULT_IRQ;
+		printk(KERN_INFO LIRC_DRIVER_NAME
+		       ": set default irq 0x%x\n",
+		       irq);
+		it87_write(IT87_CIR_IRQ, irq);
+	}
+	else
+		irq = it87_irq;
+	
+	{
+		unsigned long hw_flags;
+
+		spin_lock_irqsave(&hardware_lock, hw_flags);
+		/* reset */
+		outb(IT87_CIR_IER_RESET, io+IT87_CIR_IER);
+		/* fifo clear */
+		outb(IT87_CIR_TCR1_FIFOCLR |
+		     /*	     IT87_CIR_TCR1_ILE | */
+		     IT87_CIR_TCR1_TXRLE |
+		     IT87_CIR_TCR1_TXENDF, io+IT87_CIR_TCR1);
+		spin_unlock_irqrestore(&hardware_lock, hw_flags);
+	}
+	
+	/* get I/O port access and IRQ line */
+	retval = check_region(io, 8);
+	if (retval < 0) {
+		printk(KERN_ERR LIRC_DRIVER_NAME
+		       ": i/o port 0x%.4x already in use.\n",
+		       io);
+		/* Leaving MB PnP Mode */
+		it87_write(IT87_CFGCTRL, 0x2);
+		return retval;
+	}
+
+	/* activate CIR-Device */
+	it87_write(IT87_CIR_ACT, 0x1);
+
+	/* Leaving MB PnP Mode */
+	it87_write(IT87_CFGCTRL, 0x2);
+
+	retval = request_irq(irq, it87_interrupt, 0 /*SA_INTERRUPT*/,
+			     LIRC_DRIVER_NAME, NULL);
+	if (retval < 0) {
+		printk(KERN_ERR LIRC_DRIVER_NAME
+		       ": IRQ %d already in use.\n",
+		       irq);
+		return retval;
+	}
+
+	request_region(io, 8, LIRC_DRIVER_NAME);
+	printk(KERN_INFO LIRC_DRIVER_NAME
+	       ": I/O port 0x%.4x, IRQ %d.\n",
+	       io,
+	       irq);
+
+	init_timer(&timerlist);
+	timerlist.function = it87_timeout;
+	timerlist.data = 0xabadcafe;
+	
+	return 0;
+}
+
+
+static void drop_port(void)
+{
+/*
+        unsigned char init_bytes[4] = {IT87_INIT};
+ 
+        / * Enter MB PnP Mode * /
+        outb(init_bytes[0], IT87_ADRPORT);
+        outb(init_bytes[1], IT87_ADRPORT);
+        outb(init_bytes[2], IT87_ADRPORT);
+        outb(init_bytes[3], IT87_ADRPORT);
+
+        / * deactivate CIR-Device * /
+        it87_write(IT87_CIR_ACT, 0x0);
+
+        / * Leaving MB PnP Mode * /
+        it87_write(IT87_CFGCTRL, 0x2);
+*/
+
+	del_timer_sync(&timerlist);
+	free_irq(irq, NULL);
+	release_region(io, 8);
+}
+
+
+static int init_lirc_it87(void)
+{
+	int retval;
+	
+	init_waitqueue_head(&lirc_read_queue);
+	retval = init_port();
+	if (retval < 0)
+		return retval;
+	init_hardware();
+	printk(KERN_INFO LIRC_DRIVER_NAME
+	       ": Installed.\n");
+	return 0;
+}
+
+
+#ifdef MODULE
+
+int init_module(void)
+{
+	int retval;
+	
+	retval=init_chrdev();
+	if(retval < 0)
+		return retval;
+	retval = init_lirc_it87();
+	if (retval) {
+		drop_chrdev();
+		return retval;
+	}
+	return 0;
+}
+
+
+void cleanup_module(void)
+{
+	drop_hardware();
+	drop_chrdev();
+	drop_port();
+	printk(KERN_INFO LIRC_DRIVER_NAME ": Uninstalled.\n");
+}
+
+MODULE_DESCRIPTION("LIRC driver for ITE IT8712/IT8705 CIR port");
+MODULE_AUTHOR("Hans-Günter Lütke Uphues");
+MODULE_LICENSE("GPL");
+
+module_param(io, int, 0444);
+MODULE_PARM_DESC(io, "I/O base address (default: 0x310)");
+
+module_param(irq, int, 0444);
+MODULE_PARM_DESC(irq, "Interrupt (1,3-12) (default: 7)");
+
+module_param(it87_enable_demodulator, bool, 0444);
+MODULE_PARM_DESC(it87_enable_demodulator, 
+		 "Receiver demodulator enable/disable (1/0), default: 0");
+
+module_param(debug, bool, 0644);
+MODULE_PARM_DESC(debug, "Enable debugging messages");
+
+EXPORT_NO_SYMBOLS;
+
+#endif /* MODULE */
+
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff -urN linux-2.6.10-rc3-mm1/drivers/char/lirc/lirc_it87.h linux-2.6.10-rc3-no2/drivers/char/lirc/lirc_it87.h
--- linux-2.6.10-rc3-mm1/drivers/char/lirc/lirc_it87.h	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.10-rc3-no2/drivers/char/lirc/lirc_it87.h	2004-12-22 19:35:16.572729936 -0500
@@ -0,0 +1,116 @@
+/* lirc_it87.h */
+/* SECTION: Definitions */
+
+/********************************* ITE IT87xx ************************/
+
+/* based on the following documentation from ITE:
+   a) IT8712F Preliminary CIR Programming Guide V0.1
+   b) IT8705F Simple LPC I/O Preliminary Specifiction V0.3
+   c) IT8712F EC-LPC I/O Preliminary Specification V0.5
+*/
+
+/* IT8712/05 Ports: */
+#define IT87_ADRPORT      0x2e
+#define IT87_DATAPORT     0x2f
+#define IT87_INIT         0x87, 0x01, 0x55, 0x55
+
+/* alternate Ports: */
+/*
+#define IT87_ADRPORT      0x4e
+#define IT87_DATAPORT     0x4f
+#define IT87_INIT         0x87, 0x01, 0x55, 0xaa
+ */
+
+/* IT8712/05 Registers */
+#define IT87_CFGCTRL      0x2
+#define IT87_LDN          0x7
+#define IT87_CHIP_ID1     0x20
+#define IT87_CHIP_ID2     0x21
+#define IT87_CFG_VERSION  0x22
+#define IT87_SWSUSPEND    0x23
+
+#define IT8712_CIR_LDN    0xa
+#define IT8705_CIR_LDN    0x7
+
+/* CIR Configuration Registers: */
+#define IT87_CIR_ACT      0x30
+#define IT87_CIR_BASE_MSB 0x60
+#define IT87_CIR_BASE_LSB 0x61
+#define IT87_CIR_IRQ      0x70
+#define IT87_CIR_CONFIG   0xf0
+
+/* List of IT87_CIR registers: offset to BaseAddr */
+#define IT87_CIR_DR   0
+#define IT87_CIR_IER  1
+#define IT87_CIR_RCR  2
+#define IT87_CIR_TCR1 3
+#define IT87_CIR_TCR2 4
+#define IT87_CIR_TSR  5
+#define IT87_CIR_RSR  6
+#define IT87_CIR_BDLR 5
+#define IT87_CIR_BDHR 6
+#define IT87_CIR_IIR  7
+
+/* Bit Definitionen */
+/* IER: */
+#define IT87_CIR_IER_TM_EN   0x80
+#define IT87_CIR_IER_RESEVED 0x40
+#define IT87_CIR_IER_RESET   0x20
+#define IT87_CIR_IER_BR      0x10
+#define IT87_CIR_IER_IEC     0x8
+#define IT87_CIR_IER_RFOIE   0x4
+#define IT87_CIR_IER_RDAIE   0x2
+#define IT87_CIR_IER_TLDLIE  0x1
+
+/* RCR: */
+#define IT87_CIR_RCR_RDWOS  0x80
+#define IT87_CIR_RCR_HCFS   0x40
+#define IT87_CIR_RCR_RXEN   0x20
+#define IT87_CIR_RCR_RXEND  0x10
+#define IT87_CIR_RCR_RXACT  0x8
+#define IT87_CIR_RCR_RXDCR  0x7
+
+/* TCR1: */
+#define IT87_CIR_TCR1_FIFOCLR 0x80
+#define IT87_CIR_TCR1_ILE     0x40
+#define IT87_CIR_TCR1_FIFOTL  0x30
+#define IT87_CIR_TCR1_TXRLE   0x8
+#define IT87_CIR_TCR1_TXENDF  0x4
+#define IT87_CIR_TCR1_TXMPM   0x3
+
+/* TCR2: */
+#define IT87_CIR_TCR2_CFQ   0xf8
+#define IT87_CIR_TCR2_TXMPW 0x7
+
+/* TSR: */
+#define IT87_CIR_TSR_RESERVED 0xc0
+#define IT87_CIR_TSR_TXFBC    0x3f
+
+/* RSR: */
+#define IT87_CIR_RSR_RXFTO    0x80
+#define IT87_CIR_RSR_RESERVED 0x40
+#define IT87_CIR_RSR_RXFBC    0x3f
+
+/* IIR: */
+#define IT87_CIR_IIR_RESERVED 0xf8
+#define IT87_CIR_IIR_IID      0x6
+#define IT87_CIR_IIR_IIP      0x1
+
+/* TM: */
+#define IT87_CIR_TM_IL_SEL    0x80
+#define IT87_CIR_TM_RESERVED  0x40
+#define IT87_CIR_TM_TM_REG    0x3f
+
+#define IT87_CIR_FIFO_SIZE 32
+
+/* Baudratedivisor for IT87: power of 2: only 1,2,4 or 8) */
+#define IT87_CIR_BAUDRATE_DIVISOR 0x1
+#define IT87_CIR_DEFAULT_IOBASE 0x310
+#define IT87_CIR_DEFAULT_IRQ    0x7
+#define IT87_CIR_SPACE 0x00
+#define IT87_CIR_PULSE 0xff
+#define IT87_CIR_FREQ_MIN 27
+#define IT87_CIR_FREQ_MAX 58
+#define TIME_CONST (IT87_CIR_BAUDRATE_DIVISOR * 8000000ul / 115200ul)
+
+/********************************* ITE IT87xx ************************/
diff -urN linux-2.6.10-rc3-mm1/drivers/char/lirc/lirc_mceusb.c linux-2.6.10-rc3-no2/drivers/char/lirc/lirc_mceusb.c
--- linux-2.6.10-rc3-mm1/drivers/char/lirc/lirc_mceusb.c	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.10-rc3-no2/drivers/char/lirc/lirc_mceusb.c	2004-12-22 19:35:16.575729519 -0500
@@ -0,0 +1,1029 @@
+/*
+ * USB Microsoft IR Transceiver driver - 0.2
+ *
+ * Copyright (c) 2003-2004 Dan Conti (dconti@acm.wwu.edu)
+ *
+ * This driver is based on the USB skeleton driver packaged with the
+ * kernel, and the notice from that package has been retained below.
+ *
+ * The Microsoft IR Transceiver is a neat little IR receiver with two
+ * emitters on it designed for Windows Media Center. This driver might
+ * work for all media center remotes, but I have only tested it with
+ * the philips model. The first revision of this driver only supports
+ * the receive function - the transmit function will be much more
+ * tricky due to the nature of the hardware. Microsoft chose to build
+ * this device inexpensively, therefore making it extra dumb.
+ * There is no interrupt endpoint on this device; all usb traffic
+ * happens over two bulk endpoints. As a result of this, poll() for
+ * this device is an actual hardware poll (instead of a receive queue
+ * check) and is rather expensive.
+ *
+ * All trademarks property of their respective owners. This driver was
+ * originally based on the USB skeleton driver, although significant
+ * portions of that code have been removed as the driver has evolved.
+ *
+ * 2003_11_11 - Restructured to minimalize code interpretation in the
+ *              driver. The normal use case will be with lirc.
+ *
+ * 2004_01_01 - Removed all code interpretation. Generate mode2 data
+ *              for passing off to lirc. Cleanup
+ *
+ * 2004_01_04 - Removed devfs handle. Put in a temporary workaround
+ *              for a known issue where repeats generate two
+ *              sequential spaces (last_was_repeat_gap)
+ *
+ * 2004_02_17 - Changed top level api to no longer use fops, and
+ *              instead use new interface for polling via
+ *              lirc_thread. Restructure data read/mode2 generation to
+ *              a single pass, reducing number of buffers. Rev to .2
+ *
+ * 2004_02_27 - Last of fixups to plugin->add_to_buf API. Properly
+ *              handle broken fragments from the receiver. Up the
+ *              sample rate and remove any pacing from
+ *              fetch_more_data. Fixes all known issues.
+ * 
+ * TODO
+ *   - Fix up minor number, registration of major/minor with usb subsystem
+ *
+ */
+/*
+ * USB Skeleton driver - 1.1
+ *
+ * Copyright (C) 2001-2003 Greg Kroah-Hartman (greg@kroah.com)
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License as
+ *	published by the Free Software Foundation, version 2.
+ *
+ *
+ * This driver is to be used as a skeleton driver to be able to create a
+ * USB driver quickly.  The design of it is based on the usb-serial and
+ * dc2xx drivers.
+ *
+ * Thanks to Oliver Neukum, David Brownell, and Alan Stern for their help
+ * in debugging this driver.
+ *
+ *
+ * History:
+ *
+ * 2003-05-06 - 1.1 - changes due to usb core changes with usb_register_dev()
+ * 2003-02-25 - 1.0 - fix races involving urb->status, unlink_urb(), and
+ *			disconnect.  Fix transfer amount in read().  Use
+ *			macros instead of magic numbers in probe().  Change
+ *			size variables to size_t.  Show how to eliminate
+ *			DMA bounce buffer.
+ * 2002_12_12 - 0.9 - compile fixes and got rid of fixed minor array.
+ * 2002_09_26 - 0.8 - changes due to USB core conversion to struct device
+ *			driver.
+ * 2002_02_12 - 0.7 - zero out dev in probe function for devices that do
+ *			not have both a bulk in and bulk out endpoint.
+ *			Thanks to Holger Waechtler for the fix.
+ * 2001_11_05 - 0.6 - fix minor locking problem in skel_disconnect.
+ *			Thanks to Pete Zaitcev for the fix.
+ * 2001_09_04 - 0.5 - fix devfs bug in skel_disconnect. Thanks to wim delvaux
+ * 2001_08_21 - 0.4 - more small bug fixes.
+ * 2001_05_29 - 0.3 - more bug fixes based on review from linux-usb-devel
+ * 2001_05_24 - 0.2 - bug fixes based on review from linux-usb-devel people
+ * 2001_05_01 - 0.1 - first version
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/smp_lock.h>
+#include <linux/usb.h>
+#ifdef KERNEL_2_5
+#include <linux/completion.h>
+#include <asm/uaccess.h>
+#else
+#include <linux/spinlock.h>
+#include <linux/list.h>
+#include <linux/fcntl.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#endif
+
+#ifdef CONFIG_USB_DEBUG
+	static int debug = 1;
+#else
+	static int debug = 0;
+#endif
+
+#include "linux/kcompat.h"
+#include "linux/lirc.h"
+#include "drivers/lirc_dev/lirc_dev.h"
+
+/* Use our own dbg macro */
+#define dprintk(fmt, args...)                             \
+	do{                                               \
+		if(debug) printk(KERN_DEBUG __FILE__ ": " \
+				 fmt "\n", ## args);      \
+	}while(0)
+
+/* Version Information */
+#define DRIVER_VERSION "v0.2"
+#define DRIVER_AUTHOR "Dan Conti, dconti@acm.wwu.edu"
+#define DRIVER_DESC "USB Microsoft IR Transceiver Driver"
+#define DRIVER_NAME "lirc_mceusb"
+
+/* Define these values to match your device */
+#define USB_MCEUSB_VENDOR_ID	0x045e
+#define USB_MCEUSB_PRODUCT_ID	0x006d
+
+/* table of devices that work with this driver */
+static struct usb_device_id mceusb_table [] = {
+	{ USB_DEVICE(USB_MCEUSB_VENDOR_ID, USB_MCEUSB_PRODUCT_ID) },
+	{ }					/* Terminating entry */
+};
+
+/* we can have up to this number of device plugged in at once */
+#define MAX_DEVICES		16
+
+/* Structure to hold all of our device specific stuff */
+struct usb_skel {
+	struct usb_device *	    udev;		/* save off the usb device pointer */
+	struct usb_interface *	interface;		/* the interface for this device */
+	unsigned char   minor;				/* the starting minor number for this device */
+	unsigned char   num_ports;			/* the number of ports this device has */
+	char            num_interrupt_in;		/* number of interrupt in endpoints we have */
+	char            num_bulk_in;			/* number of bulk in endpoints we have */
+	char            num_bulk_out;			/* number of bulk out endpoints we have */
+
+	unsigned char *    bulk_in_buffer;		/* the buffer to receive data */
+	int                bulk_in_size;		/* the size of the receive buffer */
+	__u8               bulk_in_endpointAddr;	/* the address of the bulk in endpoint */
+
+	unsigned char *    bulk_out_buffer;		/* the buffer to send data */
+	int	               bulk_out_size;		/* the size of the send buffer */
+	struct urb *       write_urb;			/* the urb used to send data */
+	__u8               bulk_out_endpointAddr;	/* the address of the bulk out endpoint */
+
+	atomic_t		write_busy;		/* true iff write urb is busy */
+	struct completion	write_finished;		/* wait for the write to finish */
+	
+	wait_queue_head_t  wait_q;			/* for timeouts */
+	int                open_count;			/* number of times this port has been opened */
+	struct semaphore   sem;				/* locks this structure */
+	
+	int			present;		/* if the device is not disconnected */		
+
+	struct lirc_plugin* plugin;
+	
+	lirc_t lircdata[256];          			/* place to store values until lirc processes them */
+	int    lircidx;                			/* current index */
+	int    lirccnt;                			/* remaining values */
+	
+	int    usb_valid_bytes_in_bulk_buffer;		/* leftover data from a previous read */
+	int    mce_bytes_left_in_packet;		/* for packets split across multiple reads */
+	
+	/* Value to hold the last received space; 0 if last value
+	 * received was a pulse
+	 */
+	int    last_space;
+	
+#ifdef KERNEL_2_5
+	dma_addr_t dma_in;
+	dma_addr_t dma_out;
+#endif
+};
+
+#define MCE_TIME_UNIT 50
+
+/* driver api */
+#ifdef KERNEL_2_5
+static int mceusb_probe		(struct usb_interface *interface, const struct usb_device_id *id);
+static void mceusb_disconnect	(struct usb_interface *interface);
+static void mceusb_write_bulk_callback	(struct urb *urb, struct pt_regs *regs);
+#else
+static void * mceusb_probe	(struct usb_device *dev, unsigned int ifnum, const struct usb_device_id *id);
+static void mceusb_disconnect	(struct usb_device *dev, void *ptr);
+static void mceusb_write_bulk_callback	(struct urb *urb);
+#endif
+
+/* read data from the usb bus; convert to mode2 */
+static int msir_fetch_more_data( struct usb_skel* dev, int dont_block );
+
+/* helper functions */
+static void msir_cleanup( struct usb_skel* dev );
+static void set_use_dec( void* data );
+static int set_use_inc( void* data );
+    
+/* array of pointers to our devices that are currently connected */
+static struct usb_skel		*minor_table[MAX_DEVICES];
+
+/* lock to protect the minor_table structure */
+static DECLARE_MUTEX (minor_table_mutex);
+static void mceusb_setup( struct usb_device *udev );
+
+/* usb specific object needed to register this driver with the usb subsystem */
+static struct usb_driver mceusb_driver = {
+	.owner =	THIS_MODULE,
+	.name =		DRIVER_NAME,
+	.probe =	mceusb_probe,
+	.disconnect =	mceusb_disconnect,
+	.id_table =	mceusb_table,
+};
+
+
+/**
+ *	usb_mceusb_debug_data
+ */
+static inline void usb_mceusb_debug_data (const char *function, int size,
+					  const unsigned char *data)
+{
+	int i;
+	if (!debug)
+		return;
+	
+	printk(KERN_DEBUG __FILE__": %s - length = %d, data = ", 
+	       function, size);
+	for (i = 0; i < size; ++i) {
+		printk(KERN_DEBUG "%.2x ", data[i]);
+	}
+	printk(KERN_DEBUG "\n");
+}
+
+/**
+ *mceusb_delete
+ */
+static inline void mceusb_delete (struct usb_skel *dev)
+{
+	dprintk("%s", __func__);
+	minor_table[dev->minor] = NULL;
+#ifdef KERNEL_2_5
+	usb_buffer_free(dev->udev, dev->bulk_in_size, dev->bulk_in_buffer, dev->dma_in);
+	usb_buffer_free(dev->udev, dev->bulk_out_size, dev->bulk_out_buffer, dev->dma_out);
+#else
+	if (dev->bulk_in_buffer != NULL)
+		kfree (dev->bulk_in_buffer);
+	if (dev->bulk_out_buffer != NULL)
+		kfree (dev->bulk_out_buffer);
+#endif
+	if (dev->write_urb != NULL)
+		usb_free_urb (dev->write_urb);
+	kfree (dev);
+}
+
+static void mceusb_setup( struct usb_device *udev )
+{
+	char data[8];
+	int res;
+	
+	memset( data, 0, 8 );
+
+	/* Get Status */
+	res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+			      USB_REQ_GET_STATUS, USB_DIR_IN,
+			      0, 0, data, 2, HZ * 3);
+    
+	/*    res = usb_get_status( udev, 0, 0, data ); */
+	dprintk("%s - res = %d status = 0x%x 0x%x", __func__,
+		res, data[0], data[1]);
+    
+	/* This is a strange one. They issue a set address to the device
+	 * on the receive control pipe and expect a certain value pair back
+	 */
+	memset( data, 0, 8 );
+
+	res = usb_control_msg( udev, usb_rcvctrlpipe(udev, 0),
+			       5, USB_TYPE_VENDOR, 0, 0,
+			       data, 2, HZ * 3 );
+	dprintk("%s - res = %d, devnum = %d", __func__, res, udev->devnum);
+	dprintk("%s - data[0] = %d, data[1] = %d", __func__,
+		data[0], data[1] );
+
+    
+	/* set feature */
+	res = usb_control_msg( udev, usb_sndctrlpipe(udev, 0),
+			       USB_REQ_SET_FEATURE, USB_TYPE_VENDOR,
+			       0xc04e, 0x0000, NULL, 0, HZ * 3 );
+    
+	dprintk("%s - res = %d", __func__, res);
+
+	/* These two are sent by the windows driver, but stall for
+	 * me. I dont have an analyzer on the linux side so i can't
+	 * see what is actually different and why the device takes
+	 * issue with them
+	 */
+#if 0
+	/* this is some custom control message they send */
+	res = usb_control_msg( udev, usb_sndctrlpipe(udev, 0),
+			       0x04, USB_TYPE_VENDOR,
+			       0x0808, 0x0000, NULL, 0, HZ * 3 );
+    
+	dprintk("%s - res = %d", __func__, res);
+    
+	/* this is another custom control message they send */
+	res = usb_control_msg( udev, usb_sndctrlpipe(udev, 0),
+			       0x02, USB_TYPE_VENDOR,
+			       0x0000, 0x0100, NULL, 0, HZ * 3 );
+    
+	dprintk("%s - res = %d", __func__, res);
+#endif
+}
+
+static void msir_cleanup( struct usb_skel* dev )
+{
+	memset( dev->bulk_in_buffer, 0, dev->bulk_in_size );
+
+	dev->usb_valid_bytes_in_bulk_buffer = 0;
+
+	dev->last_space = PULSE_MASK;
+    
+	dev->mce_bytes_left_in_packet = 0;
+	dev->lircidx = 0;
+	dev->lirccnt = 0;
+	memset( dev->lircdata, 0, sizeof(dev->lircdata) );
+}
+
+static int set_use_inc(void* data)
+{
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static void set_use_dec(void* data)
+{
+	/* check for unplug here */
+	struct usb_skel* dev = (struct usb_skel*) data;
+	if( !dev->udev )
+	{ 
+		lirc_unregister_plugin( dev->minor );
+		lirc_buffer_free( dev->plugin->rbuf );
+		kfree( dev->plugin->rbuf );
+		kfree( dev->plugin );
+	}
+	
+	MOD_DEC_USE_COUNT;
+}
+
+/*
+ * msir_fetch_more_data
+ *
+ * The goal here is to read in more remote codes from the remote. In
+ * the event that the remote isn't sending us anything, the caller
+ * will block until a key is pressed (i.e. this performs phys read,
+ * filtering, and queueing of data) unless dont_block is set to 1; in
+ * this situation, it will perform a few reads and will exit out if it
+ * does not see any appropriate data
+ *
+ * dev->sem should be locked when this function is called - fine grain
+ * locking isn't really important here anyways
+ *
+ * This routine always returns the number of words available
+ *
+ */
+static int msir_fetch_more_data( struct usb_skel* dev, int dont_block )
+{
+	int retries = 0;
+	int words_to_read = 
+		(sizeof(dev->lircdata)/sizeof(lirc_t)) - dev->lirccnt;
+	int partial, this_read = 0;
+	int bulkidx = 0;
+	int bytes_left_in_packet = 0;
+	signed char* signedp = (signed char*)dev->bulk_in_buffer;
+	
+	if( words_to_read == 0 )
+		return dev->lirccnt;
+
+	/* this forces all existing data to be read by lirc before we
+	 * issue another usb command. this is the only form of
+	 * throttling we have
+	 */
+	if( dev->lirccnt )
+	{
+		return dev->lirccnt;
+	}
+
+	/* reserve room for our leading space */
+	if( dev->last_space )
+		words_to_read--;
+		
+	while( words_to_read )
+	{
+		/* handle signals and USB disconnects */
+		if( signal_pending(current) )
+		{
+			return dev->lirccnt ? dev->lirccnt : -EINTR;
+		}
+		if( !dev->udev )
+		{
+			return -ENODEV;
+		}
+
+		bulkidx = 0;
+
+		/*
+		 * perform data read (phys or from previous buffer)
+		 */
+        
+		/* use leftovers if present, otherwise perform a read */
+		if( dev->usb_valid_bytes_in_bulk_buffer )
+		{
+			this_read = partial = 
+				dev->usb_valid_bytes_in_bulk_buffer;
+			dev->usb_valid_bytes_in_bulk_buffer = 0;
+		}
+		else
+		{
+			int retval;
+            
+			this_read = dev->bulk_in_size;
+			partial = 0;
+			retval = usb_bulk_msg
+				(dev->udev,
+				 usb_rcvbulkpipe
+				 (dev->udev, dev->bulk_in_endpointAddr),
+				 (unsigned char*)dev->bulk_in_buffer,
+				 this_read, &partial, HZ*10);
+			
+			/* retry a few times on overruns; map all
+			   other errors to -EIO */
+			if( retval )
+			{
+				if( retval == -EOVERFLOW && 
+				    retries < 5 )
+				{
+					retries++;
+					interruptible_sleep_on_timeout
+						( &dev->wait_q, HZ );
+					continue;
+				}
+				else
+				{
+					return -EIO;
+				}
+			}
+            
+			retries = 0;
+			if( partial )
+				this_read = partial;
+
+			/* skip the header */
+			bulkidx += 2;
+            
+			/* check for empty reads (header only) */
+			if( this_read == 2 )
+			{
+				/* assume no data */
+				if( dont_block )
+				{
+					break;
+				}
+
+				/* sleep for a bit before performing
+				   another read */
+				interruptible_sleep_on_timeout
+					( &dev->wait_q, 1 );
+				continue;
+			}
+		}
+
+		/*
+		 * process data
+		 */
+        
+		/* at this point this_read is > 0 */
+		while( bulkidx < this_read &&
+		       (words_to_read > (dev->last_space ? 1 : 0)) )
+			//while( bulkidx < this_read && words_to_read )
+		{
+			int keycode;
+			int pulse = 0;
+            
+			/* read packet length if needed */
+			if( !bytes_left_in_packet )
+			{
+				
+				/* we assume we are on a packet length
+				 * value. it is possible, in some
+				 * cases, to get a packet that does
+				 * not start with a length, apparently
+				 * due to some sort of fragmenting,
+				 * but occaisonally we do not receive
+				 * the second half of a fragment
+				 */
+				bytes_left_in_packet = 
+					128 + signedp[bulkidx++];
+
+				/* unfortunately rather than keep all
+				 * the data in the packetized format,
+				 * the transceiver sends a trailing 8
+				 * bytes that aren't part of the
+				 * transmittion from the remote,
+				 * aren't packetized, and dont really
+				 * have any value. we can basically
+				 * tell we have hit them if 1) we have
+				 * a loooong space currently stored
+				 * up, and 2) the bytes_left value for
+				 * this packet is obviously wrong
+				 */
+				if( bytes_left_in_packet > 4  )
+				{
+					if( dev->mce_bytes_left_in_packet )
+					{
+						bytes_left_in_packet = dev->mce_bytes_left_in_packet;
+						bulkidx--;
+					}
+					bytes_left_in_packet = 0;
+					bulkidx = this_read;
+				}
+
+				/* always clear this if we have a
+				   valid packet */
+				dev->mce_bytes_left_in_packet = 0;
+                    
+				/* continue here to verify we haven't
+				   hit the end of the bulk_in */
+				continue;
+				
+			}
+
+			/*
+			 * generate mode2
+			 */
+            
+			keycode = signedp[bulkidx++];
+			if( keycode < 0 )
+			{
+				pulse = 1;
+				keycode += 128;
+			}
+			keycode *= MCE_TIME_UNIT;
+
+			bytes_left_in_packet--;
+            
+			if( pulse )
+			{
+				if( dev->last_space )
+				{
+					dev->lircdata[dev->lirccnt++] =
+						dev->last_space;
+					dev->last_space = 0;
+					words_to_read--;
+
+					/* clear the lirc_t for the pulse */
+					dev->lircdata[dev->lirccnt] = 0;
+				}
+				dev->lircdata[dev->lirccnt] += keycode;
+				dev->lircdata[dev->lirccnt] |= PULSE_BIT;
+			}
+			else
+			{
+				/* on pulse->space transition, add one
+				   for the existing pulse */
+				if( dev->lircdata[dev->lirccnt] &&
+				    !dev->last_space )
+				{
+					dev->lirccnt++;
+					words_to_read--;
+				}
+                
+				dev->last_space += keycode;
+			}
+		}
+	}
+	
+	/* save off some info if we are exiting mid-packet, or with
+	   leftovers */
+	if( bytes_left_in_packet )
+	{
+		dev->mce_bytes_left_in_packet = bytes_left_in_packet;
+	}
+	if( bulkidx < this_read )
+	{
+		dev->usb_valid_bytes_in_bulk_buffer = (this_read - bulkidx);
+		memcpy( dev->bulk_in_buffer, &(dev->bulk_in_buffer[bulkidx]),
+			dev->usb_valid_bytes_in_bulk_buffer );
+	}
+	return dev->lirccnt;
+}
+
+/* mceusb_add_to_buf: called by lirc_dev to fetch all available keys
+ * this is used as a polling interface for us: since we set
+ * plugin->sample_rate we will periodically get the below call to
+ * check for new data returns 0 on success, or -ENODATA if nothing is
+ * available
+ */
+static int mceusb_add_to_buf(void* data, struct lirc_buffer* buf )
+{
+	struct usb_skel* dev = (struct usb_skel*) data;
+
+	down( &dev->sem );
+
+	/* verify device still present */
+	if( dev->udev == NULL )
+	{
+		up( &dev->sem );
+		return -ENODEV;
+	}
+
+	if( !dev->lirccnt )
+	{
+		int res;
+		dev->lircidx = 0;
+        
+		res = msir_fetch_more_data( dev, 1 );
+		
+		if( res == 0 )
+			res = -ENODATA;
+		if( res < 0 ) {
+			up( &dev->sem );
+			return res;
+		}
+	}
+
+	if( dev->lirccnt )
+	{
+		int keys_to_copy;
+
+		/* determine available buffer space and available data */
+		keys_to_copy = lirc_buffer_available( buf );
+		if( keys_to_copy > dev->lirccnt )
+		{
+			keys_to_copy = dev->lirccnt;
+		}
+        
+		lirc_buffer_write_n( buf, (unsigned char*) &(dev->lircdata[dev->lircidx]), keys_to_copy );
+		dev->lircidx += keys_to_copy;
+		dev->lirccnt -= keys_to_copy;
+        
+		up( &dev->sem );
+		return 0;
+	}
+
+	up( &dev->sem );
+	return -ENODATA;
+}
+
+/**
+ *	mceusb_write_bulk_callback
+ */
+#ifdef KERNEL_2_5 
+static void mceusb_write_bulk_callback (struct urb *urb, struct pt_regs *regs)
+#else
+static void mceusb_write_bulk_callback (struct urb *urb)
+#endif
+{
+	struct usb_skel *dev = (struct usb_skel *)urb->context;
+
+	dprintk("%s - minor %d", __func__, dev->minor);
+
+	if ((urb->status != -ENOENT) && 
+	    (urb->status != -ECONNRESET)) {
+		dprintk("%s - nonzero write buld status received: %d",
+			__func__, urb->status);
+		return;
+	}
+
+	return;
+}
+
+/**
+ *	mceusb_probe
+ *
+ *	Called by the usb core when a new device is connected that it 
+ *	thinks this driver might be interested in.
+ */
+#ifdef KERNEL_2_5 
+static int mceusb_probe(struct usb_interface *interface, const struct usb_device_id *id)
+{
+	struct usb_device *udev = interface_to_usbdev(interface);
+	struct usb_host_interface *iface_desc;
+#else
+static void * mceusb_probe(struct usb_device *udev, unsigned int ifnum,
+			   const struct usb_device_id *id)
+{
+	struct usb_interface *interface = &udev->actconfig->interface[ifnum];
+	struct usb_interface_descriptor *iface_desc;	
+#endif
+	struct usb_skel *dev = NULL;
+	struct usb_endpoint_descriptor *endpoint;
+	
+	struct lirc_plugin* plugin;
+	struct lirc_buffer* rbuf;
+
+	int minor;
+	size_t buffer_size;
+	int i;
+	int retval = -ENOMEM;
+	
+	/* See if the device offered us matches what we can accept */
+	if ((udev->descriptor.idVendor != USB_MCEUSB_VENDOR_ID) ||
+	    (udev->descriptor.idProduct != USB_MCEUSB_PRODUCT_ID)) {
+	    	dprintk("Wrong Vendor/Product IDs");
+#ifdef KERNEL_2_5
+		return -ENODEV;
+#else
+		return NULL;
+#endif
+	}
+
+	/* select a "subminor" number (part of a minor number) */
+	down (&minor_table_mutex);
+	for (minor = 0; minor < MAX_DEVICES; ++minor) {
+		if (minor_table[minor] == NULL)
+			break;
+	}
+	if (minor >= MAX_DEVICES) {
+		info ("Too many devices plugged in, "
+		      "can not handle this device.");
+		goto error;
+	}
+
+	/* allocate memory for our device state and initialize it */
+	dev = kmalloc (sizeof(struct usb_skel), GFP_KERNEL);
+	if (dev == NULL) {
+		err ("Out of memory");
+#ifdef KERNEL_2_5
+		retval = -ENOMEM;
+#endif
+		goto error;
+	}
+	minor_table[minor] = dev;
+	
+	memset (dev, 0x00, sizeof (*dev));
+	init_MUTEX (&dev->sem);
+	dev->udev = udev;
+	dev->interface = interface;
+	dev->minor = minor;
+
+	/* set up the endpoint information */
+	/* check out the endpoints */
+	/* use only the first bulk-in and bulk-out endpoints */
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,4)
+	iface_desc = interface->cur_altsetting;
+#else
+	iface_desc = &interface->altsetting[0];
+#endif
+
+#ifdef KERNEL_2_5
+	for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+		endpoint = &iface_desc->endpoint[i].desc;
+#else
+	for (i = 0; i < iface_desc->bNumEndpoints; ++i) {
+		endpoint = &iface_desc->endpoint[i];
+#endif
+		if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) &&
+		    ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
+		     USB_ENDPOINT_XFER_BULK)) {
+			dprintk("we found a bulk in endpoint");
+			buffer_size = endpoint->wMaxPacketSize;
+			dev->bulk_in_size = buffer_size;
+			dev->bulk_in_endpointAddr = endpoint->bEndpointAddress;
+#ifdef KERNEL_2_5
+			dev->bulk_in_buffer = usb_buffer_alloc
+				(udev, buffer_size, SLAB_ATOMIC, &dev->dma_in);
+#else
+			dev->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL);
+#endif
+			if (!dev->bulk_in_buffer) {
+				err("Couldn't allocate bulk_in_buffer");
+				goto error;
+			}
+		}
+		
+		if (((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == 0x00) &&
+		    ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
+		     USB_ENDPOINT_XFER_BULK)) {
+			dprintk("we found a bulk out endpoint");
+#ifdef KERNEL_2_5
+			dev->write_urb = usb_alloc_urb(0, GFP_KERNEL);
+#else
+			dev->write_urb = usb_alloc_urb(0);
+#endif
+			if (!dev->write_urb) {
+				err("No free urbs available");
+				goto error;
+			}
+			buffer_size = endpoint->wMaxPacketSize;
+			dev->bulk_out_size = buffer_size;
+			dev->bulk_out_endpointAddr = endpoint->bEndpointAddress;
+#ifdef KERNEL_2_5
+			dev->bulk_out_buffer = usb_buffer_alloc(udev, buffer_size, SLAB_ATOMIC, &dev->dma_out);
+#else
+			dev->bulk_out_buffer = kmalloc (buffer_size, GFP_KERNEL);
+#endif
+			if (!dev->bulk_out_buffer) {
+				err("Couldn't allocate bulk_out_buffer");
+				goto error;
+			}
+#ifdef KERNEL_2_5
+			usb_fill_bulk_urb(dev->write_urb, udev, 
+				      usb_sndbulkpipe
+				      (udev, endpoint->bEndpointAddress),
+				      dev->bulk_out_buffer, buffer_size,
+				      mceusb_write_bulk_callback, dev);
+#else
+			FILL_BULK_URB(dev->write_urb, udev,
+				      usb_sndbulkpipe
+				      (udev, endpoint->bEndpointAddress),
+				      dev->bulk_out_buffer, buffer_size,
+				      mceusb_write_bulk_callback, dev);
+#endif 
+		}
+	}
+
+	if (!(dev->bulk_in_endpointAddr && dev->bulk_out_endpointAddr)) {
+		err("Couldn't find both bulk-in and bulk-out endpoints");
+		goto error;
+	}
+
+	/* init the waitq */
+	init_waitqueue_head( &dev->wait_q );
+
+
+	/* Set up our lirc plugin */
+	if(!(plugin = kmalloc(sizeof(struct lirc_plugin), GFP_KERNEL))) {
+		err("out of memory");
+		goto error;
+	}
+	memset( plugin, 0, sizeof(struct lirc_plugin) );
+
+	if(!(rbuf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL))) {
+		err("out of memory");
+		kfree( plugin );
+		goto error;
+	}
+    
+	/* the lirc_atiusb module doesn't memset rbuf here ... ? */
+	if( lirc_buffer_init( rbuf, sizeof(lirc_t), 128)) {
+		err("out of memory");
+		kfree( plugin );
+		kfree( rbuf );
+		goto error;
+	}
+
+	strcpy(plugin->name, DRIVER_NAME " ");
+	plugin->minor       = minor;
+	plugin->code_length = sizeof(lirc_t) * 8;
+	plugin->features    = LIRC_CAN_REC_MODE2; // | LIRC_CAN_SEND_MODE2;
+	plugin->data        = dev;
+	plugin->rbuf        = rbuf;
+	plugin->ioctl       = NULL;
+	plugin->set_use_inc = &set_use_inc;
+	plugin->set_use_dec = &set_use_dec;
+	plugin->sample_rate = 80;   // sample at 100hz (10ms)
+	plugin->add_to_buf  = &mceusb_add_to_buf;
+	//    plugin->fops        = &mceusb_fops;
+	if( lirc_register_plugin(plugin) < 0 )
+	{
+		kfree( plugin );
+		lirc_buffer_free( rbuf );
+		kfree( rbuf );
+		goto error;
+	}
+	dev->plugin = plugin;
+	
+	/* clear off the first few messages. these look like
+	 * calibration or test data, i can't really tell
+	 * this also flushes in case we have random ir data queued up
+	 */
+	{
+		char junk[64];
+		int partial = 0, retval, i;
+		for( i = 0; i < 40; i++ )
+		{
+			retval = usb_bulk_msg
+				(udev, usb_rcvbulkpipe
+				 (udev, dev->bulk_in_endpointAddr),
+				 junk, 64,
+				 &partial, HZ*10);
+		}
+	}
+    
+	msir_cleanup( dev );
+	mceusb_setup( udev );
+	
+#ifdef KERNEL_2_5
+	/* we can register the device now, as it is ready */
+	usb_set_intfdata (interface, dev);
+#endif	
+	/* let the user know what node this device is now attached to */
+	//info ("USB Microsoft IR Transceiver device now attached to msir%d", dev->minor);
+	up (&minor_table_mutex);
+#ifdef KERNEL_2_5
+	return 0;
+#else
+	return dev;
+#endif
+ error:
+	mceusb_delete (dev);
+	dev = NULL;
+	dprintk("%s: retval = %x", __func__, retval);
+	up (&minor_table_mutex);
+#ifdef KERNEL_2_5
+	return retval;
+#else
+	return NULL;
+#endif
+}
+
+/**
+ *	mceusb_disconnect
+ *
+ *	Called by the usb core when the device is removed from the system.
+ *
+ *	This routine guarantees that the driver will not submit any more urbs
+ *	by clearing dev->udev.  It is also supposed to terminate any currently
+ *	active urbs.  Unfortunately, usb_bulk_msg(), used in skel_read(), does
+ *	not provide any way to do this.  But at least we can cancel an active
+ *	write.
+ */
+#ifdef KERNEL_2_5 
+static void mceusb_disconnect(struct usb_interface *interface)
+#else
+static void mceusb_disconnect(struct usb_device *udev, void *ptr)
+#endif
+{
+	struct usb_skel *dev;
+	int minor;
+#ifdef KERNEL_2_5
+	dev = usb_get_intfdata (interface);
+	usb_set_intfdata (interface, NULL);
+#else
+	dev = (struct usb_skel *)ptr;
+#endif
+	
+	down (&minor_table_mutex);
+	down (&dev->sem);
+	minor = dev->minor;
+
+	/* unhook lirc things */
+	lirc_unregister_plugin( minor );
+	lirc_buffer_free( dev->plugin->rbuf );
+	kfree( dev->plugin->rbuf );
+	kfree( dev->plugin );
+#ifdef KERNEL_2_5
+	/* terminate an ongoing write */
+	if (atomic_read (&dev->write_busy)) {
+		usb_unlink_urb (dev->write_urb);
+		wait_for_completion (&dev->write_finished);
+	}
+
+	/* prevent device read, write and ioctl */
+	dev->present = 0;
+#endif
+	
+	mceusb_delete (dev);
+	
+	info("Microsoft IR Transceiver #%d now disconnected", minor);
+	up (&dev->sem);
+	up (&minor_table_mutex);
+}
+
+
+
+/**
+ *	usb_mceusb_init
+ */
+static int __init usb_mceusb_init(void)
+{
+	int result;
+
+	/* register this driver with the USB subsystem */
+	result = usb_register(&mceusb_driver);
+#ifdef KERNEL_2_5	
+	if ( result ) {
+#else
+	if ( result < 0 ) {
+#endif
+		err("usb_register failed for the " DRIVER_NAME " driver. error number %d",result);
+#ifdef KERNEL_2_5
+		return result;
+#else
+		return -1;
+#endif
+	}
+
+	info(DRIVER_DESC " " DRIVER_VERSION);
+	return 0;
+}
+
+
+/**
+ *	usb_mceusb_exit
+ */
+static void __exit usb_mceusb_exit(void)
+{
+	/* deregister this driver with the USB subsystem */
+	usb_deregister(&mceusb_driver);
+}
+
+ module_init (usb_mceusb_init);
+module_exit (usb_mceusb_exit);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE (usb, mceusb_table);
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
+
+EXPORT_NO_SYMBOLS;
diff -urN linux-2.6.10-rc3-mm1/drivers/char/lirc/lirc_parallel.c linux-2.6.10-rc3-no2/drivers/char/lirc/lirc_parallel.c
--- linux-2.6.10-rc3-mm1/drivers/char/lirc/lirc_parallel.c	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.10-rc3-no2/drivers/char/lirc/lirc_parallel.c	2004-12-22 19:35:16.578729103 -0500
@@ -0,0 +1,783 @@
+/*      $Id: lirc_parallel.c,v 5.27 2004/11/07 13:31:18 lirc Exp $      */
+
+/****************************************************************************
+ ** lirc_parallel.c *********************************************************
+ ****************************************************************************
+ * 
+ * lirc_parallel - device driver for infra-red signal receiving and
+ *                 transmitting unit built by the author
+ * 
+ * Copyright (C) 1998 Christoph Bartelmus <lirc@bartelmus.de>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */ 
+
+/***********************************************************************
+ *************************       Includes        ***********************
+ ***********************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <linux/version.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 2, 18)
+#error "**********************************************************"
+#error " Sorry, this driver needs kernel version 2.2.18 or higher "
+#error "**********************************************************"
+#endif
+
+#include <linux/config.h>
+
+#ifdef CONFIG_SMP
+#error "--- Sorry, this driver is not SMP safe. ---"
+#endif
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/config.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/time.h>
+#include <linux/mm.h>
+#include <linux/delay.h>
+
+#include <asm/io.h>
+#include <asm/signal.h>
+#include <asm/irq.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 0)
+#include <asm/div64.h>
+#endif
+
+#include <asm/uaccess.h>
+#include <linux/poll.h>
+#include <linux/parport.h>
+
+#include "linux/lirc.h"
+#include "linux/kcompat.h"
+#include "drivers/lirc_dev/lirc_dev.h"
+
+#include "lirc_parallel.h"
+
+#define LIRC_DRIVER_NAME "lirc_parallel"
+
+/***********************************************************************
+ *************************   Globale Variablen   ***********************
+ ***********************************************************************/
+
+static int debug = 0;
+unsigned int irq = LIRC_IRQ;
+unsigned int io = LIRC_PORT;
+#ifdef LIRC_TIMER
+unsigned int timer = 0;
+unsigned int default_timer = LIRC_TIMER;
+#endif
+
+#define WBUF_SIZE (256)
+#define RBUF_SIZE (256) /* this must be a power of 2 larger than 1 */
+
+static lirc_t wbuf[WBUF_SIZE];
+static lirc_t rbuf[RBUF_SIZE];
+
+DECLARE_WAIT_QUEUE_HEAD(lirc_wait);
+
+unsigned int rptr=0,wptr=0;
+unsigned int lost_irqs=0;
+int is_open=0;
+
+struct parport *pport;
+struct pardevice *ppdevice;
+int is_claimed=0;
+
+/***********************************************************************
+ *************************   Interne Funktionen  ***********************
+ ***********************************************************************/
+
+static unsigned int __inline__ in(int offset)
+{
+	switch(offset)
+	{
+	case LIRC_LP_BASE:
+		return(parport_read_data(pport));
+	case LIRC_LP_STATUS:
+		return(parport_read_status(pport));
+	case LIRC_LP_CONTROL:
+		return(parport_read_control(pport));
+	}
+	return(0); /* make compiler happy */
+}
+
+static void __inline__ out(int offset, int value)
+{
+	switch(offset)
+	{
+	case LIRC_LP_BASE:
+		parport_write_data(pport,value);
+		break;
+	case LIRC_LP_CONTROL:
+		parport_write_control(pport,value);
+		break;
+	case LIRC_LP_STATUS:
+		printk(KERN_INFO "%s: attempt to write to status register\n",
+		       LIRC_DRIVER_NAME);
+		break;
+	}
+}
+
+static unsigned int __inline__ lirc_get_timer(void)
+{
+	return(in(LIRC_PORT_TIMER)&LIRC_PORT_TIMER_BIT);
+}
+
+static unsigned int __inline__  lirc_get_signal(void)
+{
+	return(in(LIRC_PORT_SIGNAL)&LIRC_PORT_SIGNAL_BIT);
+}
+
+static void __inline__ lirc_on(void)
+{
+	out(LIRC_PORT_DATA,LIRC_PORT_DATA_BIT);
+}
+
+static void __inline__ lirc_off(void)
+{
+	out(LIRC_PORT_DATA,0);
+}
+
+static unsigned int init_lirc_timer(void)
+{
+	struct timeval tv,now;
+	unsigned int level,newlevel,timeelapsed,newtimer;
+	int count=0;
+	
+	do_gettimeofday(&tv);
+	tv.tv_sec++;                     /* wait max. 1 sec. */
+	level=lirc_get_timer();
+	do
+	{
+		newlevel=lirc_get_timer();
+		if(level==0 && newlevel!=0) count++;
+		level=newlevel;
+		do_gettimeofday(&now);
+	}
+	while(count<1000 && (now.tv_sec<tv.tv_sec 
+			     || (now.tv_sec==tv.tv_sec 
+				 && now.tv_usec<tv.tv_usec)));
+
+	timeelapsed=((now.tv_sec+1-tv.tv_sec)*1000000
+		     +(now.tv_usec-tv.tv_usec));
+	if(count>=1000 && timeelapsed>0)
+	{
+		if(default_timer==0)                    /* autodetect timer */
+		{
+			newtimer=(1000000*count)/timeelapsed;
+			printk(KERN_INFO "%s: %u Hz timer detected\n",
+			       LIRC_DRIVER_NAME,newtimer);
+			return(newtimer);
+		}
+		else
+		{
+			newtimer=(1000000*count)/timeelapsed;
+			if(abs(newtimer-default_timer)>
+			   default_timer/10) /* bad timer */
+			{
+				printk(KERN_NOTICE "%s: bad timer: %u Hz\n",
+				       LIRC_DRIVER_NAME,newtimer);
+				printk(KERN_NOTICE "%s: using default timer: "
+				       "%u Hz\n",
+				       LIRC_DRIVER_NAME,default_timer);
+				return(default_timer);
+			}
+			else
+			{
+				printk(KERN_INFO "%s: %u Hz timer detected\n",
+				       LIRC_DRIVER_NAME,newtimer);
+				return(newtimer); /* use detected value */
+			}
+		}
+	}
+	else
+	{
+		printk(KERN_NOTICE "%s: no timer detected\n",LIRC_DRIVER_NAME);
+		return(0);
+	}
+}
+
+static int lirc_claim(void)
+{
+	if(parport_claim(ppdevice)!=0)
+	{
+		printk(KERN_WARNING "%s: could not claim port\n",
+		       LIRC_DRIVER_NAME);
+		printk(KERN_WARNING "%s: waiting for port becoming available"
+		       "\n",LIRC_DRIVER_NAME);
+		if(parport_claim_or_block(ppdevice)<0)
+		{
+			printk(KERN_NOTICE "%s: could not claim port, giving"
+			       " up\n",LIRC_DRIVER_NAME);
+			return(0);
+		}
+	}
+	out(LIRC_LP_CONTROL,LP_PSELECP|LP_PINITP);
+	is_claimed=1;
+	return(1);
+}
+
+/***********************************************************************
+ *************************   interrupt handler  ************************
+ ***********************************************************************/
+
+static inline void rbuf_write(lirc_t signal)
+{
+	unsigned int nwptr;
+
+	nwptr=(wptr+1) & (RBUF_SIZE-1);
+	if(nwptr==rptr) /* no new signals will be accepted */
+	{
+		lost_irqs++;
+		printk(KERN_NOTICE "%s: buffer overrun\n",LIRC_DRIVER_NAME);
+		return;
+	}	
+	rbuf[wptr]=signal;
+	wptr=nwptr;
+}
+
+static void irq_handler(int i,void *blah,struct pt_regs * regs)
+{
+	struct timeval tv;
+	static struct timeval lasttv;
+	static int init=0;
+	long signal;
+	lirc_t data;
+	unsigned int level,newlevel;
+	unsigned int timeout;
+
+	if(!MOD_IN_USE)
+		return;
+
+	if(!is_claimed)
+	{
+		return;
+	}
+
+	/* disable interrupt */
+	/*
+	  disable_irq(irq);
+	  out(LIRC_PORT_IRQ,in(LIRC_PORT_IRQ)&(~LP_PINTEN));
+	*/
+	if(in(1)&LP_PSELECD)
+	{
+		return;
+	}
+
+#ifdef LIRC_TIMER
+	if(init)
+	{
+		do_gettimeofday(&tv);
+		
+	        signal=tv.tv_sec-lasttv.tv_sec;
+		if(signal>15)
+		{
+			data=PULSE_MASK;  /* really long time */
+		}
+		else
+		{
+			data=(lirc_t) (signal*1000000+
+				       tv.tv_usec-lasttv.tv_usec+
+				       LIRC_SFH506_DELAY);
+		};
+
+		rbuf_write(data); /* space */
+	}
+	else
+	{
+		if(timer==0) /* wake up; we'll lose this signal 
+				but it will be garbage if the device 
+				is turned on anyway
+			      */
+		{
+			timer=init_lirc_timer();
+			/* enable_irq(irq); */
+			return;
+		}
+		init=1;
+	}
+
+	timeout=timer/10;           /* timeout after 1/10 sec. */
+	signal=1;
+	level=lirc_get_timer();
+	do{
+		newlevel=lirc_get_timer();
+		if(level==0 && newlevel!=0) signal++;
+		level=newlevel;
+
+		/* giving up */
+		if(signal>timeout || (in(1)&LP_PSELECD))
+		{
+			signal=0;
+			printk(KERN_NOTICE "%s: timeout\n",LIRC_DRIVER_NAME);
+			break;
+		}
+	}
+	while(lirc_get_signal());
+	if(signal!=0)
+	{
+		/* ajust value to usecs */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 0)
+		unsigned long long helper;
+		
+		helper = ((unsigned long long) signal)*1000000;
+		do_div(helper, timer);
+		signal = (long) helper;
+#else
+		signal=(long) ((((double) signal)*1000000)/timer);
+#endif
+
+		if(signal>LIRC_SFH506_DELAY)
+		{
+			data=signal-LIRC_SFH506_DELAY;
+		}
+		else
+		{
+			data=1;
+		}
+		rbuf_write(PULSE_BIT|data); /* pulse */
+	}
+	do_gettimeofday(&lasttv);
+#else
+	/* add your code here */
+#endif
+	
+	wake_up_interruptible(&lirc_wait);
+
+	/* enable interrupt */
+	/*
+	  enable_irq(irq);
+	  out(LIRC_PORT_IRQ,in(LIRC_PORT_IRQ)|LP_PINTEN);
+	*/
+}
+
+/***********************************************************************
+ **************************   file_operations   ************************
+ ***********************************************************************/
+
+static loff_t lirc_lseek(struct file *filep,loff_t offset,int orig)
+{
+	return(-ESPIPE);
+}
+
+static ssize_t lirc_read(struct file *filep,char *buf,size_t n,loff_t *ppos)
+{
+	int result;
+	int count=0;
+	DECLARE_WAITQUEUE(wait, current);
+	
+	if(n%sizeof(lirc_t)) return(-EINVAL);
+	
+	result=verify_area(VERIFY_WRITE,buf,n);
+	if(result) return(result);
+	
+	add_wait_queue(&lirc_wait,&wait);
+	current->state=TASK_INTERRUPTIBLE;
+	while(count<n)
+	{
+		if(rptr!=wptr)
+		{
+			copy_to_user(buf+count,(char *) &rbuf[rptr],
+				     sizeof(lirc_t));
+			rptr=(rptr+1)&(RBUF_SIZE-1);
+			count+=sizeof(lirc_t);
+		}
+		else
+		{
+			if(filep->f_flags & O_NONBLOCK)
+			{
+				result=-EAGAIN;
+				break;
+			}
+			if (signal_pending(current))
+			{
+				result=-ERESTARTSYS;
+				break;
+			}
+			schedule();
+			current->state=TASK_INTERRUPTIBLE;
+		}
+	}
+	remove_wait_queue(&lirc_wait,&wait);
+	current->state=TASK_RUNNING;
+	return(count ? count:result);
+}
+
+static ssize_t lirc_write(struct file *filep,const char *buf,size_t n,
+			  loff_t *ppos)
+{
+	int result,count;
+	unsigned int i;
+	unsigned int level,newlevel;
+	unsigned long flags;
+	lirc_t counttimer;
+	
+	if(!is_claimed)
+	{
+		return(-EBUSY);
+	}
+	if(n%sizeof(lirc_t)) return(-EINVAL);
+	result=verify_area(VERIFY_READ,buf,n);
+	if(result) return(result);
+	
+	count=n/sizeof(lirc_t);
+	
+	if(count>WBUF_SIZE || count%2==0) return(-EINVAL);
+	
+	copy_from_user(wbuf,buf,n);
+	
+#ifdef LIRC_TIMER
+	if(timer==0) /* try again if device is ready */
+	{
+		timer=init_lirc_timer();
+		if(timer==0) return(-EIO);
+	}
+
+	/* ajust values from usecs */
+	for(i=0;i<count;i++)
+	{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 0)
+		unsigned long long helper;
+		
+		helper = ((unsigned long long) wbuf[i])*timer;
+		do_div(helper, 1000000);
+		wbuf[i] = (lirc_t) helper;
+#else
+		wbuf[i]=(lirc_t) (((double) wbuf[i])*timer/1000000);
+#endif
+	}
+	
+	local_irq_save(flags);
+	i=0;
+	while(i<count)
+	{
+		level=lirc_get_timer();
+		counttimer=0;
+		lirc_on();
+		do
+		{
+			newlevel=lirc_get_timer();
+			if(level==0 && newlevel!=0) counttimer++;
+			level=newlevel;
+			if(in(1)&LP_PSELECD)
+			{
+				lirc_off();
+				local_irq_restore(flags);
+				return(-EIO);
+			}
+		}
+		while(counttimer<wbuf[i]);i++;
+		
+		lirc_off();
+		if(i==count) break;
+		counttimer=0;
+		do
+		{
+			newlevel=lirc_get_timer();
+			if(level==0 && newlevel!=0) counttimer++;
+			level=newlevel;
+			if(in(1)&LP_PSELECD)
+			{
+				local_irq_restore(flags);
+				return(-EIO);
+			}
+		}
+		while(counttimer<wbuf[i]);i++;
+	}
+	local_irq_restore(flags);
+#else
+	/* 
+	   place code that handles write
+	   without extarnal timer here
+	*/
+#endif
+	return(n);
+}
+
+static unsigned int lirc_poll(struct file *file, poll_table * wait)
+{
+	poll_wait(file, &lirc_wait,wait);
+	if (rptr!=wptr)
+		return(POLLIN|POLLRDNORM);
+	return(0);
+}
+
+static int lirc_ioctl(struct inode *node,struct file *filep,unsigned int cmd,
+		      unsigned long arg)
+{
+        int result;
+	unsigned long features=LIRC_CAN_SEND_PULSE|LIRC_CAN_REC_MODE2,mode;
+	
+	switch(cmd)
+	{
+	case LIRC_GET_FEATURES:
+		result=put_user(features,(unsigned long *) arg);
+		if(result) return(result); 
+		break;
+	case LIRC_GET_SEND_MODE:
+		result=put_user(LIRC_MODE_PULSE,(unsigned long *) arg);
+		if(result) return(result); 
+		break;
+	case LIRC_GET_REC_MODE:
+		result=put_user(LIRC_MODE_MODE2,(unsigned long *) arg);
+		if(result) return(result); 
+		break;
+	case LIRC_SET_SEND_MODE:
+		result=get_user(mode,(unsigned long *) arg);
+		if(result) return(result);
+		if(mode!=LIRC_MODE_PULSE) return(-EINVAL);
+		break;
+	case LIRC_SET_REC_MODE:
+		result=get_user(mode,(unsigned long *) arg);
+		if(result) return(result);
+		if(mode!=LIRC_MODE_MODE2) return(-ENOSYS);
+		break;
+	default:
+		return(-ENOIOCTLCMD);
+	}
+	return(0);
+}
+
+static int lirc_open(struct inode* node,struct file* filep)
+{
+	if(MOD_IN_USE)
+	{
+		return(-EBUSY);
+	}
+	if(!lirc_claim())
+	{
+		return(-EBUSY);
+	}
+	pport->ops->enable_irq(pport);
+
+	/* init read ptr */
+	rptr=wptr=0;
+	lost_irqs=0;
+
+	MOD_INC_USE_COUNT;
+	is_open=1;
+	return(0);
+}
+
+static int lirc_close(struct inode* node,struct file* filep)
+{
+	if(is_claimed)
+	{
+		is_claimed=0;
+		parport_release(ppdevice);
+	}
+	is_open=0;
+	MOD_DEC_USE_COUNT;
+	return(0);
+}
+
+static struct file_operations lirc_fops = 
+{
+	llseek:  lirc_lseek,
+	read:    lirc_read,
+	write:   lirc_write,
+	poll:    lirc_poll,
+	ioctl:   lirc_ioctl,
+	open:    lirc_open,
+	release: lirc_close
+};
+
+static int set_use_inc(void* data)
+{
+#if WE_DONT_USE_LOCAL_OPEN_CLOSE
+	MOD_INC_USE_COUNT;
+#endif
+	return 0;
+}
+
+static void set_use_dec(void* data)
+{
+#if WE_DONT_USE_LOCAL_OPEN_CLOSE
+	MOD_DEC_USE_COUNT;
+#endif
+}
+
+static struct lirc_plugin plugin = {
+       name:           LIRC_DRIVER_NAME,
+       minor:          -1,
+       code_length:    1,
+       sample_rate:    0,
+       data:           NULL,
+       add_to_buf:     NULL,
+       get_queue:      NULL,
+       set_use_inc:    set_use_inc,
+       set_use_dec:    set_use_dec,
+       fops:           &lirc_fops,
+};
+
+#ifdef MODULE
+
+static int pf(void *handle);
+static void kf(void *handle);
+
+static struct timer_list poll_timer;
+static void poll_state(unsigned long ignored);
+
+static void poll_state(unsigned long ignored)
+{
+	printk(KERN_NOTICE "%s: time\n",
+	       LIRC_DRIVER_NAME);
+	del_timer(&poll_timer);
+	if(is_claimed)
+		return;
+	kf(NULL);
+	if(!is_claimed)
+	{
+		printk(KERN_NOTICE "%s: could not claim port, giving up\n",
+		       LIRC_DRIVER_NAME);
+		init_timer(&poll_timer);
+		poll_timer.expires=jiffies+HZ;
+		poll_timer.data=(unsigned long) current;
+		poll_timer.function=poll_state;
+		add_timer(&poll_timer);
+	}
+}
+
+static int pf(void *handle)
+{
+	pport->ops->disable_irq(pport);
+	is_claimed=0;
+	return(0);
+}
+
+static void kf(void *handle)
+{
+	if(!is_open)
+		return;
+	if(!lirc_claim())
+		return;
+	pport->ops->enable_irq(pport);
+	/* this is a bit annoying when you actually print...*/
+	/*
+	printk(KERN_INFO "%s: reclaimed port\n",LIRC_DRIVER_NAME);
+	*/
+}
+
+/***********************************************************************
+ ******************   init_module()/cleanup_module()  ******************
+ ***********************************************************************/
+
+int init_module(void)
+{
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 3)
+	pport=parport_find_base(io);
+#else
+	pport=parport_enumerate();
+	while(pport!=NULL)
+	{
+		if(pport->base==io)
+		{
+			break;
+		}
+		pport=pport->next;
+	}
+#endif
+	if(pport==NULL)
+	{
+		printk(KERN_NOTICE "%s: no port at %x found\n",
+		       LIRC_DRIVER_NAME,io);
+		return(-ENXIO);
+	}
+	ppdevice=parport_register_device(pport,LIRC_DRIVER_NAME,
+					 pf,kf,irq_handler,0,NULL);
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 3)
+	parport_put_port(pport);
+#endif
+	if(ppdevice==NULL)
+	{
+		printk(KERN_NOTICE "%s: parport_register_device() failed\n",
+		       LIRC_DRIVER_NAME);
+		return(-ENXIO);
+	}
+	if(parport_claim(ppdevice)!=0)
+		goto skip_init;
+	is_claimed=1;
+	out(LIRC_LP_CONTROL,LP_PSELECP|LP_PINITP);
+
+#ifdef LIRC_TIMER
+	if(debug) 
+	{
+		out(LIRC_PORT_DATA,LIRC_PORT_DATA_BIT);
+	}
+	
+	timer=init_lirc_timer();
+
+#       if 0 	/* continue even if device is offline */
+	if(timer==0) 
+	{
+		is_claimed=0;
+		parport_release(pport);
+		parport_unregister_device(ppdevice);
+		return(-EIO);
+	}
+	
+#       endif
+	if(debug)
+	{
+		out(LIRC_PORT_DATA,0);
+	}
+#endif 
+
+	is_claimed=0;
+	parport_release(ppdevice);
+ skip_init:
+	if ((plugin.minor = lirc_register_plugin(&plugin)) < 0)
+	{
+		printk(KERN_NOTICE "%s: register_chrdev() failed\n",LIRC_DRIVER_NAME);
+		parport_unregister_device(ppdevice);
+		return(-EIO);
+	}
+	printk(KERN_INFO "%s: installed using port 0x%04x irq %d\n",LIRC_DRIVER_NAME,io,irq);
+	return(0);
+}
+  
+void cleanup_module(void)
+{
+	if(MOD_IN_USE) return;
+	parport_unregister_device(ppdevice);
+	lirc_unregister_plugin(plugin.minor);
+}
+
+MODULE_DESCRIPTION("Infrared receiver driver for parallel ports.");
+MODULE_AUTHOR("Christoph Bartelmus");
+MODULE_LICENSE("GPL");
+
+module_param(io, int, 0444);
+MODULE_PARM_DESC(io, "I/O address base (0x3bc, 0x378 or 0x278)");
+
+module_param(irq, int, 0444);
+MODULE_PARM_DESC(irq, "Interrupt (7 or 5)");
+
+module_param(debug, bool, 0644);
+MODULE_PARM_DESC(debug, "Enable debugging messages");
+
+EXPORT_NO_SYMBOLS;
+
+#endif /* MODULE */
diff -urN linux-2.6.10-rc3-mm1/drivers/char/lirc/lirc_parallel.h linux-2.6.10-rc3-no2/drivers/char/lirc/lirc_parallel.h
--- linux-2.6.10-rc3-mm1/drivers/char/lirc/lirc_parallel.h	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.10-rc3-no2/drivers/char/lirc/lirc_parallel.h	2004-12-22 19:35:16.580728825 -0500
@@ -0,0 +1,24 @@
+/*      $Id: lirc_parallel.h,v 5.1 1999/07/21 18:23:37 columbus Exp $      */
+
+#ifndef _LIRC_PARALLEL_H
+#define _LIRC_PARALLEL_H
+
+#include <linux/lp.h>
+
+#define LIRC_PORT_LEN 3
+
+#define LIRC_LP_BASE    0
+#define LIRC_LP_STATUS  1
+#define LIRC_LP_CONTROL 2
+
+#define LIRC_PORT_DATA           LIRC_LP_BASE    /* base */
+#define LIRC_PORT_DATA_BIT               0x01    /* 1st bit */
+#define LIRC_PORT_TIMER        LIRC_LP_STATUS    /* status port */
+#define LIRC_PORT_TIMER_BIT          LP_PBUSY    /* busy signal */
+#define LIRC_PORT_SIGNAL       LIRC_LP_STATUS    /* status port */
+#define LIRC_PORT_SIGNAL_BIT          LP_PACK    /* ack signal */
+#define LIRC_PORT_IRQ         LIRC_LP_CONTROL    /* control port */
+
+#define LIRC_SFH506_DELAY 0             /* delay t_phl in usecs */
+
+#endif
diff -urN linux-2.6.10-rc3-mm1/drivers/char/lirc/lirc_sasem.c linux-2.6.10-rc3-no2/drivers/char/lirc/lirc_sasem.c
--- linux-2.6.10-rc3-mm1/drivers/char/lirc/lirc_sasem.c	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.10-rc3-no2/drivers/char/lirc/lirc_sasem.c	2004-12-22 19:35:16.586727991 -0500
@@ -0,0 +1,463 @@
+/* lirc_sasem.c - USB remote support for LIRC
+ * Version 0.1  [beta status]
+ *
+ * Copyright (C) 2004 Oliver Stabel <oliver.stabel@gmx.de>
+ *
+ * This driver was derived from:
+ *   Paul Miller <pmiller9@users.sourceforge.net>'s 2003-2004
+ *      "lirc_atiusb - USB remote support for LIRC"
+ *   Culver Consulting Services <henry@culcon.com>'s 2003
+ *      "Sasem OnAir VFD/IR USB driver"
+ *
+ *
+ * 2004/06/13	-	1st version
+ *
+ * TODO
+ *	- keypresses seem to be rather sluggish sometimes; check
+ *	  intervall and timing
+ *	- simulate USBLCD device to work with LCDProc
+ *	- include fs operations
+ *	- analyse LCD command set
+ *	- check USB Minor allocation
+ *	- param to enable/disable LIRC communication (??)
+ *
+ */
+
+/*
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/version.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0)
+#error "**********************************************************"
+#error " Sorry, this driver is not yet available for 2.6 kernels. "
+#error "**********************************************************"
+#endif
+
+#include <linux/module.h>
+#include <linux/kmod.h>
+#include <linux/kernel.h>
+#include <linux/usb.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <asm/uaccess.h>
+#include <asm/atomic.h>
+#include <linux/poll.h>
+#include "lirc_sasem.h"
+
+#include "linux/kcompat.h"
+#include "linux/lirc.h"
+#include "drivers/lirc_dev/lirc_dev.h"
+
+static int debug = 0;
+
+static t_usb_device_id s_sasemID [] = {
+	{ USB_DEVICE(0x11ba, 0x0101) },
+	{ }
+};
+
+static t_usb_driver s_SasemDriver =
+{
+	owner:			THIS_MODULE,
+	name:			"Sasem",
+	probe:			s_sasemProbe,
+	disconnect:		s_sasemDisconnect,
+	minor:			SASEM_MINOR,
+	id_table:		s_sasemID,
+};
+
+static int __init s_sasemInit (void)
+{
+	printk(BANNER);
+
+	if (usb_register(&s_SasemDriver)) {
+		printk("USB registration failed");
+		return -ENOSYS;
+	}
+
+	return 0;
+}
+
+static void __exit s_sasemExit (void)
+{
+	usb_deregister (&s_SasemDriver);
+}
+
+module_init (s_sasemInit);
+module_exit (s_sasemExit);
+
+static void * s_sasemProbe(t_usb_device *p_dev, unsigned p_iInterfaceNum,
+			   const t_usb_device_id *p_id)
+{
+	t_sasemDevice *l_sasemDevice = NULL;
+	t_usb_endpoint_descriptor *l_endpoint;
+	t_usb_interface_descriptor *l_currentInterfaceDescriptor;
+	int l_iPipe;
+	int l_iDevnum;
+	t_lirc_plugin *l_lircPlugin = NULL;
+	t_lirc_buffer *l_lircBuffer = NULL;
+	int l_iLircMinor = -1;
+	int l_iMemFailure;
+	char l_cBuf[63], l_cName[128]="";
+
+	if (debug) printk("onair probe\n");
+	
+	l_currentInterfaceDescriptor = p_dev->actconfig->interface->
+		altsetting;
+	l_endpoint = l_currentInterfaceDescriptor->endpoint;
+	
+	if (!(l_endpoint->bEndpointAddress & 0x80) ||
+		((l_endpoint->bmAttributes & 3) != 0x03)) {
+		printk("OnAir config endpoint error");
+		return NULL;
+	}
+
+	l_iDevnum = p_dev->devnum;
+
+	l_iMemFailure = 0;
+	if (!(l_sasemDevice = kmalloc(sizeof(t_sasemDevice), GFP_KERNEL))) {
+		printk("kmalloc(sizeof(t_sasemDevice), GFP_KERNEL)) failed");
+		l_iMemFailure = 1;
+	}
+	else {
+		memset(l_sasemDevice, 0, sizeof(t_sasemDevice));
+		if (!(l_lircPlugin = kmalloc(sizeof(t_lirc_plugin), GFP_KERNEL))) {
+			printk("kmalloc(sizeof(t_lirc_plugin), GFP_KERNEL)) failed");
+			l_iMemFailure = 2;
+		}
+		else if (!(l_lircBuffer = kmalloc(sizeof(t_lirc_buffer), GFP_KERNEL))) {
+			printk("kmalloc(sizeof(t_lirc_buffer), GFP_KERNEL)) failed");
+			l_iMemFailure = 3;
+		}
+		else if (lirc_buffer_init(l_lircBuffer, MAX_INTERRUPT_DATA, 4)) {
+			printk("lirc_buffer_init failed");
+			l_iMemFailure = 4;
+		}
+		else if (!(l_sasemDevice->m_urbIn = usb_alloc_urb(0))) {
+			printk("usb_alloc_urb(0) failed");
+			l_iMemFailure = 5;
+		} else {
+
+			memset(l_lircPlugin, 0, sizeof(t_lirc_plugin));
+			strcpy(l_lircPlugin->name, DRIVER_NAME " ");
+			l_lircPlugin->minor = -1;
+			l_lircPlugin->code_length = MAX_INTERRUPT_DATA*8;
+			l_lircPlugin->features = LIRC_CAN_REC_LIRCCODE;
+			l_lircPlugin->data = l_sasemDevice;
+			
+			l_lircPlugin->rbuf = l_lircBuffer;
+			l_lircPlugin->set_use_inc = &s_lirc_set_use_inc;
+			l_lircPlugin->set_use_dec = &s_lirc_set_use_dec;
+
+			if ((l_iLircMinor = lirc_register_plugin(l_lircPlugin)) < 0) {
+				printk("lirc_register_plugin(l_lircPlugin)) failed");
+				l_iMemFailure = 9;
+			}
+		}
+	}
+	switch (l_iMemFailure) {
+	case 9:
+		usb_free_urb(l_sasemDevice->m_urbIn);
+	case 5:
+	case 4:
+		kfree(l_lircBuffer);
+	case 3:
+		kfree(l_lircPlugin);
+	case 2:
+		kfree(l_sasemDevice);
+	case 1:
+		return NULL;
+	}
+	
+	l_lircPlugin->minor = l_iLircMinor;	
+	
+	init_MUTEX(&l_sasemDevice->m_semLock);
+	down_interruptible(&l_sasemDevice->m_semLock);
+	l_sasemDevice->m_descriptorIn = l_endpoint;
+	l_sasemDevice->m_device = p_dev;
+	l_sasemDevice->m_lircPlugin = l_lircPlugin;
+	up(&l_sasemDevice->m_semLock);
+
+	l_iPipe = usb_rcvintpipe(l_sasemDevice->m_device,
+				 l_sasemDevice->m_descriptorIn->
+				 bEndpointAddress);
+	
+	usb_fill_int_urb(l_sasemDevice->m_urbIn, l_sasemDevice->m_device,
+			 l_iPipe, l_sasemDevice->m_cBufferIn,
+			 sizeof(l_sasemDevice->m_cBufferIn),
+			 s_sasemCallbackIn, l_sasemDevice, 
+			 l_sasemDevice->m_descriptorIn->bInterval);
+
+	if (p_dev->descriptor.iManufacturer &&
+	    usb_string(p_dev, p_dev->descriptor.iManufacturer, l_cBuf, 63) > 0)
+	{
+		strncpy(l_cName, l_cBuf, 128);
+	}
+	if (p_dev->descriptor.iProduct &&
+	    usb_string(p_dev, p_dev->descriptor.iProduct, l_cBuf, 63) > 0)
+	{
+		snprintf(l_cName, 128, "%s %s", l_cName, l_cBuf);
+	}
+	printk(DRIVER_NAME "[%d]: %s on usb%d\n", l_iDevnum, l_cName,
+	       p_dev->bus->busnum);
+
+	return l_sasemDevice;
+}
+
+
+static void s_sasemDisconnect(t_usb_device *p_dev, void *p_ptr) {
+	t_sasemDevice *l_sasemDevice = p_ptr;
+	if (debug) printk("s_sasemDisconnect\n");
+
+	down_interruptible(&l_sasemDevice->m_semLock);
+	usb_unlink_urb(l_sasemDevice->m_urbIn);
+	usb_free_urb(l_sasemDevice->m_urbIn);
+	s_unregister_from_lirc(l_sasemDevice);
+	up(&l_sasemDevice->m_semLock);
+	kfree (l_sasemDevice);
+}
+
+static void s_sasemCallbackIn(t_urb *p_urb)
+{
+	t_sasemDevice *l_sasemDevice;
+	int l_iDevnum;
+	int l_iLen;
+	char l_cBuf[MAX_INTERRUPT_DATA];
+	int i;
+
+	if (debug) printk("s_sasemCallbackIn\n");
+	
+	if (!p_urb)
+	{
+		return;
+	}
+
+	if (!(l_sasemDevice = p_urb->context)) {
+		usb_unlink_urb(p_urb);
+		return;
+	}
+
+	l_iDevnum = l_sasemDevice->m_iDevnum;
+	if (debug) {
+		printk(DRIVER_NAME "[%d]: data received (length %d)\n",
+		       l_iDevnum, p_urb->actual_length);
+		printk(DRIVER_NAME 
+		       " intr_callback called %x %x %x %x %x %x %x %x\n", 
+		       l_sasemDevice->m_cBufferIn[0],
+		       l_sasemDevice->m_cBufferIn[1],
+		       l_sasemDevice->m_cBufferIn[2],
+		       l_sasemDevice->m_cBufferIn[3],
+		       l_sasemDevice->m_cBufferIn[4],
+		       l_sasemDevice->m_cBufferIn[5],
+		       l_sasemDevice->m_cBufferIn[6],
+		       l_sasemDevice->m_cBufferIn[7]);
+	}
+
+	switch (p_urb->status) {
+
+	/* success */
+	case 0:
+		l_iLen = p_urb->actual_length;
+		if (l_iLen > MAX_INTERRUPT_DATA) return;
+
+		memcpy(l_cBuf,p_urb->transfer_buffer,l_iLen);
+
+		// is this needed? The OnAir device should always
+		// return 8 bytes
+		for (i = l_iLen; i < MAX_INTERRUPT_DATA; i++) l_cBuf[i] = 0;
+
+		// the OnAir device seems not to be able to signal a
+		// pressed button by repeating its code. Keeping a
+		// button pressed first sends the real code (e.g. 0C
+		// 80 7F 41 BE 00 00 00) and then keeps sending 08 00
+		// 00 00 00 00 00 00 as long as the button is pressed
+		// (notice that in the real key code 80 = !7F and 41 =
+		// !BE is this important? maybe for validation?) maybe
+		// 08 00 00 00 00 00 00 00 is the number of presses?
+		// who knows ...
+		// so lets do the following: if a code != the 08 code
+		// arrives, store it to repeat it if necessary for
+		// LIRC. If an 08 code follows afterwards, send the
+		// old code again to the buffer do this as long as the
+		// 08 code is being sent
+		// example:
+		// Code from Remote          Lirc Buffer
+		//  0C 80 7F 41 BE 00 00 00   0C 80 7F 41 BE 00 00 00
+		//  08 00 00 00 00 00 00 00   0C 80 7F 41 BE 00 00 00
+		//  08 00 00 00 00 00 00 00   0C 80 7F 41 BE 00 00 00
+		//  08 00 00 00 00 00 00 00   0C 80 7F 41 BE 00 00 00
+		//  08 00 00 00 00 00 00 00   0C 80 7F 41 BE 00 00 00
+		//  0C 80 7F 40 BF 00 00 00   0C 80 7F 40 BF 00 00 00
+		//  08 00 00 00 00 00 00 00   0C 80 7F 40 BF 00 00 00
+		//  08 00 00 00 00 00 00 00   0C 80 7F 40 BF 00 00 00
+		//  0C 80 7F 41 BE 00 00 00   0C 80 7F 41 BE 00 00 00
+		
+		if (memcmp(l_cBuf, sc_cSasemCode, MAX_INTERRUPT_DATA) == 0) {
+			// the repeat code is being sent, so we copy
+			// the old code to LIRC
+			if (l_sasemDevice->m_iCodeSaved != 0) {
+				memcpy(l_cBuf, &l_sasemDevice->m_cLastCode,
+				       MAX_INTERRUPT_DATA);
+			}
+			// there was no old code so what to do?
+			else {
+				// TODO
+			}
+		}
+		else
+		{
+			// save the current valid code for repeats
+			memcpy(&l_sasemDevice->m_cLastCode, l_cBuf,
+			       MAX_INTERRUPT_DATA);
+			// set flag to signal a valid code was save;
+			// just for safety reasons
+			l_sasemDevice->m_iCodeSaved = 1;
+		}
+		
+		/* copy 1 code to lirc_buffer */
+		lirc_buffer_write_1(l_sasemDevice->m_lircPlugin->rbuf,
+				    l_cBuf);
+		wake_up(&l_sasemDevice->m_lircPlugin->rbuf->wait_poll);
+		break;
+
+	/* unlink */
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		usb_unlink_urb(p_urb);
+		return;
+	}
+
+	/* resubmit urb */
+	usb_submit_urb(p_urb);
+}
+
+static int s_unregister_from_lirc(t_sasemDevice *p_sasemDevice) {
+	t_lirc_plugin *l_lircPlugin = p_sasemDevice->m_lircPlugin;
+	int l_iDevnum;
+	int l_iReturn;
+
+	l_iDevnum = p_sasemDevice->m_iDevnum;
+	if (debug) printk(DRIVER_NAME "[%d]: unregister from lirc called\n",
+			  l_iDevnum);
+	
+	if ((l_iReturn = lirc_unregister_plugin(l_lircPlugin->minor)) > 0) {
+		printk(DRIVER_NAME "[%d]: error in lirc_unregister minor: %d\n"
+		       "Trying again...\n", l_iDevnum, l_lircPlugin->minor);
+		if (l_iReturn == -EBUSY) {
+			printk(DRIVER_NAME "[%d]: device is opened, "
+			       "will unregister on close\n", l_iDevnum);
+			return -EAGAIN;
+		}
+		set_current_state(TASK_INTERRUPTIBLE);
+		schedule_timeout(HZ);
+
+		if ((l_iReturn = lirc_unregister_plugin(l_lircPlugin->minor)) > 0) {
+			printk(DRIVER_NAME "[%d]: lirc_unregister failed\n",
+			       l_iDevnum);
+		}
+	}
+	
+	if (l_iReturn != 0) {
+		printk(DRIVER_NAME "[%d]: didn't free resources\n",
+		       l_iDevnum);
+		return -EAGAIN;
+	}
+	
+	printk(DRIVER_NAME "[%d]: usb remote disconnected\n", l_iDevnum);
+	
+	lirc_buffer_free(l_lircPlugin->rbuf);
+	kfree(l_lircPlugin->rbuf);
+	kfree(l_lircPlugin);
+	return 0;
+}
+
+static int s_lirc_set_use_inc(void *p_data)
+{
+	t_sasemDevice *l_sasemDevice = p_data;
+	int l_iDevnum;
+
+	if (!l_sasemDevice) {
+		printk(DRIVER_NAME "[?]: s_lirc_set_use_inc called with no context\n");
+		return -EIO;
+	}
+	
+	l_iDevnum = l_sasemDevice->m_iDevnum;
+	if (debug) printk(DRIVER_NAME "[%d]: s_lirc_set_use_inc\n", 
+			  l_iDevnum);
+
+	if (!l_sasemDevice->m_iConnected) {
+		
+		/*
+			this is the trigger from LIRC to start
+			transfering data so the URB is being submitted
+		*/
+
+		if (!l_sasemDevice->m_device)
+			return -ENOENT;
+		
+		/* set USB device in URB */
+		l_sasemDevice->m_urbIn->dev = l_sasemDevice->m_device;
+		
+		/* start communication by submitting URB */
+		if (usb_submit_urb(l_sasemDevice->m_urbIn)) {
+			printk(DRIVER_NAME "[%d]: open result = -EIO error "
+				"submitting urb\n", l_iDevnum);
+			return -EIO;
+		}
+		
+		/* indicate that URB has been submitted */
+		l_sasemDevice->m_iConnected = 1;
+	}
+
+	return 0;
+}
+
+static void s_lirc_set_use_dec(void *p_data) {
+	t_sasemDevice *l_sasemDevice = p_data;
+	int l_iDevnum;
+	
+	if (!l_sasemDevice) {
+		printk(DRIVER_NAME "[?]: s_lirc_set_use_dec called with no context\n");
+		return;
+	}
+
+	l_iDevnum = l_sasemDevice->m_iDevnum;
+	if (debug) printk(DRIVER_NAME "[%d]: s_lirc_set_use_dec\n", 
+			  l_iDevnum);
+
+	if (l_sasemDevice->m_iConnected) {
+
+		/*
+			URB has been submitted before so it can be unlinked
+		*/
+
+		down_interruptible(&l_sasemDevice->m_semLock);
+		usb_unlink_urb(l_sasemDevice->m_urbIn);
+		l_sasemDevice->m_iConnected = 0;
+		up(&l_sasemDevice->m_semLock);
+	}
+}
+
+MODULE_DESCRIPTION("Infrared receiver driver for Dign HV5 HTPC and Sasem OnAir Remocon-V");
+MODULE_AUTHOR("Oliver Stabel <oliver.stabel@gmx.de>");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE (usb, s_sasemID);
+
+module_param(debug, bool, 0644);
+MODULE_PARM_DESC(debug, "Enable debugging messages");
+
+EXPORT_NO_SYMBOLS;
diff -urN linux-2.6.10-rc3-mm1/drivers/char/lirc/lirc_sasem.h linux-2.6.10-rc3-no2/drivers/char/lirc/lirc_sasem.h
--- linux-2.6.10-rc3-mm1/drivers/char/lirc/lirc_sasem.h	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.10-rc3-no2/drivers/char/lirc/lirc_sasem.h	2004-12-22 19:35:16.589727575 -0500
@@ -0,0 +1,70 @@
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION 		"v0.1"
+#define DATE 			"June 2004"
+#define DRIVER_AUTHOR 		"Oliver Stabel <oliver.stabel@gmx.de>"
+#define DRIVER_DESC 		"USB Driver for Sasem Remote Controller V1.1"
+#define DRIVER_SHORTDESC 	"Sasem"
+#define DRIVER_NAME		"lirc_sasem"
+
+#define BANNER \
+  KERN_INFO DRIVER_SHORTDESC " " DRIVER_VERSION " (" DATE ")\n" \
+  KERN_INFO "   by " DRIVER_AUTHOR "\n"
+
+static const char longbanner[] = {
+	DRIVER_DESC ", " DRIVER_VERSION " (" DATE "), by " DRIVER_AUTHOR
+};
+
+#define MAX_INTERRUPT_DATA 8
+#define SASEM_MINOR 144
+
+static const char sc_cSasemCode[MAX_INTERRUPT_DATA] =
+	{ 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+typedef struct usb_driver t_usb_driver, *tp_usb_driver;
+typedef struct usb_device t_usb_device, *tp_usb_device;
+typedef struct usb_interface t_usb_interface, *tp_usb_interface;
+typedef struct usb_device_id t_usb_device_id, *tp_usb_device_id;
+typedef struct usb_host_interface t_usb_host_interface,
+	*tp_usb_host_interface;
+typedef struct usb_interface_descriptor t_usb_interface_descriptor,
+	*tp_usb_interface_descriptor;
+typedef struct usb_endpoint_descriptor t_usb_endpoint_descriptor,
+	*tp_usb_endpoint_descriptor;
+typedef struct urb t_urb, *tp_urb;
+
+typedef struct semaphore t_semaphore, *tp_semaphore;
+
+typedef struct lirc_plugin t_lirc_plugin, *tp_lirc_plugin;
+typedef struct lirc_buffer t_lirc_buffer;
+
+struct sasemDevice {
+	t_usb_device *m_device;
+	t_usb_endpoint_descriptor *m_descriptorIn;
+	t_usb_endpoint_descriptor *m_descriptorOut;
+	t_urb *m_urbIn;
+	t_urb *m_urbOut;
+	unsigned int m_iInterfaceNum;
+	int	m_iDevnum;
+	unsigned char m_cBufferIn[MAX_INTERRUPT_DATA];
+	t_semaphore m_semLock;
+
+	char m_cLastCode[MAX_INTERRUPT_DATA];
+	int m_iCodeSaved;
+	
+	/* lirc */
+	t_lirc_plugin *m_lircPlugin;
+	int m_iConnected;
+};
+
+typedef struct sasemDevice t_sasemDevice, *tp_sasemDevice;
+
+static void* s_sasemProbe(t_usb_device *p_dev, unsigned p_iInterfaceNum,
+			  const t_usb_device_id *p_id);
+static void s_sasemDisconnect(t_usb_device *p_dev, void *p_ptr);
+static void s_sasemCallbackIn(t_urb *p_urb);
+
+static int s_unregister_from_lirc(t_sasemDevice *p_sasemDevice);
+static int s_lirc_set_use_inc(void *p_data);
+static void s_lirc_set_use_dec(void *p_data);
diff -urN linux-2.6.10-rc3-mm1/drivers/char/lirc/lirc_serial.c linux-2.6.10-rc3-no2/drivers/char/lirc/lirc_serial.c
--- linux-2.6.10-rc3-mm1/drivers/char/lirc/lirc_serial.c	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.10-rc3-no2/drivers/char/lirc/lirc_serial.c	2004-12-22 19:35:16.593727019 -0500
@@ -0,0 +1,1093 @@
+/*      $Id: lirc_serial.c,v 5.58 2004/12/01 18:27:36 lirc Exp $      */
+
+/****************************************************************************
+ ** lirc_serial.c ***********************************************************
+ ****************************************************************************
+ *
+ * lirc_serial - Device driver that records pulse- and pause-lengths
+ *               (space-lengths) between DDCD event on a serial port.
+ *
+ * Copyright (C) 1996,97 Ralph Metzler <rjkm@thp.uni-koeln.de>
+ * Copyright (C) 1998 Trent Piepho <xyzzy@u.washington.edu>
+ * Copyright (C) 1998 Ben Pfaff <blp@gnu.org>
+ * Copyright (C) 1999 Christoph Bartelmus <lirc@bartelmus.de>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+/* Steve's changes to improve transmission fidelity:
+     - for systems with the rdtsc instruction and the clock counter, a 
+       send_pule that times the pulses directly using the counter.
+       This means that the LIRC_SERIAL_TRANSMITTER_LATENCY fudge is
+       not needed. Measurement shows very stable waveform, even where
+       PCI activity slows the access to the UART, which trips up other
+       versions.
+     - For other system, non-integer-microsecond pulse/space lengths,
+       done using fixed point binary. So, much more accurate carrier
+       frequency.
+     - fine tuned transmitter latency, taking advantage of fractional
+       microseconds in previous change
+     - Fixed bug in the way transmitter latency was accounted for by
+       tuning the pulse lengths down - the send_pulse routine ignored
+       this overhead as it timed the overall pulse length - so the
+       pulse frequency was right but overall pulse length was too
+       long. Fixed by accounting for latency on each pulse/space
+       iteration.
+
+   Steve Davies <steve@daviesfam.org>  July 2001
+*/
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+ 
+#include <linux/version.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 2, 18)
+#error "**********************************************************"
+#error " Sorry, this driver needs kernel version 2.2.18 or higher "
+#error "**********************************************************"
+#endif
+
+#include <linux/config.h>
+
+#ifndef CONFIG_SERIAL_MODULE
+#warning "******************************************"
+#warning " Your serial port driver is compiled into "
+#warning " the kernel. You will have to release the "
+#warning " port you want to use for LIRC with:      "
+#warning "    setserial /dev/ttySx uart none        "
+#warning "******************************************"
+#endif
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/serial_reg.h>
+#include <linux/time.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+#include <linux/mm.h>
+#include <linux/delay.h>
+#include <linux/poll.h>
+
+#include <asm/system.h>
+#include <asm/segment.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/fcntl.h>
+
+#include "linux/lirc.h"
+#include "linux/kcompat.h"
+#include "drivers/lirc_dev/lirc_dev.h"
+
+#if defined(LIRC_SERIAL_SOFTCARRIER) && !defined(LIRC_SERIAL_TRANSMITTER)
+#warning "Software carrier only affects transmitting"
+#endif
+
+#if defined(rdtsc)
+
+#define USE_RDTSC
+#warning "Note: using rdtsc instruction"
+#endif
+
+#ifdef LIRC_SERIAL_ANIMAX
+#ifdef LIRC_SERIAL_TRANSMITTER
+#warning "******************************************"
+#warning " This receiver does not have a            "
+#warning " transmitter diode                        "
+#warning "******************************************"
+#endif
+#endif
+
+#define LIRC_DRIVER_NAME "lirc_serial"
+
+struct lirc_serial
+{
+	int type;
+	int signal_pin;
+	int signal_pin_change;
+	int on;
+	int off;
+	long (*send_pulse)(unsigned long length);
+	void (*send_space)(long length);
+	int features;
+};
+
+#define LIRC_HOMEBREW        0
+#define LIRC_IRDEO           1
+#define LIRC_IRDEO_REMOTE    2
+#define LIRC_ANIMAX          3
+#define LIRC_IGOR            4
+
+#ifdef LIRC_SERIAL_IRDEO
+static int type=LIRC_IRDEO;
+#elif defined(LIRC_SERIAL_IRDEO_REMOTE)
+static int type=LIRC_IRDEO_REMOTE;
+#elif defined(LIRC_SERIAL_ANIMAX)
+static int type=LIRC_ANIMAX;
+#elif defined(LIRC_SERIAL_IGOR)
+static int type=LIRC_IGOR;
+#else
+static int type=LIRC_HOMEBREW;
+#endif
+
+#ifdef LIRC_SERIAL_SOFTCARRIER
+static int softcarrier=1;
+#else
+static int softcarrier=0;
+#endif
+
+static int share_irq = 0;
+static int debug = 0;
+
+#define dprintk(fmt, args...)                                   \
+	do{                                                     \
+		if(debug)                                       \
+	                printk(KERN_DEBUG LIRC_DRIVER_NAME ": " \
+                               fmt, ## args);                   \
+	}while(0)
+
+/* forward declarations */
+static long send_pulse_irdeo(unsigned long length);
+static long send_pulse_homebrew(unsigned long length);
+static void send_space_irdeo(long length);
+static void send_space_homebrew(long length);
+
+static struct lirc_serial hardware[]=
+{
+	/* home-brew receiver/transmitter */
+	{
+		LIRC_HOMEBREW,
+		UART_MSR_DCD,
+		UART_MSR_DDCD,
+		UART_MCR_RTS|UART_MCR_OUT2|UART_MCR_DTR,
+		UART_MCR_RTS|UART_MCR_OUT2,
+		send_pulse_homebrew,
+		send_space_homebrew,
+		(
+#ifdef LIRC_SERIAL_TRANSMITTER
+		 LIRC_CAN_SET_SEND_DUTY_CYCLE|
+		 LIRC_CAN_SET_SEND_CARRIER|
+		 LIRC_CAN_SEND_PULSE|
+#endif
+		 LIRC_CAN_REC_MODE2)
+	},
+	
+	/* IRdeo classic */
+	{
+		LIRC_IRDEO,
+		UART_MSR_DSR,
+		UART_MSR_DDSR,
+		UART_MCR_OUT2,
+		UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2,
+		send_pulse_irdeo,
+		send_space_irdeo,
+		(LIRC_CAN_SET_SEND_DUTY_CYCLE|
+		 LIRC_CAN_SEND_PULSE|
+		 LIRC_CAN_REC_MODE2)
+	},
+	
+	/* IRdeo remote */
+	{
+		LIRC_IRDEO_REMOTE,
+		UART_MSR_DSR,
+		UART_MSR_DDSR,
+		UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2,
+		UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2,
+		send_pulse_irdeo,
+		send_space_irdeo,
+		(LIRC_CAN_SET_SEND_DUTY_CYCLE|
+		 LIRC_CAN_SEND_PULSE|
+		 LIRC_CAN_REC_MODE2)
+	},
+	
+	/* AnimaX */
+	{
+		LIRC_ANIMAX,
+		UART_MSR_DCD,
+		UART_MSR_DDCD,
+		0,
+		UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2,
+		NULL,
+		NULL,
+		LIRC_CAN_REC_MODE2
+	},
+	
+	/* home-brew receiver/transmitter (Igor Cesko's variation) */
+	{
+		LIRC_HOMEBREW,
+		UART_MSR_DSR,
+		UART_MSR_DDSR,
+		UART_MCR_RTS|UART_MCR_OUT2|UART_MCR_DTR,
+		UART_MCR_RTS|UART_MCR_OUT2,
+		send_pulse_homebrew,
+		send_space_homebrew,
+		(
+#ifdef LIRC_SERIAL_TRANSMITTER
+		 LIRC_CAN_SET_SEND_DUTY_CYCLE|
+		 LIRC_CAN_SET_SEND_CARRIER|
+		 LIRC_CAN_SEND_PULSE|
+#endif
+		 LIRC_CAN_REC_MODE2)
+	}
+	
+};
+
+#define RS_ISR_PASS_LIMIT 256
+
+/* A long pulse code from a remote might take upto 300 bytes.  The
+   daemon should read the bytes as soon as they are generated, so take
+   the number of keys you think you can push before the daemon runs
+   and multiply by 300.  The driver will warn you if you overrun this
+   buffer.  If you have a slow computer or non-busmastering IDE disks,
+   maybe you will need to increase this.  */
+
+/* This MUST be a power of two!  It has to be larger than 1 as well. */
+
+#define RBUF_LEN 256
+#define WBUF_LEN 256
+
+static int sense = -1;   /* -1 = auto, 0 = active high, 1 = active low */
+
+static spinlock_t lirc_lock = SPIN_LOCK_UNLOCKED;
+
+static int io = LIRC_PORT;
+static int irq = LIRC_IRQ;
+
+static struct timeval lasttv = {0, 0};
+
+static struct lirc_buffer rbuf;
+
+static lirc_t wbuf[WBUF_LEN];
+
+static unsigned int freq = 38000;
+static unsigned int duty_cycle = 50;
+
+/* Initialized in init_timing_params() */
+static unsigned long period = 0;
+static unsigned long pulse_width = 0;
+static unsigned long space_width = 0;
+
+#if defined(__i386__)
+/*
+  From:
+  Linux I/O port programming mini-HOWTO
+  Author: Riku Saikkonen <Riku.Saikkonen@hut.fi>
+  v, 28 December 1997
+  
+  [...]
+  Actually, a port I/O instruction on most ports in the 0-0x3ff range
+  takes almost exactly 1 microsecond, so if you're, for example, using
+  the parallel port directly, just do additional inb()s from that port
+  to delay.
+  [...]
+*/
+/* transmitter latency 1.5625us 0x1.90 - this figure arrived at from
+ * comment above plus trimming to match actual measured frequency.
+ * This will be sensitive to cpu speed, though hopefully most of the 1.5us
+ * is spent in the uart access.  Still - for reference test machine was a
+ * 1.13GHz Athlon system - Steve
+ */
+
+/* changed from 400 to 450 as this works better on slower machines;
+   faster machines will use the rdtsc code anyway */
+
+#define LIRC_SERIAL_TRANSMITTER_LATENCY 450
+
+#else
+
+/* does anybody have information on other platforms ? */
+/* 256 = 1<<8 */
+#define LIRC_SERIAL_TRANSMITTER_LATENCY 256
+
+#endif  /* __i386__ */
+
+static inline unsigned int sinp(int offset)
+{
+	return inb(io + offset);
+}
+
+static inline void soutp(int offset, int value)
+{
+	outb(value, io + offset);
+}
+
+static inline void on(void)
+{
+	soutp(UART_MCR,hardware[type].on);
+}
+  
+static inline void off(void)
+{
+	soutp(UART_MCR,hardware[type].off);
+}
+
+#ifndef MAX_UDELAY_MS
+#define MAX_UDELAY_US 5000
+#else
+#define MAX_UDELAY_US (MAX_UDELAY_MS*1000)
+#endif
+
+static inline void safe_udelay(unsigned long usecs)
+{
+	while(usecs>MAX_UDELAY_US)
+	{
+		udelay(MAX_UDELAY_US);
+		usecs-=MAX_UDELAY_US;
+	}
+	udelay(usecs);
+}
+
+#ifdef USE_RDTSC
+/* This is an overflow/precision juggle, complicated in that we can't
+   do long long divide in the kernel */
+
+/* When we use the rdtsc instruction to measure clocks, we keep the
+ * pulse and space widths as clock cycles.  As this is CPU speed
+ * dependent, the widths must be calculated in init_port and ioctl
+ * time
+ */
+
+/* So send_pulse can quickly convert microseconds to clocks */
+static unsigned long conv_us_to_clocks = 0;
+
+static inline int init_timing_params(unsigned int new_duty_cycle,
+		unsigned int new_freq)
+{
+	unsigned long long loops_per_sec,work;
+	
+	duty_cycle=new_duty_cycle;
+	freq=new_freq;
+
+	loops_per_sec=current_cpu_data.loops_per_jiffy;
+	loops_per_sec*=HZ;
+	
+	/* How many clocks in a microsecond?, avoiding long long divide */
+	work=loops_per_sec;
+	work*=4295;  /* 4295 = 2^32 / 1e6 */
+	conv_us_to_clocks=(work>>32);
+	
+	/* Carrier period in clocks, approach good up to 32GHz clock,
+           gets carrier frequency within 8Hz */
+	period=loops_per_sec>>3;
+	period/=(freq>>3);
+
+	/* Derive pulse and space from the period */
+
+	pulse_width = period*duty_cycle/100;
+	space_width = period - pulse_width;
+	dprintk("in init_timing_params, freq=%d, duty_cycle=%d, "
+		"clk/jiffy=%ld, pulse=%ld, space=%ld, "
+		"conv_us_to_clocks=%ld\n",
+		freq, duty_cycle, current_cpu_data.loops_per_jiffy,
+		pulse_width, space_width, conv_us_to_clocks);
+	return 0;
+}
+#else /* ! USE_RDTSC */
+static inline int init_timing_params(unsigned int new_duty_cycle,
+		unsigned int new_freq)
+{
+/* period, pulse/space width are kept with 8 binary places -
+ * IE multiplied by 256. */
+	if(256*1000000L/new_freq*new_duty_cycle/100<=
+	   LIRC_SERIAL_TRANSMITTER_LATENCY) return(-EINVAL);
+	if(256*1000000L/new_freq*(100-new_duty_cycle)/100<=
+	   LIRC_SERIAL_TRANSMITTER_LATENCY) return(-EINVAL);
+	duty_cycle=new_duty_cycle;
+	freq=new_freq;
+	period=256*1000000L/freq;
+	pulse_width=period*duty_cycle/100;
+	space_width=period-pulse_width;
+	dprintk("in init_timing_params, freq=%d pulse=%ld, "
+		"space=%ld\n", freq, pulse_width, space_width);
+	return 0;
+}
+#endif /* USE_RDTSC */
+
+
+/* return value: space length delta */
+
+static long send_pulse_irdeo(unsigned long length)
+{
+	long rawbits;
+	int i;
+	unsigned char output;
+	unsigned char chunk,shifted;
+	
+	/* how many bits have to be sent ? */
+	rawbits=length*1152/10000;
+	if(duty_cycle>50) chunk=3;
+	else chunk=1;
+	for(i=0,output=0x7f;rawbits>0;rawbits-=3)
+	{
+		shifted=chunk<<(i*3);
+		shifted>>=1;
+		output&=(~shifted);
+		i++;
+		if(i==3)
+		{
+			soutp(UART_TX,output);
+			while(!(sinp(UART_LSR) & UART_LSR_THRE));
+			output=0x7f;
+			i=0;
+		}
+	}
+	if(i!=0)
+	{
+		soutp(UART_TX,output);
+		while(!(sinp(UART_LSR) & UART_LSR_TEMT));
+	}
+
+	if(i==0)
+	{
+		return((-rawbits)*10000/1152);
+	}
+	else
+	{
+		return((3-i)*3*10000/1152+(-rawbits)*10000/1152);
+	}
+}
+
+#ifdef USE_RDTSC
+/* Version that uses Pentium rdtsc instruction to measure clocks */
+
+/* This version does sub-microsecond timing using rdtsc instruction,
+ * and does away with the fudged LIRC_SERIAL_TRANSMITTER_LATENCY
+ * Implicitly i586 architecture...  - Steve
+ */
+
+static inline long send_pulse_homebrew_softcarrier(unsigned long length)
+{
+	int flag;
+	unsigned long target, start, now;
+
+	/* Get going quick as we can */
+	rdtscl(start);on();
+	/* Convert length from microseconds to clocks */
+	length*=conv_us_to_clocks;
+	/* And loop till time is up - flipping at right intervals */
+	now=start;
+	target=pulse_width;
+	flag=1;
+	while((now-start)<length)
+	{
+		/* Delay till flip time */
+		do
+		{
+			rdtscl(now);
+		}
+		while ((now-start)<target);
+		/* flip */
+		if(flag)
+		{
+			rdtscl(now);off();
+			target+=space_width;
+		}
+		else
+		{
+			rdtscl(now);on();
+			target+=pulse_width;
+		}
+		flag=!flag;
+	}
+	rdtscl(now);
+	return(((now-start)-length)/conv_us_to_clocks);
+}
+#else /* ! USE_RDTSC */
+/* Version using udelay() */
+
+/* here we use fixed point arithmetic, with 8
+   fractional bits.  that gets us within 0.1% or so of the right average
+   frequency, albeit with some jitter in pulse length - Steve */
+
+/* To match 8 fractional bits used for pulse/space length */
+
+static inline long send_pulse_homebrew_softcarrier(unsigned long length)
+{
+	int flag;
+	unsigned long actual, target, d;
+	length<<=8;
+
+	actual=target=0; flag=0;
+	while(actual<length)
+	{
+		if(flag)
+		{
+			off();
+			target+=space_width;
+		}
+		else
+		{
+			on();
+			target+=pulse_width;
+		}
+		d=(target-actual-LIRC_SERIAL_TRANSMITTER_LATENCY+128)>>8;
+		/* Note - we've checked in ioctl that the pulse/space
+		   widths are big enough so that d is > 0 */
+		udelay(d);
+		actual+=(d<<8)+LIRC_SERIAL_TRANSMITTER_LATENCY;
+		flag=!flag;
+	}
+	return((actual-length)>>8);
+}
+#endif /* USE_RDTSC */
+
+static long send_pulse_homebrew(unsigned long length)
+{
+	if(length<=0) return 0;
+	if(softcarrier)
+	{
+		return send_pulse_homebrew_softcarrier(length);
+	}
+	else
+	{
+		on();
+		safe_udelay(length);
+		return(0);
+	}
+}
+
+static void send_space_irdeo(long length)
+{
+	if(length<=0) return;
+	safe_udelay(length);
+}
+
+static void send_space_homebrew(long length)
+{
+        off();
+	if(length<=0) return;
+	safe_udelay(length);
+}
+
+static void inline rbwrite(lirc_t l)
+{
+	if(lirc_buffer_full(&rbuf))    /* no new signals will be accepted */
+	{
+		dprintk("Buffer overrun\n");
+		return;
+	}
+	_lirc_buffer_write_1(&rbuf, (void *)&l);
+}
+
+static void inline frbwrite(lirc_t l)
+{
+	/* simple noise filter */
+	static lirc_t pulse=0L,space=0L;
+	static unsigned int ptr=0;
+	
+	if(ptr>0 && (l&PULSE_BIT))
+	{
+		pulse+=l&PULSE_MASK;
+		if(pulse>250)
+		{
+			rbwrite(space);
+			rbwrite(pulse|PULSE_BIT);
+			ptr=0;
+			pulse=0;
+		}
+		return;
+	}
+	if(!(l&PULSE_BIT))
+	{
+		if(ptr==0)
+		{
+			if(l>20000)
+			{
+				space=l;
+				ptr++;
+				return;
+			}
+		}
+		else
+		{
+			if(l>20000)
+			{
+				space+=pulse;
+				if(space>PULSE_MASK) space=PULSE_MASK;
+				space+=l;
+				if(space>PULSE_MASK) space=PULSE_MASK;
+				pulse=0;
+				return;
+			}
+			rbwrite(space);
+			rbwrite(pulse|PULSE_BIT);
+			ptr=0;
+			pulse=0;
+		}
+	}
+	rbwrite(l);
+}
+
+static irqreturn_t irq_handler(int i, void *blah, struct pt_regs *regs)
+{
+	struct timeval tv;
+	int status,counter,dcd;
+	long deltv;
+	lirc_t data;
+	
+	counter=0;
+	do{
+		counter++;
+		status=sinp(UART_MSR);
+		if(counter>RS_ISR_PASS_LIMIT)
+		{
+			printk(KERN_WARNING LIRC_DRIVER_NAME ": AIEEEE: "
+			       "We're caught!\n");
+			break;
+		}
+		if((status&hardware[type].signal_pin_change) && sense!=-1)
+		{
+			/* get current time */
+			do_gettimeofday(&tv);
+			
+			/* New mode, written by Trent Piepho 
+			   <xyzzy@u.washington.edu>. */
+			
+			/* The old format was not very portable.
+			   We now use the type lirc_t to pass pulses
+			   and spaces to user space.
+			   
+			   If PULSE_BIT is set a pulse has been
+			   received, otherwise a space has been
+			   received.  The driver needs to know if your
+			   receiver is active high or active low, or
+			   the space/pulse sense could be
+			   inverted. The bits denoted by PULSE_MASK are
+			   the length in microseconds. Lengths greater
+			   than or equal to 16 seconds are clamped to
+			   PULSE_MASK.  All other bits are unused.
+			   This is a much simpler interface for user
+			   programs, as well as eliminating "out of
+			   phase" errors with space/pulse
+			   autodetection. */
+
+			/* calculate time since last interrupt in
+			   microseconds */
+			dcd=(status & hardware[type].signal_pin) ? 1:0;
+			
+			deltv=tv.tv_sec-lasttv.tv_sec;
+			if(deltv>15) 
+			{
+				dprintk("AIEEEE: %d %d %lx %lx %lx %lx\n",
+					dcd,sense,
+					tv.tv_sec,lasttv.tv_sec,
+					tv.tv_usec,lasttv.tv_usec);
+				data=PULSE_MASK; /* really long time */
+				if(!(dcd^sense)) /* sanity check */
+				{
+				        /* detecting pulse while this
+					   MUST be a space! */
+				        sense=sense ? 0:1;
+				}
+			}
+			else
+			{
+				data=(lirc_t) (deltv*1000000+
+					       tv.tv_usec-
+					       lasttv.tv_usec);
+			};
+			if(tv.tv_sec<lasttv.tv_sec ||
+			   (tv.tv_sec==lasttv.tv_sec &&
+			    tv.tv_usec<lasttv.tv_usec))
+			{
+				printk(KERN_WARNING LIRC_DRIVER_NAME
+				       ": AIEEEE: your clock just jumped "
+				       "backwards\n");
+				printk(KERN_WARNING LIRC_DRIVER_NAME
+				       ": %d %d %lx %lx %lx %lx\n",
+				       dcd,sense,
+				       tv.tv_sec,lasttv.tv_sec,
+				       tv.tv_usec,lasttv.tv_usec);
+				data=PULSE_MASK;
+			}
+			frbwrite(dcd^sense ? data : (data|PULSE_BIT));
+			lasttv=tv;
+			wake_up_interruptible(&rbuf.wait_poll);
+		}
+	} while(!(sinp(UART_IIR) & UART_IIR_NO_INT)); /* still pending ? */
+	return IRQ_RETVAL(IRQ_HANDLED);
+}
+
+static int init_port(void)
+{
+	unsigned long flags;
+
+        /* Check io region*/
+	
+        if((check_region(io,8))==-EBUSY)
+	{
+		printk(KERN_ERR  LIRC_DRIVER_NAME  
+		       ": port %04x already in use\n", io);
+		printk(KERN_WARNING LIRC_DRIVER_NAME  
+		       ": use 'setserial /dev/ttySX uart none'\n");
+		printk(KERN_WARNING LIRC_DRIVER_NAME  
+		       ": or compile the serial port driver as module and\n");
+		printk(KERN_WARNING LIRC_DRIVER_NAME  
+		       ": make sure this module is loaded first\n");
+		return(-EBUSY);
+	}
+	
+	/* Reserve io region. */
+	request_region(io, 8, LIRC_DRIVER_NAME);
+	
+	local_irq_save(flags);
+	
+	/* Set DLAB 0. */
+	soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
+	
+	/* First of all, disable all interrupts */
+	soutp(UART_IER, sinp(UART_IER)&
+	      (~(UART_IER_MSI|UART_IER_RLSI|UART_IER_THRI|UART_IER_RDI)));
+	
+	/* Clear registers. */
+	sinp(UART_LSR);
+	sinp(UART_RX);
+	sinp(UART_IIR);
+	sinp(UART_MSR);
+	
+	/* Set line for power source */
+	soutp(UART_MCR, hardware[type].off);
+	
+	/* Clear registers again to be sure. */
+	sinp(UART_LSR);
+	sinp(UART_RX);
+	sinp(UART_IIR);
+	sinp(UART_MSR);
+
+	switch(hardware[type].type)
+	{
+	case LIRC_IRDEO:
+	case LIRC_IRDEO_REMOTE:
+		/* setup port to 7N1 @ 115200 Baud */
+		/* 7N1+start = 9 bits at 115200 ~ 3 bits at 38kHz */
+		
+		/* Set DLAB 1. */
+		soutp(UART_LCR, sinp(UART_LCR) | UART_LCR_DLAB);
+		/* Set divisor to 1 => 115200 Baud */
+		soutp(UART_DLM,0);
+		soutp(UART_DLL,1);
+		/* Set DLAB 0 +  7N1 */
+		soutp(UART_LCR,UART_LCR_WLEN7);
+		/* THR interrupt already disabled at this point */
+		break;
+	default:
+		break;
+	}
+	
+	local_irq_restore(flags);
+	
+	/* Initialize pulse/space widths */
+	init_timing_params(duty_cycle, freq);
+
+	/* If pin is high, then this must be an active low receiver. */
+	if(sense==-1)
+	{
+		/* wait 1 sec for the power supply */
+		
+		set_current_state(TASK_INTERRUPTIBLE);
+		schedule_timeout(HZ);
+		
+		sense=(sinp(UART_MSR) & hardware[type].signal_pin) ? 1:0;
+		printk(KERN_INFO  LIRC_DRIVER_NAME  ": auto-detected active "
+		       "%s receiver\n",sense ? "low":"high");
+	}
+	else
+	{
+		printk(KERN_INFO  LIRC_DRIVER_NAME  ": Manually using active "
+		       "%s receiver\n",sense ? "low":"high");
+	};
+	
+	return 0;
+}
+
+static int set_use_inc(void* data)
+{
+	int result;
+	unsigned long flags;
+	
+	spin_lock(&lirc_lock);
+	if(MOD_IN_USE)
+	{
+		spin_unlock(&lirc_lock);
+		return -EBUSY;
+	}
+	
+	/* Init read buffer. */
+	if (lirc_buffer_init(&rbuf, sizeof(lirc_t), RBUF_LEN) < 0)
+		return -ENOMEM;
+	
+	/* initialize timestamp */
+	do_gettimeofday(&lasttv);
+
+	result=request_irq(irq,irq_handler,
+			   SA_INTERRUPT | (share_irq ? SA_SHIRQ:0),
+			   LIRC_DRIVER_NAME,(void *)&hardware);
+	
+	switch(result)
+	{
+	case -EBUSY:
+		printk(KERN_ERR LIRC_DRIVER_NAME ": IRQ %d busy\n", irq);
+		spin_unlock(&lirc_lock);
+                lirc_buffer_free(&rbuf);
+		return -EBUSY;
+	case -EINVAL:
+		printk(KERN_ERR LIRC_DRIVER_NAME
+		       ": Bad irq number or handler\n");
+		spin_unlock(&lirc_lock);
+                lirc_buffer_free(&rbuf);
+		return -EINVAL;
+	default:
+		dprintk("Interrupt %d, port %04x obtained\n", irq, io);
+		break;
+	};
+
+	local_irq_save(flags);
+	
+	/* Set DLAB 0. */
+	soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
+	
+	soutp(UART_IER, sinp(UART_IER)|UART_IER_MSI);
+	
+	local_irq_restore(flags);
+	
+	MOD_INC_USE_COUNT;
+	spin_unlock(&lirc_lock);
+	return 0;
+}
+
+static void set_use_dec(void* data)
+{	unsigned long flags;
+	
+	local_irq_save(flags);
+	
+	/* Set DLAB 0. */
+	soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
+	
+	/* First of all, disable all interrupts */
+	soutp(UART_IER, sinp(UART_IER)&
+	      (~(UART_IER_MSI|UART_IER_RLSI|UART_IER_THRI|UART_IER_RDI)));
+	local_irq_restore(flags);
+
+	free_irq(irq, (void *)&hardware);
+	
+	dprintk("freed IRQ %d\n", irq);
+	lirc_buffer_free(&rbuf);
+	
+	MOD_DEC_USE_COUNT;
+}
+
+static ssize_t lirc_write(struct file *file, const char *buf,
+			 size_t n, loff_t * ppos)
+{
+	int retval,i,count;
+	unsigned long flags;
+	long delta=0;
+	
+	if(!(hardware[type].features&LIRC_CAN_SEND_PULSE))
+	{
+		return(-EBADF);
+	}
+	
+	if(n%sizeof(lirc_t)) return(-EINVAL);
+	retval=verify_area(VERIFY_READ,buf,n);
+	if(retval) return(retval);
+	count=n/sizeof(lirc_t);
+	if(count>WBUF_LEN || count%2==0) return(-EINVAL);
+	copy_from_user(wbuf,buf,n);
+	local_irq_save(flags);
+	if(hardware[type].type==LIRC_IRDEO)
+	{
+		/* DTR, RTS down */
+		on();
+	}
+	for(i=0;i<count;i++)
+	{
+		if(i%2) hardware[type].send_space(wbuf[i]-delta);
+		else delta=hardware[type].send_pulse(wbuf[i]);
+	}
+	off();
+	local_irq_restore(flags);
+	return(n);
+}
+
+static int lirc_ioctl(struct inode *node,struct file *filep,unsigned int cmd,
+		      unsigned long arg)
+{
+        int result;
+	unsigned long value;
+	unsigned int ivalue;
+	
+	switch(cmd)
+	{
+	case LIRC_GET_SEND_MODE:
+		if(!(hardware[type].features&LIRC_CAN_SEND_MASK))
+		{
+			return(-ENOIOCTLCMD);
+		}
+		
+		result=put_user(LIRC_SEND2MODE
+				(hardware[type].features&LIRC_CAN_SEND_MASK),
+				(unsigned long *) arg);
+		if(result) return(result); 
+		break;
+		
+	case LIRC_SET_SEND_MODE:
+		if(!(hardware[type].features&LIRC_CAN_SEND_MASK))
+		{
+			return(-ENOIOCTLCMD);
+		}
+		
+		result=get_user(value,(unsigned long *) arg);
+		if(result) return(result);
+		/* only LIRC_MODE_PULSE supported */
+		if(value!=LIRC_MODE_PULSE) return(-ENOSYS);
+		break;
+		
+	case LIRC_GET_LENGTH:
+		return(-ENOSYS);
+		break;
+		
+	case LIRC_SET_SEND_DUTY_CYCLE:
+		dprintk("SET_SEND_DUTY_CYCLE\n");
+		if(!(hardware[type].features&LIRC_CAN_SET_SEND_DUTY_CYCLE))
+		{
+			return(-ENOIOCTLCMD);
+		}
+		
+		result=get_user(ivalue,(unsigned int *) arg);
+		if(result) return(result);
+		if(ivalue<=0 || ivalue>100) return(-EINVAL);
+		return init_timing_params(ivalue, freq);
+		break;
+		
+	case LIRC_SET_SEND_CARRIER:
+		dprintk("SET_SEND_CARRIER\n");
+		if(!(hardware[type].features&LIRC_CAN_SET_SEND_CARRIER))
+		{
+			return(-ENOIOCTLCMD);
+		}
+		
+		result=get_user(ivalue,(unsigned int *) arg);
+		if(result) return(result);
+		if(ivalue>500000 || ivalue<20000) return(-EINVAL);
+		return init_timing_params(duty_cycle, ivalue);
+		break;
+		
+	default:
+		return(-ENOIOCTLCMD);
+	}
+	return(0);
+}
+
+static struct file_operations lirc_fops =
+{
+	write:   lirc_write,
+};
+
+static struct lirc_plugin plugin = {
+	name:		LIRC_DRIVER_NAME,
+	minor:		-1,
+	code_length:	1,
+	sample_rate:	0,
+	data:		NULL,
+	add_to_buf:	NULL,
+	get_queue:	NULL,
+	rbuf:		&rbuf,
+	set_use_inc:	set_use_inc,
+	set_use_dec:	set_use_dec,
+	ioctl:		lirc_ioctl,
+	fops:		&lirc_fops,
+};
+
+#ifdef MODULE
+
+int init_module(void)
+{
+	int result;
+	
+	switch(type)
+	{
+	case LIRC_HOMEBREW:
+	case LIRC_IRDEO:
+	case LIRC_IRDEO_REMOTE:
+	case LIRC_ANIMAX:
+	case LIRC_IGOR:
+		break;
+	default:
+		return(-EINVAL);
+	}
+	if(!softcarrier && hardware[type].type==LIRC_HOMEBREW)
+	{
+		hardware[type].features&=~(LIRC_CAN_SET_SEND_DUTY_CYCLE|
+					   LIRC_CAN_SET_SEND_CARRIER);
+	}
+	if ((result = init_port()) < 0)
+		return result;
+	plugin.features = hardware[type].features;
+	if ((plugin.minor = lirc_register_plugin(&plugin)) < 0) {
+		printk(KERN_ERR  LIRC_DRIVER_NAME  
+		       ": register_chrdev failed!\n");
+		release_region(io, 8);
+		return -EIO;
+	}
+	return 0;
+}
+
+void cleanup_module(void)
+{
+	release_region(io, 8);
+	lirc_unregister_plugin(plugin.minor);
+	dprintk("cleaned up module\n");
+}
+
+MODULE_DESCRIPTION("Infra-red receiver driver for serial ports.");
+MODULE_AUTHOR("Ralph Metzler, Trent Piepho, Ben Pfaff, Christoph Bartelmus");
+MODULE_LICENSE("GPL");
+
+module_param(type, int, 0444);
+MODULE_PARM_DESC(type, "Hardware type (0 = home-brew, 1 = IRdeo,"
+		 " 2 = IRdeo Remote, 3 = AnimaX, 4 = IgorPlug");
+
+module_param(io, int, 0444);
+MODULE_PARM_DESC(io, "I/O address base (0x3f8 or 0x2f8)");
+
+module_param(irq, int, 0444);
+MODULE_PARM_DESC(irq, "Interrupt (4 or 3)");
+
+module_param(share_irq, bool, 0444);
+MODULE_PARM_DESC(share_irq, "Share interrupts (0 = off, 1 = on)");
+
+module_param(sense, bool, 0444);
+MODULE_PARM_DESC(sense, "Override autodetection of IR receiver circuit"
+		 " (0 = active high, 1 = active low )");
+
+module_param(softcarrier, bool, 0444);
+MODULE_PARM_DESC(softcarrier, "Software carrier (0 = off, 1 = on)");
+
+module_param(debug, bool, 0644);
+MODULE_PARM_DESC(debug, "Enable debugging messages");
+
+EXPORT_NO_SYMBOLS;
+
+#endif /* MODULE */
diff -urN linux-2.6.10-rc3-mm1/drivers/char/lirc/lirc_sir.c linux-2.6.10-rc3-no2/drivers/char/lirc/lirc_sir.c
--- linux-2.6.10-rc3-mm1/drivers/char/lirc/lirc_sir.c	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.10-rc3-no2/drivers/char/lirc/lirc_sir.c	2004-12-22 19:35:16.596726602 -0500
@@ -0,0 +1,1313 @@
+/*
+ * LIRC SIR driver, (C) 2000 Milan Pikula <www@fornax.sk>
+ *
+ * lirc_sir - Device driver for use with SIR (serial infra red)
+ * mode of IrDA on many notebooks.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *
+ * 2000/09/16 Frank Przybylski <mail@frankprzybylski.de> :
+ *  added timeout and relaxed pulse detection, removed gap bug
+ *
+ * 2000/12/15 Christoph Bartelmus <lirc@bartelmus.de> : 
+ *   added support for Tekram Irmate 210 (sending does not work yet,
+ *   kind of disappointing that nobody was able to implement that
+ *   before),
+ *   major clean-up
+ *
+ * 2001/02/27 Christoph Bartelmus <lirc@bartelmus.de> : 
+ *   added support for StrongARM SA1100 embedded microprocessor
+ *   parts cut'n'pasted from sa1100_ir.c (C) 2000 Russell King
+ */
+
+
+#include <linux/version.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 2, 18)
+#error "**********************************************************"
+#error " Sorry, this driver needs kernel version 2.2.18 or higher "
+#error "**********************************************************"
+#endif
+#include <linux/module.h>
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+ 
+#include <linux/config.h>
+
+#if !defined(LIRC_ON_SA1100) && !defined(CONFIG_SERIAL_MODULE)
+#warning "******************************************"
+#warning " Your serial port driver is compiled into "
+#warning " the kernel. You will have to release the "
+#warning " port you want to use for LIRC with:      "
+#warning "    setserial /dev/ttySx uart none        "
+#warning "******************************************"
+#endif
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/serial_reg.h>
+#include <linux/time.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+#include <linux/mm.h>
+#include <linux/delay.h>
+#include <linux/poll.h>
+#include <asm/system.h>
+#include <asm/segment.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/fcntl.h>
+#ifdef LIRC_ON_SA1100
+#include <asm/hardware.h>
+#ifdef CONFIG_SA1100_COLLIE
+#include <asm/arch/tc35143.h>
+#include <asm/ucb1200.h>
+#endif
+#endif
+
+#include <linux/timer.h>
+
+#include "linux/lirc.h"
+#include "drivers/lirc_dev/lirc_dev.h"
+#include "linux/kcompat.h"
+
+/* SECTION: Definitions */
+
+/**************************** Tekram dongle ***************************/
+#ifdef LIRC_SIR_TEKRAM
+/* stolen from kernel source */
+/* definitions for Tekram dongle */
+#define TEKRAM_115200 0x00
+#define TEKRAM_57600  0x01
+#define TEKRAM_38400  0x02
+#define TEKRAM_19200  0x03
+#define TEKRAM_9600   0x04
+#define TEKRAM_2400   0x08
+
+#define TEKRAM_PW 0x10 /* Pulse select bit */
+
+/* 10bit * 1s/115200bit in milli seconds = 87ms*/
+#define TIME_CONST (10000000ul/115200ul)
+
+#endif
+
+#ifdef LIRC_SIR_ACTISYS_ACT200L
+static void init_act200(void);
+#endif
+
+/******************************* SA1100 ********************************/
+#ifdef LIRC_ON_SA1100
+struct sa1100_ser2_registers
+{
+	/* HSSP control register */
+	unsigned char hscr0;
+	/* UART registers */
+	unsigned char utcr0;
+	unsigned char utcr1;
+	unsigned char utcr2;
+	unsigned char utcr3;
+	unsigned char utcr4;
+	unsigned char utdr;
+	unsigned char utsr0;
+	unsigned char utsr1;
+} sr;
+
+static int irq=IRQ_Ser2ICP;
+
+#define LIRC_ON_SA1100_TRANSMITTER_LATENCY 0
+
+/* pulse/space ratio of 50/50 */
+static unsigned long pulse_width = (13-LIRC_ON_SA1100_TRANSMITTER_LATENCY);
+/* 1000000/freq-pulse_width */
+static unsigned long space_width = (13-LIRC_ON_SA1100_TRANSMITTER_LATENCY);
+static unsigned int freq = 38000;      /* modulation frequency */
+static unsigned int duty_cycle = 50;   /* duty cycle of 50% */
+
+#endif
+
+#define RBUF_LEN 1024
+#define WBUF_LEN 1024
+
+#define LIRC_DRIVER_NAME "lirc_sir"
+
+#ifndef LIRC_SIR_TEKRAM
+#define PULSE '['
+
+/* 9bit * 1s/115200bit in milli seconds = 78.125ms*/
+#define TIME_CONST (9000000ul/115200ul)
+#endif
+
+
+/* timeout for sequences in jiffies (=5/100s) */
+/* must be longer than TIME_CONST */
+#define SIR_TIMEOUT	(HZ*5/100)
+
+#ifndef LIRC_ON_SA1100
+static int io = LIRC_PORT;
+static int irq = LIRC_IRQ;
+static int threshold = 3;
+#endif
+
+static spinlock_t timer_lock = SPIN_LOCK_UNLOCKED;
+static struct timer_list timerlist;
+/* time of last signal change detected */
+static struct timeval last_tv = {0, 0};
+/* time of last UART data ready interrupt */
+static struct timeval last_intr_tv = {0, 0};
+static int last_value = 0;
+
+static DECLARE_WAIT_QUEUE_HEAD(lirc_read_queue);
+
+static spinlock_t hardware_lock = SPIN_LOCK_UNLOCKED;
+static spinlock_t dev_lock = SPIN_LOCK_UNLOCKED;
+
+static lirc_t rx_buf[RBUF_LEN]; unsigned int rx_tail = 0, rx_head = 0;
+#ifndef LIRC_SIR_TEKRAM
+static lirc_t tx_buf[WBUF_LEN];
+#endif
+
+static int debug = 0;
+#define dprintk(fmt, args...)                                     \
+	do{                                                       \
+		if(debug) printk(KERN_DEBUG LIRC_DRIVER_NAME ": " \
+                                 fmt, ## args);                   \
+	}while(0)
+
+/* SECTION: Prototypes */
+
+/* Communication with user-space */
+static int lirc_open(struct inode * inode, struct file * file);
+static int lirc_close(struct inode * inode, struct file *file);
+static unsigned int lirc_poll(struct file * file, poll_table * wait);
+static ssize_t lirc_read(struct file * file, char * buf, size_t count,
+		loff_t * ppos);
+static ssize_t lirc_write(struct file * file, const char * buf, size_t n, loff_t * pos);
+static int lirc_ioctl(struct inode *node,struct file *filep,unsigned int cmd,
+		unsigned long arg);
+static void add_read_queue(int flag, unsigned long val);
+#ifdef MODULE
+static int init_chrdev(void);
+static void drop_chrdev(void);
+#endif
+	/* Hardware */
+static irqreturn_t sir_interrupt(int irq, void * dev_id,
+				 struct pt_regs * regs);
+#ifndef LIRC_SIR_TEKRAM
+static void send_space(unsigned long len);
+static void send_pulse(unsigned long len);
+#endif
+static int init_hardware(void);
+static void drop_hardware(void);
+	/* Initialisation */
+static int init_port(void);
+static void drop_port(void);
+int init_module(void);
+void cleanup_module(void);
+
+#ifdef LIRC_ON_SA1100
+static void inline on(void)
+{
+	PPSR|=PPC_TXD2;
+}
+  
+static void inline off(void)
+{
+	PPSR&=~PPC_TXD2;
+}
+#else
+static inline unsigned int sinp(int offset)
+{
+	return inb(io + offset);
+}
+
+static inline void soutp(int offset, int value)
+{
+	outb(value, io + offset);
+}
+#endif
+
+#ifndef MAX_UDELAY_MS
+#define MAX_UDELAY_US 5000
+#else
+#define MAX_UDELAY_US (MAX_UDELAY_MS*1000)
+#endif
+
+static inline void safe_udelay(unsigned long usecs)
+{
+	while(usecs>MAX_UDELAY_US)
+	{
+		udelay(MAX_UDELAY_US);
+		usecs-=MAX_UDELAY_US;
+	}
+	udelay(usecs);
+}
+
+/* SECTION: Communication with user-space */
+
+static int lirc_open(struct inode * inode, struct file * file)
+{
+	spin_lock(&dev_lock);
+	if (MOD_IN_USE) {
+		spin_unlock(&dev_lock);
+		return -EBUSY;
+	}
+	MOD_INC_USE_COUNT;
+	spin_unlock(&dev_lock);
+	return 0;
+}
+
+static int lirc_close(struct inode * inode, struct file *file)
+{
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+static unsigned int lirc_poll(struct file * file, poll_table * wait)
+{
+	poll_wait(file, &lirc_read_queue, wait);
+	if (rx_head != rx_tail)
+		return POLLIN | POLLRDNORM;
+	return 0;
+}
+
+static ssize_t lirc_read(struct file * file, char * buf, size_t count,
+		loff_t * ppos)
+{
+	int n=0;
+	int retval = 0;
+	DECLARE_WAITQUEUE(wait,current);
+	
+	if(n%sizeof(lirc_t)) return(-EINVAL);
+	
+	add_wait_queue(&lirc_read_queue,&wait);
+	current->state=TASK_INTERRUPTIBLE;
+	while(n<count)
+	{
+		if(rx_head!=rx_tail)
+		{
+			retval=verify_area(VERIFY_WRITE,
+					   (void *) buf+n,sizeof(lirc_t));
+			if (retval)
+			{
+				return retval;
+			}
+			copy_to_user((void *) buf+n,(void *) (rx_buf+rx_head),
+				     sizeof(lirc_t));
+			rx_head=(rx_head+1)&(RBUF_LEN-1);
+			n+=sizeof(lirc_t);
+		}
+		else
+		{
+			if(file->f_flags & O_NONBLOCK)
+			{
+				retval=-EAGAIN;
+				break;
+			}
+			if(signal_pending(current))
+			{
+				retval=-ERESTARTSYS;
+				break;
+			}
+			schedule();
+			current->state=TASK_INTERRUPTIBLE;
+		}
+	}
+	remove_wait_queue(&lirc_read_queue,&wait);
+	current->state=TASK_RUNNING;
+	return (n ? n : retval);
+}
+static ssize_t lirc_write(struct file * file, const char * buf, size_t n, loff_t * pos)
+{
+	unsigned long flags;
+#ifdef LIRC_SIR_TEKRAM
+	return(-EBADF);
+#else
+	int i;
+	int retval;
+
+        if(n%sizeof(lirc_t) || (n/sizeof(lirc_t)) > WBUF_LEN)
+		return(-EINVAL);
+	retval = verify_area(VERIFY_READ, buf, n);
+	if (retval)
+		return retval;
+	copy_from_user(tx_buf, buf, n);
+	i = 0;
+	n/=sizeof(lirc_t);
+#ifdef LIRC_ON_SA1100
+	/* disable receiver */
+	Ser2UTCR3=0;
+#endif
+	local_irq_save(flags);
+	while (1) {
+		if (i >= n)
+			break;
+		if (tx_buf[i])
+			send_pulse(tx_buf[i]);
+		i++;
+		if (i >= n)
+			break;
+		if (tx_buf[i])
+			send_space(tx_buf[i]);
+		i++;
+	}
+	local_irq_restore(flags);
+#ifdef LIRC_ON_SA1100
+	off();
+	udelay(1000); /* wait 1ms for IR diode to recover */
+	Ser2UTCR3=0;
+	/* clear status register to prevent unwanted interrupts */
+	Ser2UTSR0 &= (UTSR0_RID | UTSR0_RBB | UTSR0_REB);
+	/* enable receiver */
+	Ser2UTCR3=UTCR3_RXE|UTCR3_RIE;
+#endif
+	return n;
+#endif
+}
+
+static int lirc_ioctl(struct inode *node,struct file *filep,unsigned int cmd,
+		unsigned long arg)
+{
+	int retval = 0;
+	unsigned long value = 0;
+#ifdef LIRC_ON_SA1100
+	unsigned int ivalue;
+#endif
+
+#ifdef LIRC_SIR_TEKRAM
+	if (cmd == LIRC_GET_FEATURES)
+		value = LIRC_CAN_REC_MODE2;
+	else if (cmd == LIRC_GET_SEND_MODE)
+		value = 0;
+	else if (cmd == LIRC_GET_REC_MODE)
+		value = LIRC_MODE_MODE2;
+#elif defined(LIRC_ON_SA1100)
+	if (cmd == LIRC_GET_FEATURES)
+		value = LIRC_CAN_SEND_PULSE |
+			LIRC_CAN_SET_SEND_DUTY_CYCLE |
+			LIRC_CAN_SET_SEND_CARRIER |
+			LIRC_CAN_REC_MODE2;
+	else if (cmd == LIRC_GET_SEND_MODE)
+		value = LIRC_MODE_PULSE;
+	else if (cmd == LIRC_GET_REC_MODE)
+		value = LIRC_MODE_MODE2;
+#else
+	if (cmd == LIRC_GET_FEATURES)
+		value = LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2;
+	else if (cmd == LIRC_GET_SEND_MODE)
+		value = LIRC_MODE_PULSE;
+	else if (cmd == LIRC_GET_REC_MODE)
+		value = LIRC_MODE_MODE2;
+#endif
+
+	switch (cmd) {
+	case LIRC_GET_FEATURES:
+	case LIRC_GET_SEND_MODE:
+	case LIRC_GET_REC_MODE:
+		retval = put_user(value, (unsigned long *) arg);
+		break;
+
+	case LIRC_SET_SEND_MODE:
+	case LIRC_SET_REC_MODE:
+		retval = get_user(value, (unsigned long *) arg);
+		break;
+#ifdef LIRC_ON_SA1100
+	case LIRC_SET_SEND_DUTY_CYCLE:
+		retval=get_user(ivalue,(unsigned int *) arg);
+		if(retval) return(retval);
+		if(ivalue<=0 || ivalue>100) return(-EINVAL);
+		/* (ivalue/100)*(1000000/freq) */
+		duty_cycle=ivalue;
+		pulse_width=(unsigned long) duty_cycle*10000/freq;
+		space_width=(unsigned long) 1000000L/freq-pulse_width;
+		if(pulse_width>=LIRC_ON_SA1100_TRANSMITTER_LATENCY)
+			pulse_width-=LIRC_ON_SA1100_TRANSMITTER_LATENCY;
+		if(space_width>=LIRC_ON_SA1100_TRANSMITTER_LATENCY)
+			space_width-=LIRC_ON_SA1100_TRANSMITTER_LATENCY;
+		break;
+	case LIRC_SET_SEND_CARRIER:
+		retval=get_user(ivalue,(unsigned int *) arg);
+		if(retval) return(retval);
+		if(ivalue>500000 || ivalue<20000) return(-EINVAL);
+		freq=ivalue;
+		pulse_width=(unsigned long) duty_cycle*10000/freq;
+		space_width=(unsigned long) 1000000L/freq-pulse_width;
+		if(pulse_width>=LIRC_ON_SA1100_TRANSMITTER_LATENCY)
+			pulse_width-=LIRC_ON_SA1100_TRANSMITTER_LATENCY;
+		if(space_width>=LIRC_ON_SA1100_TRANSMITTER_LATENCY)
+			space_width-=LIRC_ON_SA1100_TRANSMITTER_LATENCY;
+		break;
+#endif
+	default:
+		retval = -ENOIOCTLCMD;
+
+	}
+	
+	if (retval)
+		return retval;
+	
+#ifdef LIRC_SIR_TEKRAM
+	if (cmd == LIRC_SET_REC_MODE) {
+		if (value != LIRC_MODE_MODE2)
+			retval = -ENOSYS;
+	} else if (cmd == LIRC_SET_SEND_MODE) {
+		retval = -ENOSYS;
+	}
+#else
+	if (cmd == LIRC_SET_REC_MODE) {
+		if (value != LIRC_MODE_MODE2)
+			retval = -ENOSYS;
+	} else if (cmd == LIRC_SET_SEND_MODE) {
+		if (value != LIRC_MODE_PULSE)
+			retval = -ENOSYS;
+	}
+#endif
+	return retval;
+}
+
+static void add_read_queue(int flag, unsigned long val)
+{
+	unsigned int new_rx_tail;
+	lirc_t newval;
+
+	dprintk("add flag %d with val %lu\n", flag, val);
+
+	newval = val & PULSE_MASK;
+
+	/* statistically pulses are ~TIME_CONST/2 too long: we could
+	   maybe make this more exactly but this is good enough */
+	if(flag) /* pulse */
+	{
+		if(newval>TIME_CONST/2)
+		{
+			newval-=TIME_CONST/2;
+		}
+		else /* should not ever happen */
+		{
+			newval=1;
+		}
+		newval|=PULSE_BIT;
+	}
+	else
+	{
+		newval+=TIME_CONST/2;
+	}
+	new_rx_tail = (rx_tail + 1) & (RBUF_LEN - 1);
+	if (new_rx_tail == rx_head) {
+		dprintk("Buffer overrun.\n");
+		return;
+	}
+	rx_buf[rx_tail] = newval;
+	rx_tail = new_rx_tail;
+	wake_up_interruptible(&lirc_read_queue);
+}
+
+static struct file_operations lirc_fops =
+{
+	read:    lirc_read,
+	write:   lirc_write,
+	poll:    lirc_poll,
+	ioctl:   lirc_ioctl,
+	open:    lirc_open,
+	release: lirc_close,
+};
+
+static int set_use_inc(void* data)
+{
+#if WE_DONT_USE_LOCAL_OPEN_CLOSE
+	MOD_INC_USE_COUNT;
+#endif
+       return 0;
+}
+
+static void set_use_dec(void* data)
+{
+#if WE_DONT_USE_LOCAL_OPEN_CLOSE
+	MOD_DEC_USE_COUNT;
+#endif
+}
+static struct lirc_plugin plugin = {
+       name:           LIRC_DRIVER_NAME,
+       minor:          -1,
+       code_length:    1,
+       sample_rate:    0,
+       data:           NULL,
+       add_to_buf:     NULL,
+       get_queue:      NULL,
+       set_use_inc:    set_use_inc,
+       set_use_dec:    set_use_dec,
+       fops:           &lirc_fops,
+};
+
+
+#ifdef MODULE
+static int init_chrdev(void)
+{
+	plugin.minor = lirc_register_plugin(&plugin);
+	if (plugin.minor < 0) {
+		printk(KERN_ERR LIRC_DRIVER_NAME ": init_chrdev() failed.\n");
+		return -EIO;
+	}
+	return 0;
+}
+
+static void drop_chrdev(void)
+{
+	lirc_unregister_plugin(plugin.minor);
+}
+#endif
+
+/* SECTION: Hardware */
+static long delta(struct timeval * tv1, struct timeval * tv2)
+{
+	unsigned long deltv;
+	
+	deltv = tv2->tv_sec - tv1->tv_sec;
+	if (deltv > 15)
+		deltv = 0xFFFFFF;
+	else
+		deltv = deltv*1000000 +
+			tv2->tv_usec -
+			tv1->tv_usec;
+	return deltv;
+}
+
+static void sir_timeout(unsigned long data) 
+{
+	/* if last received signal was a pulse, but receiving stopped
+	   within the 9 bit frame, we need to finish this pulse and
+	   simulate a signal change to from pulse to space. Otherwise
+	   upper layers will receive two sequences next time. */
+	
+	unsigned long flags;
+	unsigned long pulse_end;
+	
+	/* avoid interference with interrupt */
+ 	spin_lock_irqsave(&timer_lock, flags);
+	if (last_value)
+	{
+#ifndef LIRC_ON_SA1100
+		/* clear unread bits in UART and restart */
+		outb(UART_FCR_CLEAR_RCVR, io + UART_FCR);
+#endif
+		/* determine 'virtual' pulse end: */
+	 	pulse_end = delta(&last_tv, &last_intr_tv);
+		dprintk("timeout add %d for %lu usec\n",last_value,pulse_end);
+		add_read_queue(last_value,pulse_end);
+		last_value = 0;
+		last_tv=last_intr_tv;
+	}
+	spin_unlock_irqrestore(&timer_lock, flags);		
+}
+
+static irqreturn_t sir_interrupt(int irq, void * dev_id,
+				 struct pt_regs * regs)
+{
+	unsigned char data;
+	struct timeval curr_tv;
+	static unsigned long deltv;
+#ifdef LIRC_ON_SA1100
+	int status;
+	static int n=0;
+	
+	//printk("interrupt\n");
+	status = Ser2UTSR0;
+	/*
+	 * Deal with any receive errors first.  The bytes in error may be
+	 * the only bytes in the receive FIFO, so we do this first.
+	 */
+	while (status & UTSR0_EIF)
+	{
+		int bstat;
+
+		if(debug) {
+			dprintk("EIF\n");
+			bstat = Ser2UTSR1;
+		
+			if (bstat & UTSR1_FRE)
+				dprintk("frame error\n");
+			if (bstat & UTSR1_ROR)
+				dprintk("receive fifo overrun\n");
+			if (bstat & UTSR1_PRE)
+				dprintk("parity error\n");
+		}
+		
+		bstat = Ser2UTDR;
+		n++;
+		status = Ser2UTSR0;
+	}
+
+	if (status & (UTSR0_RFS | UTSR0_RID))
+	{
+		do_gettimeofday(&curr_tv);
+		deltv = delta(&last_tv, &curr_tv);
+		do
+		{
+			data=Ser2UTDR;
+			dprintk("%d data: %u\n", n, (unsigned int) data);
+			n++;
+		}
+		while(status&UTSR0_RID && /* do not empty fifo in
+                                             order to get UTSR0_RID in
+                                             any case */
+		      Ser2UTSR1 & UTSR1_RNE); /* data ready */
+		
+		if(status&UTSR0_RID)
+		{
+			//printk("add\n");
+			add_read_queue(0,deltv-n*TIME_CONST); /*space*/
+			add_read_queue(1,n*TIME_CONST); /*pulse*/
+			n=0;
+			last_tv=curr_tv;
+		}
+	}
+
+	if (status & UTSR0_TFS) {
+
+		printk("transmit fifo not full, shouldn't ever happen\n");
+	}
+
+	/*
+	 * We must clear certain bits.
+	 */
+	status &= (UTSR0_RID | UTSR0_RBB | UTSR0_REB);
+	if (status)
+		Ser2UTSR0 = status;
+#else
+	unsigned long deltintrtv;
+	unsigned long flags;
+	int iir, lsr;
+
+	while ((iir = inb(io + UART_IIR) & UART_IIR_ID)) {
+		switch (iir&UART_IIR_ID) { /* FIXME toto treba preriedit */
+		case UART_IIR_MSI:
+			(void) inb(io + UART_MSR);
+			break;
+		case UART_IIR_RLSI:
+			(void) inb(io + UART_LSR);
+			break;
+		case UART_IIR_THRI:
+#if 0
+			if (lsr & UART_LSR_THRE) /* FIFO is empty */
+				outb(data, io + UART_TX)
+#endif
+			break;
+		case UART_IIR_RDI:
+			/* avoid interference with timer */
+		 	spin_lock_irqsave(&timer_lock, flags);
+			do
+			{
+				del_timer(&timerlist);
+				data = inb(io + UART_RX);
+				do_gettimeofday(&curr_tv);
+				deltv = delta(&last_tv, &curr_tv);
+				deltintrtv = delta(&last_intr_tv, &curr_tv);
+				dprintk("t %lu , d %d\n", deltintrtv, (int)data);
+				/* if nothing came in last X cycles,
+				   it was gap */
+				if (deltintrtv > TIME_CONST * threshold) {
+					if (last_value) {
+						dprintk("GAP\n");
+						/* simulate signal change */
+						add_read_queue(last_value,
+							       deltv-
+							       deltintrtv);
+						last_value = 0;
+						last_tv.tv_sec = last_intr_tv.tv_sec;
+						last_tv.tv_usec = last_intr_tv.tv_usec;
+						deltv = deltintrtv;
+					}
+				}
+				data = 1;
+				if (data ^ last_value) {
+					/* deltintrtv > 2*TIME_CONST,
+                                           remember ? */
+					/* the other case is timeout */
+					add_read_queue(last_value,
+						       deltv-TIME_CONST);
+					last_value = data;
+					last_tv = curr_tv;
+					if(last_tv.tv_usec>=TIME_CONST)
+					{
+						last_tv.tv_usec-=TIME_CONST;
+					}
+					else
+					{
+						last_tv.tv_sec--;
+						last_tv.tv_usec+=1000000-
+							TIME_CONST;
+					}
+				}
+				last_intr_tv = curr_tv;
+				if (data)
+				{
+					/* start timer for end of sequence detection */
+					timerlist.expires = jiffies + SIR_TIMEOUT;
+					add_timer(&timerlist);
+				}
+			}
+			while ((lsr = inb(io + UART_LSR))
+				& UART_LSR_DR); /* data ready */
+			spin_unlock_irqrestore(&timer_lock, flags);
+			break;
+		default:
+			break;
+		}
+	}
+#endif
+	return IRQ_RETVAL(IRQ_HANDLED);
+}
+
+#ifdef LIRC_ON_SA1100
+static void send_pulse(unsigned long length)
+{
+	unsigned long k,delay;
+	int flag;
+
+	if(length==0) return;
+	/* this won't give us the carrier frequency we really want
+	   due to integer arithmetic, but we can accept this inaccuracy */
+
+	for(k=flag=0;k<length;k+=delay,flag=!flag)
+	{
+		if(flag)
+		{
+			off();
+			delay=space_width;
+		}
+		else
+		{
+			on();
+			delay=pulse_width;
+		}
+		safe_udelay(delay);
+	}
+	off();
+}
+
+static void send_space(unsigned long length)
+{
+	if(length==0) return;
+	off();
+	safe_udelay(length);
+}
+#elif defined(LIRC_SIR_TEKRAM)
+#else
+static void send_space(unsigned long len)
+{
+	safe_udelay(len);
+}
+
+static void send_pulse(unsigned long len)
+{
+	long bytes_out = len / TIME_CONST;
+	long time_left;
+
+	if (!bytes_out)
+		bytes_out++;
+	time_left = (long)len - (long)bytes_out * (long)TIME_CONST;
+	while (--bytes_out) {
+		outb(PULSE, io + UART_TX);
+		/* FIXME treba seriozne cakanie z drivers/char/serial.c */
+		while (!(inb(io + UART_LSR) & UART_LSR_THRE));
+	}
+#if 0
+	if (time_left > 0)
+		safe_udelay(time_left);
+#endif
+}
+#endif
+
+#ifdef CONFIG_SA1100_COLLIE
+static inline int sa1100_irda_set_power_collie(int state)
+{
+	if (state) {
+		/*
+		 *  0 - off
+		 *  1 - short range, lowest power
+		 *  2 - medium range, medium power
+		 *  3 - maximum range, high power
+		 */
+		ucb1200_set_io_direction(TC35143_GPIO_IR_ON,
+					 TC35143_IODIR_OUTPUT);
+		ucb1200_set_io(TC35143_GPIO_IR_ON, TC35143_IODAT_LOW);
+		udelay(100);
+	}
+	else {
+		/* OFF */
+		ucb1200_set_io_direction(TC35143_GPIO_IR_ON,
+					 TC35143_IODIR_OUTPUT);
+		ucb1200_set_io(TC35143_GPIO_IR_ON, TC35143_IODAT_HIGH);
+	}
+	return 0;
+}
+#endif
+
+static int init_hardware(void)
+{
+	unsigned long flags;
+	
+	spin_lock_irqsave(&hardware_lock, flags);
+	/* reset UART */
+#ifdef LIRC_ON_SA1100
+#ifdef CONFIG_SA1100_BITSY
+	if (machine_is_bitsy()) {
+		printk("Power on IR module\n");
+		set_bitsy_egpio(EGPIO_BITSY_IR_ON);
+	}
+#endif
+#ifdef CONFIG_SA1100_COLLIE
+	sa1100_irda_set_power_collie(3);	/* power on */
+#endif
+	sr.hscr0=Ser2HSCR0;
+
+	sr.utcr0=Ser2UTCR0;
+	sr.utcr1=Ser2UTCR1;
+	sr.utcr2=Ser2UTCR2;
+	sr.utcr3=Ser2UTCR3;
+	sr.utcr4=Ser2UTCR4;
+
+	sr.utdr=Ser2UTDR;
+	sr.utsr0=Ser2UTSR0;
+	sr.utsr1=Ser2UTSR1;
+
+	/* configure GPIO */
+	/* output */
+	PPDR|=PPC_TXD2;
+	PSDR|=PPC_TXD2;
+	/* set output to 0 */
+	off();
+	
+	/*
+	 * Enable HP-SIR modulation, and ensure that the port is disabled.
+	 */
+	Ser2UTCR3=0;
+	Ser2HSCR0=sr.hscr0 & (~HSCR0_HSSP);
+	
+	/* clear status register to prevent unwanted interrupts */
+	Ser2UTSR0 &= (UTSR0_RID | UTSR0_RBB | UTSR0_REB);
+	
+	/* 7N1 */
+	Ser2UTCR0=UTCR0_1StpBit|UTCR0_7BitData;
+	/* 115200 */
+	Ser2UTCR1=0;
+	Ser2UTCR2=1;
+	/* use HPSIR, 1.6 usec pulses */
+	Ser2UTCR4=UTCR4_HPSIR|UTCR4_Z1_6us;
+	
+	/* enable receiver, receive fifo interrupt */
+	Ser2UTCR3=UTCR3_RXE|UTCR3_RIE;
+	
+	/* clear status register to prevent unwanted interrupts */
+	Ser2UTSR0 &= (UTSR0_RID | UTSR0_RBB | UTSR0_REB);
+	
+#elif defined(LIRC_SIR_TEKRAM)
+	/* disable FIFO */ 
+	soutp(UART_FCR,
+	      UART_FCR_CLEAR_RCVR|
+	      UART_FCR_CLEAR_XMIT|
+	      UART_FCR_TRIGGER_1);
+	
+	/* Set DLAB 0. */
+	soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
+	
+	/* First of all, disable all interrupts */
+	soutp(UART_IER, sinp(UART_IER)&
+	      (~(UART_IER_MSI|UART_IER_RLSI|UART_IER_THRI|UART_IER_RDI)));
+	
+	/* Set DLAB 1. */
+	soutp(UART_LCR, sinp(UART_LCR) | UART_LCR_DLAB);
+	
+	/* Set divisor to 12 => 9600 Baud */
+	soutp(UART_DLM,0);
+	soutp(UART_DLL,12);
+	
+	/* Set DLAB 0. */
+	soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
+	
+	/* power supply */
+	soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2);
+	safe_udelay(50*1000);
+	
+	/* -DTR low -> reset PIC */
+	soutp(UART_MCR, UART_MCR_RTS|UART_MCR_OUT2);
+	udelay(1*1000);
+	
+	soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2);
+	udelay(100);
+
+
+        /* -RTS low -> send control byte */
+	soutp(UART_MCR, UART_MCR_DTR|UART_MCR_OUT2);
+	udelay(7);
+	soutp(UART_TX, TEKRAM_115200|TEKRAM_PW);
+	
+	/* one byte takes ~1042 usec to transmit at 9600,8N1 */
+	udelay(1500);
+	
+	/* back to normal operation */
+	soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2);
+	udelay(50);
+
+	udelay(1500);
+	
+	/* read previous control byte */
+	printk(KERN_INFO LIRC_DRIVER_NAME
+	       ": 0x%02x\n",sinp(UART_RX));
+	
+	/* Set DLAB 1. */
+	soutp(UART_LCR, sinp(UART_LCR) | UART_LCR_DLAB);
+	
+	/* Set divisor to 1 => 115200 Baud */
+	soutp(UART_DLM,0);
+	soutp(UART_DLL,1);
+
+	/* Set DLAB 0, 8 Bit */
+	soutp(UART_LCR, UART_LCR_WLEN8);
+	/* enable interrupts */
+	soutp(UART_IER, sinp(UART_IER)|UART_IER_RDI);
+#else
+	outb(0, io + UART_MCR);
+	outb(0, io + UART_IER);
+	/* init UART */
+		/* set DLAB, speed = 115200 */
+	outb(UART_LCR_DLAB | UART_LCR_WLEN7, io + UART_LCR);
+	outb(1, io + UART_DLL); outb(0, io + UART_DLM);
+		/* 7N1+start = 9 bits at 115200 ~ 3 bits at 44000 */
+	outb(UART_LCR_WLEN7, io + UART_LCR);
+		/* FIFO operation */
+	outb(UART_FCR_ENABLE_FIFO, io + UART_FCR);
+		/* interrupts */
+	// outb(UART_IER_RLSI|UART_IER_RDI|UART_IER_THRI, io + UART_IER);
+	outb(UART_IER_RDI, io + UART_IER);	
+	/* turn on UART */
+	outb(UART_MCR_DTR|UART_MCR_RTS|UART_MCR_OUT2, io + UART_MCR);
+#ifdef LIRC_SIR_ACTISYS_ACT200L
+	init_act200();
+#endif
+#endif
+	spin_unlock_irqrestore(&hardware_lock, flags);
+	return 0;
+}
+
+static void drop_hardware(void)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&hardware_lock, flags);
+
+#ifdef LIRC_ON_SA1100
+	Ser2UTCR3=0;
+	
+	Ser2UTCR0=sr.utcr0;
+	Ser2UTCR1=sr.utcr1;
+	Ser2UTCR2=sr.utcr2;
+	Ser2UTCR4=sr.utcr4;
+	Ser2UTCR3=sr.utcr3;
+	
+	Ser2HSCR0=sr.hscr0;
+#ifdef CONFIG_SA1100_BITSY
+	if (machine_is_bitsy()) {
+		clr_bitsy_egpio(EGPIO_BITSY_IR_ON);
+	}
+#endif
+#ifdef CONFIG_SA1100_COLLIE
+	sa1100_irda_set_power_collie(0);	/* power off */
+#endif
+#else
+	/* turn off interrupts */
+	outb(0, io + UART_IER);	
+#endif
+	spin_unlock_irqrestore(&hardware_lock, flags);
+}
+
+/* SECTION: Initialisation */
+
+static int init_port(void)
+{
+	int retval;
+	
+#ifndef LIRC_ON_SA1100
+	/* get I/O port access and IRQ line */
+	retval = check_region(io, 8);
+	if (retval < 0) {
+		printk(KERN_ERR LIRC_DRIVER_NAME
+			": i/o port 0x%.4x already in use.\n",
+			io);
+		return retval;
+	}
+#endif
+	retval = request_irq(irq, sir_interrupt, SA_INTERRUPT,
+			     LIRC_DRIVER_NAME, NULL);
+	if (retval < 0) {
+		printk(KERN_ERR LIRC_DRIVER_NAME
+			": IRQ %d already in use.\n",
+			irq);
+		return retval;
+	}
+#ifndef LIRC_ON_SA1100
+	request_region(io, 8, LIRC_DRIVER_NAME);
+	printk(KERN_INFO LIRC_DRIVER_NAME
+		": I/O port 0x%.4x, IRQ %d.\n",
+		io, irq);
+#endif
+
+	init_timer(&timerlist);
+	timerlist.function = sir_timeout;
+	timerlist.data = 0xabadcafe;
+
+	return 0;
+}
+
+static void drop_port(void)
+{
+	disable_irq(irq);
+	free_irq(irq, NULL);
+	del_timer_sync(&timerlist);
+#ifndef LIRC_ON_SA1100
+	release_region(io, 8);
+#endif
+}
+
+#ifdef LIRC_SIR_ACTISYS_ACT200L
+/******************************************************/
+/* Crystal/Cirrus CS8130 IR transceiver, used in Actisys Act200L dongle */
+/* some code borrowed from Linux IRDA driver */
+
+/* Regsiter 0: Control register #1 */
+#define ACT200L_REG0    0x00
+#define ACT200L_TXEN    0x01 /* Enable transmitter */
+#define ACT200L_RXEN    0x02 /* Enable receiver */
+#define ACT200L_ECHO    0x08 /* Echo control chars */
+
+/* Register 1: Control register #2 */
+#define ACT200L_REG1    0x10
+#define ACT200L_LODB    0x01 /* Load new baud rate count value */
+#define ACT200L_WIDE    0x04 /* Expand the maximum allowable pulse */
+
+/* Register 3: Transmit mode register #2 */
+#define ACT200L_REG3    0x30
+#define ACT200L_B0      0x01 /* DataBits, 0=6, 1=7, 2=8, 3=9(8P)  */
+#define ACT200L_B1      0x02 /* DataBits, 0=6, 1=7, 2=8, 3=9(8P)  */
+#define ACT200L_CHSY    0x04 /* StartBit Synced 0=bittime, 1=startbit */
+
+/* Register 4: Output Power register */
+#define ACT200L_REG4    0x40
+#define ACT200L_OP0     0x01 /* Enable LED1C output */
+#define ACT200L_OP1     0x02 /* Enable LED2C output */
+#define ACT200L_BLKR    0x04
+
+/* Register 5: Receive Mode register */
+#define ACT200L_REG5    0x50
+#define ACT200L_RWIDL   0x01 /* fixed 1.6us pulse mode */
+    /*.. other various IRDA bit modes, and TV remote modes..*/
+
+/* Register 6: Receive Sensitivity register #1 */
+#define ACT200L_REG6    0x60
+#define ACT200L_RS0     0x01 /* receive threshold bit 0 */
+#define ACT200L_RS1     0x02 /* receive threshold bit 1 */
+
+/* Register 7: Receive Sensitivity register #2 */
+#define ACT200L_REG7    0x70
+#define ACT200L_ENPOS   0x04 /* Ignore the falling edge */
+
+/* Register 8,9: Baud Rate Dvider register #1,#2 */
+#define ACT200L_REG8    0x80
+#define ACT200L_REG9    0x90
+
+#define ACT200L_2400    0x5f
+#define ACT200L_9600    0x17
+#define ACT200L_19200   0x0b
+#define ACT200L_38400   0x05
+#define ACT200L_57600   0x03
+#define ACT200L_115200  0x01
+
+/* Register 13: Control register #3 */
+#define ACT200L_REG13   0xd0
+#define ACT200L_SHDW    0x01 /* Enable access to shadow registers */
+
+/* Register 15: Status register */
+#define ACT200L_REG15   0xf0
+
+/* Register 21: Control register #4 */
+#define ACT200L_REG21   0x50
+#define ACT200L_EXCK    0x02 /* Disable clock output driver */
+#define ACT200L_OSCL    0x04 /* oscillator in low power, medium accuracy mode */
+
+static void init_act200(void)
+{
+	int i;
+	__u8 control[] = {
+		ACT200L_REG15,
+		ACT200L_REG13 | ACT200L_SHDW,
+		ACT200L_REG21 | ACT200L_EXCK | ACT200L_OSCL,
+		ACT200L_REG13,
+		ACT200L_REG7  | ACT200L_ENPOS,
+		ACT200L_REG6  | ACT200L_RS0  | ACT200L_RS1,
+		ACT200L_REG5  | ACT200L_RWIDL,
+		ACT200L_REG4  | ACT200L_OP0  | ACT200L_OP1 | ACT200L_BLKR,
+		ACT200L_REG3  | ACT200L_B0,
+		ACT200L_REG0  | ACT200L_TXEN | ACT200L_RXEN,
+		ACT200L_REG8 |  (ACT200L_115200       & 0x0f),
+		ACT200L_REG9 | ((ACT200L_115200 >> 4) & 0x0f),
+		ACT200L_REG1 | ACT200L_LODB | ACT200L_WIDE
+	};
+
+	/* Set DLAB 1. */
+	soutp(UART_LCR, UART_LCR_DLAB | UART_LCR_WLEN8);
+	
+	/* Set divisor to 12 => 9600 Baud */
+	soutp(UART_DLM,0);
+	soutp(UART_DLL,12);
+	
+	/* Set DLAB 0. */
+	soutp(UART_LCR, UART_LCR_WLEN8);
+	/* Set divisor to 12 => 9600 Baud */
+
+	/* power supply */
+	soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2);
+	for (i=0; i<50; i++) {
+		safe_udelay(1000);
+	}
+
+		/* Reset the dongle : set RTS low for 25 ms */
+	soutp(UART_MCR, UART_MCR_DTR|UART_MCR_OUT2);
+	for (i=0; i<25; i++) {
+		udelay(1000);
+	}
+
+	soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2);
+	udelay(100);
+
+	/* Clear DTR and set RTS to enter command mode */
+	soutp(UART_MCR, UART_MCR_RTS|UART_MCR_OUT2);
+	udelay(7);
+
+/* send out the control register settings for 115K 7N1 SIR operation */
+	for (i=0; i<sizeof(control); i++) {
+		soutp(UART_TX, control[i]);
+		/* one byte takes ~1042 usec to transmit at 9600,8N1 */
+		udelay(1500);
+	}
+
+	/* back to normal operation */
+	soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2);
+	udelay(50);
+
+	udelay(1500);
+	soutp(UART_LCR, sinp(UART_LCR) | UART_LCR_DLAB);
+
+	/* Set DLAB 1. */
+	soutp(UART_LCR, UART_LCR_DLAB | UART_LCR_WLEN7);
+
+	/* Set divisor to 1 => 115200 Baud */
+	soutp(UART_DLM,0);
+	soutp(UART_DLL,1);
+
+	/* Set DLAB 0. */
+	soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
+
+	/* Set DLAB 0, 7 Bit */
+	soutp(UART_LCR, UART_LCR_WLEN7);
+
+	/* enable interrupts */
+	soutp(UART_IER, sinp(UART_IER)|UART_IER_RDI);
+}
+#endif
+
+static int init_lirc_sir(void)
+{
+	int retval;
+
+	init_waitqueue_head(&lirc_read_queue);
+	retval = init_port();
+	if (retval < 0)
+		return retval;
+	init_hardware();
+	enable_irq(irq);
+	printk(KERN_INFO LIRC_DRIVER_NAME
+		": Installed.\n");
+	return 0;
+}
+
+#ifdef MODULE
+
+int init_module(void)
+{
+	int retval;
+	
+	retval=init_chrdev();
+	if(retval < 0)
+		return retval;
+	retval = init_lirc_sir();
+	if (retval) {
+		drop_chrdev();
+		return retval;
+	}
+	return 0;
+}
+
+void cleanup_module(void)
+{
+	drop_hardware();
+	drop_chrdev();
+	drop_port();
+	printk(KERN_INFO LIRC_DRIVER_NAME ": Uninstalled.\n");
+}
+
+#ifdef LIRC_SIR_TEKRAM
+MODULE_DESCRIPTION("Infrared receiver driver for Tekram Irmate 210");
+MODULE_AUTHOR("Christoph Bartelmus");
+#elif defined(LIRC_ON_SA1100)
+MODULE_DESCRIPTION("LIRC driver for StrongARM SA1100 embedded microprocessor");
+MODULE_AUTHOR("Christoph Bartelmus");
+#elif defined(LIRC_SIR_ACTISYS_ACT200L)
+MODULE_DESCRIPTION("LIRC driver for Actisys Act200L");
+MODULE_AUTHOR("Karl Bongers");
+#else
+MODULE_DESCRIPTION("Infrared receiver driver for SIR type serial ports");
+MODULE_AUTHOR("Milan Pikula");
+#endif
+MODULE_LICENSE("GPL");
+
+#ifdef LIRC_ON_SA1100
+module_param(irq, int, 0444);
+MODULE_PARM_DESC(irq, "Interrupt (16)");
+#else
+module_param(io, int, 0444);
+MODULE_PARM_DESC(io, "I/O address base (0x3f8 or 0x2f8)");
+
+module_param(irq, int, 0444);
+MODULE_PARM_DESC(irq, "Interrupt (4 or 3)");
+
+module_param(threshold, int, 0444);
+MODULE_PARM_DESC(threshold, "space detection threshold (3)");
+#endif
+
+module_param(debug, bool, 0644);
+MODULE_PARM_DESC(debug, "Enable debugging messages");
+
+EXPORT_NO_SYMBOLS;
+
+#endif /* MODULE */
diff -urN linux-2.6.10-rc3-mm1/include/linux/kcompat.h linux-2.6.10-rc3-no2/include/linux/kcompat.h
--- linux-2.6.10-rc3-mm1/include/linux/kcompat.h	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.10-rc3-no2/include/linux/kcompat.h	2004-12-22 19:35:16.474743550 -0500
@@ -0,0 +1,153 @@
+/*      $Id: kcompat.h,v 5.9 2004/09/05 16:48:48 lirc Exp $      */
+
+#ifndef _KCOMPAT_H
+#define _KCOMPAT_H
+
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+#define LIRC_HAVE_DEVFS
+#define LIRC_HAVE_DEVFS_26
+#define LIRC_HAVE_SYSFS
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+#define LIRC_HAVE_DEVFS
+#define LIRC_HAVE_DEVFS_24
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 3, 0)
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+static inline void del_timer_sync(struct timer_list * timerlist)
+{
+	start_bh_atomic();
+	del_timer(timerlist);
+	end_bh_atomic();
+}
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0)
+#ifdef daemonize
+#undef daemonize
+#endif
+#define daemonize(name) do {                                           \
+                                                                       \
+	lock_kernel();                                                 \
+	                                                               \
+	exit_mm(current);                                              \
+	exit_files(current);                                           \
+	exit_fs(current);                                              \
+	current->session = 1;                                          \
+	current->pgrp = 1;                                             \
+	current->euid = 0;                                             \
+	current->tty = NULL;                                           \
+	sigfillset(&current->blocked);                                 \
+	                                                               \
+	strcpy(current->comm, name);                                   \
+	                                                               \
+	unlock_kernel();                                               \
+                                                                       \
+} while (0)
+
+/* Not sure when this was introduced, sometime during 2.5.X */
+#define MODULE_PARM_int(x) MODULE_PARM(x, "i")
+#define MODULE_PARM_bool(x) MODULE_PARM(x, "i")
+#define MODULE_PARM_long(x) MODULE_PARM(x, "l")
+#define module_param(x,y,z) MODULE_PARM_##y(x)
+#else
+#include <linux/moduleparam.h>
+#endif /* Linux < 2.6.0 */
+
+#ifdef LIRC_HAVE_DEVFS_24
+#ifdef register_chrdev
+#undef register_chrdev
+#endif
+#define register_chrdev devfs_register_chrdev
+#ifdef unregister_chrdev
+#undef unregister_chrdev
+#endif
+#define unregister_chrdev devfs_unregister_chrdev
+#endif /* DEVFS 2.4 */
+
+#ifndef LIRC_HAVE_SYSFS
+#define class_simple_destroy(x) do { } while(0)
+#define class_simple_create(x,y) NULL
+#define class_simple_device_remove(x) do { } while(0)
+#define class_simple_device_add(x, y, z, xx, yy) 0
+#define IS_ERR(x) 0
+struct class_simple 
+{
+	int notused;
+};	
+#endif /* No SYSFS */
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0)
+#define KERNEL_2_5
+
+/*
+ * Recent kernels should handle some of this autmatically by 
+ * increasing/decreasing use count when a dependant module is 
+ * loaded/unloaded but we need to keep track when a chardev is 
+ * opened/closed.
+ */
+#ifdef MOD_INC_USE_COUNT
+#undef MOD_INC_USE_COUNT
+#endif
+#define MOD_INC_USE_COUNT try_module_get(THIS_MODULE)
+
+#ifdef MOD_DEC_USE_COUNT
+#undef MOD_DEC_USE_COUNT
+#endif
+#define MOD_DEC_USE_COUNT module_put(THIS_MODULE)
+
+#ifdef EXPORT_NO_SYMBOLS
+#undef EXPORT_NO_SYMBOLS
+#endif
+#define EXPORT_NO_SYMBOLS
+
+#endif /* Kernel >= 2.5.0 */
+
+#ifndef MODULE_LICENSE
+#define MODULE_LICENSE(x)
+#endif
+
+#ifndef MODULE_PARM_DESC
+#define MODULE_PARM_DESC(x,y)
+#endif
+
+#ifndef MODULE_ALIAS_CHARDEV_MAJOR
+#define MODULE_ALIAS_CHARDEV_MAJOR(x)
+#endif
+
+#ifndef MODULE_DEVICE_TABLE
+#define MODULE_DEVICE_TABLE(x,y)
+#endif
+
+#ifndef IRQ_RETVAL
+typedef void irqreturn_t;
+#define IRQ_NONE
+#define IRQ_HANDLED
+#define IRQ_RETVAL(x)
+#endif
+
+#ifndef MOD_IN_USE
+#ifdef CONFIG_MODULE_UNLOAD
+#define MOD_IN_USE module_refcount(THIS_MODULE)
+#else
+#error "LIRC modules currently require"
+#error "  'Loadable module support  --->  Module unloading'"
+#error "to be enabled in the kernel"
+#endif
+#endif
+
+#if !defined(local_irq_save)
+#define local_irq_save(flags) do{ save_flags(flags);cli(); } while(0)
+#endif
+#if !defined(local_irq_restore)
+#define local_irq_restore(flags) do{ restore_flags(flags); } while(0)
+#endif
+
+#if !defined(pci_pretty_name)
+#define pci_pretty_name(dev) ((dev)->name)
+#endif
+
+#endif /* _KCOMPAT_H */
diff -urN linux-2.6.10-rc3-mm1/include/linux/lirc.h linux-2.6.10-rc3-no2/include/linux/lirc.h
--- linux-2.6.10-rc3-mm1/include/linux/lirc.h	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.10-rc3-no2/include/linux/lirc.h	2004-12-22 19:35:16.479742855 -0500
@@ -0,0 +1,100 @@
+/*      $Id: lirc.h,v 5.8 2003/01/26 12:57:59 lirc Exp $      */
+
+#ifndef _LINUX_LIRC_H
+#define _LINUX_LIRC_H
+
+#if defined (__linux__)
+#include <asm/types.h>
+#include <linux/ioctl.h>
+#else
+#include <sys/types.h>
+typedef u_int32_t __u32;
+#endif
+
+#define PULSE_BIT  0x01000000
+#define PULSE_MASK 0x00FFFFFF
+
+typedef int lirc_t;
+
+/*
+ * lirc compatible hardware features
+ */
+
+
+#define LIRC_MODE2SEND(x) (x)
+#define LIRC_SEND2MODE(x) (x)
+#define LIRC_MODE2REC(x) ((x) << 16)
+#define LIRC_REC2MODE(x) ((x) >> 16)
+
+#define LIRC_MODE_RAW                  0x00000001
+#define LIRC_MODE_PULSE                0x00000002
+#define LIRC_MODE_MODE2                0x00000004
+#define LIRC_MODE_CODE                 0x00000008
+#define LIRC_MODE_LIRCCODE             0x00000010
+#define LIRC_MODE_STRING               0x00000020
+
+
+#define LIRC_CAN_SEND_RAW              LIRC_MODE2SEND(LIRC_MODE_RAW)
+#define LIRC_CAN_SEND_PULSE            LIRC_MODE2SEND(LIRC_MODE_PULSE)
+#define LIRC_CAN_SEND_MODE2            LIRC_MODE2SEND(LIRC_MODE_MODE2)
+#define LIRC_CAN_SEND_CODE             LIRC_MODE2SEND(LIRC_MODE_CODE)
+#define LIRC_CAN_SEND_LIRCCODE         LIRC_MODE2SEND(LIRC_MODE_LIRCCODE)
+#define LIRC_CAN_SEND_STRING           LIRC_MODE2SEND(LIRC_MODE_STRING)
+
+#define LIRC_CAN_SEND_MASK             0x0000003f
+
+#define LIRC_CAN_SET_SEND_CARRIER      0x00000100
+#define LIRC_CAN_SET_SEND_DUTY_CYCLE   0x00000200
+
+#define LIRC_CAN_REC_RAW               LIRC_MODE2REC(LIRC_MODE_RAW)
+#define LIRC_CAN_REC_PULSE             LIRC_MODE2REC(LIRC_MODE_PULSE)
+#define LIRC_CAN_REC_MODE2             LIRC_MODE2REC(LIRC_MODE_MODE2)
+#define LIRC_CAN_REC_CODE              LIRC_MODE2REC(LIRC_MODE_CODE)
+#define LIRC_CAN_REC_LIRCCODE          LIRC_MODE2REC(LIRC_MODE_LIRCCODE)
+#define LIRC_CAN_REC_STRING            LIRC_MODE2REC(LIRC_MODE_STRING)
+
+#define LIRC_CAN_REC_MASK              LIRC_MODE2REC(LIRC_CAN_SEND_MASK)
+
+#define LIRC_CAN_SET_REC_CARRIER       (LIRC_CAN_SET_SEND_CARRIER << 16)
+#define LIRC_CAN_SET_REC_DUTY_CYCLE    (LIRC_CAN_SET_SEND_DUTY_CYCLE << 16)
+
+#define LIRC_CAN_SET_REC_DUTY_CYCLE_RANGE 0x40000000
+#define LIRC_CAN_SET_REC_CARRIER_RANGE    0x80000000
+
+
+#define LIRC_CAN_SEND(x) ((x)&LIRC_CAN_SEND_MASK)
+#define LIRC_CAN_REC(x) ((x)&LIRC_CAN_REC_MASK)
+
+/*
+ * IOCTL commands for lirc driver
+ */
+
+#define LIRC_GET_FEATURES              _IOR('i', 0x00000000, __u32)
+
+#define LIRC_GET_SEND_MODE             _IOR('i', 0x00000001, __u32)
+#define LIRC_GET_REC_MODE              _IOR('i', 0x00000002, __u32)
+#define LIRC_GET_SEND_CARRIER          _IOR('i', 0x00000003, __u32)
+#define LIRC_GET_REC_CARRIER           _IOR('i', 0x00000004, __u32)
+#define LIRC_GET_SEND_DUTY_CYCLE       _IOR('i', 0x00000005, __u32)
+#define LIRC_GET_REC_DUTY_CYCLE        _IOR('i', 0x00000006, __u32)
+
+/* code length in bits, currently only for LIRC_MODE_LIRCCODE */
+#define LIRC_GET_LENGTH                _IOR('i', 0x0000000f, __u32)
+
+#define LIRC_SET_SEND_MODE             _IOW('i', 0x00000011, __u32)
+#define LIRC_SET_REC_MODE              _IOW('i', 0x00000012, __u32)
+/* Note: these can reset the according pulse_width */
+#define LIRC_SET_SEND_CARRIER          _IOW('i', 0x00000013, __u32)
+#define LIRC_SET_REC_CARRIER           _IOW('i', 0x00000014, __u32)
+#define LIRC_SET_SEND_DUTY_CYCLE       _IOW('i', 0x00000015, __u32)
+#define LIRC_SET_REC_DUTY_CYCLE        _IOW('i', 0x00000016, __u32)
+
+/* to set a range use
+   LIRC_SET_REC_DUTY_CYCLE_RANGE/LIRC_SET_REC_CARRIER_RANGE with the
+   lower bound first and later
+   LIRC_SET_REC_DUTY_CYCLE/LIRC_SET_REC_CARRIER with the upper bound */
+
+#define LIRC_SET_REC_DUTY_CYCLE_RANGE  _IOW('i', 0x0000001e, __u32)
+#define LIRC_SET_REC_CARRIER_RANGE     _IOW('i', 0x0000001f, __u32)
+
+#endif
