12191 lines
		
	
	
		
			309 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			12191 lines
		
	
	
		
			309 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
 drivers/staging/Kconfig                 |    2 +
 | 
						|
 drivers/staging/Makefile                |    1 +
 | 
						|
 drivers/staging/lirc/Kconfig            |  110 +++
 | 
						|
 drivers/staging/lirc/Makefile           |   19 +
 | 
						|
 drivers/staging/lirc/TODO               |    8 +
 | 
						|
 drivers/staging/lirc/lirc_bt829.c       |  383 +++++++++
 | 
						|
 drivers/staging/lirc/lirc_ene0100.c     |  646 ++++++++++++++
 | 
						|
 drivers/staging/lirc/lirc_ene0100.h     |  169 ++++
 | 
						|
 drivers/staging/lirc/lirc_i2c.c         |  536 ++++++++++++
 | 
						|
 drivers/staging/lirc/lirc_igorplugusb.c |  555 ++++++++++++
 | 
						|
 drivers/staging/lirc/lirc_imon.c        | 1058 +++++++++++++++++++++++
 | 
						|
 drivers/staging/lirc/lirc_it87.c        | 1019 +++++++++++++++++++++++
 | 
						|
 drivers/staging/lirc/lirc_it87.h        |  116 +++
 | 
						|
 drivers/staging/lirc/lirc_ite8709.c     |  542 ++++++++++++
 | 
						|
 drivers/staging/lirc/lirc_parallel.c    |  705 ++++++++++++++++
 | 
						|
 drivers/staging/lirc/lirc_parallel.h    |   26 +
 | 
						|
 drivers/staging/lirc/lirc_sasem.c       |  933 +++++++++++++++++++++
 | 
						|
 drivers/staging/lirc/lirc_serial.c      | 1313 +++++++++++++++++++++++++++++
 | 
						|
 drivers/staging/lirc/lirc_sir.c         | 1282 ++++++++++++++++++++++++++++
 | 
						|
 drivers/staging/lirc/lirc_streamzap.c   |  821 ++++++++++++++++++
 | 
						|
 drivers/staging/lirc/lirc_ttusbir.c     |  397 +++++++++
 | 
						|
 drivers/staging/lirc/lirc_zilog.c       | 1387 +++++++++++++++++++++++++++++++
 | 
						|
 22 files changed, 12028 insertions(+), 0 deletions(-)
 | 
						|
 | 
						|
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
 | 
						|
index 984a754..9296517 100644
 | 
						|
--- a/drivers/staging/Kconfig
 | 
						|
+++ b/drivers/staging/Kconfig
 | 
						|
@@ -147,5 +147,7 @@ source "drivers/staging/mrst-touchscreen/Kconfig"
 | 
						|
 
 | 
						|
 source "drivers/staging/msm/Kconfig"
 | 
						|
 
 | 
						|
+source "drivers/staging/lirc/Kconfig"
 | 
						|
+
 | 
						|
 endif # !STAGING_EXCLUDE_BUILD
 | 
						|
 endif # STAGING
 | 
						|
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
 | 
						|
index 9fa2513..9e9b068 100644
 | 
						|
--- a/drivers/staging/Makefile
 | 
						|
+++ b/drivers/staging/Makefile
 | 
						|
@@ -54,3 +54,4 @@ obj-$(CONFIG_ADIS16255)		+= adis16255/
 | 
						|
 obj-$(CONFIG_FB_XGI)		+= xgifb/
 | 
						|
 obj-$(CONFIG_TOUCHSCREEN_MRSTOUCH)	+= mrst-touchscreen/
 | 
						|
 obj-$(CONFIG_MSM_STAGING)	+= msm/
 | 
						|
+obj-$(CONFIG_LIRC_STAGING)	+= lirc/
 | 
						|
diff --git a/drivers/staging/lirc/Kconfig b/drivers/staging/lirc/Kconfig
 | 
						|
new file mode 100644
 | 
						|
index 0000000..968c2ad
 | 
						|
--- /dev/null
 | 
						|
+++ b/drivers/staging/lirc/Kconfig
 | 
						|
@@ -0,0 +1,110 @@
 | 
						|
+#
 | 
						|
+# LIRC driver(s) configuration
 | 
						|
+#
 | 
						|
+menuconfig LIRC_STAGING
 | 
						|
+	bool "Linux Infrared Remote Control IR receiver/transmitter drivers"
 | 
						|
+	help
 | 
						|
+	  Say Y here, and all supported Linux Infrared Remote Control IR and
 | 
						|
+	  RF receiver and transmitter drivers will be displayed. When paired
 | 
						|
+	  with a remote control and the lirc daemon, the receiver drivers
 | 
						|
+	  allow control of your Linux system via remote control.
 | 
						|
+
 | 
						|
+if LIRC_STAGING
 | 
						|
+
 | 
						|
+config LIRC_BT829
 | 
						|
+        tristate "BT829 based hardware"
 | 
						|
+	depends on LIRC_STAGING
 | 
						|
+	help
 | 
						|
+	  Driver for the IR interface on BT829-based hardware
 | 
						|
+
 | 
						|
+config LIRC_ENE0100
 | 
						|
+	tristate "ENE KB3924/ENE0100 CIR Port Reciever"
 | 
						|
+	depends on LIRC_STAGING
 | 
						|
+	help
 | 
						|
+	  This is a driver for CIR port handled by ENE KB3924 embedded
 | 
						|
+	  controller found on some notebooks.
 | 
						|
+	  It appears on PNP list as ENE0100.
 | 
						|
+
 | 
						|
+config LIRC_I2C
 | 
						|
+	tristate "I2C Based IR Receivers"
 | 
						|
+	depends on LIRC_STAGING
 | 
						|
+	help
 | 
						|
+	  Driver for I2C-based IR receivers, such as those commonly
 | 
						|
+	  found onboard Hauppauge PVR-150/250/350 video capture cards
 | 
						|
+
 | 
						|
+config LIRC_IGORPLUGUSB
 | 
						|
+	tristate "Igor Cesko's USB IR Receiver"
 | 
						|
+	depends on LIRC_STAGING && USB
 | 
						|
+	help
 | 
						|
+	  Driver for Igor Cesko's USB IR Receiver
 | 
						|
+
 | 
						|
+config LIRC_IMON
 | 
						|
+	tristate "Legacy SoundGraph iMON Receiver and Display"
 | 
						|
+	depends on LIRC_STAGING
 | 
						|
+	help
 | 
						|
+	  Driver for the original SoundGraph iMON IR Receiver and Display
 | 
						|
+
 | 
						|
+	  Current generation iMON devices use the input layer imon driver.
 | 
						|
+
 | 
						|
+config LIRC_IT87
 | 
						|
+	tristate "ITE IT87XX CIR Port Receiver"
 | 
						|
+	depends on LIRC_STAGING
 | 
						|
+	help
 | 
						|
+	  Driver for the ITE IT87xx IR Receiver
 | 
						|
+
 | 
						|
+config LIRC_ITE8709
 | 
						|
+	tristate "ITE8709 CIR Port Receiver"
 | 
						|
+	depends on LIRC_STAGING && PNP
 | 
						|
+	help
 | 
						|
+	  Driver for the ITE8709 IR Receiver
 | 
						|
+
 | 
						|
+config LIRC_PARALLEL
 | 
						|
+	tristate "Homebrew Parallel Port Receiver"
 | 
						|
+	depends on LIRC_STAGING && !SMP
 | 
						|
+	help
 | 
						|
+	  Driver for Homebrew Parallel Port Receivers
 | 
						|
+
 | 
						|
+config LIRC_SASEM
 | 
						|
+	tristate "Sasem USB IR Remote"
 | 
						|
+	depends on LIRC_STAGING
 | 
						|
+	help
 | 
						|
+	  Driver for the Sasem OnAir Remocon-V or Dign HV5 HTPC IR/VFD Module
 | 
						|
+
 | 
						|
+config LIRC_SERIAL
 | 
						|
+	tristate "Homebrew Serial Port Receiver"
 | 
						|
+	depends on LIRC_STAGING
 | 
						|
+	help
 | 
						|
+	  Driver for Homebrew Serial Port Receivers
 | 
						|
+
 | 
						|
+config LIRC_SERIAL_TRANSMITTER
 | 
						|
+	bool "Serial Port Transmitter"
 | 
						|
+	default y
 | 
						|
+	depends on LIRC_SERIAL
 | 
						|
+	help
 | 
						|
+	  Serial Port Transmitter support
 | 
						|
+
 | 
						|
+config LIRC_SIR
 | 
						|
+	tristate "Built-in SIR IrDA port"
 | 
						|
+	depends on LIRC_STAGING
 | 
						|
+	help
 | 
						|
+	  Driver for the SIR IrDA port
 | 
						|
+
 | 
						|
+config LIRC_STREAMZAP
 | 
						|
+	tristate "Streamzap PC Receiver"
 | 
						|
+	depends on LIRC_STAGING
 | 
						|
+	help
 | 
						|
+	  Driver for the Streamzap PC Receiver
 | 
						|
+
 | 
						|
+config LIRC_TTUSBIR
 | 
						|
+	tristate "Technotrend USB IR Receiver"
 | 
						|
+	depends on LIRC_STAGING && USB
 | 
						|
+	help
 | 
						|
+	  Driver for the Technotrend USB IR Receiver
 | 
						|
+
 | 
						|
+config LIRC_ZILOG
 | 
						|
+	tristate "Zilog/Hauppauge IR Transmitter"
 | 
						|
+	depends on LIRC_STAGING
 | 
						|
+	help
 | 
						|
+	  Driver for the Zilog/Hauppauge IR Transmitter, found on
 | 
						|
+	  PVR-150/500, HVR-1200/1250/1700/1800, HD-PVR and other cards
 | 
						|
+endif
 | 
						|
diff --git a/drivers/staging/lirc/Makefile b/drivers/staging/lirc/Makefile
 | 
						|
new file mode 100644
 | 
						|
index 0000000..a019182
 | 
						|
--- /dev/null
 | 
						|
+++ b/drivers/staging/lirc/Makefile
 | 
						|
@@ -0,0 +1,19 @@
 | 
						|
+# Makefile for the lirc drivers.
 | 
						|
+#
 | 
						|
+
 | 
						|
+# Each configuration option enables a list of files.
 | 
						|
+
 | 
						|
+obj-$(CONFIG_LIRC_BT829)	+= lirc_bt829.o
 | 
						|
+obj-$(CONFIG_LIRC_ENE0100)	+= lirc_ene0100.o
 | 
						|
+obj-$(CONFIG_LIRC_I2C)		+= lirc_i2c.o
 | 
						|
+obj-$(CONFIG_LIRC_IGORPLUGUSB)	+= lirc_igorplugusb.o
 | 
						|
+obj-$(CONFIG_LIRC_IMON)		+= lirc_imon.o
 | 
						|
+obj-$(CONFIG_LIRC_IT87)		+= lirc_it87.o
 | 
						|
+obj-$(CONFIG_LIRC_ITE8709)	+= lirc_ite8709.o
 | 
						|
+obj-$(CONFIG_LIRC_PARALLEL)	+= lirc_parallel.o
 | 
						|
+obj-$(CONFIG_LIRC_SASEM)	+= lirc_sasem.o
 | 
						|
+obj-$(CONFIG_LIRC_SERIAL)	+= lirc_serial.o
 | 
						|
+obj-$(CONFIG_LIRC_SIR)		+= lirc_sir.o
 | 
						|
+obj-$(CONFIG_LIRC_STREAMZAP)	+= lirc_streamzap.o
 | 
						|
+obj-$(CONFIG_LIRC_TTUSBIR)	+= lirc_ttusbir.o
 | 
						|
+obj-$(CONFIG_LIRC_ZILOG)	+= lirc_zilog.o
 | 
						|
diff --git a/drivers/staging/lirc/TODO b/drivers/staging/lirc/TODO
 | 
						|
new file mode 100644
 | 
						|
index 0000000..b6cb593
 | 
						|
--- /dev/null
 | 
						|
+++ b/drivers/staging/lirc/TODO
 | 
						|
@@ -0,0 +1,8 @@
 | 
						|
+- All drivers should either be ported to ir-core, or dropped entirely
 | 
						|
+  (see drivers/media/IR/mceusb.c vs. lirc_mceusb.c in lirc cvs for an
 | 
						|
+  example of a previously completed port).
 | 
						|
+
 | 
						|
+Please send patches to:
 | 
						|
+Jarod Wilson <jarod@wilsonet.com>
 | 
						|
+Greg Kroah-Hartman <greg@kroah.com>
 | 
						|
+
 | 
						|
diff --git a/drivers/staging/lirc/lirc_bt829.c b/drivers/staging/lirc/lirc_bt829.c
 | 
						|
new file mode 100644
 | 
						|
index 0000000..d0f34b5
 | 
						|
--- /dev/null
 | 
						|
+++ b/drivers/staging/lirc/lirc_bt829.c
 | 
						|
@@ -0,0 +1,383 @@
 | 
						|
+/*
 | 
						|
+ * 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/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 <media/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
 | 
						|
+
 | 
						|
+#define DRIVER_NAME "lirc_bt829"
 | 
						|
+
 | 
						|
+static int debug;
 | 
						|
+#define dprintk(fmt, args...)						 \
 | 
						|
+	do {								 \
 | 
						|
+		if (debug)						 \
 | 
						|
+			printk(KERN_DEBUG DRIVER_NAME ": "fmt, ## args); \
 | 
						|
+	} while (0)
 | 
						|
+
 | 
						|
+static int atir_minor;
 | 
						|
+static unsigned long pci_addr_phys;
 | 
						|
+static unsigned char *pci_addr_lin;
 | 
						|
+
 | 
						|
+static struct lirc_driver atir_driver;
 | 
						|
+
 | 
						|
+static struct pci_dev *do_pci_probe(void)
 | 
						|
+{
 | 
						|
+	struct pci_dev *my_dev;
 | 
						|
+	my_dev = pci_get_device(PCI_VENDOR_ID_ATI,
 | 
						|
+				PCI_DEVICE_ID_ATI_264VT, NULL);
 | 
						|
+	if (my_dev) {
 | 
						|
+		printk(KERN_ERR DRIVER_NAME ": Using device: %s\n",
 | 
						|
+		       pci_name(my_dev));
 | 
						|
+		pci_addr_phys = 0;
 | 
						|
+		if (my_dev->resource[0].flags & IORESOURCE_MEM) {
 | 
						|
+			pci_addr_phys = my_dev->resource[0].start;
 | 
						|
+			printk(KERN_INFO DRIVER_NAME ": memory at 0x%08X \n",
 | 
						|
+			       (unsigned int)pci_addr_phys);
 | 
						|
+		}
 | 
						|
+		if (pci_addr_phys == 0) {
 | 
						|
+			printk(KERN_ERR DRIVER_NAME ": no memory resource ?\n");
 | 
						|
+			return NULL;
 | 
						|
+		}
 | 
						|
+	} else {
 | 
						|
+		printk(KERN_ERR DRIVER_NAME ": pci_probe failed\n");
 | 
						|
+		return NULL;
 | 
						|
+	}
 | 
						|
+	return my_dev;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int atir_add_to_buf(void *data, struct lirc_buffer *buf)
 | 
						|
+{
 | 
						|
+	unsigned char key;
 | 
						|
+	int status;
 | 
						|
+	status = poll_main();
 | 
						|
+	key = (status >> 8) & 0xFF;
 | 
						|
+	if (status & 0xFF) {
 | 
						|
+		dprintk("reading key %02X\n", key);
 | 
						|
+		lirc_buffer_write(buf, &key);
 | 
						|
+		return 0;
 | 
						|
+	}
 | 
						|
+	return -ENODATA;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int atir_set_use_inc(void *data)
 | 
						|
+{
 | 
						|
+	dprintk("driver is opened\n");
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void atir_set_use_dec(void *data)
 | 
						|
+{
 | 
						|
+	dprintk("driver is closed\n");
 | 
						|
+}
 | 
						|
+
 | 
						|
+int init_module(void)
 | 
						|
+{
 | 
						|
+	struct pci_dev *pdev;
 | 
						|
+
 | 
						|
+	pdev = do_pci_probe();
 | 
						|
+	if (pdev == NULL)
 | 
						|
+		return 1;
 | 
						|
+
 | 
						|
+	if (!atir_init_start())
 | 
						|
+		return 1;
 | 
						|
+
 | 
						|
+	strcpy(atir_driver.name, "ATIR");
 | 
						|
+	atir_driver.minor       = -1;
 | 
						|
+	atir_driver.code_length = 8;
 | 
						|
+	atir_driver.sample_rate = 10;
 | 
						|
+	atir_driver.data        = 0;
 | 
						|
+	atir_driver.add_to_buf  = atir_add_to_buf;
 | 
						|
+	atir_driver.set_use_inc = atir_set_use_inc;
 | 
						|
+	atir_driver.set_use_dec = atir_set_use_dec;
 | 
						|
+	atir_driver.dev         = &pdev->dev;
 | 
						|
+	atir_driver.owner       = THIS_MODULE;
 | 
						|
+
 | 
						|
+	atir_minor = lirc_register_driver(&atir_driver);
 | 
						|
+	if (atir_minor < 0) {
 | 
						|
+		printk(KERN_ERR DRIVER_NAME ": failed to register driver!\n");
 | 
						|
+		return atir_minor;
 | 
						|
+	}
 | 
						|
+	dprintk("driver is registered on minor %d\n", atir_minor);
 | 
						|
+
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+
 | 
						|
+void cleanup_module(void)
 | 
						|
+{
 | 
						|
+	lirc_unregister_driver(atir_minor);
 | 
						|
+}
 | 
						|
+
 | 
						|
+
 | 
						|
+static int atir_init_start(void)
 | 
						|
+{
 | 
						|
+	pci_addr_lin = ioremap(pci_addr_phys + DATA_PCI_OFF, 0x400);
 | 
						|
+	if (pci_addr_lin == 0) {
 | 
						|
+		printk(KERN_INFO DRIVER_NAME ": pci mem must be mapped\n");
 | 
						|
+		return 0;
 | 
						|
+	}
 | 
						|
+	return 1;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void cycle_delay(int cycle)
 | 
						|
+{
 | 
						|
+	udelay(WAIT_CYCLE*cycle);
 | 
						|
+}
 | 
						|
+
 | 
						|
+
 | 
						|
+static int poll_main()
 | 
						|
+{
 | 
						|
+	unsigned char status_high, status_low;
 | 
						|
+
 | 
						|
+	do_i2c_start();
 | 
						|
+
 | 
						|
+	seems_wr_byte(0xAA);
 | 
						|
+	seems_wr_byte(0x01);
 | 
						|
+
 | 
						|
+	do_i2c_start();
 | 
						|
+
 | 
						|
+	seems_wr_byte(0xAB);
 | 
						|
+
 | 
						|
+	status_low = seems_rd_byte();
 | 
						|
+	status_high = seems_rd_byte();
 | 
						|
+
 | 
						|
+	do_i2c_stop();
 | 
						|
+
 | 
						|
+	return (status_high << 8) | status_low;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void do_i2c_start(void)
 | 
						|
+{
 | 
						|
+	do_set_bits(3);
 | 
						|
+	cycle_delay(4);
 | 
						|
+
 | 
						|
+	do_set_bits(1);
 | 
						|
+	cycle_delay(7);
 | 
						|
+
 | 
						|
+	do_set_bits(0);
 | 
						|
+	cycle_delay(2);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void do_i2c_stop(void)
 | 
						|
+{
 | 
						|
+	unsigned char bits;
 | 
						|
+	bits =  do_get_bits() & 0xFD;
 | 
						|
+	do_set_bits(bits);
 | 
						|
+	cycle_delay(1);
 | 
						|
+
 | 
						|
+	bits |= 1;
 | 
						|
+	do_set_bits(bits);
 | 
						|
+	cycle_delay(2);
 | 
						|
+
 | 
						|
+	bits |= 2;
 | 
						|
+	do_set_bits(bits);
 | 
						|
+	bits = 3;
 | 
						|
+	do_set_bits(bits);
 | 
						|
+	cycle_delay(2);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void seems_wr_byte(unsigned char value)
 | 
						|
+{
 | 
						|
+	int i;
 | 
						|
+	unsigned char reg;
 | 
						|
+
 | 
						|
+	reg = do_get_bits();
 | 
						|
+	for (i = 0; i < 8; i++) {
 | 
						|
+		if (value & 0x80)
 | 
						|
+			reg |= 0x02;
 | 
						|
+		else
 | 
						|
+			reg &= 0xFD;
 | 
						|
+
 | 
						|
+		do_set_bits(reg);
 | 
						|
+		cycle_delay(1);
 | 
						|
+
 | 
						|
+		reg |= 1;
 | 
						|
+		do_set_bits(reg);
 | 
						|
+		cycle_delay(1);
 | 
						|
+
 | 
						|
+		reg &= 0xFE;
 | 
						|
+		do_set_bits(reg);
 | 
						|
+		cycle_delay(1);
 | 
						|
+		value <<= 1;
 | 
						|
+	}
 | 
						|
+	cycle_delay(2);
 | 
						|
+
 | 
						|
+	reg |= 2;
 | 
						|
+	do_set_bits(reg);
 | 
						|
+
 | 
						|
+	reg |= 1;
 | 
						|
+	do_set_bits(reg);
 | 
						|
+
 | 
						|
+	cycle_delay(1);
 | 
						|
+	do_get_bits();
 | 
						|
+
 | 
						|
+	reg &= 0xFE;
 | 
						|
+	do_set_bits(reg);
 | 
						|
+	cycle_delay(3);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static unsigned char seems_rd_byte(void)
 | 
						|
+{
 | 
						|
+	int i;
 | 
						|
+	int rd_byte;
 | 
						|
+	unsigned char bits_2, bits_1;
 | 
						|
+
 | 
						|
+	bits_1 = do_get_bits() | 2;
 | 
						|
+	do_set_bits(bits_1);
 | 
						|
+
 | 
						|
+	rd_byte = 0;
 | 
						|
+	for (i = 0; i < 8; i++) {
 | 
						|
+		bits_1 &= 0xFE;
 | 
						|
+		do_set_bits(bits_1);
 | 
						|
+		cycle_delay(2);
 | 
						|
+
 | 
						|
+		bits_1 |= 1;
 | 
						|
+		do_set_bits(bits_1);
 | 
						|
+		cycle_delay(1);
 | 
						|
+
 | 
						|
+		bits_2 = do_get_bits();
 | 
						|
+		if (bits_2 & 2)
 | 
						|
+			rd_byte |= 1;
 | 
						|
+
 | 
						|
+		rd_byte <<= 1;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	bits_1 = 0;
 | 
						|
+	if (bits_2 == 0)
 | 
						|
+		bits_1 |= 2;
 | 
						|
+
 | 
						|
+	do_set_bits(bits_1);
 | 
						|
+	cycle_delay(2);
 | 
						|
+
 | 
						|
+	bits_1 |= 1;
 | 
						|
+	do_set_bits(bits_1);
 | 
						|
+	cycle_delay(3);
 | 
						|
+
 | 
						|
+	bits_1 &= 0xFE;
 | 
						|
+	do_set_bits(bits_1);
 | 
						|
+	cycle_delay(2);
 | 
						|
+
 | 
						|
+	rd_byte >>= 1;
 | 
						|
+	rd_byte &= 0xFF;
 | 
						|
+	return rd_byte;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void do_set_bits(unsigned char new_bits)
 | 
						|
+{
 | 
						|
+	int reg_val;
 | 
						|
+	reg_val = read_index(0x34);
 | 
						|
+	if (new_bits & 2) {
 | 
						|
+		reg_val &= 0xFFFFFFDF;
 | 
						|
+		reg_val |= 1;
 | 
						|
+	} else {
 | 
						|
+		reg_val &= 0xFFFFFFFE;
 | 
						|
+		reg_val |= 0x20;
 | 
						|
+	}
 | 
						|
+	reg_val |= 0x10;
 | 
						|
+	write_index(0x34, reg_val);
 | 
						|
+
 | 
						|
+	reg_val = read_index(0x31);
 | 
						|
+	if (new_bits & 1)
 | 
						|
+		reg_val |= 0x1000000;
 | 
						|
+	else
 | 
						|
+		reg_val &= 0xFEFFFFFF;
 | 
						|
+
 | 
						|
+	reg_val |= 0x8000000;
 | 
						|
+	write_index(0x31, reg_val);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static unsigned char do_get_bits(void)
 | 
						|
+{
 | 
						|
+	unsigned char bits;
 | 
						|
+	int reg_val;
 | 
						|
+
 | 
						|
+	reg_val = read_index(0x34);
 | 
						|
+	reg_val |= 0x10;
 | 
						|
+	reg_val &= 0xFFFFFFDF;
 | 
						|
+	write_index(0x34, reg_val);
 | 
						|
+
 | 
						|
+	reg_val = read_index(0x34);
 | 
						|
+	bits = 0;
 | 
						|
+	if (reg_val & 8)
 | 
						|
+		bits |= 2;
 | 
						|
+	else
 | 
						|
+		bits &= 0xFD;
 | 
						|
+
 | 
						|
+	reg_val = read_index(0x31);
 | 
						|
+	if (reg_val & 0x1000000)
 | 
						|
+		bits |= 1;
 | 
						|
+	else
 | 
						|
+		bits &= 0xFE;
 | 
						|
+
 | 
						|
+	return bits;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static unsigned int read_index(unsigned char index)
 | 
						|
+{
 | 
						|
+	unsigned char *addr;
 | 
						|
+	unsigned int value;
 | 
						|
+	/*  addr = pci_addr_lin + DATA_PCI_OFF + ((index & 0xFF) << 2); */
 | 
						|
+	addr = pci_addr_lin + ((index & 0xFF) << 2);
 | 
						|
+	value = readl(addr);
 | 
						|
+	return value;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void write_index(unsigned char index, unsigned int reg_val)
 | 
						|
+{
 | 
						|
+	unsigned char *addr;
 | 
						|
+	addr = pci_addr_lin + ((index & 0xFF) << 2);
 | 
						|
+	writel(reg_val, addr);
 | 
						|
+}
 | 
						|
+
 | 
						|
+MODULE_AUTHOR("Froenchenko Leonid");
 | 
						|
+MODULE_DESCRIPTION("IR remote driver for bt829 based TV cards");
 | 
						|
+MODULE_LICENSE("GPL");
 | 
						|
+
 | 
						|
+module_param(debug, bool, S_IRUGO | S_IWUSR);
 | 
						|
+MODULE_PARM_DESC(debug, "Debug enabled or not");
 | 
						|
diff --git a/drivers/staging/lirc/lirc_ene0100.c b/drivers/staging/lirc/lirc_ene0100.c
 | 
						|
new file mode 100644
 | 
						|
index 0000000..a152c52
 | 
						|
--- /dev/null
 | 
						|
+++ b/drivers/staging/lirc/lirc_ene0100.c
 | 
						|
@@ -0,0 +1,646 @@
 | 
						|
+/*
 | 
						|
+ * driver for ENE KB3926 B/C/D CIR (also known as ENE0100)
 | 
						|
+ *
 | 
						|
+ * Copyright (C) 2009 Maxim Levitsky <maximlevitsky@gmail.com>
 | 
						|
+ *
 | 
						|
+ * This program is free software; you can redistribute it and/or
 | 
						|
+ * modify it under the terms of the GNU General Public License as
 | 
						|
+ * published by the Free Software Foundation; either version 2 of the
 | 
						|
+ * License, or (at your option) any later version.
 | 
						|
+ *
 | 
						|
+ * This program is distributed in the hope that it will be useful, but
 | 
						|
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
						|
+ * General Public License for more details.
 | 
						|
+ *
 | 
						|
+ * You should have received a copy of the GNU General Public License
 | 
						|
+ * along with this program; if not, write to the Free Software
 | 
						|
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 | 
						|
+ * USA
 | 
						|
+ */
 | 
						|
+
 | 
						|
+#include <linux/kernel.h>
 | 
						|
+#include <linux/module.h>
 | 
						|
+#include <linux/pnp.h>
 | 
						|
+#include <linux/io.h>
 | 
						|
+#include <linux/interrupt.h>
 | 
						|
+#include <linux/sched.h>
 | 
						|
+#include "lirc_ene0100.h"
 | 
						|
+
 | 
						|
+static int sample_period = 75;
 | 
						|
+static int enable_idle = 1;
 | 
						|
+static int enable_learning;
 | 
						|
+
 | 
						|
+static void ene_set_idle(struct ene_device *dev, int idle);
 | 
						|
+static void ene_set_inputs(struct ene_device *dev, int enable);
 | 
						|
+
 | 
						|
+/* read a hardware register */
 | 
						|
+static u8 ene_hw_read_reg(struct ene_device *dev, u16 reg)
 | 
						|
+{
 | 
						|
+	outb(reg >> 8, dev->hw_io + ENE_ADDR_HI);
 | 
						|
+	outb(reg & 0xFF, dev->hw_io + ENE_ADDR_LO);
 | 
						|
+	return inb(dev->hw_io + ENE_IO);
 | 
						|
+}
 | 
						|
+
 | 
						|
+/* write a hardware register */
 | 
						|
+static void ene_hw_write_reg(struct ene_device *dev, u16 reg, u8 value)
 | 
						|
+{
 | 
						|
+	outb(reg >> 8, dev->hw_io + ENE_ADDR_HI);
 | 
						|
+	outb(reg & 0xFF, dev->hw_io + ENE_ADDR_LO);
 | 
						|
+	outb(value, dev->hw_io + ENE_IO);
 | 
						|
+}
 | 
						|
+
 | 
						|
+/* change specific bits in hardware register */
 | 
						|
+static void ene_hw_write_reg_mask(struct ene_device *dev,
 | 
						|
+				  u16 reg, u8 value, u8 mask)
 | 
						|
+{
 | 
						|
+	u8 regvalue;
 | 
						|
+
 | 
						|
+	outb(reg >> 8, dev->hw_io + ENE_ADDR_HI);
 | 
						|
+	outb(reg & 0xFF, dev->hw_io + ENE_ADDR_LO);
 | 
						|
+
 | 
						|
+	regvalue = inb(dev->hw_io + ENE_IO) & ~mask;
 | 
						|
+	regvalue |= (value & mask);
 | 
						|
+	outb(regvalue, dev->hw_io + ENE_IO);
 | 
						|
+}
 | 
						|
+
 | 
						|
+/* read irq status and ack it */
 | 
						|
+static int ene_hw_irq_status(struct ene_device *dev, int *buffer_pointer)
 | 
						|
+{
 | 
						|
+	u8 irq_status;
 | 
						|
+	u8 fw_flags1, fw_flags2;
 | 
						|
+
 | 
						|
+	fw_flags2 = ene_hw_read_reg(dev, ENE_FW2);
 | 
						|
+
 | 
						|
+	if (buffer_pointer)
 | 
						|
+		*buffer_pointer = 4 * (fw_flags2 & ENE_FW2_BUF_HIGH);
 | 
						|
+
 | 
						|
+	if (dev->hw_revision < ENE_HW_C) {
 | 
						|
+		irq_status = ene_hw_read_reg(dev, ENEB_IRQ_STATUS);
 | 
						|
+
 | 
						|
+		if (!(irq_status & ENEB_IRQ_STATUS_IR))
 | 
						|
+			return 0;
 | 
						|
+		ene_hw_write_reg(dev, ENEB_IRQ_STATUS,
 | 
						|
+				 irq_status & ~ENEB_IRQ_STATUS_IR);
 | 
						|
+
 | 
						|
+		/* rev B support only recieving */
 | 
						|
+		return ENE_IRQ_RX;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	irq_status = ene_hw_read_reg(dev, ENEC_IRQ);
 | 
						|
+
 | 
						|
+	if (!(irq_status & ENEC_IRQ_STATUS))
 | 
						|
+		return 0;
 | 
						|
+
 | 
						|
+	/* original driver does that twice - a workaround ? */
 | 
						|
+	ene_hw_write_reg(dev, ENEC_IRQ, irq_status & ~ENEC_IRQ_STATUS);
 | 
						|
+	ene_hw_write_reg(dev, ENEC_IRQ, irq_status & ~ENEC_IRQ_STATUS);
 | 
						|
+
 | 
						|
+	/* clear unknown flag in F8F9 */
 | 
						|
+	if (fw_flags2 & ENE_FW2_IRQ_CLR)
 | 
						|
+		ene_hw_write_reg(dev, ENE_FW2, fw_flags2 & ~ENE_FW2_IRQ_CLR);
 | 
						|
+
 | 
						|
+	/* check if this is a TX interrupt */
 | 
						|
+	fw_flags1 = ene_hw_read_reg(dev, ENE_FW1);
 | 
						|
+
 | 
						|
+	if (fw_flags1 & ENE_FW1_TXIRQ) {
 | 
						|
+		ene_hw_write_reg(dev, ENE_FW1, fw_flags1 & ~ENE_FW1_TXIRQ);
 | 
						|
+		return ENE_IRQ_TX;
 | 
						|
+	} else
 | 
						|
+		return ENE_IRQ_RX;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int ene_hw_detect(struct ene_device *dev)
 | 
						|
+{
 | 
						|
+	u8 chip_major, chip_minor;
 | 
						|
+	u8 hw_revision, old_ver;
 | 
						|
+	u8 tmp;
 | 
						|
+	u8 fw_capabilities;
 | 
						|
+
 | 
						|
+	tmp = ene_hw_read_reg(dev, ENE_HW_UNK);
 | 
						|
+	ene_hw_write_reg(dev, ENE_HW_UNK, tmp & ~ENE_HW_UNK_CLR);
 | 
						|
+
 | 
						|
+	chip_major = ene_hw_read_reg(dev, ENE_HW_VER_MAJOR);
 | 
						|
+	chip_minor = ene_hw_read_reg(dev, ENE_HW_VER_MINOR);
 | 
						|
+
 | 
						|
+	ene_hw_write_reg(dev, ENE_HW_UNK, tmp);
 | 
						|
+	hw_revision = ene_hw_read_reg(dev, ENE_HW_VERSION);
 | 
						|
+	old_ver = ene_hw_read_reg(dev, ENE_HW_VER_OLD);
 | 
						|
+
 | 
						|
+	if (hw_revision == 0xFF) {
 | 
						|
+
 | 
						|
+		ene_printk(KERN_WARNING, "device seems to be disabled\n");
 | 
						|
+		ene_printk(KERN_WARNING,
 | 
						|
+			"send a mail to lirc-list@lists.sourceforge.net\n");
 | 
						|
+		ene_printk(KERN_WARNING, "please attach output of acpidump\n");
 | 
						|
+
 | 
						|
+		return -ENODEV;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	if (chip_major == 0x33) {
 | 
						|
+		ene_printk(KERN_WARNING, "chips 0x33xx aren't supported yet\n");
 | 
						|
+		return -ENODEV;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	if (chip_major == 0x39 && chip_minor == 0x26 && hw_revision == 0xC0) {
 | 
						|
+		dev->hw_revision = ENE_HW_C;
 | 
						|
+		ene_printk(KERN_WARNING,
 | 
						|
+		       "KB3926C detected, driver support is not complete!\n");
 | 
						|
+
 | 
						|
+	} else if (old_ver == 0x24 && hw_revision == 0xC0) {
 | 
						|
+		dev->hw_revision = ENE_HW_B;
 | 
						|
+		ene_printk(KERN_NOTICE, "KB3926B detected\n");
 | 
						|
+	} else {
 | 
						|
+		dev->hw_revision = ENE_HW_D;
 | 
						|
+		ene_printk(KERN_WARNING,
 | 
						|
+			"unknown ENE chip detected, assuming KB3926D\n");
 | 
						|
+		ene_printk(KERN_WARNING, "driver support incomplete");
 | 
						|
+
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	ene_printk(KERN_DEBUG, "chip is 0x%02x%02x - 0x%02x, 0x%02x\n",
 | 
						|
+		chip_major, chip_minor, old_ver, hw_revision);
 | 
						|
+
 | 
						|
+
 | 
						|
+	/* detect features hardware supports */
 | 
						|
+
 | 
						|
+	if (dev->hw_revision < ENE_HW_C)
 | 
						|
+		return 0;
 | 
						|
+
 | 
						|
+	fw_capabilities = ene_hw_read_reg(dev, ENE_FW2);
 | 
						|
+
 | 
						|
+	dev->hw_gpio40_learning = fw_capabilities & ENE_FW2_GP40_AS_LEARN;
 | 
						|
+	dev->hw_learning_and_tx_capable = fw_capabilities & ENE_FW2_LEARNING;
 | 
						|
+
 | 
						|
+	dev->hw_fan_as_normal_input = dev->hw_learning_and_tx_capable &&
 | 
						|
+	    fw_capabilities & ENE_FW2_FAN_AS_NRML_IN;
 | 
						|
+
 | 
						|
+	ene_printk(KERN_NOTICE, "hardware features:\n");
 | 
						|
+	ene_printk(KERN_NOTICE,
 | 
						|
+		"learning and tx %s, gpio40_learn %s, fan_in %s\n",
 | 
						|
+	       dev->hw_learning_and_tx_capable ? "on" : "off",
 | 
						|
+	       dev->hw_gpio40_learning ? "on" : "off",
 | 
						|
+	       dev->hw_fan_as_normal_input ? "on" : "off");
 | 
						|
+
 | 
						|
+	if (!dev->hw_learning_and_tx_capable && enable_learning)
 | 
						|
+		enable_learning = 0;
 | 
						|
+
 | 
						|
+	if (dev->hw_learning_and_tx_capable) {
 | 
						|
+		ene_printk(KERN_WARNING,
 | 
						|
+		"Device supports transmitting, but the driver doesn't\n");
 | 
						|
+		ene_printk(KERN_WARNING,
 | 
						|
+		"due to lack of hardware to test against.\n");
 | 
						|
+		ene_printk(KERN_WARNING,
 | 
						|
+		"Send a mail to: lirc-list@lists.sourceforge.net\n");
 | 
						|
+	}
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+/* hardware initialization */
 | 
						|
+static int ene_hw_init(void *data)
 | 
						|
+{
 | 
						|
+	u8 reg_value;
 | 
						|
+	struct ene_device *dev = (struct ene_device *)data;
 | 
						|
+	dev->in_use = 1;
 | 
						|
+
 | 
						|
+	if (dev->hw_revision < ENE_HW_C) {
 | 
						|
+		ene_hw_write_reg(dev, ENEB_IRQ, dev->irq << 1);
 | 
						|
+		ene_hw_write_reg(dev, ENEB_IRQ_UNK1, 0x01);
 | 
						|
+	} else {
 | 
						|
+		reg_value = ene_hw_read_reg(dev, ENEC_IRQ) & 0xF0;
 | 
						|
+		reg_value |= ENEC_IRQ_UNK_EN;
 | 
						|
+		reg_value &= ~ENEC_IRQ_STATUS;
 | 
						|
+		reg_value |= (dev->irq & ENEC_IRQ_MASK);
 | 
						|
+		ene_hw_write_reg(dev, ENEC_IRQ, reg_value);
 | 
						|
+		ene_hw_write_reg(dev, ENE_TX_UNK1, 0x63);
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	ene_hw_write_reg(dev, ENE_CIR_CONF2, 0x00);
 | 
						|
+	ene_set_inputs(dev, enable_learning);
 | 
						|
+
 | 
						|
+	/* set sampling period */
 | 
						|
+	ene_hw_write_reg(dev, ENE_CIR_SAMPLE_PERIOD, sample_period);
 | 
						|
+
 | 
						|
+	/* ack any pending irqs - just in case */
 | 
						|
+	ene_hw_irq_status(dev, NULL);
 | 
						|
+
 | 
						|
+	/* enter idle mode */
 | 
						|
+	ene_set_idle(dev, 1);
 | 
						|
+
 | 
						|
+	/* enable firmware bits */
 | 
						|
+	ene_hw_write_reg_mask(dev, ENE_FW1,
 | 
						|
+			      ENE_FW1_ENABLE | ENE_FW1_IRQ,
 | 
						|
+			      ENE_FW1_ENABLE | ENE_FW1_IRQ);
 | 
						|
+	/* clear stats */
 | 
						|
+	dev->sample = 0;
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+/* this enables gpio40 signal, used if connected to wide band input*/
 | 
						|
+static void ene_enable_gpio40(struct ene_device *dev, int enable)
 | 
						|
+{
 | 
						|
+	ene_hw_write_reg_mask(dev, ENE_CIR_CONF1, enable ?
 | 
						|
+			      0 : ENE_CIR_CONF2_GPIO40DIS,
 | 
						|
+			      ENE_CIR_CONF2_GPIO40DIS);
 | 
						|
+}
 | 
						|
+
 | 
						|
+/* this enables the classic sampler */
 | 
						|
+static void ene_enable_normal_recieve(struct ene_device *dev, int enable)
 | 
						|
+{
 | 
						|
+	ene_hw_write_reg(dev, ENE_CIR_CONF1, enable ? ENE_CIR_CONF1_ADC_ON : 0);
 | 
						|
+}
 | 
						|
+
 | 
						|
+/* this enables recieve via fan input */
 | 
						|
+static void ene_enable_fan_recieve(struct ene_device *dev, int enable)
 | 
						|
+{
 | 
						|
+	if (!enable)
 | 
						|
+		ene_hw_write_reg(dev, ENE_FAN_AS_IN1, 0);
 | 
						|
+	else {
 | 
						|
+		ene_hw_write_reg(dev, ENE_FAN_AS_IN1, ENE_FAN_AS_IN1_EN);
 | 
						|
+		ene_hw_write_reg(dev, ENE_FAN_AS_IN2, ENE_FAN_AS_IN2_EN);
 | 
						|
+	}
 | 
						|
+	dev->fan_input_inuse = enable;
 | 
						|
+}
 | 
						|
+
 | 
						|
+/* determine which input to use*/
 | 
						|
+static void ene_set_inputs(struct ene_device *dev, int learning_enable)
 | 
						|
+{
 | 
						|
+	ene_enable_normal_recieve(dev, 1);
 | 
						|
+
 | 
						|
+	/* old hardware doesn't support learning mode for sure */
 | 
						|
+	if (dev->hw_revision <= ENE_HW_B)
 | 
						|
+		return;
 | 
						|
+
 | 
						|
+	/* reciever not learning capable, still set gpio40 correctly */
 | 
						|
+	if (!dev->hw_learning_and_tx_capable) {
 | 
						|
+		ene_enable_gpio40(dev, !dev->hw_gpio40_learning);
 | 
						|
+		return;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	/* enable learning mode */
 | 
						|
+	if (learning_enable) {
 | 
						|
+		ene_enable_gpio40(dev, dev->hw_gpio40_learning);
 | 
						|
+
 | 
						|
+		/* fan input is not used for learning */
 | 
						|
+		if (dev->hw_fan_as_normal_input)
 | 
						|
+			ene_enable_fan_recieve(dev, 0);
 | 
						|
+
 | 
						|
+	/* disable learning mode */
 | 
						|
+	} else {
 | 
						|
+		if (dev->hw_fan_as_normal_input) {
 | 
						|
+			ene_enable_fan_recieve(dev, 1);
 | 
						|
+			ene_enable_normal_recieve(dev, 0);
 | 
						|
+		} else
 | 
						|
+			ene_enable_gpio40(dev, !dev->hw_gpio40_learning);
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	/* set few additional settings for this mode */
 | 
						|
+	ene_hw_write_reg_mask(dev, ENE_CIR_CONF1, learning_enable ?
 | 
						|
+			      ENE_CIR_CONF1_LEARN1 : 0, ENE_CIR_CONF1_LEARN1);
 | 
						|
+
 | 
						|
+	ene_hw_write_reg_mask(dev, ENE_CIR_CONF2, learning_enable ?
 | 
						|
+			      ENE_CIR_CONF2_LEARN2 : 0, ENE_CIR_CONF2_LEARN2);
 | 
						|
+}
 | 
						|
+
 | 
						|
+/* deinitialization */
 | 
						|
+static void ene_hw_deinit(void *data)
 | 
						|
+{
 | 
						|
+	struct ene_device *dev = (struct ene_device *)data;
 | 
						|
+
 | 
						|
+	/* disable samplers */
 | 
						|
+	ene_enable_normal_recieve(dev, 0);
 | 
						|
+
 | 
						|
+	if (dev->hw_fan_as_normal_input)
 | 
						|
+		ene_enable_fan_recieve(dev, 0);
 | 
						|
+
 | 
						|
+	/* disable hardware IRQ and firmware flag */
 | 
						|
+	ene_hw_write_reg_mask(dev, ENE_FW1, 0, ENE_FW1_ENABLE | ENE_FW1_IRQ);
 | 
						|
+
 | 
						|
+	ene_set_idle(dev, 1);
 | 
						|
+	dev->in_use = 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+/*  sends current sample to userspace */
 | 
						|
+static void send_sample(struct ene_device *dev)
 | 
						|
+{
 | 
						|
+	int value = abs(dev->sample) & PULSE_MASK;
 | 
						|
+
 | 
						|
+	if (dev->sample > 0)
 | 
						|
+		value |= PULSE_BIT;
 | 
						|
+
 | 
						|
+	if (!lirc_buffer_full(dev->lirc_driver->rbuf)) {
 | 
						|
+		lirc_buffer_write(dev->lirc_driver->rbuf, (void *)&value);
 | 
						|
+		wake_up(&dev->lirc_driver->rbuf->wait_poll);
 | 
						|
+	}
 | 
						|
+	dev->sample = 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+/*  this updates current sample */
 | 
						|
+static void update_sample(struct ene_device *dev, int sample)
 | 
						|
+{
 | 
						|
+	if (!dev->sample)
 | 
						|
+		dev->sample = sample;
 | 
						|
+	else if (same_sign(dev->sample, sample))
 | 
						|
+		dev->sample += sample;
 | 
						|
+	else {
 | 
						|
+		send_sample(dev);
 | 
						|
+		dev->sample = sample;
 | 
						|
+	}
 | 
						|
+}
 | 
						|
+
 | 
						|
+/* enable or disable idle mode */
 | 
						|
+static void ene_set_idle(struct ene_device *dev, int idle)
 | 
						|
+{
 | 
						|
+	struct timeval now;
 | 
						|
+	int disable = idle && enable_idle && (dev->hw_revision < ENE_HW_C);
 | 
						|
+
 | 
						|
+	ene_hw_write_reg_mask(dev, ENE_CIR_SAMPLE_PERIOD,
 | 
						|
+			      disable ? 0 : ENE_CIR_SAMPLE_OVERFLOW,
 | 
						|
+			      ENE_CIR_SAMPLE_OVERFLOW);
 | 
						|
+	dev->idle = idle;
 | 
						|
+
 | 
						|
+	/* remember when we have entered the idle mode */
 | 
						|
+	if (idle) {
 | 
						|
+		do_gettimeofday(&dev->gap_start);
 | 
						|
+		return;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	/* send the gap between keypresses now */
 | 
						|
+	do_gettimeofday(&now);
 | 
						|
+
 | 
						|
+	if (now.tv_sec - dev->gap_start.tv_sec > 16)
 | 
						|
+		dev->sample = space(PULSE_MASK);
 | 
						|
+	else
 | 
						|
+		dev->sample = dev->sample +
 | 
						|
+		    space(1000000ull * (now.tv_sec - dev->gap_start.tv_sec))
 | 
						|
+		    + space(now.tv_usec - dev->gap_start.tv_usec);
 | 
						|
+
 | 
						|
+	if (abs(dev->sample) > PULSE_MASK)
 | 
						|
+		dev->sample = space(PULSE_MASK);
 | 
						|
+	send_sample(dev);
 | 
						|
+}
 | 
						|
+
 | 
						|
+/* interrupt handler */
 | 
						|
+static irqreturn_t ene_hw_irq(int irq, void *data)
 | 
						|
+{
 | 
						|
+	u16 hw_value;
 | 
						|
+	int i, hw_sample;
 | 
						|
+	int space;
 | 
						|
+	int buffer_pointer;
 | 
						|
+	int irq_status;
 | 
						|
+
 | 
						|
+	struct ene_device *dev = (struct ene_device *)data;
 | 
						|
+	irq_status = ene_hw_irq_status(dev, &buffer_pointer);
 | 
						|
+
 | 
						|
+	if (!irq_status)
 | 
						|
+		return IRQ_NONE;
 | 
						|
+
 | 
						|
+	/* TODO: only RX for now */
 | 
						|
+	if (irq_status == ENE_IRQ_TX)
 | 
						|
+		return IRQ_HANDLED;
 | 
						|
+
 | 
						|
+	for (i = 0; i < ENE_SAMPLES_SIZE; i++) {
 | 
						|
+
 | 
						|
+		hw_value = ene_hw_read_reg(dev,
 | 
						|
+				ENE_SAMPLE_BUFFER + buffer_pointer + i);
 | 
						|
+
 | 
						|
+		if (dev->fan_input_inuse) {
 | 
						|
+			/* read high part of the sample */
 | 
						|
+			hw_value |= ene_hw_read_reg(dev,
 | 
						|
+			    ENE_SAMPLE_BUFFER_FAN + buffer_pointer + i) << 8;
 | 
						|
+
 | 
						|
+			/* test for _space_ bit */
 | 
						|
+			space = !(hw_value & ENE_FAN_SMPL_PULS_MSK);
 | 
						|
+
 | 
						|
+			/* clear space bit, and other unused bits */
 | 
						|
+			hw_value &= ENE_FAN_VALUE_MASK;
 | 
						|
+			hw_sample = hw_value * ENE_SAMPLE_PERIOD_FAN;
 | 
						|
+
 | 
						|
+		} else {
 | 
						|
+			space = hw_value & ENE_SAMPLE_SPC_MASK;
 | 
						|
+			hw_value &= ENE_SAMPLE_VALUE_MASK;
 | 
						|
+			hw_sample = hw_value * sample_period;
 | 
						|
+		}
 | 
						|
+
 | 
						|
+		/* no more data */
 | 
						|
+		if (!(hw_value))
 | 
						|
+			break;
 | 
						|
+
 | 
						|
+		if (space)
 | 
						|
+			hw_sample *= -1;
 | 
						|
+
 | 
						|
+		/* overflow sample recieved, handle it */
 | 
						|
+
 | 
						|
+		if (!dev->fan_input_inuse && hw_value == ENE_SAMPLE_OVERFLOW) {
 | 
						|
+
 | 
						|
+			if (dev->idle)
 | 
						|
+				continue;
 | 
						|
+
 | 
						|
+			if (dev->sample > 0 || abs(dev->sample) <= ENE_MAXGAP)
 | 
						|
+				update_sample(dev, hw_sample);
 | 
						|
+			else
 | 
						|
+				ene_set_idle(dev, 1);
 | 
						|
+
 | 
						|
+			continue;
 | 
						|
+		}
 | 
						|
+
 | 
						|
+		/* normal first sample recieved */
 | 
						|
+		if (!dev->fan_input_inuse && dev->idle) {
 | 
						|
+			ene_set_idle(dev, 0);
 | 
						|
+
 | 
						|
+			/* discard first recieved value, its random
 | 
						|
+			   since its the time signal was off before
 | 
						|
+			   first pulse if idle mode is enabled, HW
 | 
						|
+			   does that for us */
 | 
						|
+
 | 
						|
+			if (!enable_idle)
 | 
						|
+				continue;
 | 
						|
+		}
 | 
						|
+		update_sample(dev, hw_sample);
 | 
						|
+		send_sample(dev);
 | 
						|
+	}
 | 
						|
+	return IRQ_HANDLED;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int ene_probe(struct pnp_dev *pnp_dev,
 | 
						|
+		     const struct pnp_device_id *dev_id)
 | 
						|
+{
 | 
						|
+	struct ene_device *dev;
 | 
						|
+	struct lirc_driver *lirc_driver;
 | 
						|
+	int error = -ENOMEM;
 | 
						|
+
 | 
						|
+	dev = kzalloc(sizeof(struct ene_device), GFP_KERNEL);
 | 
						|
+
 | 
						|
+	if (!dev)
 | 
						|
+		goto err1;
 | 
						|
+
 | 
						|
+	dev->pnp_dev = pnp_dev;
 | 
						|
+	pnp_set_drvdata(pnp_dev, dev);
 | 
						|
+
 | 
						|
+
 | 
						|
+	/* prepare lirc interface */
 | 
						|
+	error = -ENOMEM;
 | 
						|
+	lirc_driver = kzalloc(sizeof(struct lirc_driver), GFP_KERNEL);
 | 
						|
+
 | 
						|
+	if (!lirc_driver)
 | 
						|
+		goto err2;
 | 
						|
+
 | 
						|
+	dev->lirc_driver = lirc_driver;
 | 
						|
+
 | 
						|
+	strcpy(lirc_driver->name, ENE_DRIVER_NAME);
 | 
						|
+	lirc_driver->minor = -1;
 | 
						|
+	lirc_driver->code_length = sizeof(int) * 8;
 | 
						|
+	lirc_driver->features = LIRC_CAN_REC_MODE2;
 | 
						|
+	lirc_driver->data = dev;
 | 
						|
+	lirc_driver->set_use_inc = ene_hw_init;
 | 
						|
+	lirc_driver->set_use_dec = ene_hw_deinit;
 | 
						|
+	lirc_driver->dev = &pnp_dev->dev;
 | 
						|
+	lirc_driver->owner = THIS_MODULE;
 | 
						|
+
 | 
						|
+	lirc_driver->rbuf = kzalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
 | 
						|
+
 | 
						|
+	if (!lirc_driver->rbuf)
 | 
						|
+		goto err3;
 | 
						|
+
 | 
						|
+	if (lirc_buffer_init(lirc_driver->rbuf, sizeof(int), sizeof(int) * 256))
 | 
						|
+		goto err4;
 | 
						|
+
 | 
						|
+	error = -ENODEV;
 | 
						|
+	if (lirc_register_driver(lirc_driver))
 | 
						|
+		goto err5;
 | 
						|
+
 | 
						|
+	/* validate resources */
 | 
						|
+	if (!pnp_port_valid(pnp_dev, 0) ||
 | 
						|
+	    pnp_port_len(pnp_dev, 0) < ENE_MAX_IO)
 | 
						|
+		goto err6;
 | 
						|
+
 | 
						|
+	if (!pnp_irq_valid(pnp_dev, 0))
 | 
						|
+		goto err6;
 | 
						|
+
 | 
						|
+	dev->hw_io = pnp_port_start(pnp_dev, 0);
 | 
						|
+	dev->irq = pnp_irq(pnp_dev, 0);
 | 
						|
+
 | 
						|
+	/* claim the resources */
 | 
						|
+	error = -EBUSY;
 | 
						|
+	if (!request_region(dev->hw_io, ENE_MAX_IO, ENE_DRIVER_NAME))
 | 
						|
+		goto err6;
 | 
						|
+
 | 
						|
+	if (request_irq(dev->irq, ene_hw_irq,
 | 
						|
+			IRQF_SHARED, ENE_DRIVER_NAME, (void *)dev))
 | 
						|
+		goto err7;
 | 
						|
+
 | 
						|
+	/* detect hardware version and features */
 | 
						|
+	error = ene_hw_detect(dev);
 | 
						|
+	if (error)
 | 
						|
+		goto err8;
 | 
						|
+
 | 
						|
+	ene_printk(KERN_NOTICE, "driver has been succesfully loaded\n");
 | 
						|
+	return 0;
 | 
						|
+
 | 
						|
+err8:
 | 
						|
+	free_irq(dev->irq, dev);
 | 
						|
+err7:
 | 
						|
+	release_region(dev->hw_io, ENE_MAX_IO);
 | 
						|
+err6:
 | 
						|
+	lirc_unregister_driver(lirc_driver->minor);
 | 
						|
+err5:
 | 
						|
+	lirc_buffer_free(lirc_driver->rbuf);
 | 
						|
+err4:
 | 
						|
+	kfree(lirc_driver->rbuf);
 | 
						|
+err3:
 | 
						|
+	kfree(lirc_driver);
 | 
						|
+err2:
 | 
						|
+	kfree(dev);
 | 
						|
+err1:
 | 
						|
+	return error;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void ene_remove(struct pnp_dev *pnp_dev)
 | 
						|
+{
 | 
						|
+	struct ene_device *dev = pnp_get_drvdata(pnp_dev);
 | 
						|
+	ene_hw_deinit(dev);
 | 
						|
+	free_irq(dev->irq, dev);
 | 
						|
+	release_region(dev->hw_io, ENE_MAX_IO);
 | 
						|
+	lirc_unregister_driver(dev->lirc_driver->minor);
 | 
						|
+	lirc_buffer_free(dev->lirc_driver->rbuf);
 | 
						|
+	kfree(dev->lirc_driver);
 | 
						|
+	kfree(dev);
 | 
						|
+}
 | 
						|
+
 | 
						|
+#ifdef CONFIG_PM
 | 
						|
+
 | 
						|
+/* TODO: make 'wake on IR' configurable and add .shutdown */
 | 
						|
+/* currently impossible due to lack of kernel support */
 | 
						|
+
 | 
						|
+static int ene_suspend(struct pnp_dev *pnp_dev, pm_message_t state)
 | 
						|
+{
 | 
						|
+	struct ene_device *dev = pnp_get_drvdata(pnp_dev);
 | 
						|
+	ene_hw_write_reg_mask(dev, ENE_FW1, ENE_FW1_WAKE, ENE_FW1_WAKE);
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int ene_resume(struct pnp_dev *pnp_dev)
 | 
						|
+{
 | 
						|
+	struct ene_device *dev = pnp_get_drvdata(pnp_dev);
 | 
						|
+	if (dev->in_use)
 | 
						|
+		ene_hw_init(dev);
 | 
						|
+
 | 
						|
+	ene_hw_write_reg_mask(dev, ENE_FW1, 0, ENE_FW1_WAKE);
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+#endif
 | 
						|
+
 | 
						|
+static const struct pnp_device_id ene_ids[] = {
 | 
						|
+	{.id = "ENE0100",},
 | 
						|
+	{},
 | 
						|
+};
 | 
						|
+
 | 
						|
+static struct pnp_driver ene_driver = {
 | 
						|
+	.name = ENE_DRIVER_NAME,
 | 
						|
+	.id_table = ene_ids,
 | 
						|
+	.flags = PNP_DRIVER_RES_DO_NOT_CHANGE,
 | 
						|
+
 | 
						|
+	.probe = ene_probe,
 | 
						|
+	.remove = __devexit_p(ene_remove),
 | 
						|
+
 | 
						|
+#ifdef CONFIG_PM
 | 
						|
+	.suspend = ene_suspend,
 | 
						|
+	.resume = ene_resume,
 | 
						|
+#endif
 | 
						|
+};
 | 
						|
+
 | 
						|
+static int __init ene_init(void)
 | 
						|
+{
 | 
						|
+	if (sample_period < 5) {
 | 
						|
+		ene_printk(KERN_ERR, "sample period must be at\n");
 | 
						|
+		ene_printk(KERN_ERR, "least 5 us, (at least 30 recommended)\n");
 | 
						|
+		return -EINVAL;
 | 
						|
+	}
 | 
						|
+	return pnp_register_driver(&ene_driver);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void ene_exit(void)
 | 
						|
+{
 | 
						|
+	pnp_unregister_driver(&ene_driver);
 | 
						|
+}
 | 
						|
+
 | 
						|
+module_param(sample_period, int, S_IRUGO);
 | 
						|
+MODULE_PARM_DESC(sample_period, "Hardware sample period (75 us default)");
 | 
						|
+
 | 
						|
+module_param(enable_idle, bool, S_IRUGO | S_IWUSR);
 | 
						|
+MODULE_PARM_DESC(enable_idle,
 | 
						|
+	"Enables turning off signal sampling after long inactivity time; "
 | 
						|
+	"if disabled might help detecting input signal (default: enabled)");
 | 
						|
+
 | 
						|
+module_param(enable_learning, bool, S_IRUGO);
 | 
						|
+MODULE_PARM_DESC(enable_learning, "Use wide band (learning) reciever");
 | 
						|
+
 | 
						|
+MODULE_DEVICE_TABLE(pnp, ene_ids);
 | 
						|
+MODULE_DESCRIPTION
 | 
						|
+    ("LIRC driver for KB3926B/KB3926C/KB3926D (aka ENE0100) CIR port");
 | 
						|
+MODULE_AUTHOR("Maxim Levitsky");
 | 
						|
+MODULE_LICENSE("GPL");
 | 
						|
+
 | 
						|
+module_init(ene_init);
 | 
						|
+module_exit(ene_exit);
 | 
						|
diff --git a/drivers/staging/lirc/lirc_ene0100.h b/drivers/staging/lirc/lirc_ene0100.h
 | 
						|
new file mode 100644
 | 
						|
index 0000000..825045d
 | 
						|
--- /dev/null
 | 
						|
+++ b/drivers/staging/lirc/lirc_ene0100.h
 | 
						|
@@ -0,0 +1,169 @@
 | 
						|
+/*
 | 
						|
+ * driver for ENE KB3926 B/C/D CIR (also known as ENE0100)
 | 
						|
+ *
 | 
						|
+ * Copyright (C) 2009 Maxim Levitsky <maximlevitsky@gmail.com>
 | 
						|
+ *
 | 
						|
+ * This program is free software; you can redistribute it and/or
 | 
						|
+ * modify it under the terms of the GNU General Public License as
 | 
						|
+ * published by the Free Software Foundation; either version 2 of the
 | 
						|
+ * License, or (at your option) any later version.
 | 
						|
+ *
 | 
						|
+ * This program is distributed in the hope that it will be useful, but
 | 
						|
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
						|
+ * General Public License for more details.
 | 
						|
+ *
 | 
						|
+ * You should have received a copy of the GNU General Public License
 | 
						|
+ * along with this program; if not, write to the Free Software
 | 
						|
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 | 
						|
+ * USA
 | 
						|
+ */
 | 
						|
+
 | 
						|
+#include <media/lirc.h>
 | 
						|
+#include <media/lirc_dev.h>
 | 
						|
+
 | 
						|
+/* hardware address */
 | 
						|
+#define ENE_STATUS		0	 /* hardware status - unused */
 | 
						|
+#define ENE_ADDR_HI 		1	 /* hi byte of register address */
 | 
						|
+#define ENE_ADDR_LO 		2	 /* low byte of register address */
 | 
						|
+#define ENE_IO 			3	 /* read/write window */
 | 
						|
+#define ENE_MAX_IO		4
 | 
						|
+
 | 
						|
+/* 8 bytes of samples, divided in 2 halfs*/
 | 
						|
+#define ENE_SAMPLE_BUFFER 	0xF8F0	 /* regular sample buffer */
 | 
						|
+#define ENE_SAMPLE_SPC_MASK 	(1 << 7) /* sample is space */
 | 
						|
+#define ENE_SAMPLE_VALUE_MASK	0x7F
 | 
						|
+#define ENE_SAMPLE_OVERFLOW	0x7F
 | 
						|
+#define ENE_SAMPLES_SIZE	4
 | 
						|
+
 | 
						|
+/* fan input sample buffer */
 | 
						|
+#define ENE_SAMPLE_BUFFER_FAN	0xF8FB	 /* this buffer holds high byte of */
 | 
						|
+					 /* each sample of normal buffer */
 | 
						|
+
 | 
						|
+#define ENE_FAN_SMPL_PULS_MSK	0x8000	 /* this bit of combined sample */
 | 
						|
+					 /* if set, says that sample is pulse */
 | 
						|
+#define ENE_FAN_VALUE_MASK	0x0FFF   /* mask for valid bits of the value */
 | 
						|
+
 | 
						|
+/* first firmware register */
 | 
						|
+#define ENE_FW1			0xF8F8
 | 
						|
+#define	ENE_FW1_ENABLE		(1 << 0) /* enable fw processing */
 | 
						|
+#define ENE_FW1_TXIRQ		(1 << 1) /* TX interrupt pending */
 | 
						|
+#define ENE_FW1_WAKE		(1 << 6) /* enable wake from S3 */
 | 
						|
+#define ENE_FW1_IRQ		(1 << 7) /* enable interrupt */
 | 
						|
+
 | 
						|
+/* second firmware register */
 | 
						|
+#define ENE_FW2			0xF8F9
 | 
						|
+#define ENE_FW2_BUF_HIGH	(1 << 0) /* which half of the buffer to read */
 | 
						|
+#define ENE_FW2_IRQ_CLR		(1 << 2) /* clear this on IRQ */
 | 
						|
+#define ENE_FW2_GP40_AS_LEARN	(1 << 4) /* normal input is used as */
 | 
						|
+					 /* learning input */
 | 
						|
+#define ENE_FW2_FAN_AS_NRML_IN	(1 << 6) /* fan is used as normal input */
 | 
						|
+#define ENE_FW2_LEARNING	(1 << 7) /* hardware supports learning and TX */
 | 
						|
+
 | 
						|
+/* fan as input settings - only if learning capable */
 | 
						|
+#define ENE_FAN_AS_IN1		0xFE30   /* fan init reg 1 */
 | 
						|
+#define ENE_FAN_AS_IN1_EN	0xCD
 | 
						|
+#define ENE_FAN_AS_IN2		0xFE31   /* fan init reg 2 */
 | 
						|
+#define ENE_FAN_AS_IN2_EN	0x03
 | 
						|
+#define ENE_SAMPLE_PERIOD_FAN   61	 /* fan input has fixed sample period */
 | 
						|
+
 | 
						|
+/* IRQ registers block (for revision B) */
 | 
						|
+#define ENEB_IRQ		0xFD09	 /* IRQ number */
 | 
						|
+#define ENEB_IRQ_UNK1		0xFD17	 /* unknown setting = 1 */
 | 
						|
+#define ENEB_IRQ_STATUS		0xFD80	 /* irq status */
 | 
						|
+#define ENEB_IRQ_STATUS_IR	(1 << 5) /* IR irq */
 | 
						|
+
 | 
						|
+/* IRQ registers block (for revision C,D) */
 | 
						|
+#define ENEC_IRQ		0xFE9B	 /* new irq settings register */
 | 
						|
+#define ENEC_IRQ_MASK		0x0F	 /* irq number mask */
 | 
						|
+#define ENEC_IRQ_UNK_EN		(1 << 4) /* always enabled */
 | 
						|
+#define ENEC_IRQ_STATUS		(1 << 5) /* irq status and ACK */
 | 
						|
+
 | 
						|
+/* CIR block settings */
 | 
						|
+#define ENE_CIR_CONF1		0xFEC0
 | 
						|
+#define ENE_CIR_CONF1_ADC_ON	0x7	 /* reciever on gpio40 enabled */
 | 
						|
+#define ENE_CIR_CONF1_LEARN1	(1 << 3) /* enabled on learning mode */
 | 
						|
+#define ENE_CIR_CONF1_TX_ON	0x30	 /* enabled on transmit */
 | 
						|
+#define ENE_CIR_CONF1_TX_CARR	(1 << 7) /* send TX carrier or not */
 | 
						|
+
 | 
						|
+#define ENE_CIR_CONF2		0xFEC1	 /* unknown setting = 0 */
 | 
						|
+#define ENE_CIR_CONF2_LEARN2	(1 << 4) /* set on enable learning */
 | 
						|
+#define ENE_CIR_CONF2_GPIO40DIS	(1 << 5) /* disable normal input via gpio40 */
 | 
						|
+
 | 
						|
+#define ENE_CIR_SAMPLE_PERIOD	0xFEC8	 /* sample period in us */
 | 
						|
+#define ENE_CIR_SAMPLE_OVERFLOW	(1 << 7) /* interrupt on overflows if set */
 | 
						|
+
 | 
						|
+
 | 
						|
+/* transmitter - not implemented yet */
 | 
						|
+/* KB3926C and higher */
 | 
						|
+/* transmission is very similiar to recieving, a byte is written to */
 | 
						|
+/* ENE_TX_INPUT, in same manner as it is read from sample buffer */
 | 
						|
+/* sample period is fixed*/
 | 
						|
+
 | 
						|
+
 | 
						|
+/* transmitter ports */
 | 
						|
+#define ENE_TX_PORT1		0xFC01	 /* this enables one or both */
 | 
						|
+#define ENE_TX_PORT1_EN		(1 << 5) /* TX ports */
 | 
						|
+#define ENE_TX_PORT2		0xFC08
 | 
						|
+#define ENE_TX_PORT2_EN		(1 << 1)
 | 
						|
+
 | 
						|
+#define ENE_TX_INPUT		0xFEC9	 /* next byte to transmit */
 | 
						|
+#define ENE_TX_SPC_MASK 	(1 << 7) /* Transmitted sample is space */
 | 
						|
+#define ENE_TX_UNK1		0xFECB	 /* set to 0x63 */
 | 
						|
+#define ENE_TX_SMPL_PERIOD	50	 /* transmit sample period */
 | 
						|
+
 | 
						|
+
 | 
						|
+#define ENE_TX_CARRIER		0xFECE	 /* TX carrier * 2 (khz) */
 | 
						|
+#define ENE_TX_CARRIER_UNKBIT	0x80	 /* This bit set on transmit */
 | 
						|
+#define ENE_TX_CARRIER_LOW	0xFECF	 /* TX carrier / 2 */
 | 
						|
+
 | 
						|
+/* Hardware versions */
 | 
						|
+#define ENE_HW_VERSION		0xFF00	 /* hardware revision */
 | 
						|
+#define ENE_HW_UNK 		0xFF1D
 | 
						|
+#define ENE_HW_UNK_CLR		(1 << 2)
 | 
						|
+#define ENE_HW_VER_MAJOR	0xFF1E	 /* chip version */
 | 
						|
+#define ENE_HW_VER_MINOR	0xFF1F
 | 
						|
+#define ENE_HW_VER_OLD		0xFD00
 | 
						|
+
 | 
						|
+#define same_sign(a, b) ((((a) > 0) && (b) > 0) || ((a) < 0 && (b) < 0))
 | 
						|
+
 | 
						|
+#define ENE_DRIVER_NAME 	"enecir"
 | 
						|
+#define ENE_MAXGAP 		250000	 /* this is amount of time we wait
 | 
						|
+					 before turning the sampler, chosen
 | 
						|
+					 arbitry */
 | 
						|
+
 | 
						|
+#define space(len) 	       (-(len))	 /* add a space */
 | 
						|
+
 | 
						|
+/* software defines */
 | 
						|
+#define ENE_IRQ_RX		1
 | 
						|
+#define ENE_IRQ_TX		2
 | 
						|
+
 | 
						|
+#define  ENE_HW_B		1	/* 3926B */
 | 
						|
+#define  ENE_HW_C		2	/* 3926C */
 | 
						|
+#define  ENE_HW_D		3	/* 3926D */
 | 
						|
+
 | 
						|
+#define ene_printk(level, text, ...) \
 | 
						|
+	printk(level ENE_DRIVER_NAME ": " text, ## __VA_ARGS__)
 | 
						|
+
 | 
						|
+struct ene_device {
 | 
						|
+	struct pnp_dev *pnp_dev;
 | 
						|
+	struct lirc_driver *lirc_driver;
 | 
						|
+
 | 
						|
+	/* hw settings */
 | 
						|
+	unsigned long hw_io;
 | 
						|
+	int irq;
 | 
						|
+
 | 
						|
+	int hw_revision;			/* hardware revision */
 | 
						|
+	int hw_learning_and_tx_capable;		/* learning capable */
 | 
						|
+	int hw_gpio40_learning;			/* gpio40 is learning */
 | 
						|
+	int hw_fan_as_normal_input;	/* fan input is used as regular input */
 | 
						|
+
 | 
						|
+	/* device data */
 | 
						|
+	int idle;
 | 
						|
+	int fan_input_inuse;
 | 
						|
+
 | 
						|
+	int sample;
 | 
						|
+	int in_use;
 | 
						|
+
 | 
						|
+	struct timeval gap_start;
 | 
						|
+};
 | 
						|
diff --git a/drivers/staging/lirc/lirc_i2c.c b/drivers/staging/lirc/lirc_i2c.c
 | 
						|
new file mode 100644
 | 
						|
index 0000000..6df2c0e
 | 
						|
--- /dev/null
 | 
						|
+++ b/drivers/staging/lirc/lirc_i2c.c
 | 
						|
@@ -0,0 +1,536 @@
 | 
						|
+/*
 | 
						|
+ * lirc_i2c.c
 | 
						|
+ *
 | 
						|
+ * i2c IR driver for the onboard IR port on many TV tuner cards, including:
 | 
						|
+ *  -Flavors of the Hauppauge PVR-150/250/350
 | 
						|
+ *  -Hauppauge HVR-1300
 | 
						|
+ *  -PixelView (BT878P+W/FM)
 | 
						|
+ *  -KNC ONE TV Station/Anubis Typhoon TView Tuner
 | 
						|
+ *  -Asus TV-Box and Creative/VisionTek BreakOut-Box
 | 
						|
+ *  -Leadtek Winfast PVR2000
 | 
						|
+ *
 | 
						|
+ * Copyright (c) 2000 Gerd Knorr <kraxel@goldbach.in-berlin.de>
 | 
						|
+ * modified for PixelView (BT878P+W/FM) by
 | 
						|
+ *      Michal Kochanowicz <mkochano@pld.org.pl>
 | 
						|
+ *      Christoph Bartelmus <lirc@bartelmus.de>
 | 
						|
+ * modified for KNC ONE TV Station/Anubis Typhoon TView Tuner by
 | 
						|
+ *      Ulrich Mueller <ulrich.mueller42@web.de>
 | 
						|
+ * modified for Asus TV-Box and Creative/VisionTek BreakOut-Box by
 | 
						|
+ *      Stefan Jahn <stefan@lkcc.org>
 | 
						|
+ * modified for inclusion into kernel sources by
 | 
						|
+ *      Jerome Brock <jbrock@users.sourceforge.net>
 | 
						|
+ * modified for Leadtek Winfast PVR2000 by
 | 
						|
+ *      Thomas Reitmayr (treitmayr@yahoo.com)
 | 
						|
+ * modified for Hauppauge HVR-1300 by
 | 
						|
+ *      Jan Frey (jfrey@gmx.de)
 | 
						|
+ *
 | 
						|
+ * parts are cut&pasted from the old lirc_haup.c driver
 | 
						|
+ *
 | 
						|
+ *  This program is free software; you can redistribute it and/or modify
 | 
						|
+ *  it under the terms of the GNU General Public License as published by
 | 
						|
+ *  the Free Software Foundation; either version 2 of the License, or
 | 
						|
+ *  (at your option) any later version.
 | 
						|
+ *
 | 
						|
+ *  This program is distributed in the hope that it will be useful,
 | 
						|
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
						|
+ *  GNU General Public License for more details.
 | 
						|
+ *
 | 
						|
+ *  You should have received a copy of the GNU General Public License
 | 
						|
+ *  along with this program; if not, write to the Free Software
 | 
						|
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | 
						|
+ */
 | 
						|
+
 | 
						|
+
 | 
						|
+#include <linux/version.h>
 | 
						|
+#include <linux/module.h>
 | 
						|
+#include <linux/kmod.h>
 | 
						|
+#include <linux/kernel.h>
 | 
						|
+#include <linux/sched.h>
 | 
						|
+#include <linux/string.h>
 | 
						|
+#include <linux/timer.h>
 | 
						|
+#include <linux/delay.h>
 | 
						|
+#include <linux/errno.h>
 | 
						|
+#include <linux/slab.h>
 | 
						|
+#include <linux/i2c.h>
 | 
						|
+#include <linux/i2c-algo-bit.h>
 | 
						|
+
 | 
						|
+#include <media/lirc_dev.h>
 | 
						|
+
 | 
						|
+struct IR {
 | 
						|
+	struct lirc_driver l;
 | 
						|
+	struct i2c_client  c;
 | 
						|
+	int nextkey;
 | 
						|
+	unsigned char b[3];
 | 
						|
+	unsigned char bits;
 | 
						|
+	unsigned char flag;
 | 
						|
+};
 | 
						|
+
 | 
						|
+#define DEVICE_NAME "lirc_i2c"
 | 
						|
+
 | 
						|
+/* module parameters */
 | 
						|
+static int debug;	/* debug output */
 | 
						|
+static int minor = -1;	/* minor number */
 | 
						|
+
 | 
						|
+#define dprintk(fmt, args...)						\
 | 
						|
+	do {								\
 | 
						|
+		if (debug)						\
 | 
						|
+			printk(KERN_DEBUG DEVICE_NAME ": " fmt,		\
 | 
						|
+			       ## args);				\
 | 
						|
+	} while (0)
 | 
						|
+
 | 
						|
+static int reverse(int data, int bits)
 | 
						|
+{
 | 
						|
+	int i;
 | 
						|
+	int c;
 | 
						|
+
 | 
						|
+	for (c = 0, i = 0; i < bits; i++)
 | 
						|
+		c |= ((data & (1<<i)) ? 1 : 0) << (bits-1-i);
 | 
						|
+
 | 
						|
+	return c;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int add_to_buf_adap(void *data, struct lirc_buffer *buf)
 | 
						|
+{
 | 
						|
+	struct IR *ir = data;
 | 
						|
+	unsigned char keybuf[4];
 | 
						|
+
 | 
						|
+	keybuf[0] = 0x00;
 | 
						|
+	i2c_master_send(&ir->c, keybuf, 1);
 | 
						|
+	/* poll IR chip */
 | 
						|
+	if (i2c_master_recv(&ir->c, keybuf, sizeof(keybuf)) != sizeof(keybuf)) {
 | 
						|
+		dprintk("read error\n");
 | 
						|
+		return -EIO;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	dprintk("key (0x%02x%02x%02x%02x)\n",
 | 
						|
+		keybuf[0], keybuf[1], keybuf[2], keybuf[3]);
 | 
						|
+
 | 
						|
+	/* key pressed ? */
 | 
						|
+	if (keybuf[2] == 0xff)
 | 
						|
+		return -ENODATA;
 | 
						|
+
 | 
						|
+	/* remove repeat bit */
 | 
						|
+	keybuf[2] &= 0x7f;
 | 
						|
+	keybuf[3] |= 0x80;
 | 
						|
+
 | 
						|
+	lirc_buffer_write(buf, keybuf);
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int add_to_buf_pcf8574(void *data, struct lirc_buffer *buf)
 | 
						|
+{
 | 
						|
+	struct IR *ir = data;
 | 
						|
+	int rc;
 | 
						|
+	unsigned char all, mask;
 | 
						|
+	unsigned char key;
 | 
						|
+
 | 
						|
+	/* compute all valid bits (key code + pressed/release flag) */
 | 
						|
+	all = ir->bits | ir->flag;
 | 
						|
+
 | 
						|
+	/* save IR writable mask bits */
 | 
						|
+	mask = i2c_smbus_read_byte(&ir->c) & ~all;
 | 
						|
+
 | 
						|
+	/* send bit mask */
 | 
						|
+	rc = i2c_smbus_write_byte(&ir->c, (0xff & all) | mask);
 | 
						|
+
 | 
						|
+	/* receive scan code */
 | 
						|
+	rc = i2c_smbus_read_byte(&ir->c);
 | 
						|
+
 | 
						|
+	if (rc == -1) {
 | 
						|
+		dprintk("%s read error\n", ir->c.name);
 | 
						|
+		return -EIO;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	/* drop duplicate polls */
 | 
						|
+	if (ir->b[0] == (rc & all))
 | 
						|
+		return -ENODATA;
 | 
						|
+
 | 
						|
+	ir->b[0] = rc & all;
 | 
						|
+
 | 
						|
+	dprintk("%s key 0x%02X %s\n", ir->c.name, rc & ir->bits,
 | 
						|
+		(rc & ir->flag) ? "released" : "pressed");
 | 
						|
+
 | 
						|
+	/* ignore released buttons */
 | 
						|
+	if (rc & ir->flag)
 | 
						|
+		return -ENODATA;
 | 
						|
+
 | 
						|
+	/* set valid key code */
 | 
						|
+	key  = rc & ir->bits;
 | 
						|
+	lirc_buffer_write(buf, &key);
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+/* common for Hauppauge IR receivers */
 | 
						|
+static int add_to_buf_haup_common(void *data, struct lirc_buffer *buf,
 | 
						|
+		unsigned char *keybuf, int size, int offset)
 | 
						|
+{
 | 
						|
+	struct IR *ir = data;
 | 
						|
+	__u16 code;
 | 
						|
+	unsigned char codes[2];
 | 
						|
+	int ret;
 | 
						|
+
 | 
						|
+	/* poll IR chip */
 | 
						|
+	ret = i2c_master_recv(&ir->c, keybuf, size);
 | 
						|
+	if (ret == size) {
 | 
						|
+		ir->b[0] = keybuf[offset];
 | 
						|
+		ir->b[1] = keybuf[offset+1];
 | 
						|
+		ir->b[2] = keybuf[offset+2];
 | 
						|
+		if (ir->b[0] != 0x00 && ir->b[1] != 0x00)
 | 
						|
+			dprintk("key (0x%02x/0x%02x)\n", ir->b[0], ir->b[1]);
 | 
						|
+	} else {
 | 
						|
+		dprintk("read error (ret=%d)\n", ret);
 | 
						|
+		/* keep last successful 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 */
 | 
						|
+	dprintk("sending code 0x%02x%02x to lirc\n", codes[0], codes[1]);
 | 
						|
+	lirc_buffer_write(buf, codes);
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+/* specific for the Hauppauge PVR150 IR receiver */
 | 
						|
+static int add_to_buf_haup_pvr150(void *data, struct lirc_buffer *buf)
 | 
						|
+{
 | 
						|
+	unsigned char keybuf[6];
 | 
						|
+	/* fetch 6 bytes, first relevant is at offset 3 */
 | 
						|
+	return add_to_buf_haup_common(data, buf, keybuf, 6, 3);
 | 
						|
+}
 | 
						|
+
 | 
						|
+/* used for all Hauppauge IR receivers but the PVR150 */
 | 
						|
+static int add_to_buf_haup(void *data, struct lirc_buffer *buf)
 | 
						|
+{
 | 
						|
+	unsigned char keybuf[3];
 | 
						|
+	/* fetch 3 bytes, first relevant is at offset 0 */
 | 
						|
+	return add_to_buf_haup_common(data, buf, keybuf, 3, 0);
 | 
						|
+}
 | 
						|
+
 | 
						|
+
 | 
						|
+static int add_to_buf_pvr2000(void *data, struct lirc_buffer *buf)
 | 
						|
+{
 | 
						|
+	struct IR *ir = data;
 | 
						|
+	unsigned char key;
 | 
						|
+	s32 flags;
 | 
						|
+	s32 code;
 | 
						|
+
 | 
						|
+	/* poll IR chip */
 | 
						|
+	flags = i2c_smbus_read_byte_data(&ir->c, 0x10);
 | 
						|
+	if (-1 == flags) {
 | 
						|
+		dprintk("read error\n");
 | 
						|
+		return -ENODATA;
 | 
						|
+	}
 | 
						|
+	/* key pressed ? */
 | 
						|
+	if (0 == (flags & 0x80))
 | 
						|
+		return -ENODATA;
 | 
						|
+
 | 
						|
+	/* read actual key code */
 | 
						|
+	code = i2c_smbus_read_byte_data(&ir->c, 0x00);
 | 
						|
+	if (-1 == code) {
 | 
						|
+		dprintk("read error\n");
 | 
						|
+		return -ENODATA;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	key = code & 0xFF;
 | 
						|
+
 | 
						|
+	dprintk("IR Key/Flags: (0x%02x/0x%02x)\n", key, flags & 0xFF);
 | 
						|
+
 | 
						|
+	/* return it */
 | 
						|
+	lirc_buffer_write(buf, &key);
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int add_to_buf_pixelview(void *data, struct lirc_buffer *buf)
 | 
						|
+{
 | 
						|
+	struct IR *ir = data;
 | 
						|
+	unsigned char key;
 | 
						|
+
 | 
						|
+	/* poll IR chip */
 | 
						|
+	if (1 != i2c_master_recv(&ir->c, &key, 1)) {
 | 
						|
+		dprintk("read error\n");
 | 
						|
+		return -1;
 | 
						|
+	}
 | 
						|
+	dprintk("key %02x\n", key);
 | 
						|
+
 | 
						|
+	/* return it */
 | 
						|
+	lirc_buffer_write(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(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 held
 | 
						|
+	 * down, while 0xFF indicates that no button is held
 | 
						|
+	 * 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(buf, &key);
 | 
						|
+
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int set_use_inc(void *data)
 | 
						|
+{
 | 
						|
+	struct IR *ir = data;
 | 
						|
+
 | 
						|
+	dprintk("%s called\n", __func__);
 | 
						|
+
 | 
						|
+	/* lock bttv in memory while /dev/lirc is in use  */
 | 
						|
+	i2c_use_client(&ir->c);
 | 
						|
+
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void set_use_dec(void *data)
 | 
						|
+{
 | 
						|
+	struct IR *ir = data;
 | 
						|
+
 | 
						|
+	dprintk("%s called\n", __func__);
 | 
						|
+
 | 
						|
+	i2c_release_client(&ir->c);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static struct lirc_driver lirc_template = {
 | 
						|
+	.name		= "lirc_i2c",
 | 
						|
+	.set_use_inc	= set_use_inc,
 | 
						|
+	.set_use_dec	= set_use_dec,
 | 
						|
+	.dev		= NULL,
 | 
						|
+	.owner		= THIS_MODULE,
 | 
						|
+};
 | 
						|
+
 | 
						|
+static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id);
 | 
						|
+static int ir_remove(struct i2c_client *client);
 | 
						|
+static int ir_command(struct i2c_client *client, unsigned int cmd, void *arg);
 | 
						|
+
 | 
						|
+static const struct i2c_device_id ir_receiver_id[] = {
 | 
						|
+	/* Generic entry for any IR receiver */
 | 
						|
+	{ "ir_video", 0 },
 | 
						|
+	/* IR device specific entries could be added here */
 | 
						|
+	{ }
 | 
						|
+};
 | 
						|
+
 | 
						|
+static struct i2c_driver driver = {
 | 
						|
+	.driver = {
 | 
						|
+		.owner	= THIS_MODULE,
 | 
						|
+		.name	= "i2c ir driver",
 | 
						|
+	},
 | 
						|
+	.probe		= ir_probe,
 | 
						|
+	.remove		= ir_remove,
 | 
						|
+	.id_table	= ir_receiver_id,
 | 
						|
+	.command	= ir_command,
 | 
						|
+};
 | 
						|
+
 | 
						|
+static void pcf_probe(struct i2c_client *client, struct IR *ir)
 | 
						|
+{
 | 
						|
+	int ret1, ret2, ret3, ret4;
 | 
						|
+
 | 
						|
+	ret1 = i2c_smbus_write_byte(client, 0xff);
 | 
						|
+	ret2 = i2c_smbus_read_byte(client);
 | 
						|
+	ret3 = i2c_smbus_write_byte(client, 0x00);
 | 
						|
+	ret4 = i2c_smbus_read_byte(client);
 | 
						|
+
 | 
						|
+	/* in the Asus TV-Box: bit 1-0 */
 | 
						|
+	if (((ret2 & 0x03) == 0x03) && ((ret4 & 0x03) == 0x00)) {
 | 
						|
+		ir->bits = (unsigned char) ~0x07;
 | 
						|
+		ir->flag = 0x04;
 | 
						|
+	/* in the Creative/VisionTek BreakOut-Box: bit 7-6 */
 | 
						|
+	} else if (((ret2 & 0xc0) == 0xc0) && ((ret4 & 0xc0) == 0x00)) {
 | 
						|
+		ir->bits = (unsigned char) ~0xe0;
 | 
						|
+		ir->flag = 0x20;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	return;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)
 | 
						|
+{
 | 
						|
+	struct IR *ir;
 | 
						|
+	struct i2c_adapter *adap = client->adapter;
 | 
						|
+	unsigned short addr = client->addr;
 | 
						|
+	int retval;
 | 
						|
+
 | 
						|
+	ir = kzalloc(sizeof(struct IR), GFP_KERNEL);
 | 
						|
+	if (!ir)
 | 
						|
+		return -ENOMEM;
 | 
						|
+	memcpy(&ir->l, &lirc_template, sizeof(struct lirc_driver));
 | 
						|
+	memcpy(&ir->c, client, sizeof(struct i2c_client));
 | 
						|
+
 | 
						|
+	i2c_set_clientdata(client, ir);
 | 
						|
+	ir->l.data    = ir;
 | 
						|
+	ir->l.minor   = minor;
 | 
						|
+	ir->l.sample_rate = 10;
 | 
						|
+	ir->l.dev     = &ir->c.dev;
 | 
						|
+	ir->nextkey   = -1;
 | 
						|
+
 | 
						|
+	switch (addr) {
 | 
						|
+	case 0x64:
 | 
						|
+		strlcpy(ir->c.name, "Pixelview IR", I2C_NAME_SIZE);
 | 
						|
+		ir->l.code_length = 8;
 | 
						|
+		ir->l.add_to_buf = add_to_buf_pixelview;
 | 
						|
+		break;
 | 
						|
+	case 0x4b:
 | 
						|
+		strlcpy(ir->c.name, "PV951 IR", I2C_NAME_SIZE);
 | 
						|
+		ir->l.code_length = 32;
 | 
						|
+		ir->l.add_to_buf = add_to_buf_pv951;
 | 
						|
+		break;
 | 
						|
+	case 0x71:
 | 
						|
+		if (adap->id == I2C_HW_B_CX2388x)
 | 
						|
+			strlcpy(ir->c.name, "Hauppauge HVR1300", I2C_NAME_SIZE);
 | 
						|
+		else /* bt8xx or cx2341x */
 | 
						|
+			/*
 | 
						|
+			 * The PVR150 IR receiver uses the same protocol as
 | 
						|
+			 * other Hauppauge cards, but the data flow is
 | 
						|
+			 * different, so we need to deal with it by its own.
 | 
						|
+			 */
 | 
						|
+			strlcpy(ir->c.name, "Hauppauge PVR150", I2C_NAME_SIZE);
 | 
						|
+		ir->l.code_length = 13;
 | 
						|
+		ir->l.add_to_buf = add_to_buf_haup_pvr150;
 | 
						|
+		break;
 | 
						|
+	case 0x6b:
 | 
						|
+		strlcpy(ir->c.name, "Adaptec IR", I2C_NAME_SIZE);
 | 
						|
+		ir->l.code_length = 32;
 | 
						|
+		ir->l.add_to_buf = add_to_buf_adap;
 | 
						|
+		break;
 | 
						|
+	case 0x18:
 | 
						|
+	case 0x1a:
 | 
						|
+		if (adap->id == I2C_HW_B_CX2388x) {
 | 
						|
+			strlcpy(ir->c.name, "Leadtek IR", I2C_NAME_SIZE);
 | 
						|
+			ir->l.code_length = 8;
 | 
						|
+			ir->l.add_to_buf = add_to_buf_pvr2000;
 | 
						|
+		} else { /* bt8xx or cx2341x */
 | 
						|
+			strlcpy(ir->c.name, "Hauppauge IR", I2C_NAME_SIZE);
 | 
						|
+			ir->l.code_length = 13;
 | 
						|
+			ir->l.add_to_buf = add_to_buf_haup;
 | 
						|
+		}
 | 
						|
+		break;
 | 
						|
+	case 0x30:
 | 
						|
+		strlcpy(ir->c.name, "KNC ONE IR", I2C_NAME_SIZE);
 | 
						|
+		ir->l.code_length = 8;
 | 
						|
+		ir->l.add_to_buf = add_to_buf_knc1;
 | 
						|
+		break;
 | 
						|
+	case 0x21:
 | 
						|
+	case 0x23:
 | 
						|
+		pcf_probe(client, ir);
 | 
						|
+		strlcpy(ir->c.name, "TV-Box IR", I2C_NAME_SIZE);
 | 
						|
+		ir->l.code_length = 8;
 | 
						|
+		ir->l.add_to_buf = add_to_buf_pcf8574;
 | 
						|
+		break;
 | 
						|
+	default:
 | 
						|
+		/* shouldn't happen */
 | 
						|
+		printk("lirc_i2c: Huh? unknown i2c address (0x%02x)?\n", addr);
 | 
						|
+		kfree(ir);
 | 
						|
+		return -EINVAL;
 | 
						|
+	}
 | 
						|
+	printk(KERN_INFO "lirc_i2c: chip 0x%x found @ 0x%02x (%s)\n",
 | 
						|
+	       adap->id, addr, ir->c.name);
 | 
						|
+
 | 
						|
+	retval = lirc_register_driver(&ir->l);
 | 
						|
+
 | 
						|
+	if (retval < 0) {
 | 
						|
+		printk(KERN_ERR "lirc_i2c: failed to register driver!\n");
 | 
						|
+		kfree(ir);
 | 
						|
+		return retval;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	ir->l.minor = retval;
 | 
						|
+
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int ir_remove(struct i2c_client *client)
 | 
						|
+{
 | 
						|
+	struct IR *ir = i2c_get_clientdata(client);
 | 
						|
+
 | 
						|
+	/* unregister device */
 | 
						|
+	lirc_unregister_driver(ir->l.minor);
 | 
						|
+
 | 
						|
+	/* free memory */
 | 
						|
+	kfree(ir);
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int ir_command(struct i2c_client *client, unsigned int cmd, void *arg)
 | 
						|
+{
 | 
						|
+	/* nothing */
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int __init lirc_i2c_init(void)
 | 
						|
+{
 | 
						|
+	i2c_add_driver(&driver);
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void __exit lirc_i2c_exit(void)
 | 
						|
+{
 | 
						|
+	i2c_del_driver(&driver);
 | 
						|
+}
 | 
						|
+
 | 
						|
+MODULE_DESCRIPTION("Infrared receiver driver for Hauppauge and "
 | 
						|
+		   "Pixelview cards (i2c stack)");
 | 
						|
+MODULE_AUTHOR("Gerd Knorr, Michal Kochanowicz, Christoph Bartelmus, "
 | 
						|
+	      "Ulrich Mueller, Stefan Jahn, Jerome Brock");
 | 
						|
+MODULE_LICENSE("GPL");
 | 
						|
+
 | 
						|
+module_param(minor, int, S_IRUGO);
 | 
						|
+MODULE_PARM_DESC(minor, "Preferred minor device number");
 | 
						|
+
 | 
						|
+module_param(debug, bool, S_IRUGO | S_IWUSR);
 | 
						|
+MODULE_PARM_DESC(debug, "Enable debugging messages");
 | 
						|
+
 | 
						|
+module_init(lirc_i2c_init);
 | 
						|
+module_exit(lirc_i2c_exit);
 | 
						|
diff --git a/drivers/staging/lirc/lirc_igorplugusb.c b/drivers/staging/lirc/lirc_igorplugusb.c
 | 
						|
new file mode 100644
 | 
						|
index 0000000..bce600e
 | 
						|
--- /dev/null
 | 
						|
+++ b/drivers/staging/lirc/lirc_igorplugusb.c
 | 
						|
@@ -0,0 +1,555 @@
 | 
						|
+/*
 | 
						|
+ * 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/module.h>
 | 
						|
+#include <linux/kernel.h>
 | 
						|
+#include <linux/kmod.h>
 | 
						|
+#include <linux/sched.h>
 | 
						|
+#include <linux/errno.h>
 | 
						|
+#include <linux/fs.h>
 | 
						|
+#include <linux/usb.h>
 | 
						|
+#include <linux/time.h>
 | 
						|
+
 | 
						|
+#include <media/lirc.h>
 | 
						|
+#include <media/lirc_dev.h>
 | 
						|
+
 | 
						|
+
 | 
						|
+/* module identification */
 | 
						|
+#define DRIVER_VERSION		"0.1"
 | 
						|
+#define DRIVER_AUTHOR		\
 | 
						|
+	"Jan M. Hochstein <hochstein@algo.informatik.tu-darmstadt.de>"
 | 
						|
+#define DRIVER_DESC		"USB remote driver for LIRC"
 | 
						|
+#define DRIVER_NAME		"lirc_igorplugusb"
 | 
						|
+
 | 
						|
+/* debugging support */
 | 
						|
+#ifdef CONFIG_USB_DEBUG
 | 
						|
+static int debug = 1;
 | 
						|
+#else
 | 
						|
+static int debug;
 | 
						|
+#endif
 | 
						|
+
 | 
						|
+#define dprintk(fmt, args...)					\
 | 
						|
+	do {							\
 | 
						|
+		if (debug)					\
 | 
						|
+			printk(KERN_DEBUG fmt, ## args);	\
 | 
						|
+	} while (0)
 | 
						|
+
 | 
						|
+/* One mode2 pulse/space has 4 bytes. */
 | 
						|
+#define CODE_LENGTH	     sizeof(int)
 | 
						|
+
 | 
						|
+/* Igor's firmware cannot record bursts longer than 36. */
 | 
						|
+#define DEVICE_BUFLEN	   36
 | 
						|
+
 | 
						|
+/*
 | 
						|
+ * Header at the beginning of the device's buffer:
 | 
						|
+ *	unsigned char data_length
 | 
						|
+ *	unsigned char data_start    (!=0 means ring-buffer overrun)
 | 
						|
+ *	unsigned char counter       (incremented by each burst)
 | 
						|
+ */
 | 
						|
+#define DEVICE_HEADERLEN	3
 | 
						|
+
 | 
						|
+/* This is for the gap */
 | 
						|
+#define ADDITIONAL_LIRC_BYTES   2
 | 
						|
+
 | 
						|
+/* times to poll per second */
 | 
						|
+#define SAMPLE_RATE	     100
 | 
						|
+static int sample_rate = SAMPLE_RATE;
 | 
						|
+
 | 
						|
+
 | 
						|
+/**** Igor's USB Request Codes */
 | 
						|
+
 | 
						|
+#define SET_INFRABUFFER_EMPTY   1
 | 
						|
+/**
 | 
						|
+ * Params: none
 | 
						|
+ * Answer: empty
 | 
						|
+ */
 | 
						|
+
 | 
						|
+#define GET_INFRACODE	   2
 | 
						|
+/**
 | 
						|
+ * Params:
 | 
						|
+ *   wValue: offset to begin reading infra buffer
 | 
						|
+ *
 | 
						|
+ * Answer: infra data
 | 
						|
+ */
 | 
						|
+
 | 
						|
+#define SET_DATAPORT_DIRECTION  3
 | 
						|
+/**
 | 
						|
+ * Params:
 | 
						|
+ *   wValue: (byte) 1 bit for each data port pin (0=in, 1=out)
 | 
						|
+ *
 | 
						|
+ * Answer: empty
 | 
						|
+ */
 | 
						|
+
 | 
						|
+#define GET_DATAPORT_DIRECTION  4
 | 
						|
+/**
 | 
						|
+ * Params: none
 | 
						|
+ *
 | 
						|
+ * Answer: (byte) 1 bit for each data port pin (0=in, 1=out)
 | 
						|
+ */
 | 
						|
+
 | 
						|
+#define SET_OUT_DATAPORT	5
 | 
						|
+/**
 | 
						|
+ * Params:
 | 
						|
+ *   wValue: byte to write to output data port
 | 
						|
+ *
 | 
						|
+ * Answer: empty
 | 
						|
+ */
 | 
						|
+
 | 
						|
+#define GET_OUT_DATAPORT	6
 | 
						|
+/**
 | 
						|
+ * Params: none
 | 
						|
+ *
 | 
						|
+ * Answer: least significant 3 bits read from output data port
 | 
						|
+ */
 | 
						|
+
 | 
						|
+#define GET_IN_DATAPORT	 7
 | 
						|
+/**
 | 
						|
+ * Params: none
 | 
						|
+ *
 | 
						|
+ * Answer: least significant 3 bits read from input data port
 | 
						|
+ */
 | 
						|
+
 | 
						|
+#define READ_EEPROM	     8
 | 
						|
+/**
 | 
						|
+ * Params:
 | 
						|
+ *   wValue: offset to begin reading EEPROM
 | 
						|
+ *
 | 
						|
+ * Answer: EEPROM bytes
 | 
						|
+ */
 | 
						|
+
 | 
						|
+#define WRITE_EEPROM	    9
 | 
						|
+/**
 | 
						|
+ * Params:
 | 
						|
+ *   wValue: offset to EEPROM byte
 | 
						|
+ *   wIndex: byte to write
 | 
						|
+ *
 | 
						|
+ * Answer: empty
 | 
						|
+ */
 | 
						|
+
 | 
						|
+#define SEND_RS232	      10
 | 
						|
+/**
 | 
						|
+ * Params:
 | 
						|
+ *   wValue: byte to send
 | 
						|
+ *
 | 
						|
+ * Answer: empty
 | 
						|
+ */
 | 
						|
+
 | 
						|
+#define RECV_RS232	      11
 | 
						|
+/**
 | 
						|
+ * Params: none
 | 
						|
+ *
 | 
						|
+ * Answer: byte received
 | 
						|
+ */
 | 
						|
+
 | 
						|
+#define SET_RS232_BAUD	  12
 | 
						|
+/**
 | 
						|
+ * Params:
 | 
						|
+ *   wValue: byte to write to UART bit rate register (UBRR)
 | 
						|
+ *
 | 
						|
+ * Answer: empty
 | 
						|
+ */
 | 
						|
+
 | 
						|
+#define GET_RS232_BAUD	  13
 | 
						|
+/**
 | 
						|
+ * Params: none
 | 
						|
+ *
 | 
						|
+ * Answer: byte read from UART bit rate register (UBRR)
 | 
						|
+ */
 | 
						|
+
 | 
						|
+
 | 
						|
+/* data structure for each usb remote */
 | 
						|
+struct igorplug {
 | 
						|
+
 | 
						|
+	/* usb */
 | 
						|
+	struct usb_device *usbdev;
 | 
						|
+	struct urb *urb_in;
 | 
						|
+	int devnum;
 | 
						|
+
 | 
						|
+	unsigned char *buf_in;
 | 
						|
+	unsigned int len_in;
 | 
						|
+	int in_space;
 | 
						|
+	struct timeval last_time;
 | 
						|
+
 | 
						|
+	dma_addr_t dma_in;
 | 
						|
+
 | 
						|
+	/* lirc */
 | 
						|
+	struct lirc_driver *d;
 | 
						|
+
 | 
						|
+	/* handle sending (init strings) */
 | 
						|
+	int send_flags;
 | 
						|
+	wait_queue_head_t wait_out;
 | 
						|
+};
 | 
						|
+
 | 
						|
+static int unregister_from_lirc(struct igorplug *ir)
 | 
						|
+{
 | 
						|
+	struct lirc_driver *d = ir->d;
 | 
						|
+	int devnum;
 | 
						|
+
 | 
						|
+	if (!ir->d)
 | 
						|
+		return -EINVAL;
 | 
						|
+
 | 
						|
+	devnum = ir->devnum;
 | 
						|
+	dprintk(DRIVER_NAME "[%d]: unregister from lirc called\n", devnum);
 | 
						|
+
 | 
						|
+	lirc_unregister_driver(d->minor);
 | 
						|
+
 | 
						|
+	printk(DRIVER_NAME "[%d]: usb remote disconnected\n", devnum);
 | 
						|
+
 | 
						|
+	kfree(d);
 | 
						|
+	ir->d = NULL;
 | 
						|
+	kfree(ir);
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int set_use_inc(void *data)
 | 
						|
+{
 | 
						|
+	struct igorplug *ir = data;
 | 
						|
+
 | 
						|
+	if (!ir) {
 | 
						|
+		printk(DRIVER_NAME "[?]: set_use_inc called with no context\n");
 | 
						|
+		return -EIO;
 | 
						|
+	}
 | 
						|
+	dprintk(DRIVER_NAME "[%d]: set use inc\n", ir->devnum);
 | 
						|
+
 | 
						|
+	if (!ir->usbdev)
 | 
						|
+		return -ENODEV;
 | 
						|
+
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void set_use_dec(void *data)
 | 
						|
+{
 | 
						|
+	struct igorplug *ir = data;
 | 
						|
+
 | 
						|
+	if (!ir) {
 | 
						|
+		printk(DRIVER_NAME "[?]: set_use_dec called with no context\n");
 | 
						|
+		return;
 | 
						|
+	}
 | 
						|
+	dprintk(DRIVER_NAME "[%d]: set use dec\n", ir->devnum);
 | 
						|
+}
 | 
						|
+
 | 
						|
+
 | 
						|
+/**
 | 
						|
+ * Called in user context.
 | 
						|
+ * return 0 if data was added to the buffer and
 | 
						|
+ * -ENODATA if none was available. This should add some number of bits
 | 
						|
+ * evenly divisible by code_length to the buffer
 | 
						|
+ */
 | 
						|
+static int usb_remote_poll(void *data, struct lirc_buffer *buf)
 | 
						|
+{
 | 
						|
+	int ret;
 | 
						|
+	struct igorplug *ir = (struct igorplug *)data;
 | 
						|
+
 | 
						|
+	if (!ir->usbdev)  /* Has the device been removed? */
 | 
						|
+		return -ENODEV;
 | 
						|
+
 | 
						|
+	memset(ir->buf_in, 0, ir->len_in);
 | 
						|
+
 | 
						|
+	ret = usb_control_msg(
 | 
						|
+	      ir->usbdev, usb_rcvctrlpipe(ir->usbdev, 0),
 | 
						|
+	      GET_INFRACODE, USB_TYPE_VENDOR|USB_DIR_IN,
 | 
						|
+	      0/* offset */, /*unused*/0,
 | 
						|
+	      ir->buf_in, ir->len_in,
 | 
						|
+	      /*timeout*/HZ * USB_CTRL_GET_TIMEOUT);
 | 
						|
+	if (ret > 0) {
 | 
						|
+		int i = DEVICE_HEADERLEN;
 | 
						|
+		int code, timediff;
 | 
						|
+		struct timeval now;
 | 
						|
+
 | 
						|
+		if (ret <= 1)  /* ACK packet has 1 byte --> ignore */
 | 
						|
+			return -ENODATA;
 | 
						|
+
 | 
						|
+		dprintk(DRIVER_NAME ": Got %d bytes. Header: %02x %02x %02x\n",
 | 
						|
+			ret, ir->buf_in[0], ir->buf_in[1], ir->buf_in[2]);
 | 
						|
+
 | 
						|
+		if (ir->buf_in[2] != 0) {
 | 
						|
+			printk(DRIVER_NAME "[%d]: Device buffer overrun.\n",
 | 
						|
+				ir->devnum);
 | 
						|
+			/* start at earliest byte */
 | 
						|
+			i = DEVICE_HEADERLEN + ir->buf_in[2];
 | 
						|
+			/* where are we now? space, gap or pulse? */
 | 
						|
+		}
 | 
						|
+
 | 
						|
+		do_gettimeofday(&now);
 | 
						|
+		timediff = now.tv_sec - ir->last_time.tv_sec;
 | 
						|
+		if (timediff + 1 > PULSE_MASK / 1000000)
 | 
						|
+			timediff = PULSE_MASK;
 | 
						|
+		else {
 | 
						|
+			timediff *= 1000000;
 | 
						|
+			timediff += now.tv_usec - ir->last_time.tv_usec;
 | 
						|
+		}
 | 
						|
+		ir->last_time.tv_sec = now.tv_sec;
 | 
						|
+		ir->last_time.tv_usec = now.tv_usec;
 | 
						|
+
 | 
						|
+		/* create leading gap  */
 | 
						|
+		code = timediff;
 | 
						|
+		lirc_buffer_write(buf, (unsigned char *)&code);
 | 
						|
+		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(buf, (unsigned char *)&code);
 | 
						|
+			/* 1 chunk = CODE_LENGTH bytes */
 | 
						|
+			ir->in_space ^= 1;
 | 
						|
+			++i;
 | 
						|
+		}
 | 
						|
+
 | 
						|
+		ret = usb_control_msg(
 | 
						|
+		      ir->usbdev, usb_rcvctrlpipe(ir->usbdev, 0),
 | 
						|
+		      SET_INFRABUFFER_EMPTY, USB_TYPE_VENDOR|USB_DIR_IN,
 | 
						|
+		      /*unused*/0, /*unused*/0,
 | 
						|
+		      /*dummy*/ir->buf_in, /*dummy*/ir->len_in,
 | 
						|
+		      /*timeout*/HZ * USB_CTRL_GET_TIMEOUT);
 | 
						|
+		if (ret < 0)
 | 
						|
+			printk(DRIVER_NAME "[%d]: SET_INFRABUFFER_EMPTY: "
 | 
						|
+			       "error %d\n", ir->devnum, ret);
 | 
						|
+		return 0;
 | 
						|
+	} else if (ret < 0)
 | 
						|
+		printk(DRIVER_NAME "[%d]: GET_INFRACODE: error %d\n",
 | 
						|
+			ir->devnum, ret);
 | 
						|
+
 | 
						|
+	return -ENODATA;
 | 
						|
+}
 | 
						|
+
 | 
						|
+
 | 
						|
+
 | 
						|
+static int usb_remote_probe(struct usb_interface *intf,
 | 
						|
+				const struct usb_device_id *id)
 | 
						|
+{
 | 
						|
+	struct usb_device *dev = NULL;
 | 
						|
+	struct usb_host_interface *idesc = NULL;
 | 
						|
+	struct usb_host_endpoint *ep_ctl2;
 | 
						|
+	struct igorplug *ir = NULL;
 | 
						|
+	struct lirc_driver *driver = NULL;
 | 
						|
+	int devnum, pipe, maxp;
 | 
						|
+	int minor = 0;
 | 
						|
+	char buf[63], name[128] = "";
 | 
						|
+	int mem_failure = 0;
 | 
						|
+	int ret;
 | 
						|
+
 | 
						|
+	dprintk(DRIVER_NAME ": usb probe called.\n");
 | 
						|
+
 | 
						|
+	dev = interface_to_usbdev(intf);
 | 
						|
+
 | 
						|
+	idesc = intf->cur_altsetting;
 | 
						|
+
 | 
						|
+	if (idesc->desc.bNumEndpoints != 1)
 | 
						|
+		return -ENODEV;
 | 
						|
+	ep_ctl2 = idesc->endpoint;
 | 
						|
+	if (((ep_ctl2->desc.bEndpointAddress & USB_ENDPOINT_DIR_MASK)
 | 
						|
+	    != USB_DIR_IN)
 | 
						|
+	    || (ep_ctl2->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
 | 
						|
+	    != USB_ENDPOINT_XFER_CONTROL)
 | 
						|
+		return -ENODEV;
 | 
						|
+	pipe = usb_rcvctrlpipe(dev, ep_ctl2->desc.bEndpointAddress);
 | 
						|
+	devnum = dev->devnum;
 | 
						|
+	maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
 | 
						|
+
 | 
						|
+	dprintk(DRIVER_NAME "[%d]: bytes_in_key=%lu maxp=%d\n",
 | 
						|
+		devnum, CODE_LENGTH, maxp);
 | 
						|
+
 | 
						|
+
 | 
						|
+	mem_failure = 0;
 | 
						|
+	ir = kzalloc(sizeof(struct igorplug), GFP_KERNEL);
 | 
						|
+	if (!ir) {
 | 
						|
+		mem_failure = 1;
 | 
						|
+		goto mem_failure_switch;
 | 
						|
+	}
 | 
						|
+	driver = kzalloc(sizeof(struct lirc_driver), GFP_KERNEL);
 | 
						|
+	if (!driver) {
 | 
						|
+		mem_failure = 2;
 | 
						|
+		goto mem_failure_switch;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	ir->buf_in = usb_alloc_coherent(dev,
 | 
						|
+			      DEVICE_BUFLEN+DEVICE_HEADERLEN,
 | 
						|
+			      GFP_ATOMIC, &ir->dma_in);
 | 
						|
+	if (!ir->buf_in) {
 | 
						|
+		mem_failure = 3;
 | 
						|
+		goto mem_failure_switch;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	strcpy(driver->name, DRIVER_NAME " ");
 | 
						|
+	driver->minor = -1;
 | 
						|
+	driver->code_length = CODE_LENGTH * 8; /* in bits */
 | 
						|
+	driver->features = LIRC_CAN_REC_MODE2;
 | 
						|
+	driver->data = ir;
 | 
						|
+	driver->chunk_size = CODE_LENGTH;
 | 
						|
+	driver->buffer_size = DEVICE_BUFLEN + ADDITIONAL_LIRC_BYTES;
 | 
						|
+	driver->set_use_inc = &set_use_inc;
 | 
						|
+	driver->set_use_dec = &set_use_dec;
 | 
						|
+	driver->sample_rate = sample_rate;    /* per second */
 | 
						|
+	driver->add_to_buf = &usb_remote_poll;
 | 
						|
+	driver->dev = &intf->dev;
 | 
						|
+	driver->owner = THIS_MODULE;
 | 
						|
+
 | 
						|
+	init_waitqueue_head(&ir->wait_out);
 | 
						|
+
 | 
						|
+	minor = lirc_register_driver(driver);
 | 
						|
+	if (minor < 0)
 | 
						|
+		mem_failure = 9;
 | 
						|
+
 | 
						|
+mem_failure_switch:
 | 
						|
+
 | 
						|
+	switch (mem_failure) {
 | 
						|
+	case 9:
 | 
						|
+		usb_free_coherent(dev, DEVICE_BUFLEN+DEVICE_HEADERLEN,
 | 
						|
+			ir->buf_in, ir->dma_in);
 | 
						|
+	case 3:
 | 
						|
+		kfree(driver);
 | 
						|
+	case 2:
 | 
						|
+		kfree(ir);
 | 
						|
+	case 1:
 | 
						|
+		printk(DRIVER_NAME "[%d]: out of memory (code=%d)\n",
 | 
						|
+			devnum, mem_failure);
 | 
						|
+		return -ENOMEM;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	driver->minor = minor;
 | 
						|
+	ir->d = driver;
 | 
						|
+	ir->devnum = devnum;
 | 
						|
+	ir->usbdev = dev;
 | 
						|
+	ir->len_in = DEVICE_BUFLEN+DEVICE_HEADERLEN;
 | 
						|
+	ir->in_space = 1; /* First mode2 event is a space. */
 | 
						|
+	do_gettimeofday(&ir->last_time);
 | 
						|
+
 | 
						|
+	if (dev->descriptor.iManufacturer
 | 
						|
+	    && usb_string(dev, dev->descriptor.iManufacturer,
 | 
						|
+			  buf, sizeof(buf)) > 0)
 | 
						|
+		strlcpy(name, buf, sizeof(name));
 | 
						|
+	if (dev->descriptor.iProduct
 | 
						|
+	    && usb_string(dev, dev->descriptor.iProduct, buf, sizeof(buf)) > 0)
 | 
						|
+		snprintf(name + strlen(name), sizeof(name) - strlen(name),
 | 
						|
+			 " %s", buf);
 | 
						|
+	printk(DRIVER_NAME "[%d]: %s on usb%d:%d\n", devnum, name,
 | 
						|
+	       dev->bus->busnum, devnum);
 | 
						|
+
 | 
						|
+	/* clear device buffer */
 | 
						|
+	ret = usb_control_msg(ir->usbdev, usb_rcvctrlpipe(ir->usbdev, 0),
 | 
						|
+		SET_INFRABUFFER_EMPTY, USB_TYPE_VENDOR|USB_DIR_IN,
 | 
						|
+		/*unused*/0, /*unused*/0,
 | 
						|
+		/*dummy*/ir->buf_in, /*dummy*/ir->len_in,
 | 
						|
+		/*timeout*/HZ * USB_CTRL_GET_TIMEOUT);
 | 
						|
+	if (ret < 0)
 | 
						|
+		printk(DRIVER_NAME "[%d]: SET_INFRABUFFER_EMPTY: error %d\n",
 | 
						|
+			devnum, ret);
 | 
						|
+
 | 
						|
+	usb_set_intfdata(intf, ir);
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+
 | 
						|
+static void usb_remote_disconnect(struct usb_interface *intf)
 | 
						|
+{
 | 
						|
+	struct usb_device *dev = interface_to_usbdev(intf);
 | 
						|
+	struct igorplug *ir = usb_get_intfdata(intf);
 | 
						|
+	usb_set_intfdata(intf, NULL);
 | 
						|
+
 | 
						|
+	if (!ir || !ir->d)
 | 
						|
+		return;
 | 
						|
+
 | 
						|
+	ir->usbdev = NULL;
 | 
						|
+	wake_up_all(&ir->wait_out);
 | 
						|
+
 | 
						|
+	usb_free_coherent(dev, ir->len_in, ir->buf_in, ir->dma_in);
 | 
						|
+
 | 
						|
+	unregister_from_lirc(ir);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static struct usb_device_id usb_remote_id_table[] = {
 | 
						|
+	/* Igor Plug USB (Atmel's Manufact. ID) */
 | 
						|
+	{ USB_DEVICE(0x03eb, 0x0002) },
 | 
						|
+
 | 
						|
+	/* Terminating entry */
 | 
						|
+	{ }
 | 
						|
+};
 | 
						|
+
 | 
						|
+static struct usb_driver usb_remote_driver = {
 | 
						|
+	.name =		DRIVER_NAME,
 | 
						|
+	.probe =	usb_remote_probe,
 | 
						|
+	.disconnect =	usb_remote_disconnect,
 | 
						|
+	.id_table =	usb_remote_id_table
 | 
						|
+};
 | 
						|
+
 | 
						|
+static int __init usb_remote_init(void)
 | 
						|
+{
 | 
						|
+	int i;
 | 
						|
+
 | 
						|
+	printk(KERN_INFO "\n"
 | 
						|
+	       DRIVER_NAME ": " DRIVER_DESC " v" DRIVER_VERSION "\n");
 | 
						|
+	printk(DRIVER_NAME ": " DRIVER_AUTHOR "\n");
 | 
						|
+	dprintk(DRIVER_NAME ": debug mode enabled\n");
 | 
						|
+
 | 
						|
+	i = usb_register(&usb_remote_driver);
 | 
						|
+	if (i < 0) {
 | 
						|
+		printk(DRIVER_NAME ": usb register failed, result = %d\n", i);
 | 
						|
+		return -ENODEV;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void __exit usb_remote_exit(void)
 | 
						|
+{
 | 
						|
+	usb_deregister(&usb_remote_driver);
 | 
						|
+}
 | 
						|
+
 | 
						|
+module_init(usb_remote_init);
 | 
						|
+module_exit(usb_remote_exit);
 | 
						|
+
 | 
						|
+#include <linux/vermagic.h>
 | 
						|
+MODULE_INFO(vermagic, VERMAGIC_STRING);
 | 
						|
+
 | 
						|
+MODULE_DESCRIPTION(DRIVER_DESC);
 | 
						|
+MODULE_AUTHOR(DRIVER_AUTHOR);
 | 
						|
+MODULE_LICENSE("GPL");
 | 
						|
+MODULE_DEVICE_TABLE(usb, usb_remote_id_table);
 | 
						|
+
 | 
						|
+module_param(sample_rate, int, S_IRUGO | S_IWUSR);
 | 
						|
+MODULE_PARM_DESC(sample_rate, "Sampling rate in Hz (default: 100)");
 | 
						|
+
 | 
						|
diff --git a/drivers/staging/lirc/lirc_imon.c b/drivers/staging/lirc/lirc_imon.c
 | 
						|
new file mode 100644
 | 
						|
index 0000000..43856d6
 | 
						|
--- /dev/null
 | 
						|
+++ b/drivers/staging/lirc/lirc_imon.c
 | 
						|
@@ -0,0 +1,1058 @@
 | 
						|
+/*
 | 
						|
+ *   lirc_imon.c:  LIRC/VFD/LCD driver for SoundGraph iMON IR/VFD/LCD
 | 
						|
+ *		   including the iMON PAD model
 | 
						|
+ *
 | 
						|
+ *   Copyright(C) 2004  Venky Raju(dev@venky.ws)
 | 
						|
+ *   Copyright(C) 2009  Jarod Wilson <jarod@wilsonet.com>
 | 
						|
+ *
 | 
						|
+ *   lirc_imon is free software; you can redistribute it and/or modify
 | 
						|
+ *   it under the terms of the GNU General Public License as published by
 | 
						|
+ *   the Free Software Foundation; either version 2 of the License, or
 | 
						|
+ *   (at your option) any later version.
 | 
						|
+ *
 | 
						|
+ *   This program is distributed in the hope that it will be useful,
 | 
						|
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
						|
+ *   GNU General Public License for more details.
 | 
						|
+ *
 | 
						|
+ *   You should have received a copy of the GNU General Public License
 | 
						|
+ *   along with this program; if not, write to the Free Software
 | 
						|
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 | 
						|
+ */
 | 
						|
+
 | 
						|
+#include <linux/errno.h>
 | 
						|
+#include <linux/init.h>
 | 
						|
+#include <linux/kernel.h>
 | 
						|
+#include <linux/module.h>
 | 
						|
+#include <linux/slab.h>
 | 
						|
+#include <linux/uaccess.h>
 | 
						|
+#include <linux/usb.h>
 | 
						|
+
 | 
						|
+#include <media/lirc.h>
 | 
						|
+#include <media/lirc_dev.h>
 | 
						|
+
 | 
						|
+
 | 
						|
+#define MOD_AUTHOR	"Venky Raju <dev@venky.ws>"
 | 
						|
+#define MOD_DESC	"Driver for SoundGraph iMON MultiMedia IR/Display"
 | 
						|
+#define MOD_NAME	"lirc_imon"
 | 
						|
+#define MOD_VERSION	"0.8"
 | 
						|
+
 | 
						|
+#define DISPLAY_MINOR_BASE	144
 | 
						|
+#define DEVICE_NAME	"lcd%d"
 | 
						|
+
 | 
						|
+#define BUF_CHUNK_SIZE	4
 | 
						|
+#define BUF_SIZE	128
 | 
						|
+
 | 
						|
+#define BIT_DURATION	250	/* each bit received is 250us */
 | 
						|
+
 | 
						|
+/*** P R O T O T Y P E S ***/
 | 
						|
+
 | 
						|
+/* USB Callback prototypes */
 | 
						|
+static int imon_probe(struct usb_interface *interface,
 | 
						|
+		      const struct usb_device_id *id);
 | 
						|
+static void imon_disconnect(struct usb_interface *interface);
 | 
						|
+static void usb_rx_callback(struct urb *urb);
 | 
						|
+static void usb_tx_callback(struct urb *urb);
 | 
						|
+
 | 
						|
+/* suspend/resume support */
 | 
						|
+static int imon_resume(struct usb_interface *intf);
 | 
						|
+static int imon_suspend(struct usb_interface *intf, pm_message_t message);
 | 
						|
+
 | 
						|
+/* Display file_operations function prototypes */
 | 
						|
+static int display_open(struct inode *inode, struct file *file);
 | 
						|
+static int display_close(struct inode *inode, struct file *file);
 | 
						|
+
 | 
						|
+/* VFD write operation */
 | 
						|
+static ssize_t vfd_write(struct file *file, const char *buf,
 | 
						|
+			 size_t n_bytes, loff_t *pos);
 | 
						|
+
 | 
						|
+/* LIRC driver function prototypes */
 | 
						|
+static int ir_open(void *data);
 | 
						|
+static void ir_close(void *data);
 | 
						|
+
 | 
						|
+/* Driver init/exit prototypes */
 | 
						|
+static int __init imon_init(void);
 | 
						|
+static void __exit imon_exit(void);
 | 
						|
+
 | 
						|
+/*** G L O B A L S ***/
 | 
						|
+#define IMON_DATA_BUF_SZ	35
 | 
						|
+
 | 
						|
+struct imon_context {
 | 
						|
+	struct usb_device *usbdev;
 | 
						|
+	/* Newer devices have two interfaces */
 | 
						|
+	int display;			/* not all controllers do */
 | 
						|
+	int display_isopen;		/* display port has been opened */
 | 
						|
+	int ir_isopen;			/* IR port open	*/
 | 
						|
+	int dev_present;		/* USB device presence */
 | 
						|
+	struct mutex ctx_lock;		/* to lock this object */
 | 
						|
+	wait_queue_head_t remove_ok;	/* For unexpected USB disconnects */
 | 
						|
+
 | 
						|
+	int vfd_proto_6p;		/* some VFD require a 6th packet */
 | 
						|
+
 | 
						|
+	struct lirc_driver *driver;
 | 
						|
+	struct usb_endpoint_descriptor *rx_endpoint;
 | 
						|
+	struct usb_endpoint_descriptor *tx_endpoint;
 | 
						|
+	struct urb *rx_urb;
 | 
						|
+	struct urb *tx_urb;
 | 
						|
+	unsigned char usb_rx_buf[8];
 | 
						|
+	unsigned char usb_tx_buf[8];
 | 
						|
+
 | 
						|
+	struct rx_data {
 | 
						|
+		int count;		/* length of 0 or 1 sequence */
 | 
						|
+		int prev_bit;		/* logic level of sequence */
 | 
						|
+		int initial_space;	/* initial space flag */
 | 
						|
+	} rx;
 | 
						|
+
 | 
						|
+	struct tx_t {
 | 
						|
+		unsigned char data_buf[IMON_DATA_BUF_SZ]; /* user data buffer */
 | 
						|
+		struct completion finished;	/* wait for write to finish */
 | 
						|
+		atomic_t busy;			/* write in progress */
 | 
						|
+		int status;			/* status of tx completion */
 | 
						|
+	} tx;
 | 
						|
+};
 | 
						|
+
 | 
						|
+static struct file_operations display_fops = {
 | 
						|
+	.owner		= THIS_MODULE,
 | 
						|
+	.open		= &display_open,
 | 
						|
+	.write		= &vfd_write,
 | 
						|
+	.release	= &display_close
 | 
						|
+};
 | 
						|
+
 | 
						|
+/*
 | 
						|
+ * USB Device ID for iMON USB Control Boards
 | 
						|
+ *
 | 
						|
+ * The Windows drivers contain 6 different inf files, more or less one for
 | 
						|
+ * each new device until the 0x0034-0x0046 devices, which all use the same
 | 
						|
+ * driver. Some of the devices in the 34-46 range haven't been definitively
 | 
						|
+ * identified yet. Early devices have either a TriGem Computer, Inc. or a
 | 
						|
+ * Samsung vendor ID (0x0aa8 and 0x04e8 respectively), while all later
 | 
						|
+ * devices use the SoundGraph vendor ID (0x15c2).
 | 
						|
+ */
 | 
						|
+static struct usb_device_id imon_usb_id_table[] = {
 | 
						|
+	/* TriGem iMON (IR only) -- TG_iMON.inf */
 | 
						|
+	{ USB_DEVICE(0x0aa8, 0x8001) },
 | 
						|
+
 | 
						|
+	/* SoundGraph iMON (IR only) -- sg_imon.inf */
 | 
						|
+	{ USB_DEVICE(0x04e8, 0xff30) },
 | 
						|
+
 | 
						|
+	/* SoundGraph iMON VFD (IR & VFD) -- iMON_VFD.inf */
 | 
						|
+	{ USB_DEVICE(0x0aa8, 0xffda) },
 | 
						|
+
 | 
						|
+	/* SoundGraph iMON SS (IR & VFD) -- iMON_SS.inf */
 | 
						|
+	{ USB_DEVICE(0x15c2, 0xffda) },
 | 
						|
+
 | 
						|
+	{}
 | 
						|
+};
 | 
						|
+
 | 
						|
+/* Some iMON VFD models requires a 6th packet for VFD writes */
 | 
						|
+static struct usb_device_id vfd_proto_6p_list[] = {
 | 
						|
+	{ USB_DEVICE(0x15c2, 0xffda) },
 | 
						|
+	{}
 | 
						|
+};
 | 
						|
+
 | 
						|
+/* Some iMON devices have no lcd/vfd, don't set one up */
 | 
						|
+static struct usb_device_id ir_only_list[] = {
 | 
						|
+	{ USB_DEVICE(0x0aa8, 0x8001) },
 | 
						|
+	{ USB_DEVICE(0x04e8, 0xff30) },
 | 
						|
+	{}
 | 
						|
+};
 | 
						|
+
 | 
						|
+/* USB Device data */
 | 
						|
+static struct usb_driver imon_driver = {
 | 
						|
+	.name		= MOD_NAME,
 | 
						|
+	.probe		= imon_probe,
 | 
						|
+	.disconnect	= imon_disconnect,
 | 
						|
+	.suspend	= imon_suspend,
 | 
						|
+	.resume		= imon_resume,
 | 
						|
+	.id_table	= imon_usb_id_table,
 | 
						|
+};
 | 
						|
+
 | 
						|
+static struct usb_class_driver imon_class = {
 | 
						|
+	.name		= DEVICE_NAME,
 | 
						|
+	.fops		= &display_fops,
 | 
						|
+	.minor_base	= DISPLAY_MINOR_BASE,
 | 
						|
+};
 | 
						|
+
 | 
						|
+/* to prevent races between open() and disconnect(), probing, etc */
 | 
						|
+static DEFINE_MUTEX(driver_lock);
 | 
						|
+
 | 
						|
+static int debug;
 | 
						|
+
 | 
						|
+/***  M O D U L E   C O D E ***/
 | 
						|
+
 | 
						|
+MODULE_AUTHOR(MOD_AUTHOR);
 | 
						|
+MODULE_DESCRIPTION(MOD_DESC);
 | 
						|
+MODULE_VERSION(MOD_VERSION);
 | 
						|
+MODULE_LICENSE("GPL");
 | 
						|
+MODULE_DEVICE_TABLE(usb, imon_usb_id_table);
 | 
						|
+module_param(debug, int, S_IRUGO | S_IWUSR);
 | 
						|
+MODULE_PARM_DESC(debug, "Debug messages: 0=no, 1=yes(default: no)");
 | 
						|
+
 | 
						|
+static void free_imon_context(struct imon_context *context)
 | 
						|
+{
 | 
						|
+	struct device *dev = context->driver->dev;
 | 
						|
+	usb_free_urb(context->tx_urb);
 | 
						|
+	usb_free_urb(context->rx_urb);
 | 
						|
+	lirc_buffer_free(context->driver->rbuf);
 | 
						|
+	kfree(context->driver->rbuf);
 | 
						|
+	kfree(context->driver);
 | 
						|
+	kfree(context);
 | 
						|
+
 | 
						|
+	dev_dbg(dev, "%s: iMON context freed\n", __func__);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void deregister_from_lirc(struct imon_context *context)
 | 
						|
+{
 | 
						|
+	int retval;
 | 
						|
+	int minor = context->driver->minor;
 | 
						|
+
 | 
						|
+	retval = lirc_unregister_driver(minor);
 | 
						|
+	if (retval)
 | 
						|
+		err("%s: unable to deregister from lirc(%d)",
 | 
						|
+			__func__, retval);
 | 
						|
+	else
 | 
						|
+		printk(KERN_INFO MOD_NAME ": Deregistered iMON driver "
 | 
						|
+		       "(minor:%d)\n", minor);
 | 
						|
+
 | 
						|
+}
 | 
						|
+
 | 
						|
+/**
 | 
						|
+ * Called when the Display device (e.g. /dev/lcd0)
 | 
						|
+ * is opened by the application.
 | 
						|
+ */
 | 
						|
+static int display_open(struct inode *inode, struct file *file)
 | 
						|
+{
 | 
						|
+	struct usb_interface *interface;
 | 
						|
+	struct imon_context *context = NULL;
 | 
						|
+	int subminor;
 | 
						|
+	int retval = 0;
 | 
						|
+
 | 
						|
+	/* prevent races with disconnect */
 | 
						|
+	mutex_lock(&driver_lock);
 | 
						|
+
 | 
						|
+	subminor = iminor(inode);
 | 
						|
+	interface = usb_find_interface(&imon_driver, subminor);
 | 
						|
+	if (!interface) {
 | 
						|
+		err("%s: could not find interface for minor %d",
 | 
						|
+		    __func__, subminor);
 | 
						|
+		retval = -ENODEV;
 | 
						|
+		goto exit;
 | 
						|
+	}
 | 
						|
+	context = usb_get_intfdata(interface);
 | 
						|
+
 | 
						|
+	if (!context) {
 | 
						|
+		err("%s: no context found for minor %d",
 | 
						|
+					__func__, subminor);
 | 
						|
+		retval = -ENODEV;
 | 
						|
+		goto exit;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	mutex_lock(&context->ctx_lock);
 | 
						|
+
 | 
						|
+	if (!context->display) {
 | 
						|
+		err("%s: display not supported by device", __func__);
 | 
						|
+		retval = -ENODEV;
 | 
						|
+	} else if (context->display_isopen) {
 | 
						|
+		err("%s: display port is already open", __func__);
 | 
						|
+		retval = -EBUSY;
 | 
						|
+	} else {
 | 
						|
+		context->display_isopen = 1;
 | 
						|
+		file->private_data = context;
 | 
						|
+		dev_info(context->driver->dev, "display port opened\n");
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	mutex_unlock(&context->ctx_lock);
 | 
						|
+
 | 
						|
+exit:
 | 
						|
+	mutex_unlock(&driver_lock);
 | 
						|
+	return retval;
 | 
						|
+}
 | 
						|
+
 | 
						|
+/**
 | 
						|
+ * Called when the display device (e.g. /dev/lcd0)
 | 
						|
+ * is closed by the application.
 | 
						|
+ */
 | 
						|
+static int display_close(struct inode *inode, struct file *file)
 | 
						|
+{
 | 
						|
+	struct imon_context *context = NULL;
 | 
						|
+	int retval = 0;
 | 
						|
+
 | 
						|
+	context = (struct imon_context *)file->private_data;
 | 
						|
+
 | 
						|
+	if (!context) {
 | 
						|
+		err("%s: no context for device", __func__);
 | 
						|
+		return -ENODEV;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	mutex_lock(&context->ctx_lock);
 | 
						|
+
 | 
						|
+	if (!context->display) {
 | 
						|
+		err("%s: display not supported by device", __func__);
 | 
						|
+		retval = -ENODEV;
 | 
						|
+	} else if (!context->display_isopen) {
 | 
						|
+		err("%s: display is not open", __func__);
 | 
						|
+		retval = -EIO;
 | 
						|
+	} else {
 | 
						|
+		context->display_isopen = 0;
 | 
						|
+		dev_info(context->driver->dev, "display port closed\n");
 | 
						|
+		if (!context->dev_present && !context->ir_isopen) {
 | 
						|
+			/*
 | 
						|
+			 * Device disconnected before close and IR port is not
 | 
						|
+			 * open. If IR port is open, context will be deleted by
 | 
						|
+			 * ir_close.
 | 
						|
+			 */
 | 
						|
+			mutex_unlock(&context->ctx_lock);
 | 
						|
+			free_imon_context(context);
 | 
						|
+			return retval;
 | 
						|
+		}
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	mutex_unlock(&context->ctx_lock);
 | 
						|
+	return retval;
 | 
						|
+}
 | 
						|
+
 | 
						|
+/**
 | 
						|
+ * Sends a packet to the device -- this function must be called
 | 
						|
+ * with context->ctx_lock held.
 | 
						|
+ */
 | 
						|
+static int send_packet(struct imon_context *context)
 | 
						|
+{
 | 
						|
+	unsigned int pipe;
 | 
						|
+	int interval = 0;
 | 
						|
+	int retval = 0;
 | 
						|
+	struct usb_ctrlrequest *control_req = NULL;
 | 
						|
+
 | 
						|
+	/* Check if we need to use control or interrupt urb */
 | 
						|
+	pipe = usb_sndintpipe(context->usbdev,
 | 
						|
+			      context->tx_endpoint->bEndpointAddress);
 | 
						|
+	interval = context->tx_endpoint->bInterval;
 | 
						|
+
 | 
						|
+	usb_fill_int_urb(context->tx_urb, context->usbdev, pipe,
 | 
						|
+			 context->usb_tx_buf,
 | 
						|
+			 sizeof(context->usb_tx_buf),
 | 
						|
+			 usb_tx_callback, context, interval);
 | 
						|
+
 | 
						|
+	context->tx_urb->actual_length = 0;
 | 
						|
+
 | 
						|
+	init_completion(&context->tx.finished);
 | 
						|
+	atomic_set(&(context->tx.busy), 1);
 | 
						|
+
 | 
						|
+	retval = usb_submit_urb(context->tx_urb, GFP_KERNEL);
 | 
						|
+	if (retval) {
 | 
						|
+		atomic_set(&(context->tx.busy), 0);
 | 
						|
+		err("%s: error submitting urb(%d)", __func__, retval);
 | 
						|
+	} else {
 | 
						|
+		/* Wait for transmission to complete (or abort) */
 | 
						|
+		mutex_unlock(&context->ctx_lock);
 | 
						|
+		retval = wait_for_completion_interruptible(
 | 
						|
+				&context->tx.finished);
 | 
						|
+		if (retval)
 | 
						|
+			err("%s: task interrupted", __func__);
 | 
						|
+		mutex_lock(&context->ctx_lock);
 | 
						|
+
 | 
						|
+		retval = context->tx.status;
 | 
						|
+		if (retval)
 | 
						|
+			err("%s: packet tx failed (%d)", __func__, retval);
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	kfree(control_req);
 | 
						|
+
 | 
						|
+	return retval;
 | 
						|
+}
 | 
						|
+
 | 
						|
+/**
 | 
						|
+ * Writes data to the VFD.  The iMON VFD is 2x16 characters
 | 
						|
+ * and requires data in 5 consecutive USB interrupt packets,
 | 
						|
+ * each packet but the last carrying 7 bytes.
 | 
						|
+ *
 | 
						|
+ * I don't know if the VFD board supports features such as
 | 
						|
+ * scrolling, clearing rows, blanking, etc. so at
 | 
						|
+ * the caller must provide a full screen of data.  If fewer
 | 
						|
+ * than 32 bytes are provided spaces will be appended to
 | 
						|
+ * generate a full screen.
 | 
						|
+ */
 | 
						|
+static ssize_t vfd_write(struct file *file, const char *buf,
 | 
						|
+			 size_t n_bytes, loff_t *pos)
 | 
						|
+{
 | 
						|
+	int i;
 | 
						|
+	int offset;
 | 
						|
+	int seq;
 | 
						|
+	int retval = 0;
 | 
						|
+	struct imon_context *context;
 | 
						|
+	const unsigned char vfd_packet6[] = {
 | 
						|
+		0x01, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF };
 | 
						|
+	int *data_buf;
 | 
						|
+
 | 
						|
+	context = (struct imon_context *)file->private_data;
 | 
						|
+	if (!context) {
 | 
						|
+		err("%s: no context for device", __func__);
 | 
						|
+		return -ENODEV;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	mutex_lock(&context->ctx_lock);
 | 
						|
+
 | 
						|
+	if (!context->dev_present) {
 | 
						|
+		err("%s: no iMON device present", __func__);
 | 
						|
+		retval = -ENODEV;
 | 
						|
+		goto exit;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	if (n_bytes <= 0 || n_bytes > IMON_DATA_BUF_SZ - 3) {
 | 
						|
+		err("%s: invalid payload size", __func__);
 | 
						|
+		retval = -EINVAL;
 | 
						|
+		goto exit;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	data_buf = memdup_user(buf, n_bytes);
 | 
						|
+	if (IS_ERR(data_buf)) {
 | 
						|
+		retval = PTR_ERR(data_buf);
 | 
						|
+		goto exit;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	memcpy(context->tx.data_buf, data_buf, n_bytes);
 | 
						|
+
 | 
						|
+	/* Pad with spaces */
 | 
						|
+	for (i = n_bytes; i < IMON_DATA_BUF_SZ - 3; ++i)
 | 
						|
+		context->tx.data_buf[i] = ' ';
 | 
						|
+
 | 
						|
+	for (i = IMON_DATA_BUF_SZ - 3; i < IMON_DATA_BUF_SZ; ++i)
 | 
						|
+		context->tx.data_buf[i] = 0xFF;
 | 
						|
+
 | 
						|
+	offset = 0;
 | 
						|
+	seq = 0;
 | 
						|
+
 | 
						|
+	do {
 | 
						|
+		memcpy(context->usb_tx_buf, context->tx.data_buf + offset, 7);
 | 
						|
+		context->usb_tx_buf[7] = (unsigned char) seq;
 | 
						|
+
 | 
						|
+		retval = send_packet(context);
 | 
						|
+		if (retval) {
 | 
						|
+			err("%s: send packet failed for packet #%d",
 | 
						|
+					__func__, seq/2);
 | 
						|
+			goto exit;
 | 
						|
+		} else {
 | 
						|
+			seq += 2;
 | 
						|
+			offset += 7;
 | 
						|
+		}
 | 
						|
+
 | 
						|
+	} while (offset < IMON_DATA_BUF_SZ);
 | 
						|
+
 | 
						|
+	if (context->vfd_proto_6p) {
 | 
						|
+		/* Send packet #6 */
 | 
						|
+		memcpy(context->usb_tx_buf, &vfd_packet6, sizeof(vfd_packet6));
 | 
						|
+		context->usb_tx_buf[7] = (unsigned char) seq;
 | 
						|
+		retval = send_packet(context);
 | 
						|
+		if (retval)
 | 
						|
+			err("%s: send packet failed for packet #%d",
 | 
						|
+					__func__, seq/2);
 | 
						|
+	}
 | 
						|
+
 | 
						|
+exit:
 | 
						|
+	mutex_unlock(&context->ctx_lock);
 | 
						|
+
 | 
						|
+	return (!retval) ? n_bytes : retval;
 | 
						|
+}
 | 
						|
+
 | 
						|
+/**
 | 
						|
+ * Callback function for USB core API: transmit data
 | 
						|
+ */
 | 
						|
+static void usb_tx_callback(struct urb *urb)
 | 
						|
+{
 | 
						|
+	struct imon_context *context;
 | 
						|
+
 | 
						|
+	if (!urb)
 | 
						|
+		return;
 | 
						|
+	context = (struct imon_context *)urb->context;
 | 
						|
+	if (!context)
 | 
						|
+		return;
 | 
						|
+
 | 
						|
+	context->tx.status = urb->status;
 | 
						|
+
 | 
						|
+	/* notify waiters that write has finished */
 | 
						|
+	atomic_set(&context->tx.busy, 0);
 | 
						|
+	complete(&context->tx.finished);
 | 
						|
+
 | 
						|
+	return;
 | 
						|
+}
 | 
						|
+
 | 
						|
+/**
 | 
						|
+ * Called by lirc_dev when the application opens /dev/lirc
 | 
						|
+ */
 | 
						|
+static int ir_open(void *data)
 | 
						|
+{
 | 
						|
+	int retval = 0;
 | 
						|
+	struct imon_context *context;
 | 
						|
+
 | 
						|
+	/* prevent races with disconnect */
 | 
						|
+	mutex_lock(&driver_lock);
 | 
						|
+
 | 
						|
+	context = (struct imon_context *)data;
 | 
						|
+
 | 
						|
+	/* initial IR protocol decode variables */
 | 
						|
+	context->rx.count = 0;
 | 
						|
+	context->rx.initial_space = 1;
 | 
						|
+	context->rx.prev_bit = 0;
 | 
						|
+
 | 
						|
+	context->ir_isopen = 1;
 | 
						|
+	dev_info(context->driver->dev, "IR port opened\n");
 | 
						|
+
 | 
						|
+	mutex_unlock(&driver_lock);
 | 
						|
+	return retval;
 | 
						|
+}
 | 
						|
+
 | 
						|
+/**
 | 
						|
+ * Called by lirc_dev when the application closes /dev/lirc
 | 
						|
+ */
 | 
						|
+static void ir_close(void *data)
 | 
						|
+{
 | 
						|
+	struct imon_context *context;
 | 
						|
+
 | 
						|
+	context = (struct imon_context *)data;
 | 
						|
+	if (!context) {
 | 
						|
+		err("%s: no context for device", __func__);
 | 
						|
+		return;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	mutex_lock(&context->ctx_lock);
 | 
						|
+
 | 
						|
+	context->ir_isopen = 0;
 | 
						|
+	dev_info(context->driver->dev, "IR port closed\n");
 | 
						|
+
 | 
						|
+	if (!context->dev_present) {
 | 
						|
+		/*
 | 
						|
+		 * Device disconnected while IR port was still open. Driver
 | 
						|
+		 * was not deregistered at disconnect time, so do it now.
 | 
						|
+		 */
 | 
						|
+		deregister_from_lirc(context);
 | 
						|
+
 | 
						|
+		if (!context->display_isopen) {
 | 
						|
+			mutex_unlock(&context->ctx_lock);
 | 
						|
+			free_imon_context(context);
 | 
						|
+			return;
 | 
						|
+		}
 | 
						|
+		/*
 | 
						|
+		 * If display port is open, context will be deleted by
 | 
						|
+		 * display_close
 | 
						|
+		 */
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	mutex_unlock(&context->ctx_lock);
 | 
						|
+	return;
 | 
						|
+}
 | 
						|
+
 | 
						|
+/**
 | 
						|
+ * Convert bit count to time duration (in us) and submit
 | 
						|
+ * the value to lirc_dev.
 | 
						|
+ */
 | 
						|
+static void submit_data(struct imon_context *context)
 | 
						|
+{
 | 
						|
+	unsigned char buf[4];
 | 
						|
+	int value = context->rx.count;
 | 
						|
+	int i;
 | 
						|
+
 | 
						|
+	dev_dbg(context->driver->dev, "submitting data to LIRC\n");
 | 
						|
+
 | 
						|
+	value *= BIT_DURATION;
 | 
						|
+	value &= PULSE_MASK;
 | 
						|
+	if (context->rx.prev_bit)
 | 
						|
+		value |= PULSE_BIT;
 | 
						|
+
 | 
						|
+	for (i = 0; i < 4; ++i)
 | 
						|
+		buf[i] = value>>(i*8);
 | 
						|
+
 | 
						|
+	lirc_buffer_write(context->driver->rbuf, buf);
 | 
						|
+	wake_up(&context->driver->rbuf->wait_poll);
 | 
						|
+	return;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static inline int tv2int(const struct timeval *a, const struct timeval *b)
 | 
						|
+{
 | 
						|
+	int usecs = 0;
 | 
						|
+	int sec   = 0;
 | 
						|
+
 | 
						|
+	if (b->tv_usec > a->tv_usec) {
 | 
						|
+		usecs = 1000000;
 | 
						|
+		sec--;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	usecs += a->tv_usec - b->tv_usec;
 | 
						|
+
 | 
						|
+	sec += a->tv_sec - b->tv_sec;
 | 
						|
+	sec *= 1000;
 | 
						|
+	usecs /= 1000;
 | 
						|
+	sec += usecs;
 | 
						|
+
 | 
						|
+	if (sec < 0)
 | 
						|
+		sec = 1000;
 | 
						|
+
 | 
						|
+	return sec;
 | 
						|
+}
 | 
						|
+
 | 
						|
+/**
 | 
						|
+ * Process the incoming packet
 | 
						|
+ */
 | 
						|
+static void imon_incoming_packet(struct imon_context *context,
 | 
						|
+				 struct urb *urb, int intf)
 | 
						|
+{
 | 
						|
+	int len = urb->actual_length;
 | 
						|
+	unsigned char *buf = urb->transfer_buffer;
 | 
						|
+	struct device *dev = context->driver->dev;
 | 
						|
+	int octet, bit;
 | 
						|
+	unsigned char mask;
 | 
						|
+	int i, chunk_num;
 | 
						|
+
 | 
						|
+	/*
 | 
						|
+	 * just bail out if no listening IR client
 | 
						|
+	 */
 | 
						|
+	if (!context->ir_isopen)
 | 
						|
+		return;
 | 
						|
+
 | 
						|
+	if (len != 8) {
 | 
						|
+		dev_warn(dev, "imon %s: invalid incoming packet "
 | 
						|
+			 "size (len = %d, intf%d)\n", __func__, len, intf);
 | 
						|
+		return;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	if (debug) {
 | 
						|
+		printk(KERN_INFO "raw packet: ");
 | 
						|
+		for (i = 0; i < len; ++i)
 | 
						|
+			printk("%02x ", buf[i]);
 | 
						|
+		printk("\n");
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	/*
 | 
						|
+	 * Translate received data to pulse and space lengths.
 | 
						|
+	 * Received data is active low, i.e. pulses are 0 and
 | 
						|
+	 * spaces are 1.
 | 
						|
+	 *
 | 
						|
+	 * My original algorithm was essentially similar to
 | 
						|
+	 * Changwoo Ryu's with the exception that he switched
 | 
						|
+	 * the incoming bits to active high and also fed an
 | 
						|
+	 * initial space to LIRC at the start of a new sequence
 | 
						|
+	 * if the previous bit was a pulse.
 | 
						|
+	 *
 | 
						|
+	 * I've decided to adopt his algorithm.
 | 
						|
+	 */
 | 
						|
+
 | 
						|
+	if (buf[7] == 1 && context->rx.initial_space) {
 | 
						|
+		/* LIRC requires a leading space */
 | 
						|
+		context->rx.prev_bit = 0;
 | 
						|
+		context->rx.count = 4;
 | 
						|
+		submit_data(context);
 | 
						|
+		context->rx.count = 0;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	for (octet = 0; octet < 5; ++octet) {
 | 
						|
+		mask = 0x80;
 | 
						|
+		for (bit = 0; bit < 8; ++bit) {
 | 
						|
+			int curr_bit = !(buf[octet] & mask);
 | 
						|
+			if (curr_bit != context->rx.prev_bit) {
 | 
						|
+				if (context->rx.count) {
 | 
						|
+					submit_data(context);
 | 
						|
+					context->rx.count = 0;
 | 
						|
+				}
 | 
						|
+				context->rx.prev_bit = curr_bit;
 | 
						|
+			}
 | 
						|
+			++context->rx.count;
 | 
						|
+			mask >>= 1;
 | 
						|
+		}
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	if (chunk_num == 10) {
 | 
						|
+		if (context->rx.count) {
 | 
						|
+			submit_data(context);
 | 
						|
+			context->rx.count = 0;
 | 
						|
+		}
 | 
						|
+		context->rx.initial_space = context->rx.prev_bit;
 | 
						|
+	}
 | 
						|
+}
 | 
						|
+
 | 
						|
+/**
 | 
						|
+ * Callback function for USB core API: receive data
 | 
						|
+ */
 | 
						|
+static void usb_rx_callback(struct urb *urb)
 | 
						|
+{
 | 
						|
+	struct imon_context *context;
 | 
						|
+	unsigned char *buf;
 | 
						|
+	int len;
 | 
						|
+	int intfnum = 0;
 | 
						|
+
 | 
						|
+	if (!urb)
 | 
						|
+		return;
 | 
						|
+
 | 
						|
+	context = (struct imon_context *)urb->context;
 | 
						|
+	if (!context)
 | 
						|
+		return;
 | 
						|
+
 | 
						|
+	buf = urb->transfer_buffer;
 | 
						|
+	len = urb->actual_length;
 | 
						|
+
 | 
						|
+	switch (urb->status) {
 | 
						|
+	case -ENOENT:		/* usbcore unlink successful! */
 | 
						|
+		return;
 | 
						|
+
 | 
						|
+	case 0:
 | 
						|
+		imon_incoming_packet(context, urb, intfnum);
 | 
						|
+		break;
 | 
						|
+
 | 
						|
+	default:
 | 
						|
+		dev_warn(context->driver->dev, "imon %s: status(%d): ignored\n",
 | 
						|
+			 __func__, urb->status);
 | 
						|
+		break;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	usb_submit_urb(context->rx_urb, GFP_ATOMIC);
 | 
						|
+
 | 
						|
+	return;
 | 
						|
+}
 | 
						|
+
 | 
						|
+/**
 | 
						|
+ * Callback function for USB core API: Probe
 | 
						|
+ */
 | 
						|
+static int imon_probe(struct usb_interface *interface,
 | 
						|
+		      const struct usb_device_id *id)
 | 
						|
+{
 | 
						|
+	struct usb_device *usbdev = NULL;
 | 
						|
+	struct usb_host_interface *iface_desc = NULL;
 | 
						|
+	struct usb_endpoint_descriptor *rx_endpoint = NULL;
 | 
						|
+	struct usb_endpoint_descriptor *tx_endpoint = NULL;
 | 
						|
+	struct urb *rx_urb = NULL;
 | 
						|
+	struct urb *tx_urb = NULL;
 | 
						|
+	struct lirc_driver *driver = NULL;
 | 
						|
+	struct lirc_buffer *rbuf = NULL;
 | 
						|
+	struct device *dev = &interface->dev;
 | 
						|
+	int ifnum;
 | 
						|
+	int lirc_minor = 0;
 | 
						|
+	int num_endpts;
 | 
						|
+	int retval = 0;
 | 
						|
+	int display_ep_found = 0;
 | 
						|
+	int ir_ep_found = 0;
 | 
						|
+	int alloc_status = 0;
 | 
						|
+	int vfd_proto_6p = 0;
 | 
						|
+	int code_length;
 | 
						|
+	struct imon_context *context = NULL;
 | 
						|
+	int i;
 | 
						|
+	u16 vendor, product;
 | 
						|
+
 | 
						|
+	context = kzalloc(sizeof(struct imon_context), GFP_KERNEL);
 | 
						|
+	if (!context) {
 | 
						|
+		err("%s: kzalloc failed for context", __func__);
 | 
						|
+		alloc_status = 1;
 | 
						|
+		goto alloc_status_switch;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	/*
 | 
						|
+	 * Try to auto-detect the type of display if the user hasn't set
 | 
						|
+	 * it by hand via the display_type modparam. Default is VFD.
 | 
						|
+	 */
 | 
						|
+	if (usb_match_id(interface, ir_only_list))
 | 
						|
+		context->display = 0;
 | 
						|
+	else
 | 
						|
+		context->display = 1;
 | 
						|
+
 | 
						|
+	code_length = BUF_CHUNK_SIZE * 8;
 | 
						|
+
 | 
						|
+	usbdev     = usb_get_dev(interface_to_usbdev(interface));
 | 
						|
+	iface_desc = interface->cur_altsetting;
 | 
						|
+	num_endpts = iface_desc->desc.bNumEndpoints;
 | 
						|
+	ifnum      = iface_desc->desc.bInterfaceNumber;
 | 
						|
+	vendor     = le16_to_cpu(usbdev->descriptor.idVendor);
 | 
						|
+	product    = le16_to_cpu(usbdev->descriptor.idProduct);
 | 
						|
+
 | 
						|
+	dev_dbg(dev, "%s: found iMON device (%04x:%04x, intf%d)\n",
 | 
						|
+		__func__, vendor, product, ifnum);
 | 
						|
+
 | 
						|
+	/* prevent races probing devices w/multiple interfaces */
 | 
						|
+	mutex_lock(&driver_lock);
 | 
						|
+
 | 
						|
+	/*
 | 
						|
+	 * Scan the endpoint list and set:
 | 
						|
+	 *	first input endpoint = IR endpoint
 | 
						|
+	 *	first output endpoint = display endpoint
 | 
						|
+	 */
 | 
						|
+	for (i = 0; i < num_endpts && !(ir_ep_found && display_ep_found); ++i) {
 | 
						|
+		struct usb_endpoint_descriptor *ep;
 | 
						|
+		int ep_dir;
 | 
						|
+		int ep_type;
 | 
						|
+		ep = &iface_desc->endpoint[i].desc;
 | 
						|
+		ep_dir = ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK;
 | 
						|
+		ep_type = ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
 | 
						|
+
 | 
						|
+		if (!ir_ep_found &&
 | 
						|
+			ep_dir == USB_DIR_IN &&
 | 
						|
+			ep_type == USB_ENDPOINT_XFER_INT) {
 | 
						|
+
 | 
						|
+			rx_endpoint = ep;
 | 
						|
+			ir_ep_found = 1;
 | 
						|
+			dev_dbg(dev, "%s: found IR endpoint\n", __func__);
 | 
						|
+
 | 
						|
+		} else if (!display_ep_found && ep_dir == USB_DIR_OUT &&
 | 
						|
+			   ep_type == USB_ENDPOINT_XFER_INT) {
 | 
						|
+			tx_endpoint = ep;
 | 
						|
+			display_ep_found = 1;
 | 
						|
+			dev_dbg(dev, "%s: found display endpoint\n", __func__);
 | 
						|
+		}
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	/*
 | 
						|
+	 * Some iMON receivers have no display. Unfortunately, it seems
 | 
						|
+	 * that SoundGraph recycles device IDs between devices both with
 | 
						|
+	 * and without... :\
 | 
						|
+	 */
 | 
						|
+	if (context->display == 0) {
 | 
						|
+		display_ep_found = 0;
 | 
						|
+		dev_dbg(dev, "%s: device has no display\n", __func__);
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	/* Input endpoint is mandatory */
 | 
						|
+	if (!ir_ep_found) {
 | 
						|
+		err("%s: no valid input (IR) endpoint found.", __func__);
 | 
						|
+		retval = -ENODEV;
 | 
						|
+		alloc_status = 2;
 | 
						|
+		goto alloc_status_switch;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	/* Determine if display requires 6 packets */
 | 
						|
+	if (display_ep_found) {
 | 
						|
+		if (usb_match_id(interface, vfd_proto_6p_list))
 | 
						|
+			vfd_proto_6p = 1;
 | 
						|
+
 | 
						|
+		dev_dbg(dev, "%s: vfd_proto_6p: %d\n",
 | 
						|
+			__func__, vfd_proto_6p);
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	driver = kzalloc(sizeof(struct lirc_driver), GFP_KERNEL);
 | 
						|
+	if (!driver) {
 | 
						|
+		err("%s: kzalloc failed for lirc_driver", __func__);
 | 
						|
+		alloc_status = 2;
 | 
						|
+		goto alloc_status_switch;
 | 
						|
+	}
 | 
						|
+	rbuf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
 | 
						|
+	if (!rbuf) {
 | 
						|
+		err("%s: kmalloc failed for lirc_buffer", __func__);
 | 
						|
+		alloc_status = 3;
 | 
						|
+		goto alloc_status_switch;
 | 
						|
+	}
 | 
						|
+	if (lirc_buffer_init(rbuf, BUF_CHUNK_SIZE, BUF_SIZE)) {
 | 
						|
+		err("%s: lirc_buffer_init failed", __func__);
 | 
						|
+		alloc_status = 4;
 | 
						|
+		goto alloc_status_switch;
 | 
						|
+	}
 | 
						|
+	rx_urb = usb_alloc_urb(0, GFP_KERNEL);
 | 
						|
+	if (!rx_urb) {
 | 
						|
+		err("%s: usb_alloc_urb failed for IR urb", __func__);
 | 
						|
+		alloc_status = 5;
 | 
						|
+		goto alloc_status_switch;
 | 
						|
+	}
 | 
						|
+	tx_urb = usb_alloc_urb(0, GFP_KERNEL);
 | 
						|
+	if (!tx_urb) {
 | 
						|
+		err("%s: usb_alloc_urb failed for display urb",
 | 
						|
+		    __func__);
 | 
						|
+		alloc_status = 6;
 | 
						|
+		goto alloc_status_switch;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	mutex_init(&context->ctx_lock);
 | 
						|
+	context->vfd_proto_6p = vfd_proto_6p;
 | 
						|
+
 | 
						|
+	strcpy(driver->name, MOD_NAME);
 | 
						|
+	driver->minor = -1;
 | 
						|
+	driver->code_length = sizeof(int) * 8;
 | 
						|
+	driver->sample_rate = 0;
 | 
						|
+	driver->features = LIRC_CAN_REC_MODE2;
 | 
						|
+	driver->data = context;
 | 
						|
+	driver->rbuf = rbuf;
 | 
						|
+	driver->set_use_inc = ir_open;
 | 
						|
+	driver->set_use_dec = ir_close;
 | 
						|
+	driver->dev = &interface->dev;
 | 
						|
+	driver->owner = THIS_MODULE;
 | 
						|
+
 | 
						|
+	mutex_lock(&context->ctx_lock);
 | 
						|
+
 | 
						|
+	context->driver = driver;
 | 
						|
+	/* start out in keyboard mode */
 | 
						|
+
 | 
						|
+	lirc_minor = lirc_register_driver(driver);
 | 
						|
+	if (lirc_minor < 0) {
 | 
						|
+		err("%s: lirc_register_driver failed", __func__);
 | 
						|
+		alloc_status = 7;
 | 
						|
+		goto alloc_status_switch;
 | 
						|
+	} else
 | 
						|
+		dev_info(dev, "Registered iMON driver "
 | 
						|
+			 "(lirc minor: %d)\n", lirc_minor);
 | 
						|
+
 | 
						|
+	/* Needed while unregistering! */
 | 
						|
+	driver->minor = lirc_minor;
 | 
						|
+
 | 
						|
+	context->usbdev = usbdev;
 | 
						|
+	context->dev_present = 1;
 | 
						|
+	context->rx_endpoint = rx_endpoint;
 | 
						|
+	context->rx_urb = rx_urb;
 | 
						|
+
 | 
						|
+	/*
 | 
						|
+	 * tx is used to send characters to lcd/vfd, associate RF
 | 
						|
+	 * remotes, set IR protocol, and maybe more...
 | 
						|
+	 */
 | 
						|
+	context->tx_endpoint = tx_endpoint;
 | 
						|
+	context->tx_urb = tx_urb;
 | 
						|
+
 | 
						|
+	if (display_ep_found)
 | 
						|
+		context->display = 1;
 | 
						|
+
 | 
						|
+	usb_fill_int_urb(context->rx_urb, context->usbdev,
 | 
						|
+		usb_rcvintpipe(context->usbdev,
 | 
						|
+			context->rx_endpoint->bEndpointAddress),
 | 
						|
+		context->usb_rx_buf, sizeof(context->usb_rx_buf),
 | 
						|
+		usb_rx_callback, context,
 | 
						|
+		context->rx_endpoint->bInterval);
 | 
						|
+
 | 
						|
+	retval = usb_submit_urb(context->rx_urb, GFP_KERNEL);
 | 
						|
+
 | 
						|
+	if (retval) {
 | 
						|
+		err("%s: usb_submit_urb failed for intf0 (%d)",
 | 
						|
+		    __func__, retval);
 | 
						|
+		mutex_unlock(&context->ctx_lock);
 | 
						|
+		goto exit;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	usb_set_intfdata(interface, context);
 | 
						|
+
 | 
						|
+	if (context->display && ifnum == 0) {
 | 
						|
+		dev_dbg(dev, "%s: Registering iMON display with sysfs\n",
 | 
						|
+			__func__);
 | 
						|
+
 | 
						|
+		if (usb_register_dev(interface, &imon_class)) {
 | 
						|
+			/* Not a fatal error, so ignore */
 | 
						|
+			dev_info(dev, "%s: could not get a minor number for "
 | 
						|
+				 "display\n", __func__);
 | 
						|
+		}
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	dev_info(dev, "iMON device (%04x:%04x, intf%d) on "
 | 
						|
+		 "usb<%d:%d> initialized\n", vendor, product, ifnum,
 | 
						|
+		 usbdev->bus->busnum, usbdev->devnum);
 | 
						|
+
 | 
						|
+alloc_status_switch:
 | 
						|
+	mutex_unlock(&context->ctx_lock);
 | 
						|
+
 | 
						|
+	switch (alloc_status) {
 | 
						|
+	case 7:
 | 
						|
+		usb_free_urb(tx_urb);
 | 
						|
+	case 6:
 | 
						|
+		usb_free_urb(rx_urb);
 | 
						|
+	case 5:
 | 
						|
+		if (rbuf)
 | 
						|
+			lirc_buffer_free(rbuf);
 | 
						|
+	case 4:
 | 
						|
+		kfree(rbuf);
 | 
						|
+	case 3:
 | 
						|
+		kfree(driver);
 | 
						|
+	case 2:
 | 
						|
+		kfree(context);
 | 
						|
+		context = NULL;
 | 
						|
+	case 1:
 | 
						|
+		if (retval != -ENODEV)
 | 
						|
+			retval = -ENOMEM;
 | 
						|
+		break;
 | 
						|
+	case 0:
 | 
						|
+		retval = 0;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+exit:
 | 
						|
+	mutex_unlock(&driver_lock);
 | 
						|
+
 | 
						|
+	return retval;
 | 
						|
+}
 | 
						|
+
 | 
						|
+/**
 | 
						|
+ * Callback function for USB core API: disconnect
 | 
						|
+ */
 | 
						|
+static void imon_disconnect(struct usb_interface *interface)
 | 
						|
+{
 | 
						|
+	struct imon_context *context;
 | 
						|
+	int ifnum;
 | 
						|
+
 | 
						|
+	/* prevent races with ir_open()/display_open() */
 | 
						|
+	mutex_lock(&driver_lock);
 | 
						|
+
 | 
						|
+	context = usb_get_intfdata(interface);
 | 
						|
+	ifnum = interface->cur_altsetting->desc.bInterfaceNumber;
 | 
						|
+
 | 
						|
+	mutex_lock(&context->ctx_lock);
 | 
						|
+
 | 
						|
+	usb_set_intfdata(interface, NULL);
 | 
						|
+
 | 
						|
+	/* Abort ongoing write */
 | 
						|
+	if (atomic_read(&context->tx.busy)) {
 | 
						|
+		usb_kill_urb(context->tx_urb);
 | 
						|
+		complete_all(&context->tx.finished);
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	context->dev_present = 0;
 | 
						|
+	usb_kill_urb(context->rx_urb);
 | 
						|
+	if (context->display)
 | 
						|
+		usb_deregister_dev(interface, &imon_class);
 | 
						|
+
 | 
						|
+	if (!context->ir_isopen && !context->dev_present) {
 | 
						|
+		deregister_from_lirc(context);
 | 
						|
+		mutex_unlock(&context->ctx_lock);
 | 
						|
+		if (!context->display_isopen)
 | 
						|
+			free_imon_context(context);
 | 
						|
+	} else
 | 
						|
+		mutex_unlock(&context->ctx_lock);
 | 
						|
+
 | 
						|
+	mutex_unlock(&driver_lock);
 | 
						|
+
 | 
						|
+	printk(KERN_INFO "%s: iMON device (intf%d) disconnected\n",
 | 
						|
+	       __func__, ifnum);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int imon_suspend(struct usb_interface *intf, pm_message_t message)
 | 
						|
+{
 | 
						|
+	struct imon_context *context = usb_get_intfdata(intf);
 | 
						|
+
 | 
						|
+	usb_kill_urb(context->rx_urb);
 | 
						|
+
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int imon_resume(struct usb_interface *intf)
 | 
						|
+{
 | 
						|
+	int rc = 0;
 | 
						|
+	struct imon_context *context = usb_get_intfdata(intf);
 | 
						|
+
 | 
						|
+	usb_fill_int_urb(context->rx_urb, context->usbdev,
 | 
						|
+		usb_rcvintpipe(context->usbdev,
 | 
						|
+			context->rx_endpoint->bEndpointAddress),
 | 
						|
+		context->usb_rx_buf, sizeof(context->usb_rx_buf),
 | 
						|
+		usb_rx_callback, context,
 | 
						|
+		context->rx_endpoint->bInterval);
 | 
						|
+
 | 
						|
+	rc = usb_submit_urb(context->rx_urb, GFP_ATOMIC);
 | 
						|
+
 | 
						|
+	return rc;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int __init imon_init(void)
 | 
						|
+{
 | 
						|
+	int rc;
 | 
						|
+
 | 
						|
+	printk(KERN_INFO MOD_NAME ": " MOD_DESC ", v" MOD_VERSION "\n");
 | 
						|
+
 | 
						|
+	rc = usb_register(&imon_driver);
 | 
						|
+	if (rc) {
 | 
						|
+		err("%s: usb register failed(%d)", __func__, rc);
 | 
						|
+		return -ENODEV;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void __exit imon_exit(void)
 | 
						|
+{
 | 
						|
+	usb_deregister(&imon_driver);
 | 
						|
+	printk(KERN_INFO MOD_NAME ": module removed. Goodbye!\n");
 | 
						|
+}
 | 
						|
+
 | 
						|
+module_init(imon_init);
 | 
						|
+module_exit(imon_exit);
 | 
						|
diff --git a/drivers/staging/lirc/lirc_it87.c b/drivers/staging/lirc/lirc_it87.c
 | 
						|
new file mode 100644
 | 
						|
index 0000000..781abc3
 | 
						|
--- /dev/null
 | 
						|
+++ b/drivers/staging/lirc/lirc_it87.c
 | 
						|
@@ -0,0 +1,1019 @@
 | 
						|
+/*
 | 
						|
+ * LIRC driver for ITE IT8712/IT8705 CIR port
 | 
						|
+ *
 | 
						|
+ * Copyright (C) 2001 Hans-Gunter Lutke Uphues <hg_lu@web.de>
 | 
						|
+ *
 | 
						|
+ * This program is free software; you can redistribute it and/or
 | 
						|
+ * modify it under the terms of the GNU General Public License as
 | 
						|
+ * published by the Free Software Foundation; either version 2 of the
 | 
						|
+ * License, or (at your option) any later version.
 | 
						|
+ *
 | 
						|
+ * This program is distributed in the hope that it will be useful, but
 | 
						|
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
						|
+ * General Public License for more details.
 | 
						|
+
 | 
						|
+ * You should have received a copy of the GNU General Public License
 | 
						|
+ * along with this program; if not, write to the Free Software
 | 
						|
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 | 
						|
+ * USA
 | 
						|
+ *
 | 
						|
+ * ITE IT8705 and IT8712(not tested) and IT8720 CIR-port support for lirc based
 | 
						|
+ * via cut and paste from lirc_sir.c (C) 2000 Milan Pikula
 | 
						|
+ *
 | 
						|
+ * Attention: Sendmode only tested with debugging logs
 | 
						|
+ *
 | 
						|
+ * 2001/02/27 Christoph Bartelmus <lirc@bartelmus.de> :
 | 
						|
+ *   reimplemented read function
 | 
						|
+ * 2005/06/05 Andrew Calkin implemented support for Asus Digimatrix,
 | 
						|
+ *   based on work of the following member of the Outertrack Digimatrix
 | 
						|
+ *   Forum: Art103 <r_tay@hotmail.com>
 | 
						|
+ * 2009/12/24 James Edwards <jimbo-lirc@edwardsclan.net> implemeted support
 | 
						|
+ *   for ITE8704/ITE8718, on my machine, the DSDT reports 8704, but the
 | 
						|
+ *   chip identifies as 18.
 | 
						|
+ */
 | 
						|
+
 | 
						|
+#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/time.h>
 | 
						|
+#include <linux/string.h>
 | 
						|
+#include <linux/types.h>
 | 
						|
+#include <linux/wait.h>
 | 
						|
+#include <linux/mm.h>
 | 
						|
+#include <linux/delay.h>
 | 
						|
+#include <linux/poll.h>
 | 
						|
+#include <asm/system.h>
 | 
						|
+#include <linux/io.h>
 | 
						|
+#include <linux/irq.h>
 | 
						|
+#include <linux/fcntl.h>
 | 
						|
+
 | 
						|
+#include <linux/timer.h>
 | 
						|
+#include <linux/pnp.h>
 | 
						|
+
 | 
						|
+#include <media/lirc.h>
 | 
						|
+#include <media/lirc_dev.h>
 | 
						|
+
 | 
						|
+#include "lirc_it87.h"
 | 
						|
+
 | 
						|
+#ifdef LIRC_IT87_DIGIMATRIX
 | 
						|
+static int digimatrix = 1;
 | 
						|
+static int it87_freq = 36; /* kHz */
 | 
						|
+static int irq = 9;
 | 
						|
+#else
 | 
						|
+static int digimatrix;
 | 
						|
+static int it87_freq = 38; /* kHz */
 | 
						|
+static int irq = IT87_CIR_DEFAULT_IRQ;
 | 
						|
+#endif
 | 
						|
+
 | 
						|
+static unsigned long it87_bits_in_byte_out;
 | 
						|
+static unsigned long it87_send_counter;
 | 
						|
+static unsigned char it87_RXEN_mask = IT87_CIR_RCR_RXEN;
 | 
						|
+
 | 
						|
+#define RBUF_LEN 1024
 | 
						|
+
 | 
						|
+#define LIRC_DRIVER_NAME "lirc_it87"
 | 
						|
+
 | 
						|
+/* timeout for sequences in jiffies (=5/100s) */
 | 
						|
+/* must be longer than TIME_CONST */
 | 
						|
+#define IT87_TIMEOUT	(HZ*5/100)
 | 
						|
+
 | 
						|
+/* module parameters */
 | 
						|
+static int debug;
 | 
						|
+#define dprintk(fmt, args...)					\
 | 
						|
+	do {							\
 | 
						|
+		if (debug)					\
 | 
						|
+			printk(KERN_DEBUG LIRC_DRIVER_NAME ": "	\
 | 
						|
+			       fmt, ## args);			\
 | 
						|
+	} while (0)
 | 
						|
+
 | 
						|
+static int io = IT87_CIR_DEFAULT_IOBASE;
 | 
						|
+/* receiver demodulator default: off */
 | 
						|
+static int it87_enable_demodulator;
 | 
						|
+
 | 
						|
+static int timer_enabled;
 | 
						|
+static DEFINE_SPINLOCK(timer_lock);
 | 
						|
+static struct timer_list timerlist;
 | 
						|
+/* time of last signal change detected */
 | 
						|
+static struct timeval last_tv = {0, 0};
 | 
						|
+/* time of last UART data ready interrupt */
 | 
						|
+static struct timeval last_intr_tv = {0, 0};
 | 
						|
+static int last_value;
 | 
						|
+
 | 
						|
+static DECLARE_WAIT_QUEUE_HEAD(lirc_read_queue);
 | 
						|
+
 | 
						|
+static DEFINE_SPINLOCK(hardware_lock);
 | 
						|
+static DEFINE_SPINLOCK(dev_lock);
 | 
						|
+
 | 
						|
+static int rx_buf[RBUF_LEN];
 | 
						|
+unsigned int rx_tail, rx_head;
 | 
						|
+
 | 
						|
+static struct pnp_driver it87_pnp_driver;
 | 
						|
+
 | 
						|
+/* 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 long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg);
 | 
						|
+static void add_read_queue(int flag, unsigned long val);
 | 
						|
+static int init_chrdev(void);
 | 
						|
+static void drop_chrdev(void);
 | 
						|
+/* Hardware */
 | 
						|
+static irqreturn_t it87_interrupt(int irq, void *dev_id);
 | 
						|
+static void send_space(unsigned long len);
 | 
						|
+static void send_pulse(unsigned long len);
 | 
						|
+static void init_send(void);
 | 
						|
+static void terminate_send(unsigned long len);
 | 
						|
+static int init_hardware(void);
 | 
						|
+static void drop_hardware(void);
 | 
						|
+/* Initialisation */
 | 
						|
+static int init_port(void);
 | 
						|
+static void drop_port(void);
 | 
						|
+
 | 
						|
+
 | 
						|
+/* SECTION: Communication with user-space */
 | 
						|
+
 | 
						|
+static int lirc_open(struct inode *inode, struct file *file)
 | 
						|
+{
 | 
						|
+	spin_lock(&dev_lock);
 | 
						|
+	if (module_refcount(THIS_MODULE)) {
 | 
						|
+		spin_unlock(&dev_lock);
 | 
						|
+		return -EBUSY;
 | 
						|
+	}
 | 
						|
+	spin_unlock(&dev_lock);
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+
 | 
						|
+static int lirc_close(struct inode *inode, struct file *file)
 | 
						|
+{
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+
 | 
						|
+static unsigned int lirc_poll(struct file *file, poll_table *wait)
 | 
						|
+{
 | 
						|
+	poll_wait(file, &lirc_read_queue, wait);
 | 
						|
+	if (rx_head != rx_tail)
 | 
						|
+		return POLLIN | POLLRDNORM;
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+
 | 
						|
+static ssize_t lirc_read(struct file *file, char *buf,
 | 
						|
+			 size_t count, loff_t *ppos)
 | 
						|
+{
 | 
						|
+	int n = 0;
 | 
						|
+	int retval = 0;
 | 
						|
+
 | 
						|
+	while (n < count) {
 | 
						|
+		if (file->f_flags & O_NONBLOCK && rx_head == rx_tail) {
 | 
						|
+			retval = -EAGAIN;
 | 
						|
+			break;
 | 
						|
+		}
 | 
						|
+		retval = wait_event_interruptible(lirc_read_queue,
 | 
						|
+						  rx_head != rx_tail);
 | 
						|
+		if (retval)
 | 
						|
+			break;
 | 
						|
+
 | 
						|
+		if (copy_to_user((void *) buf + n, (void *) (rx_buf + rx_head),
 | 
						|
+				 sizeof(int))) {
 | 
						|
+			retval = -EFAULT;
 | 
						|
+			break;
 | 
						|
+		}
 | 
						|
+		rx_head = (rx_head + 1) & (RBUF_LEN - 1);
 | 
						|
+		n += sizeof(int);
 | 
						|
+	}
 | 
						|
+	if (n)
 | 
						|
+		return n;
 | 
						|
+	return retval;
 | 
						|
+}
 | 
						|
+
 | 
						|
+
 | 
						|
+static ssize_t lirc_write(struct file *file, const char *buf,
 | 
						|
+			  size_t n, loff_t *pos)
 | 
						|
+{
 | 
						|
+	int i = 0;
 | 
						|
+	int *tx_buf;
 | 
						|
+
 | 
						|
+	if (n % sizeof(int))
 | 
						|
+		return -EINVAL;
 | 
						|
+	tx_buf = memdup_user(buf, n);
 | 
						|
+	if (IS_ERR(tx_buf))
 | 
						|
+		return PTR_ERR(tx_buf);
 | 
						|
+	n /= sizeof(int);
 | 
						|
+	init_send();
 | 
						|
+	while (1) {
 | 
						|
+		if (i >= n)
 | 
						|
+			break;
 | 
						|
+		if (tx_buf[i])
 | 
						|
+			send_pulse(tx_buf[i]);
 | 
						|
+		i++;
 | 
						|
+		if (i >= n)
 | 
						|
+			break;
 | 
						|
+		if (tx_buf[i])
 | 
						|
+			send_space(tx_buf[i]);
 | 
						|
+		i++;
 | 
						|
+	}
 | 
						|
+	terminate_send(tx_buf[i - 1]);
 | 
						|
+	return n;
 | 
						|
+}
 | 
						|
+
 | 
						|
+
 | 
						|
+static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
 | 
						|
+{
 | 
						|
+	int retval = 0;
 | 
						|
+	unsigned long value = 0;
 | 
						|
+	unsigned int ivalue;
 | 
						|
+	unsigned long hw_flags;
 | 
						|
+
 | 
						|
+	if (cmd == LIRC_GET_FEATURES)
 | 
						|
+		value = LIRC_CAN_SEND_PULSE |
 | 
						|
+			LIRC_CAN_SET_SEND_CARRIER |
 | 
						|
+			LIRC_CAN_REC_MODE2;
 | 
						|
+	else if (cmd == LIRC_GET_SEND_MODE)
 | 
						|
+		value = LIRC_MODE_PULSE;
 | 
						|
+	else if (cmd == LIRC_GET_REC_MODE)
 | 
						|
+		value = LIRC_MODE_MODE2;
 | 
						|
+
 | 
						|
+	switch (cmd) {
 | 
						|
+	case LIRC_GET_FEATURES:
 | 
						|
+	case LIRC_GET_SEND_MODE:
 | 
						|
+	case LIRC_GET_REC_MODE:
 | 
						|
+		retval = put_user(value, (unsigned long *) arg);
 | 
						|
+		break;
 | 
						|
+
 | 
						|
+	case LIRC_SET_SEND_MODE:
 | 
						|
+	case LIRC_SET_REC_MODE:
 | 
						|
+		retval = get_user(value, (unsigned long *) arg);
 | 
						|
+		break;
 | 
						|
+
 | 
						|
+	case LIRC_SET_SEND_CARRIER:
 | 
						|
+		retval = get_user(ivalue, (unsigned int *) arg);
 | 
						|
+		if (retval)
 | 
						|
+			return retval;
 | 
						|
+		ivalue /= 1000;
 | 
						|
+		if (ivalue > IT87_CIR_FREQ_MAX ||
 | 
						|
+		    ivalue < IT87_CIR_FREQ_MIN)
 | 
						|
+			return -EINVAL;
 | 
						|
+
 | 
						|
+		it87_freq = ivalue;
 | 
						|
+
 | 
						|
+		spin_lock_irqsave(&hardware_lock, hw_flags);
 | 
						|
+		outb(((inb(io + IT87_CIR_TCR2) & IT87_CIR_TCR2_TXMPW) |
 | 
						|
+		      (it87_freq - IT87_CIR_FREQ_MIN) << 3),
 | 
						|
+		      io + IT87_CIR_TCR2);
 | 
						|
+		spin_unlock_irqrestore(&hardware_lock, hw_flags);
 | 
						|
+		dprintk("demodulation frequency: %d kHz\n", it87_freq);
 | 
						|
+
 | 
						|
+		break;
 | 
						|
+
 | 
						|
+	default:
 | 
						|
+		retval = -EINVAL;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	if (retval)
 | 
						|
+		return retval;
 | 
						|
+
 | 
						|
+	if (cmd == LIRC_SET_REC_MODE) {
 | 
						|
+		if (value != LIRC_MODE_MODE2)
 | 
						|
+			retval = -ENOSYS;
 | 
						|
+	} else if (cmd == LIRC_SET_SEND_MODE) {
 | 
						|
+		if (value != LIRC_MODE_PULSE)
 | 
						|
+			retval = -ENOSYS;
 | 
						|
+	}
 | 
						|
+	return retval;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void add_read_queue(int flag, unsigned long val)
 | 
						|
+{
 | 
						|
+	unsigned int new_rx_tail;
 | 
						|
+	int newval;
 | 
						|
+
 | 
						|
+	dprintk("add flag %d with val %lu\n", flag, val);
 | 
						|
+
 | 
						|
+	newval = val & PULSE_MASK;
 | 
						|
+
 | 
						|
+	/*
 | 
						|
+	 * statistically, pulses are ~TIME_CONST/2 too long. we could
 | 
						|
+	 * maybe make this more exact, 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 = {
 | 
						|
+	.owner		= THIS_MODULE,
 | 
						|
+	.read		= lirc_read,
 | 
						|
+	.write		= lirc_write,
 | 
						|
+	.poll		= lirc_poll,
 | 
						|
+	.unlocked_ioctl	= lirc_ioctl,
 | 
						|
+	.open		= lirc_open,
 | 
						|
+	.release	= lirc_close,
 | 
						|
+};
 | 
						|
+
 | 
						|
+static int set_use_inc(void *data)
 | 
						|
+{
 | 
						|
+       return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void set_use_dec(void *data)
 | 
						|
+{
 | 
						|
+}
 | 
						|
+
 | 
						|
+static struct lirc_driver driver = {
 | 
						|
+       .name		= LIRC_DRIVER_NAME,
 | 
						|
+       .minor		= -1,
 | 
						|
+       .code_length	= 1,
 | 
						|
+       .sample_rate	= 0,
 | 
						|
+       .data		= NULL,
 | 
						|
+       .add_to_buf	= NULL,
 | 
						|
+       .set_use_inc	= set_use_inc,
 | 
						|
+       .set_use_dec	= set_use_dec,
 | 
						|
+       .fops		= &lirc_fops,
 | 
						|
+       .dev		= NULL,
 | 
						|
+       .owner		= THIS_MODULE,
 | 
						|
+};
 | 
						|
+
 | 
						|
+
 | 
						|
+#ifdef MODULE
 | 
						|
+static int init_chrdev(void)
 | 
						|
+{
 | 
						|
+	driver.minor = lirc_register_driver(&driver);
 | 
						|
+
 | 
						|
+	if (driver.minor < 0) {
 | 
						|
+		printk(KERN_ERR LIRC_DRIVER_NAME ": init_chrdev() failed.\n");
 | 
						|
+		return -EIO;
 | 
						|
+	}
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+
 | 
						|
+static void drop_chrdev(void)
 | 
						|
+{
 | 
						|
+	lirc_unregister_driver(driver.minor);
 | 
						|
+}
 | 
						|
+#endif
 | 
						|
+
 | 
						|
+
 | 
						|
+/* SECTION: Hardware */
 | 
						|
+static long delta(struct timeval *tv1, struct timeval *tv2)
 | 
						|
+{
 | 
						|
+	unsigned long deltv;
 | 
						|
+
 | 
						|
+	deltv = tv2->tv_sec - tv1->tv_sec;
 | 
						|
+	if (deltv > 15)
 | 
						|
+		deltv = 0xFFFFFF;
 | 
						|
+	else
 | 
						|
+		deltv = deltv*1000000 + tv2->tv_usec - tv1->tv_usec;
 | 
						|
+	return deltv;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void it87_timeout(unsigned long data)
 | 
						|
+{
 | 
						|
+	unsigned long flags;
 | 
						|
+
 | 
						|
+	/* avoid interference with interrupt */
 | 
						|
+	spin_lock_irqsave(&timer_lock, flags);
 | 
						|
+
 | 
						|
+	if (digimatrix) {
 | 
						|
+		/* We have timed out. Disable the RX mechanism. */
 | 
						|
+
 | 
						|
+		outb((inb(io + IT87_CIR_RCR) & ~IT87_CIR_RCR_RXEN) |
 | 
						|
+		     IT87_CIR_RCR_RXACT, io + IT87_CIR_RCR);
 | 
						|
+		if (it87_RXEN_mask)
 | 
						|
+			outb(inb(io + IT87_CIR_RCR) | IT87_CIR_RCR_RXEN,
 | 
						|
+			     io + IT87_CIR_RCR);
 | 
						|
+		dprintk(" TIMEOUT\n");
 | 
						|
+		timer_enabled = 0;
 | 
						|
+
 | 
						|
+		/* fifo clear */
 | 
						|
+		outb(inb(io + IT87_CIR_TCR1) | IT87_CIR_TCR1_FIFOCLR,
 | 
						|
+		     io+IT87_CIR_TCR1);
 | 
						|
+
 | 
						|
+	} else {
 | 
						|
+		/*
 | 
						|
+		 * if last received signal was a pulse, but receiving stopped
 | 
						|
+		 * within the 9 bit frame, we need to finish this pulse and
 | 
						|
+		 * simulate a signal change to from pulse to space. Otherwise
 | 
						|
+		 * upper layers will receive two sequences next time.
 | 
						|
+		 */
 | 
						|
+
 | 
						|
+		if (last_value) {
 | 
						|
+			unsigned long pulse_end;
 | 
						|
+
 | 
						|
+			/* determine 'virtual' pulse end: */
 | 
						|
+			pulse_end = delta(&last_tv, &last_intr_tv);
 | 
						|
+			dprintk("timeout add %d for %lu usec\n",
 | 
						|
+				last_value, pulse_end);
 | 
						|
+			add_read_queue(last_value, pulse_end);
 | 
						|
+			last_value = 0;
 | 
						|
+			last_tv = last_intr_tv;
 | 
						|
+		}
 | 
						|
+	}
 | 
						|
+	spin_unlock_irqrestore(&timer_lock, flags);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static irqreturn_t it87_interrupt(int irq, void *dev_id)
 | 
						|
+{
 | 
						|
+	unsigned char data;
 | 
						|
+	struct timeval curr_tv;
 | 
						|
+	static unsigned long deltv;
 | 
						|
+	unsigned long deltintrtv;
 | 
						|
+	unsigned long flags, hw_flags;
 | 
						|
+	int iir, lsr;
 | 
						|
+	int fifo = 0;
 | 
						|
+	static char lastbit;
 | 
						|
+	char bit;
 | 
						|
+
 | 
						|
+	/* Bit duration in microseconds */
 | 
						|
+	const unsigned long bit_duration = 1000000ul /
 | 
						|
+		(115200 / IT87_CIR_BAUDRATE_DIVISOR);
 | 
						|
+
 | 
						|
+
 | 
						|
+	iir = inb(io + IT87_CIR_IIR);
 | 
						|
+
 | 
						|
+	switch (iir & IT87_CIR_IIR_IID) {
 | 
						|
+	case 0x4:
 | 
						|
+	case 0x6:
 | 
						|
+		lsr = inb(io + IT87_CIR_RSR) & (IT87_CIR_RSR_RXFTO |
 | 
						|
+						IT87_CIR_RSR_RXFBC);
 | 
						|
+		fifo = lsr & IT87_CIR_RSR_RXFBC;
 | 
						|
+		dprintk("iir: 0x%x fifo: 0x%x\n", iir, lsr);
 | 
						|
+
 | 
						|
+		/* avoid interference with timer */
 | 
						|
+		spin_lock_irqsave(&timer_lock, flags);
 | 
						|
+		spin_lock_irqsave(&hardware_lock, hw_flags);
 | 
						|
+		if (digimatrix) {
 | 
						|
+			static unsigned long acc_pulse;
 | 
						|
+			static unsigned long acc_space;
 | 
						|
+
 | 
						|
+			do {
 | 
						|
+				data = inb(io + IT87_CIR_DR);
 | 
						|
+				data = ~data;
 | 
						|
+				fifo--;
 | 
						|
+				if (data != 0x00) {
 | 
						|
+					if (timer_enabled)
 | 
						|
+						del_timer(&timerlist);
 | 
						|
+					/*
 | 
						|
+					 * start timer for end of
 | 
						|
+					 * sequence detection
 | 
						|
+					 */
 | 
						|
+					timerlist.expires = jiffies +
 | 
						|
+							    IT87_TIMEOUT;
 | 
						|
+					add_timer(&timerlist);
 | 
						|
+					timer_enabled = 1;
 | 
						|
+				}
 | 
						|
+				/* Loop through */
 | 
						|
+				for (bit = 0; bit < 8; ++bit) {
 | 
						|
+					if ((data >> bit) & 1) {
 | 
						|
+						++acc_pulse;
 | 
						|
+						if (lastbit == 0) {
 | 
						|
+							add_read_queue(0,
 | 
						|
+								acc_space *
 | 
						|
+								 bit_duration);
 | 
						|
+							acc_space = 0;
 | 
						|
+						}
 | 
						|
+					} else {
 | 
						|
+						++acc_space;
 | 
						|
+						if (lastbit == 1) {
 | 
						|
+							add_read_queue(1,
 | 
						|
+								acc_pulse *
 | 
						|
+								 bit_duration);
 | 
						|
+							acc_pulse = 0;
 | 
						|
+						}
 | 
						|
+					}
 | 
						|
+					lastbit = (data >> bit) & 1;
 | 
						|
+				}
 | 
						|
+
 | 
						|
+			} while (fifo != 0);
 | 
						|
+		} else { /* Normal Operation */
 | 
						|
+			do {
 | 
						|
+				del_timer(&timerlist);
 | 
						|
+				data = inb(io + IT87_CIR_DR);
 | 
						|
+
 | 
						|
+				dprintk("data=%02x\n", data);
 | 
						|
+				do_gettimeofday(&curr_tv);
 | 
						|
+				deltv = delta(&last_tv, &curr_tv);
 | 
						|
+				deltintrtv = delta(&last_intr_tv, &curr_tv);
 | 
						|
+
 | 
						|
+				dprintk("t %lu , d %d\n",
 | 
						|
+					deltintrtv, (int)data);
 | 
						|
+
 | 
						|
+				/*
 | 
						|
+				 * if nothing came in last 2 cycles,
 | 
						|
+				 * it was gap
 | 
						|
+				 */
 | 
						|
+				if (deltintrtv > TIME_CONST * 2) {
 | 
						|
+					if (last_value) {
 | 
						|
+						dprintk("GAP\n");
 | 
						|
+
 | 
						|
+						/* simulate signal change */
 | 
						|
+						add_read_queue(last_value,
 | 
						|
+							       deltv -
 | 
						|
+							       deltintrtv);
 | 
						|
+						last_value = 0;
 | 
						|
+						last_tv.tv_sec =
 | 
						|
+							last_intr_tv.tv_sec;
 | 
						|
+						last_tv.tv_usec =
 | 
						|
+							last_intr_tv.tv_usec;
 | 
						|
+						deltv = deltintrtv;
 | 
						|
+					}
 | 
						|
+				}
 | 
						|
+				data = 1;
 | 
						|
+				if (data ^ last_value) {
 | 
						|
+					/*
 | 
						|
+					 * deltintrtv > 2*TIME_CONST,
 | 
						|
+					 * remember ? the other case is
 | 
						|
+					 * timeout
 | 
						|
+					 */
 | 
						|
+					add_read_queue(last_value,
 | 
						|
+						       deltv-TIME_CONST);
 | 
						|
+					last_value = data;
 | 
						|
+					last_tv = curr_tv;
 | 
						|
+					if (last_tv.tv_usec >= TIME_CONST)
 | 
						|
+						last_tv.tv_usec -= TIME_CONST;
 | 
						|
+					else {
 | 
						|
+						last_tv.tv_sec--;
 | 
						|
+						last_tv.tv_usec += 1000000 -
 | 
						|
+							TIME_CONST;
 | 
						|
+					}
 | 
						|
+				}
 | 
						|
+				last_intr_tv = curr_tv;
 | 
						|
+				if (data) {
 | 
						|
+					/*
 | 
						|
+					 * start timer for end of
 | 
						|
+					 * sequence detection
 | 
						|
+					 */
 | 
						|
+					timerlist.expires =
 | 
						|
+						jiffies + IT87_TIMEOUT;
 | 
						|
+					add_timer(&timerlist);
 | 
						|
+				}
 | 
						|
+				outb((inb(io + IT87_CIR_RCR) &
 | 
						|
+				     ~IT87_CIR_RCR_RXEN) |
 | 
						|
+				     IT87_CIR_RCR_RXACT,
 | 
						|
+				     io + IT87_CIR_RCR);
 | 
						|
+				if (it87_RXEN_mask)
 | 
						|
+					outb(inb(io + IT87_CIR_RCR) |
 | 
						|
+					     IT87_CIR_RCR_RXEN,
 | 
						|
+					     io + IT87_CIR_RCR);
 | 
						|
+				fifo--;
 | 
						|
+			} while (fifo != 0);
 | 
						|
+		}
 | 
						|
+		spin_unlock_irqrestore(&hardware_lock, hw_flags);
 | 
						|
+		spin_unlock_irqrestore(&timer_lock, flags);
 | 
						|
+
 | 
						|
+		return IRQ_RETVAL(IRQ_HANDLED);
 | 
						|
+
 | 
						|
+	default:
 | 
						|
+		/* not our irq */
 | 
						|
+		dprintk("unknown IRQ (shouldn't happen) !!\n");
 | 
						|
+		return IRQ_RETVAL(IRQ_NONE);
 | 
						|
+	}
 | 
						|
+}
 | 
						|
+
 | 
						|
+
 | 
						|
+static void send_it87(unsigned long len, unsigned long stime,
 | 
						|
+		      unsigned char send_byte, unsigned int count_bits)
 | 
						|
+{
 | 
						|
+	long count = len / stime;
 | 
						|
+	long time_left = 0;
 | 
						|
+	static unsigned char byte_out;
 | 
						|
+	unsigned long hw_flags;
 | 
						|
+
 | 
						|
+	dprintk("%s: len=%ld, sb=%d\n", __func__, len, send_byte);
 | 
						|
+
 | 
						|
+	time_left = (long)len - (long)count * (long)stime;
 | 
						|
+	count += ((2 * time_left) / stime);
 | 
						|
+	while (count) {
 | 
						|
+		long i = 0;
 | 
						|
+		for (i = 0; i < count_bits; i++) {
 | 
						|
+			byte_out = (byte_out << 1) | (send_byte & 1);
 | 
						|
+			it87_bits_in_byte_out++;
 | 
						|
+		}
 | 
						|
+		if (it87_bits_in_byte_out == 8) {
 | 
						|
+			dprintk("out=0x%x, tsr_txfbc: 0x%x\n",
 | 
						|
+				byte_out,
 | 
						|
+				inb(io + IT87_CIR_TSR) &
 | 
						|
+				IT87_CIR_TSR_TXFBC);
 | 
						|
+
 | 
						|
+			while ((inb(io + IT87_CIR_TSR) &
 | 
						|
+				IT87_CIR_TSR_TXFBC) >= IT87_CIR_FIFO_SIZE)
 | 
						|
+				;
 | 
						|
+
 | 
						|
+			spin_lock_irqsave(&hardware_lock, hw_flags);
 | 
						|
+			outb(byte_out, io + IT87_CIR_DR);
 | 
						|
+			spin_unlock_irqrestore(&hardware_lock, hw_flags);
 | 
						|
+
 | 
						|
+			it87_bits_in_byte_out = 0;
 | 
						|
+			it87_send_counter++;
 | 
						|
+			byte_out = 0;
 | 
						|
+		}
 | 
						|
+		count--;
 | 
						|
+	}
 | 
						|
+}
 | 
						|
+
 | 
						|
+
 | 
						|
+/*TODO: 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 re-enable receiver */
 | 
						|
+	spin_lock_irqsave(&hardware_lock, flags);
 | 
						|
+	it87_RXEN_mask = IT87_CIR_RCR_RXEN;
 | 
						|
+	outb(inb(io + IT87_CIR_RCR) | IT87_CIR_RCR_RXEN,
 | 
						|
+	     io + IT87_CIR_RCR);
 | 
						|
+	spin_unlock_irqrestore(&hardware_lock, flags);
 | 
						|
+}
 | 
						|
+
 | 
						|
+
 | 
						|
+static int init_hardware(void)
 | 
						|
+{
 | 
						|
+	unsigned long flags;
 | 
						|
+	unsigned char it87_rcr = 0;
 | 
						|
+
 | 
						|
+	spin_lock_irqsave(&hardware_lock, flags);
 | 
						|
+	/* init cir-port */
 | 
						|
+	/* enable r/w-access to Baudrate-Register */
 | 
						|
+	outb(IT87_CIR_IER_BR, io + IT87_CIR_IER);
 | 
						|
+	outb(IT87_CIR_BAUDRATE_DIVISOR % 0x100, io+IT87_CIR_BDLR);
 | 
						|
+	outb(IT87_CIR_BAUDRATE_DIVISOR / 0x100, io+IT87_CIR_BDHR);
 | 
						|
+	/* Baudrate Register off, define IRQs: Input only */
 | 
						|
+	if (digimatrix) {
 | 
						|
+		outb(IT87_CIR_IER_IEC | IT87_CIR_IER_RFOIE, io + IT87_CIR_IER);
 | 
						|
+		/* RX: HCFS=0, RXDCR = 001b (33,75..38,25 kHz), RXEN=1 */
 | 
						|
+	} else {
 | 
						|
+		outb(IT87_CIR_IER_IEC | IT87_CIR_IER_RDAIE, io + IT87_CIR_IER);
 | 
						|
+		/* RX: HCFS=0, RXDCR = 001b (35,6..40,3 kHz), RXEN=1 */
 | 
						|
+	}
 | 
						|
+	it87_rcr = (IT87_CIR_RCR_RXEN & it87_RXEN_mask) | 0x1;
 | 
						|
+	if (it87_enable_demodulator)
 | 
						|
+		it87_rcr |= IT87_CIR_RCR_RXEND;
 | 
						|
+	outb(it87_rcr, io + IT87_CIR_RCR);
 | 
						|
+	if (digimatrix) {
 | 
						|
+		/* Set FIFO depth to 1 byte, and disable TX */
 | 
						|
+		outb(inb(io + IT87_CIR_TCR1) |  0x00,
 | 
						|
+		     io + IT87_CIR_TCR1);
 | 
						|
+
 | 
						|
+		/*
 | 
						|
+		 * TX: it87_freq (36kHz), 'reserved' sensitivity
 | 
						|
+		 * setting (0x00)
 | 
						|
+		 */
 | 
						|
+		outb(((it87_freq - IT87_CIR_FREQ_MIN) << 3) | 0x00,
 | 
						|
+		     io + IT87_CIR_TCR2);
 | 
						|
+	} else {
 | 
						|
+		/* TX: 38kHz, 13,3us (pulse-width) */
 | 
						|
+		outb(((it87_freq - IT87_CIR_FREQ_MIN) << 3) | 0x06,
 | 
						|
+		     io + IT87_CIR_TCR2);
 | 
						|
+	}
 | 
						|
+	spin_unlock_irqrestore(&hardware_lock, flags);
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+
 | 
						|
+static void drop_hardware(void)
 | 
						|
+{
 | 
						|
+	unsigned long flags;
 | 
						|
+
 | 
						|
+	spin_lock_irqsave(&hardware_lock, flags);
 | 
						|
+	disable_irq(irq);
 | 
						|
+	/* receiver disable */
 | 
						|
+	it87_RXEN_mask = 0;
 | 
						|
+	outb(0x1, io + IT87_CIR_RCR);
 | 
						|
+	/* turn off irqs */
 | 
						|
+	outb(0, io + IT87_CIR_IER);
 | 
						|
+	/* fifo clear */
 | 
						|
+	outb(IT87_CIR_TCR1_FIFOCLR, io+IT87_CIR_TCR1);
 | 
						|
+	/* reset */
 | 
						|
+	outb(IT87_CIR_IER_RESET, io+IT87_CIR_IER);
 | 
						|
+	enable_irq(irq);
 | 
						|
+	spin_unlock_irqrestore(&hardware_lock, flags);
 | 
						|
+}
 | 
						|
+
 | 
						|
+
 | 
						|
+static unsigned char it87_read(unsigned char port)
 | 
						|
+{
 | 
						|
+	outb(port, IT87_ADRPORT);
 | 
						|
+	return inb(IT87_DATAPORT);
 | 
						|
+}
 | 
						|
+
 | 
						|
+
 | 
						|
+static void it87_write(unsigned char port, unsigned char data)
 | 
						|
+{
 | 
						|
+	outb(port, IT87_ADRPORT);
 | 
						|
+	outb(data, IT87_DATAPORT);
 | 
						|
+}
 | 
						|
+
 | 
						|
+
 | 
						|
+/* SECTION: Initialisation */
 | 
						|
+
 | 
						|
+static int init_port(void)
 | 
						|
+{
 | 
						|
+	unsigned long hw_flags;
 | 
						|
+	int retval = 0;
 | 
						|
+
 | 
						|
+	unsigned char init_bytes[4] = IT87_INIT;
 | 
						|
+	unsigned char it87_chipid = 0;
 | 
						|
+	unsigned char ldn = 0;
 | 
						|
+	unsigned int  it87_io = 0;
 | 
						|
+	unsigned int  it87_irq = 0;
 | 
						|
+
 | 
						|
+	/* Enter MB PnP Mode */
 | 
						|
+	outb(init_bytes[0], IT87_ADRPORT);
 | 
						|
+	outb(init_bytes[1], IT87_ADRPORT);
 | 
						|
+	outb(init_bytes[2], IT87_ADRPORT);
 | 
						|
+	outb(init_bytes[3], IT87_ADRPORT);
 | 
						|
+
 | 
						|
+	/* 8712 or 8705 ? */
 | 
						|
+	it87_chipid = it87_read(IT87_CHIP_ID1);
 | 
						|
+	if (it87_chipid != 0x87) {
 | 
						|
+		retval = -ENXIO;
 | 
						|
+		return retval;
 | 
						|
+	}
 | 
						|
+	it87_chipid = it87_read(IT87_CHIP_ID2);
 | 
						|
+	if ((it87_chipid != 0x05) &&
 | 
						|
+		(it87_chipid != 0x12) &&
 | 
						|
+		(it87_chipid != 0x18) &&
 | 
						|
+		(it87_chipid != 0x20)) {
 | 
						|
+		printk(KERN_INFO LIRC_DRIVER_NAME
 | 
						|
+		       ": no IT8704/05/12/18/20 found (claimed IT87%02x), "
 | 
						|
+		       "exiting..\n", it87_chipid);
 | 
						|
+		retval = -ENXIO;
 | 
						|
+		return retval;
 | 
						|
+	}
 | 
						|
+	printk(KERN_INFO LIRC_DRIVER_NAME
 | 
						|
+	       ": found IT87%02x.\n",
 | 
						|
+	       it87_chipid);
 | 
						|
+
 | 
						|
+	/* get I/O-Port and IRQ */
 | 
						|
+	if (it87_chipid == 0x12 || it87_chipid == 0x18)
 | 
						|
+		ldn = IT8712_CIR_LDN;
 | 
						|
+	else
 | 
						|
+		ldn = IT8705_CIR_LDN;
 | 
						|
+	it87_write(IT87_LDN, ldn);
 | 
						|
+
 | 
						|
+	it87_io = it87_read(IT87_CIR_BASE_MSB) * 256 +
 | 
						|
+		  it87_read(IT87_CIR_BASE_LSB);
 | 
						|
+	if (it87_io == 0) {
 | 
						|
+		if (io == 0)
 | 
						|
+			io = IT87_CIR_DEFAULT_IOBASE;
 | 
						|
+		printk(KERN_INFO LIRC_DRIVER_NAME
 | 
						|
+		       ": set default io 0x%x\n",
 | 
						|
+		       io);
 | 
						|
+		it87_write(IT87_CIR_BASE_MSB, io / 0x100);
 | 
						|
+		it87_write(IT87_CIR_BASE_LSB, io % 0x100);
 | 
						|
+	} else
 | 
						|
+		io = it87_io;
 | 
						|
+
 | 
						|
+	it87_irq = it87_read(IT87_CIR_IRQ);
 | 
						|
+	if (digimatrix || it87_irq == 0) {
 | 
						|
+		if (irq == 0)
 | 
						|
+			irq = IT87_CIR_DEFAULT_IRQ;
 | 
						|
+		printk(KERN_INFO LIRC_DRIVER_NAME
 | 
						|
+		       ": set default irq 0x%x\n",
 | 
						|
+		       irq);
 | 
						|
+		it87_write(IT87_CIR_IRQ, irq);
 | 
						|
+	} else
 | 
						|
+		irq = it87_irq;
 | 
						|
+
 | 
						|
+	spin_lock_irqsave(&hardware_lock, hw_flags);
 | 
						|
+	/* reset */
 | 
						|
+	outb(IT87_CIR_IER_RESET, io+IT87_CIR_IER);
 | 
						|
+	/* fifo clear */
 | 
						|
+	outb(IT87_CIR_TCR1_FIFOCLR |
 | 
						|
+	     /*	     IT87_CIR_TCR1_ILE | */
 | 
						|
+	     IT87_CIR_TCR1_TXRLE |
 | 
						|
+	     IT87_CIR_TCR1_TXENDF, io+IT87_CIR_TCR1);
 | 
						|
+	spin_unlock_irqrestore(&hardware_lock, hw_flags);
 | 
						|
+
 | 
						|
+	/* get I/O port access and IRQ line */
 | 
						|
+	if (request_region(io, 8, LIRC_DRIVER_NAME) == NULL) {
 | 
						|
+		printk(KERN_ERR LIRC_DRIVER_NAME
 | 
						|
+		       ": i/o port 0x%.4x already in use.\n", io);
 | 
						|
+		/* Leaving MB PnP Mode */
 | 
						|
+		it87_write(IT87_CFGCTRL, 0x2);
 | 
						|
+		return -EBUSY;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	/* activate CIR-Device */
 | 
						|
+	it87_write(IT87_CIR_ACT, 0x1);
 | 
						|
+
 | 
						|
+	/* Leaving MB PnP Mode */
 | 
						|
+	it87_write(IT87_CFGCTRL, 0x2);
 | 
						|
+
 | 
						|
+	retval = request_irq(irq, it87_interrupt, 0 /*IRQF_DISABLED*/,
 | 
						|
+			     LIRC_DRIVER_NAME, NULL);
 | 
						|
+	if (retval < 0) {
 | 
						|
+		printk(KERN_ERR LIRC_DRIVER_NAME
 | 
						|
+		       ": IRQ %d already in use.\n",
 | 
						|
+		       irq);
 | 
						|
+		release_region(io, 8);
 | 
						|
+		return retval;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	printk(KERN_INFO LIRC_DRIVER_NAME
 | 
						|
+	       ": I/O port 0x%.4x, IRQ %d.\n", io, irq);
 | 
						|
+
 | 
						|
+	init_timer(&timerlist);
 | 
						|
+	timerlist.function = it87_timeout;
 | 
						|
+	timerlist.data = 0xabadcafe;
 | 
						|
+
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+
 | 
						|
+static void drop_port(void)
 | 
						|
+{
 | 
						|
+#if 0
 | 
						|
+	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);
 | 
						|
+#endif
 | 
						|
+
 | 
						|
+	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;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int it87_probe(struct pnp_dev *pnp_dev,
 | 
						|
+		      const struct pnp_device_id *dev_id)
 | 
						|
+{
 | 
						|
+	int retval;
 | 
						|
+
 | 
						|
+	driver.dev = &pnp_dev->dev;
 | 
						|
+
 | 
						|
+	retval = init_chrdev();
 | 
						|
+	if (retval < 0)
 | 
						|
+		return retval;
 | 
						|
+
 | 
						|
+	retval = init_lirc_it87();
 | 
						|
+	if (retval)
 | 
						|
+		goto init_lirc_it87_failed;
 | 
						|
+
 | 
						|
+	return 0;
 | 
						|
+
 | 
						|
+init_lirc_it87_failed:
 | 
						|
+	drop_chrdev();
 | 
						|
+
 | 
						|
+	return retval;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int __init lirc_it87_init(void)
 | 
						|
+{
 | 
						|
+	return pnp_register_driver(&it87_pnp_driver);
 | 
						|
+}
 | 
						|
+
 | 
						|
+
 | 
						|
+static void __exit lirc_it87_exit(void)
 | 
						|
+{
 | 
						|
+	drop_hardware();
 | 
						|
+	drop_chrdev();
 | 
						|
+	drop_port();
 | 
						|
+	pnp_unregister_driver(&it87_pnp_driver);
 | 
						|
+	printk(KERN_INFO LIRC_DRIVER_NAME ": Uninstalled.\n");
 | 
						|
+}
 | 
						|
+
 | 
						|
+/* SECTION: PNP for ITE8704/18 */
 | 
						|
+
 | 
						|
+static const struct pnp_device_id pnp_dev_table[] = {
 | 
						|
+	{"ITE8704", 0},
 | 
						|
+	{}
 | 
						|
+};
 | 
						|
+
 | 
						|
+MODULE_DEVICE_TABLE(pnp, pnp_dev_table);
 | 
						|
+
 | 
						|
+static struct pnp_driver it87_pnp_driver = {
 | 
						|
+	.name           = LIRC_DRIVER_NAME,
 | 
						|
+	.id_table       = pnp_dev_table,
 | 
						|
+	.probe		= it87_probe,
 | 
						|
+};
 | 
						|
+
 | 
						|
+module_init(lirc_it87_init);
 | 
						|
+module_exit(lirc_it87_exit);
 | 
						|
+
 | 
						|
+MODULE_DESCRIPTION("LIRC driver for ITE IT8704/05/12/18/20 CIR port");
 | 
						|
+MODULE_AUTHOR("Hans-Gunter Lutke Uphues");
 | 
						|
+MODULE_LICENSE("GPL");
 | 
						|
+
 | 
						|
+module_param(io, int, S_IRUGO);
 | 
						|
+MODULE_PARM_DESC(io, "I/O base address (default: 0x310)");
 | 
						|
+
 | 
						|
+module_param(irq, int, S_IRUGO);
 | 
						|
+#ifdef LIRC_IT87_DIGIMATRIX
 | 
						|
+MODULE_PARM_DESC(irq, "Interrupt (1,3-12) (default: 9)");
 | 
						|
+#else
 | 
						|
+MODULE_PARM_DESC(irq, "Interrupt (1,3-12) (default: 7)");
 | 
						|
+#endif
 | 
						|
+
 | 
						|
+module_param(it87_enable_demodulator, bool, S_IRUGO);
 | 
						|
+MODULE_PARM_DESC(it87_enable_demodulator,
 | 
						|
+		 "Receiver demodulator enable/disable (1/0), default: 0");
 | 
						|
+
 | 
						|
+module_param(debug, bool, S_IRUGO | S_IWUSR);
 | 
						|
+MODULE_PARM_DESC(debug, "Enable debugging messages");
 | 
						|
+
 | 
						|
+module_param(digimatrix, bool, S_IRUGO | S_IWUSR);
 | 
						|
+#ifdef LIRC_IT87_DIGIMATRIX
 | 
						|
+MODULE_PARM_DESC(digimatrix,
 | 
						|
+	"Asus Digimatrix it87 compat. enable/disable (1/0), default: 1");
 | 
						|
+#else
 | 
						|
+MODULE_PARM_DESC(digimatrix,
 | 
						|
+	"Asus Digimatrix it87 compat. enable/disable (1/0), default: 0");
 | 
						|
+#endif
 | 
						|
+
 | 
						|
+
 | 
						|
+module_param(it87_freq, int, S_IRUGO);
 | 
						|
+#ifdef LIRC_IT87_DIGIMATRIX
 | 
						|
+MODULE_PARM_DESC(it87_freq,
 | 
						|
+    "Carrier demodulator frequency (kHz), (default: 36)");
 | 
						|
+#else
 | 
						|
+MODULE_PARM_DESC(it87_freq,
 | 
						|
+    "Carrier demodulator frequency (kHz), (default: 38)");
 | 
						|
+#endif
 | 
						|
diff --git a/drivers/staging/lirc/lirc_it87.h b/drivers/staging/lirc/lirc_it87.h
 | 
						|
new file mode 100644
 | 
						|
index 0000000..cf021c8
 | 
						|
--- /dev/null
 | 
						|
+++ b/drivers/staging/lirc/lirc_it87.h
 | 
						|
@@ -0,0 +1,116 @@
 | 
						|
+/* lirc_it87.h */
 | 
						|
+/* SECTION: Definitions */
 | 
						|
+
 | 
						|
+/********************************* ITE IT87xx ************************/
 | 
						|
+
 | 
						|
+/* based on the following documentation from ITE:
 | 
						|
+   a) IT8712F Preliminary CIR Programming Guide V0.1
 | 
						|
+   b) IT8705F Simple LPC I/O Preliminary Specification 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 Definition */
 | 
						|
+/* IER: */
 | 
						|
+#define IT87_CIR_IER_TM_EN   0x80
 | 
						|
+#define IT87_CIR_IER_RESEVED 0x40
 | 
						|
+#define IT87_CIR_IER_RESET   0x20
 | 
						|
+#define IT87_CIR_IER_BR      0x10
 | 
						|
+#define IT87_CIR_IER_IEC     0x8
 | 
						|
+#define IT87_CIR_IER_RFOIE   0x4
 | 
						|
+#define IT87_CIR_IER_RDAIE   0x2
 | 
						|
+#define IT87_CIR_IER_TLDLIE  0x1
 | 
						|
+
 | 
						|
+/* RCR: */
 | 
						|
+#define IT87_CIR_RCR_RDWOS  0x80
 | 
						|
+#define IT87_CIR_RCR_HCFS   0x40
 | 
						|
+#define IT87_CIR_RCR_RXEN   0x20
 | 
						|
+#define IT87_CIR_RCR_RXEND  0x10
 | 
						|
+#define IT87_CIR_RCR_RXACT  0x8
 | 
						|
+#define IT87_CIR_RCR_RXDCR  0x7
 | 
						|
+
 | 
						|
+/* TCR1: */
 | 
						|
+#define IT87_CIR_TCR1_FIFOCLR 0x80
 | 
						|
+#define IT87_CIR_TCR1_ILE     0x40
 | 
						|
+#define IT87_CIR_TCR1_FIFOTL  0x30
 | 
						|
+#define IT87_CIR_TCR1_TXRLE   0x8
 | 
						|
+#define IT87_CIR_TCR1_TXENDF  0x4
 | 
						|
+#define IT87_CIR_TCR1_TXMPM   0x3
 | 
						|
+
 | 
						|
+/* TCR2: */
 | 
						|
+#define IT87_CIR_TCR2_CFQ   0xf8
 | 
						|
+#define IT87_CIR_TCR2_TXMPW 0x7
 | 
						|
+
 | 
						|
+/* TSR: */
 | 
						|
+#define IT87_CIR_TSR_RESERVED 0xc0
 | 
						|
+#define IT87_CIR_TSR_TXFBC    0x3f
 | 
						|
+
 | 
						|
+/* RSR: */
 | 
						|
+#define IT87_CIR_RSR_RXFTO    0x80
 | 
						|
+#define IT87_CIR_RSR_RESERVED 0x40
 | 
						|
+#define IT87_CIR_RSR_RXFBC    0x3f
 | 
						|
+
 | 
						|
+/* IIR: */
 | 
						|
+#define IT87_CIR_IIR_RESERVED 0xf8
 | 
						|
+#define IT87_CIR_IIR_IID      0x6
 | 
						|
+#define IT87_CIR_IIR_IIP      0x1
 | 
						|
+
 | 
						|
+/* TM: */
 | 
						|
+#define IT87_CIR_TM_IL_SEL    0x80
 | 
						|
+#define IT87_CIR_TM_RESERVED  0x40
 | 
						|
+#define IT87_CIR_TM_TM_REG    0x3f
 | 
						|
+
 | 
						|
+#define IT87_CIR_FIFO_SIZE 32
 | 
						|
+
 | 
						|
+/* Baudratedivisor for IT87: power of 2: only 1,2,4 or 8) */
 | 
						|
+#define IT87_CIR_BAUDRATE_DIVISOR 0x1
 | 
						|
+#define IT87_CIR_DEFAULT_IOBASE 0x310
 | 
						|
+#define IT87_CIR_DEFAULT_IRQ    0x7
 | 
						|
+#define IT87_CIR_SPACE 0x00
 | 
						|
+#define IT87_CIR_PULSE 0xff
 | 
						|
+#define IT87_CIR_FREQ_MIN 27
 | 
						|
+#define IT87_CIR_FREQ_MAX 58
 | 
						|
+#define TIME_CONST (IT87_CIR_BAUDRATE_DIVISOR * 8000000ul / 115200ul)
 | 
						|
+
 | 
						|
+/********************************* ITE IT87xx ************************/
 | 
						|
diff --git a/drivers/staging/lirc/lirc_ite8709.c b/drivers/staging/lirc/lirc_ite8709.c
 | 
						|
new file mode 100644
 | 
						|
index 0000000..9352f45
 | 
						|
--- /dev/null
 | 
						|
+++ b/drivers/staging/lirc/lirc_ite8709.c
 | 
						|
@@ -0,0 +1,542 @@
 | 
						|
+/*
 | 
						|
+ * LIRC driver for ITE8709 CIR port
 | 
						|
+ *
 | 
						|
+ * Copyright (C) 2008 Grégory Lardière <spmf2004-lirc@yahoo.fr>
 | 
						|
+ *
 | 
						|
+ * This program is free software; you can redistribute it and/or
 | 
						|
+ * modify it under the terms of the GNU General Public License as
 | 
						|
+ * published by the Free Software Foundation; either version 2 of the
 | 
						|
+ * License, or (at your option) any later version.
 | 
						|
+ *
 | 
						|
+ * This program is distributed in the hope that it will be useful, but
 | 
						|
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
						|
+ * General Public License for more details.
 | 
						|
+ *
 | 
						|
+ * You should have received a copy of the GNU General Public License
 | 
						|
+ * along with this program; if not, write to the Free Software
 | 
						|
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 | 
						|
+ * USA
 | 
						|
+ */
 | 
						|
+
 | 
						|
+#include <linux/module.h>
 | 
						|
+#include <linux/interrupt.h>
 | 
						|
+#include <linux/sched.h>
 | 
						|
+#include <linux/delay.h>
 | 
						|
+#include <linux/pnp.h>
 | 
						|
+#include <linux/io.h>
 | 
						|
+
 | 
						|
+#include <media/lirc.h>
 | 
						|
+#include <media/lirc_dev.h>
 | 
						|
+
 | 
						|
+#define LIRC_DRIVER_NAME "lirc_ite8709"
 | 
						|
+
 | 
						|
+#define BUF_CHUNK_SIZE	sizeof(int)
 | 
						|
+#define BUF_SIZE	(128*BUF_CHUNK_SIZE)
 | 
						|
+
 | 
						|
+/*
 | 
						|
+ * The ITE8709 device seems to be the combination of IT8512 superIO chip and
 | 
						|
+ * a specific firmware running on the IT8512's embedded micro-controller.
 | 
						|
+ * In addition of the embedded micro-controller, the IT8512 chip contains a
 | 
						|
+ * CIR module and several other modules. A few modules are directly accessible
 | 
						|
+ * by the host CPU, but most of them are only accessible by the
 | 
						|
+ * micro-controller. The CIR module is only accessible by the micro-controller.
 | 
						|
+ * The battery-backed SRAM module is accessible by the host CPU and the
 | 
						|
+ * micro-controller. So one of the MC's firmware role is to act as a bridge
 | 
						|
+ * between the host CPU and the CIR module. The firmware implements a kind of
 | 
						|
+ * communication protocol using the SRAM module as a shared memory. The IT8512
 | 
						|
+ * specification is publicly available on ITE's web site, but the communication
 | 
						|
+ * protocol is not, so it was reverse-engineered.
 | 
						|
+ */
 | 
						|
+
 | 
						|
+/* ITE8709 Registers addresses and values (reverse-engineered) */
 | 
						|
+#define ITE8709_MODE		0x1a
 | 
						|
+#define ITE8709_REG_ADR		0x1b
 | 
						|
+#define ITE8709_REG_VAL		0x1c
 | 
						|
+#define ITE8709_IIR		0x1e  /* Interrupt identification register */
 | 
						|
+#define ITE8709_RFSR		0x1f  /* Receiver FIFO status register */
 | 
						|
+#define ITE8709_FIFO_START	0x20
 | 
						|
+
 | 
						|
+#define ITE8709_MODE_READY	0X00
 | 
						|
+#define ITE8709_MODE_WRITE	0X01
 | 
						|
+#define ITE8709_MODE_READ	0X02
 | 
						|
+#define ITE8709_IIR_RDAI	0x02  /* Receiver data available interrupt */
 | 
						|
+#define ITE8709_IIR_RFOI	0x04  /* Receiver FIFO overrun interrupt */
 | 
						|
+#define ITE8709_RFSR_MASK	0x3f  /* FIFO byte count mask */
 | 
						|
+
 | 
						|
+/*
 | 
						|
+ * IT8512 CIR-module registers addresses and values
 | 
						|
+ * (from IT8512 E/F specification v0.4.1)
 | 
						|
+ */
 | 
						|
+#define IT8512_REG_MSTCR	0x01  /* Master control register */
 | 
						|
+#define IT8512_REG_IER		0x02  /* Interrupt enable register */
 | 
						|
+#define IT8512_REG_CFR		0x04  /* Carrier frequency register */
 | 
						|
+#define IT8512_REG_RCR		0x05  /* Receive control register */
 | 
						|
+#define IT8512_REG_BDLR		0x08  /* Baud rate divisor low byte register */
 | 
						|
+#define IT8512_REG_BDHR		0x09  /* Baud rate divisor high byte register */
 | 
						|
+
 | 
						|
+#define IT8512_MSTCR_RESET	0x01  /* Reset registers to default value */
 | 
						|
+#define IT8512_MSTCR_FIFOCLR	0x02  /* Clear FIFO */
 | 
						|
+#define IT8512_MSTCR_FIFOTL_7	0x04  /* FIFO threshold level : 7 */
 | 
						|
+#define IT8512_MSTCR_FIFOTL_25	0x0c  /* FIFO threshold level : 25 */
 | 
						|
+#define IT8512_IER_RDAIE	0x02  /* Enable data interrupt request */
 | 
						|
+#define IT8512_IER_RFOIE	0x04  /* Enable FIFO overrun interrupt req */
 | 
						|
+#define IT8512_IER_IEC		0x80  /* Enable interrupt request */
 | 
						|
+#define IT8512_CFR_CF_36KHZ	0x09  /* Carrier freq : low speed, 36kHz */
 | 
						|
+#define IT8512_RCR_RXDCR_1	0x01  /* Demodulation carrier range : 1 */
 | 
						|
+#define IT8512_RCR_RXACT	0x08  /* Receiver active */
 | 
						|
+#define IT8512_RCR_RXEN		0x80  /* Receiver enable */
 | 
						|
+#define IT8512_BDR_6		6     /* Baud rate divisor : 6 */
 | 
						|
+
 | 
						|
+/* Actual values used by this driver */
 | 
						|
+#define CFG_FIFOTL	IT8512_MSTCR_FIFOTL_25
 | 
						|
+#define CFG_CR_FREQ	IT8512_CFR_CF_36KHZ
 | 
						|
+#define CFG_DCR		IT8512_RCR_RXDCR_1
 | 
						|
+#define CFG_BDR		IT8512_BDR_6
 | 
						|
+#define CFG_TIMEOUT	100000 /* Rearm interrupt when a space is > 100 ms */
 | 
						|
+
 | 
						|
+static int debug;
 | 
						|
+
 | 
						|
+struct ite8709_device {
 | 
						|
+	int use_count;
 | 
						|
+	int io;
 | 
						|
+	int irq;
 | 
						|
+	spinlock_t hardware_lock;
 | 
						|
+	unsigned long long acc_pulse;
 | 
						|
+	unsigned long long acc_space;
 | 
						|
+	char lastbit;
 | 
						|
+	struct timeval last_tv;
 | 
						|
+	struct lirc_driver driver;
 | 
						|
+	struct tasklet_struct tasklet;
 | 
						|
+	char force_rearm;
 | 
						|
+	char rearmed;
 | 
						|
+	char device_busy;
 | 
						|
+};
 | 
						|
+
 | 
						|
+#define dprintk(fmt, args...)					\
 | 
						|
+	do {							\
 | 
						|
+		if (debug)					\
 | 
						|
+			printk(KERN_DEBUG LIRC_DRIVER_NAME ": "	\
 | 
						|
+				fmt, ## args);			\
 | 
						|
+	} while (0)
 | 
						|
+
 | 
						|
+
 | 
						|
+static unsigned char ite8709_read(struct ite8709_device *dev,
 | 
						|
+					unsigned char port)
 | 
						|
+{
 | 
						|
+	outb(port, dev->io);
 | 
						|
+	return inb(dev->io+1);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void ite8709_write(struct ite8709_device *dev, unsigned char port,
 | 
						|
+				unsigned char data)
 | 
						|
+{
 | 
						|
+	outb(port, dev->io);
 | 
						|
+	outb(data, dev->io+1);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void ite8709_wait_device(struct ite8709_device *dev)
 | 
						|
+{
 | 
						|
+	int i = 0;
 | 
						|
+	/*
 | 
						|
+	 * loop until device tells it's ready to continue
 | 
						|
+	 * iterations count is usually ~750 but can sometimes achieve 13000
 | 
						|
+	 */
 | 
						|
+	for (i = 0; i < 15000; i++) {
 | 
						|
+		udelay(2);
 | 
						|
+		if (ite8709_read(dev, ITE8709_MODE) == ITE8709_MODE_READY)
 | 
						|
+			break;
 | 
						|
+	}
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void ite8709_write_register(struct ite8709_device *dev,
 | 
						|
+				unsigned char reg_adr, unsigned char reg_value)
 | 
						|
+{
 | 
						|
+	ite8709_wait_device(dev);
 | 
						|
+
 | 
						|
+	ite8709_write(dev, ITE8709_REG_VAL, reg_value);
 | 
						|
+	ite8709_write(dev, ITE8709_REG_ADR, reg_adr);
 | 
						|
+	ite8709_write(dev, ITE8709_MODE, ITE8709_MODE_WRITE);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void ite8709_init_hardware(struct ite8709_device *dev)
 | 
						|
+{
 | 
						|
+	spin_lock_irq(&dev->hardware_lock);
 | 
						|
+	dev->device_busy = 1;
 | 
						|
+	spin_unlock_irq(&dev->hardware_lock);
 | 
						|
+
 | 
						|
+	ite8709_write_register(dev, IT8512_REG_BDHR, (CFG_BDR >> 8) & 0xff);
 | 
						|
+	ite8709_write_register(dev, IT8512_REG_BDLR, CFG_BDR & 0xff);
 | 
						|
+	ite8709_write_register(dev, IT8512_REG_CFR, CFG_CR_FREQ);
 | 
						|
+	ite8709_write_register(dev, IT8512_REG_IER,
 | 
						|
+			IT8512_IER_IEC | IT8512_IER_RFOIE | IT8512_IER_RDAIE);
 | 
						|
+	ite8709_write_register(dev, IT8512_REG_RCR, CFG_DCR);
 | 
						|
+	ite8709_write_register(dev, IT8512_REG_MSTCR,
 | 
						|
+					CFG_FIFOTL | IT8512_MSTCR_FIFOCLR);
 | 
						|
+	ite8709_write_register(dev, IT8512_REG_RCR,
 | 
						|
+				IT8512_RCR_RXEN | IT8512_RCR_RXACT | CFG_DCR);
 | 
						|
+
 | 
						|
+	spin_lock_irq(&dev->hardware_lock);
 | 
						|
+	dev->device_busy = 0;
 | 
						|
+	spin_unlock_irq(&dev->hardware_lock);
 | 
						|
+
 | 
						|
+	tasklet_enable(&dev->tasklet);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void ite8709_drop_hardware(struct ite8709_device *dev)
 | 
						|
+{
 | 
						|
+	tasklet_disable(&dev->tasklet);
 | 
						|
+
 | 
						|
+	spin_lock_irq(&dev->hardware_lock);
 | 
						|
+	dev->device_busy = 1;
 | 
						|
+	spin_unlock_irq(&dev->hardware_lock);
 | 
						|
+
 | 
						|
+	ite8709_write_register(dev, IT8512_REG_RCR, 0);
 | 
						|
+	ite8709_write_register(dev, IT8512_REG_MSTCR,
 | 
						|
+				IT8512_MSTCR_RESET | IT8512_MSTCR_FIFOCLR);
 | 
						|
+
 | 
						|
+	spin_lock_irq(&dev->hardware_lock);
 | 
						|
+	dev->device_busy = 0;
 | 
						|
+	spin_unlock_irq(&dev->hardware_lock);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int ite8709_set_use_inc(void *data)
 | 
						|
+{
 | 
						|
+	struct ite8709_device *dev;
 | 
						|
+	dev = data;
 | 
						|
+	if (dev->use_count == 0)
 | 
						|
+		ite8709_init_hardware(dev);
 | 
						|
+	dev->use_count++;
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void ite8709_set_use_dec(void *data)
 | 
						|
+{
 | 
						|
+	struct ite8709_device *dev;
 | 
						|
+	dev = data;
 | 
						|
+	dev->use_count--;
 | 
						|
+	if (dev->use_count == 0)
 | 
						|
+		ite8709_drop_hardware(dev);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void ite8709_add_read_queue(struct ite8709_device *dev, int flag,
 | 
						|
+					unsigned long long val)
 | 
						|
+{
 | 
						|
+	int value;
 | 
						|
+
 | 
						|
+	dprintk("add a %llu usec %s\n", val, flag ? "pulse" : "space");
 | 
						|
+
 | 
						|
+	value = (val > PULSE_MASK) ? PULSE_MASK : val;
 | 
						|
+	if (flag)
 | 
						|
+		value |= PULSE_BIT;
 | 
						|
+
 | 
						|
+	if (!lirc_buffer_full(dev->driver.rbuf)) {
 | 
						|
+		lirc_buffer_write(dev->driver.rbuf, (void *) &value);
 | 
						|
+		wake_up(&dev->driver.rbuf->wait_poll);
 | 
						|
+	}
 | 
						|
+}
 | 
						|
+
 | 
						|
+static irqreturn_t ite8709_interrupt(int irq, void *dev_id)
 | 
						|
+{
 | 
						|
+	unsigned char data;
 | 
						|
+	int iir, rfsr, i;
 | 
						|
+	int fifo = 0;
 | 
						|
+	char bit;
 | 
						|
+	struct timeval curr_tv;
 | 
						|
+
 | 
						|
+	/* Bit duration in microseconds */
 | 
						|
+	const unsigned long bit_duration = 1000000ul / (115200 / CFG_BDR);
 | 
						|
+
 | 
						|
+	struct ite8709_device *dev;
 | 
						|
+	dev = dev_id;
 | 
						|
+
 | 
						|
+	/*
 | 
						|
+	 * If device is busy, we simply discard data because we are in one of
 | 
						|
+	 * these two cases : shutting down or rearming the device, so this
 | 
						|
+	 * doesn't really matter and this avoids waiting too long in IRQ ctx
 | 
						|
+	 */
 | 
						|
+	spin_lock(&dev->hardware_lock);
 | 
						|
+	if (dev->device_busy) {
 | 
						|
+		spin_unlock(&dev->hardware_lock);
 | 
						|
+		return IRQ_RETVAL(IRQ_HANDLED);
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	iir = ite8709_read(dev, ITE8709_IIR);
 | 
						|
+
 | 
						|
+	switch (iir) {
 | 
						|
+	case ITE8709_IIR_RFOI:
 | 
						|
+		dprintk("fifo overrun, scheduling forced rearm just in case\n");
 | 
						|
+		dev->force_rearm = 1;
 | 
						|
+		tasklet_schedule(&dev->tasklet);
 | 
						|
+		spin_unlock(&dev->hardware_lock);
 | 
						|
+		return IRQ_RETVAL(IRQ_HANDLED);
 | 
						|
+
 | 
						|
+	case ITE8709_IIR_RDAI:
 | 
						|
+		rfsr = ite8709_read(dev, ITE8709_RFSR);
 | 
						|
+		fifo = rfsr & ITE8709_RFSR_MASK;
 | 
						|
+		if (fifo > 32)
 | 
						|
+			fifo = 32;
 | 
						|
+		dprintk("iir: 0x%x rfsr: 0x%x fifo: %d\n", iir, rfsr, fifo);
 | 
						|
+
 | 
						|
+		if (dev->rearmed) {
 | 
						|
+			do_gettimeofday(&curr_tv);
 | 
						|
+			dev->acc_space += 1000000ull
 | 
						|
+				* (curr_tv.tv_sec - dev->last_tv.tv_sec)
 | 
						|
+				+ (curr_tv.tv_usec - dev->last_tv.tv_usec);
 | 
						|
+			dev->rearmed = 0;
 | 
						|
+		}
 | 
						|
+		for (i = 0; i < fifo; i++) {
 | 
						|
+			data = ite8709_read(dev, i+ITE8709_FIFO_START);
 | 
						|
+			data = ~data;
 | 
						|
+			/* Loop through */
 | 
						|
+			for (bit = 0; bit < 8; ++bit) {
 | 
						|
+				if ((data >> bit) & 1) {
 | 
						|
+					dev->acc_pulse += bit_duration;
 | 
						|
+					if (dev->lastbit == 0) {
 | 
						|
+						ite8709_add_read_queue(dev, 0,
 | 
						|
+							dev->acc_space);
 | 
						|
+						dev->acc_space = 0;
 | 
						|
+					}
 | 
						|
+				} else {
 | 
						|
+					dev->acc_space += bit_duration;
 | 
						|
+					if (dev->lastbit == 1) {
 | 
						|
+						ite8709_add_read_queue(dev, 1,
 | 
						|
+							dev->acc_pulse);
 | 
						|
+						dev->acc_pulse = 0;
 | 
						|
+					}
 | 
						|
+				}
 | 
						|
+				dev->lastbit = (data >> bit) & 1;
 | 
						|
+			}
 | 
						|
+		}
 | 
						|
+		ite8709_write(dev, ITE8709_RFSR, 0);
 | 
						|
+
 | 
						|
+		if (dev->acc_space > CFG_TIMEOUT) {
 | 
						|
+			dprintk("scheduling rearm IRQ\n");
 | 
						|
+			do_gettimeofday(&dev->last_tv);
 | 
						|
+			dev->force_rearm = 0;
 | 
						|
+			tasklet_schedule(&dev->tasklet);
 | 
						|
+		}
 | 
						|
+
 | 
						|
+		spin_unlock(&dev->hardware_lock);
 | 
						|
+		return IRQ_RETVAL(IRQ_HANDLED);
 | 
						|
+
 | 
						|
+	default:
 | 
						|
+		/* not our irq */
 | 
						|
+		dprintk("unknown IRQ (shouldn't happen) !!\n");
 | 
						|
+		spin_unlock(&dev->hardware_lock);
 | 
						|
+		return IRQ_RETVAL(IRQ_NONE);
 | 
						|
+	}
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void ite8709_rearm_irq(unsigned long data)
 | 
						|
+{
 | 
						|
+	struct ite8709_device *dev;
 | 
						|
+	unsigned long flags;
 | 
						|
+	dev = (struct ite8709_device *) data;
 | 
						|
+
 | 
						|
+	spin_lock_irqsave(&dev->hardware_lock, flags);
 | 
						|
+	dev->device_busy = 1;
 | 
						|
+	spin_unlock_irqrestore(&dev->hardware_lock, flags);
 | 
						|
+
 | 
						|
+	if (dev->force_rearm || dev->acc_space > CFG_TIMEOUT) {
 | 
						|
+		dprintk("rearming IRQ\n");
 | 
						|
+		ite8709_write_register(dev, IT8512_REG_RCR,
 | 
						|
+						IT8512_RCR_RXACT | CFG_DCR);
 | 
						|
+		ite8709_write_register(dev, IT8512_REG_MSTCR,
 | 
						|
+					CFG_FIFOTL | IT8512_MSTCR_FIFOCLR);
 | 
						|
+		ite8709_write_register(dev, IT8512_REG_RCR,
 | 
						|
+				IT8512_RCR_RXEN | IT8512_RCR_RXACT | CFG_DCR);
 | 
						|
+		if (!dev->force_rearm)
 | 
						|
+			dev->rearmed = 1;
 | 
						|
+		dev->force_rearm = 0;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	spin_lock_irqsave(&dev->hardware_lock, flags);
 | 
						|
+	dev->device_busy = 0;
 | 
						|
+	spin_unlock_irqrestore(&dev->hardware_lock, flags);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int ite8709_cleanup(struct ite8709_device *dev, int stage, int errno,
 | 
						|
+				char *msg)
 | 
						|
+{
 | 
						|
+	if (msg != NULL)
 | 
						|
+		printk(KERN_ERR LIRC_DRIVER_NAME ": %s\n", msg);
 | 
						|
+
 | 
						|
+	switch (stage) {
 | 
						|
+	case 6:
 | 
						|
+		if (dev->use_count > 0)
 | 
						|
+			ite8709_drop_hardware(dev);
 | 
						|
+	case 5:
 | 
						|
+		free_irq(dev->irq, dev);
 | 
						|
+	case 4:
 | 
						|
+		release_region(dev->io, 2);
 | 
						|
+	case 3:
 | 
						|
+		lirc_unregister_driver(dev->driver.minor);
 | 
						|
+	case 2:
 | 
						|
+		lirc_buffer_free(dev->driver.rbuf);
 | 
						|
+		kfree(dev->driver.rbuf);
 | 
						|
+	case 1:
 | 
						|
+		kfree(dev);
 | 
						|
+	case 0:
 | 
						|
+		;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	return errno;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int __devinit ite8709_pnp_probe(struct pnp_dev *dev,
 | 
						|
+					const struct pnp_device_id *dev_id)
 | 
						|
+{
 | 
						|
+	struct lirc_driver *driver;
 | 
						|
+	struct ite8709_device *ite8709_dev;
 | 
						|
+	int ret;
 | 
						|
+
 | 
						|
+	/* Check resources validity */
 | 
						|
+	if (!pnp_irq_valid(dev, 0))
 | 
						|
+		return ite8709_cleanup(NULL, 0, -ENODEV, "invalid IRQ");
 | 
						|
+	if (!pnp_port_valid(dev, 2))
 | 
						|
+		return ite8709_cleanup(NULL, 0, -ENODEV, "invalid IO port");
 | 
						|
+
 | 
						|
+	/* Allocate memory for device struct */
 | 
						|
+	ite8709_dev = kzalloc(sizeof(struct ite8709_device), GFP_KERNEL);
 | 
						|
+	if (ite8709_dev == NULL)
 | 
						|
+		return ite8709_cleanup(NULL, 0, -ENOMEM, "kzalloc failed");
 | 
						|
+	pnp_set_drvdata(dev, ite8709_dev);
 | 
						|
+
 | 
						|
+	/* Initialize device struct */
 | 
						|
+	ite8709_dev->use_count = 0;
 | 
						|
+	ite8709_dev->irq = pnp_irq(dev, 0);
 | 
						|
+	ite8709_dev->io = pnp_port_start(dev, 2);
 | 
						|
+	ite8709_dev->hardware_lock =
 | 
						|
+		__SPIN_LOCK_UNLOCKED(ite8709_dev->hardware_lock);
 | 
						|
+	ite8709_dev->acc_pulse = 0;
 | 
						|
+	ite8709_dev->acc_space = 0;
 | 
						|
+	ite8709_dev->lastbit = 0;
 | 
						|
+	do_gettimeofday(&ite8709_dev->last_tv);
 | 
						|
+	tasklet_init(&ite8709_dev->tasklet, ite8709_rearm_irq,
 | 
						|
+							(long) ite8709_dev);
 | 
						|
+	ite8709_dev->force_rearm = 0;
 | 
						|
+	ite8709_dev->rearmed = 0;
 | 
						|
+	ite8709_dev->device_busy = 0;
 | 
						|
+
 | 
						|
+	/* Initialize driver struct */
 | 
						|
+	driver = &ite8709_dev->driver;
 | 
						|
+	strcpy(driver->name, LIRC_DRIVER_NAME);
 | 
						|
+	driver->minor = -1;
 | 
						|
+	driver->code_length = sizeof(int) * 8;
 | 
						|
+	driver->sample_rate = 0;
 | 
						|
+	driver->features = LIRC_CAN_REC_MODE2;
 | 
						|
+	driver->data = ite8709_dev;
 | 
						|
+	driver->add_to_buf = NULL;
 | 
						|
+	driver->set_use_inc = ite8709_set_use_inc;
 | 
						|
+	driver->set_use_dec = ite8709_set_use_dec;
 | 
						|
+	driver->dev = &dev->dev;
 | 
						|
+	driver->owner = THIS_MODULE;
 | 
						|
+
 | 
						|
+	/* Initialize LIRC buffer */
 | 
						|
+	driver->rbuf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
 | 
						|
+	if (!driver->rbuf)
 | 
						|
+		return ite8709_cleanup(ite8709_dev, 1, -ENOMEM,
 | 
						|
+				       "can't allocate lirc_buffer");
 | 
						|
+	if (lirc_buffer_init(driver->rbuf, BUF_CHUNK_SIZE, BUF_SIZE))
 | 
						|
+		return ite8709_cleanup(ite8709_dev, 1, -ENOMEM,
 | 
						|
+				       "lirc_buffer_init() failed");
 | 
						|
+
 | 
						|
+	/* Register LIRC driver */
 | 
						|
+	ret = lirc_register_driver(driver);
 | 
						|
+	if (ret < 0)
 | 
						|
+		return ite8709_cleanup(ite8709_dev, 2, ret,
 | 
						|
+					"lirc_register_driver() failed");
 | 
						|
+
 | 
						|
+	/* Reserve I/O port access */
 | 
						|
+	if (!request_region(ite8709_dev->io, 2, LIRC_DRIVER_NAME))
 | 
						|
+		return ite8709_cleanup(ite8709_dev, 3, -EBUSY,
 | 
						|
+						"i/o port already in use");
 | 
						|
+
 | 
						|
+	/* Reserve IRQ line */
 | 
						|
+	ret = request_irq(ite8709_dev->irq, ite8709_interrupt, 0,
 | 
						|
+					LIRC_DRIVER_NAME, ite8709_dev);
 | 
						|
+	if (ret < 0)
 | 
						|
+		return ite8709_cleanup(ite8709_dev, 4, ret,
 | 
						|
+						"IRQ already in use");
 | 
						|
+
 | 
						|
+	/* Initialize hardware */
 | 
						|
+	ite8709_drop_hardware(ite8709_dev); /* Shutdown hw until first use */
 | 
						|
+
 | 
						|
+	printk(KERN_INFO LIRC_DRIVER_NAME ": device found : irq=%d io=0x%x\n",
 | 
						|
+					ite8709_dev->irq, ite8709_dev->io);
 | 
						|
+
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void __devexit ite8709_pnp_remove(struct pnp_dev *dev)
 | 
						|
+{
 | 
						|
+	struct ite8709_device *ite8709_dev;
 | 
						|
+	ite8709_dev = pnp_get_drvdata(dev);
 | 
						|
+
 | 
						|
+	ite8709_cleanup(ite8709_dev, 6, 0, NULL);
 | 
						|
+
 | 
						|
+	printk(KERN_INFO LIRC_DRIVER_NAME ": device removed\n");
 | 
						|
+}
 | 
						|
+
 | 
						|
+#ifdef CONFIG_PM
 | 
						|
+static int ite8709_pnp_suspend(struct pnp_dev *dev, pm_message_t state)
 | 
						|
+{
 | 
						|
+	struct ite8709_device *ite8709_dev;
 | 
						|
+	ite8709_dev = pnp_get_drvdata(dev);
 | 
						|
+
 | 
						|
+	if (ite8709_dev->use_count > 0)
 | 
						|
+		ite8709_drop_hardware(ite8709_dev);
 | 
						|
+
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int ite8709_pnp_resume(struct pnp_dev *dev)
 | 
						|
+{
 | 
						|
+	struct ite8709_device *ite8709_dev;
 | 
						|
+	ite8709_dev = pnp_get_drvdata(dev);
 | 
						|
+
 | 
						|
+	if (ite8709_dev->use_count > 0)
 | 
						|
+		ite8709_init_hardware(ite8709_dev);
 | 
						|
+
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+#else
 | 
						|
+#define ite8709_pnp_suspend NULL
 | 
						|
+#define ite8709_pnp_resume NULL
 | 
						|
+#endif
 | 
						|
+
 | 
						|
+static const struct pnp_device_id pnp_dev_table[] = {
 | 
						|
+	{"ITE8709", 0},
 | 
						|
+	{}
 | 
						|
+};
 | 
						|
+
 | 
						|
+MODULE_DEVICE_TABLE(pnp, pnp_dev_table);
 | 
						|
+
 | 
						|
+static struct pnp_driver ite8709_pnp_driver = {
 | 
						|
+	.name           = LIRC_DRIVER_NAME,
 | 
						|
+	.probe          = ite8709_pnp_probe,
 | 
						|
+	.remove         = __devexit_p(ite8709_pnp_remove),
 | 
						|
+	.suspend        = ite8709_pnp_suspend,
 | 
						|
+	.resume         = ite8709_pnp_resume,
 | 
						|
+	.id_table       = pnp_dev_table,
 | 
						|
+};
 | 
						|
+
 | 
						|
+static int __init ite8709_init_module(void)
 | 
						|
+{
 | 
						|
+	return pnp_register_driver(&ite8709_pnp_driver);
 | 
						|
+}
 | 
						|
+module_init(ite8709_init_module);
 | 
						|
+
 | 
						|
+static void __exit ite8709_cleanup_module(void)
 | 
						|
+{
 | 
						|
+	pnp_unregister_driver(&ite8709_pnp_driver);
 | 
						|
+}
 | 
						|
+module_exit(ite8709_cleanup_module);
 | 
						|
+
 | 
						|
+MODULE_DESCRIPTION("LIRC driver for ITE8709 CIR port");
 | 
						|
+MODULE_AUTHOR("Grégory Lardière");
 | 
						|
+MODULE_LICENSE("GPL");
 | 
						|
+
 | 
						|
+module_param(debug, bool, S_IRUGO | S_IWUSR);
 | 
						|
+MODULE_PARM_DESC(debug, "Enable debugging messages");
 | 
						|
diff --git a/drivers/staging/lirc/lirc_parallel.c b/drivers/staging/lirc/lirc_parallel.c
 | 
						|
new file mode 100644
 | 
						|
index 0000000..df12e7b
 | 
						|
--- /dev/null
 | 
						|
+++ b/drivers/staging/lirc/lirc_parallel.c
 | 
						|
@@ -0,0 +1,705 @@
 | 
						|
+/*
 | 
						|
+ * 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 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/fs.h>
 | 
						|
+#include <linux/kernel.h>
 | 
						|
+#include <linux/ioport.h>
 | 
						|
+#include <linux/time.h>
 | 
						|
+#include <linux/mm.h>
 | 
						|
+#include <linux/delay.h>
 | 
						|
+
 | 
						|
+#include <linux/io.h>
 | 
						|
+#include <linux/signal.h>
 | 
						|
+#include <linux/irq.h>
 | 
						|
+#include <linux/uaccess.h>
 | 
						|
+#include <asm/div64.h>
 | 
						|
+
 | 
						|
+#include <linux/poll.h>
 | 
						|
+#include <linux/parport.h>
 | 
						|
+
 | 
						|
+#include <media/lirc.h>
 | 
						|
+#include <media/lirc_dev.h>
 | 
						|
+
 | 
						|
+#include "lirc_parallel.h"
 | 
						|
+
 | 
						|
+#define LIRC_DRIVER_NAME "lirc_parallel"
 | 
						|
+
 | 
						|
+#ifndef LIRC_IRQ
 | 
						|
+#define LIRC_IRQ 7
 | 
						|
+#endif
 | 
						|
+#ifndef LIRC_PORT
 | 
						|
+#define LIRC_PORT 0x378
 | 
						|
+#endif
 | 
						|
+#ifndef LIRC_TIMER
 | 
						|
+#define LIRC_TIMER 65536
 | 
						|
+#endif
 | 
						|
+
 | 
						|
+/*** Global Variables ***/
 | 
						|
+
 | 
						|
+static int debug;
 | 
						|
+static int check_pselecd;
 | 
						|
+
 | 
						|
+unsigned int irq = LIRC_IRQ;
 | 
						|
+unsigned int io = LIRC_PORT;
 | 
						|
+#ifdef LIRC_TIMER
 | 
						|
+unsigned int timer;
 | 
						|
+unsigned int default_timer = LIRC_TIMER;
 | 
						|
+#endif
 | 
						|
+
 | 
						|
+#define RBUF_SIZE (256) /* this must be a power of 2 larger than 1 */
 | 
						|
+
 | 
						|
+static int rbuf[RBUF_SIZE];
 | 
						|
+
 | 
						|
+DECLARE_WAIT_QUEUE_HEAD(lirc_wait);
 | 
						|
+
 | 
						|
+unsigned int rptr;
 | 
						|
+unsigned int wptr;
 | 
						|
+unsigned int lost_irqs;
 | 
						|
+int is_open;
 | 
						|
+
 | 
						|
+struct parport *pport;
 | 
						|
+struct pardevice *ppdevice;
 | 
						|
+int is_claimed;
 | 
						|
+
 | 
						|
+unsigned int tx_mask = 1;
 | 
						|
+
 | 
						|
+/*** Internal Functions ***/
 | 
						|
+
 | 
						|
+static unsigned int in(int offset)
 | 
						|
+{
 | 
						|
+	switch (offset) {
 | 
						|
+	case LIRC_LP_BASE:
 | 
						|
+		return parport_read_data(pport);
 | 
						|
+	case LIRC_LP_STATUS:
 | 
						|
+		return parport_read_status(pport);
 | 
						|
+	case LIRC_LP_CONTROL:
 | 
						|
+		return parport_read_control(pport);
 | 
						|
+	}
 | 
						|
+	return 0; /* make compiler happy */
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void out(int offset, int value)
 | 
						|
+{
 | 
						|
+	switch (offset) {
 | 
						|
+	case LIRC_LP_BASE:
 | 
						|
+		parport_write_data(pport, value);
 | 
						|
+		break;
 | 
						|
+	case LIRC_LP_CONTROL:
 | 
						|
+		parport_write_control(pport, value);
 | 
						|
+		break;
 | 
						|
+	case LIRC_LP_STATUS:
 | 
						|
+		printk(KERN_INFO "%s: attempt to write to status register\n",
 | 
						|
+		       LIRC_DRIVER_NAME);
 | 
						|
+		break;
 | 
						|
+	}
 | 
						|
+}
 | 
						|
+
 | 
						|
+static unsigned int lirc_get_timer(void)
 | 
						|
+{
 | 
						|
+	return in(LIRC_PORT_TIMER) & LIRC_PORT_TIMER_BIT;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static unsigned int lirc_get_signal(void)
 | 
						|
+{
 | 
						|
+	return in(LIRC_PORT_SIGNAL) & LIRC_PORT_SIGNAL_BIT;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void lirc_on(void)
 | 
						|
+{
 | 
						|
+	out(LIRC_PORT_DATA, tx_mask);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void lirc_off(void)
 | 
						|
+{
 | 
						|
+	out(LIRC_PORT_DATA, 0);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static unsigned int init_lirc_timer(void)
 | 
						|
+{
 | 
						|
+	struct timeval tv, now;
 | 
						|
+	unsigned int level, newlevel, timeelapsed, newtimer;
 | 
						|
+	int count = 0;
 | 
						|
+
 | 
						|
+	do_gettimeofday(&tv);
 | 
						|
+	tv.tv_sec++;                     /* wait max. 1 sec. */
 | 
						|
+	level = lirc_get_timer();
 | 
						|
+	do {
 | 
						|
+		newlevel = lirc_get_timer();
 | 
						|
+		if (level == 0 && newlevel != 0)
 | 
						|
+			count++;
 | 
						|
+		level = newlevel;
 | 
						|
+		do_gettimeofday(&now);
 | 
						|
+	} while (count < 1000 && (now.tv_sec < tv.tv_sec
 | 
						|
+			     || (now.tv_sec == tv.tv_sec
 | 
						|
+				 && now.tv_usec < tv.tv_usec)));
 | 
						|
+
 | 
						|
+	timeelapsed = ((now.tv_sec + 1 - tv.tv_sec)*1000000
 | 
						|
+		     + (now.tv_usec - tv.tv_usec));
 | 
						|
+	if (count >= 1000 && timeelapsed > 0) {
 | 
						|
+		if (default_timer == 0) {
 | 
						|
+			/* autodetect timer */
 | 
						|
+			newtimer = (1000000*count)/timeelapsed;
 | 
						|
+			printk(KERN_INFO "%s: %u Hz timer detected\n",
 | 
						|
+			       LIRC_DRIVER_NAME, newtimer);
 | 
						|
+			return newtimer;
 | 
						|
+		}  else {
 | 
						|
+			newtimer = (1000000*count)/timeelapsed;
 | 
						|
+			if (abs(newtimer - default_timer) > default_timer/10) {
 | 
						|
+				/* bad timer */
 | 
						|
+				printk(KERN_NOTICE "%s: bad timer: %u Hz\n",
 | 
						|
+				       LIRC_DRIVER_NAME, newtimer);
 | 
						|
+				printk(KERN_NOTICE "%s: using default timer: "
 | 
						|
+				       "%u Hz\n",
 | 
						|
+				       LIRC_DRIVER_NAME, default_timer);
 | 
						|
+				return default_timer;
 | 
						|
+			} else {
 | 
						|
+				printk(KERN_INFO "%s: %u Hz timer detected\n",
 | 
						|
+				       LIRC_DRIVER_NAME, newtimer);
 | 
						|
+				return newtimer; /* use detected value */
 | 
						|
+			}
 | 
						|
+		}
 | 
						|
+	} else {
 | 
						|
+		printk(KERN_NOTICE "%s: no timer detected\n", LIRC_DRIVER_NAME);
 | 
						|
+		return 0;
 | 
						|
+	}
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int lirc_claim(void)
 | 
						|
+{
 | 
						|
+	if (parport_claim(ppdevice) != 0) {
 | 
						|
+		printk(KERN_WARNING "%s: could not claim port\n",
 | 
						|
+		       LIRC_DRIVER_NAME);
 | 
						|
+		printk(KERN_WARNING "%s: waiting for port becoming available"
 | 
						|
+		       "\n", LIRC_DRIVER_NAME);
 | 
						|
+		if (parport_claim_or_block(ppdevice) < 0) {
 | 
						|
+			printk(KERN_NOTICE "%s: could not claim port, giving"
 | 
						|
+			       " up\n", LIRC_DRIVER_NAME);
 | 
						|
+			return 0;
 | 
						|
+		}
 | 
						|
+	}
 | 
						|
+	out(LIRC_LP_CONTROL, LP_PSELECP|LP_PINITP);
 | 
						|
+	is_claimed = 1;
 | 
						|
+	return 1;
 | 
						|
+}
 | 
						|
+
 | 
						|
+/*** interrupt handler ***/
 | 
						|
+
 | 
						|
+static void rbuf_write(int signal)
 | 
						|
+{
 | 
						|
+	unsigned int nwptr;
 | 
						|
+
 | 
						|
+	nwptr = (wptr + 1) & (RBUF_SIZE - 1);
 | 
						|
+	if (nwptr == rptr) {
 | 
						|
+		/* no new signals will be accepted */
 | 
						|
+		lost_irqs++;
 | 
						|
+		printk(KERN_NOTICE "%s: buffer overrun\n", LIRC_DRIVER_NAME);
 | 
						|
+		return;
 | 
						|
+	}
 | 
						|
+	rbuf[wptr] = signal;
 | 
						|
+	wptr = nwptr;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void irq_handler(void *blah)
 | 
						|
+{
 | 
						|
+	struct timeval tv;
 | 
						|
+	static struct timeval lasttv;
 | 
						|
+	static int init;
 | 
						|
+	long signal;
 | 
						|
+	int data;
 | 
						|
+	unsigned int level, newlevel;
 | 
						|
+	unsigned int timeout;
 | 
						|
+
 | 
						|
+	if (!module_refcount(THIS_MODULE))
 | 
						|
+		return;
 | 
						|
+
 | 
						|
+	if (!is_claimed)
 | 
						|
+		return;
 | 
						|
+
 | 
						|
+#if 0
 | 
						|
+	/* disable interrupt */
 | 
						|
+	  disable_irq(irq);
 | 
						|
+	  out(LIRC_PORT_IRQ, in(LIRC_PORT_IRQ) & (~LP_PINTEN));
 | 
						|
+#endif
 | 
						|
+	if (check_pselecd && (in(1) & LP_PSELECD))
 | 
						|
+		return;
 | 
						|
+
 | 
						|
+#ifdef LIRC_TIMER
 | 
						|
+	if (init) {
 | 
						|
+		do_gettimeofday(&tv);
 | 
						|
+
 | 
						|
+		signal = tv.tv_sec - lasttv.tv_sec;
 | 
						|
+		if (signal > 15)
 | 
						|
+			/* really long time */
 | 
						|
+			data = PULSE_MASK;
 | 
						|
+		else
 | 
						|
+			data = (int) (signal*1000000 +
 | 
						|
+					 tv.tv_usec - lasttv.tv_usec +
 | 
						|
+					 LIRC_SFH506_DELAY);
 | 
						|
+
 | 
						|
+		rbuf_write(data); /* space */
 | 
						|
+	} else {
 | 
						|
+		if (timer == 0) {
 | 
						|
+			/*
 | 
						|
+			 * wake up; we'll lose this signal, but it will be
 | 
						|
+			 * garbage if the device is turned on anyway
 | 
						|
+			 */
 | 
						|
+			timer = init_lirc_timer();
 | 
						|
+			/* enable_irq(irq); */
 | 
						|
+			return;
 | 
						|
+		}
 | 
						|
+		init = 1;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	timeout = timer/10;	/* timeout after 1/10 sec. */
 | 
						|
+	signal = 1;
 | 
						|
+	level = lirc_get_timer();
 | 
						|
+	do {
 | 
						|
+		newlevel = lirc_get_timer();
 | 
						|
+		if (level == 0 && newlevel != 0)
 | 
						|
+			signal++;
 | 
						|
+		level = newlevel;
 | 
						|
+
 | 
						|
+		/* giving up */
 | 
						|
+		if (signal > timeout
 | 
						|
+		    || (check_pselecd && (in(1) & LP_PSELECD))) {
 | 
						|
+			signal = 0;
 | 
						|
+			printk(KERN_NOTICE "%s: timeout\n", LIRC_DRIVER_NAME);
 | 
						|
+			break;
 | 
						|
+		}
 | 
						|
+	} while (lirc_get_signal());
 | 
						|
+
 | 
						|
+	if (signal != 0) {
 | 
						|
+		/* ajust value to usecs */
 | 
						|
+		unsigned long long helper;
 | 
						|
+
 | 
						|
+		helper = ((unsigned long long) signal)*1000000;
 | 
						|
+		do_div(helper, timer);
 | 
						|
+		signal = (long) helper;
 | 
						|
+
 | 
						|
+		if (signal > LIRC_SFH506_DELAY)
 | 
						|
+			data = signal - LIRC_SFH506_DELAY;
 | 
						|
+		else
 | 
						|
+			data = 1;
 | 
						|
+		rbuf_write(PULSE_BIT|data); /* pulse */
 | 
						|
+	}
 | 
						|
+	do_gettimeofday(&lasttv);
 | 
						|
+#else
 | 
						|
+	/* add your code here */
 | 
						|
+#endif
 | 
						|
+
 | 
						|
+	wake_up_interruptible(&lirc_wait);
 | 
						|
+
 | 
						|
+	/* enable interrupt */
 | 
						|
+	/*
 | 
						|
+	  enable_irq(irq);
 | 
						|
+	  out(LIRC_PORT_IRQ, in(LIRC_PORT_IRQ)|LP_PINTEN);
 | 
						|
+	*/
 | 
						|
+}
 | 
						|
+
 | 
						|
+/*** file operations ***/
 | 
						|
+
 | 
						|
+static loff_t lirc_lseek(struct file *filep, loff_t offset, int orig)
 | 
						|
+{
 | 
						|
+	return -ESPIPE;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static ssize_t lirc_read(struct file *filep, char *buf, size_t n, loff_t *ppos)
 | 
						|
+{
 | 
						|
+	int result = 0;
 | 
						|
+	int count = 0;
 | 
						|
+	DECLARE_WAITQUEUE(wait, current);
 | 
						|
+
 | 
						|
+	if (n % sizeof(int))
 | 
						|
+		return -EINVAL;
 | 
						|
+
 | 
						|
+	add_wait_queue(&lirc_wait, &wait);
 | 
						|
+	set_current_state(TASK_INTERRUPTIBLE);
 | 
						|
+	while (count < n) {
 | 
						|
+		if (rptr != wptr) {
 | 
						|
+			if (copy_to_user(buf+count, (char *) &rbuf[rptr],
 | 
						|
+					 sizeof(int))) {
 | 
						|
+				result = -EFAULT;
 | 
						|
+				break;
 | 
						|
+			}
 | 
						|
+			rptr = (rptr + 1) & (RBUF_SIZE - 1);
 | 
						|
+			count += sizeof(int);
 | 
						|
+		} else {
 | 
						|
+			if (filep->f_flags & O_NONBLOCK) {
 | 
						|
+				result = -EAGAIN;
 | 
						|
+				break;
 | 
						|
+			}
 | 
						|
+			if (signal_pending(current)) {
 | 
						|
+				result = -ERESTARTSYS;
 | 
						|
+				break;
 | 
						|
+			}
 | 
						|
+			schedule();
 | 
						|
+			set_current_state(TASK_INTERRUPTIBLE);
 | 
						|
+		}
 | 
						|
+	}
 | 
						|
+	remove_wait_queue(&lirc_wait, &wait);
 | 
						|
+	set_current_state(TASK_RUNNING);
 | 
						|
+	return count ? count : result;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static ssize_t lirc_write(struct file *filep, const char *buf, size_t n,
 | 
						|
+			  loff_t *ppos)
 | 
						|
+{
 | 
						|
+	int count;
 | 
						|
+	unsigned int i;
 | 
						|
+	unsigned int level, newlevel;
 | 
						|
+	unsigned long flags;
 | 
						|
+	int counttimer;
 | 
						|
+	int *wbuf;
 | 
						|
+
 | 
						|
+	if (!is_claimed)
 | 
						|
+		return -EBUSY;
 | 
						|
+
 | 
						|
+	count = n / sizeof(int);
 | 
						|
+
 | 
						|
+	if (n % sizeof(int) || count % 2 == 0)
 | 
						|
+		return -EINVAL;
 | 
						|
+
 | 
						|
+	wbuf = memdup_user(buf, n);
 | 
						|
+	if (IS_ERR(wbuf))
 | 
						|
+		return PTR_ERR(wbuf);
 | 
						|
+
 | 
						|
+#ifdef LIRC_TIMER
 | 
						|
+	if (timer == 0) {
 | 
						|
+		/* try again if device is ready */
 | 
						|
+		timer = init_lirc_timer();
 | 
						|
+		if (timer == 0)
 | 
						|
+			return -EIO;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	/* adjust values from usecs */
 | 
						|
+	for (i = 0; i < count; i++) {
 | 
						|
+		unsigned long long helper;
 | 
						|
+
 | 
						|
+		helper = ((unsigned long long) wbuf[i])*timer;
 | 
						|
+		do_div(helper, 1000000);
 | 
						|
+		wbuf[i] = (int) helper;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	local_irq_save(flags);
 | 
						|
+	i = 0;
 | 
						|
+	while (i < count) {
 | 
						|
+		level = lirc_get_timer();
 | 
						|
+		counttimer = 0;
 | 
						|
+		lirc_on();
 | 
						|
+		do {
 | 
						|
+			newlevel = lirc_get_timer();
 | 
						|
+			if (level == 0 && newlevel != 0)
 | 
						|
+				counttimer++;
 | 
						|
+			level = newlevel;
 | 
						|
+			if (check_pselecd && (in(1) & LP_PSELECD)) {
 | 
						|
+				lirc_off();
 | 
						|
+				local_irq_restore(flags);
 | 
						|
+				return -EIO;
 | 
						|
+			}
 | 
						|
+		} while (counttimer < wbuf[i]);
 | 
						|
+		i++;
 | 
						|
+
 | 
						|
+		lirc_off();
 | 
						|
+		if (i == count)
 | 
						|
+			break;
 | 
						|
+		counttimer = 0;
 | 
						|
+		do {
 | 
						|
+			newlevel = lirc_get_timer();
 | 
						|
+			if (level == 0 && newlevel != 0)
 | 
						|
+				counttimer++;
 | 
						|
+			level = newlevel;
 | 
						|
+			if (check_pselecd && (in(1) & LP_PSELECD)) {
 | 
						|
+				local_irq_restore(flags);
 | 
						|
+				return -EIO;
 | 
						|
+			}
 | 
						|
+		} while (counttimer < wbuf[i]);
 | 
						|
+		i++;
 | 
						|
+	}
 | 
						|
+	local_irq_restore(flags);
 | 
						|
+#else
 | 
						|
+	/* place code that handles write without external timer here */
 | 
						|
+#endif
 | 
						|
+	return n;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static unsigned int lirc_poll(struct file *file, poll_table *wait)
 | 
						|
+{
 | 
						|
+	poll_wait(file, &lirc_wait, wait);
 | 
						|
+	if (rptr != wptr)
 | 
						|
+		return POLLIN | POLLRDNORM;
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
 | 
						|
+{
 | 
						|
+	int result;
 | 
						|
+	unsigned long features = LIRC_CAN_SET_TRANSMITTER_MASK |
 | 
						|
+				 LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2;
 | 
						|
+	unsigned long mode;
 | 
						|
+	unsigned int ivalue;
 | 
						|
+
 | 
						|
+	switch (cmd) {
 | 
						|
+	case LIRC_GET_FEATURES:
 | 
						|
+		result = put_user(features, (unsigned long *) arg);
 | 
						|
+		if (result)
 | 
						|
+			return result;
 | 
						|
+		break;
 | 
						|
+	case LIRC_GET_SEND_MODE:
 | 
						|
+		result = put_user(LIRC_MODE_PULSE, (unsigned long *) arg);
 | 
						|
+		if (result)
 | 
						|
+			return result;
 | 
						|
+		break;
 | 
						|
+	case LIRC_GET_REC_MODE:
 | 
						|
+		result = put_user(LIRC_MODE_MODE2, (unsigned long *) arg);
 | 
						|
+		if (result)
 | 
						|
+			return result;
 | 
						|
+		break;
 | 
						|
+	case LIRC_SET_SEND_MODE:
 | 
						|
+		result = get_user(mode, (unsigned long *) arg);
 | 
						|
+		if (result)
 | 
						|
+			return result;
 | 
						|
+		if (mode != LIRC_MODE_PULSE)
 | 
						|
+			return -EINVAL;
 | 
						|
+		break;
 | 
						|
+	case LIRC_SET_REC_MODE:
 | 
						|
+		result = get_user(mode, (unsigned long *) arg);
 | 
						|
+		if (result)
 | 
						|
+			return result;
 | 
						|
+		if (mode != LIRC_MODE_MODE2)
 | 
						|
+			return -ENOSYS;
 | 
						|
+		break;
 | 
						|
+	case LIRC_SET_TRANSMITTER_MASK:
 | 
						|
+		result = get_user(ivalue, (unsigned int *) arg);
 | 
						|
+		if (result)
 | 
						|
+			return result;
 | 
						|
+		if ((ivalue & LIRC_PARALLEL_TRANSMITTER_MASK) != ivalue)
 | 
						|
+			return LIRC_PARALLEL_MAX_TRANSMITTERS;
 | 
						|
+		tx_mask = ivalue;
 | 
						|
+		break;
 | 
						|
+	default:
 | 
						|
+		return -ENOIOCTLCMD;
 | 
						|
+	}
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int lirc_open(struct inode *node, struct file *filep)
 | 
						|
+{
 | 
						|
+	if (module_refcount(THIS_MODULE) || !lirc_claim())
 | 
						|
+		return -EBUSY;
 | 
						|
+
 | 
						|
+	parport_enable_irq(pport);
 | 
						|
+
 | 
						|
+	/* init read ptr */
 | 
						|
+	rptr = 0;
 | 
						|
+	wptr = 0;
 | 
						|
+	lost_irqs = 0;
 | 
						|
+
 | 
						|
+	is_open = 1;
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int lirc_close(struct inode *node, struct file *filep)
 | 
						|
+{
 | 
						|
+	if (is_claimed) {
 | 
						|
+		is_claimed = 0;
 | 
						|
+		parport_release(ppdevice);
 | 
						|
+	}
 | 
						|
+	is_open = 0;
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static struct file_operations lirc_fops = {
 | 
						|
+	.owner		= THIS_MODULE,
 | 
						|
+	.llseek		= lirc_lseek,
 | 
						|
+	.read		= lirc_read,
 | 
						|
+	.write		= lirc_write,
 | 
						|
+	.poll		= lirc_poll,
 | 
						|
+	.unlocked_ioctl	= lirc_ioctl,
 | 
						|
+	.open		= lirc_open,
 | 
						|
+	.release	= lirc_close
 | 
						|
+};
 | 
						|
+
 | 
						|
+static int set_use_inc(void *data)
 | 
						|
+{
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void set_use_dec(void *data)
 | 
						|
+{
 | 
						|
+}
 | 
						|
+
 | 
						|
+static struct lirc_driver driver = {
 | 
						|
+       .name		= LIRC_DRIVER_NAME,
 | 
						|
+       .minor		= -1,
 | 
						|
+       .code_length	= 1,
 | 
						|
+       .sample_rate	= 0,
 | 
						|
+       .data		= NULL,
 | 
						|
+       .add_to_buf	= NULL,
 | 
						|
+       .set_use_inc	= set_use_inc,
 | 
						|
+       .set_use_dec	= set_use_dec,
 | 
						|
+       .fops		= &lirc_fops,
 | 
						|
+       .dev		= NULL,
 | 
						|
+       .owner		= THIS_MODULE,
 | 
						|
+};
 | 
						|
+
 | 
						|
+static int pf(void *handle);
 | 
						|
+static void kf(void *handle);
 | 
						|
+
 | 
						|
+static struct timer_list poll_timer;
 | 
						|
+static void poll_state(unsigned long ignored);
 | 
						|
+
 | 
						|
+static void poll_state(unsigned long ignored)
 | 
						|
+{
 | 
						|
+	printk(KERN_NOTICE "%s: time\n",
 | 
						|
+	       LIRC_DRIVER_NAME);
 | 
						|
+	del_timer(&poll_timer);
 | 
						|
+	if (is_claimed)
 | 
						|
+		return;
 | 
						|
+	kf(NULL);
 | 
						|
+	if (!is_claimed) {
 | 
						|
+		printk(KERN_NOTICE "%s: could not claim port, giving up\n",
 | 
						|
+		       LIRC_DRIVER_NAME);
 | 
						|
+		init_timer(&poll_timer);
 | 
						|
+		poll_timer.expires = jiffies + HZ;
 | 
						|
+		poll_timer.data = (unsigned long)current;
 | 
						|
+		poll_timer.function = poll_state;
 | 
						|
+		add_timer(&poll_timer);
 | 
						|
+	}
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int pf(void *handle)
 | 
						|
+{
 | 
						|
+	parport_disable_irq(pport);
 | 
						|
+	is_claimed = 0;
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void kf(void *handle)
 | 
						|
+{
 | 
						|
+	if (!is_open)
 | 
						|
+		return;
 | 
						|
+	if (!lirc_claim())
 | 
						|
+		return;
 | 
						|
+	parport_enable_irq(pport);
 | 
						|
+	lirc_off();
 | 
						|
+	/* this is a bit annoying when you actually print...*/
 | 
						|
+	/*
 | 
						|
+	printk(KERN_INFO "%s: reclaimed port\n", LIRC_DRIVER_NAME);
 | 
						|
+	*/
 | 
						|
+}
 | 
						|
+
 | 
						|
+/*** module initialization and cleanup ***/
 | 
						|
+
 | 
						|
+static int __init lirc_parallel_init(void)
 | 
						|
+{
 | 
						|
+	pport = parport_find_base(io);
 | 
						|
+	if (pport == NULL) {
 | 
						|
+		printk(KERN_NOTICE "%s: no port at %x found\n",
 | 
						|
+		       LIRC_DRIVER_NAME, io);
 | 
						|
+		return -ENXIO;
 | 
						|
+	}
 | 
						|
+	ppdevice = parport_register_device(pport, LIRC_DRIVER_NAME,
 | 
						|
+					   pf, kf, irq_handler, 0, NULL);
 | 
						|
+	parport_put_port(pport);
 | 
						|
+	if (ppdevice == NULL) {
 | 
						|
+		printk(KERN_NOTICE "%s: parport_register_device() failed\n",
 | 
						|
+		       LIRC_DRIVER_NAME);
 | 
						|
+		return -ENXIO;
 | 
						|
+	}
 | 
						|
+	if (parport_claim(ppdevice) != 0)
 | 
						|
+		goto skip_init;
 | 
						|
+	is_claimed = 1;
 | 
						|
+	out(LIRC_LP_CONTROL, LP_PSELECP|LP_PINITP);
 | 
						|
+
 | 
						|
+#ifdef LIRC_TIMER
 | 
						|
+	if (debug)
 | 
						|
+		out(LIRC_PORT_DATA, tx_mask);
 | 
						|
+
 | 
						|
+	timer = init_lirc_timer();
 | 
						|
+
 | 
						|
+#if 0	/* continue even if device is offline */
 | 
						|
+	if (timer == 0) {
 | 
						|
+		is_claimed = 0;
 | 
						|
+		parport_release(pport);
 | 
						|
+		parport_unregister_device(ppdevice);
 | 
						|
+		return -EIO;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+#endif
 | 
						|
+	if (debug)
 | 
						|
+		out(LIRC_PORT_DATA, 0);
 | 
						|
+#endif
 | 
						|
+
 | 
						|
+	is_claimed = 0;
 | 
						|
+	parport_release(ppdevice);
 | 
						|
+ skip_init:
 | 
						|
+	driver.minor = lirc_register_driver(&driver);
 | 
						|
+	if (driver.minor < 0) {
 | 
						|
+		printk(KERN_NOTICE "%s: register_chrdev() failed\n",
 | 
						|
+		       LIRC_DRIVER_NAME);
 | 
						|
+		parport_unregister_device(ppdevice);
 | 
						|
+		return -EIO;
 | 
						|
+	}
 | 
						|
+	printk(KERN_INFO "%s: installed using port 0x%04x irq %d\n",
 | 
						|
+	       LIRC_DRIVER_NAME, io, irq);
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void __exit lirc_parallel_exit(void)
 | 
						|
+{
 | 
						|
+	parport_unregister_device(ppdevice);
 | 
						|
+	lirc_unregister_driver(driver.minor);
 | 
						|
+}
 | 
						|
+
 | 
						|
+module_init(lirc_parallel_init);
 | 
						|
+module_exit(lirc_parallel_exit);
 | 
						|
+
 | 
						|
+MODULE_DESCRIPTION("Infrared receiver driver for parallel ports.");
 | 
						|
+MODULE_AUTHOR("Christoph Bartelmus");
 | 
						|
+MODULE_LICENSE("GPL");
 | 
						|
+
 | 
						|
+module_param(io, int, S_IRUGO);
 | 
						|
+MODULE_PARM_DESC(io, "I/O address base (0x3bc, 0x378 or 0x278)");
 | 
						|
+
 | 
						|
+module_param(irq, int, S_IRUGO);
 | 
						|
+MODULE_PARM_DESC(irq, "Interrupt (7 or 5)");
 | 
						|
+
 | 
						|
+module_param(tx_mask, int, S_IRUGO);
 | 
						|
+MODULE_PARM_DESC(tx_maxk, "Transmitter mask (default: 0x01)");
 | 
						|
+
 | 
						|
+module_param(debug, bool, S_IRUGO | S_IWUSR);
 | 
						|
+MODULE_PARM_DESC(debug, "Enable debugging messages");
 | 
						|
+
 | 
						|
+module_param(check_pselecd, bool, S_IRUGO | S_IWUSR);
 | 
						|
+MODULE_PARM_DESC(debug, "Check for printer (default: 0)");
 | 
						|
diff --git a/drivers/staging/lirc/lirc_parallel.h b/drivers/staging/lirc/lirc_parallel.h
 | 
						|
new file mode 100644
 | 
						|
index 0000000..4bed6af
 | 
						|
--- /dev/null
 | 
						|
+++ b/drivers/staging/lirc/lirc_parallel.h
 | 
						|
@@ -0,0 +1,26 @@
 | 
						|
+/* lirc_parallel.h */
 | 
						|
+
 | 
						|
+#ifndef _LIRC_PARALLEL_H
 | 
						|
+#define _LIRC_PARALLEL_H
 | 
						|
+
 | 
						|
+#include <linux/lp.h>
 | 
						|
+
 | 
						|
+#define LIRC_PORT_LEN 3
 | 
						|
+
 | 
						|
+#define LIRC_LP_BASE    0
 | 
						|
+#define LIRC_LP_STATUS  1
 | 
						|
+#define LIRC_LP_CONTROL 2
 | 
						|
+
 | 
						|
+#define LIRC_PORT_DATA           LIRC_LP_BASE    /* base */
 | 
						|
+#define LIRC_PORT_TIMER        LIRC_LP_STATUS    /* status port */
 | 
						|
+#define LIRC_PORT_TIMER_BIT          LP_PBUSY    /* busy signal */
 | 
						|
+#define LIRC_PORT_SIGNAL       LIRC_LP_STATUS    /* status port */
 | 
						|
+#define LIRC_PORT_SIGNAL_BIT          LP_PACK    /* ack signal */
 | 
						|
+#define LIRC_PORT_IRQ         LIRC_LP_CONTROL    /* control port */
 | 
						|
+
 | 
						|
+#define LIRC_SFH506_DELAY 0             /* delay t_phl in usecs */
 | 
						|
+
 | 
						|
+#define LIRC_PARALLEL_MAX_TRANSMITTERS 8
 | 
						|
+#define LIRC_PARALLEL_TRANSMITTER_MASK ((1<<LIRC_PARALLEL_MAX_TRANSMITTERS) - 1)
 | 
						|
+
 | 
						|
+#endif
 | 
						|
diff --git a/drivers/staging/lirc/lirc_sasem.c b/drivers/staging/lirc/lirc_sasem.c
 | 
						|
new file mode 100644
 | 
						|
index 0000000..9e516a1
 | 
						|
--- /dev/null
 | 
						|
+++ b/drivers/staging/lirc/lirc_sasem.c
 | 
						|
@@ -0,0 +1,933 @@
 | 
						|
+/*
 | 
						|
+ * lirc_sasem.c - USB remote support for LIRC
 | 
						|
+ * Version 0.5
 | 
						|
+ *
 | 
						|
+ * Copyright (C) 2004-2005 Oliver Stabel <oliver.stabel@gmx.de>
 | 
						|
+ *			 Tim Davies <tim@opensystems.net.au>
 | 
						|
+ *
 | 
						|
+ * This driver was derived from:
 | 
						|
+ *   Venky Raju <dev@venky.ws>
 | 
						|
+ *      "lirc_imon - "LIRC/VFD driver for Ahanix/Soundgraph IMON IR/VFD"
 | 
						|
+ *   Paul Miller <pmiller9@users.sourceforge.net>'s 2003-2004
 | 
						|
+ *      "lirc_atiusb - USB remote support for LIRC"
 | 
						|
+ *   Culver Consulting Services <henry@culcon.com>'s 2003
 | 
						|
+ *      "Sasem OnAir VFD/IR USB driver"
 | 
						|
+ *
 | 
						|
+ *
 | 
						|
+ * NOTE - The LCDproc iMon driver should work with this module.  More info at
 | 
						|
+ *	http://www.frogstorm.info/sasem
 | 
						|
+ */
 | 
						|
+
 | 
						|
+/*
 | 
						|
+ *  This program is free software; you can redistribute it and/or modify
 | 
						|
+ *  it under the terms of the GNU General Public License as published by
 | 
						|
+ *  the Free Software Foundation; either version 2 of the License, or
 | 
						|
+ *  (at your option) any later version.
 | 
						|
+ *
 | 
						|
+ *  This program is distributed in the hope that it will be useful,
 | 
						|
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
						|
+ *  GNU General Public License for more details.
 | 
						|
+ *
 | 
						|
+ *  You should have received a copy of the GNU General Public License
 | 
						|
+ *  along with this program; if not, write to the Free Software
 | 
						|
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | 
						|
+ */
 | 
						|
+
 | 
						|
+#include <linux/errno.h>
 | 
						|
+#include <linux/init.h>
 | 
						|
+#include <linux/kernel.h>
 | 
						|
+#include <linux/module.h>
 | 
						|
+#include <linux/slab.h>
 | 
						|
+#include <linux/uaccess.h>
 | 
						|
+#include <linux/usb.h>
 | 
						|
+
 | 
						|
+#include <media/lirc.h>
 | 
						|
+#include <media/lirc_dev.h>
 | 
						|
+
 | 
						|
+
 | 
						|
+#define MOD_AUTHOR	"Oliver Stabel <oliver.stabel@gmx.de>, " \
 | 
						|
+			"Tim Davies <tim@opensystems.net.au>"
 | 
						|
+#define MOD_DESC	"USB Driver for Sasem Remote Controller V1.1"
 | 
						|
+#define MOD_NAME	"lirc_sasem"
 | 
						|
+#define MOD_VERSION	"0.5"
 | 
						|
+
 | 
						|
+#define VFD_MINOR_BASE	144	/* Same as LCD */
 | 
						|
+#define DEVICE_NAME	"lcd%d"
 | 
						|
+
 | 
						|
+#define BUF_CHUNK_SIZE	8
 | 
						|
+#define BUF_SIZE	128
 | 
						|
+
 | 
						|
+#define IOCTL_LCD_CONTRAST 1
 | 
						|
+
 | 
						|
+/*** P R O T O T Y P E S ***/
 | 
						|
+
 | 
						|
+/* USB Callback prototypes */
 | 
						|
+static int sasem_probe(struct usb_interface *interface,
 | 
						|
+			const struct usb_device_id *id);
 | 
						|
+static void sasem_disconnect(struct usb_interface *interface);
 | 
						|
+static void usb_rx_callback(struct urb *urb);
 | 
						|
+static void usb_tx_callback(struct urb *urb);
 | 
						|
+
 | 
						|
+/* VFD file_operations function prototypes */
 | 
						|
+static int vfd_open(struct inode *inode, struct file *file);
 | 
						|
+static long vfd_ioctl(struct file *file, unsigned cmd, unsigned long arg);
 | 
						|
+static int vfd_close(struct inode *inode, struct file *file);
 | 
						|
+static ssize_t vfd_write(struct file *file, const char *buf,
 | 
						|
+				size_t n_bytes, loff_t *pos);
 | 
						|
+
 | 
						|
+/* LIRC driver function prototypes */
 | 
						|
+static int ir_open(void *data);
 | 
						|
+static void ir_close(void *data);
 | 
						|
+
 | 
						|
+/* Driver init/exit prototypes */
 | 
						|
+static int __init sasem_init(void);
 | 
						|
+static void __exit sasem_exit(void);
 | 
						|
+
 | 
						|
+/*** G L O B A L S ***/
 | 
						|
+#define SASEM_DATA_BUF_SZ	32
 | 
						|
+
 | 
						|
+struct sasem_context {
 | 
						|
+
 | 
						|
+	struct usb_device *dev;
 | 
						|
+	int vfd_isopen;			/* VFD port has been opened       */
 | 
						|
+	unsigned int vfd_contrast;	/* VFD contrast		   */
 | 
						|
+	int ir_isopen;			/* IR port has been opened	*/
 | 
						|
+	int dev_present;		/* USB device presence	    */
 | 
						|
+	struct mutex ctx_lock;		/* to lock this object	    */
 | 
						|
+	wait_queue_head_t remove_ok;	/* For unexpected USB disconnects */
 | 
						|
+
 | 
						|
+	struct lirc_driver *driver;
 | 
						|
+	struct usb_endpoint_descriptor *rx_endpoint;
 | 
						|
+	struct usb_endpoint_descriptor *tx_endpoint;
 | 
						|
+	struct urb *rx_urb;
 | 
						|
+	struct urb *tx_urb;
 | 
						|
+	unsigned char usb_rx_buf[8];
 | 
						|
+	unsigned char usb_tx_buf[8];
 | 
						|
+
 | 
						|
+	struct tx_t {
 | 
						|
+		unsigned char data_buf[SASEM_DATA_BUF_SZ]; /* user data buffer */
 | 
						|
+		struct completion finished;  /* wait for write to finish  */
 | 
						|
+		atomic_t busy;		     /* write in progress	 */
 | 
						|
+		int status;		     /* status of tx completion   */
 | 
						|
+	} tx;
 | 
						|
+
 | 
						|
+	/* for dealing with repeat codes (wish there was a toggle bit!) */
 | 
						|
+	struct timeval presstime;
 | 
						|
+	char lastcode[8];
 | 
						|
+	int codesaved;
 | 
						|
+};
 | 
						|
+
 | 
						|
+/* VFD file operations */
 | 
						|
+static struct file_operations vfd_fops = {
 | 
						|
+	.owner		= THIS_MODULE,
 | 
						|
+	.open		= &vfd_open,
 | 
						|
+	.write		= &vfd_write,
 | 
						|
+	.unlocked_ioctl	= &vfd_ioctl,
 | 
						|
+	.release	= &vfd_close,
 | 
						|
+};
 | 
						|
+
 | 
						|
+/* USB Device ID for Sasem USB Control Board */
 | 
						|
+static struct usb_device_id sasem_usb_id_table[] = {
 | 
						|
+	/* Sasem USB Control Board */
 | 
						|
+	{ USB_DEVICE(0x11ba, 0x0101) },
 | 
						|
+	/* Terminating entry */
 | 
						|
+	{}
 | 
						|
+};
 | 
						|
+
 | 
						|
+/* USB Device data */
 | 
						|
+static struct usb_driver sasem_driver = {
 | 
						|
+	.name		= MOD_NAME,
 | 
						|
+	.probe		= sasem_probe,
 | 
						|
+	.disconnect	= sasem_disconnect,
 | 
						|
+	.id_table	= sasem_usb_id_table,
 | 
						|
+};
 | 
						|
+
 | 
						|
+static struct usb_class_driver sasem_class = {
 | 
						|
+	.name		= DEVICE_NAME,
 | 
						|
+	.fops		= &vfd_fops,
 | 
						|
+	.minor_base	= VFD_MINOR_BASE,
 | 
						|
+};
 | 
						|
+
 | 
						|
+/* to prevent races between open() and disconnect() */
 | 
						|
+static DEFINE_MUTEX(disconnect_lock);
 | 
						|
+
 | 
						|
+static int debug;
 | 
						|
+
 | 
						|
+
 | 
						|
+/*** M O D U L E   C O D E ***/
 | 
						|
+
 | 
						|
+MODULE_AUTHOR(MOD_AUTHOR);
 | 
						|
+MODULE_DESCRIPTION(MOD_DESC);
 | 
						|
+MODULE_LICENSE("GPL");
 | 
						|
+module_param(debug, int, S_IRUGO | S_IWUSR);
 | 
						|
+MODULE_PARM_DESC(debug, "Debug messages: 0=no, 1=yes (default: no)");
 | 
						|
+
 | 
						|
+static void delete_context(struct sasem_context *context)
 | 
						|
+{
 | 
						|
+	usb_free_urb(context->tx_urb);  /* VFD */
 | 
						|
+	usb_free_urb(context->rx_urb);  /* IR */
 | 
						|
+	lirc_buffer_free(context->driver->rbuf);
 | 
						|
+	kfree(context->driver->rbuf);
 | 
						|
+	kfree(context->driver);
 | 
						|
+	kfree(context);
 | 
						|
+
 | 
						|
+	if (debug)
 | 
						|
+		printk(KERN_INFO "%s: context deleted\n", __func__);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void deregister_from_lirc(struct sasem_context *context)
 | 
						|
+{
 | 
						|
+	int retval;
 | 
						|
+	int minor = context->driver->minor;
 | 
						|
+
 | 
						|
+	retval = lirc_unregister_driver(minor);
 | 
						|
+	if (retval)
 | 
						|
+		err("%s: unable to deregister from lirc (%d)",
 | 
						|
+			__func__, retval);
 | 
						|
+	else
 | 
						|
+		printk(KERN_INFO "Deregistered Sasem driver (minor:%d)\n",
 | 
						|
+		       minor);
 | 
						|
+
 | 
						|
+}
 | 
						|
+
 | 
						|
+/**
 | 
						|
+ * Called when the VFD device (e.g. /dev/usb/lcd)
 | 
						|
+ * is opened by the application.
 | 
						|
+ */
 | 
						|
+static int vfd_open(struct inode *inode, struct file *file)
 | 
						|
+{
 | 
						|
+	struct usb_interface *interface;
 | 
						|
+	struct sasem_context *context = NULL;
 | 
						|
+	int subminor;
 | 
						|
+	int retval = 0;
 | 
						|
+
 | 
						|
+	/* prevent races with disconnect */
 | 
						|
+	mutex_lock(&disconnect_lock);
 | 
						|
+
 | 
						|
+	subminor = iminor(inode);
 | 
						|
+	interface = usb_find_interface(&sasem_driver, subminor);
 | 
						|
+	if (!interface) {
 | 
						|
+		err("%s: could not find interface for minor %d",
 | 
						|
+		    __func__, subminor);
 | 
						|
+		retval = -ENODEV;
 | 
						|
+		goto exit;
 | 
						|
+	}
 | 
						|
+	context = usb_get_intfdata(interface);
 | 
						|
+
 | 
						|
+	if (!context) {
 | 
						|
+		err("%s: no context found for minor %d",
 | 
						|
+					__func__, subminor);
 | 
						|
+		retval = -ENODEV;
 | 
						|
+		goto exit;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	mutex_lock(&context->ctx_lock);
 | 
						|
+
 | 
						|
+	if (context->vfd_isopen) {
 | 
						|
+		err("%s: VFD port is already open", __func__);
 | 
						|
+		retval = -EBUSY;
 | 
						|
+	} else {
 | 
						|
+		context->vfd_isopen = 1;
 | 
						|
+		file->private_data = context;
 | 
						|
+		printk(KERN_INFO "VFD port opened\n");
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	mutex_unlock(&context->ctx_lock);
 | 
						|
+
 | 
						|
+exit:
 | 
						|
+	mutex_unlock(&disconnect_lock);
 | 
						|
+	return retval;
 | 
						|
+}
 | 
						|
+
 | 
						|
+/**
 | 
						|
+ * Called when the VFD device (e.g. /dev/usb/lcd)
 | 
						|
+ * is closed by the application.
 | 
						|
+ */
 | 
						|
+static long vfd_ioctl(struct file *file, unsigned cmd, unsigned long arg)
 | 
						|
+{
 | 
						|
+	struct sasem_context *context = NULL;
 | 
						|
+
 | 
						|
+	context = (struct sasem_context *) file->private_data;
 | 
						|
+
 | 
						|
+	if (!context) {
 | 
						|
+		err("%s: no context for device", __func__);
 | 
						|
+		return -ENODEV;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	mutex_lock(&context->ctx_lock);
 | 
						|
+
 | 
						|
+	switch (cmd) {
 | 
						|
+	case IOCTL_LCD_CONTRAST:
 | 
						|
+		if (arg > 1000)
 | 
						|
+			arg = 1000;
 | 
						|
+		context->vfd_contrast = (unsigned int)arg;
 | 
						|
+		break;
 | 
						|
+	default:
 | 
						|
+		printk(KERN_INFO "Unknown IOCTL command\n");
 | 
						|
+		mutex_unlock(&context->ctx_lock);
 | 
						|
+		return -ENOIOCTLCMD;  /* not supported */
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	mutex_unlock(&context->ctx_lock);
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+/**
 | 
						|
+ * Called when the VFD device (e.g. /dev/usb/lcd)
 | 
						|
+ * is closed by the application.
 | 
						|
+ */
 | 
						|
+static int vfd_close(struct inode *inode, struct file *file)
 | 
						|
+{
 | 
						|
+	struct sasem_context *context = NULL;
 | 
						|
+	int retval = 0;
 | 
						|
+
 | 
						|
+	context = (struct sasem_context *) file->private_data;
 | 
						|
+
 | 
						|
+	if (!context) {
 | 
						|
+		err("%s: no context for device", __func__);
 | 
						|
+		return -ENODEV;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	mutex_lock(&context->ctx_lock);
 | 
						|
+
 | 
						|
+	if (!context->vfd_isopen) {
 | 
						|
+		err("%s: VFD is not open", __func__);
 | 
						|
+		retval = -EIO;
 | 
						|
+	} else {
 | 
						|
+		context->vfd_isopen = 0;
 | 
						|
+		printk(KERN_INFO "VFD port closed\n");
 | 
						|
+		if (!context->dev_present && !context->ir_isopen) {
 | 
						|
+
 | 
						|
+			/* Device disconnected before close and IR port is
 | 
						|
+			 * not open. If IR port is open, context will be
 | 
						|
+			 * deleted by ir_close. */
 | 
						|
+			mutex_unlock(&context->ctx_lock);
 | 
						|
+			delete_context(context);
 | 
						|
+			return retval;
 | 
						|
+		}
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	mutex_unlock(&context->ctx_lock);
 | 
						|
+	return retval;
 | 
						|
+}
 | 
						|
+
 | 
						|
+/**
 | 
						|
+ * Sends a packet to the VFD.
 | 
						|
+ */
 | 
						|
+static int send_packet(struct sasem_context *context)
 | 
						|
+{
 | 
						|
+	unsigned int pipe;
 | 
						|
+	int interval = 0;
 | 
						|
+	int retval = 0;
 | 
						|
+
 | 
						|
+	pipe = usb_sndintpipe(context->dev,
 | 
						|
+			context->tx_endpoint->bEndpointAddress);
 | 
						|
+	interval = context->tx_endpoint->bInterval;
 | 
						|
+
 | 
						|
+	usb_fill_int_urb(context->tx_urb, context->dev, pipe,
 | 
						|
+		context->usb_tx_buf, sizeof(context->usb_tx_buf),
 | 
						|
+		usb_tx_callback, context, interval);
 | 
						|
+
 | 
						|
+	context->tx_urb->actual_length = 0;
 | 
						|
+
 | 
						|
+	init_completion(&context->tx.finished);
 | 
						|
+	atomic_set(&(context->tx.busy), 1);
 | 
						|
+
 | 
						|
+	retval =  usb_submit_urb(context->tx_urb, GFP_KERNEL);
 | 
						|
+	if (retval) {
 | 
						|
+		atomic_set(&(context->tx.busy), 0);
 | 
						|
+		err("%s: error submitting urb (%d)", __func__, retval);
 | 
						|
+	} else {
 | 
						|
+		/* Wait for transmission to complete (or abort) */
 | 
						|
+		mutex_unlock(&context->ctx_lock);
 | 
						|
+		wait_for_completion(&context->tx.finished);
 | 
						|
+		mutex_lock(&context->ctx_lock);
 | 
						|
+
 | 
						|
+		retval = context->tx.status;
 | 
						|
+		if (retval)
 | 
						|
+			err("%s: packet tx failed (%d)", __func__, retval);
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	return retval;
 | 
						|
+}
 | 
						|
+
 | 
						|
+/**
 | 
						|
+ * Writes data to the VFD.  The Sasem VFD is 2x16 characters
 | 
						|
+ * and requires data in 9 consecutive USB interrupt packets,
 | 
						|
+ * each packet carrying 8 bytes.
 | 
						|
+ */
 | 
						|
+static ssize_t vfd_write(struct file *file, const char *buf,
 | 
						|
+				size_t n_bytes, loff_t *pos)
 | 
						|
+{
 | 
						|
+	int i;
 | 
						|
+	int retval = 0;
 | 
						|
+	struct sasem_context *context;
 | 
						|
+	int *data_buf;
 | 
						|
+
 | 
						|
+	context = (struct sasem_context *) file->private_data;
 | 
						|
+	if (!context) {
 | 
						|
+		err("%s: no context for device", __func__);
 | 
						|
+		return -ENODEV;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	mutex_lock(&context->ctx_lock);
 | 
						|
+
 | 
						|
+	if (!context->dev_present) {
 | 
						|
+		err("%s: no Sasem device present", __func__);
 | 
						|
+		retval = -ENODEV;
 | 
						|
+		goto exit;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	if (n_bytes <= 0 || n_bytes > SASEM_DATA_BUF_SZ) {
 | 
						|
+		err("%s: invalid payload size", __func__);
 | 
						|
+		retval = -EINVAL;
 | 
						|
+		goto exit;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	data_buf = memdup_user(buf, n_bytes);
 | 
						|
+	if (PTR_ERR(data_buf))
 | 
						|
+		return PTR_ERR(data_buf);
 | 
						|
+
 | 
						|
+	memcpy(context->tx.data_buf, data_buf, n_bytes);
 | 
						|
+
 | 
						|
+	/* Pad with spaces */
 | 
						|
+	for (i = n_bytes; i < SASEM_DATA_BUF_SZ; ++i)
 | 
						|
+		context->tx.data_buf[i] = ' ';
 | 
						|
+
 | 
						|
+	/* Nine 8 byte packets to be sent */
 | 
						|
+	/* NOTE: "\x07\x01\0\0\0\0\0\0" or "\x0c\0\0\0\0\0\0\0"
 | 
						|
+	 *       will clear the VFD */
 | 
						|
+	for (i = 0; i < 9; i++) {
 | 
						|
+		switch (i) {
 | 
						|
+		case 0:
 | 
						|
+			memcpy(context->usb_tx_buf, "\x07\0\0\0\0\0\0\0", 8);
 | 
						|
+			context->usb_tx_buf[1] = (context->vfd_contrast) ?
 | 
						|
+				(0x2B - (context->vfd_contrast - 1) / 250)
 | 
						|
+				: 0x2B;
 | 
						|
+			break;
 | 
						|
+		case 1:
 | 
						|
+			memcpy(context->usb_tx_buf, "\x09\x01\0\0\0\0\0\0", 8);
 | 
						|
+			break;
 | 
						|
+		case 2:
 | 
						|
+			memcpy(context->usb_tx_buf, "\x0b\x01\0\0\0\0\0\0", 8);
 | 
						|
+			break;
 | 
						|
+		case 3:
 | 
						|
+			memcpy(context->usb_tx_buf, context->tx.data_buf, 8);
 | 
						|
+			break;
 | 
						|
+		case 4:
 | 
						|
+			memcpy(context->usb_tx_buf,
 | 
						|
+			       context->tx.data_buf + 8, 8);
 | 
						|
+			break;
 | 
						|
+		case 5:
 | 
						|
+			memcpy(context->usb_tx_buf, "\x09\x01\0\0\0\0\0\0", 8);
 | 
						|
+			break;
 | 
						|
+		case 6:
 | 
						|
+			memcpy(context->usb_tx_buf, "\x0b\x02\0\0\0\0\0\0", 8);
 | 
						|
+			break;
 | 
						|
+		case 7:
 | 
						|
+			memcpy(context->usb_tx_buf,
 | 
						|
+			       context->tx.data_buf + 16, 8);
 | 
						|
+			break;
 | 
						|
+		case 8:
 | 
						|
+			memcpy(context->usb_tx_buf,
 | 
						|
+			       context->tx.data_buf + 24, 8);
 | 
						|
+			break;
 | 
						|
+		}
 | 
						|
+		retval = send_packet(context);
 | 
						|
+		if (retval) {
 | 
						|
+
 | 
						|
+			err("%s: send packet failed for packet #%d",
 | 
						|
+					__func__, i);
 | 
						|
+			goto exit;
 | 
						|
+		}
 | 
						|
+	}
 | 
						|
+exit:
 | 
						|
+
 | 
						|
+	mutex_unlock(&context->ctx_lock);
 | 
						|
+
 | 
						|
+	return (!retval) ? n_bytes : retval;
 | 
						|
+}
 | 
						|
+
 | 
						|
+/**
 | 
						|
+ * Callback function for USB core API: transmit data
 | 
						|
+ */
 | 
						|
+static void usb_tx_callback(struct urb *urb)
 | 
						|
+{
 | 
						|
+	struct sasem_context *context;
 | 
						|
+
 | 
						|
+	if (!urb)
 | 
						|
+		return;
 | 
						|
+	context = (struct sasem_context *) urb->context;
 | 
						|
+	if (!context)
 | 
						|
+		return;
 | 
						|
+
 | 
						|
+	context->tx.status = urb->status;
 | 
						|
+
 | 
						|
+	/* notify waiters that write has finished */
 | 
						|
+	atomic_set(&context->tx.busy, 0);
 | 
						|
+	complete(&context->tx.finished);
 | 
						|
+
 | 
						|
+	return;
 | 
						|
+}
 | 
						|
+
 | 
						|
+/**
 | 
						|
+ * Called by lirc_dev when the application opens /dev/lirc
 | 
						|
+ */
 | 
						|
+static int ir_open(void *data)
 | 
						|
+{
 | 
						|
+	int retval = 0;
 | 
						|
+	struct sasem_context *context;
 | 
						|
+
 | 
						|
+	/* prevent races with disconnect */
 | 
						|
+	mutex_lock(&disconnect_lock);
 | 
						|
+
 | 
						|
+	context = (struct sasem_context *) data;
 | 
						|
+
 | 
						|
+	mutex_lock(&context->ctx_lock);
 | 
						|
+
 | 
						|
+	if (context->ir_isopen) {
 | 
						|
+		err("%s: IR port is already open", __func__);
 | 
						|
+		retval = -EBUSY;
 | 
						|
+		goto exit;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	usb_fill_int_urb(context->rx_urb, context->dev,
 | 
						|
+		usb_rcvintpipe(context->dev,
 | 
						|
+				context->rx_endpoint->bEndpointAddress),
 | 
						|
+		context->usb_rx_buf, sizeof(context->usb_rx_buf),
 | 
						|
+		usb_rx_callback, context, context->rx_endpoint->bInterval);
 | 
						|
+
 | 
						|
+	retval = usb_submit_urb(context->rx_urb, GFP_KERNEL);
 | 
						|
+
 | 
						|
+	if (retval)
 | 
						|
+		err("%s: usb_submit_urb failed for ir_open (%d)",
 | 
						|
+		    __func__, retval);
 | 
						|
+	else {
 | 
						|
+		context->ir_isopen = 1;
 | 
						|
+		printk(KERN_INFO "IR port opened\n");
 | 
						|
+	}
 | 
						|
+
 | 
						|
+exit:
 | 
						|
+	mutex_unlock(&context->ctx_lock);
 | 
						|
+
 | 
						|
+	mutex_unlock(&disconnect_lock);
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+/**
 | 
						|
+ * Called by lirc_dev when the application closes /dev/lirc
 | 
						|
+ */
 | 
						|
+static void ir_close(void *data)
 | 
						|
+{
 | 
						|
+	struct sasem_context *context;
 | 
						|
+
 | 
						|
+	context = (struct sasem_context *)data;
 | 
						|
+	if (!context) {
 | 
						|
+		err("%s: no context for device", __func__);
 | 
						|
+		return;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	mutex_lock(&context->ctx_lock);
 | 
						|
+
 | 
						|
+	usb_kill_urb(context->rx_urb);
 | 
						|
+	context->ir_isopen = 0;
 | 
						|
+	printk(KERN_INFO "IR port closed\n");
 | 
						|
+
 | 
						|
+	if (!context->dev_present) {
 | 
						|
+
 | 
						|
+		/*
 | 
						|
+		 * Device disconnected while IR port was
 | 
						|
+		 * still open. Driver was not deregistered
 | 
						|
+		 * at disconnect time, so do it now.
 | 
						|
+		 */
 | 
						|
+		deregister_from_lirc(context);
 | 
						|
+
 | 
						|
+		if (!context->vfd_isopen) {
 | 
						|
+
 | 
						|
+			mutex_unlock(&context->ctx_lock);
 | 
						|
+			delete_context(context);
 | 
						|
+			return;
 | 
						|
+		}
 | 
						|
+		/* If VFD port is open, context will be deleted by vfd_close */
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	mutex_unlock(&context->ctx_lock);
 | 
						|
+	return;
 | 
						|
+}
 | 
						|
+
 | 
						|
+/**
 | 
						|
+ * Process the incoming packet
 | 
						|
+ */
 | 
						|
+static void incoming_packet(struct sasem_context *context,
 | 
						|
+				   struct urb *urb)
 | 
						|
+{
 | 
						|
+	int len = urb->actual_length;
 | 
						|
+	unsigned char *buf = urb->transfer_buffer;
 | 
						|
+	long ms;
 | 
						|
+	struct timeval tv;
 | 
						|
+
 | 
						|
+	if (len != 8) {
 | 
						|
+		printk(KERN_WARNING "%s: invalid incoming packet size (%d)\n",
 | 
						|
+		     __func__, len);
 | 
						|
+		return;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+#ifdef DEBUG
 | 
						|
+	int i;
 | 
						|
+	for (i = 0; i < 8; ++i)
 | 
						|
+		printk(KERN_INFO "%02x ", buf[i]);
 | 
						|
+	printk(KERN_INFO "\n");
 | 
						|
+#endif
 | 
						|
+
 | 
						|
+	/*
 | 
						|
+	 * Lirc could deal with the repeat code, but we really need to block it
 | 
						|
+	 * if it arrives too late.  Otherwise we could repeat the wrong code.
 | 
						|
+	 */
 | 
						|
+
 | 
						|
+	/* get the time since the last button press */
 | 
						|
+	do_gettimeofday(&tv);
 | 
						|
+	ms = (tv.tv_sec - context->presstime.tv_sec) * 1000 +
 | 
						|
+	     (tv.tv_usec - context->presstime.tv_usec) / 1000;
 | 
						|
+
 | 
						|
+	if (memcmp(buf, "\x08\0\0\0\0\0\0\0", 8) == 0) {
 | 
						|
+		/*
 | 
						|
+		 * the repeat code is being sent, so we copy
 | 
						|
+		 * the old code to LIRC
 | 
						|
+		 */
 | 
						|
+
 | 
						|
+		/*
 | 
						|
+		 * NOTE: Only if the last code was less than 250ms ago
 | 
						|
+		 * - no one should be able to push another (undetected) button
 | 
						|
+		 *   in that time and then get a false repeat of the previous
 | 
						|
+		 *   press but it is long enough for a genuine repeat
 | 
						|
+		 */
 | 
						|
+		if ((ms < 250) && (context->codesaved != 0)) {
 | 
						|
+			memcpy(buf, &context->lastcode, 8);
 | 
						|
+			context->presstime.tv_sec = tv.tv_sec;
 | 
						|
+			context->presstime.tv_usec = tv.tv_usec;
 | 
						|
+		}
 | 
						|
+	} else {
 | 
						|
+		/* save the current valid code for repeats */
 | 
						|
+		memcpy(&context->lastcode, buf, 8);
 | 
						|
+		/*
 | 
						|
+		 * set flag to signal a valid code was save;
 | 
						|
+		 * just for safety reasons
 | 
						|
+		 */
 | 
						|
+		context->codesaved = 1;
 | 
						|
+		context->presstime.tv_sec = tv.tv_sec;
 | 
						|
+		context->presstime.tv_usec = tv.tv_usec;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	lirc_buffer_write(context->driver->rbuf, buf);
 | 
						|
+	wake_up(&context->driver->rbuf->wait_poll);
 | 
						|
+}
 | 
						|
+
 | 
						|
+/**
 | 
						|
+ * Callback function for USB core API: receive data
 | 
						|
+ */
 | 
						|
+static void usb_rx_callback(struct urb *urb)
 | 
						|
+{
 | 
						|
+	struct sasem_context *context;
 | 
						|
+
 | 
						|
+	if (!urb)
 | 
						|
+		return;
 | 
						|
+	context = (struct sasem_context *) urb->context;
 | 
						|
+	if (!context)
 | 
						|
+		return;
 | 
						|
+
 | 
						|
+	switch (urb->status) {
 | 
						|
+
 | 
						|
+	case -ENOENT:		/* usbcore unlink successful! */
 | 
						|
+		return;
 | 
						|
+
 | 
						|
+	case 0:
 | 
						|
+		if (context->ir_isopen)
 | 
						|
+			incoming_packet(context, urb);
 | 
						|
+		break;
 | 
						|
+
 | 
						|
+	default:
 | 
						|
+		printk(KERN_WARNING "%s: status (%d): ignored",
 | 
						|
+			 __func__, urb->status);
 | 
						|
+		break;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	usb_submit_urb(context->rx_urb, GFP_ATOMIC);
 | 
						|
+	return;
 | 
						|
+}
 | 
						|
+
 | 
						|
+
 | 
						|
+
 | 
						|
+/**
 | 
						|
+ * Callback function for USB core API: Probe
 | 
						|
+ */
 | 
						|
+static int sasem_probe(struct usb_interface *interface,
 | 
						|
+			const struct usb_device_id *id)
 | 
						|
+{
 | 
						|
+	struct usb_device *dev = NULL;
 | 
						|
+	struct usb_host_interface *iface_desc = NULL;
 | 
						|
+	struct usb_endpoint_descriptor *rx_endpoint = NULL;
 | 
						|
+	struct usb_endpoint_descriptor *tx_endpoint = NULL;
 | 
						|
+	struct urb *rx_urb = NULL;
 | 
						|
+	struct urb *tx_urb = NULL;
 | 
						|
+	struct lirc_driver *driver = NULL;
 | 
						|
+	struct lirc_buffer *rbuf = NULL;
 | 
						|
+	int lirc_minor = 0;
 | 
						|
+	int num_endpoints;
 | 
						|
+	int retval = 0;
 | 
						|
+	int vfd_ep_found;
 | 
						|
+	int ir_ep_found;
 | 
						|
+	int alloc_status;
 | 
						|
+	struct sasem_context *context = NULL;
 | 
						|
+	int i;
 | 
						|
+
 | 
						|
+	printk(KERN_INFO "%s: found Sasem device\n", __func__);
 | 
						|
+
 | 
						|
+
 | 
						|
+	dev = usb_get_dev(interface_to_usbdev(interface));
 | 
						|
+	iface_desc = interface->cur_altsetting;
 | 
						|
+	num_endpoints = iface_desc->desc.bNumEndpoints;
 | 
						|
+
 | 
						|
+	/*
 | 
						|
+	 * Scan the endpoint list and set:
 | 
						|
+	 *	first input endpoint = IR endpoint
 | 
						|
+	 *	first output endpoint = VFD endpoint
 | 
						|
+	 */
 | 
						|
+
 | 
						|
+	ir_ep_found = 0;
 | 
						|
+	vfd_ep_found = 0;
 | 
						|
+
 | 
						|
+	for (i = 0; i < num_endpoints && !(ir_ep_found && vfd_ep_found); ++i) {
 | 
						|
+
 | 
						|
+		struct usb_endpoint_descriptor *ep;
 | 
						|
+		int ep_dir;
 | 
						|
+		int ep_type;
 | 
						|
+		ep = &iface_desc->endpoint [i].desc;
 | 
						|
+		ep_dir = ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK;
 | 
						|
+		ep_type = ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
 | 
						|
+
 | 
						|
+		if (!ir_ep_found &&
 | 
						|
+			ep_dir == USB_DIR_IN &&
 | 
						|
+			ep_type == USB_ENDPOINT_XFER_INT) {
 | 
						|
+
 | 
						|
+			rx_endpoint = ep;
 | 
						|
+			ir_ep_found = 1;
 | 
						|
+			if (debug)
 | 
						|
+				printk(KERN_INFO "%s: found IR endpoint\n",
 | 
						|
+				       __func__);
 | 
						|
+
 | 
						|
+		} else if (!vfd_ep_found &&
 | 
						|
+			ep_dir == USB_DIR_OUT &&
 | 
						|
+			ep_type == USB_ENDPOINT_XFER_INT) {
 | 
						|
+
 | 
						|
+			tx_endpoint = ep;
 | 
						|
+			vfd_ep_found = 1;
 | 
						|
+			if (debug)
 | 
						|
+				printk(KERN_INFO "%s: found VFD endpoint\n",
 | 
						|
+				       __func__);
 | 
						|
+		}
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	/* Input endpoint is mandatory */
 | 
						|
+	if (!ir_ep_found) {
 | 
						|
+
 | 
						|
+		err("%s: no valid input (IR) endpoint found.", __func__);
 | 
						|
+		retval = -ENODEV;
 | 
						|
+		goto exit;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	if (!vfd_ep_found)
 | 
						|
+		printk(KERN_INFO "%s: no valid output (VFD) endpoint found.\n",
 | 
						|
+		       __func__);
 | 
						|
+
 | 
						|
+
 | 
						|
+	/* Allocate memory */
 | 
						|
+	alloc_status = 0;
 | 
						|
+
 | 
						|
+	context = kzalloc(sizeof(struct sasem_context), GFP_KERNEL);
 | 
						|
+	if (!context) {
 | 
						|
+		err("%s: kzalloc failed for context", __func__);
 | 
						|
+		alloc_status = 1;
 | 
						|
+		goto alloc_status_switch;
 | 
						|
+	}
 | 
						|
+	driver = kzalloc(sizeof(struct lirc_driver), GFP_KERNEL);
 | 
						|
+	if (!driver) {
 | 
						|
+		err("%s: kzalloc failed for lirc_driver", __func__);
 | 
						|
+		alloc_status = 2;
 | 
						|
+		goto alloc_status_switch;
 | 
						|
+	}
 | 
						|
+	rbuf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
 | 
						|
+	if (!rbuf) {
 | 
						|
+		err("%s: kmalloc failed for lirc_buffer", __func__);
 | 
						|
+		alloc_status = 3;
 | 
						|
+		goto alloc_status_switch;
 | 
						|
+	}
 | 
						|
+	if (lirc_buffer_init(rbuf, BUF_CHUNK_SIZE, BUF_SIZE)) {
 | 
						|
+		err("%s: lirc_buffer_init failed", __func__);
 | 
						|
+		alloc_status = 4;
 | 
						|
+		goto alloc_status_switch;
 | 
						|
+	}
 | 
						|
+	rx_urb = usb_alloc_urb(0, GFP_KERNEL);
 | 
						|
+	if (!rx_urb) {
 | 
						|
+		err("%s: usb_alloc_urb failed for IR urb", __func__);
 | 
						|
+		alloc_status = 5;
 | 
						|
+		goto alloc_status_switch;
 | 
						|
+	}
 | 
						|
+	if (vfd_ep_found) {
 | 
						|
+		tx_urb = usb_alloc_urb(0, GFP_KERNEL);
 | 
						|
+		if (!tx_urb) {
 | 
						|
+			err("%s: usb_alloc_urb failed for VFD urb",
 | 
						|
+			    __func__);
 | 
						|
+			alloc_status = 6;
 | 
						|
+			goto alloc_status_switch;
 | 
						|
+		}
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	mutex_init(&context->ctx_lock);
 | 
						|
+
 | 
						|
+	strcpy(driver->name, MOD_NAME);
 | 
						|
+	driver->minor = -1;
 | 
						|
+	driver->code_length = 64;
 | 
						|
+	driver->sample_rate = 0;
 | 
						|
+	driver->features = LIRC_CAN_REC_LIRCCODE;
 | 
						|
+	driver->data = context;
 | 
						|
+	driver->rbuf = rbuf;
 | 
						|
+	driver->set_use_inc = ir_open;
 | 
						|
+	driver->set_use_dec = ir_close;
 | 
						|
+	driver->dev   = &interface->dev;
 | 
						|
+	driver->owner = THIS_MODULE;
 | 
						|
+
 | 
						|
+	mutex_lock(&context->ctx_lock);
 | 
						|
+
 | 
						|
+	lirc_minor = lirc_register_driver(driver);
 | 
						|
+	if (lirc_minor < 0) {
 | 
						|
+		err("%s: lirc_register_driver failed", __func__);
 | 
						|
+		alloc_status = 7;
 | 
						|
+		mutex_unlock(&context->ctx_lock);
 | 
						|
+	} else
 | 
						|
+		printk(KERN_INFO "%s: Registered Sasem driver (minor:%d)\n",
 | 
						|
+			__func__, lirc_minor);
 | 
						|
+
 | 
						|
+alloc_status_switch:
 | 
						|
+
 | 
						|
+	switch (alloc_status) {
 | 
						|
+
 | 
						|
+	case 7:
 | 
						|
+		if (vfd_ep_found)
 | 
						|
+			usb_free_urb(tx_urb);
 | 
						|
+	case 6:
 | 
						|
+		usb_free_urb(rx_urb);
 | 
						|
+	case 5:
 | 
						|
+		lirc_buffer_free(rbuf);
 | 
						|
+	case 4:
 | 
						|
+		kfree(rbuf);
 | 
						|
+	case 3:
 | 
						|
+		kfree(driver);
 | 
						|
+	case 2:
 | 
						|
+		kfree(context);
 | 
						|
+		context = NULL;
 | 
						|
+	case 1:
 | 
						|
+		retval = -ENOMEM;
 | 
						|
+		goto exit;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	/* Needed while unregistering! */
 | 
						|
+	driver->minor = lirc_minor;
 | 
						|
+
 | 
						|
+	context->dev = dev;
 | 
						|
+	context->dev_present = 1;
 | 
						|
+	context->rx_endpoint = rx_endpoint;
 | 
						|
+	context->rx_urb = rx_urb;
 | 
						|
+	if (vfd_ep_found) {
 | 
						|
+		context->tx_endpoint = tx_endpoint;
 | 
						|
+		context->tx_urb = tx_urb;
 | 
						|
+		context->vfd_contrast = 1000;   /* range 0 - 1000 */
 | 
						|
+	}
 | 
						|
+	context->driver = driver;
 | 
						|
+
 | 
						|
+	usb_set_intfdata(interface, context);
 | 
						|
+
 | 
						|
+	if (vfd_ep_found) {
 | 
						|
+
 | 
						|
+		if (debug)
 | 
						|
+			printk(KERN_INFO "Registering VFD with sysfs\n");
 | 
						|
+		if (usb_register_dev(interface, &sasem_class))
 | 
						|
+			/* Not a fatal error, so ignore */
 | 
						|
+			printk(KERN_INFO "%s: could not get a minor number "
 | 
						|
+			       "for VFD\n", __func__);
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	printk(KERN_INFO "%s: Sasem device on usb<%d:%d> initialized\n",
 | 
						|
+			__func__, dev->bus->busnum, dev->devnum);
 | 
						|
+
 | 
						|
+	mutex_unlock(&context->ctx_lock);
 | 
						|
+exit:
 | 
						|
+	return retval;
 | 
						|
+}
 | 
						|
+
 | 
						|
+/**
 | 
						|
+ * Callback function for USB core API: disonnect
 | 
						|
+ */
 | 
						|
+static void sasem_disconnect(struct usb_interface *interface)
 | 
						|
+{
 | 
						|
+	struct sasem_context *context;
 | 
						|
+
 | 
						|
+	/* prevent races with ir_open()/vfd_open() */
 | 
						|
+	mutex_lock(&disconnect_lock);
 | 
						|
+
 | 
						|
+	context = usb_get_intfdata(interface);
 | 
						|
+	mutex_lock(&context->ctx_lock);
 | 
						|
+
 | 
						|
+	printk(KERN_INFO "%s: Sasem device disconnected\n", __func__);
 | 
						|
+
 | 
						|
+	usb_set_intfdata(interface, NULL);
 | 
						|
+	context->dev_present = 0;
 | 
						|
+
 | 
						|
+	/* Stop reception */
 | 
						|
+	usb_kill_urb(context->rx_urb);
 | 
						|
+
 | 
						|
+	/* Abort ongoing write */
 | 
						|
+	if (atomic_read(&context->tx.busy)) {
 | 
						|
+
 | 
						|
+		usb_kill_urb(context->tx_urb);
 | 
						|
+		wait_for_completion(&context->tx.finished);
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	/* De-register from lirc_dev if IR port is not open */
 | 
						|
+	if (!context->ir_isopen)
 | 
						|
+		deregister_from_lirc(context);
 | 
						|
+
 | 
						|
+	usb_deregister_dev(interface, &sasem_class);
 | 
						|
+
 | 
						|
+	mutex_unlock(&context->ctx_lock);
 | 
						|
+
 | 
						|
+	if (!context->ir_isopen && !context->vfd_isopen)
 | 
						|
+		delete_context(context);
 | 
						|
+
 | 
						|
+	mutex_unlock(&disconnect_lock);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int __init sasem_init(void)
 | 
						|
+{
 | 
						|
+	int rc;
 | 
						|
+
 | 
						|
+	printk(KERN_INFO MOD_DESC ", v" MOD_VERSION "\n");
 | 
						|
+	printk(KERN_INFO MOD_AUTHOR "\n");
 | 
						|
+
 | 
						|
+	rc = usb_register(&sasem_driver);
 | 
						|
+	if (rc < 0) {
 | 
						|
+		err("%s: usb register failed (%d)", __func__, rc);
 | 
						|
+		return -ENODEV;
 | 
						|
+	}
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void __exit sasem_exit(void)
 | 
						|
+{
 | 
						|
+	usb_deregister(&sasem_driver);
 | 
						|
+	printk(KERN_INFO "module removed. Goodbye!\n");
 | 
						|
+}
 | 
						|
+
 | 
						|
+
 | 
						|
+module_init(sasem_init);
 | 
						|
+module_exit(sasem_exit);
 | 
						|
diff --git a/drivers/staging/lirc/lirc_serial.c b/drivers/staging/lirc/lirc_serial.c
 | 
						|
new file mode 100644
 | 
						|
index 0000000..d2ea3f0
 | 
						|
--- /dev/null
 | 
						|
+++ b/drivers/staging/lirc/lirc_serial.c
 | 
						|
@@ -0,0 +1,1313 @@
 | 
						|
+/*
 | 
						|
+ * lirc_serial.c
 | 
						|
+ *
 | 
						|
+ * lirc_serial - Device driver that records pulse- and pause-lengths
 | 
						|
+ *	       (space-lengths) between DDCD event on a serial port.
 | 
						|
+ *
 | 
						|
+ * Copyright (C) 1996,97 Ralph Metzler <rjkm@thp.uni-koeln.de>
 | 
						|
+ * Copyright (C) 1998 Trent Piepho <xyzzy@u.washington.edu>
 | 
						|
+ * Copyright (C) 1998 Ben Pfaff <blp@gnu.org>
 | 
						|
+ * Copyright (C) 1999 Christoph Bartelmus <lirc@bartelmus.de>
 | 
						|
+ * Copyright (C) 2007 Andrei Tanas <andrei@tanas.ca> (suspend/resume support)
 | 
						|
+ *  This program is free software; you can redistribute it and/or modify
 | 
						|
+ *  it under the terms of the GNU General Public License as published by
 | 
						|
+ *  the Free Software Foundation; either version 2 of the License, or
 | 
						|
+ *  (at your option) any later version.
 | 
						|
+ *
 | 
						|
+ *  This program is distributed in the hope that it will be useful,
 | 
						|
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
						|
+ *  GNU General Public License for more details.
 | 
						|
+ *
 | 
						|
+ *  You should have received a copy of the GNU General Public License
 | 
						|
+ *  along with this program; if not, write to the Free Software
 | 
						|
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | 
						|
+ *
 | 
						|
+ */
 | 
						|
+
 | 
						|
+/*
 | 
						|
+ * Steve's changes to improve transmission fidelity:
 | 
						|
+ *   - for systems with the rdtsc instruction and the clock counter, a
 | 
						|
+ *     send_pule that times the pulses directly using the counter.
 | 
						|
+ *     This means that the LIRC_SERIAL_TRANSMITTER_LATENCY fudge is
 | 
						|
+ *     not needed. Measurement shows very stable waveform, even where
 | 
						|
+ *     PCI activity slows the access to the UART, which trips up other
 | 
						|
+ *     versions.
 | 
						|
+ *   - For other system, non-integer-microsecond pulse/space lengths,
 | 
						|
+ *     done using fixed point binary. So, much more accurate carrier
 | 
						|
+ *     frequency.
 | 
						|
+ *   - fine tuned transmitter latency, taking advantage of fractional
 | 
						|
+ *     microseconds in previous change
 | 
						|
+ *   - Fixed bug in the way transmitter latency was accounted for by
 | 
						|
+ *     tuning the pulse lengths down - the send_pulse routine ignored
 | 
						|
+ *     this overhead as it timed the overall pulse length - so the
 | 
						|
+ *     pulse frequency was right but overall pulse length was too
 | 
						|
+ *     long. Fixed by accounting for latency on each pulse/space
 | 
						|
+ *     iteration.
 | 
						|
+ *
 | 
						|
+ * Steve Davies <steve@daviesfam.org>  July 2001
 | 
						|
+ */
 | 
						|
+
 | 
						|
+#include <linux/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/serial_reg.h>
 | 
						|
+#include <linux/time.h>
 | 
						|
+#include <linux/string.h>
 | 
						|
+#include <linux/types.h>
 | 
						|
+#include <linux/wait.h>
 | 
						|
+#include <linux/mm.h>
 | 
						|
+#include <linux/delay.h>
 | 
						|
+#include <linux/poll.h>
 | 
						|
+#include <linux/platform_device.h>
 | 
						|
+
 | 
						|
+#include <asm/system.h>
 | 
						|
+#include <linux/io.h>
 | 
						|
+#include <linux/irq.h>
 | 
						|
+#include <linux/fcntl.h>
 | 
						|
+#include <linux/spinlock.h>
 | 
						|
+
 | 
						|
+#ifdef CONFIG_LIRC_SERIAL_NSLU2
 | 
						|
+#include <asm/hardware.h>
 | 
						|
+#endif
 | 
						|
+/* From Intel IXP42X Developer's Manual (#252480-005): */
 | 
						|
+/* ftp://download.intel.com/design/network/manuals/25248005.pdf */
 | 
						|
+#define UART_IE_IXP42X_UUE   0x40 /* IXP42X UART Unit enable */
 | 
						|
+#define UART_IE_IXP42X_RTOIE 0x10 /* IXP42X Receiver Data Timeout int.enable */
 | 
						|
+
 | 
						|
+#include <media/lirc.h>
 | 
						|
+#include <media/lirc_dev.h>
 | 
						|
+
 | 
						|
+#define LIRC_DRIVER_NAME "lirc_serial"
 | 
						|
+
 | 
						|
+struct lirc_serial {
 | 
						|
+	int signal_pin;
 | 
						|
+	int signal_pin_change;
 | 
						|
+	u8 on;
 | 
						|
+	u8 off;
 | 
						|
+	long (*send_pulse)(unsigned long length);
 | 
						|
+	void (*send_space)(long length);
 | 
						|
+	int features;
 | 
						|
+	spinlock_t lock;
 | 
						|
+};
 | 
						|
+
 | 
						|
+#define LIRC_HOMEBREW		0
 | 
						|
+#define LIRC_IRDEO		1
 | 
						|
+#define LIRC_IRDEO_REMOTE	2
 | 
						|
+#define LIRC_ANIMAX		3
 | 
						|
+#define LIRC_IGOR		4
 | 
						|
+#define LIRC_NSLU2		5
 | 
						|
+
 | 
						|
+/*** module parameters ***/
 | 
						|
+static int type;
 | 
						|
+static int io;
 | 
						|
+static int irq;
 | 
						|
+static int iommap;
 | 
						|
+static int ioshift;
 | 
						|
+static int softcarrier = 1;
 | 
						|
+static int share_irq;
 | 
						|
+static int debug;
 | 
						|
+static int sense = -1;	/* -1 = auto, 0 = active high, 1 = active low */
 | 
						|
+static int txsense;	/* 0 = active high, 1 = active low */
 | 
						|
+
 | 
						|
+#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[] = {
 | 
						|
+	[LIRC_HOMEBREW] = {
 | 
						|
+		.signal_pin        = UART_MSR_DCD,
 | 
						|
+		.signal_pin_change = UART_MSR_DDCD,
 | 
						|
+		.on  = (UART_MCR_RTS | UART_MCR_OUT2 | UART_MCR_DTR),
 | 
						|
+		.off = (UART_MCR_RTS | UART_MCR_OUT2),
 | 
						|
+		.send_pulse = send_pulse_homebrew,
 | 
						|
+		.send_space = send_space_homebrew,
 | 
						|
+#ifdef CONFIG_LIRC_SERIAL_TRANSMITTER
 | 
						|
+		.features    = (LIRC_CAN_SET_SEND_DUTY_CYCLE |
 | 
						|
+				LIRC_CAN_SET_SEND_CARRIER |
 | 
						|
+				LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2)
 | 
						|
+#else
 | 
						|
+		.features    = LIRC_CAN_REC_MODE2
 | 
						|
+#endif
 | 
						|
+	},
 | 
						|
+
 | 
						|
+	[LIRC_IRDEO] = {
 | 
						|
+		.signal_pin        = UART_MSR_DSR,
 | 
						|
+		.signal_pin_change = UART_MSR_DDSR,
 | 
						|
+		.on  = UART_MCR_OUT2,
 | 
						|
+		.off = (UART_MCR_RTS | UART_MCR_DTR | UART_MCR_OUT2),
 | 
						|
+		.send_pulse  = send_pulse_irdeo,
 | 
						|
+		.send_space  = send_space_irdeo,
 | 
						|
+		.features    = (LIRC_CAN_SET_SEND_DUTY_CYCLE |
 | 
						|
+				LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2)
 | 
						|
+	},
 | 
						|
+
 | 
						|
+	[LIRC_IRDEO_REMOTE] = {
 | 
						|
+		.signal_pin        = UART_MSR_DSR,
 | 
						|
+		.signal_pin_change = UART_MSR_DDSR,
 | 
						|
+		.on  = (UART_MCR_RTS | UART_MCR_DTR | UART_MCR_OUT2),
 | 
						|
+		.off = (UART_MCR_RTS | UART_MCR_DTR | UART_MCR_OUT2),
 | 
						|
+		.send_pulse  = send_pulse_irdeo,
 | 
						|
+		.send_space  = send_space_irdeo,
 | 
						|
+		.features    = (LIRC_CAN_SET_SEND_DUTY_CYCLE |
 | 
						|
+				LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2)
 | 
						|
+	},
 | 
						|
+
 | 
						|
+	[LIRC_ANIMAX] = {
 | 
						|
+		.signal_pin        = UART_MSR_DCD,
 | 
						|
+		.signal_pin_change = UART_MSR_DDCD,
 | 
						|
+		.on  = 0,
 | 
						|
+		.off = (UART_MCR_RTS | UART_MCR_DTR | UART_MCR_OUT2),
 | 
						|
+		.send_pulse = NULL,
 | 
						|
+		.send_space = NULL,
 | 
						|
+		.features   = LIRC_CAN_REC_MODE2
 | 
						|
+	},
 | 
						|
+
 | 
						|
+	[LIRC_IGOR] = {
 | 
						|
+		.signal_pin        = UART_MSR_DSR,
 | 
						|
+		.signal_pin_change = UART_MSR_DDSR,
 | 
						|
+		.on  = (UART_MCR_RTS | UART_MCR_OUT2 | UART_MCR_DTR),
 | 
						|
+		.off = (UART_MCR_RTS | UART_MCR_OUT2),
 | 
						|
+		.send_pulse = send_pulse_homebrew,
 | 
						|
+		.send_space = send_space_homebrew,
 | 
						|
+#ifdef CONFIG_LIRC_SERIAL_TRANSMITTER
 | 
						|
+		.features    = (LIRC_CAN_SET_SEND_DUTY_CYCLE |
 | 
						|
+				LIRC_CAN_SET_SEND_CARRIER |
 | 
						|
+				LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2)
 | 
						|
+#else
 | 
						|
+		.features    = LIRC_CAN_REC_MODE2
 | 
						|
+#endif
 | 
						|
+	},
 | 
						|
+
 | 
						|
+#ifdef CONFIG_LIRC_SERIAL_NSLU2
 | 
						|
+	/*
 | 
						|
+	 * Modified Linksys Network Storage Link USB 2.0 (NSLU2):
 | 
						|
+	 * We receive on CTS of the 2nd serial port (R142,LHS), we
 | 
						|
+	 * transmit with a IR diode between GPIO[1] (green status LED),
 | 
						|
+	 * and ground (Matthias Goebl <matthias.goebl@goebl.net>).
 | 
						|
+	 * See also http://www.nslu2-linux.org for this device
 | 
						|
+	 */
 | 
						|
+	[LIRC_NSLU2] = {
 | 
						|
+		.signal_pin        = UART_MSR_CTS,
 | 
						|
+		.signal_pin_change = UART_MSR_DCTS,
 | 
						|
+		.on  = (UART_MCR_RTS | UART_MCR_OUT2 | UART_MCR_DTR),
 | 
						|
+		.off = (UART_MCR_RTS | UART_MCR_OUT2),
 | 
						|
+		.send_pulse = send_pulse_homebrew,
 | 
						|
+		.send_space = send_space_homebrew,
 | 
						|
+#ifdef CONFIG_LIRC_SERIAL_TRANSMITTER
 | 
						|
+		.features    = (LIRC_CAN_SET_SEND_DUTY_CYCLE |
 | 
						|
+				LIRC_CAN_SET_SEND_CARRIER |
 | 
						|
+				LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2)
 | 
						|
+#else
 | 
						|
+		.features    = LIRC_CAN_REC_MODE2
 | 
						|
+#endif
 | 
						|
+	},
 | 
						|
+#endif
 | 
						|
+
 | 
						|
+};
 | 
						|
+
 | 
						|
+#define RS_ISR_PASS_LIMIT 256
 | 
						|
+
 | 
						|
+/*
 | 
						|
+ * A long pulse code from a remote might take up to 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
 | 
						|
+
 | 
						|
+static struct timeval lasttv = {0, 0};
 | 
						|
+
 | 
						|
+static struct lirc_buffer rbuf;
 | 
						|
+
 | 
						|
+static unsigned int freq = 38000;
 | 
						|
+static unsigned int duty_cycle = 50;
 | 
						|
+
 | 
						|
+/* Initialized in init_timing_params() */
 | 
						|
+static unsigned long period;
 | 
						|
+static unsigned long pulse_width;
 | 
						|
+static unsigned long space_width;
 | 
						|
+
 | 
						|
+#if defined(__i386__)
 | 
						|
+/*
 | 
						|
+ * From:
 | 
						|
+ * Linux I/O port programming mini-HOWTO
 | 
						|
+ * Author: Riku Saikkonen <Riku.Saikkonen@hut.fi>
 | 
						|
+ * v, 28 December 1997
 | 
						|
+ *
 | 
						|
+ * [...]
 | 
						|
+ * Actually, a port I/O instruction on most ports in the 0-0x3ff range
 | 
						|
+ * takes almost exactly 1 microsecond, so if you're, for example, using
 | 
						|
+ * the parallel port directly, just do additional inb()s from that port
 | 
						|
+ * to delay.
 | 
						|
+ * [...]
 | 
						|
+ */
 | 
						|
+/* transmitter latency 1.5625us 0x1.90 - this figure arrived at from
 | 
						|
+ * comment above plus trimming to match actual measured frequency.
 | 
						|
+ * This will be sensitive to cpu speed, though hopefully most of the 1.5us
 | 
						|
+ * is spent in the uart access.  Still - for reference test machine was a
 | 
						|
+ * 1.13GHz Athlon system - Steve
 | 
						|
+ */
 | 
						|
+
 | 
						|
+/*
 | 
						|
+ * changed from 400 to 450 as this works better on slower machines;
 | 
						|
+ * faster machines will use the rdtsc code anyway
 | 
						|
+ */
 | 
						|
+#define LIRC_SERIAL_TRANSMITTER_LATENCY 450
 | 
						|
+
 | 
						|
+#else
 | 
						|
+
 | 
						|
+/* does anybody have information on other platforms ? */
 | 
						|
+/* 256 = 1<<8 */
 | 
						|
+#define LIRC_SERIAL_TRANSMITTER_LATENCY 256
 | 
						|
+
 | 
						|
+#endif  /* __i386__ */
 | 
						|
+/*
 | 
						|
+ * FIXME: should we be using hrtimers instead of this
 | 
						|
+ * LIRC_SERIAL_TRANSMITTER_LATENCY nonsense?
 | 
						|
+ */
 | 
						|
+
 | 
						|
+/* fetch serial input packet (1 byte) from register offset */
 | 
						|
+static u8 sinp(int offset)
 | 
						|
+{
 | 
						|
+	if (iommap != 0)
 | 
						|
+		/* the register is memory-mapped */
 | 
						|
+		offset <<= ioshift;
 | 
						|
+
 | 
						|
+	return inb(io + offset);
 | 
						|
+}
 | 
						|
+
 | 
						|
+/* write serial output packet (1 byte) of value to register offset */
 | 
						|
+static void soutp(int offset, u8 value)
 | 
						|
+{
 | 
						|
+	if (iommap != 0)
 | 
						|
+		/* the register is memory-mapped */
 | 
						|
+		offset <<= ioshift;
 | 
						|
+
 | 
						|
+	outb(value, io + offset);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void on(void)
 | 
						|
+{
 | 
						|
+#ifdef CONFIG_LIRC_SERIAL_NSLU2
 | 
						|
+	/*
 | 
						|
+	 * On NSLU2, we put the transmit diode between the output of the green
 | 
						|
+	 * status LED and ground
 | 
						|
+	 */
 | 
						|
+	if (type == LIRC_NSLU2) {
 | 
						|
+		gpio_line_set(NSLU2_LED_GRN, IXP4XX_GPIO_LOW);
 | 
						|
+		return;
 | 
						|
+	}
 | 
						|
+#endif
 | 
						|
+	if (txsense)
 | 
						|
+		soutp(UART_MCR, hardware[type].off);
 | 
						|
+	else
 | 
						|
+		soutp(UART_MCR, hardware[type].on);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void off(void)
 | 
						|
+{
 | 
						|
+#ifdef CONFIG_LIRC_SERIAL_NSLU2
 | 
						|
+	if (type == LIRC_NSLU2) {
 | 
						|
+		gpio_line_set(NSLU2_LED_GRN, IXP4XX_GPIO_HIGH);
 | 
						|
+		return;
 | 
						|
+	}
 | 
						|
+#endif
 | 
						|
+	if (txsense)
 | 
						|
+		soutp(UART_MCR, hardware[type].on);
 | 
						|
+	else
 | 
						|
+		soutp(UART_MCR, hardware[type].off);
 | 
						|
+}
 | 
						|
+
 | 
						|
+#ifndef MAX_UDELAY_MS
 | 
						|
+#define MAX_UDELAY_US 5000
 | 
						|
+#else
 | 
						|
+#define MAX_UDELAY_US (MAX_UDELAY_MS*1000)
 | 
						|
+#endif
 | 
						|
+
 | 
						|
+static void safe_udelay(unsigned long usecs)
 | 
						|
+{
 | 
						|
+	while (usecs > MAX_UDELAY_US) {
 | 
						|
+		udelay(MAX_UDELAY_US);
 | 
						|
+		usecs -= MAX_UDELAY_US;
 | 
						|
+	}
 | 
						|
+	udelay(usecs);
 | 
						|
+}
 | 
						|
+
 | 
						|
+#ifdef USE_RDTSC
 | 
						|
+/*
 | 
						|
+ * This is an overflow/precision juggle, complicated in that we can't
 | 
						|
+ * do long long divide in the kernel
 | 
						|
+ */
 | 
						|
+
 | 
						|
+/*
 | 
						|
+ * When we use the rdtsc instruction to measure clocks, we keep the
 | 
						|
+ * pulse and space widths as clock cycles.  As this is CPU speed
 | 
						|
+ * dependent, the widths must be calculated in init_port and ioctl
 | 
						|
+ * time
 | 
						|
+ */
 | 
						|
+
 | 
						|
+/* So send_pulse can quickly convert microseconds to clocks */
 | 
						|
+static unsigned long conv_us_to_clocks;
 | 
						|
+
 | 
						|
+static 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 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, ret;
 | 
						|
+	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)
 | 
						|
+		ret = (-rawbits) * 10000 / 1152;
 | 
						|
+	else
 | 
						|
+		ret = (3 - i) * 3 * 10000 / 1152 + (-rawbits) * 10000 / 1152;
 | 
						|
+
 | 
						|
+	return ret;
 | 
						|
+}
 | 
						|
+
 | 
						|
+#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 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;
 | 
						|
+	/*
 | 
						|
+	 * FIXME: This looks like a hard busy wait, without even an occasional,
 | 
						|
+	 * polite, cpu_relax() call.  There's got to be a better way?
 | 
						|
+	 *
 | 
						|
+	 * The i2c code has the result of a lot of bit-banging work, I wonder if
 | 
						|
+	 * there's something there which could be helpful here.
 | 
						|
+	 */
 | 
						|
+	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 long send_pulse_homebrew_softcarrier(unsigned long length)
 | 
						|
+{
 | 
						|
+	int flag;
 | 
						|
+	unsigned long actual, target, d;
 | 
						|
+	length <<= 8;
 | 
						|
+
 | 
						|
+	actual = 0; target = 0; flag = 0;
 | 
						|
+	while (actual < length) {
 | 
						|
+		if (flag) {
 | 
						|
+			off();
 | 
						|
+			target += space_width;
 | 
						|
+		} else {
 | 
						|
+			on();
 | 
						|
+			target += pulse_width;
 | 
						|
+		}
 | 
						|
+		d = (target - actual -
 | 
						|
+		     LIRC_SERIAL_TRANSMITTER_LATENCY + 128) >> 8;
 | 
						|
+		/*
 | 
						|
+		 * Note - we've checked in ioctl that the pulse/space
 | 
						|
+		 * widths are big enough so that d is > 0
 | 
						|
+		 */
 | 
						|
+		udelay(d);
 | 
						|
+		actual += (d << 8) + LIRC_SERIAL_TRANSMITTER_LATENCY;
 | 
						|
+		flag = !flag;
 | 
						|
+	}
 | 
						|
+	return (actual-length) >> 8;
 | 
						|
+}
 | 
						|
+#endif /* USE_RDTSC */
 | 
						|
+
 | 
						|
+static long send_pulse_homebrew(unsigned long length)
 | 
						|
+{
 | 
						|
+	if (length <= 0)
 | 
						|
+		return 0;
 | 
						|
+
 | 
						|
+	if (softcarrier)
 | 
						|
+		return send_pulse_homebrew_softcarrier(length);
 | 
						|
+	else {
 | 
						|
+		on();
 | 
						|
+		safe_udelay(length);
 | 
						|
+		return 0;
 | 
						|
+	}
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void send_space_irdeo(long length)
 | 
						|
+{
 | 
						|
+	if (length <= 0)
 | 
						|
+		return;
 | 
						|
+
 | 
						|
+	safe_udelay(length);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void send_space_homebrew(long length)
 | 
						|
+{
 | 
						|
+	off();
 | 
						|
+	if (length <= 0)
 | 
						|
+		return;
 | 
						|
+	safe_udelay(length);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void rbwrite(int l)
 | 
						|
+{
 | 
						|
+	if (lirc_buffer_full(&rbuf)) {
 | 
						|
+		/* no new signals will be accepted */
 | 
						|
+		dprintk("Buffer overrun\n");
 | 
						|
+		return;
 | 
						|
+	}
 | 
						|
+	lirc_buffer_write(&rbuf, (void *)&l);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void frbwrite(int l)
 | 
						|
+{
 | 
						|
+	/* simple noise filter */
 | 
						|
+	static int pulse, space;
 | 
						|
+	static unsigned int ptr;
 | 
						|
+
 | 
						|
+	if (ptr > 0 && (l & PULSE_BIT)) {
 | 
						|
+		pulse += l & PULSE_MASK;
 | 
						|
+		if (pulse > 250) {
 | 
						|
+			rbwrite(space);
 | 
						|
+			rbwrite(pulse | PULSE_BIT);
 | 
						|
+			ptr = 0;
 | 
						|
+			pulse = 0;
 | 
						|
+		}
 | 
						|
+		return;
 | 
						|
+	}
 | 
						|
+	if (!(l & PULSE_BIT)) {
 | 
						|
+		if (ptr == 0) {
 | 
						|
+			if (l > 20000) {
 | 
						|
+				space = l;
 | 
						|
+				ptr++;
 | 
						|
+				return;
 | 
						|
+			}
 | 
						|
+		} else {
 | 
						|
+			if (l > 20000) {
 | 
						|
+				space += pulse;
 | 
						|
+				if (space > PULSE_MASK)
 | 
						|
+					space = PULSE_MASK;
 | 
						|
+				space += l;
 | 
						|
+				if (space > PULSE_MASK)
 | 
						|
+					space = PULSE_MASK;
 | 
						|
+				pulse = 0;
 | 
						|
+				return;
 | 
						|
+			}
 | 
						|
+			rbwrite(space);
 | 
						|
+			rbwrite(pulse | PULSE_BIT);
 | 
						|
+			ptr = 0;
 | 
						|
+			pulse = 0;
 | 
						|
+		}
 | 
						|
+	}
 | 
						|
+	rbwrite(l);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static irqreturn_t irq_handler(int i, void *blah)
 | 
						|
+{
 | 
						|
+	struct timeval tv;
 | 
						|
+	int counter, dcd;
 | 
						|
+	u8 status;
 | 
						|
+	long deltv;
 | 
						|
+	int data;
 | 
						|
+	static int last_dcd = -1;
 | 
						|
+
 | 
						|
+	if ((sinp(UART_IIR) & UART_IIR_NO_INT)) {
 | 
						|
+		/* not our interrupt */
 | 
						|
+		return IRQ_NONE;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	counter = 0;
 | 
						|
+	do {
 | 
						|
+		counter++;
 | 
						|
+		status = sinp(UART_MSR);
 | 
						|
+		if (counter > RS_ISR_PASS_LIMIT) {
 | 
						|
+			printk(KERN_WARNING LIRC_DRIVER_NAME ": AIEEEE: "
 | 
						|
+			       "We're caught!\n");
 | 
						|
+			break;
 | 
						|
+		}
 | 
						|
+		if ((status & hardware[type].signal_pin_change)
 | 
						|
+		    && sense != -1) {
 | 
						|
+			/* get current time */
 | 
						|
+			do_gettimeofday(&tv);
 | 
						|
+
 | 
						|
+			/* New mode, written by Trent Piepho
 | 
						|
+			   <xyzzy@u.washington.edu>. */
 | 
						|
+
 | 
						|
+			/*
 | 
						|
+			 * The old format was not very portable.
 | 
						|
+			 * We now use an int to pass pulses
 | 
						|
+			 * and spaces to user space.
 | 
						|
+			 *
 | 
						|
+			 * If PULSE_BIT is set a pulse has been
 | 
						|
+			 * received, otherwise a space has been
 | 
						|
+			 * received.  The driver needs to know if your
 | 
						|
+			 * receiver is active high or active low, or
 | 
						|
+			 * the space/pulse sense could be
 | 
						|
+			 * inverted. The bits denoted by PULSE_MASK are
 | 
						|
+			 * the length in microseconds. Lengths greater
 | 
						|
+			 * than or equal to 16 seconds are clamped to
 | 
						|
+			 * PULSE_MASK.  All other bits are unused.
 | 
						|
+			 * This is a much simpler interface for user
 | 
						|
+			 * programs, as well as eliminating "out of
 | 
						|
+			 * phase" errors with space/pulse
 | 
						|
+			 * autodetection.
 | 
						|
+			 */
 | 
						|
+
 | 
						|
+			/* calc time since last interrupt in microseconds */
 | 
						|
+			dcd = (status & hardware[type].signal_pin) ? 1 : 0;
 | 
						|
+
 | 
						|
+			if (dcd == last_dcd) {
 | 
						|
+				printk(KERN_WARNING LIRC_DRIVER_NAME
 | 
						|
+				": ignoring spike: %d %d %lx %lx %lx %lx\n",
 | 
						|
+				dcd, sense,
 | 
						|
+				tv.tv_sec, lasttv.tv_sec,
 | 
						|
+				tv.tv_usec, lasttv.tv_usec);
 | 
						|
+				continue;
 | 
						|
+			}
 | 
						|
+
 | 
						|
+			deltv = tv.tv_sec-lasttv.tv_sec;
 | 
						|
+			if (tv.tv_sec < lasttv.tv_sec ||
 | 
						|
+			    (tv.tv_sec == lasttv.tv_sec &&
 | 
						|
+			     tv.tv_usec < lasttv.tv_usec)) {
 | 
						|
+				printk(KERN_WARNING LIRC_DRIVER_NAME
 | 
						|
+				       ": AIEEEE: your clock just jumped "
 | 
						|
+				       "backwards\n");
 | 
						|
+				printk(KERN_WARNING LIRC_DRIVER_NAME
 | 
						|
+				       ": %d %d %lx %lx %lx %lx\n",
 | 
						|
+				       dcd, sense,
 | 
						|
+				       tv.tv_sec, lasttv.tv_sec,
 | 
						|
+				       tv.tv_usec, lasttv.tv_usec);
 | 
						|
+				data = PULSE_MASK;
 | 
						|
+			} else if (deltv > 15) {
 | 
						|
+				data = PULSE_MASK; /* really long time */
 | 
						|
+				if (!(dcd^sense)) {
 | 
						|
+					/* sanity check */
 | 
						|
+					printk(KERN_WARNING LIRC_DRIVER_NAME
 | 
						|
+					       ": AIEEEE: "
 | 
						|
+					       "%d %d %lx %lx %lx %lx\n",
 | 
						|
+					       dcd, sense,
 | 
						|
+					       tv.tv_sec, lasttv.tv_sec,
 | 
						|
+					       tv.tv_usec, lasttv.tv_usec);
 | 
						|
+					/*
 | 
						|
+					 * detecting pulse while this
 | 
						|
+					 * MUST be a space!
 | 
						|
+					 */
 | 
						|
+					sense = sense ? 0 : 1;
 | 
						|
+				}
 | 
						|
+			} else
 | 
						|
+				data = (int) (deltv*1000000 +
 | 
						|
+					       tv.tv_usec -
 | 
						|
+					       lasttv.tv_usec);
 | 
						|
+			frbwrite(dcd^sense ? data : (data|PULSE_BIT));
 | 
						|
+			lasttv = tv;
 | 
						|
+			last_dcd = dcd;
 | 
						|
+			wake_up_interruptible(&rbuf.wait_poll);
 | 
						|
+		}
 | 
						|
+	} while (!(sinp(UART_IIR) & UART_IIR_NO_INT)); /* still pending ? */
 | 
						|
+	return IRQ_HANDLED;
 | 
						|
+}
 | 
						|
+
 | 
						|
+
 | 
						|
+static int hardware_init_port(void)
 | 
						|
+{
 | 
						|
+	u8 scratch, scratch2, scratch3;
 | 
						|
+
 | 
						|
+	/*
 | 
						|
+	 * This is a simple port existence test, borrowed from the autoconfig
 | 
						|
+	 * function in drivers/serial/8250.c
 | 
						|
+	 */
 | 
						|
+	scratch = sinp(UART_IER);
 | 
						|
+	soutp(UART_IER, 0);
 | 
						|
+#ifdef __i386__
 | 
						|
+	outb(0xff, 0x080);
 | 
						|
+#endif
 | 
						|
+	scratch2 = sinp(UART_IER) & 0x0f;
 | 
						|
+	soutp(UART_IER, 0x0f);
 | 
						|
+#ifdef __i386__
 | 
						|
+	outb(0x00, 0x080);
 | 
						|
+#endif
 | 
						|
+	scratch3 = sinp(UART_IER) & 0x0f;
 | 
						|
+	soutp(UART_IER, scratch);
 | 
						|
+	if (scratch2 != 0 || scratch3 != 0x0f) {
 | 
						|
+		/* we fail, there's nothing here */
 | 
						|
+		printk(KERN_ERR LIRC_DRIVER_NAME ": port existence test "
 | 
						|
+		       "failed, cannot continue\n");
 | 
						|
+		return -EINVAL;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+
 | 
						|
+
 | 
						|
+	/* 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);
 | 
						|
+
 | 
						|
+#ifdef CONFIG_LIRC_SERIAL_NSLU2
 | 
						|
+	if (type == LIRC_NSLU2) {
 | 
						|
+		/* Setup NSLU2 UART */
 | 
						|
+
 | 
						|
+		/* Enable UART */
 | 
						|
+		soutp(UART_IER, sinp(UART_IER) | UART_IE_IXP42X_UUE);
 | 
						|
+		/* Disable Receiver data Time out interrupt */
 | 
						|
+		soutp(UART_IER, sinp(UART_IER) & ~UART_IE_IXP42X_RTOIE);
 | 
						|
+		/* set out2 = interrupt unmask; off() doesn't set MCR
 | 
						|
+		   on NSLU2 */
 | 
						|
+		soutp(UART_MCR, UART_MCR_RTS|UART_MCR_OUT2);
 | 
						|
+	}
 | 
						|
+#endif
 | 
						|
+
 | 
						|
+	/* Set line for power source */
 | 
						|
+	off();
 | 
						|
+
 | 
						|
+	/* Clear registers again to be sure. */
 | 
						|
+	sinp(UART_LSR);
 | 
						|
+	sinp(UART_RX);
 | 
						|
+	sinp(UART_IIR);
 | 
						|
+	sinp(UART_MSR);
 | 
						|
+
 | 
						|
+	switch (type) {
 | 
						|
+	case LIRC_IRDEO:
 | 
						|
+	case LIRC_IRDEO_REMOTE:
 | 
						|
+		/* setup port to 7N1 @ 115200 Baud */
 | 
						|
+		/* 7N1+start = 9 bits at 115200 ~ 3 bits at 38kHz */
 | 
						|
+
 | 
						|
+		/* Set DLAB 1. */
 | 
						|
+		soutp(UART_LCR, sinp(UART_LCR) | UART_LCR_DLAB);
 | 
						|
+		/* Set divisor to 1 => 115200 Baud */
 | 
						|
+		soutp(UART_DLM, 0);
 | 
						|
+		soutp(UART_DLL, 1);
 | 
						|
+		/* Set DLAB 0 +  7N1 */
 | 
						|
+		soutp(UART_LCR, UART_LCR_WLEN7);
 | 
						|
+		/* THR interrupt already disabled at this point */
 | 
						|
+		break;
 | 
						|
+	default:
 | 
						|
+		break;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int init_port(void)
 | 
						|
+{
 | 
						|
+	int i, nlow, nhigh;
 | 
						|
+
 | 
						|
+	/* Reserve io region. */
 | 
						|
+	/*
 | 
						|
+	 * Future MMAP-Developers: Attention!
 | 
						|
+	 * For memory mapped I/O you *might* need to use ioremap() first,
 | 
						|
+	 * for the NSLU2 it's done in boot code.
 | 
						|
+	 */
 | 
						|
+	if (((iommap != 0)
 | 
						|
+	     && (request_mem_region(iommap, 8 << ioshift,
 | 
						|
+				    LIRC_DRIVER_NAME) == NULL))
 | 
						|
+	   || ((iommap == 0)
 | 
						|
+	       && (request_region(io, 8, LIRC_DRIVER_NAME) == NULL))) {
 | 
						|
+		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;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	if (hardware_init_port() < 0)
 | 
						|
+		return -EINVAL;
 | 
						|
+
 | 
						|
+	/* Initialize pulse/space widths */
 | 
						|
+	init_timing_params(duty_cycle, freq);
 | 
						|
+
 | 
						|
+	/* If pin is high, then this must be an active low receiver. */
 | 
						|
+	if (sense == -1) {
 | 
						|
+		/* wait 1/2 sec for the power supply */
 | 
						|
+		msleep(500);
 | 
						|
+
 | 
						|
+		/*
 | 
						|
+		 * probe 9 times every 0.04s, collect "votes" for
 | 
						|
+		 * active high/low
 | 
						|
+		 */
 | 
						|
+		nlow = 0;
 | 
						|
+		nhigh = 0;
 | 
						|
+		for (i = 0; i < 9; i++) {
 | 
						|
+			if (sinp(UART_MSR) & hardware[type].signal_pin)
 | 
						|
+				nlow++;
 | 
						|
+			else
 | 
						|
+				nhigh++;
 | 
						|
+			msleep(40);
 | 
						|
+		}
 | 
						|
+		sense = (nlow >= nhigh ? 1 : 0);
 | 
						|
+		printk(KERN_INFO LIRC_DRIVER_NAME  ": auto-detected active "
 | 
						|
+		       "%s receiver\n", sense ? "low" : "high");
 | 
						|
+	} else
 | 
						|
+		printk(KERN_INFO LIRC_DRIVER_NAME  ": Manually using active "
 | 
						|
+		       "%s receiver\n", sense ? "low" : "high");
 | 
						|
+
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int set_use_inc(void *data)
 | 
						|
+{
 | 
						|
+	int result;
 | 
						|
+	unsigned long flags;
 | 
						|
+
 | 
						|
+	/* initialize timestamp */
 | 
						|
+	do_gettimeofday(&lasttv);
 | 
						|
+
 | 
						|
+	result = request_irq(irq, irq_handler,
 | 
						|
+			     IRQF_DISABLED | (share_irq ? IRQF_SHARED : 0),
 | 
						|
+			     LIRC_DRIVER_NAME, (void *)&hardware);
 | 
						|
+
 | 
						|
+	switch (result) {
 | 
						|
+	case -EBUSY:
 | 
						|
+		printk(KERN_ERR LIRC_DRIVER_NAME ": IRQ %d busy\n", irq);
 | 
						|
+		return -EBUSY;
 | 
						|
+	case -EINVAL:
 | 
						|
+		printk(KERN_ERR LIRC_DRIVER_NAME
 | 
						|
+		       ": Bad irq number or handler\n");
 | 
						|
+		return -EINVAL;
 | 
						|
+	default:
 | 
						|
+		dprintk("Interrupt %d, port %04x obtained\n", irq, io);
 | 
						|
+		break;
 | 
						|
+	};
 | 
						|
+
 | 
						|
+	spin_lock_irqsave(&hardware[type].lock, flags);
 | 
						|
+
 | 
						|
+	/* Set DLAB 0. */
 | 
						|
+	soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
 | 
						|
+
 | 
						|
+	soutp(UART_IER, sinp(UART_IER)|UART_IER_MSI);
 | 
						|
+
 | 
						|
+	spin_unlock_irqrestore(&hardware[type].lock, flags);
 | 
						|
+
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void set_use_dec(void *data)
 | 
						|
+{	unsigned long flags;
 | 
						|
+
 | 
						|
+	spin_lock_irqsave(&hardware[type].lock, 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)));
 | 
						|
+	spin_unlock_irqrestore(&hardware[type].lock, flags);
 | 
						|
+
 | 
						|
+	free_irq(irq, (void *)&hardware);
 | 
						|
+
 | 
						|
+	dprintk("freed IRQ %d\n", irq);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static ssize_t lirc_write(struct file *file, const char *buf,
 | 
						|
+			 size_t n, loff_t *ppos)
 | 
						|
+{
 | 
						|
+	int i, count;
 | 
						|
+	unsigned long flags;
 | 
						|
+	long delta = 0;
 | 
						|
+	int *wbuf;
 | 
						|
+
 | 
						|
+	if (!(hardware[type].features & LIRC_CAN_SEND_PULSE))
 | 
						|
+		return -EBADF;
 | 
						|
+
 | 
						|
+	count = n / sizeof(int);
 | 
						|
+	if (n % sizeof(int) || count % 2 == 0)
 | 
						|
+		return -EINVAL;
 | 
						|
+	wbuf = memdup_user(buf, n);
 | 
						|
+	if (PTR_ERR(wbuf))
 | 
						|
+		return PTR_ERR(wbuf);
 | 
						|
+	spin_lock_irqsave(&hardware[type].lock, flags);
 | 
						|
+	if (type == LIRC_IRDEO) {
 | 
						|
+		/* DTR, RTS down */
 | 
						|
+		on();
 | 
						|
+	}
 | 
						|
+	for (i = 0; i < count; i++) {
 | 
						|
+		if (i%2)
 | 
						|
+			hardware[type].send_space(wbuf[i] - delta);
 | 
						|
+		else
 | 
						|
+			delta = hardware[type].send_pulse(wbuf[i]);
 | 
						|
+	}
 | 
						|
+	off();
 | 
						|
+	spin_unlock_irqrestore(&hardware[type].lock, flags);
 | 
						|
+	return n;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static long lirc_ioctl(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 lirc_dev_fop_ioctl(filep, cmd, arg);
 | 
						|
+	}
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static struct file_operations lirc_fops = {
 | 
						|
+	.owner		= THIS_MODULE,
 | 
						|
+	.write		= lirc_write,
 | 
						|
+	.unlocked_ioctl	= lirc_ioctl,
 | 
						|
+	.read		= lirc_dev_fop_read,
 | 
						|
+	.poll		= lirc_dev_fop_poll,
 | 
						|
+	.open		= lirc_dev_fop_open,
 | 
						|
+	.release	= lirc_dev_fop_close,
 | 
						|
+};
 | 
						|
+
 | 
						|
+static struct lirc_driver driver = {
 | 
						|
+	.name		= LIRC_DRIVER_NAME,
 | 
						|
+	.minor		= -1,
 | 
						|
+	.code_length	= 1,
 | 
						|
+	.sample_rate	= 0,
 | 
						|
+	.data		= NULL,
 | 
						|
+	.add_to_buf	= NULL,
 | 
						|
+	.rbuf		= &rbuf,
 | 
						|
+	.set_use_inc	= set_use_inc,
 | 
						|
+	.set_use_dec	= set_use_dec,
 | 
						|
+	.fops		= &lirc_fops,
 | 
						|
+	.dev		= NULL,
 | 
						|
+	.owner		= THIS_MODULE,
 | 
						|
+};
 | 
						|
+
 | 
						|
+static struct platform_device *lirc_serial_dev;
 | 
						|
+
 | 
						|
+static int __devinit lirc_serial_probe(struct platform_device *dev)
 | 
						|
+{
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int __devexit lirc_serial_remove(struct platform_device *dev)
 | 
						|
+{
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int lirc_serial_suspend(struct platform_device *dev,
 | 
						|
+			       pm_message_t state)
 | 
						|
+{
 | 
						|
+	/* Set DLAB 0. */
 | 
						|
+	soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
 | 
						|
+
 | 
						|
+	/* Disable all interrupts */
 | 
						|
+	soutp(UART_IER, sinp(UART_IER) &
 | 
						|
+	      (~(UART_IER_MSI|UART_IER_RLSI|UART_IER_THRI|UART_IER_RDI)));
 | 
						|
+
 | 
						|
+	/* Clear registers. */
 | 
						|
+	sinp(UART_LSR);
 | 
						|
+	sinp(UART_RX);
 | 
						|
+	sinp(UART_IIR);
 | 
						|
+	sinp(UART_MSR);
 | 
						|
+
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+/* twisty maze... need a forward-declaration here... */
 | 
						|
+static void lirc_serial_exit(void);
 | 
						|
+
 | 
						|
+static int lirc_serial_resume(struct platform_device *dev)
 | 
						|
+{
 | 
						|
+	unsigned long flags;
 | 
						|
+
 | 
						|
+	if (hardware_init_port() < 0) {
 | 
						|
+		lirc_serial_exit();
 | 
						|
+		return -EINVAL;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	spin_lock_irqsave(&hardware[type].lock, flags);
 | 
						|
+	/* Enable Interrupt */
 | 
						|
+	do_gettimeofday(&lasttv);
 | 
						|
+	soutp(UART_IER, sinp(UART_IER)|UART_IER_MSI);
 | 
						|
+	off();
 | 
						|
+
 | 
						|
+	lirc_buffer_clear(&rbuf);
 | 
						|
+
 | 
						|
+	spin_unlock_irqrestore(&hardware[type].lock, flags);
 | 
						|
+
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static struct platform_driver lirc_serial_driver = {
 | 
						|
+	.probe		= lirc_serial_probe,
 | 
						|
+	.remove		= __devexit_p(lirc_serial_remove),
 | 
						|
+	.suspend	= lirc_serial_suspend,
 | 
						|
+	.resume		= lirc_serial_resume,
 | 
						|
+	.driver		= {
 | 
						|
+		.name	= "lirc_serial",
 | 
						|
+		.owner	= THIS_MODULE,
 | 
						|
+	},
 | 
						|
+};
 | 
						|
+
 | 
						|
+static int __init lirc_serial_init(void)
 | 
						|
+{
 | 
						|
+	int result;
 | 
						|
+
 | 
						|
+	/* Init read buffer. */
 | 
						|
+	result = lirc_buffer_init(&rbuf, sizeof(int), RBUF_LEN);
 | 
						|
+	if (result < 0)
 | 
						|
+		return -ENOMEM;
 | 
						|
+
 | 
						|
+	result = platform_driver_register(&lirc_serial_driver);
 | 
						|
+	if (result) {
 | 
						|
+		printk("lirc register returned %d\n", result);
 | 
						|
+		goto exit_buffer_free;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	lirc_serial_dev = platform_device_alloc("lirc_serial", 0);
 | 
						|
+	if (!lirc_serial_dev) {
 | 
						|
+		result = -ENOMEM;
 | 
						|
+		goto exit_driver_unregister;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	result = platform_device_add(lirc_serial_dev);
 | 
						|
+	if (result)
 | 
						|
+		goto exit_device_put;
 | 
						|
+
 | 
						|
+	return 0;
 | 
						|
+
 | 
						|
+exit_device_put:
 | 
						|
+	platform_device_put(lirc_serial_dev);
 | 
						|
+exit_driver_unregister:
 | 
						|
+	platform_driver_unregister(&lirc_serial_driver);
 | 
						|
+exit_buffer_free:
 | 
						|
+	lirc_buffer_free(&rbuf);
 | 
						|
+	return result;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void lirc_serial_exit(void)
 | 
						|
+{
 | 
						|
+	platform_device_unregister(lirc_serial_dev);
 | 
						|
+	platform_driver_unregister(&lirc_serial_driver);
 | 
						|
+	lirc_buffer_free(&rbuf);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int __init lirc_serial_init_module(void)
 | 
						|
+{
 | 
						|
+	int result;
 | 
						|
+
 | 
						|
+	result = lirc_serial_init();
 | 
						|
+	if (result)
 | 
						|
+		return result;
 | 
						|
+
 | 
						|
+	switch (type) {
 | 
						|
+	case LIRC_HOMEBREW:
 | 
						|
+	case LIRC_IRDEO:
 | 
						|
+	case LIRC_IRDEO_REMOTE:
 | 
						|
+	case LIRC_ANIMAX:
 | 
						|
+	case LIRC_IGOR:
 | 
						|
+		/* if nothing specified, use ttyS0/com1 and irq 4 */
 | 
						|
+		io = io ? io : 0x3f8;
 | 
						|
+		irq = irq ? irq : 4;
 | 
						|
+		break;
 | 
						|
+#ifdef CONFIG_LIRC_SERIAL_NSLU2
 | 
						|
+	case LIRC_NSLU2:
 | 
						|
+		io = io ? io : IRQ_IXP4XX_UART2;
 | 
						|
+		irq = irq ? irq : (IXP4XX_UART2_BASE_VIRT + REG_OFFSET);
 | 
						|
+		iommap = iommap ? iommap : IXP4XX_UART2_BASE_PHYS;
 | 
						|
+		ioshift = ioshift ? ioshift : 2;
 | 
						|
+		break;
 | 
						|
+#endif
 | 
						|
+	default:
 | 
						|
+		result = -EINVAL;
 | 
						|
+		goto exit_serial_exit;
 | 
						|
+	}
 | 
						|
+	if (!softcarrier) {
 | 
						|
+		switch (type) {
 | 
						|
+		case LIRC_HOMEBREW:
 | 
						|
+		case LIRC_IGOR:
 | 
						|
+#ifdef CONFIG_LIRC_SERIAL_NSLU2
 | 
						|
+		case LIRC_NSLU2:
 | 
						|
+#endif
 | 
						|
+			hardware[type].features &=
 | 
						|
+				~(LIRC_CAN_SET_SEND_DUTY_CYCLE|
 | 
						|
+				  LIRC_CAN_SET_SEND_CARRIER);
 | 
						|
+			break;
 | 
						|
+		}
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	result = init_port();
 | 
						|
+	if (result < 0)
 | 
						|
+		goto exit_serial_exit;
 | 
						|
+	driver.features = hardware[type].features;
 | 
						|
+	driver.dev = &lirc_serial_dev->dev;
 | 
						|
+	driver.minor = lirc_register_driver(&driver);
 | 
						|
+	if (driver.minor < 0) {
 | 
						|
+		printk(KERN_ERR  LIRC_DRIVER_NAME
 | 
						|
+		       ": register_chrdev failed!\n");
 | 
						|
+		result = -EIO;
 | 
						|
+		goto exit_release;
 | 
						|
+	}
 | 
						|
+	return 0;
 | 
						|
+exit_release:
 | 
						|
+	release_region(io, 8);
 | 
						|
+exit_serial_exit:
 | 
						|
+	lirc_serial_exit();
 | 
						|
+	return result;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void __exit lirc_serial_exit_module(void)
 | 
						|
+{
 | 
						|
+	lirc_serial_exit();
 | 
						|
+	if (iommap != 0)
 | 
						|
+		release_mem_region(iommap, 8 << ioshift);
 | 
						|
+	else
 | 
						|
+		release_region(io, 8);
 | 
						|
+	lirc_unregister_driver(driver.minor);
 | 
						|
+	dprintk("cleaned up module\n");
 | 
						|
+}
 | 
						|
+
 | 
						|
+
 | 
						|
+module_init(lirc_serial_init_module);
 | 
						|
+module_exit(lirc_serial_exit_module);
 | 
						|
+
 | 
						|
+MODULE_DESCRIPTION("Infra-red receiver driver for serial ports.");
 | 
						|
+MODULE_AUTHOR("Ralph Metzler, Trent Piepho, Ben Pfaff, "
 | 
						|
+	      "Christoph Bartelmus, Andrei Tanas");
 | 
						|
+MODULE_LICENSE("GPL");
 | 
						|
+
 | 
						|
+module_param(type, int, S_IRUGO);
 | 
						|
+MODULE_PARM_DESC(type, "Hardware type (0 = home-brew, 1 = IRdeo,"
 | 
						|
+		 " 2 = IRdeo Remote, 3 = AnimaX, 4 = IgorPlug,"
 | 
						|
+		 " 5 = NSLU2 RX:CTS2/TX:GreenLED)");
 | 
						|
+
 | 
						|
+module_param(io, int, S_IRUGO);
 | 
						|
+MODULE_PARM_DESC(io, "I/O address base (0x3f8 or 0x2f8)");
 | 
						|
+
 | 
						|
+/* some architectures (e.g. intel xscale) have memory mapped registers */
 | 
						|
+module_param(iommap, bool, S_IRUGO);
 | 
						|
+MODULE_PARM_DESC(iommap, "physical base for memory mapped I/O"
 | 
						|
+		" (0 = no memory mapped io)");
 | 
						|
+
 | 
						|
+/*
 | 
						|
+ * some architectures (e.g. intel xscale) align the 8bit serial registers
 | 
						|
+ * on 32bit word boundaries.
 | 
						|
+ * See linux-kernel/serial/8250.c serial_in()/out()
 | 
						|
+ */
 | 
						|
+module_param(ioshift, int, S_IRUGO);
 | 
						|
+MODULE_PARM_DESC(ioshift, "shift I/O register offset (0 = no shift)");
 | 
						|
+
 | 
						|
+module_param(irq, int, S_IRUGO);
 | 
						|
+MODULE_PARM_DESC(irq, "Interrupt (4 or 3)");
 | 
						|
+
 | 
						|
+module_param(share_irq, bool, S_IRUGO);
 | 
						|
+MODULE_PARM_DESC(share_irq, "Share interrupts (0 = off, 1 = on)");
 | 
						|
+
 | 
						|
+module_param(sense, bool, S_IRUGO);
 | 
						|
+MODULE_PARM_DESC(sense, "Override autodetection of IR receiver circuit"
 | 
						|
+		 " (0 = active high, 1 = active low )");
 | 
						|
+
 | 
						|
+#ifdef CONFIG_LIRC_SERIAL_TRANSMITTER
 | 
						|
+module_param(txsense, bool, S_IRUGO);
 | 
						|
+MODULE_PARM_DESC(txsense, "Sense of transmitter circuit"
 | 
						|
+		 " (0 = active high, 1 = active low )");
 | 
						|
+#endif
 | 
						|
+
 | 
						|
+module_param(softcarrier, bool, S_IRUGO);
 | 
						|
+MODULE_PARM_DESC(softcarrier, "Software carrier (0 = off, 1 = on, default on)");
 | 
						|
+
 | 
						|
+module_param(debug, bool, S_IRUGO | S_IWUSR);
 | 
						|
+MODULE_PARM_DESC(debug, "Enable debugging messages");
 | 
						|
diff --git a/drivers/staging/lirc/lirc_sir.c b/drivers/staging/lirc/lirc_sir.c
 | 
						|
new file mode 100644
 | 
						|
index 0000000..97146d1
 | 
						|
--- /dev/null
 | 
						|
+++ b/drivers/staging/lirc/lirc_sir.c
 | 
						|
@@ -0,0 +1,1282 @@
 | 
						|
+/*
 | 
						|
+ * 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/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/serial_reg.h>
 | 
						|
+#include <linux/time.h>
 | 
						|
+#include <linux/string.h>
 | 
						|
+#include <linux/types.h>
 | 
						|
+#include <linux/wait.h>
 | 
						|
+#include <linux/mm.h>
 | 
						|
+#include <linux/delay.h>
 | 
						|
+#include <linux/poll.h>
 | 
						|
+#include <asm/system.h>
 | 
						|
+#include <linux/io.h>
 | 
						|
+#include <asm/irq.h>
 | 
						|
+#include <linux/fcntl.h>
 | 
						|
+#ifdef LIRC_ON_SA1100
 | 
						|
+#include <asm/hardware.h>
 | 
						|
+#ifdef CONFIG_SA1100_COLLIE
 | 
						|
+#include <asm/arch/tc35143.h>
 | 
						|
+#include <asm/ucb1200.h>
 | 
						|
+#endif
 | 
						|
+#endif
 | 
						|
+
 | 
						|
+#include <linux/timer.h>
 | 
						|
+
 | 
						|
+#include <media/lirc.h>
 | 
						|
+#include <media/lirc_dev.h>
 | 
						|
+
 | 
						|
+/* SECTION: Definitions */
 | 
						|
+
 | 
						|
+/*** Tekram dongle ***/
 | 
						|
+#ifdef LIRC_SIR_TEKRAM
 | 
						|
+/* stolen from kernel source */
 | 
						|
+/* definitions for Tekram dongle */
 | 
						|
+#define TEKRAM_115200 0x00
 | 
						|
+#define TEKRAM_57600  0x01
 | 
						|
+#define TEKRAM_38400  0x02
 | 
						|
+#define TEKRAM_19200  0x03
 | 
						|
+#define TEKRAM_9600   0x04
 | 
						|
+#define TEKRAM_2400   0x08
 | 
						|
+
 | 
						|
+#define TEKRAM_PW 0x10 /* Pulse select bit */
 | 
						|
+
 | 
						|
+/* 10bit * 1s/115200bit in milliseconds = 87ms*/
 | 
						|
+#define TIME_CONST (10000000ul/115200ul)
 | 
						|
+
 | 
						|
+#endif
 | 
						|
+
 | 
						|
+#ifdef LIRC_SIR_ACTISYS_ACT200L
 | 
						|
+static void init_act200(void);
 | 
						|
+#elif defined(LIRC_SIR_ACTISYS_ACT220L)
 | 
						|
+static void init_act220(void);
 | 
						|
+#endif
 | 
						|
+
 | 
						|
+/*** SA1100 ***/
 | 
						|
+#ifdef LIRC_ON_SA1100
 | 
						|
+struct sa1100_ser2_registers {
 | 
						|
+	/* HSSP control register */
 | 
						|
+	unsigned char hscr0;
 | 
						|
+	/* UART registers */
 | 
						|
+	unsigned char utcr0;
 | 
						|
+	unsigned char utcr1;
 | 
						|
+	unsigned char utcr2;
 | 
						|
+	unsigned char utcr3;
 | 
						|
+	unsigned char utcr4;
 | 
						|
+	unsigned char utdr;
 | 
						|
+	unsigned char utsr0;
 | 
						|
+	unsigned char utsr1;
 | 
						|
+} sr;
 | 
						|
+
 | 
						|
+static int irq = IRQ_Ser2ICP;
 | 
						|
+
 | 
						|
+#define LIRC_ON_SA1100_TRANSMITTER_LATENCY 0
 | 
						|
+
 | 
						|
+/* pulse/space ratio of 50/50 */
 | 
						|
+static unsigned long pulse_width = (13-LIRC_ON_SA1100_TRANSMITTER_LATENCY);
 | 
						|
+/* 1000000/freq-pulse_width */
 | 
						|
+static unsigned long space_width = (13-LIRC_ON_SA1100_TRANSMITTER_LATENCY);
 | 
						|
+static unsigned int freq = 38000;      /* modulation frequency */
 | 
						|
+static unsigned int duty_cycle = 50;   /* duty cycle of 50% */
 | 
						|
+
 | 
						|
+#endif
 | 
						|
+
 | 
						|
+#define RBUF_LEN 1024
 | 
						|
+#define WBUF_LEN 1024
 | 
						|
+
 | 
						|
+#define LIRC_DRIVER_NAME "lirc_sir"
 | 
						|
+
 | 
						|
+#define PULSE '['
 | 
						|
+
 | 
						|
+#ifndef LIRC_SIR_TEKRAM
 | 
						|
+/* 9bit * 1s/115200bit in milli seconds = 78.125ms*/
 | 
						|
+#define TIME_CONST (9000000ul/115200ul)
 | 
						|
+#endif
 | 
						|
+
 | 
						|
+
 | 
						|
+/* timeout for sequences in jiffies (=5/100s), must be longer than TIME_CONST */
 | 
						|
+#define SIR_TIMEOUT	(HZ*5/100)
 | 
						|
+
 | 
						|
+#ifndef LIRC_ON_SA1100
 | 
						|
+#ifndef LIRC_IRQ
 | 
						|
+#define LIRC_IRQ 4
 | 
						|
+#endif
 | 
						|
+#ifndef LIRC_PORT
 | 
						|
+/* for external dongles, default to com1 */
 | 
						|
+#if defined(LIRC_SIR_ACTISYS_ACT200L) || \
 | 
						|
+    defined(LIRC_SIR_ACTISYS_ACT220L) || \
 | 
						|
+    defined(LIRC_SIR_TEKRAM)
 | 
						|
+#define LIRC_PORT 0x3f8
 | 
						|
+#else
 | 
						|
+/* onboard sir ports are typically com3 */
 | 
						|
+#define LIRC_PORT 0x3e8
 | 
						|
+#endif
 | 
						|
+#endif
 | 
						|
+
 | 
						|
+static int io = LIRC_PORT;
 | 
						|
+static int irq = LIRC_IRQ;
 | 
						|
+static int threshold = 3;
 | 
						|
+#endif
 | 
						|
+
 | 
						|
+static DEFINE_SPINLOCK(timer_lock);
 | 
						|
+static struct timer_list timerlist;
 | 
						|
+/* time of last signal change detected */
 | 
						|
+static struct timeval last_tv = {0, 0};
 | 
						|
+/* time of last UART data ready interrupt */
 | 
						|
+static struct timeval last_intr_tv = {0, 0};
 | 
						|
+static int last_value;
 | 
						|
+
 | 
						|
+static DECLARE_WAIT_QUEUE_HEAD(lirc_read_queue);
 | 
						|
+
 | 
						|
+static DEFINE_SPINLOCK(hardware_lock);
 | 
						|
+
 | 
						|
+static int rx_buf[RBUF_LEN];
 | 
						|
+static unsigned int rx_tail, rx_head;
 | 
						|
+
 | 
						|
+static int debug;
 | 
						|
+#define dprintk(fmt, args...)						\
 | 
						|
+	do {								\
 | 
						|
+		if (debug)						\
 | 
						|
+			printk(KERN_DEBUG LIRC_DRIVER_NAME ": "		\
 | 
						|
+				fmt, ## args);				\
 | 
						|
+	} while (0)
 | 
						|
+
 | 
						|
+/* SECTION: Prototypes */
 | 
						|
+
 | 
						|
+/* Communication with user-space */
 | 
						|
+static 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 long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg);
 | 
						|
+static void add_read_queue(int flag, unsigned long val);
 | 
						|
+static int init_chrdev(void);
 | 
						|
+static void drop_chrdev(void);
 | 
						|
+/* Hardware */
 | 
						|
+static irqreturn_t sir_interrupt(int irq, void *dev_id);
 | 
						|
+static void send_space(unsigned long len);
 | 
						|
+static void send_pulse(unsigned long len);
 | 
						|
+static int init_hardware(void);
 | 
						|
+static void drop_hardware(void);
 | 
						|
+/* Initialisation */
 | 
						|
+static int init_port(void);
 | 
						|
+static void drop_port(void);
 | 
						|
+
 | 
						|
+#ifdef LIRC_ON_SA1100
 | 
						|
+static void on(void)
 | 
						|
+{
 | 
						|
+	PPSR |= PPC_TXD2;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void off(void)
 | 
						|
+{
 | 
						|
+	PPSR &= ~PPC_TXD2;
 | 
						|
+}
 | 
						|
+#else
 | 
						|
+static inline unsigned int sinp(int offset)
 | 
						|
+{
 | 
						|
+	return inb(io + offset);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static inline void soutp(int offset, int value)
 | 
						|
+{
 | 
						|
+	outb(value, io + offset);
 | 
						|
+}
 | 
						|
+#endif
 | 
						|
+
 | 
						|
+#ifndef MAX_UDELAY_MS
 | 
						|
+#define MAX_UDELAY_US 5000
 | 
						|
+#else
 | 
						|
+#define MAX_UDELAY_US (MAX_UDELAY_MS*1000)
 | 
						|
+#endif
 | 
						|
+
 | 
						|
+static 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 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 (count % sizeof(int))
 | 
						|
+		return -EINVAL;
 | 
						|
+
 | 
						|
+	add_wait_queue(&lirc_read_queue, &wait);
 | 
						|
+	set_current_state(TASK_INTERRUPTIBLE);
 | 
						|
+	while (n < count) {
 | 
						|
+		if (rx_head != rx_tail) {
 | 
						|
+			if (copy_to_user((void *) buf + n,
 | 
						|
+					(void *) (rx_buf + rx_head),
 | 
						|
+					sizeof(int))) {
 | 
						|
+				retval = -EFAULT;
 | 
						|
+				break;
 | 
						|
+			}
 | 
						|
+			rx_head = (rx_head + 1) & (RBUF_LEN - 1);
 | 
						|
+			n += sizeof(int);
 | 
						|
+		} else {
 | 
						|
+			if (file->f_flags & O_NONBLOCK) {
 | 
						|
+				retval = -EAGAIN;
 | 
						|
+				break;
 | 
						|
+			}
 | 
						|
+			if (signal_pending(current)) {
 | 
						|
+				retval = -ERESTARTSYS;
 | 
						|
+				break;
 | 
						|
+			}
 | 
						|
+			schedule();
 | 
						|
+			set_current_state(TASK_INTERRUPTIBLE);
 | 
						|
+		}
 | 
						|
+	}
 | 
						|
+	remove_wait_queue(&lirc_read_queue, &wait);
 | 
						|
+	set_current_state(TASK_RUNNING);
 | 
						|
+	return n ? n : retval;
 | 
						|
+}
 | 
						|
+static ssize_t lirc_write(struct file *file, const char *buf, size_t n,
 | 
						|
+				loff_t *pos)
 | 
						|
+{
 | 
						|
+	unsigned long flags;
 | 
						|
+	int i, count;
 | 
						|
+	int *tx_buf;
 | 
						|
+
 | 
						|
+	count = n / sizeof(int);
 | 
						|
+	if (n % sizeof(int) || count % 2 == 0)
 | 
						|
+		return -EINVAL;
 | 
						|
+	tx_buf = memdup_user(buf, n);
 | 
						|
+	if (IS_ERR(tx_buf))
 | 
						|
+		return PTR_ERR(tx_buf);
 | 
						|
+	i = 0;
 | 
						|
+#ifdef LIRC_ON_SA1100
 | 
						|
+	/* disable receiver */
 | 
						|
+	Ser2UTCR3 = 0;
 | 
						|
+#endif
 | 
						|
+	local_irq_save(flags);
 | 
						|
+	while (1) {
 | 
						|
+		if (i >= count)
 | 
						|
+			break;
 | 
						|
+		if (tx_buf[i])
 | 
						|
+			send_pulse(tx_buf[i]);
 | 
						|
+		i++;
 | 
						|
+		if (i >= count)
 | 
						|
+			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 count;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
 | 
						|
+{
 | 
						|
+	int retval = 0;
 | 
						|
+	unsigned long value = 0;
 | 
						|
+#ifdef LIRC_ON_SA1100
 | 
						|
+	unsigned int ivalue;
 | 
						|
+
 | 
						|
+	if (cmd == LIRC_GET_FEATURES)
 | 
						|
+		value = LIRC_CAN_SEND_PULSE |
 | 
						|
+			LIRC_CAN_SET_SEND_DUTY_CYCLE |
 | 
						|
+			LIRC_CAN_SET_SEND_CARRIER |
 | 
						|
+			LIRC_CAN_REC_MODE2;
 | 
						|
+	else if (cmd == LIRC_GET_SEND_MODE)
 | 
						|
+		value = LIRC_MODE_PULSE;
 | 
						|
+	else if (cmd == LIRC_GET_REC_MODE)
 | 
						|
+		value = LIRC_MODE_MODE2;
 | 
						|
+#else
 | 
						|
+	if (cmd == LIRC_GET_FEATURES)
 | 
						|
+		value = LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2;
 | 
						|
+	else if (cmd == LIRC_GET_SEND_MODE)
 | 
						|
+		value = LIRC_MODE_PULSE;
 | 
						|
+	else if (cmd == LIRC_GET_REC_MODE)
 | 
						|
+		value = LIRC_MODE_MODE2;
 | 
						|
+#endif
 | 
						|
+
 | 
						|
+	switch (cmd) {
 | 
						|
+	case LIRC_GET_FEATURES:
 | 
						|
+	case LIRC_GET_SEND_MODE:
 | 
						|
+	case LIRC_GET_REC_MODE:
 | 
						|
+		retval = put_user(value, (unsigned long *) arg);
 | 
						|
+		break;
 | 
						|
+
 | 
						|
+	case LIRC_SET_SEND_MODE:
 | 
						|
+	case LIRC_SET_REC_MODE:
 | 
						|
+		retval = get_user(value, (unsigned long *) arg);
 | 
						|
+		break;
 | 
						|
+#ifdef LIRC_ON_SA1100
 | 
						|
+	case LIRC_SET_SEND_DUTY_CYCLE:
 | 
						|
+		retval = get_user(ivalue, (unsigned int *) arg);
 | 
						|
+		if (retval)
 | 
						|
+			return 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;
 | 
						|
+	if (cmd == LIRC_SET_REC_MODE) {
 | 
						|
+		if (value != LIRC_MODE_MODE2)
 | 
						|
+			retval = -ENOSYS;
 | 
						|
+	} else if (cmd == LIRC_SET_SEND_MODE) {
 | 
						|
+		if (value != LIRC_MODE_PULSE)
 | 
						|
+			retval = -ENOSYS;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	return retval;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void add_read_queue(int flag, unsigned long val)
 | 
						|
+{
 | 
						|
+	unsigned int new_rx_tail;
 | 
						|
+	int newval;
 | 
						|
+
 | 
						|
+	dprintk("add flag %d with val %lu\n", flag, val);
 | 
						|
+
 | 
						|
+	newval = val & PULSE_MASK;
 | 
						|
+
 | 
						|
+	/*
 | 
						|
+	 * statistically, pulses are ~TIME_CONST/2 too long. we could
 | 
						|
+	 * maybe make this more exact, 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 = {
 | 
						|
+	.owner		= THIS_MODULE,
 | 
						|
+	.read		= lirc_read,
 | 
						|
+	.write		= lirc_write,
 | 
						|
+	.poll		= lirc_poll,
 | 
						|
+	.unlocked_ioctl	= lirc_ioctl,
 | 
						|
+	.open		= lirc_dev_fop_open,
 | 
						|
+	.release	= lirc_dev_fop_close,
 | 
						|
+};
 | 
						|
+
 | 
						|
+static int set_use_inc(void *data)
 | 
						|
+{
 | 
						|
+       return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void set_use_dec(void *data)
 | 
						|
+{
 | 
						|
+}
 | 
						|
+
 | 
						|
+static struct lirc_driver driver = {
 | 
						|
+       .name		= LIRC_DRIVER_NAME,
 | 
						|
+       .minor		= -1,
 | 
						|
+       .code_length	= 1,
 | 
						|
+       .sample_rate	= 0,
 | 
						|
+       .data		= NULL,
 | 
						|
+       .add_to_buf	= NULL,
 | 
						|
+       .set_use_inc	= set_use_inc,
 | 
						|
+       .set_use_dec	= set_use_dec,
 | 
						|
+       .fops		= &lirc_fops,
 | 
						|
+       .dev		= NULL,
 | 
						|
+       .owner		= THIS_MODULE,
 | 
						|
+};
 | 
						|
+
 | 
						|
+
 | 
						|
+static int init_chrdev(void)
 | 
						|
+{
 | 
						|
+	driver.minor = lirc_register_driver(&driver);
 | 
						|
+	if (driver.minor < 0) {
 | 
						|
+		printk(KERN_ERR LIRC_DRIVER_NAME ": init_chrdev() failed.\n");
 | 
						|
+		return -EIO;
 | 
						|
+	}
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void drop_chrdev(void)
 | 
						|
+{
 | 
						|
+	lirc_unregister_driver(driver.minor);
 | 
						|
+}
 | 
						|
+
 | 
						|
+/* SECTION: Hardware */
 | 
						|
+static long delta(struct timeval *tv1, struct timeval *tv2)
 | 
						|
+{
 | 
						|
+	unsigned long deltv;
 | 
						|
+
 | 
						|
+	deltv = tv2->tv_sec - tv1->tv_sec;
 | 
						|
+	if (deltv > 15)
 | 
						|
+		deltv = 0xFFFFFF;
 | 
						|
+	else
 | 
						|
+		deltv = deltv*1000000 +
 | 
						|
+			tv2->tv_usec -
 | 
						|
+			tv1->tv_usec;
 | 
						|
+	return deltv;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void sir_timeout(unsigned long data)
 | 
						|
+{
 | 
						|
+	/*
 | 
						|
+	 * if last received signal was a pulse, but receiving stopped
 | 
						|
+	 * within the 9 bit frame, we need to finish this pulse and
 | 
						|
+	 * simulate a signal change to from pulse to space. Otherwise
 | 
						|
+	 * upper layers will receive two sequences next time.
 | 
						|
+	 */
 | 
						|
+
 | 
						|
+	unsigned long flags;
 | 
						|
+	unsigned long pulse_end;
 | 
						|
+
 | 
						|
+	/* avoid interference with interrupt */
 | 
						|
+	spin_lock_irqsave(&timer_lock, flags);
 | 
						|
+	if (last_value) {
 | 
						|
+#ifndef LIRC_ON_SA1100
 | 
						|
+		/* clear unread bits in UART and restart */
 | 
						|
+		outb(UART_FCR_CLEAR_RCVR, io + UART_FCR);
 | 
						|
+#endif
 | 
						|
+		/* determine 'virtual' pulse end: */
 | 
						|
+		pulse_end = delta(&last_tv, &last_intr_tv);
 | 
						|
+		dprintk("timeout add %d for %lu usec\n", last_value, pulse_end);
 | 
						|
+		add_read_queue(last_value, pulse_end);
 | 
						|
+		last_value = 0;
 | 
						|
+		last_tv = last_intr_tv;
 | 
						|
+	}
 | 
						|
+	spin_unlock_irqrestore(&timer_lock, flags);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static irqreturn_t sir_interrupt(int irq, void *dev_id)
 | 
						|
+{
 | 
						|
+	unsigned char data;
 | 
						|
+	struct timeval curr_tv;
 | 
						|
+	static unsigned long deltv;
 | 
						|
+#ifdef LIRC_ON_SA1100
 | 
						|
+	int status;
 | 
						|
+	static int n;
 | 
						|
+
 | 
						|
+	status = Ser2UTSR0;
 | 
						|
+	/*
 | 
						|
+	 * Deal with any receive errors first.  The bytes in error may be
 | 
						|
+	 * the only bytes in the receive FIFO, so we do this first.
 | 
						|
+	 */
 | 
						|
+	while (status & UTSR0_EIF) {
 | 
						|
+		int bstat;
 | 
						|
+
 | 
						|
+		if (debug) {
 | 
						|
+			dprintk("EIF\n");
 | 
						|
+			bstat = Ser2UTSR1;
 | 
						|
+
 | 
						|
+			if (bstat & UTSR1_FRE)
 | 
						|
+				dprintk("frame error\n");
 | 
						|
+			if (bstat & UTSR1_ROR)
 | 
						|
+				dprintk("receive fifo overrun\n");
 | 
						|
+			if (bstat & UTSR1_PRE)
 | 
						|
+				dprintk("parity error\n");
 | 
						|
+		}
 | 
						|
+
 | 
						|
+		bstat = Ser2UTDR;
 | 
						|
+		n++;
 | 
						|
+		status = Ser2UTSR0;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	if (status & (UTSR0_RFS | UTSR0_RID)) {
 | 
						|
+		do_gettimeofday(&curr_tv);
 | 
						|
+		deltv = delta(&last_tv, &curr_tv);
 | 
						|
+		do {
 | 
						|
+			data = Ser2UTDR;
 | 
						|
+			dprintk("%d data: %u\n", n, (unsigned int) data);
 | 
						|
+			n++;
 | 
						|
+		} while (status & UTSR0_RID && /* do not empty fifo in order to
 | 
						|
+						* get UTSR0_RID in any case */
 | 
						|
+		      Ser2UTSR1 & UTSR1_RNE); /* data ready */
 | 
						|
+
 | 
						|
+		if (status&UTSR0_RID) {
 | 
						|
+			add_read_queue(0 , deltv - n * TIME_CONST); /*space*/
 | 
						|
+			add_read_queue(1, n * TIME_CONST); /*pulse*/
 | 
						|
+			n = 0;
 | 
						|
+			last_tv = curr_tv;
 | 
						|
+		}
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	if (status & UTSR0_TFS)
 | 
						|
+		printk(KERN_ERR "transmit fifo not full, shouldn't happen\n");
 | 
						|
+
 | 
						|
+	/* We must clear certain bits. */
 | 
						|
+	status &= (UTSR0_RID | UTSR0_RBB | UTSR0_REB);
 | 
						|
+	if (status)
 | 
						|
+		Ser2UTSR0 = status;
 | 
						|
+#else
 | 
						|
+	unsigned long deltintrtv;
 | 
						|
+	unsigned long flags;
 | 
						|
+	int iir, lsr;
 | 
						|
+
 | 
						|
+	while ((iir = inb(io + UART_IIR) & UART_IIR_ID)) {
 | 
						|
+		switch (iir&UART_IIR_ID) { /* FIXME toto treba preriedit */
 | 
						|
+		case UART_IIR_MSI:
 | 
						|
+			(void) inb(io + UART_MSR);
 | 
						|
+			break;
 | 
						|
+		case UART_IIR_RLSI:
 | 
						|
+			(void) inb(io + UART_LSR);
 | 
						|
+			break;
 | 
						|
+		case UART_IIR_THRI:
 | 
						|
+#if 0
 | 
						|
+			if (lsr & UART_LSR_THRE) /* FIFO is empty */
 | 
						|
+				outb(data, io + UART_TX)
 | 
						|
+#endif
 | 
						|
+			break;
 | 
						|
+		case UART_IIR_RDI:
 | 
						|
+			/* avoid interference with timer */
 | 
						|
+			spin_lock_irqsave(&timer_lock, flags);
 | 
						|
+			do {
 | 
						|
+				del_timer(&timerlist);
 | 
						|
+				data = inb(io + UART_RX);
 | 
						|
+				do_gettimeofday(&curr_tv);
 | 
						|
+				deltv = delta(&last_tv, &curr_tv);
 | 
						|
+				deltintrtv = delta(&last_intr_tv, &curr_tv);
 | 
						|
+				dprintk("t %lu, d %d\n", deltintrtv, (int)data);
 | 
						|
+				/*
 | 
						|
+				 * if nothing came in last X cycles,
 | 
						|
+				 * it was gap
 | 
						|
+				 */
 | 
						|
+				if (deltintrtv > TIME_CONST * threshold) {
 | 
						|
+					if (last_value) {
 | 
						|
+						dprintk("GAP\n");
 | 
						|
+						/* simulate signal change */
 | 
						|
+						add_read_queue(last_value,
 | 
						|
+							       deltv -
 | 
						|
+							       deltintrtv);
 | 
						|
+						last_value = 0;
 | 
						|
+						last_tv.tv_sec =
 | 
						|
+							last_intr_tv.tv_sec;
 | 
						|
+						last_tv.tv_usec =
 | 
						|
+							last_intr_tv.tv_usec;
 | 
						|
+						deltv = deltintrtv;
 | 
						|
+					}
 | 
						|
+				}
 | 
						|
+				data = 1;
 | 
						|
+				if (data ^ last_value) {
 | 
						|
+					/*
 | 
						|
+					 * deltintrtv > 2*TIME_CONST, remember?
 | 
						|
+					 * the other case is timeout
 | 
						|
+					 */
 | 
						|
+					add_read_queue(last_value,
 | 
						|
+						       deltv-TIME_CONST);
 | 
						|
+					last_value = data;
 | 
						|
+					last_tv = curr_tv;
 | 
						|
+					if (last_tv.tv_usec >= TIME_CONST) {
 | 
						|
+						last_tv.tv_usec -= TIME_CONST;
 | 
						|
+					} else {
 | 
						|
+						last_tv.tv_sec--;
 | 
						|
+						last_tv.tv_usec += 1000000 -
 | 
						|
+							TIME_CONST;
 | 
						|
+					}
 | 
						|
+				}
 | 
						|
+				last_intr_tv = curr_tv;
 | 
						|
+				if (data) {
 | 
						|
+					/*
 | 
						|
+					 * start timer for end of
 | 
						|
+					 * sequence detection
 | 
						|
+					 */
 | 
						|
+					timerlist.expires = jiffies +
 | 
						|
+								SIR_TIMEOUT;
 | 
						|
+					add_timer(&timerlist);
 | 
						|
+				}
 | 
						|
+
 | 
						|
+				lsr = inb(io + UART_LSR);
 | 
						|
+			} while (lsr & UART_LSR_DR); /* data ready */
 | 
						|
+			spin_unlock_irqrestore(&timer_lock, flags);
 | 
						|
+			break;
 | 
						|
+		default:
 | 
						|
+			break;
 | 
						|
+		}
 | 
						|
+	}
 | 
						|
+#endif
 | 
						|
+	return IRQ_RETVAL(IRQ_HANDLED);
 | 
						|
+}
 | 
						|
+
 | 
						|
+#ifdef LIRC_ON_SA1100
 | 
						|
+static void send_pulse(unsigned long length)
 | 
						|
+{
 | 
						|
+	unsigned long k, delay;
 | 
						|
+	int flag;
 | 
						|
+
 | 
						|
+	if (length == 0)
 | 
						|
+		return;
 | 
						|
+	/*
 | 
						|
+	 * this won't give us the carrier frequency we really want
 | 
						|
+	 * due to integer arithmetic, but we can accept this inaccuracy
 | 
						|
+	 */
 | 
						|
+
 | 
						|
+	for (k = flag = 0; k < length; k += delay, flag = !flag) {
 | 
						|
+		if (flag) {
 | 
						|
+			off();
 | 
						|
+			delay = space_width;
 | 
						|
+		} else {
 | 
						|
+			on();
 | 
						|
+			delay = pulse_width;
 | 
						|
+		}
 | 
						|
+		safe_udelay(delay);
 | 
						|
+	}
 | 
						|
+	off();
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void send_space(unsigned long length)
 | 
						|
+{
 | 
						|
+	if (length == 0)
 | 
						|
+		return;
 | 
						|
+	off();
 | 
						|
+	safe_udelay(length);
 | 
						|
+}
 | 
						|
+#else
 | 
						|
+static void send_space(unsigned long len)
 | 
						|
+{
 | 
						|
+	safe_udelay(len);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void send_pulse(unsigned long len)
 | 
						|
+{
 | 
						|
+	long bytes_out = len / TIME_CONST;
 | 
						|
+	long time_left;
 | 
						|
+
 | 
						|
+	time_left = (long)len - (long)bytes_out * (long)TIME_CONST;
 | 
						|
+	if (bytes_out == 0) {
 | 
						|
+		bytes_out++;
 | 
						|
+		time_left = 0;
 | 
						|
+	}
 | 
						|
+	while (bytes_out--) {
 | 
						|
+		outb(PULSE, io + UART_TX);
 | 
						|
+		/* FIXME treba seriozne cakanie z char/serial.c */
 | 
						|
+		while (!(inb(io + UART_LSR) & UART_LSR_THRE))
 | 
						|
+			;
 | 
						|
+	}
 | 
						|
+#if 0
 | 
						|
+	if (time_left > 0)
 | 
						|
+		safe_udelay(time_left);
 | 
						|
+#endif
 | 
						|
+}
 | 
						|
+#endif
 | 
						|
+
 | 
						|
+#ifdef CONFIG_SA1100_COLLIE
 | 
						|
+static int sa1100_irda_set_power_collie(int state)
 | 
						|
+{
 | 
						|
+	if (state) {
 | 
						|
+		/*
 | 
						|
+		 *  0 - off
 | 
						|
+		 *  1 - short range, lowest power
 | 
						|
+		 *  2 - medium range, medium power
 | 
						|
+		 *  3 - maximum range, high power
 | 
						|
+		 */
 | 
						|
+		ucb1200_set_io_direction(TC35143_GPIO_IR_ON,
 | 
						|
+					 TC35143_IODIR_OUTPUT);
 | 
						|
+		ucb1200_set_io(TC35143_GPIO_IR_ON, TC35143_IODAT_LOW);
 | 
						|
+		udelay(100);
 | 
						|
+	} else {
 | 
						|
+		/* OFF */
 | 
						|
+		ucb1200_set_io_direction(TC35143_GPIO_IR_ON,
 | 
						|
+					 TC35143_IODIR_OUTPUT);
 | 
						|
+		ucb1200_set_io(TC35143_GPIO_IR_ON, TC35143_IODAT_HIGH);
 | 
						|
+	}
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+#endif
 | 
						|
+
 | 
						|
+static int init_hardware(void)
 | 
						|
+{
 | 
						|
+	unsigned long flags;
 | 
						|
+
 | 
						|
+	spin_lock_irqsave(&hardware_lock, flags);
 | 
						|
+	/* reset UART */
 | 
						|
+#ifdef LIRC_ON_SA1100
 | 
						|
+#ifdef CONFIG_SA1100_BITSY
 | 
						|
+	if (machine_is_bitsy()) {
 | 
						|
+		printk(KERN_INFO "Power on IR module\n");
 | 
						|
+		set_bitsy_egpio(EGPIO_BITSY_IR_ON);
 | 
						|
+	}
 | 
						|
+#endif
 | 
						|
+#ifdef CONFIG_SA1100_COLLIE
 | 
						|
+	sa1100_irda_set_power_collie(3);	/* power on */
 | 
						|
+#endif
 | 
						|
+	sr.hscr0 = Ser2HSCR0;
 | 
						|
+
 | 
						|
+	sr.utcr0 = Ser2UTCR0;
 | 
						|
+	sr.utcr1 = Ser2UTCR1;
 | 
						|
+	sr.utcr2 = Ser2UTCR2;
 | 
						|
+	sr.utcr3 = Ser2UTCR3;
 | 
						|
+	sr.utcr4 = Ser2UTCR4;
 | 
						|
+
 | 
						|
+	sr.utdr = Ser2UTDR;
 | 
						|
+	sr.utsr0 = Ser2UTSR0;
 | 
						|
+	sr.utsr1 = Ser2UTSR1;
 | 
						|
+
 | 
						|
+	/* configure GPIO */
 | 
						|
+	/* output */
 | 
						|
+	PPDR |= PPC_TXD2;
 | 
						|
+	PSDR |= PPC_TXD2;
 | 
						|
+	/* set output to 0 */
 | 
						|
+	off();
 | 
						|
+
 | 
						|
+	/* Enable HP-SIR modulation, and ensure that the port is disabled. */
 | 
						|
+	Ser2UTCR3 = 0;
 | 
						|
+	Ser2HSCR0 = sr.hscr0 & (~HSCR0_HSSP);
 | 
						|
+
 | 
						|
+	/* clear status register to prevent unwanted interrupts */
 | 
						|
+	Ser2UTSR0 &= (UTSR0_RID | UTSR0_RBB | UTSR0_REB);
 | 
						|
+
 | 
						|
+	/* 7N1 */
 | 
						|
+	Ser2UTCR0 = UTCR0_1StpBit|UTCR0_7BitData;
 | 
						|
+	/* 115200 */
 | 
						|
+	Ser2UTCR1 = 0;
 | 
						|
+	Ser2UTCR2 = 1;
 | 
						|
+	/* use HPSIR, 1.6 usec pulses */
 | 
						|
+	Ser2UTCR4 = UTCR4_HPSIR|UTCR4_Z1_6us;
 | 
						|
+
 | 
						|
+	/* enable receiver, receive fifo interrupt */
 | 
						|
+	Ser2UTCR3 = UTCR3_RXE|UTCR3_RIE;
 | 
						|
+
 | 
						|
+	/* clear status register to prevent unwanted interrupts */
 | 
						|
+	Ser2UTSR0 &= (UTSR0_RID | UTSR0_RBB | UTSR0_REB);
 | 
						|
+
 | 
						|
+#elif defined(LIRC_SIR_TEKRAM)
 | 
						|
+	/* disable FIFO */
 | 
						|
+	soutp(UART_FCR,
 | 
						|
+	      UART_FCR_CLEAR_RCVR|
 | 
						|
+	      UART_FCR_CLEAR_XMIT|
 | 
						|
+	      UART_FCR_TRIGGER_1);
 | 
						|
+
 | 
						|
+	/* Set DLAB 0. */
 | 
						|
+	soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
 | 
						|
+
 | 
						|
+	/* First of all, disable all interrupts */
 | 
						|
+	soutp(UART_IER, sinp(UART_IER) &
 | 
						|
+	      (~(UART_IER_MSI|UART_IER_RLSI|UART_IER_THRI|UART_IER_RDI)));
 | 
						|
+
 | 
						|
+	/* Set DLAB 1. */
 | 
						|
+	soutp(UART_LCR, sinp(UART_LCR) | UART_LCR_DLAB);
 | 
						|
+
 | 
						|
+	/* Set divisor to 12 => 9600 Baud */
 | 
						|
+	soutp(UART_DLM, 0);
 | 
						|
+	soutp(UART_DLL, 12);
 | 
						|
+
 | 
						|
+	/* Set DLAB 0. */
 | 
						|
+	soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
 | 
						|
+
 | 
						|
+	/* power supply */
 | 
						|
+	soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2);
 | 
						|
+	safe_udelay(50*1000);
 | 
						|
+
 | 
						|
+	/* -DTR low -> reset PIC */
 | 
						|
+	soutp(UART_MCR, UART_MCR_RTS|UART_MCR_OUT2);
 | 
						|
+	udelay(1*1000);
 | 
						|
+
 | 
						|
+	soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2);
 | 
						|
+	udelay(100);
 | 
						|
+
 | 
						|
+
 | 
						|
+	/* -RTS low -> send control byte */
 | 
						|
+	soutp(UART_MCR, UART_MCR_DTR|UART_MCR_OUT2);
 | 
						|
+	udelay(7);
 | 
						|
+	soutp(UART_TX, TEKRAM_115200|TEKRAM_PW);
 | 
						|
+
 | 
						|
+	/* one byte takes ~1042 usec to transmit at 9600,8N1 */
 | 
						|
+	udelay(1500);
 | 
						|
+
 | 
						|
+	/* back to normal operation */
 | 
						|
+	soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2);
 | 
						|
+	udelay(50);
 | 
						|
+
 | 
						|
+	udelay(1500);
 | 
						|
+
 | 
						|
+	/* read previous control byte */
 | 
						|
+	printk(KERN_INFO LIRC_DRIVER_NAME
 | 
						|
+	       ": 0x%02x\n", sinp(UART_RX));
 | 
						|
+
 | 
						|
+	/* Set DLAB 1. */
 | 
						|
+	soutp(UART_LCR, sinp(UART_LCR) | UART_LCR_DLAB);
 | 
						|
+
 | 
						|
+	/* Set divisor to 1 => 115200 Baud */
 | 
						|
+	soutp(UART_DLM, 0);
 | 
						|
+	soutp(UART_DLL, 1);
 | 
						|
+
 | 
						|
+	/* Set DLAB 0, 8 Bit */
 | 
						|
+	soutp(UART_LCR, UART_LCR_WLEN8);
 | 
						|
+	/* enable interrupts */
 | 
						|
+	soutp(UART_IER, sinp(UART_IER)|UART_IER_RDI);
 | 
						|
+#else
 | 
						|
+	outb(0, io + UART_MCR);
 | 
						|
+	outb(0, io + UART_IER);
 | 
						|
+	/* init UART */
 | 
						|
+	/* set DLAB, speed = 115200 */
 | 
						|
+	outb(UART_LCR_DLAB | UART_LCR_WLEN7, io + UART_LCR);
 | 
						|
+	outb(1, io + UART_DLL); outb(0, io + UART_DLM);
 | 
						|
+	/* 7N1+start = 9 bits at 115200 ~ 3 bits at 44000 */
 | 
						|
+	outb(UART_LCR_WLEN7, io + UART_LCR);
 | 
						|
+	/* FIFO operation */
 | 
						|
+	outb(UART_FCR_ENABLE_FIFO, io + UART_FCR);
 | 
						|
+	/* interrupts */
 | 
						|
+	/* outb(UART_IER_RLSI|UART_IER_RDI|UART_IER_THRI, io + UART_IER); */
 | 
						|
+	outb(UART_IER_RDI, io + UART_IER);
 | 
						|
+	/* turn on UART */
 | 
						|
+	outb(UART_MCR_DTR|UART_MCR_RTS|UART_MCR_OUT2, io + UART_MCR);
 | 
						|
+#ifdef LIRC_SIR_ACTISYS_ACT200L
 | 
						|
+	init_act200();
 | 
						|
+#elif defined(LIRC_SIR_ACTISYS_ACT220L)
 | 
						|
+	init_act220();
 | 
						|
+#endif
 | 
						|
+#endif
 | 
						|
+	spin_unlock_irqrestore(&hardware_lock, flags);
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void drop_hardware(void)
 | 
						|
+{
 | 
						|
+	unsigned long flags;
 | 
						|
+
 | 
						|
+	spin_lock_irqsave(&hardware_lock, flags);
 | 
						|
+
 | 
						|
+#ifdef LIRC_ON_SA1100
 | 
						|
+	Ser2UTCR3 = 0;
 | 
						|
+
 | 
						|
+	Ser2UTCR0 = sr.utcr0;
 | 
						|
+	Ser2UTCR1 = sr.utcr1;
 | 
						|
+	Ser2UTCR2 = sr.utcr2;
 | 
						|
+	Ser2UTCR4 = sr.utcr4;
 | 
						|
+	Ser2UTCR3 = sr.utcr3;
 | 
						|
+
 | 
						|
+	Ser2HSCR0 = sr.hscr0;
 | 
						|
+#ifdef CONFIG_SA1100_BITSY
 | 
						|
+	if (machine_is_bitsy())
 | 
						|
+		clr_bitsy_egpio(EGPIO_BITSY_IR_ON);
 | 
						|
+#endif
 | 
						|
+#ifdef CONFIG_SA1100_COLLIE
 | 
						|
+	sa1100_irda_set_power_collie(0);	/* power off */
 | 
						|
+#endif
 | 
						|
+#else
 | 
						|
+	/* turn off interrupts */
 | 
						|
+	outb(0, io + UART_IER);
 | 
						|
+#endif
 | 
						|
+	spin_unlock_irqrestore(&hardware_lock, flags);
 | 
						|
+}
 | 
						|
+
 | 
						|
+/* SECTION: Initialisation */
 | 
						|
+
 | 
						|
+static int init_port(void)
 | 
						|
+{
 | 
						|
+	int retval;
 | 
						|
+
 | 
						|
+	/* get I/O port access and IRQ line */
 | 
						|
+#ifndef LIRC_ON_SA1100
 | 
						|
+	if (request_region(io, 8, LIRC_DRIVER_NAME) == NULL) {
 | 
						|
+		printk(KERN_ERR LIRC_DRIVER_NAME
 | 
						|
+		       ": i/o port 0x%.4x already in use.\n", io);
 | 
						|
+		return -EBUSY;
 | 
						|
+	}
 | 
						|
+#endif
 | 
						|
+	retval = request_irq(irq, sir_interrupt, IRQF_DISABLED,
 | 
						|
+			     LIRC_DRIVER_NAME, NULL);
 | 
						|
+	if (retval < 0) {
 | 
						|
+#               ifndef LIRC_ON_SA1100
 | 
						|
+		release_region(io, 8);
 | 
						|
+#               endif
 | 
						|
+		printk(KERN_ERR LIRC_DRIVER_NAME
 | 
						|
+			": IRQ %d already in use.\n",
 | 
						|
+			irq);
 | 
						|
+		return retval;
 | 
						|
+	}
 | 
						|
+#ifndef LIRC_ON_SA1100
 | 
						|
+	printk(KERN_INFO LIRC_DRIVER_NAME
 | 
						|
+		": I/O port 0x%.4x, IRQ %d.\n",
 | 
						|
+		io, irq);
 | 
						|
+#endif
 | 
						|
+
 | 
						|
+	init_timer(&timerlist);
 | 
						|
+	timerlist.function = sir_timeout;
 | 
						|
+	timerlist.data = 0xabadcafe;
 | 
						|
+
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void drop_port(void)
 | 
						|
+{
 | 
						|
+	free_irq(irq, NULL);
 | 
						|
+	del_timer_sync(&timerlist);
 | 
						|
+#ifndef LIRC_ON_SA1100
 | 
						|
+	release_region(io, 8);
 | 
						|
+#endif
 | 
						|
+}
 | 
						|
+
 | 
						|
+#ifdef LIRC_SIR_ACTISYS_ACT200L
 | 
						|
+/* Crystal/Cirrus CS8130 IR transceiver, used in Actisys Act200L dongle */
 | 
						|
+/* some code borrowed from Linux IRDA driver */
 | 
						|
+
 | 
						|
+/* Register 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 Divider register #1,#2 */
 | 
						|
+#define ACT200L_REG8    0x80
 | 
						|
+#define ACT200L_REG9    0x90
 | 
						|
+
 | 
						|
+#define ACT200L_2400    0x5f
 | 
						|
+#define ACT200L_9600    0x17
 | 
						|
+#define ACT200L_19200   0x0b
 | 
						|
+#define ACT200L_38400   0x05
 | 
						|
+#define ACT200L_57600   0x03
 | 
						|
+#define ACT200L_115200  0x01
 | 
						|
+
 | 
						|
+/* Register 13: Control register #3 */
 | 
						|
+#define ACT200L_REG13   0xd0
 | 
						|
+#define ACT200L_SHDW    0x01 /* Enable access to shadow registers */
 | 
						|
+
 | 
						|
+/* Register 15: Status register */
 | 
						|
+#define ACT200L_REG15   0xf0
 | 
						|
+
 | 
						|
+/* Register 21: Control register #4 */
 | 
						|
+#define ACT200L_REG21   0x50
 | 
						|
+#define ACT200L_EXCK    0x02 /* Disable clock output driver */
 | 
						|
+#define ACT200L_OSCL    0x04 /* oscillator in low power, medium accuracy mode */
 | 
						|
+
 | 
						|
+static void init_act200(void)
 | 
						|
+{
 | 
						|
+	int i;
 | 
						|
+	__u8 control[] = {
 | 
						|
+		ACT200L_REG15,
 | 
						|
+		ACT200L_REG13 | ACT200L_SHDW,
 | 
						|
+		ACT200L_REG21 | ACT200L_EXCK | ACT200L_OSCL,
 | 
						|
+		ACT200L_REG13,
 | 
						|
+		ACT200L_REG7  | ACT200L_ENPOS,
 | 
						|
+		ACT200L_REG6  | ACT200L_RS0  | ACT200L_RS1,
 | 
						|
+		ACT200L_REG5  | ACT200L_RWIDL,
 | 
						|
+		ACT200L_REG4  | ACT200L_OP0  | ACT200L_OP1 | ACT200L_BLKR,
 | 
						|
+		ACT200L_REG3  | ACT200L_B0,
 | 
						|
+		ACT200L_REG0  | ACT200L_TXEN | ACT200L_RXEN,
 | 
						|
+		ACT200L_REG8 |  (ACT200L_115200       & 0x0f),
 | 
						|
+		ACT200L_REG9 | ((ACT200L_115200 >> 4) & 0x0f),
 | 
						|
+		ACT200L_REG1 | ACT200L_LODB | ACT200L_WIDE
 | 
						|
+	};
 | 
						|
+
 | 
						|
+	/* Set DLAB 1. */
 | 
						|
+	soutp(UART_LCR, UART_LCR_DLAB | UART_LCR_WLEN8);
 | 
						|
+
 | 
						|
+	/* Set divisor to 12 => 9600 Baud */
 | 
						|
+	soutp(UART_DLM, 0);
 | 
						|
+	soutp(UART_DLL, 12);
 | 
						|
+
 | 
						|
+	/* Set DLAB 0. */
 | 
						|
+	soutp(UART_LCR, UART_LCR_WLEN8);
 | 
						|
+	/* Set divisor to 12 => 9600 Baud */
 | 
						|
+
 | 
						|
+	/* power supply */
 | 
						|
+	soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2);
 | 
						|
+	for (i = 0; i < 50; i++)
 | 
						|
+		safe_udelay(1000);
 | 
						|
+
 | 
						|
+		/* Reset the dongle : set RTS low for 25 ms */
 | 
						|
+	soutp(UART_MCR, UART_MCR_DTR|UART_MCR_OUT2);
 | 
						|
+	for (i = 0; i < 25; i++)
 | 
						|
+		udelay(1000);
 | 
						|
+
 | 
						|
+	soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2);
 | 
						|
+	udelay(100);
 | 
						|
+
 | 
						|
+	/* Clear DTR and set RTS to enter command mode */
 | 
						|
+	soutp(UART_MCR, UART_MCR_RTS|UART_MCR_OUT2);
 | 
						|
+	udelay(7);
 | 
						|
+
 | 
						|
+	/* send out the control register settings for 115K 7N1 SIR operation */
 | 
						|
+	for (i = 0; i < sizeof(control); i++) {
 | 
						|
+		soutp(UART_TX, control[i]);
 | 
						|
+		/* one byte takes ~1042 usec to transmit at 9600,8N1 */
 | 
						|
+		udelay(1500);
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	/* back to normal operation */
 | 
						|
+	soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2);
 | 
						|
+	udelay(50);
 | 
						|
+
 | 
						|
+	udelay(1500);
 | 
						|
+	soutp(UART_LCR, sinp(UART_LCR) | UART_LCR_DLAB);
 | 
						|
+
 | 
						|
+	/* Set DLAB 1. */
 | 
						|
+	soutp(UART_LCR, UART_LCR_DLAB | UART_LCR_WLEN7);
 | 
						|
+
 | 
						|
+	/* Set divisor to 1 => 115200 Baud */
 | 
						|
+	soutp(UART_DLM, 0);
 | 
						|
+	soutp(UART_DLL, 1);
 | 
						|
+
 | 
						|
+	/* Set DLAB 0. */
 | 
						|
+	soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
 | 
						|
+
 | 
						|
+	/* Set DLAB 0, 7 Bit */
 | 
						|
+	soutp(UART_LCR, UART_LCR_WLEN7);
 | 
						|
+
 | 
						|
+	/* enable interrupts */
 | 
						|
+	soutp(UART_IER, sinp(UART_IER)|UART_IER_RDI);
 | 
						|
+}
 | 
						|
+#endif
 | 
						|
+
 | 
						|
+#ifdef LIRC_SIR_ACTISYS_ACT220L
 | 
						|
+/*
 | 
						|
+ * Derived from linux IrDA driver (net/irda/actisys.c)
 | 
						|
+ * Drop me a mail for any kind of comment: maxx@spaceboyz.net
 | 
						|
+ */
 | 
						|
+
 | 
						|
+void init_act220(void)
 | 
						|
+{
 | 
						|
+	int i;
 | 
						|
+
 | 
						|
+	/* DLAB 1 */
 | 
						|
+	soutp(UART_LCR, UART_LCR_DLAB|UART_LCR_WLEN7);
 | 
						|
+
 | 
						|
+	/* 9600 baud */
 | 
						|
+	soutp(UART_DLM, 0);
 | 
						|
+	soutp(UART_DLL, 12);
 | 
						|
+
 | 
						|
+	/* DLAB 0 */
 | 
						|
+	soutp(UART_LCR, UART_LCR_WLEN7);
 | 
						|
+
 | 
						|
+	/* reset the dongle, set DTR low for 10us */
 | 
						|
+	soutp(UART_MCR, UART_MCR_RTS|UART_MCR_OUT2);
 | 
						|
+	udelay(10);
 | 
						|
+
 | 
						|
+	/* back to normal (still 9600) */
 | 
						|
+	soutp(UART_MCR, UART_MCR_DTR|UART_MCR_RTS|UART_MCR_OUT2);
 | 
						|
+
 | 
						|
+	/*
 | 
						|
+	 * send RTS pulses until we reach 115200
 | 
						|
+	 * i hope this is really the same for act220l/act220l+
 | 
						|
+	 */
 | 
						|
+	for (i = 0; i < 3; i++) {
 | 
						|
+		udelay(10);
 | 
						|
+		/* set RTS low for 10 us */
 | 
						|
+		soutp(UART_MCR, UART_MCR_DTR|UART_MCR_OUT2);
 | 
						|
+		udelay(10);
 | 
						|
+		/* set RTS high for 10 us */
 | 
						|
+		soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2);
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	/* back to normal operation */
 | 
						|
+	udelay(1500); /* better safe than sorry ;) */
 | 
						|
+
 | 
						|
+	/* Set DLAB 1. */
 | 
						|
+	soutp(UART_LCR, UART_LCR_DLAB | UART_LCR_WLEN7);
 | 
						|
+
 | 
						|
+	/* Set divisor to 1 => 115200 Baud */
 | 
						|
+	soutp(UART_DLM, 0);
 | 
						|
+	soutp(UART_DLL, 1);
 | 
						|
+
 | 
						|
+	/* Set DLAB 0, 7 Bit */
 | 
						|
+	/* The dongle doesn't seem to have any problems with operation at 7N1 */
 | 
						|
+	soutp(UART_LCR, UART_LCR_WLEN7);
 | 
						|
+
 | 
						|
+	/* enable interrupts */
 | 
						|
+	soutp(UART_IER, UART_IER_RDI);
 | 
						|
+}
 | 
						|
+#endif
 | 
						|
+
 | 
						|
+static int init_lirc_sir(void)
 | 
						|
+{
 | 
						|
+	int retval;
 | 
						|
+
 | 
						|
+	init_waitqueue_head(&lirc_read_queue);
 | 
						|
+	retval = init_port();
 | 
						|
+	if (retval < 0)
 | 
						|
+		return retval;
 | 
						|
+	init_hardware();
 | 
						|
+	printk(KERN_INFO LIRC_DRIVER_NAME
 | 
						|
+		": Installed.\n");
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+
 | 
						|
+static int __init lirc_sir_init(void)
 | 
						|
+{
 | 
						|
+	int retval;
 | 
						|
+
 | 
						|
+	retval = init_chrdev();
 | 
						|
+	if (retval < 0)
 | 
						|
+		return retval;
 | 
						|
+	retval = init_lirc_sir();
 | 
						|
+	if (retval) {
 | 
						|
+		drop_chrdev();
 | 
						|
+		return retval;
 | 
						|
+	}
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void __exit lirc_sir_exit(void)
 | 
						|
+{
 | 
						|
+	drop_hardware();
 | 
						|
+	drop_chrdev();
 | 
						|
+	drop_port();
 | 
						|
+	printk(KERN_INFO LIRC_DRIVER_NAME ": Uninstalled.\n");
 | 
						|
+}
 | 
						|
+
 | 
						|
+module_init(lirc_sir_init);
 | 
						|
+module_exit(lirc_sir_exit);
 | 
						|
+
 | 
						|
+#ifdef LIRC_SIR_TEKRAM
 | 
						|
+MODULE_DESCRIPTION("Infrared receiver driver for Tekram Irmate 210");
 | 
						|
+MODULE_AUTHOR("Christoph Bartelmus");
 | 
						|
+#elif defined(LIRC_ON_SA1100)
 | 
						|
+MODULE_DESCRIPTION("LIRC driver for StrongARM SA1100 embedded microprocessor");
 | 
						|
+MODULE_AUTHOR("Christoph Bartelmus");
 | 
						|
+#elif defined(LIRC_SIR_ACTISYS_ACT200L)
 | 
						|
+MODULE_DESCRIPTION("LIRC driver for Actisys Act200L");
 | 
						|
+MODULE_AUTHOR("Karl Bongers");
 | 
						|
+#elif defined(LIRC_SIR_ACTISYS_ACT220L)
 | 
						|
+MODULE_DESCRIPTION("LIRC driver for Actisys Act220L(+)");
 | 
						|
+MODULE_AUTHOR("Jan Roemisch");
 | 
						|
+#else
 | 
						|
+MODULE_DESCRIPTION("Infrared receiver driver for SIR type serial ports");
 | 
						|
+MODULE_AUTHOR("Milan Pikula");
 | 
						|
+#endif
 | 
						|
+MODULE_LICENSE("GPL");
 | 
						|
+
 | 
						|
+#ifdef LIRC_ON_SA1100
 | 
						|
+module_param(irq, int, S_IRUGO);
 | 
						|
+MODULE_PARM_DESC(irq, "Interrupt (16)");
 | 
						|
+#else
 | 
						|
+module_param(io, int, S_IRUGO);
 | 
						|
+MODULE_PARM_DESC(io, "I/O address base (0x3f8 or 0x2f8)");
 | 
						|
+
 | 
						|
+module_param(irq, int, S_IRUGO);
 | 
						|
+MODULE_PARM_DESC(irq, "Interrupt (4 or 3)");
 | 
						|
+
 | 
						|
+module_param(threshold, int, S_IRUGO);
 | 
						|
+MODULE_PARM_DESC(threshold, "space detection threshold (3)");
 | 
						|
+#endif
 | 
						|
+
 | 
						|
+module_param(debug, bool, S_IRUGO | S_IWUSR);
 | 
						|
+MODULE_PARM_DESC(debug, "Enable debugging messages");
 | 
						|
diff --git a/drivers/staging/lirc/lirc_streamzap.c b/drivers/staging/lirc/lirc_streamzap.c
 | 
						|
new file mode 100644
 | 
						|
index 0000000..5b46ac4
 | 
						|
--- /dev/null
 | 
						|
+++ b/drivers/staging/lirc/lirc_streamzap.c
 | 
						|
@@ -0,0 +1,821 @@
 | 
						|
+/*
 | 
						|
+ * Streamzap Remote Control driver
 | 
						|
+ *
 | 
						|
+ * Copyright (c) 2005 Christoph Bartelmus <lirc@bartelmus.de>
 | 
						|
+ *
 | 
						|
+ * This driver was based on the work of Greg Wickham and Adrian
 | 
						|
+ * Dewhurst. It was substantially rewritten to support correct signal
 | 
						|
+ * gaps and now maintains a delay buffer, which is used to present
 | 
						|
+ * consistent timing behaviour to user space applications. Without the
 | 
						|
+ * delay buffer an ugly hack would be required in lircd, which can
 | 
						|
+ * cause sluggish signal decoding in certain situations.
 | 
						|
+ *
 | 
						|
+ * This driver is based on the USB skeleton driver packaged with the
 | 
						|
+ * kernel; copyright (C) 2001-2003 Greg Kroah-Hartman (greg@kroah.com)
 | 
						|
+ *
 | 
						|
+ *  This program is free software; you can redistribute it and/or modify
 | 
						|
+ *  it under the terms of the GNU General Public License as published by
 | 
						|
+ *  the Free Software Foundation; either version 2 of the License, or
 | 
						|
+ *  (at your option) any later version.
 | 
						|
+ *
 | 
						|
+ *  This program is distributed in the hope that it will be useful,
 | 
						|
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
						|
+ *  GNU General Public License for more details.
 | 
						|
+ *
 | 
						|
+ *  You should have received a copy of the GNU General Public License
 | 
						|
+ *  along with this program; if not, write to the Free Software
 | 
						|
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | 
						|
+ */
 | 
						|
+
 | 
						|
+#include <linux/kernel.h>
 | 
						|
+#include <linux/errno.h>
 | 
						|
+#include <linux/init.h>
 | 
						|
+#include <linux/slab.h>
 | 
						|
+#include <linux/module.h>
 | 
						|
+#include <linux/smp_lock.h>
 | 
						|
+#include <linux/completion.h>
 | 
						|
+#include <linux/uaccess.h>
 | 
						|
+#include <linux/usb.h>
 | 
						|
+
 | 
						|
+#include <media/lirc.h>
 | 
						|
+#include <media/lirc_dev.h>
 | 
						|
+
 | 
						|
+#define DRIVER_VERSION	"1.28"
 | 
						|
+#define DRIVER_NAME	"lirc_streamzap"
 | 
						|
+#define DRIVER_DESC	"Streamzap Remote Control driver"
 | 
						|
+
 | 
						|
+static int debug;
 | 
						|
+
 | 
						|
+#define USB_STREAMZAP_VENDOR_ID		0x0e9c
 | 
						|
+#define USB_STREAMZAP_PRODUCT_ID	0x0000
 | 
						|
+
 | 
						|
+/* Use our own dbg macro */
 | 
						|
+#define dprintk(fmt, args...)					\
 | 
						|
+	do {							\
 | 
						|
+		if (debug)					\
 | 
						|
+			printk(KERN_DEBUG DRIVER_NAME "[%d]: "	\
 | 
						|
+			       fmt "\n", ## args);		\
 | 
						|
+	} while (0)
 | 
						|
+
 | 
						|
+/* table of devices that work with this driver */
 | 
						|
+static struct usb_device_id streamzap_table[] = {
 | 
						|
+	/* Streamzap Remote Control */
 | 
						|
+	{ USB_DEVICE(USB_STREAMZAP_VENDOR_ID, USB_STREAMZAP_PRODUCT_ID) },
 | 
						|
+	/* Terminating entry */
 | 
						|
+	{ }
 | 
						|
+};
 | 
						|
+
 | 
						|
+MODULE_DEVICE_TABLE(usb, streamzap_table);
 | 
						|
+
 | 
						|
+#define STREAMZAP_PULSE_MASK 0xf0
 | 
						|
+#define STREAMZAP_SPACE_MASK 0x0f
 | 
						|
+#define STREAMZAP_TIMEOUT    0xff
 | 
						|
+#define STREAMZAP_RESOLUTION 256
 | 
						|
+
 | 
						|
+/* number of samples buffered */
 | 
						|
+#define STREAMZAP_BUF_LEN 128
 | 
						|
+
 | 
						|
+enum StreamzapDecoderState {
 | 
						|
+	PulseSpace,
 | 
						|
+	FullPulse,
 | 
						|
+	FullSpace,
 | 
						|
+	IgnorePulse
 | 
						|
+};
 | 
						|
+
 | 
						|
+/* Structure to hold all of our device specific stuff
 | 
						|
+ *
 | 
						|
+ * some remarks regarding locking:
 | 
						|
+ * theoretically this struct can be accessed from three threads:
 | 
						|
+ *
 | 
						|
+ * - from lirc_dev through set_use_inc/set_use_dec
 | 
						|
+ *
 | 
						|
+ * - from the USB layer throuh probe/disconnect/irq
 | 
						|
+ *
 | 
						|
+ *   Careful placement of lirc_register_driver/lirc_unregister_driver
 | 
						|
+ *   calls will prevent conflicts. lirc_dev makes sure that
 | 
						|
+ *   set_use_inc/set_use_dec are not being executed and will not be
 | 
						|
+ *   called after lirc_unregister_driver returns.
 | 
						|
+ *
 | 
						|
+ * - by the timer callback
 | 
						|
+ *
 | 
						|
+ *   The timer is only running when the device is connected and the
 | 
						|
+ *   LIRC device is open. Making sure the timer is deleted by
 | 
						|
+ *   set_use_dec will make conflicts impossible.
 | 
						|
+ */
 | 
						|
+struct usb_streamzap {
 | 
						|
+
 | 
						|
+	/* usb */
 | 
						|
+	/* save off the usb device pointer */
 | 
						|
+	struct usb_device	*udev;
 | 
						|
+	/* the interface for this device */
 | 
						|
+	struct usb_interface	*interface;
 | 
						|
+
 | 
						|
+	/* buffer & dma */
 | 
						|
+	unsigned char		*buf_in;
 | 
						|
+	dma_addr_t		dma_in;
 | 
						|
+	unsigned int		buf_in_len;
 | 
						|
+
 | 
						|
+	struct usb_endpoint_descriptor *endpoint;
 | 
						|
+
 | 
						|
+	/* IRQ */
 | 
						|
+	struct urb		*urb_in;
 | 
						|
+
 | 
						|
+	/* lirc */
 | 
						|
+	struct lirc_driver	*driver;
 | 
						|
+	struct lirc_buffer	*delay_buf;
 | 
						|
+
 | 
						|
+	/* timer used to support delay buffering */
 | 
						|
+	struct timer_list	delay_timer;
 | 
						|
+	int			timer_running;
 | 
						|
+	spinlock_t		timer_lock;
 | 
						|
+
 | 
						|
+	/* tracks whether we are currently receiving some signal */
 | 
						|
+	int			idle;
 | 
						|
+	/* sum of signal lengths received since signal start */
 | 
						|
+	unsigned long		sum;
 | 
						|
+	/* start time of signal; necessary for gap tracking */
 | 
						|
+	struct timeval		signal_last;
 | 
						|
+	struct timeval		signal_start;
 | 
						|
+	enum StreamzapDecoderState decoder_state;
 | 
						|
+	struct timer_list	flush_timer;
 | 
						|
+	int			flush;
 | 
						|
+	int			in_use;
 | 
						|
+	int			timeout_enabled;
 | 
						|
+};
 | 
						|
+
 | 
						|
+
 | 
						|
+/* local function prototypes */
 | 
						|
+static int streamzap_probe(struct usb_interface *interface,
 | 
						|
+			   const struct usb_device_id *id);
 | 
						|
+static void streamzap_disconnect(struct usb_interface *interface);
 | 
						|
+static void usb_streamzap_irq(struct urb *urb);
 | 
						|
+static int streamzap_use_inc(void *data);
 | 
						|
+static void streamzap_use_dec(void *data);
 | 
						|
+static long streamzap_ioctl(struct file *filep, unsigned int cmd,
 | 
						|
+			    unsigned long arg);
 | 
						|
+static int streamzap_suspend(struct usb_interface *intf, pm_message_t message);
 | 
						|
+static int streamzap_resume(struct usb_interface *intf);
 | 
						|
+
 | 
						|
+/* usb specific object needed to register this driver with the usb subsystem */
 | 
						|
+
 | 
						|
+static struct usb_driver streamzap_driver = {
 | 
						|
+	.name =		DRIVER_NAME,
 | 
						|
+	.probe =	streamzap_probe,
 | 
						|
+	.disconnect =	streamzap_disconnect,
 | 
						|
+	.suspend =	streamzap_suspend,
 | 
						|
+	.resume =	streamzap_resume,
 | 
						|
+	.id_table =	streamzap_table,
 | 
						|
+};
 | 
						|
+
 | 
						|
+static void stop_timer(struct usb_streamzap *sz)
 | 
						|
+{
 | 
						|
+	unsigned long flags;
 | 
						|
+
 | 
						|
+	spin_lock_irqsave(&sz->timer_lock, flags);
 | 
						|
+	if (sz->timer_running) {
 | 
						|
+		sz->timer_running = 0;
 | 
						|
+		spin_unlock_irqrestore(&sz->timer_lock, flags);
 | 
						|
+		del_timer_sync(&sz->delay_timer);
 | 
						|
+	} else {
 | 
						|
+		spin_unlock_irqrestore(&sz->timer_lock, flags);
 | 
						|
+	}
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void flush_timeout(unsigned long arg)
 | 
						|
+{
 | 
						|
+	struct usb_streamzap *sz = (struct usb_streamzap *) arg;
 | 
						|
+
 | 
						|
+	/* finally start accepting data */
 | 
						|
+	sz->flush = 0;
 | 
						|
+}
 | 
						|
+static void delay_timeout(unsigned long arg)
 | 
						|
+{
 | 
						|
+	unsigned long flags;
 | 
						|
+	/* deliver data every 10 ms */
 | 
						|
+	static unsigned long timer_inc =
 | 
						|
+		(10000/(1000000/HZ)) == 0 ? 1 : (10000/(1000000/HZ));
 | 
						|
+	struct usb_streamzap *sz = (struct usb_streamzap *) arg;
 | 
						|
+	int data;
 | 
						|
+
 | 
						|
+	spin_lock_irqsave(&sz->timer_lock, flags);
 | 
						|
+
 | 
						|
+	if (!lirc_buffer_empty(sz->delay_buf) &&
 | 
						|
+	    !lirc_buffer_full(sz->driver->rbuf)) {
 | 
						|
+		lirc_buffer_read(sz->delay_buf, (unsigned char *) &data);
 | 
						|
+		lirc_buffer_write(sz->driver->rbuf, (unsigned char *) &data);
 | 
						|
+	}
 | 
						|
+	if (!lirc_buffer_empty(sz->delay_buf)) {
 | 
						|
+		while (lirc_buffer_available(sz->delay_buf) <
 | 
						|
+		       STREAMZAP_BUF_LEN / 2 &&
 | 
						|
+		       !lirc_buffer_full(sz->driver->rbuf)) {
 | 
						|
+			lirc_buffer_read(sz->delay_buf,
 | 
						|
+					   (unsigned char *) &data);
 | 
						|
+			lirc_buffer_write(sz->driver->rbuf,
 | 
						|
+					    (unsigned char *) &data);
 | 
						|
+		}
 | 
						|
+		if (sz->timer_running) {
 | 
						|
+			sz->delay_timer.expires = jiffies + timer_inc;
 | 
						|
+			add_timer(&sz->delay_timer);
 | 
						|
+		}
 | 
						|
+	} else {
 | 
						|
+		sz->timer_running = 0;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	if (!lirc_buffer_empty(sz->driver->rbuf))
 | 
						|
+		wake_up(&sz->driver->rbuf->wait_poll);
 | 
						|
+
 | 
						|
+	spin_unlock_irqrestore(&sz->timer_lock, flags);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void flush_delay_buffer(struct usb_streamzap *sz)
 | 
						|
+{
 | 
						|
+	int data;
 | 
						|
+	int empty = 1;
 | 
						|
+
 | 
						|
+	while (!lirc_buffer_empty(sz->delay_buf)) {
 | 
						|
+		empty = 0;
 | 
						|
+		lirc_buffer_read(sz->delay_buf, (unsigned char *) &data);
 | 
						|
+		if (!lirc_buffer_full(sz->driver->rbuf)) {
 | 
						|
+			lirc_buffer_write(sz->driver->rbuf,
 | 
						|
+					    (unsigned char *) &data);
 | 
						|
+		} else {
 | 
						|
+			dprintk("buffer overflow", sz->driver->minor);
 | 
						|
+		}
 | 
						|
+	}
 | 
						|
+	if (!empty)
 | 
						|
+		wake_up(&sz->driver->rbuf->wait_poll);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void push(struct usb_streamzap *sz, unsigned char *data)
 | 
						|
+{
 | 
						|
+	unsigned long flags;
 | 
						|
+
 | 
						|
+	spin_lock_irqsave(&sz->timer_lock, flags);
 | 
						|
+	if (lirc_buffer_full(sz->delay_buf)) {
 | 
						|
+		int read_data;
 | 
						|
+
 | 
						|
+		lirc_buffer_read(sz->delay_buf,
 | 
						|
+				   (unsigned char *) &read_data);
 | 
						|
+		if (!lirc_buffer_full(sz->driver->rbuf)) {
 | 
						|
+			lirc_buffer_write(sz->driver->rbuf,
 | 
						|
+					    (unsigned char *) &read_data);
 | 
						|
+		} else {
 | 
						|
+			dprintk("buffer overflow", sz->driver->minor);
 | 
						|
+		}
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	lirc_buffer_write(sz->delay_buf, data);
 | 
						|
+
 | 
						|
+	if (!sz->timer_running) {
 | 
						|
+		sz->delay_timer.expires = jiffies + HZ/10;
 | 
						|
+		add_timer(&sz->delay_timer);
 | 
						|
+		sz->timer_running = 1;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	spin_unlock_irqrestore(&sz->timer_lock, flags);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void push_full_pulse(struct usb_streamzap *sz,
 | 
						|
+			    unsigned char value)
 | 
						|
+{
 | 
						|
+	int pulse;
 | 
						|
+
 | 
						|
+	if (sz->idle) {
 | 
						|
+		long deltv;
 | 
						|
+		int tmp;
 | 
						|
+
 | 
						|
+		sz->signal_last = sz->signal_start;
 | 
						|
+		do_gettimeofday(&sz->signal_start);
 | 
						|
+
 | 
						|
+		deltv = sz->signal_start.tv_sec-sz->signal_last.tv_sec;
 | 
						|
+		if (deltv > 15) {
 | 
						|
+			/* really long time */
 | 
						|
+			tmp = LIRC_SPACE(LIRC_VALUE_MASK);
 | 
						|
+		} else {
 | 
						|
+			tmp = (int) (deltv*1000000+
 | 
						|
+					sz->signal_start.tv_usec -
 | 
						|
+					sz->signal_last.tv_usec);
 | 
						|
+			tmp -= sz->sum;
 | 
						|
+			tmp = LIRC_SPACE(tmp);
 | 
						|
+		}
 | 
						|
+		dprintk("ls %u", sz->driver->minor, tmp);
 | 
						|
+		push(sz, (char *)&tmp);
 | 
						|
+
 | 
						|
+		sz->idle = 0;
 | 
						|
+		sz->sum = 0;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	pulse = ((int) value) * STREAMZAP_RESOLUTION;
 | 
						|
+	pulse += STREAMZAP_RESOLUTION / 2;
 | 
						|
+	sz->sum += pulse;
 | 
						|
+	pulse = LIRC_PULSE(pulse);
 | 
						|
+
 | 
						|
+	dprintk("p %u", sz->driver->minor, pulse & PULSE_MASK);
 | 
						|
+	push(sz, (char *)&pulse);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void push_half_pulse(struct usb_streamzap *sz,
 | 
						|
+			    unsigned char value)
 | 
						|
+{
 | 
						|
+	push_full_pulse(sz, (value & STREAMZAP_PULSE_MASK)>>4);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void push_full_space(struct usb_streamzap *sz,
 | 
						|
+			    unsigned char value)
 | 
						|
+{
 | 
						|
+	int space;
 | 
						|
+
 | 
						|
+	space = ((int) value)*STREAMZAP_RESOLUTION;
 | 
						|
+	space += STREAMZAP_RESOLUTION/2;
 | 
						|
+	sz->sum += space;
 | 
						|
+	space = LIRC_SPACE(space);
 | 
						|
+	dprintk("s %u", sz->driver->minor, space);
 | 
						|
+	push(sz, (char *)&space);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void push_half_space(struct usb_streamzap *sz,
 | 
						|
+			    unsigned char value)
 | 
						|
+{
 | 
						|
+	push_full_space(sz, value & STREAMZAP_SPACE_MASK);
 | 
						|
+}
 | 
						|
+
 | 
						|
+/**
 | 
						|
+ * usb_streamzap_irq - IRQ handler
 | 
						|
+ *
 | 
						|
+ * This procedure is invoked on reception of data from
 | 
						|
+ * the usb remote.
 | 
						|
+ */
 | 
						|
+static void usb_streamzap_irq(struct urb *urb)
 | 
						|
+{
 | 
						|
+	struct usb_streamzap *sz;
 | 
						|
+	int		len;
 | 
						|
+	unsigned int	i = 0;
 | 
						|
+
 | 
						|
+	if (!urb)
 | 
						|
+		return;
 | 
						|
+
 | 
						|
+	sz = urb->context;
 | 
						|
+	len = urb->actual_length;
 | 
						|
+
 | 
						|
+	switch (urb->status) {
 | 
						|
+	case -ECONNRESET:
 | 
						|
+	case -ENOENT:
 | 
						|
+	case -ESHUTDOWN:
 | 
						|
+		/*
 | 
						|
+		 * this urb is terminated, clean up.
 | 
						|
+		 * sz might already be invalid at this point
 | 
						|
+		 */
 | 
						|
+		dprintk("urb status: %d", -1, urb->status);
 | 
						|
+		return;
 | 
						|
+	default:
 | 
						|
+		break;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	dprintk("received %d", sz->driver->minor, urb->actual_length);
 | 
						|
+	if (!sz->flush) {
 | 
						|
+		for (i = 0; i < urb->actual_length; i++) {
 | 
						|
+			dprintk("%d: %x", sz->driver->minor,
 | 
						|
+				i, (unsigned char) sz->buf_in[i]);
 | 
						|
+			switch (sz->decoder_state) {
 | 
						|
+			case PulseSpace:
 | 
						|
+				if ((sz->buf_in[i]&STREAMZAP_PULSE_MASK) ==
 | 
						|
+				    STREAMZAP_PULSE_MASK) {
 | 
						|
+					sz->decoder_state = FullPulse;
 | 
						|
+					continue;
 | 
						|
+				} else if ((sz->buf_in[i]&STREAMZAP_SPACE_MASK)
 | 
						|
+					   == STREAMZAP_SPACE_MASK) {
 | 
						|
+					push_half_pulse(sz, sz->buf_in[i]);
 | 
						|
+					sz->decoder_state = FullSpace;
 | 
						|
+					continue;
 | 
						|
+				} else {
 | 
						|
+					push_half_pulse(sz, sz->buf_in[i]);
 | 
						|
+					push_half_space(sz, sz->buf_in[i]);
 | 
						|
+				}
 | 
						|
+				break;
 | 
						|
+			case FullPulse:
 | 
						|
+				push_full_pulse(sz, sz->buf_in[i]);
 | 
						|
+				sz->decoder_state = IgnorePulse;
 | 
						|
+				break;
 | 
						|
+			case FullSpace:
 | 
						|
+				if (sz->buf_in[i] == STREAMZAP_TIMEOUT) {
 | 
						|
+					sz->idle = 1;
 | 
						|
+					stop_timer(sz);
 | 
						|
+					if (sz->timeout_enabled) {
 | 
						|
+						int timeout =
 | 
						|
+							LIRC_TIMEOUT
 | 
						|
+							(STREAMZAP_TIMEOUT *
 | 
						|
+							STREAMZAP_RESOLUTION);
 | 
						|
+						push(sz, (char *)&timeout);
 | 
						|
+					}
 | 
						|
+					flush_delay_buffer(sz);
 | 
						|
+				} else
 | 
						|
+					push_full_space(sz, sz->buf_in[i]);
 | 
						|
+				sz->decoder_state = PulseSpace;
 | 
						|
+				break;
 | 
						|
+			case IgnorePulse:
 | 
						|
+				if ((sz->buf_in[i]&STREAMZAP_SPACE_MASK) ==
 | 
						|
+				    STREAMZAP_SPACE_MASK) {
 | 
						|
+					sz->decoder_state = FullSpace;
 | 
						|
+					continue;
 | 
						|
+				}
 | 
						|
+				push_half_space(sz, sz->buf_in[i]);
 | 
						|
+				sz->decoder_state = PulseSpace;
 | 
						|
+				break;
 | 
						|
+			}
 | 
						|
+		}
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	usb_submit_urb(urb, GFP_ATOMIC);
 | 
						|
+
 | 
						|
+	return;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static struct file_operations streamzap_fops = {
 | 
						|
+	.owner		= THIS_MODULE,
 | 
						|
+	.unlocked_ioctl	= streamzap_ioctl,
 | 
						|
+	.read		= lirc_dev_fop_read,
 | 
						|
+	.write		= lirc_dev_fop_write,
 | 
						|
+	.poll		= lirc_dev_fop_poll,
 | 
						|
+	.open		= lirc_dev_fop_open,
 | 
						|
+	.release	= lirc_dev_fop_close,
 | 
						|
+};
 | 
						|
+
 | 
						|
+
 | 
						|
+/**
 | 
						|
+ *	streamzap_probe
 | 
						|
+ *
 | 
						|
+ *	Called by usb-core to associated with a candidate device
 | 
						|
+ *	On any failure the return value is the ERROR
 | 
						|
+ *	On success return 0
 | 
						|
+ */
 | 
						|
+static int streamzap_probe(struct usb_interface *interface,
 | 
						|
+			   const struct usb_device_id *id)
 | 
						|
+{
 | 
						|
+	struct usb_device *udev = interface_to_usbdev(interface);
 | 
						|
+	struct usb_host_interface *iface_host;
 | 
						|
+	struct usb_streamzap *sz;
 | 
						|
+	struct lirc_driver *driver;
 | 
						|
+	struct lirc_buffer *lirc_buf;
 | 
						|
+	struct lirc_buffer *delay_buf;
 | 
						|
+	char buf[63], name[128] = "";
 | 
						|
+	int retval = -ENOMEM;
 | 
						|
+	int minor = 0;
 | 
						|
+
 | 
						|
+	/* Allocate space for device driver specific data */
 | 
						|
+	sz = kzalloc(sizeof(struct usb_streamzap), GFP_KERNEL);
 | 
						|
+	if (sz == NULL)
 | 
						|
+		return -ENOMEM;
 | 
						|
+
 | 
						|
+	sz->udev = udev;
 | 
						|
+	sz->interface = interface;
 | 
						|
+
 | 
						|
+	/* Check to ensure endpoint information matches requirements */
 | 
						|
+	iface_host = interface->cur_altsetting;
 | 
						|
+
 | 
						|
+	if (iface_host->desc.bNumEndpoints != 1) {
 | 
						|
+		err("%s: Unexpected desc.bNumEndpoints (%d)", __func__,
 | 
						|
+		    iface_host->desc.bNumEndpoints);
 | 
						|
+		retval = -ENODEV;
 | 
						|
+		goto free_sz;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	sz->endpoint = &(iface_host->endpoint[0].desc);
 | 
						|
+	if ((sz->endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
 | 
						|
+	    != USB_DIR_IN) {
 | 
						|
+		err("%s: endpoint doesn't match input device 02%02x",
 | 
						|
+		    __func__, sz->endpoint->bEndpointAddress);
 | 
						|
+		retval = -ENODEV;
 | 
						|
+		goto free_sz;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	if ((sz->endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
 | 
						|
+	    != USB_ENDPOINT_XFER_INT) {
 | 
						|
+		err("%s: endpoint attributes don't match xfer 02%02x",
 | 
						|
+		    __func__, sz->endpoint->bmAttributes);
 | 
						|
+		retval = -ENODEV;
 | 
						|
+		goto free_sz;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	if (sz->endpoint->wMaxPacketSize == 0) {
 | 
						|
+		err("%s: endpoint message size==0? ", __func__);
 | 
						|
+		retval = -ENODEV;
 | 
						|
+		goto free_sz;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	/* Allocate the USB buffer and IRQ URB */
 | 
						|
+
 | 
						|
+	sz->buf_in_len = sz->endpoint->wMaxPacketSize;
 | 
						|
+	sz->buf_in = usb_alloc_coherent(sz->udev, sz->buf_in_len,
 | 
						|
+				      GFP_ATOMIC, &sz->dma_in);
 | 
						|
+	if (sz->buf_in == NULL)
 | 
						|
+		goto free_sz;
 | 
						|
+
 | 
						|
+	sz->urb_in = usb_alloc_urb(0, GFP_KERNEL);
 | 
						|
+	if (sz->urb_in == NULL)
 | 
						|
+		goto free_sz;
 | 
						|
+
 | 
						|
+	/* Connect this device to the LIRC sub-system */
 | 
						|
+	driver = kzalloc(sizeof(struct lirc_driver), GFP_KERNEL);
 | 
						|
+	if (!driver)
 | 
						|
+		goto free_sz;
 | 
						|
+
 | 
						|
+	lirc_buf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
 | 
						|
+	if (!lirc_buf)
 | 
						|
+		goto free_driver;
 | 
						|
+	if (lirc_buffer_init(lirc_buf, sizeof(int), STREAMZAP_BUF_LEN))
 | 
						|
+		goto kfree_lirc_buf;
 | 
						|
+
 | 
						|
+	delay_buf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
 | 
						|
+	if (!delay_buf)
 | 
						|
+		goto free_lirc_buf;
 | 
						|
+	if (lirc_buffer_init(delay_buf, sizeof(int), STREAMZAP_BUF_LEN))
 | 
						|
+		goto kfree_delay_buf;
 | 
						|
+
 | 
						|
+	sz->driver = driver;
 | 
						|
+	strcpy(sz->driver->name, DRIVER_NAME);
 | 
						|
+	sz->driver->minor = -1;
 | 
						|
+	sz->driver->sample_rate = 0;
 | 
						|
+	sz->driver->code_length = sizeof(int) * 8;
 | 
						|
+	sz->driver->features = LIRC_CAN_REC_MODE2 |
 | 
						|
+		LIRC_CAN_GET_REC_RESOLUTION |
 | 
						|
+		LIRC_CAN_SET_REC_TIMEOUT;
 | 
						|
+	sz->driver->data = sz;
 | 
						|
+	sz->driver->min_timeout = STREAMZAP_TIMEOUT * STREAMZAP_RESOLUTION;
 | 
						|
+	sz->driver->max_timeout = STREAMZAP_TIMEOUT * STREAMZAP_RESOLUTION;
 | 
						|
+	sz->driver->rbuf = lirc_buf;
 | 
						|
+	sz->delay_buf = delay_buf;
 | 
						|
+	sz->driver->set_use_inc = &streamzap_use_inc;
 | 
						|
+	sz->driver->set_use_dec = &streamzap_use_dec;
 | 
						|
+	sz->driver->fops = &streamzap_fops;
 | 
						|
+	sz->driver->dev = &interface->dev;
 | 
						|
+	sz->driver->owner = THIS_MODULE;
 | 
						|
+
 | 
						|
+	sz->idle = 1;
 | 
						|
+	sz->decoder_state = PulseSpace;
 | 
						|
+	init_timer(&sz->delay_timer);
 | 
						|
+	sz->delay_timer.function = delay_timeout;
 | 
						|
+	sz->delay_timer.data = (unsigned long) sz;
 | 
						|
+	sz->timer_running = 0;
 | 
						|
+	spin_lock_init(&sz->timer_lock);
 | 
						|
+
 | 
						|
+	init_timer(&sz->flush_timer);
 | 
						|
+	sz->flush_timer.function = flush_timeout;
 | 
						|
+	sz->flush_timer.data = (unsigned long) sz;
 | 
						|
+	/* Complete final initialisations */
 | 
						|
+
 | 
						|
+	usb_fill_int_urb(sz->urb_in, udev,
 | 
						|
+		usb_rcvintpipe(udev, sz->endpoint->bEndpointAddress),
 | 
						|
+		sz->buf_in, sz->buf_in_len, usb_streamzap_irq, sz,
 | 
						|
+		sz->endpoint->bInterval);
 | 
						|
+	sz->urb_in->transfer_dma = sz->dma_in;
 | 
						|
+	sz->urb_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
 | 
						|
+
 | 
						|
+	if (udev->descriptor.iManufacturer
 | 
						|
+	    && usb_string(udev, udev->descriptor.iManufacturer,
 | 
						|
+			  buf, sizeof(buf)) > 0)
 | 
						|
+		strlcpy(name, buf, sizeof(name));
 | 
						|
+
 | 
						|
+	if (udev->descriptor.iProduct
 | 
						|
+	    && usb_string(udev, udev->descriptor.iProduct,
 | 
						|
+			  buf, sizeof(buf)) > 0)
 | 
						|
+		snprintf(name + strlen(name), sizeof(name) - strlen(name),
 | 
						|
+			 " %s", buf);
 | 
						|
+
 | 
						|
+	minor = lirc_register_driver(driver);
 | 
						|
+
 | 
						|
+	if (minor < 0)
 | 
						|
+		goto free_delay_buf;
 | 
						|
+
 | 
						|
+	sz->driver->minor = minor;
 | 
						|
+
 | 
						|
+	usb_set_intfdata(interface, sz);
 | 
						|
+
 | 
						|
+	printk(KERN_INFO DRIVER_NAME "[%d]: %s on usb%d:%d attached\n",
 | 
						|
+	       sz->driver->minor, name,
 | 
						|
+	       udev->bus->busnum, sz->udev->devnum);
 | 
						|
+
 | 
						|
+	return 0;
 | 
						|
+
 | 
						|
+free_delay_buf:
 | 
						|
+	lirc_buffer_free(sz->delay_buf);
 | 
						|
+kfree_delay_buf:
 | 
						|
+	kfree(delay_buf);
 | 
						|
+free_lirc_buf:
 | 
						|
+	lirc_buffer_free(sz->driver->rbuf);
 | 
						|
+kfree_lirc_buf:
 | 
						|
+	kfree(lirc_buf);
 | 
						|
+free_driver:
 | 
						|
+	kfree(driver);
 | 
						|
+free_sz:
 | 
						|
+	if (retval == -ENOMEM)
 | 
						|
+		err("Out of memory");
 | 
						|
+
 | 
						|
+	if (sz) {
 | 
						|
+		usb_free_urb(sz->urb_in);
 | 
						|
+		usb_free_coherent(udev, sz->buf_in_len, sz->buf_in, sz->dma_in);
 | 
						|
+		kfree(sz);
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	return retval;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int streamzap_use_inc(void *data)
 | 
						|
+{
 | 
						|
+	struct usb_streamzap *sz = data;
 | 
						|
+
 | 
						|
+	if (!sz) {
 | 
						|
+		dprintk("%s called with no context", -1, __func__);
 | 
						|
+		return -EINVAL;
 | 
						|
+	}
 | 
						|
+	dprintk("set use inc", sz->driver->minor);
 | 
						|
+
 | 
						|
+	lirc_buffer_clear(sz->driver->rbuf);
 | 
						|
+	lirc_buffer_clear(sz->delay_buf);
 | 
						|
+
 | 
						|
+	sz->flush_timer.expires = jiffies + HZ;
 | 
						|
+	sz->flush = 1;
 | 
						|
+	add_timer(&sz->flush_timer);
 | 
						|
+
 | 
						|
+	sz->urb_in->dev = sz->udev;
 | 
						|
+	if (usb_submit_urb(sz->urb_in, GFP_ATOMIC)) {
 | 
						|
+		dprintk("open result = -EIO error submitting urb",
 | 
						|
+			sz->driver->minor);
 | 
						|
+		return -EIO;
 | 
						|
+	}
 | 
						|
+	sz->in_use++;
 | 
						|
+
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void streamzap_use_dec(void *data)
 | 
						|
+{
 | 
						|
+	struct usb_streamzap *sz = data;
 | 
						|
+
 | 
						|
+	if (!sz) {
 | 
						|
+		dprintk("%s called with no context", -1, __func__);
 | 
						|
+		return;
 | 
						|
+	}
 | 
						|
+	dprintk("set use dec", sz->driver->minor);
 | 
						|
+
 | 
						|
+	if (sz->flush) {
 | 
						|
+		sz->flush = 0;
 | 
						|
+		del_timer_sync(&sz->flush_timer);
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	usb_kill_urb(sz->urb_in);
 | 
						|
+
 | 
						|
+	stop_timer(sz);
 | 
						|
+
 | 
						|
+	sz->in_use--;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static long streamzap_ioctl(struct file *filep, unsigned int cmd,
 | 
						|
+			    unsigned long arg)
 | 
						|
+{
 | 
						|
+	int result = 0;
 | 
						|
+	int val;
 | 
						|
+	struct usb_streamzap *sz = lirc_get_pdata(filep);
 | 
						|
+
 | 
						|
+	switch (cmd) {
 | 
						|
+	case LIRC_GET_REC_RESOLUTION:
 | 
						|
+		result = put_user(STREAMZAP_RESOLUTION, (unsigned int *) arg);
 | 
						|
+		break;
 | 
						|
+	case LIRC_SET_REC_TIMEOUT:
 | 
						|
+		result = get_user(val, (int *)arg);
 | 
						|
+		if (result == 0) {
 | 
						|
+			if (val == STREAMZAP_TIMEOUT * STREAMZAP_RESOLUTION)
 | 
						|
+				sz->timeout_enabled = 1;
 | 
						|
+			else if (val == 0)
 | 
						|
+				sz->timeout_enabled = 0;
 | 
						|
+			else
 | 
						|
+				result = -EINVAL;
 | 
						|
+		}
 | 
						|
+		break;
 | 
						|
+	default:
 | 
						|
+		return lirc_dev_fop_ioctl(filep, cmd, arg);
 | 
						|
+	}
 | 
						|
+	return result;
 | 
						|
+}
 | 
						|
+
 | 
						|
+/**
 | 
						|
+ * streamzap_disconnect
 | 
						|
+ *
 | 
						|
+ * Called by the usb core when the device is removed from the system.
 | 
						|
+ *
 | 
						|
+ * This routine guarantees that the driver will not submit any more urbs
 | 
						|
+ * by clearing dev->udev.  It is also supposed to terminate any currently
 | 
						|
+ * active urbs.  Unfortunately, usb_bulk_msg(), used in streamzap_read(),
 | 
						|
+ * does not provide any way to do this.
 | 
						|
+ */
 | 
						|
+static void streamzap_disconnect(struct usb_interface *interface)
 | 
						|
+{
 | 
						|
+	struct usb_streamzap *sz;
 | 
						|
+	int errnum;
 | 
						|
+	int minor;
 | 
						|
+
 | 
						|
+	sz = usb_get_intfdata(interface);
 | 
						|
+
 | 
						|
+	/* unregister from the LIRC sub-system */
 | 
						|
+
 | 
						|
+	errnum = lirc_unregister_driver(sz->driver->minor);
 | 
						|
+	if (errnum != 0)
 | 
						|
+		dprintk("error in lirc_unregister: (returned %d)",
 | 
						|
+			sz->driver->minor, errnum);
 | 
						|
+
 | 
						|
+	lirc_buffer_free(sz->delay_buf);
 | 
						|
+	lirc_buffer_free(sz->driver->rbuf);
 | 
						|
+
 | 
						|
+	/* unregister from the USB sub-system */
 | 
						|
+
 | 
						|
+	usb_free_urb(sz->urb_in);
 | 
						|
+
 | 
						|
+	usb_free_coherent(sz->udev, sz->buf_in_len, sz->buf_in, sz->dma_in);
 | 
						|
+
 | 
						|
+	minor = sz->driver->minor;
 | 
						|
+	kfree(sz->driver->rbuf);
 | 
						|
+	kfree(sz->driver);
 | 
						|
+	kfree(sz->delay_buf);
 | 
						|
+	kfree(sz);
 | 
						|
+
 | 
						|
+	printk(KERN_INFO DRIVER_NAME "[%d]: disconnected\n", minor);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int streamzap_suspend(struct usb_interface *intf, pm_message_t message)
 | 
						|
+{
 | 
						|
+	struct usb_streamzap *sz = usb_get_intfdata(intf);
 | 
						|
+
 | 
						|
+	printk(KERN_INFO DRIVER_NAME "[%d]: suspend\n", sz->driver->minor);
 | 
						|
+	if (sz->in_use) {
 | 
						|
+		if (sz->flush) {
 | 
						|
+			sz->flush = 0;
 | 
						|
+			del_timer_sync(&sz->flush_timer);
 | 
						|
+		}
 | 
						|
+
 | 
						|
+		stop_timer(sz);
 | 
						|
+
 | 
						|
+		usb_kill_urb(sz->urb_in);
 | 
						|
+	}
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int streamzap_resume(struct usb_interface *intf)
 | 
						|
+{
 | 
						|
+	struct usb_streamzap *sz = usb_get_intfdata(intf);
 | 
						|
+
 | 
						|
+	lirc_buffer_clear(sz->driver->rbuf);
 | 
						|
+	lirc_buffer_clear(sz->delay_buf);
 | 
						|
+
 | 
						|
+	if (sz->in_use) {
 | 
						|
+		sz->flush_timer.expires = jiffies + HZ;
 | 
						|
+		sz->flush = 1;
 | 
						|
+		add_timer(&sz->flush_timer);
 | 
						|
+
 | 
						|
+		sz->urb_in->dev = sz->udev;
 | 
						|
+		if (usb_submit_urb(sz->urb_in, GFP_ATOMIC)) {
 | 
						|
+			dprintk("open result = -EIO error submitting urb",
 | 
						|
+				sz->driver->minor);
 | 
						|
+			return -EIO;
 | 
						|
+		}
 | 
						|
+	}
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+/**
 | 
						|
+ *	usb_streamzap_init
 | 
						|
+ */
 | 
						|
+static int __init usb_streamzap_init(void)
 | 
						|
+{
 | 
						|
+	int result;
 | 
						|
+
 | 
						|
+	/* register this driver with the USB subsystem */
 | 
						|
+	result = usb_register(&streamzap_driver);
 | 
						|
+
 | 
						|
+	if (result) {
 | 
						|
+		err("usb_register failed. Error number %d",
 | 
						|
+		    result);
 | 
						|
+		return result;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	printk(KERN_INFO DRIVER_NAME " " DRIVER_VERSION " registered\n");
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+/**
 | 
						|
+ *	usb_streamzap_exit
 | 
						|
+ */
 | 
						|
+static void __exit usb_streamzap_exit(void)
 | 
						|
+{
 | 
						|
+	usb_deregister(&streamzap_driver);
 | 
						|
+}
 | 
						|
+
 | 
						|
+
 | 
						|
+module_init(usb_streamzap_init);
 | 
						|
+module_exit(usb_streamzap_exit);
 | 
						|
+
 | 
						|
+MODULE_AUTHOR("Christoph Bartelmus, Greg Wickham, Adrian Dewhurst");
 | 
						|
+MODULE_DESCRIPTION(DRIVER_DESC);
 | 
						|
+MODULE_LICENSE("GPL");
 | 
						|
+
 | 
						|
+module_param(debug, bool, S_IRUGO | S_IWUSR);
 | 
						|
+MODULE_PARM_DESC(debug, "Enable debugging messages");
 | 
						|
diff --git a/drivers/staging/lirc/lirc_ttusbir.c b/drivers/staging/lirc/lirc_ttusbir.c
 | 
						|
new file mode 100644
 | 
						|
index 0000000..1f1da47
 | 
						|
--- /dev/null
 | 
						|
+++ b/drivers/staging/lirc/lirc_ttusbir.c
 | 
						|
@@ -0,0 +1,397 @@
 | 
						|
+/*
 | 
						|
+ * lirc_ttusbir.c
 | 
						|
+ *
 | 
						|
+ * lirc_ttusbir - LIRC device driver for the TechnoTrend USB IR Receiver
 | 
						|
+ *
 | 
						|
+ * Copyright (C) 2007 Stefan Macher <st_maker-lirc@yahoo.de>
 | 
						|
+ *
 | 
						|
+ * This LIRC driver provides access to the TechnoTrend USB IR Receiver.
 | 
						|
+ * The receiver delivers the IR signal as raw sampled true/false data in
 | 
						|
+ * isochronous USB packets each of size 128 byte.
 | 
						|
+ * Currently the driver reduces the sampling rate by factor of 8 as this
 | 
						|
+ * is still more than enough to decode RC-5 - others should be analyzed.
 | 
						|
+ * But the driver does not rely on RC-5 it should be able to decode every
 | 
						|
+ * IR signal that is not too fast.
 | 
						|
+ */
 | 
						|
+
 | 
						|
+/*
 | 
						|
+ *  This program is free software; you can redistribute it and/or modify
 | 
						|
+ *  it under the terms of the GNU General Public License as published by
 | 
						|
+ *  the Free Software Foundation; either version 2 of the License, or
 | 
						|
+ *  (at your option) any later version.
 | 
						|
+ *
 | 
						|
+ *  This program is distributed in the hope that it will be useful,
 | 
						|
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
						|
+ *  GNU General Public License for more details.
 | 
						|
+ *
 | 
						|
+ *  You should have received a copy of the GNU General Public License
 | 
						|
+ *  along with this program; if not, write to the Free Software
 | 
						|
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | 
						|
+ */
 | 
						|
+
 | 
						|
+#include <linux/version.h>
 | 
						|
+#include <linux/kernel.h>
 | 
						|
+#include <linux/init.h>
 | 
						|
+#include <linux/module.h>
 | 
						|
+#include <linux/errno.h>
 | 
						|
+#include <linux/slab.h>
 | 
						|
+#include <linux/usb.h>
 | 
						|
+
 | 
						|
+#include <media/lirc.h>
 | 
						|
+#include <media/lirc_dev.h>
 | 
						|
+
 | 
						|
+MODULE_DESCRIPTION("TechnoTrend USB IR device driver for LIRC");
 | 
						|
+MODULE_AUTHOR("Stefan Macher (st_maker-lirc@yahoo.de)");
 | 
						|
+MODULE_LICENSE("GPL");
 | 
						|
+
 | 
						|
+/* #define DEBUG */
 | 
						|
+#ifdef DEBUG
 | 
						|
+#define DPRINTK printk
 | 
						|
+#else
 | 
						|
+#define DPRINTK(_x_, a...)
 | 
						|
+#endif
 | 
						|
+
 | 
						|
+/* function declarations */
 | 
						|
+static int probe(struct usb_interface *intf, const struct usb_device_id *id);
 | 
						|
+static void disconnect(struct usb_interface *intf);
 | 
						|
+static void urb_complete(struct urb *urb);
 | 
						|
+static int set_use_inc(void *data);
 | 
						|
+static void set_use_dec(void *data);
 | 
						|
+
 | 
						|
+static int num_urbs = 2;
 | 
						|
+module_param(num_urbs, int, S_IRUGO);
 | 
						|
+MODULE_PARM_DESC(num_urbs,
 | 
						|
+		 "Number of URBs in queue. Try to increase to 4 in case "
 | 
						|
+		 "of problems (default: 2; minimum: 2)");
 | 
						|
+
 | 
						|
+/* table of devices that work with this driver */
 | 
						|
+static struct usb_device_id device_id_table[] = {
 | 
						|
+	/* TechnoTrend USB IR Receiver */
 | 
						|
+	{ USB_DEVICE(0x0B48, 0x2003) },
 | 
						|
+	/* Terminating entry */
 | 
						|
+	{ }
 | 
						|
+};
 | 
						|
+MODULE_DEVICE_TABLE(usb, device_id_table);
 | 
						|
+
 | 
						|
+/* USB driver definition */
 | 
						|
+static struct usb_driver usb_driver = {
 | 
						|
+	.name = "TTUSBIR",
 | 
						|
+	.id_table = &(device_id_table[0]),
 | 
						|
+	.probe = probe,
 | 
						|
+	.disconnect = disconnect,
 | 
						|
+};
 | 
						|
+
 | 
						|
+/* USB device definition */
 | 
						|
+struct ttusbir_device {
 | 
						|
+	struct usb_driver *usb_driver;
 | 
						|
+	struct usb_device *udev;
 | 
						|
+	struct usb_interface *interf;
 | 
						|
+	struct usb_class_driver class_driver;
 | 
						|
+	unsigned int ifnum; /* Interface number to use */
 | 
						|
+	unsigned int alt_setting; /* alternate setting to use */
 | 
						|
+	unsigned int endpoint; /* Endpoint to use */
 | 
						|
+	struct urb **urb; /* num_urb URB pointers*/
 | 
						|
+	char **buffer; /* 128 byte buffer for each URB */
 | 
						|
+	struct lirc_buffer rbuf; /* Buffer towards LIRC */
 | 
						|
+	struct lirc_driver driver;
 | 
						|
+	int minor;
 | 
						|
+	int last_pulse; /* remembers if last received byte was pulse or space */
 | 
						|
+	int last_num; /* remembers how many last bytes appeared */
 | 
						|
+	int opened;
 | 
						|
+};
 | 
						|
+
 | 
						|
+/*** LIRC specific functions ***/
 | 
						|
+static int set_use_inc(void *data)
 | 
						|
+{
 | 
						|
+	int i, retval;
 | 
						|
+	struct ttusbir_device *ttusbir = data;
 | 
						|
+
 | 
						|
+	DPRINTK("Sending first URBs\n");
 | 
						|
+	/* @TODO Do I need to check if I am already opened */
 | 
						|
+	ttusbir->opened = 1;
 | 
						|
+
 | 
						|
+	for (i = 0; i < num_urbs; i++) {
 | 
						|
+		retval = usb_submit_urb(ttusbir->urb[i], GFP_KERNEL);
 | 
						|
+		if (retval) {
 | 
						|
+			err("%s: usb_submit_urb failed on urb %d",
 | 
						|
+			    __func__, i);
 | 
						|
+			return retval;
 | 
						|
+		}
 | 
						|
+	}
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void set_use_dec(void *data)
 | 
						|
+{
 | 
						|
+	struct ttusbir_device *ttusbir = data;
 | 
						|
+
 | 
						|
+	DPRINTK("Device closed\n");
 | 
						|
+
 | 
						|
+	ttusbir->opened = 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+/*** USB specific functions ***/
 | 
						|
+
 | 
						|
+/*
 | 
						|
+ * This mapping table is used to do a very simple filtering of the
 | 
						|
+ * input signal.
 | 
						|
+ * For a value with at least 4 bits set it returns 0xFF otherwise
 | 
						|
+ * 0x00.  For faster IR signals this can not be used. But for RC-5 we
 | 
						|
+ * still have about 14 samples per pulse/space, i.e. we sample with 14
 | 
						|
+ * times higher frequency than the signal frequency
 | 
						|
+ */
 | 
						|
+const unsigned char map_table[] =
 | 
						|
+{
 | 
						|
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 | 
						|
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 | 
						|
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 | 
						|
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
 | 
						|
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 | 
						|
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
 | 
						|
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
 | 
						|
+	0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
 | 
						|
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 | 
						|
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
 | 
						|
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
 | 
						|
+	0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
 | 
						|
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
 | 
						|
+	0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
 | 
						|
+	0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
 | 
						|
+	0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
 | 
						|
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 | 
						|
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
 | 
						|
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
 | 
						|
+	0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
 | 
						|
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
 | 
						|
+	0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
 | 
						|
+	0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
 | 
						|
+	0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
 | 
						|
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
 | 
						|
+	0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
 | 
						|
+	0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
 | 
						|
+	0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
 | 
						|
+	0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
 | 
						|
+	0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
 | 
						|
+	0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
 | 
						|
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
 | 
						|
+};
 | 
						|
+
 | 
						|
+static void urb_complete(struct urb *urb)
 | 
						|
+{
 | 
						|
+	struct ttusbir_device *ttusbir;
 | 
						|
+	unsigned char *buf;
 | 
						|
+	int i;
 | 
						|
+	int l;
 | 
						|
+
 | 
						|
+	ttusbir = urb->context;
 | 
						|
+
 | 
						|
+	if (!ttusbir->opened)
 | 
						|
+		return;
 | 
						|
+
 | 
						|
+	buf = (unsigned char *)urb->transfer_buffer;
 | 
						|
+
 | 
						|
+	for (i = 0; i < 128; i++) {
 | 
						|
+		/* Here we do the filtering and some kind of down sampling */
 | 
						|
+		buf[i] = ~map_table[buf[i]];
 | 
						|
+		if (ttusbir->last_pulse == buf[i]) {
 | 
						|
+			if (ttusbir->last_num < PULSE_MASK/63)
 | 
						|
+				ttusbir->last_num++;
 | 
						|
+		/*
 | 
						|
+		 * else we are in a idle period and do not need to
 | 
						|
+		 * increment any longer
 | 
						|
+		 */
 | 
						|
+		} else {
 | 
						|
+			l = ttusbir->last_num * 62; /* about 62 = us/byte */
 | 
						|
+			if (ttusbir->last_pulse) /* pulse or space? */
 | 
						|
+				l |= PULSE_BIT;
 | 
						|
+			if (!lirc_buffer_full(&ttusbir->rbuf)) {
 | 
						|
+				lirc_buffer_write(&ttusbir->rbuf, (void *)&l);
 | 
						|
+				wake_up_interruptible(&ttusbir->rbuf.wait_poll);
 | 
						|
+			}
 | 
						|
+			ttusbir->last_num = 0;
 | 
						|
+			ttusbir->last_pulse = buf[i];
 | 
						|
+		}
 | 
						|
+	}
 | 
						|
+	usb_submit_urb(urb, GFP_ATOMIC); /* keep data rolling :-) */
 | 
						|
+}
 | 
						|
+
 | 
						|
+/*
 | 
						|
+ * Called whenever the USB subsystem thinks we could be the right driver
 | 
						|
+ * to handle this device
 | 
						|
+ */
 | 
						|
+static int probe(struct usb_interface *intf, const struct usb_device_id *id)
 | 
						|
+{
 | 
						|
+	int alt_set, endp;
 | 
						|
+	int found = 0;
 | 
						|
+	int i, j;
 | 
						|
+	int struct_size;
 | 
						|
+	struct usb_host_interface *host_interf;
 | 
						|
+	struct usb_interface_descriptor *interf_desc;
 | 
						|
+	struct usb_host_endpoint *host_endpoint;
 | 
						|
+	struct ttusbir_device *ttusbir;
 | 
						|
+
 | 
						|
+	DPRINTK("Module ttusbir probe\n");
 | 
						|
+
 | 
						|
+	/* To reduce memory fragmentation we use only one allocation */
 | 
						|
+	struct_size =  sizeof(struct ttusbir_device) +
 | 
						|
+		(sizeof(struct urb *) * num_urbs) +
 | 
						|
+		(sizeof(char *) * num_urbs) +
 | 
						|
+		(num_urbs * 128);
 | 
						|
+	ttusbir = kzalloc(struct_size, GFP_KERNEL);
 | 
						|
+	if (!ttusbir)
 | 
						|
+		return -ENOMEM;
 | 
						|
+
 | 
						|
+	ttusbir->urb = (struct urb **)((char *)ttusbir +
 | 
						|
+				      sizeof(struct ttusbir_device));
 | 
						|
+	ttusbir->buffer = (char **)((char *)ttusbir->urb +
 | 
						|
+				   (sizeof(struct urb *) * num_urbs));
 | 
						|
+	for (i = 0; i < num_urbs; i++)
 | 
						|
+		ttusbir->buffer[i] = (char *)ttusbir->buffer +
 | 
						|
+			(sizeof(char *)*num_urbs) + (i * 128);
 | 
						|
+
 | 
						|
+	ttusbir->usb_driver = &usb_driver;
 | 
						|
+	ttusbir->alt_setting = -1;
 | 
						|
+	/* @TODO check if error can be returned */
 | 
						|
+	ttusbir->udev = usb_get_dev(interface_to_usbdev(intf));
 | 
						|
+	ttusbir->interf = intf;
 | 
						|
+	ttusbir->last_pulse = 0x00;
 | 
						|
+	ttusbir->last_num = 0;
 | 
						|
+
 | 
						|
+	/*
 | 
						|
+	 * Now look for interface setting we can handle
 | 
						|
+	 * We are searching for the alt setting where end point
 | 
						|
+	 * 0x82 has max packet size 16
 | 
						|
+	 */
 | 
						|
+	for (alt_set = 0; alt_set < intf->num_altsetting && !found; alt_set++) {
 | 
						|
+		host_interf = &intf->altsetting[alt_set];
 | 
						|
+		interf_desc = &host_interf->desc;
 | 
						|
+		for (endp = 0; endp < interf_desc->bNumEndpoints; endp++) {
 | 
						|
+			host_endpoint = &host_interf->endpoint[endp];
 | 
						|
+			if ((host_endpoint->desc.bEndpointAddress == 0x82) &&
 | 
						|
+			    (host_endpoint->desc.wMaxPacketSize == 0x10)) {
 | 
						|
+				ttusbir->alt_setting = alt_set;
 | 
						|
+				ttusbir->endpoint = endp;
 | 
						|
+				found = 1;
 | 
						|
+				break;
 | 
						|
+			}
 | 
						|
+		}
 | 
						|
+	}
 | 
						|
+	if (ttusbir->alt_setting != -1)
 | 
						|
+		DPRINTK("alt setting: %d\n", ttusbir->alt_setting);
 | 
						|
+	else {
 | 
						|
+		err("Could not find alternate setting\n");
 | 
						|
+		kfree(ttusbir);
 | 
						|
+		return -EINVAL;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	/* OK lets setup this interface setting */
 | 
						|
+	usb_set_interface(ttusbir->udev, 0, ttusbir->alt_setting);
 | 
						|
+
 | 
						|
+	/* Store device info in interface structure */
 | 
						|
+	usb_set_intfdata(intf, ttusbir);
 | 
						|
+
 | 
						|
+	/* Register as a LIRC driver */
 | 
						|
+	if (lirc_buffer_init(&ttusbir->rbuf, sizeof(int), 256) < 0) {
 | 
						|
+		err("Could not get memory for LIRC data buffer\n");
 | 
						|
+		usb_set_intfdata(intf, NULL);
 | 
						|
+		kfree(ttusbir);
 | 
						|
+		return -ENOMEM;
 | 
						|
+	}
 | 
						|
+	strcpy(ttusbir->driver.name, "TTUSBIR");
 | 
						|
+	ttusbir->driver.minor = -1;
 | 
						|
+	ttusbir->driver.code_length = 1;
 | 
						|
+	ttusbir->driver.sample_rate = 0;
 | 
						|
+	ttusbir->driver.data = ttusbir;
 | 
						|
+	ttusbir->driver.add_to_buf = NULL;
 | 
						|
+	ttusbir->driver.rbuf = &ttusbir->rbuf;
 | 
						|
+	ttusbir->driver.set_use_inc = set_use_inc;
 | 
						|
+	ttusbir->driver.set_use_dec = set_use_dec;
 | 
						|
+	ttusbir->driver.dev = &intf->dev;
 | 
						|
+	ttusbir->driver.owner = THIS_MODULE;
 | 
						|
+	ttusbir->driver.features = LIRC_CAN_REC_MODE2;
 | 
						|
+	ttusbir->minor = lirc_register_driver(&ttusbir->driver);
 | 
						|
+	if (ttusbir->minor < 0) {
 | 
						|
+		err("Error registering as LIRC driver\n");
 | 
						|
+		usb_set_intfdata(intf, NULL);
 | 
						|
+		lirc_buffer_free(&ttusbir->rbuf);
 | 
						|
+		kfree(ttusbir);
 | 
						|
+		return -EIO;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	/* Allocate and setup the URB that we will use to talk to the device */
 | 
						|
+	for (i = 0; i < num_urbs; i++) {
 | 
						|
+		ttusbir->urb[i] = usb_alloc_urb(8, GFP_KERNEL);
 | 
						|
+		if (!ttusbir->urb[i]) {
 | 
						|
+			err("Could not allocate memory for the URB\n");
 | 
						|
+			for (j = i - 1; j >= 0; j--)
 | 
						|
+				kfree(ttusbir->urb[j]);
 | 
						|
+			lirc_buffer_free(&ttusbir->rbuf);
 | 
						|
+			lirc_unregister_driver(ttusbir->minor);
 | 
						|
+			kfree(ttusbir);
 | 
						|
+			usb_set_intfdata(intf, NULL);
 | 
						|
+			return -ENOMEM;
 | 
						|
+		}
 | 
						|
+		ttusbir->urb[i]->dev = ttusbir->udev;
 | 
						|
+		ttusbir->urb[i]->context = ttusbir;
 | 
						|
+		ttusbir->urb[i]->pipe = usb_rcvisocpipe(ttusbir->udev,
 | 
						|
+							ttusbir->endpoint);
 | 
						|
+		ttusbir->urb[i]->interval = 1;
 | 
						|
+		ttusbir->urb[i]->transfer_flags = URB_ISO_ASAP;
 | 
						|
+		ttusbir->urb[i]->transfer_buffer = &ttusbir->buffer[i][0];
 | 
						|
+		ttusbir->urb[i]->complete = urb_complete;
 | 
						|
+		ttusbir->urb[i]->number_of_packets = 8;
 | 
						|
+		ttusbir->urb[i]->transfer_buffer_length = 128;
 | 
						|
+		for (j = 0; j < 8; j++) {
 | 
						|
+			ttusbir->urb[i]->iso_frame_desc[j].offset = j*16;
 | 
						|
+			ttusbir->urb[i]->iso_frame_desc[j].length = 16;
 | 
						|
+		}
 | 
						|
+	}
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+/**
 | 
						|
+ * Called when the driver is unloaded or the device is unplugged
 | 
						|
+ */
 | 
						|
+static void disconnect(struct usb_interface *intf)
 | 
						|
+{
 | 
						|
+	int i;
 | 
						|
+	struct ttusbir_device *ttusbir;
 | 
						|
+
 | 
						|
+	DPRINTK("Module ttusbir disconnect\n");
 | 
						|
+
 | 
						|
+	ttusbir = (struct ttusbir_device *) usb_get_intfdata(intf);
 | 
						|
+	usb_set_intfdata(intf, NULL);
 | 
						|
+	lirc_unregister_driver(ttusbir->minor);
 | 
						|
+	DPRINTK("unregistered\n");
 | 
						|
+
 | 
						|
+	for (i = 0; i < num_urbs; i++) {
 | 
						|
+		usb_kill_urb(ttusbir->urb[i]);
 | 
						|
+		usb_free_urb(ttusbir->urb[i]);
 | 
						|
+	}
 | 
						|
+	DPRINTK("URBs killed\n");
 | 
						|
+	lirc_buffer_free(&ttusbir->rbuf);
 | 
						|
+	kfree(ttusbir);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int ttusbir_init_module(void)
 | 
						|
+{
 | 
						|
+	int result;
 | 
						|
+
 | 
						|
+	DPRINTK(KERN_DEBUG "Module ttusbir init\n");
 | 
						|
+
 | 
						|
+	/* register this driver with the USB subsystem */
 | 
						|
+	result = usb_register(&usb_driver);
 | 
						|
+	if (result)
 | 
						|
+		err("usb_register failed. Error number %d", result);
 | 
						|
+	return result;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void ttusbir_exit_module(void)
 | 
						|
+{
 | 
						|
+	printk(KERN_DEBUG "Module ttusbir exit\n");
 | 
						|
+	usb_deregister(&usb_driver);
 | 
						|
+}
 | 
						|
+
 | 
						|
+module_init(ttusbir_init_module);
 | 
						|
+module_exit(ttusbir_exit_module);
 | 
						|
diff --git a/drivers/staging/lirc/lirc_zilog.c b/drivers/staging/lirc/lirc_zilog.c
 | 
						|
new file mode 100644
 | 
						|
index 0000000..1b013bf
 | 
						|
--- /dev/null
 | 
						|
+++ b/drivers/staging/lirc/lirc_zilog.c
 | 
						|
@@ -0,0 +1,1387 @@
 | 
						|
+/*
 | 
						|
+ * i2c IR lirc driver for devices with zilog IR processors
 | 
						|
+ *
 | 
						|
+ * Copyright (c) 2000 Gerd Knorr <kraxel@goldbach.in-berlin.de>
 | 
						|
+ * modified for PixelView (BT878P+W/FM) by
 | 
						|
+ *      Michal Kochanowicz <mkochano@pld.org.pl>
 | 
						|
+ *      Christoph Bartelmus <lirc@bartelmus.de>
 | 
						|
+ * modified for KNC ONE TV Station/Anubis Typhoon TView Tuner by
 | 
						|
+ *      Ulrich Mueller <ulrich.mueller42@web.de>
 | 
						|
+ * modified for Asus TV-Box and Creative/VisionTek BreakOut-Box by
 | 
						|
+ *      Stefan Jahn <stefan@lkcc.org>
 | 
						|
+ * modified for inclusion into kernel sources by
 | 
						|
+ *      Jerome Brock <jbrock@users.sourceforge.net>
 | 
						|
+ * modified for Leadtek Winfast PVR2000 by
 | 
						|
+ *      Thomas Reitmayr (treitmayr@yahoo.com)
 | 
						|
+ * modified for Hauppauge PVR-150 IR TX device by
 | 
						|
+ *      Mark Weaver <mark@npsl.co.uk>
 | 
						|
+ * changed name from lirc_pvr150 to lirc_zilog, works on more than pvr-150
 | 
						|
+ *	Jarod Wilson <jarod@redhat.com>
 | 
						|
+ *
 | 
						|
+ * parts are cut&pasted from the lirc_i2c.c driver
 | 
						|
+ *
 | 
						|
+ *  This program is free software; you can redistribute it and/or modify
 | 
						|
+ *  it under the terms of the GNU General Public License as published by
 | 
						|
+ *  the Free Software Foundation; either version 2 of the License, or
 | 
						|
+ *  (at your option) any later version.
 | 
						|
+ *
 | 
						|
+ *  This program is distributed in the hope that it will be useful,
 | 
						|
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
						|
+ *  GNU General Public License for more details.
 | 
						|
+ *
 | 
						|
+ *  You should have received a copy of the GNU General Public License
 | 
						|
+ *  along with this program; if not, write to the Free Software
 | 
						|
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | 
						|
+ *
 | 
						|
+ */
 | 
						|
+
 | 
						|
+
 | 
						|
+#include <linux/version.h>
 | 
						|
+#include <linux/module.h>
 | 
						|
+#include <linux/kmod.h>
 | 
						|
+#include <linux/kernel.h>
 | 
						|
+#include <linux/sched.h>
 | 
						|
+#include <linux/fs.h>
 | 
						|
+#include <linux/poll.h>
 | 
						|
+#include <linux/string.h>
 | 
						|
+#include <linux/timer.h>
 | 
						|
+#include <linux/delay.h>
 | 
						|
+#include <linux/completion.h>
 | 
						|
+#include <linux/errno.h>
 | 
						|
+#include <linux/slab.h>
 | 
						|
+#include <linux/i2c.h>
 | 
						|
+#include <linux/firmware.h>
 | 
						|
+#include <linux/vmalloc.h>
 | 
						|
+
 | 
						|
+#include <linux/mutex.h>
 | 
						|
+#include <linux/kthread.h>
 | 
						|
+
 | 
						|
+#include <media/lirc_dev.h>
 | 
						|
+#include <media/lirc.h>
 | 
						|
+
 | 
						|
+struct IR {
 | 
						|
+	struct lirc_driver l;
 | 
						|
+
 | 
						|
+	/* Device info */
 | 
						|
+	struct mutex ir_lock;
 | 
						|
+	int open;
 | 
						|
+
 | 
						|
+	/* RX device */
 | 
						|
+	struct i2c_client c_rx;
 | 
						|
+	int have_rx;
 | 
						|
+
 | 
						|
+	/* RX device buffer & lock */
 | 
						|
+	struct lirc_buffer buf;
 | 
						|
+	struct mutex buf_lock;
 | 
						|
+
 | 
						|
+	/* RX polling thread data */
 | 
						|
+	struct completion *t_notify;
 | 
						|
+	struct completion *t_notify2;
 | 
						|
+	int shutdown;
 | 
						|
+	struct task_struct *task;
 | 
						|
+
 | 
						|
+	/* RX read data */
 | 
						|
+	unsigned char b[3];
 | 
						|
+
 | 
						|
+	/* TX device */
 | 
						|
+	struct i2c_client c_tx;
 | 
						|
+	int need_boot;
 | 
						|
+	int have_tx;
 | 
						|
+};
 | 
						|
+
 | 
						|
+/* Minor -> data mapping */
 | 
						|
+static struct IR *ir_devices[MAX_IRCTL_DEVICES];
 | 
						|
+
 | 
						|
+/* Block size for IR transmitter */
 | 
						|
+#define TX_BLOCK_SIZE	99
 | 
						|
+
 | 
						|
+/* Hauppauge IR transmitter data */
 | 
						|
+struct tx_data_struct {
 | 
						|
+	/* Boot block */
 | 
						|
+	unsigned char *boot_data;
 | 
						|
+
 | 
						|
+	/* Start of binary data block */
 | 
						|
+	unsigned char *datap;
 | 
						|
+
 | 
						|
+	/* End of binary data block */
 | 
						|
+	unsigned char *endp;
 | 
						|
+
 | 
						|
+	/* Number of installed codesets */
 | 
						|
+	unsigned int num_code_sets;
 | 
						|
+
 | 
						|
+	/* Pointers to codesets */
 | 
						|
+	unsigned char **code_sets;
 | 
						|
+
 | 
						|
+	/* Global fixed data template */
 | 
						|
+	int fixed[TX_BLOCK_SIZE];
 | 
						|
+};
 | 
						|
+
 | 
						|
+static struct tx_data_struct *tx_data;
 | 
						|
+static struct mutex tx_data_lock;
 | 
						|
+
 | 
						|
+#define zilog_notify(s, args...) printk(KERN_NOTICE KBUILD_MODNAME ": " s, \
 | 
						|
+					## args)
 | 
						|
+#define zilog_error(s, args...) printk(KERN_ERR KBUILD_MODNAME ": " s, ## args)
 | 
						|
+
 | 
						|
+#define ZILOG_HAUPPAUGE_IR_RX_NAME "Zilog/Hauppauge IR RX"
 | 
						|
+#define ZILOG_HAUPPAUGE_IR_TX_NAME "Zilog/Hauppauge IR TX"
 | 
						|
+
 | 
						|
+/* module parameters */
 | 
						|
+static int debug;	/* debug output */
 | 
						|
+static int disable_rx;	/* disable RX device */
 | 
						|
+static int disable_tx;	/* disable TX device */
 | 
						|
+static int minor = -1;	/* minor number */
 | 
						|
+
 | 
						|
+#define dprintk(fmt, args...)						\
 | 
						|
+	do {								\
 | 
						|
+		if (debug)						\
 | 
						|
+			printk(KERN_DEBUG KBUILD_MODNAME ": " fmt,	\
 | 
						|
+				 ## args);				\
 | 
						|
+	} while (0)
 | 
						|
+
 | 
						|
+static int add_to_buf(struct IR *ir)
 | 
						|
+{
 | 
						|
+	__u16 code;
 | 
						|
+	unsigned char codes[2];
 | 
						|
+	unsigned char keybuf[6];
 | 
						|
+	int got_data = 0;
 | 
						|
+	int ret;
 | 
						|
+	int failures = 0;
 | 
						|
+	unsigned char sendbuf[1] = { 0 };
 | 
						|
+
 | 
						|
+	if (lirc_buffer_full(&ir->buf)) {
 | 
						|
+		dprintk("buffer overflow\n");
 | 
						|
+		return -EOVERFLOW;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	/*
 | 
						|
+	 * service the device as long as it is returning
 | 
						|
+	 * data and we have space
 | 
						|
+	 */
 | 
						|
+	do {
 | 
						|
+		/*
 | 
						|
+		 * Lock i2c bus for the duration.  RX/TX chips interfere so
 | 
						|
+		 * this is worth it
 | 
						|
+		 */
 | 
						|
+		mutex_lock(&ir->ir_lock);
 | 
						|
+
 | 
						|
+		/*
 | 
						|
+		 * Send random "poll command" (?)  Windows driver does this
 | 
						|
+		 * and it is a good point to detect chip failure.
 | 
						|
+		 */
 | 
						|
+		ret = i2c_master_send(&ir->c_rx, sendbuf, 1);
 | 
						|
+		if (ret != 1) {
 | 
						|
+			zilog_error("i2c_master_send failed with %d\n",	ret);
 | 
						|
+			if (failures >= 3) {
 | 
						|
+				mutex_unlock(&ir->ir_lock);
 | 
						|
+				zilog_error("unable to read from the IR chip "
 | 
						|
+					    "after 3 resets, giving up\n");
 | 
						|
+				return ret;
 | 
						|
+			}
 | 
						|
+
 | 
						|
+			/* Looks like the chip crashed, reset it */
 | 
						|
+			zilog_error("polling the IR receiver chip failed, "
 | 
						|
+				    "trying reset\n");
 | 
						|
+
 | 
						|
+			set_current_state(TASK_UNINTERRUPTIBLE);
 | 
						|
+			schedule_timeout((100 * HZ + 999) / 1000);
 | 
						|
+			ir->need_boot = 1;
 | 
						|
+
 | 
						|
+			++failures;
 | 
						|
+			mutex_unlock(&ir->ir_lock);
 | 
						|
+			continue;
 | 
						|
+		}
 | 
						|
+
 | 
						|
+		ret = i2c_master_recv(&ir->c_rx, keybuf, sizeof(keybuf));
 | 
						|
+		mutex_unlock(&ir->ir_lock);
 | 
						|
+		if (ret != sizeof(keybuf)) {
 | 
						|
+			zilog_error("i2c_master_recv failed with %d -- "
 | 
						|
+				    "keeping last read buffer\n", ret);
 | 
						|
+		} else {
 | 
						|
+			ir->b[0] = keybuf[3];
 | 
						|
+			ir->b[1] = keybuf[4];
 | 
						|
+			ir->b[2] = keybuf[5];
 | 
						|
+			dprintk("key (0x%02x/0x%02x)\n", ir->b[0], ir->b[1]);
 | 
						|
+		}
 | 
						|
+
 | 
						|
+		/* key pressed ? */
 | 
						|
+#ifdef I2C_HW_B_HDPVR
 | 
						|
+		if (ir->c_rx.adapter->id == I2C_HW_B_HDPVR) {
 | 
						|
+			if (got_data && (keybuf[0] == 0x80))
 | 
						|
+				return 0;
 | 
						|
+			else if (got_data && (keybuf[0] == 0x00))
 | 
						|
+				return -ENODATA;
 | 
						|
+		} else if ((ir->b[0] & 0x80) == 0)
 | 
						|
+#else
 | 
						|
+		if ((ir->b[0] & 0x80) == 0)
 | 
						|
+#endif
 | 
						|
+			return got_data ? 0 : -ENODATA;
 | 
						|
+
 | 
						|
+		/* look what we have */
 | 
						|
+		code = (((__u16)ir->b[0] & 0x7f) << 6) | (ir->b[1] >> 2);
 | 
						|
+
 | 
						|
+		codes[0] = (code >> 8) & 0xff;
 | 
						|
+		codes[1] = code & 0xff;
 | 
						|
+
 | 
						|
+		/* return it */
 | 
						|
+		lirc_buffer_write(&ir->buf, codes);
 | 
						|
+		++got_data;
 | 
						|
+	} while (!lirc_buffer_full(&ir->buf));
 | 
						|
+
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+/*
 | 
						|
+ * Main function of the polling thread -- from lirc_dev.
 | 
						|
+ * We don't fit the LIRC model at all anymore.  This is horrible, but
 | 
						|
+ * basically we have a single RX/TX device with a nasty failure mode
 | 
						|
+ * that needs to be accounted for across the pair.  lirc lets us provide
 | 
						|
+ * fops, but prevents us from using the internal polling, etc. if we do
 | 
						|
+ * so.  Hence the replication.  Might be neater to extend the LIRC model
 | 
						|
+ * to account for this but I'd think it's a very special case of seriously
 | 
						|
+ * messed up hardware.
 | 
						|
+ */
 | 
						|
+static int lirc_thread(void *arg)
 | 
						|
+{
 | 
						|
+	struct IR *ir = arg;
 | 
						|
+
 | 
						|
+	if (ir->t_notify != NULL)
 | 
						|
+		complete(ir->t_notify);
 | 
						|
+
 | 
						|
+	dprintk("poll thread started\n");
 | 
						|
+
 | 
						|
+	do {
 | 
						|
+		if (ir->open) {
 | 
						|
+			set_current_state(TASK_INTERRUPTIBLE);
 | 
						|
+
 | 
						|
+			/*
 | 
						|
+			 * This is ~113*2 + 24 + jitter (2*repeat gap +
 | 
						|
+			 * code length).  We use this interval as the chip
 | 
						|
+			 * resets every time you poll it (bad!).  This is
 | 
						|
+			 * therefore just sufficient to catch all of the
 | 
						|
+			 * button presses.  It makes the remote much more
 | 
						|
+			 * responsive.  You can see the difference by
 | 
						|
+			 * running irw and holding down a button.  With
 | 
						|
+			 * 100ms, the old polling interval, you'll notice
 | 
						|
+			 * breaks in the repeat sequence corresponding to
 | 
						|
+			 * lost keypresses.
 | 
						|
+			 */
 | 
						|
+			schedule_timeout((260 * HZ) / 1000);
 | 
						|
+			if (ir->shutdown)
 | 
						|
+				break;
 | 
						|
+			if (!add_to_buf(ir))
 | 
						|
+				wake_up_interruptible(&ir->buf.wait_poll);
 | 
						|
+		} else {
 | 
						|
+			/* if device not opened so we can sleep half a second */
 | 
						|
+			set_current_state(TASK_INTERRUPTIBLE);
 | 
						|
+			schedule_timeout(HZ/2);
 | 
						|
+		}
 | 
						|
+	} while (!ir->shutdown);
 | 
						|
+
 | 
						|
+	if (ir->t_notify2 != NULL)
 | 
						|
+		wait_for_completion(ir->t_notify2);
 | 
						|
+
 | 
						|
+	ir->task = NULL;
 | 
						|
+	if (ir->t_notify != NULL)
 | 
						|
+		complete(ir->t_notify);
 | 
						|
+
 | 
						|
+	dprintk("poll thread ended\n");
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int set_use_inc(void *data)
 | 
						|
+{
 | 
						|
+	struct IR *ir = data;
 | 
						|
+
 | 
						|
+	if (ir->l.owner == NULL || try_module_get(ir->l.owner) == 0)
 | 
						|
+		return -ENODEV;
 | 
						|
+
 | 
						|
+	/* lock bttv in memory while /dev/lirc is in use  */
 | 
						|
+	/*
 | 
						|
+	 * this is completely broken code. lirc_unregister_driver()
 | 
						|
+	 * must be possible even when the device is open
 | 
						|
+	 */
 | 
						|
+	if (ir->c_rx.addr)
 | 
						|
+		i2c_use_client(&ir->c_rx);
 | 
						|
+	if (ir->c_tx.addr)
 | 
						|
+		i2c_use_client(&ir->c_tx);
 | 
						|
+
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void set_use_dec(void *data)
 | 
						|
+{
 | 
						|
+	struct IR *ir = data;
 | 
						|
+
 | 
						|
+	if (ir->c_rx.addr)
 | 
						|
+		i2c_release_client(&ir->c_rx);
 | 
						|
+	if (ir->c_tx.addr)
 | 
						|
+		i2c_release_client(&ir->c_tx);
 | 
						|
+	if (ir->l.owner != NULL)
 | 
						|
+		module_put(ir->l.owner);
 | 
						|
+}
 | 
						|
+
 | 
						|
+/* safe read of a uint32 (always network byte order) */
 | 
						|
+static int read_uint32(unsigned char **data,
 | 
						|
+				     unsigned char *endp, unsigned int *val)
 | 
						|
+{
 | 
						|
+	if (*data + 4 > endp)
 | 
						|
+		return 0;
 | 
						|
+	*val = ((*data)[0] << 24) | ((*data)[1] << 16) |
 | 
						|
+	       ((*data)[2] << 8) | (*data)[3];
 | 
						|
+	*data += 4;
 | 
						|
+	return 1;
 | 
						|
+}
 | 
						|
+
 | 
						|
+/* safe read of a uint8 */
 | 
						|
+static int read_uint8(unsigned char **data,
 | 
						|
+				    unsigned char *endp, unsigned char *val)
 | 
						|
+{
 | 
						|
+	if (*data + 1 > endp)
 | 
						|
+		return 0;
 | 
						|
+	*val = *((*data)++);
 | 
						|
+	return 1;
 | 
						|
+}
 | 
						|
+
 | 
						|
+/* safe skipping of N bytes */
 | 
						|
+static int skip(unsigned char **data,
 | 
						|
+			      unsigned char *endp, unsigned int distance)
 | 
						|
+{
 | 
						|
+	if (*data + distance > endp)
 | 
						|
+		return 0;
 | 
						|
+	*data += distance;
 | 
						|
+	return 1;
 | 
						|
+}
 | 
						|
+
 | 
						|
+/* decompress key data into the given buffer */
 | 
						|
+static int get_key_data(unsigned char *buf,
 | 
						|
+			     unsigned int codeset, unsigned int key)
 | 
						|
+{
 | 
						|
+	unsigned char *data, *endp, *diffs, *key_block;
 | 
						|
+	unsigned char keys, ndiffs, id;
 | 
						|
+	unsigned int base, lim, pos, i;
 | 
						|
+
 | 
						|
+	/* Binary search for the codeset */
 | 
						|
+	for (base = 0, lim = tx_data->num_code_sets; lim; lim >>= 1) {
 | 
						|
+		pos = base + (lim >> 1);
 | 
						|
+		data = tx_data->code_sets[pos];
 | 
						|
+
 | 
						|
+		if (!read_uint32(&data, tx_data->endp, &i))
 | 
						|
+			goto corrupt;
 | 
						|
+
 | 
						|
+		if (i == codeset)
 | 
						|
+			break;
 | 
						|
+		else if (codeset > i) {
 | 
						|
+			base = pos + 1;
 | 
						|
+			--lim;
 | 
						|
+		}
 | 
						|
+	}
 | 
						|
+	/* Not found? */
 | 
						|
+	if (!lim)
 | 
						|
+		return -EPROTO;
 | 
						|
+
 | 
						|
+	/* Set end of data block */
 | 
						|
+	endp = pos < tx_data->num_code_sets - 1 ?
 | 
						|
+		tx_data->code_sets[pos + 1] : tx_data->endp;
 | 
						|
+
 | 
						|
+	/* Read the block header */
 | 
						|
+	if (!read_uint8(&data, endp, &keys) ||
 | 
						|
+	    !read_uint8(&data, endp, &ndiffs) ||
 | 
						|
+	    ndiffs > TX_BLOCK_SIZE || keys == 0)
 | 
						|
+		goto corrupt;
 | 
						|
+
 | 
						|
+	/* Save diffs & skip */
 | 
						|
+	diffs = data;
 | 
						|
+	if (!skip(&data, endp, ndiffs))
 | 
						|
+		goto corrupt;
 | 
						|
+
 | 
						|
+	/* Read the id of the first key */
 | 
						|
+	if (!read_uint8(&data, endp, &id))
 | 
						|
+		goto corrupt;
 | 
						|
+
 | 
						|
+	/* Unpack the first key's data */
 | 
						|
+	for (i = 0; i < TX_BLOCK_SIZE; ++i) {
 | 
						|
+		if (tx_data->fixed[i] == -1) {
 | 
						|
+			if (!read_uint8(&data, endp, &buf[i]))
 | 
						|
+				goto corrupt;
 | 
						|
+		} else {
 | 
						|
+			buf[i] = (unsigned char)tx_data->fixed[i];
 | 
						|
+		}
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	/* Early out key found/not found */
 | 
						|
+	if (key == id)
 | 
						|
+		return 0;
 | 
						|
+	if (keys == 1)
 | 
						|
+		return -EPROTO;
 | 
						|
+
 | 
						|
+	/* Sanity check */
 | 
						|
+	key_block = data;
 | 
						|
+	if (!skip(&data, endp, (keys - 1) * (ndiffs + 1)))
 | 
						|
+		goto corrupt;
 | 
						|
+
 | 
						|
+	/* Binary search for the key */
 | 
						|
+	for (base = 0, lim = keys - 1; lim; lim >>= 1) {
 | 
						|
+		/* Seek to block */
 | 
						|
+		unsigned char *key_data;
 | 
						|
+		pos = base + (lim >> 1);
 | 
						|
+		key_data = key_block + (ndiffs + 1) * pos;
 | 
						|
+
 | 
						|
+		if (*key_data == key) {
 | 
						|
+			/* skip key id */
 | 
						|
+			++key_data;
 | 
						|
+
 | 
						|
+			/* found, so unpack the diffs */
 | 
						|
+			for (i = 0; i < ndiffs; ++i) {
 | 
						|
+				unsigned char val;
 | 
						|
+				if (!read_uint8(&key_data, endp, &val) ||
 | 
						|
+				    diffs[i] >= TX_BLOCK_SIZE)
 | 
						|
+					goto corrupt;
 | 
						|
+				buf[diffs[i]] = val;
 | 
						|
+			}
 | 
						|
+
 | 
						|
+			return 0;
 | 
						|
+		} else if (key > *key_data) {
 | 
						|
+			base = pos + 1;
 | 
						|
+			--lim;
 | 
						|
+		}
 | 
						|
+	}
 | 
						|
+	/* Key not found */
 | 
						|
+	return -EPROTO;
 | 
						|
+
 | 
						|
+corrupt:
 | 
						|
+	zilog_error("firmware is corrupt\n");
 | 
						|
+	return -EFAULT;
 | 
						|
+}
 | 
						|
+
 | 
						|
+/* send a block of data to the IR TX device */
 | 
						|
+static int send_data_block(struct IR *ir, unsigned char *data_block)
 | 
						|
+{
 | 
						|
+	int i, j, ret;
 | 
						|
+	unsigned char buf[5];
 | 
						|
+
 | 
						|
+	for (i = 0; i < TX_BLOCK_SIZE;) {
 | 
						|
+		int tosend = TX_BLOCK_SIZE - i;
 | 
						|
+		if (tosend > 4)
 | 
						|
+			tosend = 4;
 | 
						|
+		buf[0] = (unsigned char)(i + 1);
 | 
						|
+		for (j = 0; j < tosend; ++j)
 | 
						|
+			buf[1 + j] = data_block[i + j];
 | 
						|
+		dprintk("%02x %02x %02x %02x %02x",
 | 
						|
+			buf[0], buf[1], buf[2], buf[3], buf[4]);
 | 
						|
+		ret = i2c_master_send(&ir->c_tx, buf, tosend + 1);
 | 
						|
+		if (ret != tosend + 1) {
 | 
						|
+			zilog_error("i2c_master_send failed with %d\n", ret);
 | 
						|
+			return ret < 0 ? ret : -EFAULT;
 | 
						|
+		}
 | 
						|
+		i += tosend;
 | 
						|
+	}
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+/* send boot data to the IR TX device */
 | 
						|
+static int send_boot_data(struct IR *ir)
 | 
						|
+{
 | 
						|
+	int ret;
 | 
						|
+	unsigned char buf[4];
 | 
						|
+
 | 
						|
+	/* send the boot block */
 | 
						|
+	ret = send_data_block(ir, tx_data->boot_data);
 | 
						|
+	if (ret != 0)
 | 
						|
+		return ret;
 | 
						|
+
 | 
						|
+	/* kick it off? */
 | 
						|
+	buf[0] = 0x00;
 | 
						|
+	buf[1] = 0x20;
 | 
						|
+	ret = i2c_master_send(&ir->c_tx, buf, 2);
 | 
						|
+	if (ret != 2) {
 | 
						|
+		zilog_error("i2c_master_send failed with %d\n", ret);
 | 
						|
+		return ret < 0 ? ret : -EFAULT;
 | 
						|
+	}
 | 
						|
+	ret = i2c_master_send(&ir->c_tx, buf, 1);
 | 
						|
+	if (ret != 1) {
 | 
						|
+		zilog_error("i2c_master_send failed with %d\n", ret);
 | 
						|
+		return ret < 0 ? ret : -EFAULT;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	/* Here comes the firmware version... (hopefully) */
 | 
						|
+	ret = i2c_master_recv(&ir->c_tx, buf, 4);
 | 
						|
+	if (ret != 4) {
 | 
						|
+		zilog_error("i2c_master_recv failed with %d\n", ret);
 | 
						|
+		return 0;
 | 
						|
+	}
 | 
						|
+	if (buf[0] != 0x80) {
 | 
						|
+		zilog_error("unexpected IR TX response: %02x\n", buf[0]);
 | 
						|
+		return 0;
 | 
						|
+	}
 | 
						|
+	zilog_notify("Zilog/Hauppauge IR blaster firmware version "
 | 
						|
+		     "%d.%d.%d loaded\n", buf[1], buf[2], buf[3]);
 | 
						|
+
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+/* unload "firmware", lock held */
 | 
						|
+static void fw_unload_locked(void)
 | 
						|
+{
 | 
						|
+	if (tx_data) {
 | 
						|
+		if (tx_data->code_sets)
 | 
						|
+			vfree(tx_data->code_sets);
 | 
						|
+
 | 
						|
+		if (tx_data->datap)
 | 
						|
+			vfree(tx_data->datap);
 | 
						|
+
 | 
						|
+		vfree(tx_data);
 | 
						|
+		tx_data = NULL;
 | 
						|
+		dprintk("successfully unloaded IR blaster firmware\n");
 | 
						|
+	}
 | 
						|
+}
 | 
						|
+
 | 
						|
+/* unload "firmware" for the IR TX device */
 | 
						|
+static void fw_unload(void)
 | 
						|
+{
 | 
						|
+	mutex_lock(&tx_data_lock);
 | 
						|
+	fw_unload_locked();
 | 
						|
+	mutex_unlock(&tx_data_lock);
 | 
						|
+}
 | 
						|
+
 | 
						|
+/* load "firmware" for the IR TX device */
 | 
						|
+static int fw_load(struct IR *ir)
 | 
						|
+{
 | 
						|
+	int ret;
 | 
						|
+	unsigned int i;
 | 
						|
+	unsigned char *data, version, num_global_fixed;
 | 
						|
+	const struct firmware *fw_entry;
 | 
						|
+
 | 
						|
+	/* Already loaded? */
 | 
						|
+	mutex_lock(&tx_data_lock);
 | 
						|
+	if (tx_data) {
 | 
						|
+		ret = 0;
 | 
						|
+		goto out;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	/* Request codeset data file */
 | 
						|
+	ret = request_firmware(&fw_entry, "haup-ir-blaster.bin", &ir->c_tx.dev);
 | 
						|
+	if (ret != 0) {
 | 
						|
+		zilog_error("firmware haup-ir-blaster.bin not available "
 | 
						|
+			    "(%d)\n", ret);
 | 
						|
+		ret = ret < 0 ? ret : -EFAULT;
 | 
						|
+		goto out;
 | 
						|
+	}
 | 
						|
+	dprintk("firmware of size %zu loaded\n", fw_entry->size);
 | 
						|
+
 | 
						|
+	/* Parse the file */
 | 
						|
+	tx_data = vmalloc(sizeof(*tx_data));
 | 
						|
+	if (tx_data == NULL) {
 | 
						|
+		zilog_error("out of memory\n");
 | 
						|
+		release_firmware(fw_entry);
 | 
						|
+		ret = -ENOMEM;
 | 
						|
+		goto out;
 | 
						|
+	}
 | 
						|
+	tx_data->code_sets = NULL;
 | 
						|
+
 | 
						|
+	/* Copy the data so hotplug doesn't get confused and timeout */
 | 
						|
+	tx_data->datap = vmalloc(fw_entry->size);
 | 
						|
+	if (tx_data->datap == NULL) {
 | 
						|
+		zilog_error("out of memory\n");
 | 
						|
+		release_firmware(fw_entry);
 | 
						|
+		vfree(tx_data);
 | 
						|
+		ret = -ENOMEM;
 | 
						|
+		goto out;
 | 
						|
+	}
 | 
						|
+	memcpy(tx_data->datap, fw_entry->data, fw_entry->size);
 | 
						|
+	tx_data->endp = tx_data->datap + fw_entry->size;
 | 
						|
+	release_firmware(fw_entry); fw_entry = NULL;
 | 
						|
+
 | 
						|
+	/* Check version */
 | 
						|
+	data = tx_data->datap;
 | 
						|
+	if (!read_uint8(&data, tx_data->endp, &version))
 | 
						|
+		goto corrupt;
 | 
						|
+	if (version != 1) {
 | 
						|
+		zilog_error("unsupported code set file version (%u, expected"
 | 
						|
+			    "1) -- please upgrade to a newer driver",
 | 
						|
+			    version);
 | 
						|
+		fw_unload_locked();
 | 
						|
+		ret = -EFAULT;
 | 
						|
+		goto out;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	/* Save boot block for later */
 | 
						|
+	tx_data->boot_data = data;
 | 
						|
+	if (!skip(&data, tx_data->endp, TX_BLOCK_SIZE))
 | 
						|
+		goto corrupt;
 | 
						|
+
 | 
						|
+	if (!read_uint32(&data, tx_data->endp,
 | 
						|
+			      &tx_data->num_code_sets))
 | 
						|
+		goto corrupt;
 | 
						|
+
 | 
						|
+	dprintk("%u IR blaster codesets loaded\n", tx_data->num_code_sets);
 | 
						|
+
 | 
						|
+	tx_data->code_sets = vmalloc(
 | 
						|
+		tx_data->num_code_sets * sizeof(char *));
 | 
						|
+	if (tx_data->code_sets == NULL) {
 | 
						|
+		fw_unload_locked();
 | 
						|
+		ret = -ENOMEM;
 | 
						|
+		goto out;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	for (i = 0; i < TX_BLOCK_SIZE; ++i)
 | 
						|
+		tx_data->fixed[i] = -1;
 | 
						|
+
 | 
						|
+	/* Read global fixed data template */
 | 
						|
+	if (!read_uint8(&data, tx_data->endp, &num_global_fixed) ||
 | 
						|
+	    num_global_fixed > TX_BLOCK_SIZE)
 | 
						|
+		goto corrupt;
 | 
						|
+	for (i = 0; i < num_global_fixed; ++i) {
 | 
						|
+		unsigned char pos, val;
 | 
						|
+		if (!read_uint8(&data, tx_data->endp, &pos) ||
 | 
						|
+		    !read_uint8(&data, tx_data->endp, &val) ||
 | 
						|
+		    pos >= TX_BLOCK_SIZE)
 | 
						|
+			goto corrupt;
 | 
						|
+		tx_data->fixed[pos] = (int)val;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	/* Filch out the position of each code set */
 | 
						|
+	for (i = 0; i < tx_data->num_code_sets; ++i) {
 | 
						|
+		unsigned int id;
 | 
						|
+		unsigned char keys;
 | 
						|
+		unsigned char ndiffs;
 | 
						|
+
 | 
						|
+		/* Save the codeset position */
 | 
						|
+		tx_data->code_sets[i] = data;
 | 
						|
+
 | 
						|
+		/* Read header */
 | 
						|
+		if (!read_uint32(&data, tx_data->endp, &id) ||
 | 
						|
+		    !read_uint8(&data, tx_data->endp, &keys) ||
 | 
						|
+		    !read_uint8(&data, tx_data->endp, &ndiffs) ||
 | 
						|
+		    ndiffs > TX_BLOCK_SIZE || keys == 0)
 | 
						|
+			goto corrupt;
 | 
						|
+
 | 
						|
+		/* skip diff positions */
 | 
						|
+		if (!skip(&data, tx_data->endp, ndiffs))
 | 
						|
+			goto corrupt;
 | 
						|
+
 | 
						|
+		/*
 | 
						|
+		 * After the diffs we have the first key id + data -
 | 
						|
+		 * global fixed
 | 
						|
+		 */
 | 
						|
+		if (!skip(&data, tx_data->endp,
 | 
						|
+			       1 + TX_BLOCK_SIZE - num_global_fixed))
 | 
						|
+			goto corrupt;
 | 
						|
+
 | 
						|
+		/* Then we have keys-1 blocks of key id+diffs */
 | 
						|
+		if (!skip(&data, tx_data->endp,
 | 
						|
+			       (ndiffs + 1) * (keys - 1)))
 | 
						|
+			goto corrupt;
 | 
						|
+	}
 | 
						|
+	ret = 0;
 | 
						|
+	goto out;
 | 
						|
+
 | 
						|
+corrupt:
 | 
						|
+	zilog_error("firmware is corrupt\n");
 | 
						|
+	fw_unload_locked();
 | 
						|
+	ret = -EFAULT;
 | 
						|
+
 | 
						|
+out:
 | 
						|
+	mutex_unlock(&tx_data_lock);
 | 
						|
+	return ret;
 | 
						|
+}
 | 
						|
+
 | 
						|
+/* initialise the IR TX device */
 | 
						|
+static int tx_init(struct IR *ir)
 | 
						|
+{
 | 
						|
+	int ret;
 | 
						|
+
 | 
						|
+	/* Load 'firmware' */
 | 
						|
+	ret = fw_load(ir);
 | 
						|
+	if (ret != 0)
 | 
						|
+		return ret;
 | 
						|
+
 | 
						|
+	/* Send boot block */
 | 
						|
+	ret = send_boot_data(ir);
 | 
						|
+	if (ret != 0)
 | 
						|
+		return ret;
 | 
						|
+	ir->need_boot = 0;
 | 
						|
+
 | 
						|
+	/* Looks good */
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+/* do nothing stub to make LIRC happy */
 | 
						|
+static loff_t lseek(struct file *filep, loff_t offset, int orig)
 | 
						|
+{
 | 
						|
+	return -ESPIPE;
 | 
						|
+}
 | 
						|
+
 | 
						|
+/* copied from lirc_dev */
 | 
						|
+static ssize_t read(struct file *filep, char *outbuf, size_t n, loff_t *ppos)
 | 
						|
+{
 | 
						|
+	struct IR *ir = (struct IR *)filep->private_data;
 | 
						|
+	unsigned char buf[ir->buf.chunk_size];
 | 
						|
+	int ret = 0, written = 0;
 | 
						|
+	DECLARE_WAITQUEUE(wait, current);
 | 
						|
+
 | 
						|
+	dprintk("read called\n");
 | 
						|
+	if (ir->c_rx.addr == 0)
 | 
						|
+		return -ENODEV;
 | 
						|
+
 | 
						|
+	if (mutex_lock_interruptible(&ir->buf_lock))
 | 
						|
+		return -ERESTARTSYS;
 | 
						|
+
 | 
						|
+	if (n % ir->buf.chunk_size) {
 | 
						|
+		dprintk("read result = -EINVAL\n");
 | 
						|
+		mutex_unlock(&ir->buf_lock);
 | 
						|
+		return -EINVAL;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	/*
 | 
						|
+	 * we add ourselves to the task queue before buffer check
 | 
						|
+	 * to avoid losing scan code (in case when queue is awaken somewhere
 | 
						|
+	 * between while condition checking and scheduling)
 | 
						|
+	 */
 | 
						|
+	add_wait_queue(&ir->buf.wait_poll, &wait);
 | 
						|
+	set_current_state(TASK_INTERRUPTIBLE);
 | 
						|
+
 | 
						|
+	/*
 | 
						|
+	 * while we didn't provide 'length' bytes, device is opened in blocking
 | 
						|
+	 * mode and 'copy_to_user' is happy, wait for data.
 | 
						|
+	 */
 | 
						|
+	while (written < n && ret == 0) {
 | 
						|
+		if (lirc_buffer_empty(&ir->buf)) {
 | 
						|
+			/*
 | 
						|
+			 * According to the read(2) man page, 'written' can be
 | 
						|
+			 * returned as less than 'n', instead of blocking
 | 
						|
+			 * again, returning -EWOULDBLOCK, or returning
 | 
						|
+			 * -ERESTARTSYS
 | 
						|
+			 */
 | 
						|
+			if (written)
 | 
						|
+				break;
 | 
						|
+			if (filep->f_flags & O_NONBLOCK) {
 | 
						|
+				ret = -EWOULDBLOCK;
 | 
						|
+				break;
 | 
						|
+			}
 | 
						|
+			if (signal_pending(current)) {
 | 
						|
+				ret = -ERESTARTSYS;
 | 
						|
+				break;
 | 
						|
+			}
 | 
						|
+			schedule();
 | 
						|
+			set_current_state(TASK_INTERRUPTIBLE);
 | 
						|
+		} else {
 | 
						|
+			lirc_buffer_read(&ir->buf, buf);
 | 
						|
+			ret = copy_to_user((void *)outbuf+written, buf,
 | 
						|
+					   ir->buf.chunk_size);
 | 
						|
+			written += ir->buf.chunk_size;
 | 
						|
+		}
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	remove_wait_queue(&ir->buf.wait_poll, &wait);
 | 
						|
+	set_current_state(TASK_RUNNING);
 | 
						|
+	mutex_unlock(&ir->buf_lock);
 | 
						|
+
 | 
						|
+	dprintk("read result = %s (%d)\n",
 | 
						|
+		ret ? "-EFAULT" : "OK", ret);
 | 
						|
+
 | 
						|
+	return ret ? ret : written;
 | 
						|
+}
 | 
						|
+
 | 
						|
+/* send a keypress to the IR TX device */
 | 
						|
+static int send_code(struct IR *ir, unsigned int code, unsigned int key)
 | 
						|
+{
 | 
						|
+	unsigned char data_block[TX_BLOCK_SIZE];
 | 
						|
+	unsigned char buf[2];
 | 
						|
+	int i, ret;
 | 
						|
+
 | 
						|
+	/* Get data for the codeset/key */
 | 
						|
+	ret = get_key_data(data_block, code, key);
 | 
						|
+
 | 
						|
+	if (ret == -EPROTO) {
 | 
						|
+		zilog_error("failed to get data for code %u, key %u -- check "
 | 
						|
+			    "lircd.conf entries\n", code, key);
 | 
						|
+		return ret;
 | 
						|
+	} else if (ret != 0)
 | 
						|
+		return ret;
 | 
						|
+
 | 
						|
+	/* Send the data block */
 | 
						|
+	ret = send_data_block(ir, data_block);
 | 
						|
+	if (ret != 0)
 | 
						|
+		return ret;
 | 
						|
+
 | 
						|
+	/* Send data block length? */
 | 
						|
+	buf[0] = 0x00;
 | 
						|
+	buf[1] = 0x40;
 | 
						|
+	ret = i2c_master_send(&ir->c_tx, buf, 2);
 | 
						|
+	if (ret != 2) {
 | 
						|
+		zilog_error("i2c_master_send failed with %d\n", ret);
 | 
						|
+		return ret < 0 ? ret : -EFAULT;
 | 
						|
+	}
 | 
						|
+	ret = i2c_master_send(&ir->c_tx, buf, 1);
 | 
						|
+	if (ret != 1) {
 | 
						|
+		zilog_error("i2c_master_send failed with %d\n", ret);
 | 
						|
+		return ret < 0 ? ret : -EFAULT;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	/* Send finished download? */
 | 
						|
+	ret = i2c_master_recv(&ir->c_tx, buf, 1);
 | 
						|
+	if (ret != 1) {
 | 
						|
+		zilog_error("i2c_master_recv failed with %d\n", ret);
 | 
						|
+		return ret < 0 ? ret : -EFAULT;
 | 
						|
+	}
 | 
						|
+	if (buf[0] != 0xA0) {
 | 
						|
+		zilog_error("unexpected IR TX response #1: %02x\n",
 | 
						|
+			buf[0]);
 | 
						|
+		return -EFAULT;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	/* Send prepare command? */
 | 
						|
+	buf[0] = 0x00;
 | 
						|
+	buf[1] = 0x80;
 | 
						|
+	ret = i2c_master_send(&ir->c_tx, buf, 2);
 | 
						|
+	if (ret != 2) {
 | 
						|
+		zilog_error("i2c_master_send failed with %d\n", ret);
 | 
						|
+		return ret < 0 ? ret : -EFAULT;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+#ifdef I2C_HW_B_HDPVR
 | 
						|
+	/*
 | 
						|
+	 * The sleep bits aren't necessary on the HD PVR, and in fact, the
 | 
						|
+	 * last i2c_master_recv always fails with a -5, so for now, we're
 | 
						|
+	 * going to skip this whole mess and say we're done on the HD PVR
 | 
						|
+	 */
 | 
						|
+	if (ir->c_rx.adapter->id == I2C_HW_B_HDPVR)
 | 
						|
+		goto done;
 | 
						|
+#endif
 | 
						|
+
 | 
						|
+	/*
 | 
						|
+	 * This bit NAKs until the device is ready, so we retry it
 | 
						|
+	 * sleeping a bit each time.  This seems to be what the windows
 | 
						|
+	 * driver does, approximately.
 | 
						|
+	 * Try for up to 1s.
 | 
						|
+	 */
 | 
						|
+	for (i = 0; i < 20; ++i) {
 | 
						|
+		set_current_state(TASK_UNINTERRUPTIBLE);
 | 
						|
+		schedule_timeout((50 * HZ + 999) / 1000);
 | 
						|
+		ret = i2c_master_send(&ir->c_tx, buf, 1);
 | 
						|
+		if (ret == 1)
 | 
						|
+			break;
 | 
						|
+		dprintk("NAK expected: i2c_master_send "
 | 
						|
+			"failed with %d (try %d)\n", ret, i+1);
 | 
						|
+	}
 | 
						|
+	if (ret != 1) {
 | 
						|
+		zilog_error("IR TX chip never got ready: last i2c_master_send "
 | 
						|
+			    "failed with %d\n", ret);
 | 
						|
+		return ret < 0 ? ret : -EFAULT;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	/* Seems to be an 'ok' response */
 | 
						|
+	i = i2c_master_recv(&ir->c_tx, buf, 1);
 | 
						|
+	if (i != 1) {
 | 
						|
+		zilog_error("i2c_master_recv failed with %d\n", ret);
 | 
						|
+		return -EFAULT;
 | 
						|
+	}
 | 
						|
+	if (buf[0] != 0x80) {
 | 
						|
+		zilog_error("unexpected IR TX response #2: %02x\n", buf[0]);
 | 
						|
+		return -EFAULT;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+done:
 | 
						|
+	/* Oh good, it worked */
 | 
						|
+	dprintk("sent code %u, key %u\n", code, key);
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+/*
 | 
						|
+ * Write a code to the device.  We take in a 32-bit number (an int) and then
 | 
						|
+ * decode this to a codeset/key index.  The key data is then decompressed and
 | 
						|
+ * sent to the device.  We have a spin lock as per i2c documentation to prevent
 | 
						|
+ * multiple concurrent sends which would probably cause the device to explode.
 | 
						|
+ */
 | 
						|
+static ssize_t write(struct file *filep, const char *buf, size_t n,
 | 
						|
+			  loff_t *ppos)
 | 
						|
+{
 | 
						|
+	struct IR *ir = (struct IR *)filep->private_data;
 | 
						|
+	size_t i;
 | 
						|
+	int failures = 0;
 | 
						|
+
 | 
						|
+	if (ir->c_tx.addr == 0)
 | 
						|
+		return -ENODEV;
 | 
						|
+
 | 
						|
+	/* Validate user parameters */
 | 
						|
+	if (n % sizeof(int))
 | 
						|
+		return -EINVAL;
 | 
						|
+
 | 
						|
+	/* Lock i2c bus for the duration */
 | 
						|
+	mutex_lock(&ir->ir_lock);
 | 
						|
+
 | 
						|
+	/* Send each keypress */
 | 
						|
+	for (i = 0; i < n;) {
 | 
						|
+		int ret = 0;
 | 
						|
+		int command;
 | 
						|
+
 | 
						|
+		if (copy_from_user(&command, buf + i, sizeof(command))) {
 | 
						|
+			mutex_unlock(&ir->ir_lock);
 | 
						|
+			return -EFAULT;
 | 
						|
+		}
 | 
						|
+
 | 
						|
+		/* Send boot data first if required */
 | 
						|
+		if (ir->need_boot == 1) {
 | 
						|
+			ret = send_boot_data(ir);
 | 
						|
+			if (ret == 0)
 | 
						|
+				ir->need_boot = 0;
 | 
						|
+		}
 | 
						|
+
 | 
						|
+		/* Send the code */
 | 
						|
+		if (ret == 0) {
 | 
						|
+			ret = send_code(ir, (unsigned)command >> 16,
 | 
						|
+					    (unsigned)command & 0xFFFF);
 | 
						|
+			if (ret == -EPROTO) {
 | 
						|
+				mutex_unlock(&ir->ir_lock);
 | 
						|
+				return ret;
 | 
						|
+			}
 | 
						|
+		}
 | 
						|
+
 | 
						|
+		/*
 | 
						|
+		 * Hmm, a failure.  If we've had a few then give up, otherwise
 | 
						|
+		 * try a reset
 | 
						|
+		 */
 | 
						|
+		if (ret != 0) {
 | 
						|
+			/* Looks like the chip crashed, reset it */
 | 
						|
+			zilog_error("sending to the IR transmitter chip "
 | 
						|
+				    "failed, trying reset\n");
 | 
						|
+
 | 
						|
+			if (failures >= 3) {
 | 
						|
+				zilog_error("unable to send to the IR chip "
 | 
						|
+					    "after 3 resets, giving up\n");
 | 
						|
+				mutex_unlock(&ir->ir_lock);
 | 
						|
+				return ret;
 | 
						|
+			}
 | 
						|
+			set_current_state(TASK_UNINTERRUPTIBLE);
 | 
						|
+			schedule_timeout((100 * HZ + 999) / 1000);
 | 
						|
+			ir->need_boot = 1;
 | 
						|
+			++failures;
 | 
						|
+		} else
 | 
						|
+			i += sizeof(int);
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	/* Release i2c bus */
 | 
						|
+	mutex_unlock(&ir->ir_lock);
 | 
						|
+
 | 
						|
+	/* All looks good */
 | 
						|
+	return n;
 | 
						|
+}
 | 
						|
+
 | 
						|
+/* copied from lirc_dev */
 | 
						|
+static unsigned int poll(struct file *filep, poll_table *wait)
 | 
						|
+{
 | 
						|
+	struct IR *ir = (struct IR *)filep->private_data;
 | 
						|
+	unsigned int ret;
 | 
						|
+
 | 
						|
+	dprintk("poll called\n");
 | 
						|
+	if (ir->c_rx.addr == 0)
 | 
						|
+		return -ENODEV;
 | 
						|
+
 | 
						|
+	mutex_lock(&ir->buf_lock);
 | 
						|
+
 | 
						|
+	poll_wait(filep, &ir->buf.wait_poll, wait);
 | 
						|
+
 | 
						|
+	dprintk("poll result = %s\n",
 | 
						|
+		lirc_buffer_empty(&ir->buf) ? "0" : "POLLIN|POLLRDNORM");
 | 
						|
+
 | 
						|
+	ret = lirc_buffer_empty(&ir->buf) ? 0 : (POLLIN|POLLRDNORM);
 | 
						|
+
 | 
						|
+	mutex_unlock(&ir->buf_lock);
 | 
						|
+	return ret;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static long ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
 | 
						|
+{
 | 
						|
+	struct IR *ir = (struct IR *)filep->private_data;
 | 
						|
+	int result;
 | 
						|
+	unsigned long mode, features = 0;
 | 
						|
+
 | 
						|
+	if (ir->c_rx.addr != 0)
 | 
						|
+		features |= LIRC_CAN_REC_LIRCCODE;
 | 
						|
+	if (ir->c_tx.addr != 0)
 | 
						|
+		features |= LIRC_CAN_SEND_PULSE;
 | 
						|
+
 | 
						|
+	switch (cmd) {
 | 
						|
+	case LIRC_GET_LENGTH:
 | 
						|
+		result = put_user((unsigned long)13,
 | 
						|
+				  (unsigned long *)arg);
 | 
						|
+		break;
 | 
						|
+	case LIRC_GET_FEATURES:
 | 
						|
+		result = put_user(features, (unsigned long *) arg);
 | 
						|
+		break;
 | 
						|
+	case LIRC_GET_REC_MODE:
 | 
						|
+		if (!(features&LIRC_CAN_REC_MASK))
 | 
						|
+			return -ENOSYS;
 | 
						|
+
 | 
						|
+		result = put_user(LIRC_REC2MODE
 | 
						|
+				  (features&LIRC_CAN_REC_MASK),
 | 
						|
+				  (unsigned long *)arg);
 | 
						|
+		break;
 | 
						|
+	case LIRC_SET_REC_MODE:
 | 
						|
+		if (!(features&LIRC_CAN_REC_MASK))
 | 
						|
+			return -ENOSYS;
 | 
						|
+
 | 
						|
+		result = get_user(mode, (unsigned long *)arg);
 | 
						|
+		if (!result && !(LIRC_MODE2REC(mode) & features))
 | 
						|
+			result = -EINVAL;
 | 
						|
+		break;
 | 
						|
+	case LIRC_GET_SEND_MODE:
 | 
						|
+		if (!(features&LIRC_CAN_SEND_MASK))
 | 
						|
+			return -ENOSYS;
 | 
						|
+
 | 
						|
+		result = put_user(LIRC_MODE_PULSE, (unsigned long *) arg);
 | 
						|
+		break;
 | 
						|
+	case LIRC_SET_SEND_MODE:
 | 
						|
+		if (!(features&LIRC_CAN_SEND_MASK))
 | 
						|
+			return -ENOSYS;
 | 
						|
+
 | 
						|
+		result = get_user(mode, (unsigned long *) arg);
 | 
						|
+		if (!result && mode != LIRC_MODE_PULSE)
 | 
						|
+			return -EINVAL;
 | 
						|
+		break;
 | 
						|
+	default:
 | 
						|
+		return -EINVAL;
 | 
						|
+	}
 | 
						|
+	return result;
 | 
						|
+}
 | 
						|
+
 | 
						|
+/*
 | 
						|
+ * Open the IR device.  Get hold of our IR structure and
 | 
						|
+ * stash it in private_data for the file
 | 
						|
+ */
 | 
						|
+static int open(struct inode *node, struct file *filep)
 | 
						|
+{
 | 
						|
+	struct IR *ir;
 | 
						|
+	int ret;
 | 
						|
+
 | 
						|
+	/* find our IR struct */
 | 
						|
+	unsigned minor = MINOR(node->i_rdev);
 | 
						|
+	if (minor >= MAX_IRCTL_DEVICES) {
 | 
						|
+		dprintk("minor %d: open result = -ENODEV\n",
 | 
						|
+			minor);
 | 
						|
+		return -ENODEV;
 | 
						|
+	}
 | 
						|
+	ir = ir_devices[minor];
 | 
						|
+
 | 
						|
+	/* increment in use count */
 | 
						|
+	mutex_lock(&ir->ir_lock);
 | 
						|
+	++ir->open;
 | 
						|
+	ret = set_use_inc(ir);
 | 
						|
+	if (ret != 0) {
 | 
						|
+		--ir->open;
 | 
						|
+		mutex_unlock(&ir->ir_lock);
 | 
						|
+		return ret;
 | 
						|
+	}
 | 
						|
+	mutex_unlock(&ir->ir_lock);
 | 
						|
+
 | 
						|
+	/* stash our IR struct */
 | 
						|
+	filep->private_data = ir;
 | 
						|
+
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+/* Close the IR device */
 | 
						|
+static int close(struct inode *node, struct file *filep)
 | 
						|
+{
 | 
						|
+	/* find our IR struct */
 | 
						|
+	struct IR *ir = (struct IR *)filep->private_data;
 | 
						|
+	if (ir == NULL) {
 | 
						|
+		zilog_error("close: no private_data attached to the file!\n");
 | 
						|
+		return -ENODEV;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	/* decrement in use count */
 | 
						|
+	mutex_lock(&ir->ir_lock);
 | 
						|
+	--ir->open;
 | 
						|
+	set_use_dec(ir);
 | 
						|
+	mutex_unlock(&ir->ir_lock);
 | 
						|
+
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static struct lirc_driver lirc_template = {
 | 
						|
+	.name		= "lirc_zilog",
 | 
						|
+	.set_use_inc	= set_use_inc,
 | 
						|
+	.set_use_dec	= set_use_dec,
 | 
						|
+	.owner		= THIS_MODULE
 | 
						|
+};
 | 
						|
+
 | 
						|
+static int ir_remove(struct i2c_client *client);
 | 
						|
+static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id);
 | 
						|
+static int ir_command(struct i2c_client *client, unsigned int cmd, void *arg);
 | 
						|
+
 | 
						|
+static const struct i2c_device_id ir_transceiver_id[] = {
 | 
						|
+	/* Generic entry for any IR transceiver */
 | 
						|
+	{ "ir_video", 0 },
 | 
						|
+	/* IR device specific entries should be added here */
 | 
						|
+	{ "ir_tx_z8f0811_haup", 0 },
 | 
						|
+	{ "ir_rx_z8f0811_haup", 0 },
 | 
						|
+	{ }
 | 
						|
+};
 | 
						|
+
 | 
						|
+static struct i2c_driver driver = {
 | 
						|
+	.driver = {
 | 
						|
+		.owner	= THIS_MODULE,
 | 
						|
+		.name	= "Zilog/Hauppauge i2c IR",
 | 
						|
+	},
 | 
						|
+	.probe		= ir_probe,
 | 
						|
+	.remove		= ir_remove,
 | 
						|
+	.command	= ir_command,
 | 
						|
+	.id_table	= ir_transceiver_id,
 | 
						|
+};
 | 
						|
+
 | 
						|
+static struct file_operations lirc_fops = {
 | 
						|
+	.owner		= THIS_MODULE,
 | 
						|
+	.llseek		= lseek,
 | 
						|
+	.read		= read,
 | 
						|
+	.write		= write,
 | 
						|
+	.poll		= poll,
 | 
						|
+	.unlocked_ioctl	= ioctl,
 | 
						|
+	.open		= open,
 | 
						|
+	.release	= close
 | 
						|
+};
 | 
						|
+
 | 
						|
+static int ir_remove(struct i2c_client *client)
 | 
						|
+{
 | 
						|
+	struct IR *ir = i2c_get_clientdata(client);
 | 
						|
+
 | 
						|
+	mutex_lock(&ir->ir_lock);
 | 
						|
+
 | 
						|
+	if (ir->have_rx || ir->have_tx) {
 | 
						|
+		DECLARE_COMPLETION(tn);
 | 
						|
+		DECLARE_COMPLETION(tn2);
 | 
						|
+
 | 
						|
+		/* end up polling thread */
 | 
						|
+		if (ir->task && !IS_ERR(ir->task)) {
 | 
						|
+			ir->t_notify = &tn;
 | 
						|
+			ir->t_notify2 = &tn2;
 | 
						|
+			ir->shutdown = 1;
 | 
						|
+			wake_up_process(ir->task);
 | 
						|
+			complete(&tn2);
 | 
						|
+			wait_for_completion(&tn);
 | 
						|
+			ir->t_notify = NULL;
 | 
						|
+			ir->t_notify2 = NULL;
 | 
						|
+		}
 | 
						|
+
 | 
						|
+	} else {
 | 
						|
+		mutex_unlock(&ir->ir_lock);
 | 
						|
+		zilog_error("%s: detached from something we didn't "
 | 
						|
+			    "attach to\n", __func__);
 | 
						|
+		return -ENODEV;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	/* unregister lirc driver */
 | 
						|
+	if (ir->l.minor >= 0 && ir->l.minor < MAX_IRCTL_DEVICES) {
 | 
						|
+		lirc_unregister_driver(ir->l.minor);
 | 
						|
+		ir_devices[ir->l.minor] = NULL;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	/* free memory */
 | 
						|
+	lirc_buffer_free(&ir->buf);
 | 
						|
+	mutex_unlock(&ir->ir_lock);
 | 
						|
+	kfree(ir);
 | 
						|
+
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)
 | 
						|
+{
 | 
						|
+	struct IR *ir = NULL;
 | 
						|
+	struct i2c_adapter *adap = client->adapter;
 | 
						|
+	char buf;
 | 
						|
+	int ret;
 | 
						|
+	int have_rx = 0, have_tx = 0;
 | 
						|
+
 | 
						|
+	dprintk("%s: adapter id=0x%x, client addr=0x%02x\n",
 | 
						|
+		__func__, adap->id, client->addr);
 | 
						|
+
 | 
						|
+	/*
 | 
						|
+	 * The external IR receiver is at i2c address 0x71.
 | 
						|
+	 * The IR transmitter is at 0x70.
 | 
						|
+	 */
 | 
						|
+	client->addr = 0x70;
 | 
						|
+
 | 
						|
+	if (!disable_tx) {
 | 
						|
+		if (i2c_master_recv(client, &buf, 1) == 1)
 | 
						|
+			have_tx = 1;
 | 
						|
+		dprintk("probe 0x70 @ %s: %s\n",
 | 
						|
+			adap->name, have_tx ? "success" : "failed");
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	if (!disable_rx) {
 | 
						|
+		client->addr = 0x71;
 | 
						|
+		if (i2c_master_recv(client, &buf, 1) == 1)
 | 
						|
+			have_rx = 1;
 | 
						|
+		dprintk("probe 0x71 @ %s: %s\n",
 | 
						|
+			adap->name, have_rx ? "success" : "failed");
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	if (!(have_rx || have_tx)) {
 | 
						|
+		zilog_error("%s: no devices found\n", adap->name);
 | 
						|
+		goto out_nodev;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	printk(KERN_INFO "lirc_zilog: chip found with %s\n",
 | 
						|
+		have_rx && have_tx ? "RX and TX" :
 | 
						|
+			have_rx ? "RX only" : "TX only");
 | 
						|
+
 | 
						|
+	ir = kzalloc(sizeof(struct IR), GFP_KERNEL);
 | 
						|
+
 | 
						|
+	if (!ir)
 | 
						|
+		goto out_nomem;
 | 
						|
+
 | 
						|
+	ret = lirc_buffer_init(&ir->buf, 2, BUFLEN / 2);
 | 
						|
+	if (ret)
 | 
						|
+		goto out_nomem;
 | 
						|
+
 | 
						|
+	mutex_init(&ir->ir_lock);
 | 
						|
+	mutex_init(&ir->buf_lock);
 | 
						|
+	ir->need_boot = 1;
 | 
						|
+
 | 
						|
+	memcpy(&ir->l, &lirc_template, sizeof(struct lirc_driver));
 | 
						|
+	ir->l.minor = -1;
 | 
						|
+
 | 
						|
+	/* I2C attach to device */
 | 
						|
+	i2c_set_clientdata(client, ir);
 | 
						|
+
 | 
						|
+	/* initialise RX device */
 | 
						|
+	if (have_rx) {
 | 
						|
+		DECLARE_COMPLETION(tn);
 | 
						|
+		memcpy(&ir->c_rx, client, sizeof(struct i2c_client));
 | 
						|
+
 | 
						|
+		ir->c_rx.addr = 0x71;
 | 
						|
+		strlcpy(ir->c_rx.name, ZILOG_HAUPPAUGE_IR_RX_NAME,
 | 
						|
+			I2C_NAME_SIZE);
 | 
						|
+
 | 
						|
+		/* try to fire up polling thread */
 | 
						|
+		ir->t_notify = &tn;
 | 
						|
+		ir->task = kthread_run(lirc_thread, ir, "lirc_zilog");
 | 
						|
+		if (IS_ERR(ir->task)) {
 | 
						|
+			ret = PTR_ERR(ir->task);
 | 
						|
+			zilog_error("lirc_register_driver: cannot run "
 | 
						|
+				    "poll thread %d\n", ret);
 | 
						|
+			goto err;
 | 
						|
+		}
 | 
						|
+		wait_for_completion(&tn);
 | 
						|
+		ir->t_notify = NULL;
 | 
						|
+		ir->have_rx = 1;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	/* initialise TX device */
 | 
						|
+	if (have_tx) {
 | 
						|
+		memcpy(&ir->c_tx, client, sizeof(struct i2c_client));
 | 
						|
+		ir->c_tx.addr = 0x70;
 | 
						|
+		strlcpy(ir->c_tx.name, ZILOG_HAUPPAUGE_IR_TX_NAME,
 | 
						|
+			I2C_NAME_SIZE);
 | 
						|
+		ir->have_tx = 1;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	/* set lirc_dev stuff */
 | 
						|
+	ir->l.code_length = 13;
 | 
						|
+	ir->l.rbuf	  = &ir->buf;
 | 
						|
+	ir->l.fops	  = &lirc_fops;
 | 
						|
+	ir->l.data	  = ir;
 | 
						|
+	ir->l.minor       = minor;
 | 
						|
+	ir->l.dev         = &adap->dev;
 | 
						|
+	ir->l.sample_rate = 0;
 | 
						|
+
 | 
						|
+	/* register with lirc */
 | 
						|
+	ir->l.minor = lirc_register_driver(&ir->l);
 | 
						|
+	if (ir->l.minor < 0 || ir->l.minor >= MAX_IRCTL_DEVICES) {
 | 
						|
+		zilog_error("ir_attach: \"minor\" must be between 0 and %d "
 | 
						|
+			    "(%d)!\n", MAX_IRCTL_DEVICES-1, ir->l.minor);
 | 
						|
+		ret = -EBADRQC;
 | 
						|
+		goto err;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	/* store this for getting back in open() later on */
 | 
						|
+	ir_devices[ir->l.minor] = ir;
 | 
						|
+
 | 
						|
+	/*
 | 
						|
+	 * if we have the tx device, load the 'firmware'.  We do this
 | 
						|
+	 * after registering with lirc as otherwise hotplug seems to take
 | 
						|
+	 * 10s to create the lirc device.
 | 
						|
+	 */
 | 
						|
+	if (have_tx) {
 | 
						|
+		/* Special TX init */
 | 
						|
+		ret = tx_init(ir);
 | 
						|
+		if (ret != 0)
 | 
						|
+			goto err;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	return 0;
 | 
						|
+
 | 
						|
+err:
 | 
						|
+	/* undo everything, hopefully... */
 | 
						|
+	if (ir->c_rx.addr)
 | 
						|
+		ir_remove(&ir->c_rx);
 | 
						|
+	if (ir->c_tx.addr)
 | 
						|
+		ir_remove(&ir->c_tx);
 | 
						|
+	return ret;
 | 
						|
+
 | 
						|
+out_nodev:
 | 
						|
+	zilog_error("no device found\n");
 | 
						|
+	return -ENODEV;
 | 
						|
+
 | 
						|
+out_nomem:
 | 
						|
+	zilog_error("memory allocation failure\n");
 | 
						|
+	kfree(ir);
 | 
						|
+	return -ENOMEM;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int ir_command(struct i2c_client *client, unsigned int cmd, void *arg)
 | 
						|
+{
 | 
						|
+	/* nothing */
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int __init zilog_init(void)
 | 
						|
+{
 | 
						|
+	int ret;
 | 
						|
+
 | 
						|
+	zilog_notify("Zilog/Hauppauge IR driver initializing\n");
 | 
						|
+
 | 
						|
+	mutex_init(&tx_data_lock);
 | 
						|
+
 | 
						|
+	request_module("firmware_class");
 | 
						|
+
 | 
						|
+	ret = i2c_add_driver(&driver);
 | 
						|
+	if (ret)
 | 
						|
+		zilog_error("initialization failed\n");
 | 
						|
+	else
 | 
						|
+		zilog_notify("initialization complete\n");
 | 
						|
+
 | 
						|
+	return ret;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void __exit zilog_exit(void)
 | 
						|
+{
 | 
						|
+	i2c_del_driver(&driver);
 | 
						|
+	/* if loaded */
 | 
						|
+	fw_unload();
 | 
						|
+	zilog_notify("Zilog/Hauppauge IR driver unloaded\n");
 | 
						|
+}
 | 
						|
+
 | 
						|
+module_init(zilog_init);
 | 
						|
+module_exit(zilog_exit);
 | 
						|
+
 | 
						|
+MODULE_DESCRIPTION("Zilog/Hauppauge infrared transmitter driver (i2c stack)");
 | 
						|
+MODULE_AUTHOR("Gerd Knorr, Michal Kochanowicz, Christoph Bartelmus, "
 | 
						|
+	      "Ulrich Mueller, Stefan Jahn, Jerome Brock, Mark Weaver");
 | 
						|
+MODULE_LICENSE("GPL");
 | 
						|
+/* for compat with old name, which isn't all that accurate anymore */
 | 
						|
+MODULE_ALIAS("lirc_pvr150");
 | 
						|
+
 | 
						|
+module_param(minor, int, 0444);
 | 
						|
+MODULE_PARM_DESC(minor, "Preferred minor device number");
 | 
						|
+
 | 
						|
+module_param(debug, bool, 0644);
 | 
						|
+MODULE_PARM_DESC(debug, "Enable debugging messages");
 | 
						|
+
 | 
						|
+module_param(disable_rx, bool, 0644);
 | 
						|
+MODULE_PARM_DESC(disable_rx, "Disable the IR receiver device");
 | 
						|
+
 | 
						|
+module_param(disable_tx, bool, 0644);
 | 
						|
+MODULE_PARM_DESC(disable_tx, "Disable the IR transmitter device");
 |