aboutsummaryrefslogtreecommitdiff
path: root/ubuntu
diff options
context:
space:
mode:
authorLeann Ogasawara <leann.ogasawara@canonical.com>2010-03-12 16:32:23 -0800
committerLeann Ogasawara <leann.ogasawara@canonical.com>2010-09-02 12:55:08 -0700
commitb5ce7d9e1625a0efc05e52cb7c7c3e7a2e226c3e (patch)
tree2207ac4d04d2e7cf549984948f6408268e6ec4ae /ubuntu
parentc508f51900b848531b5d623a5552a6428e9ae242 (diff)
UBUNTU: SAUCE: Update to LIRC 0.8.6
OriginalLocation: www.lirc.org BugLink: http://bugs.launchpad.net/bugs/432678 Signed-off-by: Mario Limonciello <superm1@ubuntu.com> Signed-off-by: Tim Gardner <tim.gardner@canonical.com>
Diffstat (limited to 'ubuntu')
-rw-r--r--ubuntu/lirc/Kconfig10
-rw-r--r--ubuntu/lirc/Makefile2
-rw-r--r--ubuntu/lirc/lirc.h9
-rw-r--r--ubuntu/lirc/lirc_dev/lirc_dev.c44
-rw-r--r--ubuntu/lirc/lirc_ene0100/Makefile (renamed from ubuntu/lirc/lirc_mceusb2/Makefile)2
-rw-r--r--ubuntu/lirc/lirc_ene0100/lirc_ene0100.c653
-rw-r--r--ubuntu/lirc/lirc_ene0100/lirc_ene0100.h170
-rw-r--r--ubuntu/lirc/lirc_i2c/lirc_i2c.c254
-rw-r--r--ubuntu/lirc/lirc_igorplugusb/lirc_igorplugusb.c61
-rw-r--r--ubuntu/lirc/lirc_imon/lirc_imon.c1747
-rw-r--r--ubuntu/lirc/lirc_it87/lirc_it87.c20
-rw-r--r--ubuntu/lirc/lirc_mceusb/lirc_mceusb.c1770
-rw-r--r--ubuntu/lirc/lirc_mceusb2/lirc_mceusb2.c1145
-rw-r--r--ubuntu/lirc/lirc_sir/lirc_sir.c8
-rw-r--r--ubuntu/lirc/lirc_wpc8769l/lirc_wpc8769l.h52
15 files changed, 3479 insertions, 2468 deletions
diff --git a/ubuntu/lirc/Kconfig b/ubuntu/lirc/Kconfig
index f709f78636b..21dcc40d1f9 100644
--- a/ubuntu/lirc/Kconfig
+++ b/ubuntu/lirc/Kconfig
@@ -12,6 +12,11 @@ config LIRC_BT829
default m
depends on LIRC_DEV
+config LIRC_ENE0100
+ tristate "LIRC ENE0100"
+ default m
+ depends on LIRC_DEV
+
config LIRC_I2C
tristate "LIRC I2C interface remote"
default m
@@ -42,11 +47,6 @@ config LIRC_MCEUSB
default m
depends on LIRC_DEV
-config LIRC_MCEUSB2
- tristate "LIRC Microsoft Media Center Remote v2"
- default m
- depends on LIRC_DEV
-
config LIRC_PARALLEL
tristate "LIRC Parallel port custom remote"
default n
diff --git a/ubuntu/lirc/Makefile b/ubuntu/lirc/Makefile
index ca80518299f..006a6c8c97f 100644
--- a/ubuntu/lirc/Makefile
+++ b/ubuntu/lirc/Makefile
@@ -5,13 +5,13 @@ EXTRA_CFLAGS =-DIRCTL_DEV_MAJOR=61 -DLIRC_SERIAL_TRANSMITTER -DLIRC_SERIAL_SOFTC
obj-$(CONFIG_LIRC_DEV) += lirc_dev/
obj-$(CONFIG_LIRC_ATIUSB) += lirc_atiusb/
obj-$(CONFIG_LIRC_BT829) += lirc_bt829/
+obj-$(CONFIG_LIRC_ENE0100) += lirc_ene0100/
obj-$(CONFIG_LIRC_I2C) += lirc_i2c/
obj-$(CONFIG_LIRC_IGORPLUGUSB) += lirc_igorplugusb/
obj-$(CONFIG_LIRC_IMON) += lirc_imon/
obj-$(CONFIG_LIRC_IT87) += lirc_it87/
obj-$(CONFIG_LIRC_ITE8709) += lirc_ite8709/
obj-$(CONFIG_LIRC_MCEUSB) += lirc_mceusb/
-obj-$(CONFIG_LIRC_MCEUSB2) += lirc_mceusb2/
obj-$(CONFIG_LIRC_PARALLEL) += lirc_parallel/
obj-$(CONFIG_LIRC_SASEM) += lirc_sasem/
obj-$(CONFIG_LIRC_SERIAL) += lirc_serial/
diff --git a/ubuntu/lirc/lirc.h b/ubuntu/lirc/lirc.h
index 20495e6349b..7c4d09e0c44 100644
--- a/ubuntu/lirc/lirc.h
+++ b/ubuntu/lirc/lirc.h
@@ -1,15 +1,16 @@
-/* $Id: lirc.h,v 5.18 2009/01/30 19:39:26 lirc Exp $ */
+/* $Id: lirc.h,v 5.19 2009/08/29 07:52:41 lirc Exp $ */
#ifndef _LINUX_LIRC_H
#define _LINUX_LIRC_H
#if defined(__linux__)
#include <linux/ioctl.h>
-#else
-#if defined(__NetBSD__)
+#elif defined(_NetBSD_)
+#include <sys/ioctl.h>
+#elif defined(_CYGWIN_)
+#define __USE_LINUX_IOCTL_DEFS
#include <sys/ioctl.h>
#endif
-#endif
#define PULSE_BIT 0x01000000
#define PULSE_MASK 0x00FFFFFF
diff --git a/ubuntu/lirc/lirc_dev/lirc_dev.c b/ubuntu/lirc/lirc_dev/lirc_dev.c
index 4e1e3808f95..7618c0aea0e 100644
--- a/ubuntu/lirc/lirc_dev/lirc_dev.c
+++ b/ubuntu/lirc/lirc_dev/lirc_dev.c
@@ -17,7 +17,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
- * $Id: lirc_dev.c,v 1.93 2009/03/29 10:55:31 lirc Exp $
+ * $Id: lirc_dev.c,v 1.96 2009/08/31 16:57:55 lirc Exp $
*
*/
@@ -106,7 +106,7 @@ struct irctl {
#endif
};
-static DEFINE_MUTEX(driver_lock);
+static DEFINE_MUTEX(lirc_dev_lock);
static struct irctl *irctls[MAX_IRCTL_DEVICES];
static struct file_operations fops;
@@ -307,14 +307,7 @@ int lirc_register_driver(struct lirc_driver *d)
}
}
- if (d->owner == NULL) {
- printk(KERN_ERR "lirc_dev: lirc_register_driver: "
- "no module owner registered\n");
- err = -EBADRQC;
- goto out;
- }
-
- mutex_lock(&driver_lock);
+ mutex_lock(&lirc_dev_lock);
minor = d->minor;
@@ -421,7 +414,7 @@ int lirc_register_driver(struct lirc_driver *d)
#endif
}
ir->attached = 1;
- mutex_unlock(&driver_lock);
+ mutex_unlock(&lirc_dev_lock);
/*
* Recent kernels should handle this autmatically by increasing/decreasing
@@ -445,7 +438,7 @@ out_sysfs:
devfs_remove(DEV_LIRC "/%i", ir->d.minor);
#endif
out_lock:
- mutex_unlock(&driver_lock);
+ mutex_unlock(&lirc_dev_lock);
out:
return err;
}
@@ -468,12 +461,12 @@ int lirc_unregister_driver(int minor)
ir = irctls[minor];
- mutex_lock(&driver_lock);
+ mutex_lock(&lirc_dev_lock);
if (ir->d.minor != minor) {
printk(KERN_ERR "lirc_dev: lirc_unregister_driver: "
"minor (%d) device not registered!", minor);
- mutex_unlock(&driver_lock);
+ mutex_unlock(&lirc_dev_lock);
return -ENOENT;
}
@@ -531,7 +524,7 @@ int lirc_unregister_driver(int minor)
lirc_device_destroy(lirc_class,
MKDEV(IRCTL_DEV_MAJOR, ir->d.minor));
- mutex_unlock(&driver_lock);
+ mutex_unlock(&lirc_dev_lock);
/*
* Recent kernels should handle this autmatically by increasing/decreasing
@@ -564,7 +557,7 @@ static int irctl_open(struct inode *inode, struct file *file)
if (ir->d.fops && ir->d.fops->open)
return ir->d.fops->open(inode, file);
- if (mutex_lock_interruptible(&driver_lock))
+ if (mutex_lock_interruptible(&lirc_dev_lock))
return -ERESTARTSYS;
if (ir->d.minor == NOPLUG) {
@@ -577,7 +570,7 @@ static int irctl_open(struct inode *inode, struct file *file)
goto error;
}
- if (ir->d.owner != NULL && try_module_get(ir->d.owner)) {
+ if (try_module_get(ir->d.owner)) {
++ir->open;
retval = ir->d.set_use_inc(ir->d.data);
@@ -598,20 +591,13 @@ static int irctl_open(struct inode *inode, struct file *file)
if (ir->task)
wake_up_process(ir->task);
#endif
- } else {
- if (ir->d.owner == NULL)
- dprintk(LOGHEAD "no module owner!!!\n",
- ir->d.name, ir->d.minor);
-
- retval = -ENODEV;
}
-
error:
if (ir)
dprintk(LOGHEAD "open result = %d\n", ir->d.name, ir->d.minor,
retval);
- mutex_unlock(&driver_lock);
+ mutex_unlock(&lirc_dev_lock);
return retval;
}
@@ -626,7 +612,7 @@ static int irctl_close(struct inode *inode, struct file *file)
if (ir->d.fops && ir->d.fops->release)
return ir->d.fops->release(inode, file);
- if (mutex_lock_interruptible(&driver_lock))
+ if (mutex_lock_interruptible(&lirc_dev_lock))
return -ERESTARTSYS;
--ir->open;
@@ -639,7 +625,7 @@ static int irctl_close(struct inode *inode, struct file *file)
kfree(ir);
}
- mutex_unlock(&driver_lock);
+ mutex_unlock(&lirc_dev_lock);
return 0;
}
@@ -784,7 +770,7 @@ static long irctl_compat_ioctl(struct file *file,
cmd = _IOC(_IOC_DIR(cmd32), _IOC_TYPE(cmd32), _IOC_NR(cmd32),
(_IOC_TYPECHECK(unsigned long)));
- ret = irctl_ioctl(file->f_path.dentry->d_inode, file,
+ ret = irctl_ioctl(file->f_dentry->d_inode, file,
cmd, (unsigned long)(&val));
set_fs(old_fs);
@@ -818,7 +804,7 @@ static long irctl_compat_ioctl(struct file *file,
*/
lock_kernel();
cmd = cmd32;
- ret = irctl_ioctl(file->f_path.dentry->d_inode,
+ ret = irctl_ioctl(file->f_dentry->d_inode,
file, cmd, arg);
unlock_kernel();
return ret;
diff --git a/ubuntu/lirc/lirc_mceusb2/Makefile b/ubuntu/lirc/lirc_ene0100/Makefile
index 7d081f7bcfa..b5f2e6fd137 100644
--- a/ubuntu/lirc/lirc_mceusb2/Makefile
+++ b/ubuntu/lirc/lirc_ene0100/Makefile
@@ -1,3 +1,3 @@
EXTRA_CFLAGS =-DIRCTL_DEV_MAJOR=61 -DLIRC_SERIAL_TRANSMITTER -DLIRC_SERIAL_SOFTCARRIER -I$(src)/..
-obj-$(CONFIG_LIRC_MCEUSB2) += lirc_mceusb2.o
+obj-$(CONFIG_LIRC_ENE0100) += lirc_ene0100.o
diff --git a/ubuntu/lirc/lirc_ene0100/lirc_ene0100.c b/ubuntu/lirc/lirc_ene0100/lirc_ene0100.c
new file mode 100644
index 00000000000..da9519fcadd
--- /dev/null
+++ b/ubuntu/lirc/lirc_ene0100/lirc_ene0100.c
@@ -0,0 +1,653 @@
+/*
+ * 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 "lirc_ene0100.h"
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 16)
+#error "Sorry, this driver needs kernel version 2.6.16 or higher"
+#else
+
+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 */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
+static irqreturn_t ene_hw_irq(int irq, void *data)
+#else
+static irqreturn_t ene_hw_irq(int irq, void *data, struct pt_regs *regs)
+#endif
+{
+ 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);
+#endif
diff --git a/ubuntu/lirc/lirc_ene0100/lirc_ene0100.h b/ubuntu/lirc/lirc_ene0100/lirc_ene0100.h
new file mode 100644
index 00000000000..ae197531357
--- /dev/null
+++ b/ubuntu/lirc/lirc_ene0100/lirc_ene0100.h
@@ -0,0 +1,170 @@
+/*
+ * 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 "../kcompat.h"
+#include "../lirc.h"
+#include "../lirc_dev/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/ubuntu/lirc/lirc_i2c/lirc_i2c.c b/ubuntu/lirc/lirc_i2c/lirc_i2c.c
index baf04fa2514..5acf079f24e 100644
--- a/ubuntu/lirc/lirc_i2c/lirc_i2c.c
+++ b/ubuntu/lirc/lirc_i2c/lirc_i2c.c
@@ -1,3 +1,5 @@
+/* $Id: lirc_i2c.c,v 1.70 2009/08/30 16:59:53 jarodwilson Exp $ */
+
/*
* lirc_i2c.c
*
@@ -42,6 +44,10 @@
*/
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
#include <linux/version.h>
#include <linux/module.h>
#include <linux/kmod.h>
@@ -55,6 +61,7 @@
#include <linux/i2c.h>
#include <linux/i2c-algo-bit.h>
+#include "../kcompat.h"
#include "../lirc_dev/lirc_dev.h"
struct IR {
@@ -326,12 +333,22 @@ static int add_to_buf_knc1(void *data, struct lirc_buffer *buf)
static int set_use_inc(void *data)
{
struct IR *ir = data;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25)
+ int ret;
+#endif
dprintk("%s called\n", __func__);
- /* lock bttv in memory while /dev/lirc is in use */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)
i2c_use_client(&ir->c);
+#else
+ /* lock bttv in memory while /dev/lirc is in use */
+ ret = i2c_use_client(&ir->c);
+ if (ret != 0)
+ return ret;
+#endif
+ MOD_INC_USE_COUNT;
return 0;
}
@@ -342,6 +359,7 @@ static void set_use_dec(void *data)
dprintk("%s called\n", __func__);
i2c_release_client(&ir->c);
+ MOD_DEC_USE_COUNT;
}
static struct lirc_driver lirc_template = {
@@ -352,28 +370,58 @@ static struct lirc_driver lirc_template = {
.owner = THIS_MODULE,
};
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)
+static int ir_attach(struct i2c_adapter *adap, int addr,
+ unsigned short flags, int kind);
+static int ir_probe(struct i2c_adapter *adap);
+# else
static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id);
+#endif
static int ir_remove(struct i2c_client *client);
static int ir_command(struct i2c_client *client, unsigned int cmd, void *arg);
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 30)
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 */
{ }
};
+#endif
static struct i2c_driver driver = {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 16)
+ .name = "i2c ir driver",
+ .flags = I2C_DF_NOTIFY,
+#else
.driver = {
.owner = THIS_MODULE,
.name = "i2c ir driver",
},
+#endif
+ .id = I2C_DRIVERID_EXP3, /* FIXME */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)
+ .attach_adapter = ir_probe,
+ .detach_client = ir_remove,
+#else
.probe = ir_probe,
.remove = ir_remove,
.id_table = ir_receiver_id,
+#endif
.command = ir_command,
};
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)
+static struct i2c_client client_template = {
+ .name = "unset",
+ .driver = &driver
+};
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)
+static int ir_attach(struct i2c_adapter *adap, int addr,
+ unsigned short flags, int kind)
+#else
static void pcf_probe(struct i2c_client *client, struct IR *ir)
{
int ret1, ret2, ret3, ret4;
@@ -397,22 +445,39 @@ static void pcf_probe(struct i2c_client *client, struct IR *ir)
}
static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)
+#endif
{
struct IR *ir;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)
+ int err, retval;
+
+ client_template.adapter = adap;
+ client_template.addr = addr;
+#else
struct i2c_adapter *adap = client->adapter;
unsigned short addr = client->addr;
int retval;
+#endif
- ir = kzalloc(sizeof(struct IR), GFP_KERNEL);
+ ir = kmalloc(sizeof(struct IR), GFP_KERNEL);
if (!ir)
return -ENOMEM;
memcpy(&ir->l, &lirc_template, sizeof(struct lirc_driver));
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)
+ memcpy(&ir->c, &client_template, sizeof(struct i2c_client));
+
+ ir->c.adapter = adap;
+ ir->c.addr = addr;
+ i2c_set_clientdata(&ir->c, ir);
+#else
memcpy(&ir->c, client, sizeof(struct i2c_client));
i2c_set_clientdata(client, ir);
+#endif
ir->l.data = ir;
ir->l.minor = minor;
ir->l.sample_rate = 10;
+ ir->l.dev = &ir->c.dev;
ir->nextkey = -1;
switch (addr) {
@@ -427,8 +492,12 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)
ir->l.add_to_buf = add_to_buf_pv951;
break;
case 0x71:
- if (adap->id == I2C_HW_B_BT848 ||
- adap->id == I2C_HW_B_CX2341X) {
+#ifdef I2C_HW_B_CX2341X
+ if (adap->id == (I2C_ALGO_BIT | I2C_HW_B_BT848) ||
+ adap->id == (I2C_ALGO_BIT | I2C_HW_B_CX2341X)) {
+#else
+ if (adap->id == (I2C_ALGO_BIT | I2C_HW_B_BT848)) {
+#endif
/*
* The PVR150 IR receiver uses the same protocol as
* other Hauppauge cards, but the data flow is
@@ -447,8 +516,12 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)
break;
case 0x18:
case 0x1a:
- if (adap->id == I2C_HW_B_BT848 ||
- adap->id == I2C_HW_B_CX2341X) {
+#ifdef I2C_HW_B_CX2341X
+ if (adap->id == (I2C_ALGO_BIT | I2C_HW_B_BT848) ||
+ adap->id == (I2C_ALGO_BIT | I2C_HW_B_CX2341X)) {
+#else
+ if (adap->id == (I2C_ALGO_BIT | I2C_HW_B_BT848)) {
+#endif
strlcpy(ir->c.name, "Hauppauge IR", I2C_NAME_SIZE);
ir->l.code_length = 13;
ir->l.add_to_buf = add_to_buf_haup;
@@ -465,7 +538,12 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)
break;
case 0x21:
case 0x23:
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)
+ ir->bits = flags & 0xff;
+ ir->flag = (flags >> 8) & 0xff;
+#else
pcf_probe(client, ir);
+#endif
strlcpy(ir->c.name, "TV-Box IR", I2C_NAME_SIZE);
ir->l.code_length = 8;
ir->l.add_to_buf = add_to_buf_pcf8574;
@@ -479,10 +557,22 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)
printk(KERN_INFO "lirc_i2c: chip 0x%x found @ 0x%02x (%s)\n",
adap->id, addr, ir->c.name);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)
+ /* register device */
+ err = i2c_attach_client(&ir->c);
+ if (err) {
+ kfree(ir);
+ return err;
+ }
+#endif
+
retval = lirc_register_driver(&ir->l);
if (retval < 0) {
printk(KERN_ERR "lirc_i2c: failed to register driver!\n");
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)
+ i2c_detach_client(&ir->c);
+#endif
kfree(ir);
return retval;
}
@@ -498,20 +588,169 @@ static int ir_remove(struct i2c_client *client)
/* unregister device */
lirc_unregister_driver(ir->l.minor);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)
+ i2c_detach_client(&ir->c);
+#endif
/* free memory */
kfree(ir);
return 0;
}
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)
+static int ir_probe(struct i2c_adapter *adap)
+{
+ /*
+ * The external IR receiver is at i2c address 0x34 (0x35 for
+ * reads). Future Hauppauge cards will have an internal
+ * receiver at 0x30 (0x31 for reads). In theory, both can be
+ * fitted, and Hauppauge suggest an external overrides an
+ * internal.
+ *
+ * That's why we probe 0x1a (~0x34) first. CB
+ *
+ * The i2c address for the Hauppauge PVR-150 card is 0xe2,
+ * so we need to probe 0x71 as well.
+ */
+
+ static const int probe[] = {
+ 0x1a, /* Hauppauge IR external */
+ 0x18, /* Hauppauge IR internal */
+ 0x71, /* Hauppauge IR (PVR150) */
+ 0x4b, /* PV951 IR */
+ 0x64, /* Pixelview IR */
+ 0x30, /* KNC ONE IR */
+ 0x6b, /* Adaptec IR */
+ -1};
+
+#ifdef I2C_HW_B_CX2388x
+ static const int probe_cx88[] = {
+ 0x18, /* Leadtek Winfast PVR2000 */
+ 0x71, /* Hauppauge HVR-IR */
+ -1};
+#endif
+
+ struct i2c_client c;
+ char buf;
+ int i, rc;
+
+ memset(&c, 0, sizeof(c));
+#ifdef I2C_HW_B_CX2341X
+ if (adap->id == (I2C_ALGO_BIT | I2C_HW_B_BT848) ||
+ adap->id == (I2C_ALGO_BIT | I2C_HW_B_CX2341X)) {
+#else
+ if (adap->id == (I2C_ALGO_BIT | I2C_HW_B_BT848)) {
+#endif
+ c.adapter = adap;
+ for (i = 0; -1 != probe[i]; i++) {
+ c.addr = probe[i];
+ rc = i2c_master_recv(&c, &buf, 1);
+ dprintk("probe 0x%02x @ %s: %s\n",
+ probe[i], adap->name,
+ (1 == rc) ? "yes" : "no");
+ if (1 == rc) {
+ rc = ir_attach(adap, probe[i], 0, 0);
+ if (rc < 0)
+ goto attach_fail;
+ }
+ }
+ }
+
+#ifdef I2C_HW_B_CX2388x
+ /* Leadtek Winfast PVR2000 or Hauppauge HVR-1300 */
+ else if (adap->id == (I2C_ALGO_BIT | I2C_HW_B_CX2388x)) {
+ c.adapter = adap;
+ for (i = 0; -1 != probe_cx88[i]; i++) {
+ c.addr = probe_cx88[i];
+ rc = i2c_master_recv(&c, &buf, 1);
+ dprintk("probe 0x%02x @ %s: %s\n",
+ c.addr, adap->name,
+ (1 == rc) ? "yes" : "no");
+ if (1 == rc) {
+ rc = ir_attach(adap, c.addr, 0, 0);
+ if (rc < 0)
+ goto attach_fail;
+ }
+ }
+ }
+#endif
+
+ /* Asus TV-Box and Creative/VisionTek BreakOut-Box (PCF8574) */
+ else if (adap->id == (I2C_ALGO_BIT | I2C_HW_B_RIVA)) {
+ /*
+ * addresses to probe;
+ * leave 0x24 and 0x25 because SAA7113H possibly uses it
+ * 0x21 and 0x22 possibly used by SAA7108E
+ * Asus: 0x21 is a correct address (channel 1 of PCF8574)
+ * Creative: 0x23 is a correct address (channel 3 of PCF8574)
+ * VisionTek: 0x23 is a correct address (channel 3 of PCF8574)
+ */
+ static const int pcf_probe[] = { 0x20, 0x21, 0x22, 0x23,
+ 0x24, 0x25, 0x26, 0x27, -1 };
+ int ret1, ret2, ret3, ret4;
+ unsigned char bits = 0, flag = 0;
+
+ c.adapter = adap;
+ for (i = 0; -1 != pcf_probe[i]; i++) {
+ c.addr = pcf_probe[i];
+ ret1 = i2c_smbus_write_byte(&c, 0xff);
+ ret2 = i2c_smbus_read_byte(&c);
+ ret3 = i2c_smbus_write_byte(&c, 0x00);
+ ret4 = i2c_smbus_read_byte(&c);
+
+ /* ensure that the writable bitmask works correctly */
+ rc = 0;
+ if (ret1 != -1 && ret2 != -1 &&
+ ret3 != -1 && ret4 != -1) {
+ /* in the Asus TV-Box: bit 1-0 */
+ if (((ret2 & 0x03) == 0x03) &&
+ ((ret4 & 0x03) == 0x00)) {
+ bits = (unsigned char) ~0x07;
+ flag = 0x04;
+ rc = 1;
+ }
+ /* in the Creative/VisionTek BreakOut-Box: bit 7-6 */
+ if (((ret2 & 0xc0) == 0xc0) &&
+ ((ret4 & 0xc0) == 0x00)) {
+ bits = (unsigned char) ~0xe0;
+ flag = 0x20;
+ rc = 1;
+ }
+ }
+ dprintk("probe 0x%02x @ %s: %s\n",
+ c.addr, adap->name, rc ? "yes" : "no");
+ if (rc) {
+ rc = ir_attach(adap, pcf_probe[i],
+ bits | (flag << 8), 0);
+ if (rc < 0)
+ goto attach_fail;
+ }
+ }
+ }
+
+ return 0;
+
+attach_fail:
+ printk(KERN_ERR "lirc_i2c: %s: ir_attach failed!\n", __func__);
+ return rc;
+
+}
+#endif
+
static int ir_command(struct i2c_client *client, unsigned int cmd, void *arg)
{
/* nothing */
return 0;
}
+#ifdef MODULE
+
static int __init lirc_i2c_init(void)
{
+ request_module("bttv");
+ request_module("rivatv");
+ request_module("ivtv");
+ request_module("cx8800");
i2c_add_driver(&driver);
return 0;
}
@@ -535,3 +774,6 @@ MODULE_PARM_DESC(debug, "Enable debugging messages");
module_init(lirc_i2c_init);
module_exit(lirc_i2c_exit);
+EXPORT_NO_SYMBOLS;
+
+#endif /* MODULE */
diff --git a/ubuntu/lirc/lirc_igorplugusb/lirc_igorplugusb.c b/ubuntu/lirc/lirc_igorplugusb/lirc_igorplugusb.c
index 4ff2725dab9..67055706e15 100644
--- a/ubuntu/lirc/lirc_igorplugusb/lirc_igorplugusb.c
+++ b/ubuntu/lirc/lirc_igorplugusb/lirc_igorplugusb.c
@@ -69,7 +69,7 @@
#endif
/* module identification */
-#define DRIVER_VERSION "0.1"
+#define DRIVER_VERSION "0.2"
#define DRIVER_AUTHOR \
"Jan M. Hochstein <hochstein@algo.informatik.tu-darmstadt.de>"
#define DRIVER_DESC "USB remote driver for LIRC"
@@ -268,6 +268,23 @@ static void set_use_dec(void *data)
MOD_DEC_USE_COUNT;
}
+static void send_fragment(struct igorplug *ir, struct lirc_buffer *buf,
+ int i, int max)
+{
+ /* MODE2: pulse/space (PULSE_BIT) in 1us units */
+ while (i < max) {
+ /* 1 Igor-tick = 85.333333 us */
+ lirc_t code = (unsigned int)ir->buf_in[i] * 85 +
+ (unsigned int)ir->buf_in[i] / 3;
+ ir->last_time.tv_usec += code;
+ if (ir->in_space)
+ code |= PULSE_BIT;
+ lirc_buffer_write_n(buf, (unsigned char *)&code, 1);
+ /* 1 chunk = CODE_LENGTH bytes */
+ ir->in_space ^= 1;
+ ++i;
+ }
+}
/**
* Called in user context.
@@ -292,24 +309,16 @@ static int usb_remote_poll(void *data, struct lirc_buffer *buf)
ir->buf_in, ir->len_in,
/*timeout*/HZ * USB_CTRL_GET_TIMEOUT);
if (ret > 0) {
- int i = DEVICE_HEADERLEN;
lirc_t code, timediff;
struct timeval now;
- if (ret <= 1) /* ACK packet has 1 byte --> ignore */
+ /* ACK packet has 1 byte --> ignore */
+ if (ret < DEVICE_HEADERLEN)
return -ENODATA;
dprintk(": 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(KERN_WARNING 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)
@@ -326,19 +335,21 @@ static int usb_remote_poll(void *data, struct lirc_buffer *buf)
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;
- ir->last_time.tv_usec += code;
- if (ir->in_space)
- code |= PULSE_BIT;
- lirc_buffer_write(buf, (unsigned char *)&code);
- /* 1 chunk = CODE_LENGTH bytes */
- ir->in_space ^= 1;
- ++i;
+ if (ir->buf_in[2] == 0)
+ send_fragment(ir, buf, DEVICE_HEADERLEN, ret);
+ else {
+ printk(KERN_WARNING DRIVER_NAME
+ "[%d]: Device buffer overrun.\n", ir->devnum);
+ /* HHHNNNNNNNNNNNOOOOOOOO H = header
+ <---[2]---> N = newer
+ <---------ret--------> O = older */
+ ir->buf_in[2] %= ret - DEVICE_HEADERLEN; /* sanitize */
+ /* keep even-ness to not desync pulse/pause */
+ send_fragment(ir, buf, DEVICE_HEADERLEN +
+ ir->buf_in[2] - (ir->buf_in[2] & 1),
+ ret);
+ send_fragment(ir, buf, DEVICE_HEADERLEN,
+ DEVICE_HEADERLEN + ir->buf_in[2]);
}
ret = usb_control_msg(
@@ -569,6 +580,8 @@ static void usb_remote_disconnect(struct usb_device *dev, void *ptr)
static struct usb_device_id usb_remote_id_table [] = {
/* Igor Plug USB (Atmel's Manufact. ID) */
{ USB_DEVICE(0x03eb, 0x0002) },
+ /* Fit PC2 Infrared Adapter */
+ { USB_DEVICE(0x03eb, 0x21fe) },
/* Terminating entry */
{ }
diff --git a/ubuntu/lirc/lirc_imon/lirc_imon.c b/ubuntu/lirc/lirc_imon/lirc_imon.c
index 52abbb341dd..fcaa45fe937 100644
--- a/ubuntu/lirc/lirc_imon/lirc_imon.c
+++ b/ubuntu/lirc/lirc_imon/lirc_imon.c
@@ -1,8 +1,8 @@
/*
- * lirc_imon.c: LIRC/VFD/LCD driver for Ahanix/Soundgraph iMON IR/VFD/LCD
+ * lirc_imon.c: LIRC/VFD/LCD driver for SoundGraph iMON IR/VFD/LCD
* including the iMON PAD model
*
- * $Id: lirc_imon.c,v 1.59 2009/04/10 20:51:29 jarodwilson Exp $
+ * $Id: lirc_imon.c,v 1.111 2009/09/11 04:56:18 jarodwilson Exp $
*
* Copyright(C) 2004 Venky Raju(dev@venky.ws)
*
@@ -26,8 +26,8 @@
#include <config.h>
#endif
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 22)
-#error "*** Sorry, this driver requires kernel version 2.4.22 or higher"
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0)
+#error "*** Sorry, this driver requires a 2.6 kernel"
#endif
#include <linux/autoconf.h>
@@ -43,7 +43,13 @@
#include <linux/uaccess.h>
#endif
#include <linux/usb.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18)
+#include <linux/usb/input.h>
+#else
+#include <linux/input.h>
+#endif
#include <linux/time.h>
+#include <linux/timer.h>
#include "../kcompat.h"
#include "../lirc.h"
@@ -51,44 +57,47 @@
#define MOD_AUTHOR "Venky Raju <dev@venky.ws>"
-#define MOD_DESC "Driver for Soundgraph iMON MultiMedia IR/Display"
+#define MOD_DESC "Driver for SoundGraph iMON MultiMedia IR/Display"
#define MOD_NAME "lirc_imon"
-#define MOD_VERSION "0.5"
+#define MOD_VERSION "0.6"
-#define DISPLAY_MINOR_BASE 144 /* Same as LCD */
+#define DISPLAY_MINOR_BASE 144
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 15)
#define DEVFS_MODE (S_IFCHR | S_IRUSR | S_IWUSR | \
S_IRGRP | S_IWGRP | S_IROTH)
#endif
-#define DEVFS_NAME LIRC_DEVFS_PREFIX "lcd%d"
+#define DEVICE_NAME LIRC_DEVFS_PREFIX "lcd%d"
#define BUF_CHUNK_SIZE 4
#define BUF_SIZE 128
#define BIT_DURATION 250 /* each bit received is 250us */
+#define IMON_CLOCK_ENABLE_PACKETS 2
+
+#define dprintk(fmt, args...) \
+ do { \
+ if (debug) \
+ printk(KERN_INFO MOD_NAME ": " fmt, ## args); \
+ } while (0)
+
/*** P R O T O T Y P E S ***/
/* USB Callback prototypes */
-#ifdef KERNEL_2_5
static int imon_probe(struct usb_interface *interface,
const struct usb_device_id *id);
static void imon_disconnect(struct usb_interface *interface);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
-static void usb_rx_callback(struct urb *urb, struct pt_regs *regs);
+static void usb_rx_callback_intf0(struct urb *urb, struct pt_regs *regs);
+static void usb_rx_callback_intf1(struct urb *urb, struct pt_regs *regs);
static void usb_tx_callback(struct urb *urb, struct pt_regs *regs);
#else
-static void usb_rx_callback(struct urb *urb);
-static void usb_tx_callback(struct urb *urb);
-#endif
-#else
-static void *imon_probe(struct usb_device *dev, unsigned int intf,
- const struct usb_device_id *id);
-static void imon_disconnect(struct usb_device *dev, void *data);
-static void usb_rx_callback(struct urb *urb);
+static void usb_rx_callback_intf0(struct urb *urb);
+static void usb_rx_callback_intf1(struct urb *urb);
static void usb_tx_callback(struct urb *urb);
#endif
+/* suspend/resume support */
static int imon_resume(struct usb_interface *intf);
static int imon_suspend(struct usb_interface *intf, pm_message_t message);
@@ -115,26 +124,27 @@ static void __exit imon_exit(void);
/*** G L O B A L S ***/
struct imon_context {
- struct usb_device *usbdev;
+ struct usb_device *usbdev_intf0;
+ /* Newer devices have two interfaces */
+ struct usb_device *usbdev_intf1;
int display_supported; /* not all controllers do */
int display_isopen; /* display port has been opened */
-#if !defined(KERNEL_2_5)
- int subminor; /* index into minor_table */
- devfs_handle_t devfs;
-#endif
int ir_isopen; /* IR port open */
int ir_isassociating; /* IR port open for association */
- int dev_present; /* USB device presence */
+ int dev_present_intf0; /* USB device presence, interface 0 */
+ int dev_present_intf1; /* USB device presence, interface 1 */
struct mutex lock; /* to lock this object */
wait_queue_head_t remove_ok; /* For unexpected USB disconnects */
- int display_proto_6p; /* display requires 6th packet */
+ int vfd_proto_6p; /* some VFD require a 6th packet */
int ir_onboard_decode; /* IR signals decoded onboard */
struct lirc_driver *driver;
- struct usb_endpoint_descriptor *rx_endpoint;
+ struct usb_endpoint_descriptor *rx_endpoint_intf0;
+ struct usb_endpoint_descriptor *rx_endpoint_intf1;
struct usb_endpoint_descriptor *tx_endpoint;
- struct urb *rx_urb;
+ struct urb *rx_urb_intf0;
+ struct urb *rx_urb_intf1;
struct urb *tx_urb;
int tx_control;
unsigned char usb_rx_buf[8];
@@ -152,8 +162,26 @@ struct imon_context {
atomic_t busy; /* write in progress */
int status; /* status of tx completion */
} tx;
+
+ int ffdc_dev; /* is this the overused ffdc ID? */
+ int ir_protocol; /* iMON or MCE (RC6) IR protocol? */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18)
+ struct input_dev *mouse; /* input device for iMON PAD remote */
+ struct input_dev *touch; /* input device for touchscreen */
+#endif
+ int display_type; /* store the display type */
+ int pad_mouse; /* toggle kbd(0)/mouse(1) mode */
+ int touch_x; /* x coordinate on touchscreen */
+ int touch_y; /* y coordinate on touchscreen */
+ char name_mouse[128];
+ char phys_mouse[64];
+ char name_touch[128];
+ char phys_touch[64];
+ struct timer_list timer;
};
+#define TOUCH_TIMEOUT (HZ/30)
+
/* display file operations. Nb: lcd_write will be subbed in as needed later */
static struct file_operations display_fops = {
.owner = THIS_MODULE,
@@ -163,30 +191,81 @@ static struct file_operations display_fops = {
};
enum {
- IMON_DISPLAY_TYPE_AUTO,
- IMON_DISPLAY_TYPE_VFD,
- IMON_DISPLAY_TYPE_LCD,
- IMON_DISPLAY_TYPE_NONE,
+ IMON_DISPLAY_TYPE_AUTO = 0,
+ IMON_DISPLAY_TYPE_VFD = 1,
+ IMON_DISPLAY_TYPE_LCD = 2,
+ IMON_DISPLAY_TYPE_VGA = 3,
+ IMON_DISPLAY_TYPE_NONE = 4,
};
-/* USB Device ID for iMON USB Control Board */
+enum {
+ IMON_IR_PROTOCOL_IMON = 0,
+ IMON_IR_PROTOCOL_MCE = 1,
+ IMON_IR_PROTOCOL_IMON_NOPAD = 2,
+};
+/*
+ * 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[] = {
- /* iMON USB Control Board (IR & VFD) */
- { USB_DEVICE(0x0aa8, 0xffda) },
- /* iMON USB Control Board (IR only) */
+ /* TriGem iMON (IR only) -- TG_iMON.inf */
{ USB_DEVICE(0x0aa8, 0x8001) },
- /* iMON USB Control Board (ext IR only) */
+
+ /* SoundGraph iMON (IR only) -- sg_imon.inf */
{ USB_DEVICE(0x04e8, 0xff30) },
- /* iMON USB Control Board (IR & VFD) */
+
+ /* SoundGraph iMON VFD (IR & VFD) -- iMON_VFD.inf */
+ { USB_DEVICE(0x0aa8, 0xffda) },
+
+ /* SoundGraph iMON SS (IR & VFD) -- iMON_SS.inf */
{ USB_DEVICE(0x15c2, 0xffda) },
- /* iMON USB Control Board (IR & LCD) *and* iMON Knob (IR only) */
+
+ /*
+ * Several devices with this same device ID, all use iMON_PAD.inf
+ * SoundGraph iMON PAD (IR & VFD)
+ * SoundGraph iMON PAD (IR & LCD)
+ * SoundGraph iMON Knob (IR only)
+ */
+ /* SoundGraph iMON PAD (IR & VFD/LCD), iMON Knob */
{ USB_DEVICE(0x15c2, 0xffdc) },
- /* iMON USB Control Board (IR & LCD) */
+
+ /*
+ * Newer devices, all driven by the latest iMON Windows driver, full
+ * list of device IDs extracted via 'strings Setup/data1.hdr |grep 15c2'
+ * Need user input to fill in details on unknown devices.
+ */
+ /* SoundGraph iMON OEM Touch LCD (IR & 7" VGA LCD) */
{ USB_DEVICE(0x15c2, 0x0034) },
- /* iMON USB Control Board (IR & VFD) */
+ /* SoundGraph iMON OEM Touch LCD (IR & 4.3" VGA LCD) */
+ { USB_DEVICE(0x15c2, 0x0035) },
+ /* SoundGraph iMON OEM VFD (IR & VFD) */
{ USB_DEVICE(0x15c2, 0x0036) },
- /* iMON USB Control Board (IR & LCD) */
+ /* device specifics unknown */
+ { USB_DEVICE(0x15c2, 0x0037) },
+ /* SoundGraph iMON OEM LCD (IR & LCD) */
{ USB_DEVICE(0x15c2, 0x0038) },
+ /* device specifics unknown */
+ { USB_DEVICE(0x15c2, 0x0039) },
+ /* device specifics unknown */
+ { USB_DEVICE(0x15c2, 0x003a) },
+ /* device specifics unknown */
+ { USB_DEVICE(0x15c2, 0x003b) },
+ /* SoundGraph iMON OEM Inside (IR only) */
+ { USB_DEVICE(0x15c2, 0x003c) },
+ /* device specifics unknown */
+ { USB_DEVICE(0x15c2, 0x003d) },
+ /* device specifics unknown */
+ { USB_DEVICE(0x15c2, 0x003e) },
+ /* device specifics unknown */
+ { USB_DEVICE(0x15c2, 0x003f) },
+ /* device specifics unknown */
+ { USB_DEVICE(0x15c2, 0x0040) },
/* SoundGraph iMON MINI (IR only) */
{ USB_DEVICE(0x15c2, 0x0041) },
/* Antec Veris Multimedia Station EZ External (IR only) */
@@ -197,50 +276,56 @@ static struct usb_device_id imon_usb_id_table[] = {
{ USB_DEVICE(0x15c2, 0x0044) },
/* Antec Veris Multimedia Station Premiere (IR & LCD) */
{ USB_DEVICE(0x15c2, 0x0045) },
+ /* device specifics unknown */
+ { USB_DEVICE(0x15c2, 0x0046) },
{}
};
-/* Some iMON models requires a 6th packet */
-static struct usb_device_id display_proto_6p_list[] = {
+/* Some iMON VFD models requires a 6th packet for VFD writes */
+static struct usb_device_id vfd_proto_6p_list[] = {
{ USB_DEVICE(0x15c2, 0xffda) },
{ USB_DEVICE(0x15c2, 0xffdc) },
- { USB_DEVICE(0x15c2, 0x0034) },
{ USB_DEVICE(0x15c2, 0x0036) },
- { USB_DEVICE(0x15c2, 0x0038) },
- { USB_DEVICE(0x15c2, 0x0041) },
- { USB_DEVICE(0x15c2, 0x0042) },
- { USB_DEVICE(0x15c2, 0x0043) },
{ USB_DEVICE(0x15c2, 0x0044) },
- { USB_DEVICE(0x15c2, 0x0045) },
{}
};
-static unsigned char display_packet6[] = {
- 0x01, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF };
/* newer iMON models use control endpoints */
static struct usb_device_id ctl_ep_device_list[] = {
{ USB_DEVICE(0x15c2, 0x0034) },
+ { USB_DEVICE(0x15c2, 0x0035) },
{ USB_DEVICE(0x15c2, 0x0036) },
+ { USB_DEVICE(0x15c2, 0x0037) },
{ USB_DEVICE(0x15c2, 0x0038) },
+ { USB_DEVICE(0x15c2, 0x0039) },
+ { USB_DEVICE(0x15c2, 0x003a) },
+ { USB_DEVICE(0x15c2, 0x003b) },
+ { USB_DEVICE(0x15c2, 0x003c) },
+ { USB_DEVICE(0x15c2, 0x003d) },
+ { USB_DEVICE(0x15c2, 0x003e) },
+ { USB_DEVICE(0x15c2, 0x003f) },
+ { USB_DEVICE(0x15c2, 0x0040) },
{ USB_DEVICE(0x15c2, 0x0041) },
{ USB_DEVICE(0x15c2, 0x0042) },
{ USB_DEVICE(0x15c2, 0x0043) },
{ USB_DEVICE(0x15c2, 0x0044) },
{ USB_DEVICE(0x15c2, 0x0045) },
+ { USB_DEVICE(0x15c2, 0x0046) },
{}
};
/* iMON LCD models use a different write op */
static struct usb_device_id lcd_device_list[] = {
{ USB_DEVICE(0x15c2, 0xffdc) },
- { USB_DEVICE(0x15c2, 0x0034) },
{ USB_DEVICE(0x15c2, 0x0038) },
{ USB_DEVICE(0x15c2, 0x0045) },
{}
};
-/* iMON devices with front panel buttons need a larger buffer */
+/* iMON devices with front panel buttons or touchscreen need a larger buffer */
static struct usb_device_id large_buffer_list[] = {
+ { USB_DEVICE(0x15c2, 0x0034) },
+ { USB_DEVICE(0x15c2, 0x0035) },
{ USB_DEVICE(0x15c2, 0x0038) },
{ USB_DEVICE(0x15c2, 0x0045) },
};
@@ -249,13 +334,24 @@ static struct usb_device_id large_buffer_list[] = {
static struct usb_device_id ir_onboard_decode_list[] = {
{ USB_DEVICE(0x15c2, 0xffdc) },
{ USB_DEVICE(0x15c2, 0x0034) },
+ { USB_DEVICE(0x15c2, 0x0035) },
{ USB_DEVICE(0x15c2, 0x0036) },
+ { USB_DEVICE(0x15c2, 0x0037) },
{ USB_DEVICE(0x15c2, 0x0038) },
+ { USB_DEVICE(0x15c2, 0x0039) },
+ { USB_DEVICE(0x15c2, 0x003a) },
+ { USB_DEVICE(0x15c2, 0x003b) },
+ { USB_DEVICE(0x15c2, 0x003c) },
+ { USB_DEVICE(0x15c2, 0x003d) },
+ { USB_DEVICE(0x15c2, 0x003e) },
+ { USB_DEVICE(0x15c2, 0x003f) },
+ { USB_DEVICE(0x15c2, 0x0040) },
{ USB_DEVICE(0x15c2, 0x0041) },
{ USB_DEVICE(0x15c2, 0x0042) },
{ USB_DEVICE(0x15c2, 0x0043) },
{ USB_DEVICE(0x15c2, 0x0044) },
{ USB_DEVICE(0x15c2, 0x0045) },
+ { USB_DEVICE(0x15c2, 0x0046) },
{}
};
@@ -265,12 +361,20 @@ static struct usb_device_id ir_only_list[] = {
{ USB_DEVICE(0x04e8, 0xff30) },
/* the first imon lcd and the knob share this device id. :\ */
/*{ USB_DEVICE(0x15c2, 0xffdc) },*/
+ { USB_DEVICE(0x15c2, 0x003c) },
{ USB_DEVICE(0x15c2, 0x0041) },
{ USB_DEVICE(0x15c2, 0x0042) },
{ USB_DEVICE(0x15c2, 0x0043) },
{}
};
+/* iMON devices with VGA touchscreens */
+static struct usb_device_id imon_touchscreen_list[] = {
+ { USB_DEVICE(0x15c2, 0x0034) },
+ { USB_DEVICE(0x15c2, 0x0035) },
+ {}
+};
+
/* USB Device data */
static struct usb_driver imon_driver = {
LIRC_THIS_MODULE(.owner = THIS_MODULE)
@@ -280,68 +384,72 @@ static struct usb_driver imon_driver = {
.suspend = imon_suspend,
.resume = imon_resume,
.id_table = imon_usb_id_table,
-#if !defined(KERNEL_2_5)
- .fops = &display_fops,
- .minor = DISPLAY_MINOR_BASE,
-#endif
};
-#ifdef KERNEL_2_5
static struct usb_class_driver imon_class = {
- .name = DEVFS_NAME,
+ .name = DEVICE_NAME,
.fops = &display_fops,
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 15)
.mode = DEVFS_MODE,
#endif
.minor_base = DISPLAY_MINOR_BASE,
};
-#endif
-/* to prevent races between open() and disconnect() */
-static DEFINE_MUTEX(disconnect_lock);
+/* to prevent races between open() and disconnect(), probing, etc */
+static DEFINE_MUTEX(driver_lock);
static int debug;
-/* lcd, vfd or none? should be auto-detected, but can be overridden... */
+/* lcd, vfd, vga or none? should be auto-detected, but can be overridden... */
static int display_type;
-#if !defined(KERNEL_2_5)
-
-#define MAX_DEVICES 4 /* In case there's more than one iMON device */
+/* IR protocol: native iMON, Windows MCE (RC-6), or iMON w/o PAD stabilize */
+static int ir_protocol;
-static struct imon_context *minor_table[MAX_DEVICES];
+/*
+ * In certain use cases, mouse mode isn't really helpful, and could actually
+ * cause confusion, so allow disabling it when the IR device is open.
+ */
+static int nomouse;
-/* the global usb devfs handle */
-extern devfs_handle_t usb_devfs_handle;
+/* threshold at which a pad push registers as an arrow key in kbd mode */
+static int pad_thresh;
-#endif
/*** 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)");
-
module_param(display_type, int, S_IRUGO);
MODULE_PARM_DESC(display_type, "Type of attached display. 0=autodetect, "
- "1=vfd, 2=lcd, 3=none (default: autodetect)");
-
-static void delete_context(struct imon_context *context)
+ "1=vfd, 2=lcd, 3=vga, 4=none (default: autodetect)");
+module_param(ir_protocol, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(ir_protocol, "Which IR protocol to use. 0=native iMON, "
+ "1=Windows Media Center Ed. (RC-6), 2=iMON w/o PAD stabilize "
+ "(default: native iMON)");
+module_param(nomouse, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(nomouse, "Disable mouse input device mode when IR device is "
+ "open. 0=don't disable, 1=disable. (default: don't disable)");
+module_param(pad_thresh, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(pad_thresh, "Threshold at which a pad push registers as an "
+ "arrow key in kbd mode (default: 28)");
+
+static void free_imon_context(struct imon_context *context)
{
- if (context->display_supported)
- usb_free_urb(context->tx_urb);
- usb_free_urb(context->rx_urb);
+ usb_free_urb(context->tx_urb);
+ usb_free_urb(context->rx_urb_intf0);
+ usb_free_urb(context->rx_urb_intf1);
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__);
+ dprintk("%s: iMON context freed\n", __func__);
}
static void deregister_from_lirc(struct imon_context *context)
@@ -354,8 +462,8 @@ static void deregister_from_lirc(struct imon_context *context)
err("%s: unable to deregister from lirc(%d)",
__func__, retval);
else
- printk(KERN_INFO "Deregistered iMON driver(minor:%d)\n",
- minor);
+ printk(KERN_INFO MOD_NAME ": Deregistered iMON driver "
+ "(minor:%d)\n", minor);
}
@@ -365,17 +473,14 @@ static void deregister_from_lirc(struct imon_context *context)
*/
static int display_open(struct inode *inode, struct file *file)
{
-#ifdef KERNEL_2_5
struct usb_interface *interface;
-#endif
struct imon_context *context = NULL;
int subminor;
int retval = 0;
/* prevent races with disconnect */
- mutex_lock(&disconnect_lock);
+ mutex_lock(&driver_lock);
-#ifdef KERNEL_2_5
subminor = iminor(inode);
interface = usb_find_interface(&imon_driver, subminor);
if (!interface) {
@@ -385,15 +490,6 @@ static int display_open(struct inode *inode, struct file *file)
goto exit;
}
context = usb_get_intfdata(interface);
-#else
- subminor = MINOR(inode->i_rdev) - DISPLAY_MINOR_BASE;
- if (subminor < 0 || subminor >= MAX_DEVICES) {
- err("%s: no record of minor %d", __func__, subminor);
- retval = -ENODEV;
- goto exit;
- }
- context = minor_table[subminor];
-#endif
if (!context) {
err("%s: no context found for minor %d",
@@ -420,12 +516,12 @@ static int display_open(struct inode *inode, struct file *file)
mutex_unlock(&context->lock);
exit:
- mutex_unlock(&disconnect_lock);
+ mutex_unlock(&driver_lock);
return retval;
}
/**
- * Called when the display device(e.g. /dev/lcd0)
+ * Called when the display device (e.g. /dev/lcd0)
* is closed by the application.
*/
static int display_close(struct inode *inode, struct file *file)
@@ -433,7 +529,7 @@ 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;
+ context = (struct imon_context *)file->private_data;
if (!context) {
err("%s: no context for device", __func__);
@@ -452,14 +548,14 @@ static int display_close(struct inode *inode, struct file *file)
context->display_isopen = 0;
MOD_DEC_USE_COUNT;
printk(KERN_INFO "display port closed\n");
- if (!context->dev_present && !context->ir_isopen) {
+ if (!context->dev_present_intf0 && !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->lock);
- delete_context(context);
+ free_imon_context(context);
return retval;
}
}
@@ -469,7 +565,7 @@ static int display_close(struct inode *inode, struct file *file)
}
/**
- * Sends a packet to the display.
+ * Sends a packet to the device
*/
static int send_packet(struct imon_context *context)
{
@@ -480,13 +576,11 @@ static int send_packet(struct imon_context *context)
/* Check if we need to use control or interrupt urb */
if (!context->tx_control) {
- pipe = usb_sndintpipe(context->usbdev,
+ pipe = usb_sndintpipe(context->usbdev_intf0,
context->tx_endpoint->bEndpointAddress);
-#ifdef KERNEL_2_5
interval = context->tx_endpoint->bInterval;
-#endif /* Use 0 for 2.4 kernels */
- usb_fill_int_urb(context->tx_urb, context->usbdev, pipe,
+ usb_fill_int_urb(context->tx_urb, context->usbdev_intf0, pipe,
context->usb_tx_buf,
sizeof(context->usb_tx_buf),
usb_tx_callback, context, interval);
@@ -507,10 +601,10 @@ static int send_packet(struct imon_context *context)
control_req->wLength = cpu_to_le16(0x0008);
/* control pipe is endpoint 0x00 */
- pipe = usb_sndctrlpipe(context->usbdev, 0);
+ pipe = usb_sndctrlpipe(context->usbdev_intf0, 0);
/* build the control urb */
- usb_fill_control_urb(context->tx_urb, context->usbdev, pipe,
+ usb_fill_control_urb(context->tx_urb, context->usbdev_intf0, pipe,
(unsigned char *)control_req,
context->usb_tx_buf,
sizeof(context->usb_tx_buf),
@@ -521,18 +615,17 @@ static int send_packet(struct imon_context *context)
init_completion(&context->tx.finished);
atomic_set(&(context->tx.busy), 1);
-#ifdef KERNEL_2_5
retval = usb_submit_urb(context->tx_urb, GFP_KERNEL);
-#else
- retval = usb_submit_urb(context->tx_urb);
-#endif
if (retval) {
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->lock);
- wait_for_completion(&context->tx.finished);
+ retval = wait_for_completion_interruptible(
+ &context->tx.finished);
+ if (retval)
+ err("%s: task interrupted", __func__);
mutex_lock(&context->lock);
retval = context->tx.status;
@@ -559,24 +652,115 @@ static int send_associate_24g(struct imon_context *context)
const unsigned char packet[8] = { 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x20 };
- if (!context->dev_present) {
+ if (!context) {
+ err("%s: no context for device", __func__);
+ return -ENODEV;
+ }
+
+ if (!context->dev_present_intf0) {
err("%s: no iMON device present", __func__);
- retval = -ENODEV;
- goto exit;
+ return -ENODEV;
}
memcpy(context->usb_tx_buf, packet, sizeof(packet));
retval = send_packet(context);
-exit:
return retval;
}
-#ifdef KERNEL_2_5
/**
- * These are the sysfs functions to handle the association on the iMON 2.4G LT.
+ * Sends packets to setup and show clock on iMON display
+ *
+ * Arguments: year - last 2 digits of year, month - 1..12,
+ * day - 1..31, dow - day of the week (0-Sun...6-Sat),
+ * hour - 0..23, minute - 0..59, second - 0..59
*/
+static int send_set_imon_clock(struct imon_context *context,
+ unsigned int year, unsigned int month,
+ unsigned int day, unsigned int dow,
+ unsigned int hour, unsigned int minute,
+ unsigned int second)
+{
+ unsigned char clock_enable_pkt[IMON_CLOCK_ENABLE_PACKETS][8];
+ int retval = 0;
+ int i;
+ if (!context) {
+ err("%s: no context for device", __func__);
+ return -ENODEV;
+ }
+
+ switch(context->display_type) {
+ case IMON_DISPLAY_TYPE_LCD:
+ clock_enable_pkt[0][0] = 0x80;
+ clock_enable_pkt[0][1] = year;
+ clock_enable_pkt[0][2] = month-1;
+ clock_enable_pkt[0][3] = day;
+ clock_enable_pkt[0][4] = hour;
+ clock_enable_pkt[0][5] = minute;
+ clock_enable_pkt[0][6] = second;
+
+ clock_enable_pkt[1][0] = 0x80;
+ clock_enable_pkt[1][1] = 0;
+ clock_enable_pkt[1][2] = 0;
+ clock_enable_pkt[1][3] = 0;
+ clock_enable_pkt[1][4] = 0;
+ clock_enable_pkt[1][5] = 0;
+ clock_enable_pkt[1][6] = 0;
+
+ if (context->ffdc_dev) {
+ clock_enable_pkt[0][7] = 0x50;
+ clock_enable_pkt[1][7] = 0x51;
+ } else {
+ clock_enable_pkt[0][7] = 0x88;
+ clock_enable_pkt[1][7] = 0x8a;
+ }
+
+ break;
+
+ case IMON_DISPLAY_TYPE_VFD:
+ clock_enable_pkt[0][0] = year;
+ clock_enable_pkt[0][1] = month-1;
+ clock_enable_pkt[0][2] = day;
+ clock_enable_pkt[0][3] = dow;
+ clock_enable_pkt[0][4] = hour;
+ clock_enable_pkt[0][5] = minute;
+ clock_enable_pkt[0][6] = second;
+ clock_enable_pkt[0][7] = 0x40;
+
+ clock_enable_pkt[1][0] = 0;
+ clock_enable_pkt[1][1] = 0;
+ clock_enable_pkt[1][2] = 1;
+ clock_enable_pkt[1][3] = 0;
+ clock_enable_pkt[1][4] = 0;
+ clock_enable_pkt[1][5] = 0;
+ clock_enable_pkt[1][6] = 0;
+ clock_enable_pkt[1][7] = 0x42;
+
+ break;
+
+ default:
+ return -ENODEV;
+ }
+
+
+ for (i = 0; i < IMON_CLOCK_ENABLE_PACKETS; i++) {
+ memcpy(context->usb_tx_buf, clock_enable_pkt[i], 8);
+ retval = send_packet(context);
+ if (retval) {
+ err("%s: send_packet failed for packet %d",
+ __func__, i);
+ break;
+ }
+ }
+
+ return retval;
+
+}
+
+/**
+ * These are the sysfs functions to handle the association on the iMON 2.4G LT.
+ */
static ssize_t show_associate_remote(struct device *d,
struct device_attribute *attr,
char *buf)
@@ -588,16 +772,15 @@ static ssize_t show_associate_remote(struct device *d,
mutex_lock(&context->lock);
if (context->ir_isassociating) {
- strcpy(buf, "The device it associating press some button "
- "on the remote.\n");
+ strcpy(buf, "associating\n");
} else if (context->ir_isopen) {
- strcpy(buf, "Device is open and ready to associate.\n"
- "Echo something into this file to start "
- "the process.\n");
+ strcpy(buf, "open\n");
} else {
- strcpy(buf, "Device is closed, you need to open it to "
- "associate the remote(you can use irw).\n");
+ strcpy(buf, "closed\n");
}
+ printk(KERN_INFO "Visit http://www.lirc.org/html/imon-24g.html for "
+ "instructions on how to associate your iMON 2.4G DT/LT "
+ "remote\n");
mutex_unlock(&context->lock);
return strlen(buf);
}
@@ -628,21 +811,105 @@ static ssize_t store_associate_remote(struct device *d,
return count;
}
+/**
+ * sysfs functions to control internal imon clock
+ */
+static ssize_t show_imon_clock(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ struct imon_context *context = dev_get_drvdata(d);
+ size_t len;
+
+ if (!context)
+ return -ENODEV;
+
+ mutex_lock(&context->lock);
+
+ if (!context->display_supported) {
+ len = snprintf(buf, PAGE_SIZE, "Not supported.");
+ } else {
+ len = snprintf(buf, PAGE_SIZE,
+ "To set the clock on your iMON display:\n"
+ "# date \"+%%y %%m %%d %%w %%H %%M %%S\" > imon_clock\n"
+ "%s", context->display_isopen ?
+ "\nNOTE: imon device must be closed\n" : "");
+ }
+
+ mutex_unlock(&context->lock);
+
+ return len;
+}
+
+static ssize_t store_imon_clock(struct device *d,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct imon_context *context = dev_get_drvdata(d);
+ ssize_t retval;
+ unsigned int year, month, day, dow, hour, minute, second;
+
+ if (!context)
+ return -ENODEV;
+
+ mutex_lock(&context->lock);
+
+ if (!context->display_supported) {
+ retval = -ENODEV;
+ goto exit;
+ } else if (context->display_isopen) {
+ retval = -EBUSY;
+ goto exit;
+ }
+
+ if (sscanf(buf, "%u %u %u %u %u %u %u", &year, &month, &day, &dow,
+ &hour, &minute, &second) != 7) {
+ retval = -EINVAL;
+ goto exit;
+ }
+
+ if ((month < 1 || month > 12) ||
+ (day < 1 || day > 31) || (dow > 6) ||
+ (hour > 23) || (minute > 59) || (second > 59)) {
+ retval = -EINVAL;
+ goto exit;
+ }
+
+ retval = send_set_imon_clock(context, year, month, day, dow,
+ hour, minute, second);
+ if (retval)
+ goto exit;
+
+ retval = count;
+exit:
+ mutex_unlock(&context->lock);
+
+ return retval;
+}
+
+
+static DEVICE_ATTR(imon_clock, S_IWUSR | S_IRUGO, show_imon_clock,
+ store_imon_clock);
+
static DEVICE_ATTR(associate_remote, S_IWUSR | S_IRUGO, show_associate_remote,
store_associate_remote);
-static struct attribute *imon_sysfs_entries[] = {
- &dev_attr_associate_remote.attr,
+static struct attribute *imon_display_sysfs_entries[] = {
+ &dev_attr_imon_clock.attr,
NULL
};
-static struct attribute_group imon_attribute_group = {
- .attrs = imon_sysfs_entries
+static struct attribute_group imon_display_attribute_group = {
+ .attrs = imon_display_sysfs_entries
};
-#endif
-
+static struct attribute *imon_rf_sysfs_entries[] = {
+ &dev_attr_associate_remote.attr,
+ NULL
+};
+static struct attribute_group imon_rf_attribute_group = {
+ .attrs = imon_rf_sysfs_entries
+};
/**
* Writes data to the VFD. The iMON VFD is 2x16 characters
@@ -663,8 +930,10 @@ static ssize_t vfd_write(struct file *file, const char *buf,
int seq;
int retval = 0;
struct imon_context *context;
+ const unsigned char vfd_packet6[] = {
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF };
- context = (struct imon_context *) file->private_data;
+ context = (struct imon_context *)file->private_data;
if (!context) {
err("%s: no context for device", __func__);
return -ENODEV;
@@ -672,7 +941,7 @@ static ssize_t vfd_write(struct file *file, const char *buf,
mutex_lock(&context->lock);
- if (!context->dev_present) {
+ if (!context->dev_present_intf0) {
err("%s: no iMON device present", __func__);
retval = -ENODEV;
goto exit;
@@ -715,9 +984,9 @@ static ssize_t vfd_write(struct file *file, const char *buf,
} while (offset < 35);
- if (context->display_proto_6p) {
+ if (context->vfd_proto_6p) {
/* Send packet #6 */
- memcpy(context->usb_tx_buf, display_packet6, 7);
+ memcpy(context->usb_tx_buf, &vfd_packet6, sizeof(vfd_packet6));
context->usb_tx_buf[7] = (unsigned char) seq;
retval = send_packet(context);
if (retval)
@@ -750,7 +1019,7 @@ static ssize_t lcd_write(struct file *file, const char *buf,
int retval = 0;
struct imon_context *context;
- context = (struct imon_context *) file->private_data;
+ context = (struct imon_context *)file->private_data;
if (!context) {
err("%s: no context for device", __func__);
return -ENODEV;
@@ -758,8 +1027,8 @@ static ssize_t lcd_write(struct file *file, const char *buf,
mutex_lock(&context->lock);
- if (!context->dev_present) {
- err("%s: no iMON device present", __func__);
+ if (!context->display_supported) {
+ err("%s: no iMON display present", __func__);
retval = -ENODEV;
goto exit;
}
@@ -780,9 +1049,8 @@ static ssize_t lcd_write(struct file *file, const char *buf,
if (retval) {
err("%s: send packet failed!", __func__);
goto exit;
- } else if (debug) {
- printk(KERN_INFO "%s: write %d bytes to LCD\n",
- __func__, (int) n_bytes);
+ } else {
+ dprintk("%s: write %d bytes to LCD\n", __func__, (int) n_bytes);
}
exit:
mutex_unlock(&context->lock);
@@ -792,7 +1060,7 @@ exit:
/**
* Callback function for USB core API: transmit data
*/
-#if defined(KERNEL_2_5) && LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
static void usb_tx_callback(struct urb *urb, struct pt_regs *regs)
#else
static void usb_tx_callback(struct urb *urb)
@@ -802,7 +1070,7 @@ static void usb_tx_callback(struct urb *urb)
if (!urb)
return;
- context = (struct imon_context *) urb->context;
+ context = (struct imon_context *)urb->context;
if (!context)
return;
@@ -816,6 +1084,56 @@ static void usb_tx_callback(struct urb *urb)
}
/**
+ * iMON IR receivers support two different signal sets -- those used by
+ * the iMON remotes, and those used by the Windows MCE remotes (which is
+ * really just RC-6), but only one or the other at a time, as the signals
+ * are decoded onboard the receiver.
+ */
+static void imon_set_ir_protocol(struct imon_context *context)
+{
+ int retval;
+ unsigned char ir_proto_packet[] =
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86 };
+
+ switch (ir_protocol) {
+ case IMON_IR_PROTOCOL_MCE:
+ /* MCE proto not supported on devices without tx control */
+ if (!context->tx_control) {
+ printk(KERN_INFO "%s: MCE IR protocol not supported on "
+ "this device, using iMON protocol\n", __func__);
+ context->ir_protocol = IMON_IR_PROTOCOL_IMON;
+ return;
+ }
+ dprintk("Configuring IR receiver for MCE protocol\n");
+ ir_proto_packet[0] = 0x01;
+ context->ir_protocol = IMON_IR_PROTOCOL_MCE;
+ break;
+ case IMON_IR_PROTOCOL_IMON:
+ dprintk("Configuring IR receiver for iMON protocol\n");
+ /* ir_proto_packet[0] = 0x00; // already the default */
+ context->ir_protocol = IMON_IR_PROTOCOL_IMON;
+ break;
+ case IMON_IR_PROTOCOL_IMON_NOPAD:
+ dprintk("Configuring IR receiver for iMON protocol without "
+ "PAD stabilize function enabled\n");
+ /* ir_proto_packet[0] = 0x00; // already the default */
+ context->ir_protocol = IMON_IR_PROTOCOL_IMON_NOPAD;
+ break;
+ default:
+ printk(KERN_INFO "%s: unknown IR protocol specified, will "
+ "just default to iMON protocol\n", __func__);
+ context->ir_protocol = IMON_IR_PROTOCOL_IMON;
+ break;
+ }
+ memcpy(context->usb_tx_buf, &ir_proto_packet,
+ sizeof(ir_proto_packet));
+ retval = send_packet(context);
+ if (retval)
+ printk(KERN_INFO "%s: failed to set remote type\n", __func__);
+}
+
+
+/**
* Called by lirc_dev when the application opens /dev/lirc
*/
static int ir_open(void *data)
@@ -824,48 +1142,23 @@ static int ir_open(void *data)
struct imon_context *context;
/* prevent races with disconnect */
- mutex_lock(&disconnect_lock);
-
- context = (struct imon_context *) data;
-
- mutex_lock(&context->lock);
+ mutex_lock(&driver_lock);
- if (context->ir_isopen) {
- err("%s: IR port is already open", __func__);
- retval = -EBUSY;
- goto exit;
- }
+ context = (struct imon_context *)data;
/* initial IR protocol decode variables */
context->rx.count = 0;
context->rx.initial_space = 1;
context->rx.prev_bit = 0;
- usb_fill_int_urb(context->rx_urb, context->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);
-
-#ifdef KERNEL_2_5
- retval = usb_submit_urb(context->rx_urb, GFP_KERNEL);
-#else
- retval = usb_submit_urb(context->rx_urb);
-#endif
-
- if (retval)
- err("%s: usb_submit_urb failed for ir_open(%d)",
- __func__, retval);
- else {
- MOD_INC_USE_COUNT;
- context->ir_isopen = 1;
- printk(KERN_INFO "IR port opened\n");
- }
+ /* set new IR protocol if it has changed since init or last open */
+ if (ir_protocol != context->ir_protocol)
+ imon_set_ir_protocol(context);
-exit:
- mutex_unlock(&context->lock);
+ context->ir_isopen = 1;
+ printk(KERN_INFO MOD_NAME ": IR port opened\n");
- mutex_unlock(&disconnect_lock);
+ mutex_unlock(&driver_lock);
return retval;
}
@@ -884,13 +1177,12 @@ static void ir_close(void *data)
mutex_lock(&context->lock);
- usb_kill_urb(context->rx_urb);
context->ir_isopen = 0;
context->ir_isassociating = 0;
MOD_DEC_USE_COUNT;
- printk(KERN_INFO "IR port closed\n");
+ printk(KERN_INFO MOD_NAME ": IR port closed\n");
- if (!context->dev_present) {
+ if (!context->dev_present_intf0) {
/*
* Device disconnected while IR port was still open. Driver
* was not deregistered at disconnect time, so do it now.
@@ -899,7 +1191,7 @@ static void ir_close(void *data)
if (!context->display_isopen) {
mutex_unlock(&context->lock);
- delete_context(context);
+ free_imon_context(context);
return;
}
/*
@@ -913,7 +1205,7 @@ static void ir_close(void *data)
}
/**
- * Convert bit count to time duration(in us) and submit
+ * Convert bit count to time duration (in us) and submit
* the value to lirc_dev.
*/
static void submit_data(struct imon_context *context)
@@ -922,8 +1214,7 @@ static void submit_data(struct imon_context *context)
int value = context->rx.count;
int i;
- if (debug)
- printk(KERN_INFO "submitting data to LIRC\n");
+ dprintk("submitting data to LIRC\n");
value *= BIT_DURATION;
value &= PULSE_MASK;
@@ -940,116 +1231,233 @@ static void submit_data(struct imon_context *context)
static inline int tv2int(const struct timeval *a, const struct timeval *b)
{
- int usecs = 0;
- int sec = 0;
+ int usecs = 0;
+ int sec = 0;
- if (b->tv_usec > a->tv_usec) {
- usecs = 1000000;
- sec--;
- }
+ if (b->tv_usec > a->tv_usec) {
+ usecs = 1000000;
+ sec--;
+ }
- usecs += a->tv_usec - b->tv_usec;
+ usecs += a->tv_usec - b->tv_usec;
- sec += a->tv_sec - b->tv_sec;
- sec *= 1000;
- usecs /= 1000;
- sec += usecs;
+ sec += a->tv_sec - b->tv_sec;
+ sec *= 1000;
+ usecs /= 1000;
+ sec += usecs;
- if (sec < 0)
- sec = 1000;
+ if (sec < 0)
+ sec = 1000;
- return sec;
+ return sec;
}
/**
- * The directional pad is overly sensitive in keyboard mode, so we do some
- * interesting contortions to make it less touchy.
+ * The directional pad behaves a bit differently, depending on whether this is
+ * one of the older ffdc devices or a newer device. Newer devices appear to
+ * have a higher resolution matrix for more precise mouse movement, but it
+ * makes things overly sensitive in keyboard mode, so we do some interesting
+ * contortions to make it less touchy. Older devices run through the same
+ * routine with shorter timeout and a smaller threshold.
*/
-#define IMON_PAD_TIMEOUT 1000 /* in msecs */
-#define IMON_PAD_THRESHOLD 80 /* 160x160 square */
-static int stabilize(int a, int b)
+static int stabilize(int a, int b, u16 timeout, u16 threshold)
{
- struct timeval ct;
- static struct timeval prev_time = {0, 0};
- static struct timeval hit_time = {0, 0};
- static int x, y, prev_result, hits;
- int result = 0;
- int msec, msec_hit;
-
- do_gettimeofday(&ct);
- msec = tv2int(&ct, &prev_time);
- msec_hit = tv2int(&ct, &hit_time);
-
- if (msec > 100) {
- x = 0;
- y = 0;
- hits = 0;
- }
-
- x += a;
- y += b;
-
- prev_time = ct;
-
- if (abs(x) > IMON_PAD_THRESHOLD || abs(y) > IMON_PAD_THRESHOLD) {
- if (abs(y) > abs(x))
- result = (y > 0) ? 0x7F : 0x80;
- else
- result = (x > 0) ? 0x7F00 : 0x8000;
-
- x = 0;
- y = 0;
-
- if (result == prev_result) {
- hits++;
-
- if (hits > 3) {
- switch (result) {
- case 0x7F:
- y = 17 * IMON_PAD_THRESHOLD / 30;
- break;
- case 0x80:
- y -= 17 * IMON_PAD_THRESHOLD / 30;
- break;
- case 0x7F00:
- x = 17 * IMON_PAD_THRESHOLD / 30;
- break;
- case 0x8000:
- x -= 17 * IMON_PAD_THRESHOLD / 30;
- break;
- }
- }
-
- if (hits == 2 && msec_hit < IMON_PAD_TIMEOUT) {
- result = 0;
- hits = 1;
- }
- } else {
- prev_result = result;
- hits = 1;
- hit_time = ct;
- }
- }
-
- return result;
+ struct timeval ct;
+ static struct timeval prev_time = {0, 0};
+ static struct timeval hit_time = {0, 0};
+ static int x, y, prev_result, hits;
+ int result = 0;
+ int msec, msec_hit;
+
+ do_gettimeofday(&ct);
+ msec = tv2int(&ct, &prev_time);
+ msec_hit = tv2int(&ct, &hit_time);
+
+ if (msec > 100) {
+ x = 0;
+ y = 0;
+ hits = 0;
+ }
+
+ x += a;
+ y += b;
+
+ prev_time = ct;
+
+ if (abs(x) > threshold || abs(y) > threshold) {
+ if (abs(y) > abs(x))
+ result = (y > 0) ? 0x7F : 0x80;
+ else
+ result = (x > 0) ? 0x7F00 : 0x8000;
+
+ x = 0;
+ y = 0;
+
+ if (result == prev_result) {
+ hits++;
+
+ if (hits > 3) {
+ switch (result) {
+ case 0x7F:
+ y = 17 * threshold / 30;
+ break;
+ case 0x80:
+ y -= 17 * threshold / 30;
+ break;
+ case 0x7F00:
+ x = 17 * threshold / 30;
+ break;
+ case 0x8000:
+ x -= 17 * threshold / 30;
+ break;
+ }
+ }
+
+ if (hits == 2 && msec_hit < timeout) {
+ result = 0;
+ hits = 1;
+ }
+ } else {
+ prev_result = result;
+ hits = 1;
+ hit_time = ct;
+ }
+ }
+
+ return result;
}
/**
* Process the incoming packet
*/
-static void incoming_packet(struct imon_context *context,
- struct urb *urb)
+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;
+ char rel_x = 0x00, rel_y = 0x00;
int octet, bit;
unsigned char mask;
- int chunk_num, dir;
-#ifdef DEBUG
- int i;
+ int i, chunk_num;
+ int ts_input = 0;
+ int dir = 0;
+ u16 timeout, threshold;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18)
+ int mouse_input;
+ int right_shift = 1;
+ struct input_dev *mouse = NULL;
+ struct input_dev *touch = NULL;
+ const unsigned char toggle_button1[] = { 0x29, 0x91, 0x15, 0xb7 };
+ const unsigned char toggle_button2[] = { 0x29, 0x91, 0x35, 0xb7 };
+ const unsigned char ch_up[] = { 0x28, 0x93, 0x95, 0xb7 };
+ const unsigned char ch_down[] = { 0x28, 0x87, 0x95, 0xb7 };
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18)
+ mouse = context->mouse;
+ if (context->display_type == IMON_DISPLAY_TYPE_VGA)
+ touch = context->touch;
+
+ /* keyboard/mouse mode toggle button */
+ if (memcmp(buf, toggle_button1, 4) == 0 ||
+ memcmp(buf, toggle_button2, 4) == 0) {
+ if (!nomouse) {
+ context->pad_mouse = ~(context->pad_mouse) & 0x1;
+ dprintk("toggling to %s mode\n",
+ context->pad_mouse ? "mouse" : "keyboard");
+ } else {
+ context->pad_mouse = 0;
+ dprintk("mouse mode was disabled by modparam\n");
+ }
+ return;
+ }
+
+ /* send touchscreen events through input subsystem if touchpad data */
+ if (context->display_type == IMON_DISPLAY_TYPE_VGA && len == 8 &&
+ buf[7] == 0x86) {
+ if (touch == NULL) {
+ printk(KERN_WARNING "%s: touchscreen input device is "
+ "NULL!\n", __func__);
+ return;
+ }
+ mod_timer(&context->timer, jiffies + TOUCH_TIMEOUT);
+ context->touch_x = (buf[0] << 4) | (buf[1] >> 4);
+ context->touch_y = 0xfff - ((buf[2] << 4) | (buf[1] & 0xf));
+ input_report_abs(touch, ABS_X, context->touch_x);
+ input_report_abs(touch, ABS_Y, context->touch_y);
+ input_report_key(touch, BTN_TOUCH, 0x01);
+ input_sync(touch);
+ ts_input = 1;
+
+ /* send mouse events through input subsystem in mouse mode */
+ } else if (context->pad_mouse || !context->ir_isopen) {
+ /* newer iMON device PAD or mouse button */
+ if (!context->ffdc_dev && (buf[0] & 0x01) && len == 5) {
+ mouse_input = 1;
+ rel_x = buf[2];
+ rel_y = buf[3];
+ right_shift = 1;
+ /* 0xffdc iMON PAD or mouse button input */
+ } else if (context->ffdc_dev && (buf[0] & 0x40) &&
+ !((buf[1] & 0x01) || ((buf[1] >> 2) & 0x01))) {
+ mouse_input = 1;
+ rel_x = (buf[1] & 0x08) | (buf[1] & 0x10) >> 2 |
+ (buf[1] & 0x20) >> 4 | (buf[1] & 0x40) >> 6;
+ if (buf[0] & 0x02)
+ rel_x |= ~0x0f;
+ rel_x = rel_x + rel_x / 2;
+ rel_y = (buf[2] & 0x08) | (buf[2] & 0x10) >> 2 |
+ (buf[2] & 0x20) >> 4 | (buf[2] & 0x40) >> 6;
+ if (buf[0] & 0x01)
+ rel_y |= ~0x0f;
+ rel_y = rel_y + rel_y / 2;
+ right_shift = 2;
+ /* some ffdc devices decode mouse buttons differently... */
+ } else if (context->ffdc_dev && (buf[0] == 0x68)) {
+ mouse_input = 1;
+ right_shift = 2;
+ /* ch+/- buttons, which we use for an emulated scroll wheel */
+ } else if (!memcmp(buf, ch_up, 4)) {
+ mouse_input = 1;
+ dir = 1;
+ } else if (!memcmp(buf, ch_down, 4)) {
+ mouse_input = 1;
+ dir = -1;
+ } else
+ mouse_input = 0;
+
+ if (mouse_input) {
+ if (mouse == NULL) {
+ printk(KERN_WARNING "%s: mouse input device "
+ "is NULL!\n", __func__);
+ return;
+ }
+ dprintk("sending mouse data via input subsystem\n");
+
+ if (dir) {
+ input_report_rel(mouse, REL_WHEEL, dir);
+ } else if (rel_x || rel_y) {
+ input_report_rel(mouse, REL_X, rel_x);
+ input_report_rel(mouse, REL_Y, rel_y);
+ } else {
+ input_report_key(mouse, BTN_LEFT, buf[1] & 0x1);
+ input_report_key(mouse, BTN_RIGHT,
+ buf[1] >> right_shift & 0x1);
+ }
+ input_sync(mouse);
+ return;
+ }
+ }
#endif
/*
+ * at this point, mouse and touchscreen input has been handled, so
+ * anything else goes to lirc -- bail out if no listening IR client
+ */
+ if (!context->ir_isopen)
+ return;
+
+ /*
* we need to add some special handling for
* the imon's IR mouse events
*/
@@ -1057,6 +1465,11 @@ static void incoming_packet(struct imon_context *context,
/* first, pad to 8 bytes so it conforms with everything else */
buf[5] = buf[6] = buf[7] = 0;
len = 8;
+ timeout = 500; /* in msecs */
+ /* (2*threshold) x (2*threshold) square */
+ threshold = pad_thresh ? pad_thresh : 28;
+ rel_x = buf[2];
+ rel_y = buf[3];
/*
* the imon directional pad functions more like a touchpad.
@@ -1068,13 +1481,25 @@ static void incoming_packet(struct imon_context *context,
* diagonals, it has a tendancy to jump back and forth, so lets
* try to ignore when they get too close
*/
- if ((buf[1] == 0) && ((buf[2] != 0) || (buf[3] != 0))) {
- dir = stabilize((int)(char)buf[2], (int)(char)buf[3]);
- if (!dir)
- return;
- buf[2] = dir & 0xFF;
- buf[3] = (dir >> 8) & 0xFF;
+ if (context->ir_protocol == IMON_IR_PROTOCOL_IMON) {
+ if ((buf[1] == 0) && ((rel_x != 0) || (rel_y != 0))) {
+ dir = stabilize((int)rel_x, (int)rel_y,
+ timeout, threshold);
+ if (!dir)
+ return;
+ buf[2] = dir & 0xFF;
+ buf[3] = (dir >> 8) & 0xFF;
+ }
+ } else {
+ if (abs(rel_y) > abs(rel_x)) {
+ buf[2] = (rel_y > 0) ? 0x7F : 0x80;
+ buf[3] = 0;
+ } else {
+ buf[2] = 0;
+ buf[3] = (rel_x > 0) ? 0x7F : 0x80;
+ }
}
+
} else if ((len == 8) && (buf[0] & 0x40) &&
!(buf[1] & 0x01 || buf[1] >> 2 & 0x01)) {
/*
@@ -1089,34 +1514,60 @@ static void incoming_packet(struct imon_context *context,
* stabilize(). The resulting codes will be 0x01008000,
* 0x01007F00, ..., so one can use the normal imon-pad config
* from the remotes dir.
- *
*/
+ timeout = 10; /* in msecs */
+ /* (2*threshold) x (2*threshold) square */
+ threshold = pad_thresh ? pad_thresh : 15;
/* buf[1] is x */
- int rel_x = (buf[1] & 0x08) | (buf[1] & 0x10) >> 2 |
- (buf[1] & 0x20) >> 4 | (buf[1] & 0x40) >> 6;
+ rel_x = (buf[1] & 0x08) | (buf[1] & 0x10) >> 2 |
+ (buf[1] & 0x20) >> 4 | (buf[1] & 0x40) >> 6;
if(buf[0] & 0x02)
rel_x |= ~0x10+1;
/* buf[2] is y */
- int rel_y = (buf[2] & 0x08) | (buf[2] & 0x10) >> 2 |
- (buf[2] & 0x20) >> 4 | (buf[2] & 0x40) >> 6;
+ rel_y = (buf[2] & 0x08) | (buf[2] & 0x10) >> 2 |
+ (buf[2] & 0x20) >> 4 | (buf[2] & 0x40) >> 6;
if(buf[0] & 0x01)
rel_y |= ~0x10+1;
buf[0] = 0x01;
-
buf[1] = buf[4] = buf[5] = buf[6] = buf[7] = 0;
- dir = stabilize(rel_x, rel_y);
- if (!dir)
- return;
- buf[2] = dir & 0xFF;
- buf[3] = (dir >> 8) & 0xFF;
+ if (context->ir_protocol == IMON_IR_PROTOCOL_IMON) {
+ dir = stabilize((int)rel_x, (int)rel_y,
+ timeout, threshold);
+ if (!dir)
+ return;
+ buf[2] = dir & 0xFF;
+ buf[3] = (dir >> 8) & 0xFF;
+ } else {
+ if (abs(rel_y) > abs(rel_x)) {
+ buf[2] = (rel_y > 0) ? 0x7F : 0x80;
+ buf[3] = 0;
+ } else {
+ buf[2] = 0;
+ buf[3] = (rel_x > 0) ? 0x7F : 0x80;
+ }
+ }
+
+ } else if (ts_input) {
+ /*
+ * this is touchscreen input, which we need to down-sample
+ * to a 64 button matrix at the moment...
+ */
+ buf[0] = buf[0] >> 5;
+ buf[1] = 0x00;
+ buf[2] = buf[2] >> 5;
+ buf[3] = 0x00;
+ buf[4] = 0x00;
+ buf[5] = 0x00;
+ buf[6] = 0x14;
+ buf[7] = 0xff;
}
if (len != 8) {
- printk(KERN_WARNING "%s: invalid incoming packet size(%d)\n",
- __func__, len);
+ printk(KERN_WARNING "imon %s: invalid incoming packet "
+ "size (len = %d, intf%d)\n", __func__, len, intf);
return;
}
@@ -1135,7 +1586,7 @@ static void incoming_packet(struct imon_context *context,
chunk_num = buf[7];
- if (chunk_num == 0xFF)
+ if (chunk_num == 0xFF && !ts_input)
return; /* filler frame, no data here */
if (buf[0] == 0xFF &&
@@ -1148,11 +1599,15 @@ static void incoming_packet(struct imon_context *context,
(buf[6] == 0x5E && buf[7] == 0xAF))) /* DT */
return; /* filler frame, no data here */
-#ifdef DEBUG
- for (i = 0; i < 8; ++i)
- printk(KERN_INFO "%02x ", buf[i]);
- printk(KERN_INFO "\n");
-#endif
+ if (debug) {
+ if (context->ir_onboard_decode)
+ printk("intf%d decoded packet: ", intf);
+ else
+ printk("raw packet: ");
+ for (i = 0; i < len; ++i)
+ printk("%02x ", buf[i]);
+ printk("\n");
+ }
if (context->ir_onboard_decode) {
/* The signals have been decoded onboard the iMON controller */
@@ -1209,92 +1664,169 @@ static void incoming_packet(struct imon_context *context,
}
/**
+ * report touchscreen input
+ */
+static void imon_touch_display_timeout(unsigned long data)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18)
+ struct imon_context *context = (struct imon_context *)data;
+ struct input_dev *touch;
+
+ if (!context->display_type == IMON_DISPLAY_TYPE_VGA)
+ return;
+
+ touch = context->touch;
+ input_report_abs(touch, ABS_X, context->touch_x);
+ input_report_abs(touch, ABS_Y, context->touch_y);
+ input_report_key(touch, BTN_TOUCH, 0x00);
+ input_sync(touch);
+#endif
+
+ return;
+}
+
+/**
* Callback function for USB core API: receive data
*/
-#if defined(KERNEL_2_5) && LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
-static void usb_rx_callback(struct urb *urb, struct pt_regs *regs)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
+static void usb_rx_callback_intf0(struct urb *urb, struct pt_regs *regs)
#else
-static void usb_rx_callback(struct urb *urb)
+static void usb_rx_callback_intf0(struct urb *urb)
#endif
{
struct imon_context *context;
+ unsigned char *buf;
+ int len;
+ int intfnum = 0;
if (!urb)
return;
- context = (struct imon_context *) urb->context;
+
+ 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:
- if (context->ir_isopen)
- incoming_packet(context, urb);
+ imon_incoming_packet(context, urb, intfnum);
break;
+
default:
- printk(KERN_WARNING "%s: status(%d): ignored\n",
+ printk(KERN_WARNING "imon %s: status(%d): ignored\n",
__func__, urb->status);
break;
}
-#ifdef KERNEL_2_5
- usb_submit_urb(context->rx_urb, GFP_ATOMIC);
-#endif
+ usb_submit_urb(context->rx_urb_intf0, GFP_ATOMIC);
+
return;
}
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
+static void usb_rx_callback_intf1(struct urb *urb, struct pt_regs *regs)
+#else
+static void usb_rx_callback_intf1(struct urb *urb)
+#endif
+{
+ struct imon_context *context;
+ unsigned char *buf;
+ int len;
+ int intfnum = 1;
+
+ 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:
+ printk(KERN_WARNING "imon %s: status(%d): ignored\n",
+ __func__, urb->status);
+ break;
+ }
+
+ usb_submit_urb(context->rx_urb_intf1, GFP_ATOMIC);
+
+ return;
+}
/**
* Callback function for USB core API: Probe
*/
-#ifdef KERNEL_2_5
static int imon_probe(struct usb_interface *interface,
const struct usb_device_id *id)
-#else
-static void *imon_probe(struct usb_device *dev, unsigned int intf,
- const struct usb_device_id *id)
-#endif
{
-#ifdef KERNEL_2_5
struct usb_device *usbdev = NULL;
struct usb_host_interface *iface_desc = NULL;
-#else
- struct usb_interface *interface = NULL;
- struct usb_interface_descriptor *iface_desc = NULL;
- char name[10];
- int subminor = 0;
-#endif
struct usb_endpoint_descriptor *rx_endpoint = NULL;
struct usb_endpoint_descriptor *tx_endpoint = NULL;
struct urb *rx_urb = NULL;
struct urb *tx_urb = NULL;
struct lirc_driver *driver = NULL;
struct lirc_buffer *rbuf = NULL;
+ struct usb_interface *first_if;
+ int ifnum;
int lirc_minor = 0;
- int num_endpoints;
+ int num_endpts;
int retval = 0;
- int display_ep_found;
- int ir_ep_found;
- int alloc_status;
- int display_proto_6p = 0;
+ int display_ep_found = 0;
+ int ir_ep_found = 0;
+ int alloc_status = 0;
+ int vfd_proto_6p = 0;
int ir_onboard_decode = 0;
int buf_chunk_size = BUF_CHUNK_SIZE;
int code_length;
int tx_control = 0;
struct imon_context *context = NULL;
- int i;
+ struct imon_context *first_if_context = NULL;
+ int i, sysfs_err;
+ int configured_display_type = IMON_DISPLAY_TYPE_VFD;
+ u16 vendor, product;
+ const unsigned char fp_packet[] = { 0x40, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x88 };
- printk(KERN_INFO "%s: found iMON device\n", __func__);
+ /*
+ * 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 (display_type == IMON_DISPLAY_TYPE_AUTO) {
+ if (usb_match_id(interface, lcd_device_list))
+ configured_display_type = IMON_DISPLAY_TYPE_LCD;
+ else if (usb_match_id(interface, imon_touchscreen_list))
+ configured_display_type = IMON_DISPLAY_TYPE_VGA;
+ else if (usb_match_id(interface, ir_only_list))
+ configured_display_type = IMON_DISPLAY_TYPE_NONE;
+ else
+ configured_display_type = IMON_DISPLAY_TYPE_VFD;
+ } else {
+ configured_display_type = display_type;
+ dprintk("%s: overriding display type to %d via modparam\n",
+ __func__, display_type);
+ }
/*
* If it's the LCD, as opposed to the VFD, we just need to replace
* the "write" file op.
*/
- if ((display_type == IMON_DISPLAY_TYPE_AUTO &&
- usb_match_id(interface, lcd_device_list)) ||
- display_type == IMON_DISPLAY_TYPE_LCD)
+ if (configured_display_type == IMON_DISPLAY_TYPE_LCD)
display_fops.write = &lcd_write;
/*
@@ -1306,47 +1838,32 @@ static void *imon_probe(struct usb_device *dev, unsigned int intf,
code_length = buf_chunk_size * 8;
-#if !defined(KERNEL_2_5)
- for (subminor = 0; subminor < MAX_DEVICES; ++subminor) {
- if (minor_table[subminor] == NULL)
- break;
- }
- if (subminor == MAX_DEVICES) {
- err("%s: allowed max number of devices already present",
- __func__);
- retval = -ENOMEM;
- goto exit;
- }
-#endif
-#ifdef KERNEL_2_5
- usbdev = usb_get_dev(interface_to_usbdev(interface));
+ usbdev = usb_get_dev(interface_to_usbdev(interface));
iface_desc = interface->cur_altsetting;
- num_endpoints = iface_desc->desc.bNumEndpoints;
-#else
- interface = &usbdev->actconfig->interface[intf];
- iface_desc = &interface->altsetting[interface->act_altsetting];
- num_endpoints = iface_desc->bNumEndpoints;
-#endif
+ 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);
+
+ dprintk("%s: found iMON device (%04x:%04x, intf%d)\n",
+ __func__, vendor, product, ifnum);
+
+ /* prevent races probing devices w/multiple interfaces */
+ mutex_lock(&driver_lock);
+
+ first_if = usb_ifnum_to_if(usbdev, 0);
+ first_if_context = (struct imon_context *)usb_get_intfdata(first_if);
/*
* Scan the endpoint list and set:
* first input endpoint = IR endpoint
* first output endpoint = display endpoint
*/
-
- ir_ep_found = 0;
- display_ep_found = 0;
-
- for (i = 0; i < num_endpoints && !(ir_ep_found && display_ep_found);
- ++i) {
+ for (i = 0; i < num_endpts && !(ir_ep_found && display_ep_found); ++i) {
struct usb_endpoint_descriptor *ep;
int ep_dir;
int ep_type;
-#ifdef KERNEL_2_5
ep = &iface_desc->endpoint[i].desc;
-#else
- ep = &iface_desc->endpoint[i];
-#endif
ep_dir = ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK;
ep_type = ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
@@ -1356,18 +1873,14 @@ static void *imon_probe(struct usb_device *dev, unsigned int intf,
rx_endpoint = ep;
ir_ep_found = 1;
- if (debug)
- printk(KERN_INFO "%s: found IR endpoint\n",
- __func__);
+ dprintk("%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;
- if (debug)
- printk(KERN_INFO "%s: found display endpoint\n",
- __func__);
+ dprintk("%s: found display endpoint\n", __func__);
}
}
@@ -1379,10 +1892,8 @@ static void *imon_probe(struct usb_device *dev, unsigned int intf,
if (usb_match_id(interface, ctl_ep_device_list)) {
tx_control = 1;
display_ep_found = 1;
- if (debug)
- printk(KERN_INFO "%s: LCD device uses control "
- "endpoint, not interface OUT "
- "endpoint\n", __func__);
+ dprintk("%s: device uses control endpoint, not "
+ "interface OUT endpoint\n", __func__);
}
}
@@ -1391,14 +1902,18 @@ static void *imon_probe(struct usb_device *dev, unsigned int intf,
* that SoundGraph recycles device IDs between devices both with
* and without... :\
*/
- if ((display_type == IMON_DISPLAY_TYPE_AUTO &&
- usb_match_id(interface, ir_only_list)) ||
- display_type == IMON_DISPLAY_TYPE_NONE) {
- tx_control = 0;
+ if (configured_display_type == IMON_DISPLAY_TYPE_NONE) {
display_ep_found = 0;
- if (debug)
- printk(KERN_INFO "%s: device has no display\n",
- __func__);
+ dprintk("%s: device has no display\n", __func__);
+ }
+
+ /*
+ * iMON Touch devices have a VGA touchscreen, but no "display", as
+ * that refers to e.g. /dev/lcd0 (a character device LCD or VFD).
+ */
+ if (configured_display_type == IMON_DISPLAY_TYPE_VGA) {
+ display_ep_found = 0;
+ dprintk("%s: iMON Touch device found\n", __func__);
}
/* Input endpoint is mandatory */
@@ -1411,171 +1926,296 @@ static void *imon_probe(struct usb_device *dev, unsigned int intf,
if (usb_match_id(interface, ir_onboard_decode_list))
ir_onboard_decode = 1;
- if (debug)
- printk(KERN_INFO "ir_onboard_decode: %d\n",
- ir_onboard_decode);
+ dprintk("%s: ir_onboard_decode: %d\n",
+ __func__, ir_onboard_decode);
}
/* Determine if display requires 6 packets */
if (display_ep_found) {
- if (usb_match_id(interface, display_proto_6p_list))
- display_proto_6p = 1;
+ if (usb_match_id(interface, vfd_proto_6p_list))
+ vfd_proto_6p = 1;
- if (debug)
- printk(KERN_INFO "display_proto_6p: %d\n",
- display_proto_6p);
+ dprintk("%s: vfd_proto_6p: %d\n",
+ __func__, vfd_proto_6p);
}
- alloc_status = 0;
-
- context = kzalloc(sizeof(struct imon_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;
- }
-#ifdef KERNEL_2_5
- rx_urb = usb_alloc_urb(0, GFP_KERNEL);
-#else
- rx_urb = usb_alloc_urb(0);
-#endif
- if (!rx_urb) {
- err("%s: usb_alloc_urb failed for IR urb", __func__);
- alloc_status = 5;
- goto alloc_status_switch;
- }
- if (display_ep_found) {
-#ifdef KERNEL_2_5
+ if (ifnum == 0) {
+ context = kzalloc(sizeof(struct imon_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;
+ }
tx_urb = usb_alloc_urb(0, GFP_KERNEL);
-#else
- tx_urb = usb_alloc_urb(0);
-#endif
if (!tx_urb) {
err("%s: usb_alloc_urb failed for display urb",
__func__);
alloc_status = 6;
goto alloc_status_switch;
}
- }
- mutex_init(&context->lock);
- context->display_proto_6p = display_proto_6p;
- context->ir_onboard_decode = ir_onboard_decode;
-
- strcpy(driver->name, MOD_NAME);
- driver->minor = -1;
- driver->code_length = ir_onboard_decode ?
- buf_chunk_size * 8 : sizeof(lirc_t) * 8;
- driver->sample_rate = 0;
- driver->features = (ir_onboard_decode) ?
- LIRC_CAN_REC_LIRCCODE : LIRC_CAN_REC_MODE2;
- driver->data = context;
- driver->rbuf = rbuf;
- driver->set_use_inc = ir_open;
- driver->set_use_dec = ir_close;
+ mutex_init(&context->lock);
+ context->vfd_proto_6p = vfd_proto_6p;
+ context->ir_onboard_decode = ir_onboard_decode;
+
+ strcpy(driver->name, MOD_NAME);
+ driver->minor = -1;
+ driver->code_length = ir_onboard_decode ?
+ code_length : sizeof(int) * 8;
+ driver->sample_rate = 0;
+ driver->features = (ir_onboard_decode) ?
+ LIRC_CAN_REC_LIRCCODE : LIRC_CAN_REC_MODE2;
+ driver->data = context;
+ driver->rbuf = rbuf;
+ driver->set_use_inc = ir_open;
+ driver->set_use_dec = ir_close;
#ifdef LIRC_HAVE_SYSFS
- driver->dev = &interface->dev;
+ driver->dev = &interface->dev;
#endif
- driver->owner = THIS_MODULE;
+ driver->owner = THIS_MODULE;
- mutex_lock(&context->lock);
+ mutex_lock(&context->lock);
- lirc_minor = lirc_register_driver(driver);
- if (lirc_minor < 0) {
- err("%s: lirc_register_driver failed", __func__);
- alloc_status = 7;
- mutex_unlock(&context->lock);
- goto alloc_status_switch;
- } else
- printk(KERN_INFO "%s: Registered iMON driver(minor:%d)\n",
- __func__, lirc_minor);
+ context->driver = driver;
+ /* start out in keyboard mode */
+ context->pad_mouse = 0;
- /* Needed while unregistering! */
- driver->minor = lirc_minor;
+ init_timer(&context->timer);
+ context->timer.data = (unsigned long)context;
+ context->timer.function = imon_touch_display_timeout;
- context->usbdev = usbdev;
- context->dev_present = 1;
- context->rx_endpoint = rx_endpoint;
- context->rx_urb = rx_urb;
- if (display_ep_found) {
- context->display_supported = 1;
+ 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
+ printk(KERN_INFO MOD_NAME ": Registered iMON driver "
+ "(lirc minor: %d)\n", lirc_minor);
+
+ /* Needed while unregistering! */
+ driver->minor = lirc_minor;
+
+ } else {
+ /* this is the secondary interface on the device */
+ if (first_if_context->driver) {
+ 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;
+ }
+
+ context = first_if_context;
+ }
+ mutex_lock(&context->lock);
+ }
+
+ if (ifnum == 0) {
+ context->usbdev_intf0 = usbdev;
+ context->dev_present_intf0 = 1;
+ context->rx_endpoint_intf0 = rx_endpoint;
+ context->rx_urb_intf0 = 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;
context->tx_control = tx_control;
+
+ if (display_ep_found)
+ context->display_supported = 1;
+
+ if (product == 0xffdc)
+ context->ffdc_dev = 1;
+
+ context->display_type = configured_display_type;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18)
+ context->mouse = input_allocate_device();
+
+ snprintf(context->name_mouse, sizeof(context->name_mouse),
+ "iMON PAD IR Mouse (%04x:%04x)",
+ vendor, product);
+ context->mouse->name = context->name_mouse;
+
+ usb_make_path(usbdev, context->phys_mouse, sizeof(context->phys_mouse));
+ strlcat(context->phys_mouse, "/input0", sizeof(context->phys_mouse));
+ context->mouse->phys = context->phys_mouse;
+
+ context->mouse->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
+ context->mouse->keybit[BIT_WORD(BTN_MOUSE)] =
+ BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_RIGHT) |
+ BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_SIDE) |
+ BIT_MASK(BTN_EXTRA);
+ context->mouse->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y) |
+ BIT_MASK(REL_WHEEL);
+
+ input_set_drvdata(context->mouse, context);
+
+ usb_to_input_id(usbdev, &context->mouse->id);
+ context->mouse->dev.parent = &interface->dev;
+ retval = input_register_device(context->mouse);
+ if (retval)
+ printk(KERN_INFO "%s: pad mouse input device setup failed\n",
+ __func__);
+#endif
+
+ usb_fill_int_urb(context->rx_urb_intf0, context->usbdev_intf0,
+ usb_rcvintpipe(context->usbdev_intf0,
+ context->rx_endpoint_intf0->bEndpointAddress),
+ context->usb_rx_buf, sizeof(context->usb_rx_buf),
+ usb_rx_callback_intf0, context,
+ context->rx_endpoint_intf0->bInterval);
+
+ retval = usb_submit_urb(context->rx_urb_intf0, GFP_KERNEL);
+
+ if (retval) {
+ err("%s: usb_submit_urb failed for intf0 (%d)",
+ __func__, retval);
+ mutex_unlock(&context->lock);
+ goto exit;
+ }
+
+ } else {
+ context->usbdev_intf1 = usbdev;
+ context->dev_present_intf1 = 1;
+ context->rx_endpoint_intf1 = rx_endpoint;
+ context->rx_urb_intf1 = rx_urb;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18)
+ if (context->display_type == IMON_DISPLAY_TYPE_VGA) {
+ context->touch = input_allocate_device();
+
+ snprintf(context->name_touch, sizeof(context->name_touch),
+ "iMON USB Touchscreen (%04x:%04x)",
+ vendor, product);
+ context->touch->name = context->name_touch;
+
+ usb_make_path(usbdev, context->phys_touch,
+ sizeof(context->phys_touch));
+ strlcat(context->phys_touch, "/input1",
+ sizeof(context->phys_touch));
+ context->touch->phys = context->phys_touch;
+
+ context->touch->evbit[0] =
+ BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ context->touch->keybit[BIT_WORD(BTN_TOUCH)] =
+ BIT_MASK(BTN_TOUCH);
+ input_set_abs_params(context->touch, ABS_X,
+ 0x00, 0xfff, 0, 0);
+ input_set_abs_params(context->touch, ABS_Y,
+ 0x00, 0xfff, 0, 0);
+
+ input_set_drvdata(context->touch, context);
+
+ usb_to_input_id(usbdev, &context->touch->id);
+ context->touch->dev.parent = &interface->dev;
+ retval = input_register_device(context->touch);
+ if (retval)
+ printk(KERN_INFO "%s: touchscreen input device setup failed\n",
+ __func__);
+ } else
+ context->touch = NULL;
+#endif
+
+ usb_fill_int_urb(context->rx_urb_intf1, context->usbdev_intf1,
+ usb_rcvintpipe(context->usbdev_intf1,
+ context->rx_endpoint_intf1->bEndpointAddress),
+ context->usb_rx_buf, sizeof(context->usb_rx_buf),
+ usb_rx_callback_intf1, context,
+ context->rx_endpoint_intf1->bInterval);
+
+ retval = usb_submit_urb(context->rx_urb_intf1, GFP_KERNEL);
+
+ if (retval) {
+ err("%s: usb_submit_urb failed for intf1 (%d)",
+ __func__, retval);
+ mutex_unlock(&context->lock);
+ goto exit;
+ }
}
- context->driver = driver;
-#ifdef KERNEL_2_5
usb_set_intfdata(interface, context);
- if (cpu_to_le16(usbdev->descriptor.idProduct) == 0xffdc) {
- int err;
-
- err = sysfs_create_group(&interface->dev.kobj,
- &imon_attribute_group);
- if (err)
- err("%s: Could not create sysfs entries(%d)",
- __func__, err);
+ /* RF products *also* use 0xffdc... sigh... */
+ if (context->ffdc_dev) {
+ sysfs_err = sysfs_create_group(&interface->dev.kobj,
+ &imon_rf_attribute_group);
+ if (sysfs_err)
+ err("%s: Could not create RF sysfs entries(%d)",
+ __func__, sysfs_err);
}
-#else
- minor_table[subminor] = context;
- context->subminor = subminor;
-#endif
- if (display_ep_found) {
- if (debug)
- printk(KERN_INFO "Registering display with devfs\n");
-#ifdef KERNEL_2_5
+ if (context->display_supported && ifnum == 0) {
+ dprintk("%s: Registering iMON display with sysfs\n", __func__);
+
+ /* set up sysfs entry for built-in clock */
+ sysfs_err = sysfs_create_group(&interface->dev.kobj,
+ &imon_display_attribute_group);
+ if (sysfs_err)
+ err("%s: Could not create display sysfs entries(%d)",
+ __func__, sysfs_err);
+
if (usb_register_dev(interface, &imon_class)) {
/* Not a fatal error, so ignore */
printk(KERN_INFO "%s: could not get a minor number for "
"display\n", __func__);
}
-#else
- sprintf(name, DEVFS_NAME, subminor);
- if (!(context->devfs = devfs_register(usb_devfs_handle, name,
- DEVFS_FL_DEFAULT, USB_MAJOR,
- DISPLAY_MINOR_BASE + subminor,
- DEVFS_MODE, &display_fops, NULL))) {
- /* not a fatal error so ignore */
- printk(KERN_INFO "%s: devfs register failed for "
- "display\n", __func__);
- }
-#endif
+
+ /* Enable front-panel buttons and/or knobs */
+ memcpy(context->usb_tx_buf, &fp_packet, sizeof(fp_packet));
+ retval = send_packet(context);
+ /* Not fatal, but warn about it */
+ if (retval)
+ printk(KERN_INFO "%s: failed to enable front-panel "
+ "buttons and/or knobs\n", __func__);
}
- printk(KERN_INFO "%s: iMON device on usb<%d:%d> initialized\n",
- __func__, usbdev->bus->busnum, usbdev->devnum);
+ /* set IR protocol/remote type */
+ imon_set_ir_protocol(context);
- mutex_unlock(&context->lock);
+ printk(KERN_INFO MOD_NAME ": 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->lock);
switch (alloc_status) {
case 7:
- if (display_ep_found)
- usb_free_urb(tx_urb);
+ usb_free_urb(tx_urb);
case 6:
usb_free_urb(rx_urb);
case 5:
- lirc_buffer_free(rbuf);
+ if (rbuf)
+ lirc_buffer_free(rbuf);
case 4:
kfree(rbuf);
case 3:
@@ -1585,89 +2225,93 @@ alloc_status_switch:
context = NULL;
case 1:
retval = -ENOMEM;
+ break;
case 0:
- ;
+ retval = 0;
}
exit:
-#ifdef KERNEL_2_5
+ mutex_unlock(&driver_lock);
+
return retval;
-#else
- return (retval == 0) ? context : NULL;
-#endif
}
/**
* Callback function for USB core API: disconnect
*/
-#ifdef KERNEL_2_5
static void imon_disconnect(struct usb_interface *interface)
-#else
-static void imon_disconnect(struct usb_device *dev, void *data)
-#endif
{
struct imon_context *context;
+ int ifnum;
/* prevent races with ir_open()/display_open() */
- mutex_lock(&disconnect_lock);
+ mutex_lock(&driver_lock);
-#ifdef KERNEL_2_5
context = usb_get_intfdata(interface);
-#else
- context = (struct imon_context *)data;
-#endif
- mutex_lock(&context->lock);
+ ifnum = interface->cur_altsetting->desc.bInterfaceNumber;
- printk(KERN_INFO "%s: iMON device disconnected\n", __func__);
+ mutex_lock(&context->lock);
-#ifdef KERNEL_2_5
/*
* sysfs_remove_group is safe to call even if sysfs_create_group
* hasn't been called
*/
sysfs_remove_group(&interface->dev.kobj,
- &imon_attribute_group);
- usb_set_intfdata(interface, NULL);
-#else
- minor_table[context->subminor] = NULL;
-#endif
- context->dev_present = 0;
+ &imon_display_attribute_group);
+ sysfs_remove_group(&interface->dev.kobj,
+ &imon_rf_attribute_group);
- /* Stop reception */
- usb_kill_urb(context->rx_urb);
+ usb_set_intfdata(interface, NULL);
/* Abort ongoing write */
if (atomic_read(&context->tx.busy)) {
usb_kill_urb(context->tx_urb);
- wait_for_completion(&context->tx.finished);
+ complete_all(&context->tx.finished);
}
- /* De-register from lirc_dev if IR port is not open */
- if (!context->ir_isopen)
- deregister_from_lirc(context);
-
- if (context->display_supported)
-#ifdef KERNEL_2_5
- usb_deregister_dev(interface, &imon_class);
-#else
- if (context->devfs)
- devfs_unregister(context->devfs);
+ if (ifnum == 0) {
+ context->dev_present_intf0 = 0;
+ usb_kill_urb(context->rx_urb_intf0);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18)
+ input_unregister_device(context->mouse);
#endif
+ if (context->display_supported)
+ usb_deregister_dev(interface, &imon_class);
+ } else {
+ context->dev_present_intf1 = 0;
+ usb_kill_urb(context->rx_urb_intf1);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18)
+ if (context->display_type == IMON_DISPLAY_TYPE_VGA)
+ input_unregister_device(context->touch);
+#endif
+ }
- mutex_unlock(&context->lock);
+ if (!context->ir_isopen && !context->dev_present_intf0 &&
+ !context->dev_present_intf1) {
+ del_timer_sync(&context->timer);
+ deregister_from_lirc(context);
+ mutex_unlock(&context->lock);
+ if (!context->display_isopen)
+ free_imon_context(context);
+ } else
+ mutex_unlock(&context->lock);
- if (!context->ir_isopen && !context->display_isopen)
- delete_context(context);
+ mutex_unlock(&driver_lock);
- mutex_unlock(&disconnect_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);
+ int ifnum = intf->cur_altsetting->desc.bInterfaceNumber;
+
+ if (ifnum == 0)
+ usb_kill_urb(context->rx_urb_intf0);
+ else
+ usb_kill_urb(context->rx_urb_intf1);
- if (context->ir_isopen)
- usb_kill_urb(context->rx_urb);
return 0;
}
@@ -1675,9 +2319,28 @@ static int imon_resume(struct usb_interface *intf)
{
int rc = 0;
struct imon_context *context = usb_get_intfdata(intf);
+ int ifnum = intf->cur_altsetting->desc.bInterfaceNumber;
- if (context->ir_isopen)
- rc = usb_submit_urb(context->rx_urb, GFP_ATOMIC);
+ if (ifnum == 0) {
+ usb_fill_int_urb(context->rx_urb_intf0, context->usbdev_intf0,
+ usb_rcvintpipe(context->usbdev_intf0,
+ context->rx_endpoint_intf0->bEndpointAddress),
+ context->usb_rx_buf, sizeof(context->usb_rx_buf),
+ usb_rx_callback_intf0, context,
+ context->rx_endpoint_intf0->bInterval);
+
+ rc = usb_submit_urb(context->rx_urb_intf0, GFP_ATOMIC);
+
+ } else {
+ usb_fill_int_urb(context->rx_urb_intf1, context->usbdev_intf1,
+ usb_rcvintpipe(context->usbdev_intf1,
+ context->rx_endpoint_intf1->bEndpointAddress),
+ context->usb_rx_buf, sizeof(context->usb_rx_buf),
+ usb_rx_callback_intf1, context,
+ context->rx_endpoint_intf1->bInterval);
+
+ rc = usb_submit_urb(context->rx_urb_intf1, GFP_ATOMIC);
+ }
return rc;
}
@@ -1686,26 +2349,22 @@ static int __init imon_init(void)
{
int rc;
- printk(KERN_INFO MOD_DESC ", v" MOD_VERSION "\n");
- printk(KERN_INFO MOD_AUTHOR "\n");
+ 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 "module removed. Goodbye!\n");
+ printk(KERN_INFO MOD_NAME ": module removed. Goodbye!\n");
}
module_init(imon_init);
module_exit(imon_exit);
-
-#if !defined(KERNEL_2_5)
-EXPORT_NO_SYMBOLS;
-#endif
diff --git a/ubuntu/lirc/lirc_it87/lirc_it87.c b/ubuntu/lirc/lirc_it87/lirc_it87.c
index 9fc1793ad0e..256406b1289 100644
--- a/ubuntu/lirc/lirc_it87/lirc_it87.c
+++ b/ubuntu/lirc/lirc_it87/lirc_it87.c
@@ -1,7 +1,7 @@
/*
- * LIRC driver for ITE IT8712/IT8705 CIR port
+ * LIRC driver for ITE IT8712/IT8705/IT8720 CIR port
*
- * Copyright (C) 2001 Hans-Günter Lütke Uphues <hg_lu@web.de>
+ * Copyright (C) 2001 Hans-Günter Lütke Uphues <hg_lu@web.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -18,7 +18,7 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA
*
- * ITE IT8705 and IT8712(not tested) CIR-port support for lirc based
+ * ITE IT8705, 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
@@ -816,9 +816,11 @@ static int init_port(void)
return retval;
}
it87_chipid = it87_read(IT87_CHIP_ID2);
- if ((it87_chipid != 0x12) && (it87_chipid != 0x05)) {
+ if ((it87_chipid != 0x12) &&
+ (it87_chipid != 0x05) &&
+ (it87_chipid != 0x20)) {
printk(KERN_INFO LIRC_DRIVER_NAME
- ": no IT8705/12 found, exiting..\n");
+ ": no IT8705/12/20 found, exiting..\n");
retval = -ENXIO;
return retval;
}
@@ -908,16 +910,16 @@ static void drop_port(void)
#if 0
unsigned char init_bytes[4] = IT87_INIT;
- / * Enter MB PnP Mode * /
+ /* 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 * /
+ /* deactivate CIR-Device */
it87_write(IT87_CIR_ACT, 0x0);
- / * Leaving MB PnP Mode * /
+ /* Leaving MB PnP Mode */
it87_write(IT87_CFGCTRL, 0x2);
#endif
@@ -969,7 +971,7 @@ module_init(lirc_it87_init);
module_exit(lirc_it87_exit);
MODULE_DESCRIPTION("LIRC driver for ITE IT8712/IT8705 CIR port");
-MODULE_AUTHOR("Hans-Günter Lütke Uphues");
+MODULE_AUTHOR("Hans-Günter Lütke Uphues");
MODULE_LICENSE("GPL");
module_param(io, int, S_IRUGO);
diff --git a/ubuntu/lirc/lirc_mceusb/lirc_mceusb.c b/ubuntu/lirc/lirc_mceusb/lirc_mceusb.c
index df92dd3f1b5..08517697da7 100644
--- a/ubuntu/lirc/lirc_mceusb/lirc_mceusb.c
+++ b/ubuntu/lirc/lirc_mceusb/lirc_mceusb.c
@@ -1,867 +1,1289 @@
/*
- * USB Microsoft IR Transceiver driver - 0.2
+ * LIRC driver for Windows Media Center Edition USB Infrared Transceivers
*
- * Copyright (c) 2003-2004 Dan Conti (dconti@acm.wwu.edu)
+ * (C) by Martin A. Blatter <martin_a_blatter@yahoo.com>
*
- * The Microsoft IR Transceiver is a neat little IR receiver with two
- * emitters on it designed for Windows Media Center. This driver might
- * work for all media center remotes, but I have only tested it with
- * the philips model. The first revision of this driver only supports
- * the receive function - the transmit function will be much more
- * tricky due to the nature of the hardware. Microsoft chose to build
- * this device inexpensively, therefore making it extra dumb.
- * There is no interrupt endpoint on this device; all usb traffic
- * happens over two bulk endpoints. As a result of this, poll() for
- * this device is an actual hardware poll (instead of a receive queue
- * check) and is rather expensive.
+ * Transmitter support and reception code cleanup.
+ * (C) by Daniel Melander <lirc@rajidae.se>
*
- * All trademarks property of their respective owners.
+ * Original lirc_mceusb driver for 1st-gen device:
+ * Copyright (c) 2003-2004 Dan Conti <dconti@acm.wwu.edu>
*
- * TODO
- * - Fix up minor number, registration of major/minor with usb subsystem
+ * Original lirc_mceusb driver deprecated in favor of this driver, which
+ * supports the 1st-gen device now too. Transmitting on the 1st-gen device
+ * only functions on port #2 at the moment.
+ *
+ * Support for 1st-gen device added June 2009,
+ * by Jarod Wilson <jarod@wilsonet.com>
+ *
+ * Initial transmission support for 1st-gen device added August 2009,
+ * by Patrick Calhoun <phineas@ou.edu>
+ *
+ * Derived from ATI USB driver by Paul Miller and the original
+ * MCE USB driver by Dan Conti ((and now including chunks of the latter
+ * relevant to the 1st-gen device initialization)
+ *
+ * This driver will only work reliably with kernel version 2.6.10
+ * or higher, probably because of differences in USB device enumeration
+ * in the kernel code. Device initialization fails most of the time
+ * with earlier kernel versions.
+ *
+ **********************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
+#include <linux/version.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 5)
+#error "*******************************************************"
+#error "Sorry, this driver needs kernel version 2.6.5 or higher"
+#error "*******************************************************"
+#endif
#include <linux/autoconf.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
+#include <linux/kmod.h>
#include <linux/smp_lock.h>
-#include <linux/usb.h>
-#ifdef KERNEL_2_5
#include <linux/completion.h>
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18)
#include <asm/uaccess.h>
#else
#include <linux/uaccess.h>
#endif
-#else
-#include <linux/spinlock.h>
-#include <linux/list.h>
-#include <linux/fcntl.h>
-#include <linux/poll.h>
-#include <linux/sched.h>
-#include <linux/signal.h>
-#endif
+#include <linux/usb.h>
+#include <linux/wait.h>
+#include <linux/time.h>
+
+#include "../lirc.h"
+#include "../kcompat.h"
+#include "../lirc_dev/lirc_dev.h"
+#define DRIVER_VERSION "1.90"
+#define DRIVER_AUTHOR "Daniel Melander <lirc@rajidae.se>, " \
+ "Martin Blatter <martin_a_blatter@yahoo.com>, " \
+ "Dan Conti <dconti@acm.wwu.edu>"
+#define DRIVER_DESC "Windows Media Center Edition USB IR Transceiver " \
+ "driver for LIRC"
+#define DRIVER_NAME "lirc_mceusb"
+
+#define USB_BUFLEN 32 /* USB reception buffer length */
+#define LIRCBUF_SIZE 256 /* LIRC work buffer length */
+
+/* MCE constants */
+#define MCE_CMDBUF_SIZE 384 /* MCE Command buffer length */
+#define MCE_TIME_UNIT 50 /* Approx 50us resolution */
+#define MCE_CODE_LENGTH 5 /* Normal length of packet (with header) */
+#define MCE_PACKET_SIZE 4 /* Normal length of packet (without header) */
+#define MCE_PACKET_HEADER 0x84 /* Actual header format is 0x80 + num_bytes */
+#define MCE_CONTROL_HEADER 0x9F /* MCE status header */
+#define MCE_TX_HEADER_LENGTH 3 /* # of bytes in the initializing tx header */
+#define MCE_MAX_CHANNELS 2 /* Two transmitters, hardware dependent? */
+#define MCE_DEFAULT_TX_MASK 0x03 /* Val opts: TX1=0x01, TX2=0x02, ALL=0x03 */
+#define MCE_PULSE_BIT 0x80 /* Pulse bit, MSB set == PULSE else SPACE */
+#define MCE_PULSE_MASK 0x7F /* Pulse mask */
+#define MCE_MAX_PULSE_LENGTH 0x7F /* Longest transmittable pulse symbol */
+#define MCE_PACKET_LENGTH_MASK 0x7F /* Pulse mask */
+
+
+/* module parameters */
#ifdef CONFIG_USB_DEBUG
static int debug = 1;
#else
static int debug;
#endif
-
-#include "../kcompat.h"
-#include "../lirc.h"
-#include "../lirc_dev/lirc_dev.h"
-
-/* Use our own dbg macro */
-#define dprintk(fmt, args...) \
- do { \
- if (debug) \
- printk(KERN_DEBUG __FILE__ ": " \
- fmt "\n", ## args); \
+#define dprintk(fmt, args...) \
+ do { \
+ if (debug) \
+ printk(KERN_DEBUG fmt, ## args); \
} while (0)
-/* Version Information */
-#define DRIVER_VERSION "v0.2"
-#define DRIVER_AUTHOR "Dan Conti, dconti@acm.wwu.edu"
-#define DRIVER_DESC "USB Microsoft IR Transceiver Driver"
-#define DRIVER_NAME "lirc_mceusb"
-
-/* Define these values to match your device */
-#define USB_MCEUSB_VENDOR_ID 0x045e
-#define USB_MCEUSB_PRODUCT_ID 0x006d
-
-/* table of devices that work with this driver */
-static struct usb_device_id mceusb_table[] = {
- /* USB Microsoft IR Transceiver */
- { USB_DEVICE(USB_MCEUSB_VENDOR_ID, USB_MCEUSB_PRODUCT_ID) },
+/* general constants */
+#define SEND_FLAG_IN_PROGRESS 1
+#define SEND_FLAG_COMPLETE 2
+#define RECV_FLAG_IN_PROGRESS 3
+#define RECV_FLAG_COMPLETE 4
+
+#define MCEUSB_INBOUND 1
+#define MCEUSB_OUTBOUND 2
+
+#define VENDOR_PHILIPS 0x0471
+#define VENDOR_SMK 0x0609
+#define VENDOR_TATUNG 0x1460
+#define VENDOR_GATEWAY 0x107b
+#define VENDOR_SHUTTLE 0x1308
+#define VENDOR_SHUTTLE2 0x051c
+#define VENDOR_MITSUMI 0x03ee
+#define VENDOR_TOPSEED 0x1784
+#define VENDOR_RICAVISION 0x179d
+#define VENDOR_ITRON 0x195d
+#define VENDOR_FIC 0x1509
+#define VENDOR_LG 0x043e
+#define VENDOR_MICROSOFT 0x045e
+#define VENDOR_FORMOSA 0x147a
+#define VENDOR_FINTEK 0x1934
+#define VENDOR_PINNACLE 0x2304
+#define VENDOR_ECS 0x1019
+#define VENDOR_WISTRON 0x0fb8
+#define VENDOR_COMPRO 0x185b
+#define VENDOR_NORTHSTAR 0x04eb
+
+static struct usb_device_id mceusb_dev_table[] = {
+ /* Original Microsoft MCE IR Transceiver (often HP-branded) */
+ { USB_DEVICE(VENDOR_MICROSOFT, 0x006d) },
+ /* Philips Infrared Transceiver - Sahara branded */
+ { USB_DEVICE(VENDOR_PHILIPS, 0x0608) },
+ /* Philips Infrared Transceiver - HP branded */
+ { USB_DEVICE(VENDOR_PHILIPS, 0x060c) },
+ /* Philips SRM5100 */
+ { USB_DEVICE(VENDOR_PHILIPS, 0x060d) },
+ /* Philips Infrared Transceiver - Omaura */
+ { USB_DEVICE(VENDOR_PHILIPS, 0x060f) },
+ /* Philips Infrared Transceiver - Spinel plus */
+ { USB_DEVICE(VENDOR_PHILIPS, 0x0613) },
+ /* Philips eHome Infrared Transceiver */
+ { USB_DEVICE(VENDOR_PHILIPS, 0x0815) },
+ /* SMK/Toshiba G83C0004D410 */
+ { USB_DEVICE(VENDOR_SMK, 0x031d) },
+ /* SMK eHome Infrared Transceiver (Sony VAIO) */
+ { USB_DEVICE(VENDOR_SMK, 0x0322) },
+ /* bundled with Hauppauge PVR-150 */
+ { USB_DEVICE(VENDOR_SMK, 0x0334) },
+ /* Tatung eHome Infrared Transceiver */
+ { USB_DEVICE(VENDOR_TATUNG, 0x9150) },
+ /* Shuttle eHome Infrared Transceiver */
+ { USB_DEVICE(VENDOR_SHUTTLE, 0xc001) },
+ /* Shuttle eHome Infrared Transceiver */
+ { USB_DEVICE(VENDOR_SHUTTLE2, 0xc001) },
+ /* Gateway eHome Infrared Transceiver */
+ { USB_DEVICE(VENDOR_GATEWAY, 0x3009) },
+ /* Mitsumi */
+ { USB_DEVICE(VENDOR_MITSUMI, 0x2501) },
+ /* Topseed eHome Infrared Transceiver */
+ { USB_DEVICE(VENDOR_TOPSEED, 0x0001) },
+ /* Topseed HP eHome Infrared Transceiver */
+ { USB_DEVICE(VENDOR_TOPSEED, 0x0006) },
+ /* Topseed eHome Infrared Transceiver */
+ { USB_DEVICE(VENDOR_TOPSEED, 0x0007) },
+ /* Topseed eHome Infrared Transceiver */
+ { USB_DEVICE(VENDOR_TOPSEED, 0x0008) },
+ /* Topseed eHome Infrared Transceiver */
+ { USB_DEVICE(VENDOR_TOPSEED, 0x000a) },
+ /* Ricavision internal Infrared Transceiver */
+ { USB_DEVICE(VENDOR_RICAVISION, 0x0010) },
+ /* Itron ione Libra Q-11 */
+ { USB_DEVICE(VENDOR_ITRON, 0x7002) },
+ /* FIC eHome Infrared Transceiver */
+ { USB_DEVICE(VENDOR_FIC, 0x9242) },
+ /* LG eHome Infrared Transceiver */
+ { USB_DEVICE(VENDOR_LG, 0x9803) },
+ /* Microsoft MCE Infrared Transceiver */
+ { USB_DEVICE(VENDOR_MICROSOFT, 0x00a0) },
+ /* Formosa eHome Infrared Transceiver */
+ { USB_DEVICE(VENDOR_FORMOSA, 0xe015) },
+ /* Formosa21 / eHome Infrared Receiver */
+ { USB_DEVICE(VENDOR_FORMOSA, 0xe016) },
+ /* Formosa aim / Trust MCE Infrared Receiver */
+ { USB_DEVICE(VENDOR_FORMOSA, 0xe017) },
+ /* Formosa Industrial Computing / Beanbag Emulation Device */
+ { USB_DEVICE(VENDOR_FORMOSA, 0xe018) },
+ /* Formosa21 / eHome Infrared Receiver */
+ { USB_DEVICE(VENDOR_FORMOSA, 0xe03a) },
+ /* Formosa Industrial Computing AIM IR605/A */
+ { USB_DEVICE(VENDOR_FORMOSA, 0xe03c) },
+ /* Fintek eHome Infrared Transceiver */
+ { USB_DEVICE(VENDOR_FINTEK, 0x0602) },
+ /* Fintek eHome Infrared Transceiver (in the AOpen MP45) */
+ { USB_DEVICE(VENDOR_FINTEK, 0x0702) },
+ /* Pinnacle Remote Kit */
+ { USB_DEVICE(VENDOR_PINNACLE, 0x0225) },
+ /* Elitegroup Computer Systems IR */
+ { USB_DEVICE(VENDOR_ECS, 0x0f38) },
+ /* Wistron Corp. eHome Infrared Receiver */
+ { USB_DEVICE(VENDOR_WISTRON, 0x0002) },
+ /* Compro K100 */
+ { USB_DEVICE(VENDOR_COMPRO, 0x3020) },
+ /* Compro K100 v2 */
+ { USB_DEVICE(VENDOR_COMPRO, 0x3082) },
+ /* Northstar Systems eHome Infrared Transceiver */
+ { USB_DEVICE(VENDOR_NORTHSTAR, 0xe004) },
/* Terminating entry */
{ }
};
-/* we can have up to this number of device plugged in at once */
-#define MAX_DEVICES 16
-
-/* Structure to hold all of our device specific stuff */
-struct mceusb_device {
- struct usb_device *udev; /* save off the usb device pointer */
- struct usb_interface *interface; /* the interface for this device */
- unsigned char minor; /* the starting minor number for this device */
- unsigned char num_ports; /* the number of ports this device has */
- char num_interrupt_in; /* number of interrupt in endpoints */
- char num_bulk_in; /* number of bulk in endpoints */
- char num_bulk_out; /* number of bulk out endpoints */
-
- unsigned char *bulk_in_buffer; /* the buffer to receive data */
- int bulk_in_size; /* the size of the receive buffer */
- __u8 bulk_in_endpointAddr; /* the address of bulk in endpoint */
-
- unsigned char *bulk_out_buffer; /* the buffer to send data */
- int bulk_out_size; /* the size of the send buffer */
- struct urb *write_urb; /* the urb used to send data */
- __u8 bulk_out_endpointAddr; /* the address of bulk out endpoint */
-
- wait_queue_head_t wait_q; /* for timeouts */
- struct mutex lock; /* locks this structure */
+static struct usb_device_id pinnacle_list[] = {
+ { USB_DEVICE(VENDOR_PINNACLE, 0x0225) },
+ {}
+};
- struct lirc_driver *driver;
+static struct usb_device_id microsoft_gen1_list[] = {
+ { USB_DEVICE(VENDOR_MICROSOFT, 0x006d) },
+ {}
+};
- lirc_t lircdata[256]; /* place to store data until lirc processes it */
- int lircidx; /* current index */
- int lirccnt; /* remaining values */
+static struct usb_device_id transmitter_mask_list[] = {
+ { USB_DEVICE(VENDOR_SMK, 0x031d) },
+ { USB_DEVICE(VENDOR_SMK, 0x0322) },
+ { USB_DEVICE(VENDOR_SMK, 0x0334) },
+ { USB_DEVICE(VENDOR_TOPSEED, 0x0001) },
+ { USB_DEVICE(VENDOR_TOPSEED, 0x0006) },
+ { USB_DEVICE(VENDOR_TOPSEED, 0x0007) },
+ { USB_DEVICE(VENDOR_TOPSEED, 0x0008) },
+ { USB_DEVICE(VENDOR_TOPSEED, 0x000a) },
+ { USB_DEVICE(VENDOR_PINNACLE, 0x0225) },
+ {}
+};
- int usb_valid_bytes_in_bulk_buffer; /* leftover data from prior read */
- int mce_bytes_left_in_packet; /* for packets split across reads */
+/* data structure for each usb transceiver */
+struct mceusb_dev {
- /* Value to hold the last received space; 0 if last value
- * received was a pulse */
- int last_space;
+ /* usb */
+ struct usb_device *usbdev;
+ struct urb *urb_in;
+ int devnum;
+ struct usb_endpoint_descriptor *usb_ep_in;
+ struct usb_endpoint_descriptor *usb_ep_out;
-#ifdef KERNEL_2_5
+ /* buffers and dma */
+ unsigned char *buf_in;
+ unsigned int len_in;
dma_addr_t dma_in;
dma_addr_t dma_out;
-#endif
+ unsigned int overflow_len;
+
+ /* lirc */
+ struct lirc_driver *d;
+ lirc_t lircdata;
+ unsigned char is_pulse;
+ struct {
+ u32 connected:1;
+ u32 pinnacle:1;
+ u32 transmitter_mask_inverted:1;
+ u32 microsoft_gen1:1;
+ u32 reserved:28;
+ } flags;
+
+ unsigned char transmitter_mask;
+ unsigned int carrier_freq;
+
+ /* handle sending (init strings) */
+ int send_flags;
+ wait_queue_head_t wait_out;
+
+ struct mutex lock;
};
-#define MCE_TIME_UNIT 50
+/* init strings */
+static char init1[] = {0x00, 0xff, 0xaa, 0xff, 0x0b};
+static char init2[] = {0xff, 0x18};
-/* driver api */
-#ifdef KERNEL_2_5
-static int mceusb_probe(struct usb_interface *interface,
- const struct usb_device_id *id);
-static void mceusb_disconnect(struct usb_interface *interface);
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
-static void mceusb_write_bulk_callback(struct urb *urb, struct pt_regs *regs);
+static char pin_init1[] = { 0x9f, 0x07};
+static char pin_init2[] = { 0x9f, 0x13};
+static char pin_init3[] = { 0x9f, 0x0d};
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 11)
+static unsigned long usecs_to_jiffies(const unsigned int u)
+{
+ if (u > jiffies_to_usecs(MAX_JIFFY_OFFSET))
+ return MAX_JIFFY_OFFSET;
+#if HZ <= USEC_PER_SEC && !(USEC_PER_SEC % HZ)
+ return (u + (USEC_PER_SEC / HZ) - 1) / (USEC_PER_SEC / HZ);
+#elif HZ > USEC_PER_SEC && !(HZ % USEC_PER_SEC)
+ return u * (HZ / USEC_PER_SEC);
#else
-static void mceusb_write_bulk_callback(struct urb *urb);
+ return (u * HZ + USEC_PER_SEC - 1) / USEC_PER_SEC;
#endif
-#else
-static void *mceusb_probe(struct usb_device *dev, unsigned int ifnum,
- const struct usb_device_id *id);
-static void mceusb_disconnect(struct usb_device *dev, void *ptr);
-static void mceusb_write_bulk_callback(struct urb *urb);
+}
#endif
+static void mceusb_dev_printdata(struct mceusb_dev *ir, char *buf, int len)
+{
+ char codes[USB_BUFLEN * 3 + 1];
+ int i;
-/* read data from the usb bus; convert to mode2 */
-static int msir_fetch_more_data(struct mceusb_device *dev, int dont_block);
-
-/* helper functions */
-static void msir_cleanup(struct mceusb_device *dev);
-static void set_use_dec(void *data);
-static int set_use_inc(void *data);
-
-/* array of pointers to our devices that are currently connected */
-static struct mceusb_device *minor_table[MAX_DEVICES];
+ if (len <= 0)
+ return;
-/* lock to protect the minor_table structure */
-static DEFINE_MUTEX(minor_table_mutex);
-static void mceusb_setup(struct usb_device *udev);
+ if (ir->flags.microsoft_gen1 && len <= 2)
+ return;
-/* usb specific object needed to register this driver with the usb subsystem */
-static struct usb_driver mceusb_driver = {
- LIRC_THIS_MODULE(.owner = THIS_MODULE)
- .name = DRIVER_NAME,
- .probe = mceusb_probe,
- .disconnect = mceusb_disconnect,
- .id_table = mceusb_table,
-};
+ for (i = 0; i < len && i < USB_BUFLEN; i++)
+ snprintf(codes + i * 3, 4, "%02x ", buf[i] & 0xFF);
-static void mceusb_delete(struct mceusb_device *dev)
-{
- dprintk("%s", __func__);
- minor_table[dev->minor] = NULL;
-#ifdef KERNEL_2_5
- usb_buffer_free(dev->udev, dev->bulk_in_size,
- dev->bulk_in_buffer, dev->dma_in);
- usb_buffer_free(dev->udev, dev->bulk_out_size,
- dev->bulk_out_buffer, dev->dma_out);
-#else
- if (dev->bulk_in_buffer != NULL)
- kfree(dev->bulk_in_buffer);
- if (dev->bulk_out_buffer != NULL)
- kfree(dev->bulk_out_buffer);
-#endif
- if (dev->write_urb != NULL)
- usb_free_urb(dev->write_urb);
- kfree(dev);
+ printk(KERN_INFO "" DRIVER_NAME "[%d]: data received %s (length=%d)\n",
+ ir->devnum, codes, len);
}
-static void mceusb_setup(struct usb_device *udev)
+static void usb_async_callback(struct urb *urb, struct pt_regs *regs)
{
- char data[8];
- int res;
+ struct mceusb_dev *ir;
+ int len;
- memset(data, 0, 8);
+ if (!urb)
+ return;
- /* Get Status */
- res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
- USB_REQ_GET_STATUS, USB_DIR_IN,
- 0, 0, data, 2, HZ * 3);
+ ir = urb->context;
+ if (ir) {
+ len = urb->actual_length;
- /* res = usb_get_status( udev, 0, 0, data ); */
- dprintk("%s - res = %d status = 0x%x 0x%x", __func__,
- res, data[0], data[1]);
-
- /*
- * This is a strange one. They issue a set address to the device
- * on the receive control pipe and expect a certain value pair back
- */
- memset(data, 0, 8);
-
- res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
- 5, USB_TYPE_VENDOR, 0, 0,
- data, 2, HZ * 3);
- dprintk("%s - res = %d, devnum = %d", __func__, res, udev->devnum);
- dprintk("%s - data[0] = %d, data[1] = %d", __func__,
- data[0], data[1]);
+ dprintk(DRIVER_NAME
+ "[%d]: callback called (status=%d len=%d)\n",
+ ir->devnum, urb->status, len);
+ if (debug)
+ mceusb_dev_printdata(ir, urb->transfer_buffer, len);
+ }
- /* set feature */
- res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
- USB_REQ_SET_FEATURE, USB_TYPE_VENDOR,
- 0xc04e, 0x0000, NULL, 0, HZ * 3);
+}
- dprintk("%s - res = %d", __func__, res);
+/* request incoming or send outgoing usb packet - used to initialize remote */
+static void request_packet_async(struct mceusb_dev *ir,
+ struct usb_endpoint_descriptor *ep,
+ unsigned char *data, int size, int urb_type)
+{
+ int res;
+ struct urb *async_urb;
+ unsigned char *async_buf;
+
+ if (urb_type) {
+ async_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (unlikely(!async_urb))
+ return;
+
+ async_buf = kmalloc(size, GFP_KERNEL);
+ if (!async_buf) {
+ usb_free_urb(async_urb);
+ return;
+ }
- /*
- * These two are sent by the windows driver, but stall for
- * me. I don't have an analyzer on the Linux side so I can't
- * see what is actually different and why the device takes
- * issue with them
- */
-#if 0
- /* this is some custom control message they send */
- res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
- 0x04, USB_TYPE_VENDOR,
- 0x0808, 0x0000, NULL, 0, HZ * 3);
+ if (urb_type == MCEUSB_OUTBOUND) {
+ /* outbound data */
+ usb_fill_int_urb(async_urb, ir->usbdev,
+ usb_sndintpipe(ir->usbdev,
+ ep->bEndpointAddress),
+ async_buf, size,
+ (usb_complete_t) usb_async_callback,
+ ir, ep->bInterval);
+ memcpy(async_buf, data, size);
+ } else {
+ /* inbound data */
+ usb_fill_int_urb(async_urb, ir->usbdev,
+ usb_rcvintpipe(ir->usbdev,
+ ep->bEndpointAddress),
+ async_buf, size,
+ (usb_complete_t) usb_async_callback,
+ ir, ep->bInterval);
+ }
+ async_urb->transfer_flags = URB_ASYNC_UNLINK;
+ } else {
+ /* standard request */
+ async_urb = ir->urb_in;
+ ir->send_flags = RECV_FLAG_IN_PROGRESS;
+ }
- dprintk("%s - res = %d", __func__, res);
+ dprintk(DRIVER_NAME "[%d]: receive request called (size=%#x)\n",
+ ir->devnum, size);
- /* this is another custom control message they send */
- res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
- 0x02, USB_TYPE_VENDOR,
- 0x0000, 0x0100, NULL, 0, HZ * 3);
+ async_urb->transfer_buffer_length = size;
+ async_urb->dev = ir->usbdev;
- dprintk("%s - res = %d", __func__, res);
-#endif
+ res = usb_submit_urb(async_urb, GFP_ATOMIC);
+ if (res) {
+ dprintk(DRIVER_NAME "[%d]: receive request FAILED! (res=%d)\n",
+ ir->devnum, res);
+ return;
+ }
+ dprintk(DRIVER_NAME "[%d]: receive request complete (res=%d)\n",
+ ir->devnum, res);
}
-static void msir_cleanup(struct mceusb_device *dev)
+static int unregister_from_lirc(struct mceusb_dev *ir)
{
- memset(dev->bulk_in_buffer, 0, dev->bulk_in_size);
+ struct lirc_driver *d = ir->d;
+ int devnum;
+ int rtn;
+
+ devnum = ir->devnum;
+ dprintk(DRIVER_NAME "[%d]: unregister from lirc called\n", devnum);
+
+ rtn = lirc_unregister_driver(d->minor);
+ if (rtn > 0) {
+ printk(DRIVER_NAME "[%d]: error in lirc_unregister minor: %d\n"
+ "Trying again...\n", devnum, d->minor);
+ if (rtn == -EBUSY) {
+ printk(DRIVER_NAME
+ "[%d]: device is opened, will unregister"
+ " on close\n", devnum);
+ return -EAGAIN;
+ }
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(HZ);
+
+ rtn = lirc_unregister_driver(d->minor);
+ if (rtn > 0)
+ printk(DRIVER_NAME "[%d]: lirc_unregister failed\n",
+ devnum);
+ }
- dev->usb_valid_bytes_in_bulk_buffer = 0;
+ if (rtn) {
+ printk(DRIVER_NAME "[%d]: didn't free resources\n", devnum);
+ return -EAGAIN;
+ }
- dev->last_space = PULSE_MASK;
+ printk(DRIVER_NAME "[%d]: usb remote disconnected\n", devnum);
- dev->mce_bytes_left_in_packet = 0;
- dev->lircidx = 0;
- dev->lirccnt = 0;
- memset(dev->lircdata, 0, sizeof(dev->lircdata));
+ lirc_buffer_free(d->rbuf);
+ kfree(d->rbuf);
+ kfree(d);
+ kfree(ir);
+ return 0;
}
-static int set_use_inc(void *data)
+static int mceusb_ir_open(void *data)
{
+ struct mceusb_dev *ir = data;
+
+ if (!ir) {
+ printk(DRIVER_NAME "[?]: %s called with no context\n",
+ __func__);
+ return -EIO;
+ }
+ dprintk(DRIVER_NAME "[%d]: mceusb IR device opened\n", ir->devnum);
+
MOD_INC_USE_COUNT;
+ if (!ir->flags.connected) {
+ if (!ir->usbdev)
+ return -ENOENT;
+ ir->flags.connected = 1;
+ }
+
return 0;
}
-static void set_use_dec(void *data)
+static void mceusb_ir_close(void *data)
{
+ struct mceusb_dev *ir = data;
+
+ if (!ir) {
+ printk(DRIVER_NAME "[?]: %s called with no context\n",
+ __func__);
+ return;
+ }
+ dprintk(DRIVER_NAME "[%d]: mceusb IR device closed\n", ir->devnum);
+
+ if (ir->flags.connected) {
+ mutex_lock(&ir->lock);
+ ir->flags.connected = 0;
+ mutex_unlock(&ir->lock);
+ }
MOD_DEC_USE_COUNT;
}
-/*
- * msir_fetch_more_data
- *
- * The goal here is to read in more remote codes from the remote. In
- * the event that the remote isn't sending us anything, the caller
- * will block until a key is pressed (i.e. this performs phys read,
- * filtering, and queueing of data) unless dont_block is set to 1; in
- * this situation, it will perform a few reads and will exit out if it
- * does not see any appropriate data
- *
- * dev->lock should be locked when this function is called - fine grain
- * locking isn't really important here anyways
- *
- * This routine always returns the number of words available
- *
- */
-static int msir_fetch_more_data(struct mceusb_device *dev, int dont_block)
+static void send_packet_to_lirc(struct mceusb_dev *ir)
{
- int retries = 0;
- int words_to_read =
- (sizeof(dev->lircdata)/sizeof(lirc_t)) - dev->lirccnt;
- int partial, this_read = 0;
- int bulkidx = 0;
- int bytes_left_in_packet = 0;
- signed char *signedp = (signed char *)dev->bulk_in_buffer;
+ if (ir->lircdata) {
+ lirc_buffer_write(ir->d->rbuf,
+ (unsigned char *) &ir->lircdata);
+ wake_up(&ir->d->rbuf->wait_poll);
+ ir->lircdata = 0;
+ }
+}
- if (words_to_read == 0)
- return dev->lirccnt;
+static void mceusb_process_ir_data(struct mceusb_dev *ir, int buf_len)
+{
+ int i, j;
+ int packet_len = 0;
+ int start_index = 0;
+
+ /* skip meaningless 0xb1 0x60 header bytes on orig receiver */
+ if (ir->flags.microsoft_gen1)
+ start_index = 2;
+
+ /* this should only trigger w/the 1st-gen mce receiver */
+ for (i = start_index; i < (start_index + ir->overflow_len) &&
+ i < buf_len; i++) {
+ /* rising/falling flank */
+ if (ir->is_pulse != (ir->buf_in[i] & MCE_PULSE_BIT)) {
+ send_packet_to_lirc(ir);
+ ir->is_pulse = ir->buf_in[i] & MCE_PULSE_BIT;
+ }
- /*
- * this forces all existing data to be read by lirc before we
- * issue another usb command. this is the only form of
- * throttling we have
- */
- if (dev->lirccnt)
- return dev->lirccnt;
+ /* accumulate mce pulse/space values */
+ ir->lircdata += (ir->buf_in[i] & MCE_PULSE_MASK) *
+ MCE_TIME_UNIT;
+ ir->lircdata |= (ir->is_pulse ? PULSE_BIT : 0);
+ }
+ start_index += ir->overflow_len;
+ ir->overflow_len = 0;
+
+ for (i = start_index; i < buf_len; i++) {
+ /* decode mce packets of the form (84),AA,BB,CC,DD */
+ if (ir->buf_in[i] >= 0x80 && ir->buf_in[i] <= 0x9e) {
+ /* data headers */
+ /* decode packet data */
+ packet_len = ir->buf_in[i] & MCE_PACKET_LENGTH_MASK;
+ ir->overflow_len = i + 1 + packet_len - buf_len;
+ for (j = 1; j <= packet_len && (i + j < buf_len); j++) {
+ /* rising/falling flank */
+ if (ir->is_pulse !=
+ (ir->buf_in[i + j] & MCE_PULSE_BIT)) {
+ send_packet_to_lirc(ir);
+ ir->is_pulse =
+ ir->buf_in[i + j] &
+ MCE_PULSE_BIT;
+ }
- /* reserve room for our leading space */
- if (dev->last_space)
- words_to_read--;
+ /* accumulate mce pulse/space values */
+ ir->lircdata +=
+ (ir->buf_in[i + j] & MCE_PULSE_MASK) *
+ MCE_TIME_UNIT;
+ ir->lircdata |= (ir->is_pulse ? PULSE_BIT : 0);
+ }
- while (words_to_read) {
- /* handle signals and USB disconnects */
- if (signal_pending(current))
- return dev->lirccnt ? dev->lirccnt : -EINTR;
+ i += packet_len;
+ } else if (ir->buf_in[i] == MCE_CONTROL_HEADER) {
+ /* status header (0x9F) */
+ /*
+ * A transmission containing one or more consecutive ir
+ * commands always ends with a GAP of 100ms followed by
+ * the sequence 0x9F 0x01 0x01 0x9F 0x15 0x00 0x00 0x80
+ */
- bulkidx = 0;
+#if 0
+ Uncomment this if the last 100ms "infinity"-space should be transmitted
+ to lirc directly instead of at the beginning of the next transmission.
+ Changes pulse/space order.
+
+ if (++i < buf_len && ir->buf_in[i]==0x01)
+ send_packet_to_lirc(ir);
- /* perform data read (phys or from previous buffer) */
+#endif
- /* use leftovers if present, otherwise perform a read */
- if (dev->usb_valid_bytes_in_bulk_buffer) {
- this_read = dev->usb_valid_bytes_in_bulk_buffer;
- partial = this_read;
- dev->usb_valid_bytes_in_bulk_buffer = 0;
+ /* end decode loop */
+ dprintk(DRIVER_NAME "[%d] %s: found control header\n",
+ ir->devnum, __func__);
+ ir->overflow_len = 0;
+ break;
} else {
- int retval;
+ dprintk(DRIVER_NAME "[%d] %s: stray packet?\n",
+ ir->devnum, __func__);
+ ir->overflow_len = 0;
+ }
+ }
- this_read = dev->bulk_in_size;
- partial = 0;
- retval = usb_bulk_msg(dev->udev,
- usb_rcvbulkpipe(dev->udev,
- dev->bulk_in_endpointAddr),
- (unsigned char *)dev->bulk_in_buffer,
- this_read, &partial, HZ*10);
+ return;
+}
- /*
- * retry a few times on overruns; map all
- * other errors to -EIO
- */
- if (retval) {
- if (retval == -EOVERFLOW && retries < 5) {
- retries++;
- interruptible_sleep_on_timeout(
- &dev->wait_q, HZ);
- continue;
- } else
- return -EIO;
- }
+static void mceusb_dev_recv(struct urb *urb, struct pt_regs *regs)
+{
+ struct mceusb_dev *ir;
+ int buf_len;
- retries = 0;
- if (partial)
- this_read = partial;
+ if (!urb)
+ return;
- /* skip the header */
- bulkidx += 2;
+ ir = urb->context;
+ if (!ir) {
+ urb->transfer_flags |= URB_ASYNC_UNLINK;
+ usb_unlink_urb(urb);
+ return;
+ }
- /* check for empty reads (header only) */
- if (this_read == 2) {
- /* assume no data */
- if (dont_block)
- break;
+ buf_len = urb->actual_length;
- /*
- * sleep for a bit before performing
- * another read
- */
- interruptible_sleep_on_timeout(&dev->wait_q, 1);
- continue;
- }
- }
+ if (debug)
+ mceusb_dev_printdata(ir, urb->transfer_buffer, buf_len);
- /* process data */
+ if (ir->send_flags == RECV_FLAG_IN_PROGRESS) {
+ ir->send_flags = SEND_FLAG_COMPLETE;
+ dprintk(DRIVER_NAME "[%d]: setup answer received %d bytes\n",
+ ir->devnum, buf_len);
+ }
- /* at this point this_read is > 0 */
- while (bulkidx < this_read &&
- (words_to_read > (dev->last_space ? 1 : 0))) {
- /* while( bulkidx < this_read && words_to_read) */
- int keycode;
- int pulse = 0;
+ switch (urb->status) {
+ /* success */
+ case 0:
+ mceusb_process_ir_data(ir, buf_len);
+ break;
+
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ urb->transfer_flags |= URB_ASYNC_UNLINK;
+ usb_unlink_urb(urb);
+ return;
- /* read packet length if needed */
- if (!bytes_left_in_packet) {
- /*
- * we assume we are on a packet length
- * value. it is possible, in some
- * cases, to get a packet that does
- * not start with a length, apparently
- * due to some sort of fragmenting,
- * but occasionally we do not receive
- * the second half of a fragment
- */
- bytes_left_in_packet =
- 128 + signedp[bulkidx++];
+ case -EPIPE:
+ default:
+ break;
+ }
- /*
- * unfortunately rather than keep all
- * the data in the packetized format,
- * the transceiver sends a trailing 8
- * bytes that aren't part of the
- * transmission from the remote,
- * aren't packetized, and don't really
- * have any value. we can basically
- * tell we have hit them if 1) we have
- * a loooong space currently stored
- * up, and 2) the bytes_left value for
- * this packet is obviously wrong
- */
- if (bytes_left_in_packet > 4) {
- if (dev->mce_bytes_left_in_packet) {
- bytes_left_in_packet =
- dev->mce_bytes_left_in_packet;
- bulkidx--;
- }
- bytes_left_in_packet = 0;
- bulkidx = this_read;
- }
+ usb_submit_urb(urb, GFP_ATOMIC);
+}
- /*
- * always clear this if we have a
- * valid packet
- */
- dev->mce_bytes_left_in_packet = 0;
- /*
- * continue here to verify we haven't
- * hit the end of the bulk_in
- */
- continue;
+static ssize_t mceusb_transmit_ir(struct file *file, const char *buf,
+ size_t n, loff_t *ppos)
+{
+ int i, count = 0, cmdcount = 0;
+ struct mceusb_dev *ir = NULL;
+ lirc_t wbuf[LIRCBUF_SIZE]; /* Workbuffer with values from lirc */
+ unsigned char cmdbuf[MCE_CMDBUF_SIZE]; /* MCE command buffer */
+ unsigned long signal_duration = 0; /* Singnal length in us */
+ struct timeval start_time, end_time;
+
+ do_gettimeofday(&start_time);
+
+ /* Retrieve lirc_driver data for the device */
+ ir = lirc_get_pdata(file);
+ if (!ir || !ir->usb_ep_out)
+ return -EFAULT;
+
+ if (n % sizeof(lirc_t))
+ return -EINVAL;
+ count = n / sizeof(lirc_t);
+
+ /* Check if command is within limits */
+ if (count > LIRCBUF_SIZE || count%2 == 0)
+ return -EINVAL;
+ if (copy_from_user(wbuf, buf, n))
+ return -EFAULT;
+
+ /* MCE tx init header */
+ cmdbuf[cmdcount++] = MCE_CONTROL_HEADER;
+ cmdbuf[cmdcount++] = 0x08;
+ cmdbuf[cmdcount++] = ir->transmitter_mask;
+
+ /* Generate mce packet data */
+ for (i = 0; (i < count) && (cmdcount < MCE_CMDBUF_SIZE); i++) {
+ signal_duration += wbuf[i];
+ wbuf[i] = wbuf[i] / MCE_TIME_UNIT;
+
+ do { /* loop to support long pulses/spaces > 127*50us=6.35ms */
+
+ /* Insert mce packet header every 4th entry */
+ if ((cmdcount < MCE_CMDBUF_SIZE) &&
+ (cmdcount - MCE_TX_HEADER_LENGTH) %
+ MCE_CODE_LENGTH == 0)
+ cmdbuf[cmdcount++] = MCE_PACKET_HEADER;
+
+ /* Insert mce packet data */
+ if (cmdcount < MCE_CMDBUF_SIZE)
+ cmdbuf[cmdcount++] =
+ (wbuf[i] < MCE_PULSE_BIT ?
+ wbuf[i] : MCE_MAX_PULSE_LENGTH) |
+ (i & 1 ? 0x00 : MCE_PULSE_BIT);
+ else
+ return -EINVAL;
+ } while ((wbuf[i] > MCE_MAX_PULSE_LENGTH) &&
+ (wbuf[i] -= MCE_MAX_PULSE_LENGTH));
+ }
- }
+ /* Fix packet length in last header */
+ cmdbuf[cmdcount - (cmdcount - MCE_TX_HEADER_LENGTH) % MCE_CODE_LENGTH] =
+ 0x80 + (cmdcount - MCE_TX_HEADER_LENGTH) % MCE_CODE_LENGTH - 1;
- /* generate mode2 */
+ /* Check if we have room for the empty packet at the end */
+ if (cmdcount >= MCE_CMDBUF_SIZE)
+ return -EINVAL;
- keycode = signedp[bulkidx++];
- if (keycode < 0) {
- pulse = 1;
- keycode += 128;
- }
- keycode *= MCE_TIME_UNIT;
+ /* All mce commands end with an empty packet (0x80) */
+ cmdbuf[cmdcount++] = 0x80;
- bytes_left_in_packet--;
+ /* Transmit the command to the mce device */
+ request_packet_async(ir, ir->usb_ep_out, cmdbuf,
+ cmdcount, MCEUSB_OUTBOUND);
- if (pulse) {
- if (dev->last_space) {
- dev->lircdata[dev->lirccnt++] =
- dev->last_space;
- dev->last_space = 0;
- words_to_read--;
+ /*
+ * The lircd gap calculation expects the write function to
+ * wait the time it takes for the ircommand to be sent before
+ * it returns.
+ */
+ do_gettimeofday(&end_time);
+ signal_duration -= (end_time.tv_usec - start_time.tv_usec) +
+ (end_time.tv_sec - start_time.tv_sec) * 1000000;
- /* clear for the pulse */
- dev->lircdata[dev->lirccnt] = 0;
- }
- dev->lircdata[dev->lirccnt] += keycode;
- dev->lircdata[dev->lirccnt] |= PULSE_BIT;
- } else {
- /*
- * on pulse->space transition, add one
- * for the existing pulse
- */
- if (dev->lircdata[dev->lirccnt] &&
- !dev->last_space) {
- dev->lirccnt++;
- words_to_read--;
- }
+ /* delay with the closest number of ticks */
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(usecs_to_jiffies(signal_duration));
- dev->last_space += keycode;
+ return n;
+}
+
+static void set_transmitter_mask(struct mceusb_dev *ir, unsigned int mask)
+{
+ if (ir->flags.transmitter_mask_inverted)
+ /*
+ * The mask begins at 0x02 and has an inverted
+ * numbering scheme
+ */
+ ir->transmitter_mask =
+ (mask != 0x03 ? mask ^ 0x03 : mask) << 1;
+ else
+ ir->transmitter_mask = mask;
+}
+
+
+/* Sets the send carrier frequency */
+static int set_send_carrier(struct mceusb_dev *ir, int carrier)
+{
+ int clk = 10000000;
+ int prescaler = 0, divisor = 0;
+ unsigned char cmdbuf[] = { 0x9F, 0x06, 0x01, 0x80 };
+
+ /* Carrier is changed */
+ if (ir->carrier_freq != carrier) {
+
+ if (carrier <= 0) {
+ ir->carrier_freq = carrier;
+ dprintk(DRIVER_NAME "[%d]: SET_CARRIER disabling "
+ "carrier modulation\n", ir->devnum);
+ request_packet_async(ir, ir->usb_ep_out,
+ cmdbuf, sizeof(cmdbuf),
+ MCEUSB_OUTBOUND);
+ return carrier;
+ }
+
+ for (prescaler = 0; prescaler < 4; ++prescaler) {
+ divisor = (clk >> (2 * prescaler)) / carrier;
+ if (divisor <= 0xFF) {
+ ir->carrier_freq = carrier;
+ cmdbuf[2] = prescaler;
+ cmdbuf[3] = divisor;
+ dprintk(DRIVER_NAME "[%d]: SET_CARRIER "
+ "requesting %d Hz\n",
+ ir->devnum, carrier);
+
+ /* Transmit new carrier to mce device */
+ request_packet_async(ir, ir->usb_ep_out,
+ cmdbuf, sizeof(cmdbuf),
+ MCEUSB_OUTBOUND);
+ return carrier;
}
}
- }
- /* save off some info if we're exiting mid-packet, or with leftovers */
- if (bytes_left_in_packet)
- dev->mce_bytes_left_in_packet = bytes_left_in_packet;
- if (bulkidx < this_read) {
- dev->usb_valid_bytes_in_bulk_buffer = (this_read - bulkidx);
- memcpy(dev->bulk_in_buffer, &(dev->bulk_in_buffer[bulkidx]),
- dev->usb_valid_bytes_in_bulk_buffer);
+ return -EINVAL;
+
}
- return dev->lirccnt;
+
+ return carrier;
}
-/**
- * mceusb_add_to_buf: called by lirc_dev to fetch all available keys
- * this is used as a polling interface for us: since we set
- * driver->sample_rate we will periodically get the below call to
- * check for new data returns 0 on success, or -ENODATA if nothing is
- * available
- */
-static int mceusb_add_to_buf(void *data, struct lirc_buffer *buf)
+
+static int mceusb_lirc_ioctl(struct inode *node, struct file *filep,
+ unsigned int cmd, unsigned long arg)
{
- struct mceusb_device *dev = (struct mceusb_device *) data;
+ int result;
+ unsigned int ivalue;
+ unsigned long lvalue;
+ struct mceusb_dev *ir = NULL;
+
+ /* Retrieve lirc_driver data for the device */
+ ir = lirc_get_pdata(filep);
+ if (!ir || !ir->usb_ep_out)
+ return -EFAULT;
+
+
+ switch (cmd) {
+ case LIRC_SET_TRANSMITTER_MASK:
+
+ result = get_user(ivalue, (unsigned int *) arg);
+ if (result)
+ return result;
+ switch (ivalue) {
+ case 0x01: /* Transmitter 1 => 0x04 */
+ case 0x02: /* Transmitter 2 => 0x02 */
+ case 0x03: /* Transmitter 1 & 2 => 0x06 */
+ set_transmitter_mask(ir, ivalue);
+ break;
- mutex_lock(&dev->lock);
+ default: /* Unsupported transmitter mask */
+ return MCE_MAX_CHANNELS;
+ }
- if (!dev->lirccnt) {
- int res;
- dev->lircidx = 0;
+ dprintk(DRIVER_NAME ": SET_TRANSMITTERS mask=%d\n", ivalue);
+ break;
- res = msir_fetch_more_data(dev, 1);
+ case LIRC_GET_SEND_MODE:
- if (res == 0)
- res = -ENODATA;
- if (res < 0) {
- mutex_unlock(&dev->lock);
- return res;
- }
- }
+ result = put_user(LIRC_SEND2MODE(LIRC_CAN_SEND_PULSE &
+ LIRC_CAN_SEND_MASK),
+ (unsigned long *) arg);
- if (dev->lirccnt) {
- int keys_to_copy;
+ if (result)
+ return result;
+ break;
- /* determine available buffer space and available data */
- keys_to_copy = lirc_buffer_available(buf);
- if (keys_to_copy > dev->lirccnt)
- keys_to_copy = dev->lirccnt;
+ case LIRC_SET_SEND_MODE:
- lirc_buffer_write_n(buf,
- (unsigned char *) &(dev->lircdata[dev->lircidx]),
- keys_to_copy);
- dev->lircidx += keys_to_copy;
- dev->lirccnt -= keys_to_copy;
+ result = get_user(lvalue, (unsigned long *) arg);
- mutex_unlock(&dev->lock);
- return 0;
- }
+ if (result)
+ return result;
+ if (lvalue != (LIRC_MODE_PULSE&LIRC_CAN_SEND_MASK))
+ return -EINVAL;
+ break;
- mutex_unlock(&dev->lock);
- return -ENODATA;
-}
+ case LIRC_SET_SEND_CARRIER:
-#if defined(KERNEL_2_5) && LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
-static void mceusb_write_bulk_callback(struct urb *urb, struct pt_regs *regs)
-#else
-static void mceusb_write_bulk_callback(struct urb *urb)
-#endif
-{
- struct mceusb_device *dev = (struct mceusb_device *)urb->context;
+ result = get_user(ivalue, (unsigned int *) arg);
+ if (result)
+ return result;
- dprintk("%s - minor %d", __func__, dev->minor);
+ set_send_carrier(ir, ivalue);
+ break;
- if ((urb->status != -ENOENT) &&
- (urb->status != -ECONNRESET)) {
- dprintk("%s - nonzero write buld status received: %d",
- __func__, urb->status);
- return;
+ default:
+ return -ENOIOCTLCMD;
}
- return;
+ return 0;
}
-/**
- * mceusb_probe
- *
- * Called by the usb core when a new device is connected that it
- * thinks this driver might be interested in.
- */
-#ifdef KERNEL_2_5
-static int mceusb_probe(struct usb_interface *interface,
- const struct usb_device_id *id)
-{
- struct usb_device *udev = interface_to_usbdev(interface);
- struct usb_host_interface *iface_desc;
-#else
-static void *mceusb_probe(struct usb_device *udev, unsigned int ifnum,
- const struct usb_device_id *id)
-{
- struct usb_interface *interface = &udev->actconfig->interface[ifnum];
- struct usb_interface_descriptor *iface_desc;
-#endif
- struct mceusb_device *dev = NULL;
- struct usb_endpoint_descriptor *endpoint;
-
- struct lirc_driver *driver;
+static struct file_operations lirc_fops = {
+ .owner = THIS_MODULE,
+ .write = mceusb_transmit_ir,
+ .ioctl = mceusb_lirc_ioctl,
+};
- int minor;
- size_t buffer_size;
- int i;
- int retval = -ENOMEM;
- char junk[64];
+static int mceusb_gen1_init(struct mceusb_dev *ir)
+{
+ int i, ret;
+ char junk[64], data[8];
int partial = 0;
- /* See if the device offered us matches what we can accept */
- if (cpu_to_le16(udev->descriptor.idVendor) != USB_MCEUSB_VENDOR_ID ||
- cpu_to_le16(udev->descriptor.idProduct) != USB_MCEUSB_PRODUCT_ID) {
- dprintk("Wrong Vendor/Product IDs");
-#ifdef KERNEL_2_5
- return -ENODEV;
-#else
- return NULL;
-#endif
- }
+ /*
+ * Clear off the first few messages. These look like calibration
+ * or test data, I can't really tell. This also flushes in case
+ * we have random ir data queued up.
+ */
+ for (i = 0; i < 40; i++)
+ usb_bulk_msg(ir->usbdev,
+ usb_rcvbulkpipe(ir->usbdev,
+ ir->usb_ep_in->bEndpointAddress),
+ junk, 64, &partial, HZ * 10);
- /* select a "subminor" number (part of a minor number) */
- mutex_lock(&minor_table_mutex);
- for (minor = 0; minor < MAX_DEVICES; ++minor) {
- if (minor_table[minor] == NULL)
- break;
- }
- if (minor >= MAX_DEVICES) {
- printk(KERN_INFO "Too many devices plugged in, "
- "can not handle this device.\n");
- goto error;
- }
+ ir->is_pulse = 1;
- /* allocate memory for our device state and initialize it */
- dev = kzalloc(sizeof(struct mceusb_device), GFP_KERNEL);
- if (dev == NULL) {
- err("Out of memory");
-#ifdef KERNEL_2_5
- retval = -ENOMEM;
-#endif
- goto error;
- }
- minor_table[minor] = dev;
+ memset(data, 0, 8);
+
+ /* Get Status */
+ ret = usb_control_msg(ir->usbdev, usb_rcvctrlpipe(ir->usbdev, 0),
+ USB_REQ_GET_STATUS, USB_DIR_IN,
+ 0, 0, data, 2, HZ * 3);
- mutex_init(&dev->lock);
- dev->udev = udev;
- dev->interface = interface;
- dev->minor = minor;
+ /* ret = usb_get_status( ir->usbdev, 0, 0, data ); */
+ dprintk("%s - ret = %d status = 0x%x 0x%x\n", __func__,
+ ret, data[0], data[1]);
/*
- * set up the endpoint information, check out the endpoints.
- * use only the first bulk-in and bulk-out endpoints
+ * This is a strange one. They issue a set address to the device
+ * on the receive control pipe and expect a certain value pair back
*/
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 5)
- iface_desc = &interface->altsetting[0];
-#else
- iface_desc = interface->cur_altsetting;
-#endif
+ memset(data, 0, 8);
-#ifdef KERNEL_2_5
- for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
- endpoint = &iface_desc->endpoint[i].desc;
-#else
- for (i = 0; i < iface_desc->bNumEndpoints; ++i) {
- endpoint = &iface_desc->endpoint[i];
-#endif
- if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) &&
- ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
- USB_ENDPOINT_XFER_BULK)) {
- dprintk("we found a bulk in endpoint");
- buffer_size = endpoint->wMaxPacketSize;
- dev->bulk_in_size = buffer_size;
- dev->bulk_in_endpointAddr = endpoint->bEndpointAddress;
-#ifdef KERNEL_2_5
- dev->bulk_in_buffer =
- usb_buffer_alloc(udev, buffer_size,
- GFP_ATOMIC, &dev->dma_in);
-#else
- dev->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL);
-#endif
- if (!dev->bulk_in_buffer) {
- err("Couldn't allocate bulk_in_buffer");
- goto error;
- }
+ ret = usb_control_msg(ir->usbdev, usb_rcvctrlpipe(ir->usbdev, 0),
+ USB_REQ_SET_ADDRESS, USB_TYPE_VENDOR, 0, 0,
+ data, 2, HZ * 3);
+ dprintk("%s - ret = %d, devnum = %d\n",
+ __func__, ret, ir->usbdev->devnum);
+ dprintk("%s - data[0] = %d, data[1] = %d\n",
+ __func__, data[0], data[1]);
+
+ /* set feature */
+ ret = usb_control_msg(ir->usbdev, usb_sndctrlpipe(ir->usbdev, 0),
+ USB_REQ_SET_FEATURE, USB_TYPE_VENDOR,
+ 0xc04e, 0x0000, NULL, 0, HZ * 3);
+
+ dprintk("%s - ret = %d\n", __func__, ret);
+
+ /* strange: bRequest == 4 */
+ ret = usb_control_msg(ir->usbdev, usb_sndctrlpipe(ir->usbdev, 0),
+ 4, USB_TYPE_VENDOR,
+ 0x0808, 0x0000, NULL, 0, HZ * 3);
+ dprintk("%s - retB = %d\n", __func__, ret);
+
+ /* strange: bRequest == 2 */
+ ret = usb_control_msg(ir->usbdev, usb_sndctrlpipe(ir->usbdev, 0),
+ 2, USB_TYPE_VENDOR,
+ 0x0000, 0x0100, NULL, 0, HZ * 3);
+ dprintk("%s - retC = %d\n", __func__, ret);
+
+ return ret;
+
+};
+
+static int mceusb_dev_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usb_device *dev = interface_to_usbdev(intf);
+ struct usb_host_interface *idesc;
+ struct usb_endpoint_descriptor *ep = NULL;
+ struct usb_endpoint_descriptor *ep_in = NULL;
+ struct usb_endpoint_descriptor *ep_out = NULL;
+ struct usb_host_config *config;
+ struct mceusb_dev *ir = NULL;
+ struct lirc_driver *driver = NULL;
+ struct lirc_buffer *rbuf = NULL;
+ int devnum, pipe, maxp;
+ int minor = 0;
+ int i;
+ char buf[63], name[128] = "";
+ int mem_failure = 0;
+ int is_pinnacle;
+ int is_microsoft_gen1;
+
+ dprintk(DRIVER_NAME ": %s called\n", __func__);
+
+ usb_reset_device(dev);
+
+ config = dev->actconfig;
+
+ idesc = intf->cur_altsetting;
+
+ is_pinnacle = usb_match_id(intf, pinnacle_list) ? 1 : 0;
+
+ is_microsoft_gen1 = usb_match_id(intf, microsoft_gen1_list) ? 1 : 0;
+
+ /* step through the endpoints to find first bulk in and out endpoint */
+ for (i = 0; i < idesc->desc.bNumEndpoints; ++i) {
+ ep = &idesc->endpoint[i].desc;
+
+ if ((ep_in == NULL)
+ && ((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
+ == USB_DIR_IN)
+ && (((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+ == USB_ENDPOINT_XFER_BULK)
+ || ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+ == USB_ENDPOINT_XFER_INT))) {
+
+ dprintk(DRIVER_NAME ": acceptable inbound endpoint "
+ "found\n");
+ ep_in = ep;
+ ep_in->bmAttributes = USB_ENDPOINT_XFER_INT;
+ if (is_pinnacle)
+ /*
+ * setting seems to 1 seem to cause issues with
+ * Pinnacle timing out on transfer.
+ */
+ ep_in->bInterval = ep->bInterval;
+ else
+ ep_in->bInterval = 1;
}
- if (((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
- == 0x00)
- && ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
- USB_ENDPOINT_XFER_BULK)) {
- dprintk("we found a bulk out endpoint");
-#ifdef KERNEL_2_5
- dev->write_urb = usb_alloc_urb(0, GFP_KERNEL);
-#else
- dev->write_urb = usb_alloc_urb(0);
-#endif
- if (!dev->write_urb) {
- err("No free urbs available");
- goto error;
- }
- buffer_size = endpoint->wMaxPacketSize;
- dev->bulk_out_size = buffer_size;
- dev->bulk_out_endpointAddr = endpoint->bEndpointAddress;
-#ifdef KERNEL_2_5
- dev->bulk_out_buffer =
- usb_buffer_alloc(udev, buffer_size,
- GFP_ATOMIC, &dev->dma_out);
-#else
- dev->bulk_out_buffer = kmalloc(buffer_size, GFP_KERNEL);
-#endif
- if (!dev->bulk_out_buffer) {
- err("Couldn't allocate bulk_out_buffer");
- goto error;
- }
-#ifdef KERNEL_2_5
- usb_fill_bulk_urb(dev->write_urb, udev,
- usb_sndbulkpipe
- (udev, endpoint->bEndpointAddress),
- dev->bulk_out_buffer, buffer_size,
- mceusb_write_bulk_callback, dev);
- dev->write_urb->transfer_dma = dev->dma_out;
- dev->write_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
-#else
- FILL_BULK_URB(dev->write_urb, udev,
- usb_sndbulkpipe(udev,
- endpoint->bEndpointAddress),
- dev->bulk_out_buffer, buffer_size,
- mceusb_write_bulk_callback, dev);
-#endif
+ if ((ep_out == NULL)
+ && ((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
+ == USB_DIR_OUT)
+ && (((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+ == USB_ENDPOINT_XFER_BULK)
+ || ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+ == USB_ENDPOINT_XFER_INT))) {
+
+ dprintk(DRIVER_NAME ": acceptable outbound endpoint "
+ "found\n");
+ ep_out = ep;
+ ep_out->bmAttributes = USB_ENDPOINT_XFER_INT;
+ if (is_pinnacle)
+ /*
+ * setting seems to 1 seem to cause issues with
+ * Pinnacle timing out on transfer.
+ */
+ ep_out->bInterval = ep->bInterval;
+ else
+ ep_out->bInterval = 1;
}
}
-
- if (!(dev->bulk_in_endpointAddr && dev->bulk_out_endpointAddr)) {
- err("Couldn't find both bulk-in and bulk-out endpoints");
- goto error;
+ if (ep_in == NULL || ep_out == NULL) {
+ dprintk(DRIVER_NAME ": inbound and/or "
+ "outbound endpoint not found\n");
+ return -ENODEV;
}
- /* init the waitq */
- init_waitqueue_head(&dev->wait_q);
+ devnum = dev->devnum;
+ pipe = usb_rcvintpipe(dev, ep_in->bEndpointAddress);
+ maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
+ mem_failure = 0;
+ ir = kzalloc(sizeof(struct mceusb_dev), GFP_KERNEL);
+ if (!ir) {
+ mem_failure = 1;
+ goto mem_failure_switch;
+ }
- /* Set up our lirc driver */
driver = kzalloc(sizeof(struct lirc_driver), GFP_KERNEL);
if (!driver) {
- err("out of memory");
- goto error;
+ mem_failure = 2;
+ goto mem_failure_switch;
+ }
+
+ rbuf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
+ if (!rbuf) {
+ mem_failure = 3;
+ goto mem_failure_switch;
+ }
+
+ if (lirc_buffer_init(rbuf, sizeof(lirc_t), LIRCBUF_SIZE)) {
+ mem_failure = 4;
+ goto mem_failure_switch;
+ }
+
+ ir->buf_in = usb_buffer_alloc(dev, maxp, GFP_ATOMIC, &ir->dma_in);
+ if (!ir->buf_in) {
+ mem_failure = 5;
+ goto mem_failure_switch;
+ }
+
+ ir->urb_in = usb_alloc_urb(0, GFP_KERNEL);
+ if (!ir->urb_in) {
+ mem_failure = 7;
+ goto mem_failure_switch;
}
strcpy(driver->name, DRIVER_NAME " ");
- driver->minor = minor;
+ driver->minor = -1;
+ driver->features = LIRC_CAN_SEND_PULSE |
+ LIRC_CAN_SET_TRANSMITTER_MASK |
+ LIRC_CAN_REC_MODE2 |
+ LIRC_CAN_SET_SEND_CARRIER;
+ driver->data = ir;
+ driver->rbuf = rbuf;
+ driver->set_use_inc = &mceusb_ir_open;
+ driver->set_use_dec = &mceusb_ir_close;
driver->code_length = sizeof(lirc_t) * 8;
- driver->features = LIRC_CAN_REC_MODE2; /* | LIRC_CAN_SEND_MODE2; */
- driver->data = dev;
- driver->buffer_size = 128;
- driver->set_use_inc = &set_use_inc;
- driver->set_use_dec = &set_use_dec;
- driver->sample_rate = 80; /* sample at 100hz (10ms) */
- driver->add_to_buf = &mceusb_add_to_buf;
-#ifdef LIRC_HAVE_SYSFS
- driver->dev = &interface->dev;
-#endif
+ driver->fops = &lirc_fops;
+ driver->dev = &intf->dev;
driver->owner = THIS_MODULE;
- if (lirc_register_driver(driver) < 0) {
+
+ mutex_init(&ir->lock);
+ 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_urb(ir->urb_in);
+ case 7:
+ usb_buffer_free(dev, maxp, ir->buf_in, ir->dma_in);
+ case 5:
+ lirc_buffer_free(rbuf);
+ case 4:
+ kfree(rbuf);
+ case 3:
kfree(driver);
- goto error;
+ 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 = maxp;
+ ir->overflow_len = 0;
+ ir->flags.connected = 0;
+ ir->flags.pinnacle = is_pinnacle;
+ ir->flags.microsoft_gen1 = is_microsoft_gen1;
+ ir->flags.transmitter_mask_inverted =
+ usb_match_id(intf, transmitter_mask_list) ? 0 : 1;
+
+ ir->lircdata = PULSE_MASK;
+ ir->is_pulse = 0;
+
+ /* ir->flags.transmitter_mask_inverted must be set */
+ set_transmitter_mask(ir, MCE_DEFAULT_TX_MASK);
+ /* Saving usb interface data for use by the transmitter routine */
+ ir->usb_ep_in = ep_in;
+ ir->usb_ep_out = ep_out;
+
+ 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);
+
+ /* inbound data */
+ usb_fill_int_urb(ir->urb_in, dev, pipe, ir->buf_in,
+ maxp, (usb_complete_t) mceusb_dev_recv, ir, ep_in->bInterval);
+ ir->urb_in->transfer_dma = ir->dma_in;
+ ir->urb_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ /* initialize device */
+ if (ir->flags.pinnacle) {
+ int usbret;
+
+ /*
+ * I have no idea why but this reset seems to be crucial to
+ * getting the device to do outbound IO correctly - without
+ * this the device seems to hang, ignoring all input - although
+ * IR signals are correctly sent from the device, no input is
+ * interpreted by the device and the host never does the
+ * completion routine
+ */
+
+ usbret = usb_reset_configuration(dev);
+ printk(DRIVER_NAME "[%d]: usb reset config ret %x\n",
+ devnum, usbret);
+
+ /*
+ * its possible we really should wait for a return
+ * for each of these...
+ */
+ request_packet_async(ir, ep_in, NULL, maxp, MCEUSB_INBOUND);
+ request_packet_async(ir, ep_out, pin_init1, sizeof(pin_init1),
+ MCEUSB_OUTBOUND);
+ request_packet_async(ir, ep_in, NULL, maxp, MCEUSB_INBOUND);
+ request_packet_async(ir, ep_out, pin_init2, sizeof(pin_init2),
+ MCEUSB_OUTBOUND);
+ request_packet_async(ir, ep_in, NULL, maxp, MCEUSB_INBOUND);
+ request_packet_async(ir, ep_out, pin_init3, sizeof(pin_init3),
+ MCEUSB_OUTBOUND);
+ } else if (ir->flags.microsoft_gen1) {
+ /* original ms mce device requires some additional setup */
+ mceusb_gen1_init(ir);
+ } else {
+
+ request_packet_async(ir, ep_in, NULL, maxp, MCEUSB_INBOUND);
+ request_packet_async(ir, ep_in, NULL, maxp, MCEUSB_INBOUND);
+ request_packet_async(ir, ep_out, init1,
+ sizeof(init1), MCEUSB_OUTBOUND);
+ request_packet_async(ir, ep_in, NULL, maxp, MCEUSB_INBOUND);
+ request_packet_async(ir, ep_out, init2,
+ sizeof(init2), MCEUSB_OUTBOUND);
}
- dev->driver = driver;
/*
- * clear off the first few messages. these look like
- * calibration or test data, i can't really tell
- * this also flushes in case we have random ir data queued up
+ * if we don't issue the correct number of receives (MCEUSB_INBOUND)
+ * for each outbound, then the first few ir pulses will be interpreted
+ * by the usb_async_callback routine - we should ensure we have the
+ * right amount OR less - as the meusb_dev_recv routine will handle
+ * the control packets OK - they start with 0x9f - but the async
+ * callback doesn't handle ir pulse packets
*/
- for (i = 0; i < 40; i++)
- (void) usb_bulk_msg(udev,
- usb_rcvbulkpipe(udev,
- dev->bulk_in_endpointAddr),
- junk, 64, &partial, HZ*10);
+ request_packet_async(ir, ep_in, NULL, maxp, 0);
- msir_cleanup(dev);
- mceusb_setup(udev);
+ usb_set_intfdata(intf, ir);
-#ifdef KERNEL_2_5
- /* we can register the device now, as it is ready */
- usb_set_intfdata(interface, dev);
-#endif
- mutex_unlock(&minor_table_mutex);
-#ifdef KERNEL_2_5
return 0;
-#else
- return dev;
-#endif
-error:
- if (likely(dev))
- mceusb_delete(dev);
-
- dev = NULL;
- dprintk("%s: retval = %x", __func__, retval);
- mutex_unlock(&minor_table_mutex);
-#ifdef KERNEL_2_5
- return retval;
-#else
- return NULL;
-#endif
}
-/**
- * mceusb_disconnect
- *
- * Called by the usb core when the device is removed from the system.
- *
- */
-#ifdef KERNEL_2_5
-static void mceusb_disconnect(struct usb_interface *interface)
-#else
-static void mceusb_disconnect(struct usb_device *udev, void *ptr)
-#endif
+
+static void mceusb_dev_disconnect(struct usb_interface *intf)
{
- struct mceusb_device *dev;
- int minor;
-#ifdef KERNEL_2_5
- dev = usb_get_intfdata(interface);
- usb_set_intfdata(interface, NULL);
-#else
- dev = (struct mceusb_device *)ptr;
-#endif
+ struct usb_device *dev = interface_to_usbdev(intf);
+ struct mceusb_dev *ir = usb_get_intfdata(intf);
- mutex_lock(&minor_table_mutex);
- mutex_lock(&dev->lock);
- minor = dev->minor;
+ usb_set_intfdata(intf, NULL);
- /* unhook lirc things */
- lirc_unregister_driver(minor);
- lirc_buffer_free(dev->driver->rbuf);
- kfree(dev->driver->rbuf);
- kfree(dev->driver);
+ if (!ir || !ir->d)
+ return;
- mutex_unlock(&dev->lock);
- mceusb_delete(dev);
+ ir->usbdev = NULL;
+ wake_up_all(&ir->wait_out);
- printk(KERN_INFO "Microsoft IR Transceiver #%d now disconnected\n",
- minor);
- mutex_unlock(&minor_table_mutex);
-}
+ mutex_lock(&ir->lock);
+ usb_kill_urb(ir->urb_in);
+ usb_free_urb(ir->urb_in);
+ usb_buffer_free(dev, ir->len_in, ir->buf_in, ir->dma_in);
+ mutex_unlock(&ir->lock);
+ unregister_from_lirc(ir);
+}
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
+static int mceusb_dev_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ struct mceusb_dev *ir = usb_get_intfdata(intf);
+ printk(DRIVER_NAME "[%d]: suspend\n", ir->devnum);
+ usb_kill_urb(ir->urb_in);
+ return 0;
+}
-static int __init usb_mceusb_init(void)
+static int mceusb_dev_resume(struct usb_interface *intf)
{
- int result;
+ struct mceusb_dev *ir = usb_get_intfdata(intf);
+ printk(DRIVER_NAME "[%d]: resume\n", ir->devnum);
+ if (usb_submit_urb(ir->urb_in, GFP_ATOMIC))
+ return -EIO;
+ return 0;
+}
+#endif
- /* register this driver with the USB subsystem */
- result = usb_register(&mceusb_driver);
-#ifdef KERNEL_2_5
- if (result) {
-#else
- if (result < 0) {
+static struct usb_driver mceusb_dev_driver = {
+ LIRC_THIS_MODULE(.owner = THIS_MODULE)
+ .name = DRIVER_NAME,
+ .probe = mceusb_dev_probe,
+ .disconnect = mceusb_dev_disconnect,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
+ .suspend = mceusb_dev_suspend,
+ .resume = mceusb_dev_resume,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23)
+ .reset_resume = mceusb_dev_resume,
#endif
- err("usb_register failed for the " DRIVER_NAME
- " driver. error number %d", result);
-#ifdef KERNEL_2_5
- return result;
-#else
- return -1;
#endif
+ .id_table = mceusb_dev_table
+};
+
+static int __init mceusb_dev_init(void)
+{
+ int i;
+
+ printk(KERN_INFO DRIVER_NAME ": " DRIVER_DESC " " DRIVER_VERSION "\n");
+ printk(KERN_INFO DRIVER_NAME ": " DRIVER_AUTHOR "\n");
+ dprintk(DRIVER_NAME ": debug mode enabled\n");
+
+ i = usb_register(&mceusb_dev_driver);
+ if (i < 0) {
+ printk(DRIVER_NAME ": usb register failed, result = %d\n", i);
+ return -ENODEV;
}
- printk(KERN_INFO DRIVER_DESC " " DRIVER_VERSION "\n");
return 0;
}
-
-static void __exit usb_mceusb_exit(void)
+static void __exit mceusb_dev_exit(void)
{
- usb_deregister(&mceusb_driver);
+ usb_deregister(&mceusb_dev_driver);
}
-module_init(usb_mceusb_init);
-module_exit(usb_mceusb_exit);
+module_init(mceusb_dev_init);
+module_exit(mceusb_dev_exit);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_LICENSE("GPL");
-MODULE_DEVICE_TABLE(usb, mceusb_table);
+MODULE_DEVICE_TABLE(usb, mceusb_dev_table);
+/* this was originally lirc_mceusb2, lirc_mceusb and lirc_mceusb2 merged now */
+MODULE_ALIAS("lirc_mceusb2");
-module_param(debug, int, S_IRUGO | S_IWUSR);
+module_param(debug, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(debug, "Debug enabled or not");
EXPORT_NO_SYMBOLS;
diff --git a/ubuntu/lirc/lirc_mceusb2/lirc_mceusb2.c b/ubuntu/lirc/lirc_mceusb2/lirc_mceusb2.c
deleted file mode 100644
index abfff4a9bd8..00000000000
--- a/ubuntu/lirc/lirc_mceusb2/lirc_mceusb2.c
+++ /dev/null
@@ -1,1145 +0,0 @@
-/*
- * LIRC driver for Philips eHome USB Infrared Transceiver
- * and the Microsoft MCE 2005 Remote Control
- *
- * (C) by Martin A. Blatter <martin_a_blatter@yahoo.com>
- *
- * Transmitter support and reception code cleanup.
- * (C) by Daniel Melander <lirc@rajidae.se>
- *
- * Derived from ATI USB driver by Paul Miller and the original
- * MCE USB driver by Dan Corti
- *
- * This driver will only work reliably with kernel version 2.6.10
- * or higher, probably because of differences in USB device enumeration
- * in the kernel code. Device initialization fails most of the time
- * with earlier kernel versions.
- *
- **********************************************************************
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- */
-
-#include <linux/version.h>
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 5)
-#error "*******************************************************"
-#error "Sorry, this driver needs kernel version 2.6.5 or higher"
-#error "*******************************************************"
-#endif
-#include <linux/autoconf.h>
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <linux/kmod.h>
-#include <linux/smp_lock.h>
-#include <linux/completion.h>
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18)
-#include <asm/uaccess.h>
-#else
-#include <linux/uaccess.h>
-#endif
-#include <linux/usb.h>
-#include <linux/wait.h>
-#include <linux/time.h>
-
-#include "../lirc.h"
-#include "../kcompat.h"
-#include "../lirc_dev/lirc_dev.h"
-
-#define DRIVER_VERSION "$Revision: 1.81 $"
-#define DRIVER_AUTHOR "Daniel Melander <lirc@rajidae.se>, " \
- "Martin Blatter <martin_a_blatter@yahoo.com>"
-#define DRIVER_DESC "Philips eHome USB IR Transceiver and Microsoft " \
- "MCE 2005 Remote Control driver for LIRC"
-#define DRIVER_NAME "lirc_mceusb2"
-
-#define USB_BUFLEN 32 /* USB reception buffer length */
-#define LIRCBUF_SIZE 256 /* LIRC work buffer length */
-
-/* MCE constants */
-#define MCE_CMDBUF_SIZE 384 /* MCE Command buffer length */
-#define MCE_TIME_UNIT 50 /* Approx 50us resolution */
-#define MCE_CODE_LENGTH 5 /* Normal length of packet (with header) */
-#define MCE_PACKET_SIZE 4 /* Normal length of packet (without header) */
-#define MCE_PACKET_HEADER 0x84 /* Actual header format is 0x80 + num_bytes */
-#define MCE_CONTROL_HEADER 0x9F /* MCE status header */
-#define MCE_TX_HEADER_LENGTH 3 /* # of bytes in the initializing tx header */
-#define MCE_MAX_CHANNELS 2 /* Two transmitters, hardware dependent? */
-#define MCE_DEFAULT_TX_MASK 0x03 /* Val opts: TX1=0x01, TX2=0x02, ALL=0x03 */
-#define MCE_PULSE_BIT 0x80 /* Pulse bit, MSB set == PULSE else SPACE */
-#define MCE_PULSE_MASK 0x7F /* Pulse mask */
-#define MCE_MAX_PULSE_LENGTH 0x7F /* Longest transmittable pulse symbol */
-#define MCE_PACKET_LENGTH_MASK 0x7F /* Pulse mask */
-
-
-/* module parameters */
-#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)
-
-/* general constants */
-#define SEND_FLAG_IN_PROGRESS 1
-#define SEND_FLAG_COMPLETE 2
-#define RECV_FLAG_IN_PROGRESS 3
-#define RECV_FLAG_COMPLETE 4
-
-#define PHILUSB_INBOUND 1
-#define PHILUSB_OUTBOUND 2
-
-#define VENDOR_PHILIPS 0x0471
-#define VENDOR_SMK 0x0609
-#define VENDOR_TATUNG 0x1460
-#define VENDOR_GATEWAY 0x107b
-#define VENDOR_SHUTTLE 0x1308
-#define VENDOR_SHUTTLE2 0x051c
-#define VENDOR_MITSUMI 0x03ee
-#define VENDOR_TOPSEED 0x1784
-#define VENDOR_RICAVISION 0x179d
-#define VENDOR_ITRON 0x195d
-#define VENDOR_FIC 0x1509
-#define VENDOR_LG 0x043e
-#define VENDOR_MICROSOFT 0x045e
-#define VENDOR_FORMOSA 0x147a
-#define VENDOR_FINTEK 0x1934
-#define VENDOR_PINNACLE 0x2304
-#define VENDOR_ECS 0x1019
-#define VENDOR_WISTRON 0x0fb8
-#define VENDOR_COMPRO 0x185b
-#define VENDOR_NORTHSTAR 0x04eb
-
-static struct usb_device_id mceusb_dev_table[] = {
- /* Philips Infrared Transceiver - Sahara branded */
- { USB_DEVICE(VENDOR_PHILIPS, 0x0608) },
- /* Philips Infrared Transceiver - HP branded */
- { USB_DEVICE(VENDOR_PHILIPS, 0x060c) },
- /* Philips SRM5100 */
- { USB_DEVICE(VENDOR_PHILIPS, 0x060d) },
- /* Philips Infrared Transceiver - Omaura */
- { USB_DEVICE(VENDOR_PHILIPS, 0x060f) },
- /* Philips Infrared Transceiver - Spinel plus */
- { USB_DEVICE(VENDOR_PHILIPS, 0x0613) },
- /* Philips eHome Infrared Transceiver */
- { USB_DEVICE(VENDOR_PHILIPS, 0x0815) },
- /* SMK/Toshiba G83C0004D410 */
- { USB_DEVICE(VENDOR_SMK, 0x031d) },
- /* SMK eHome Infrared Transceiver (Sony VAIO) */
- { USB_DEVICE(VENDOR_SMK, 0x0322) },
- /* bundled with Hauppauge PVR-150 */
- { USB_DEVICE(VENDOR_SMK, 0x0334) },
- /* Tatung eHome Infrared Transceiver */
- { USB_DEVICE(VENDOR_TATUNG, 0x9150) },
- /* Shuttle eHome Infrared Transceiver */
- { USB_DEVICE(VENDOR_SHUTTLE, 0xc001) },
- /* Shuttle eHome Infrared Transceiver */
- { USB_DEVICE(VENDOR_SHUTTLE2, 0xc001) },
- /* Gateway eHome Infrared Transceiver */
- { USB_DEVICE(VENDOR_GATEWAY, 0x3009) },
- /* Mitsumi */
- { USB_DEVICE(VENDOR_MITSUMI, 0x2501) },
- /* Topseed eHome Infrared Transceiver */
- { USB_DEVICE(VENDOR_TOPSEED, 0x0001) },
- /* Topseed HP eHome Infrared Transceiver */
- { USB_DEVICE(VENDOR_TOPSEED, 0x0006) },
- /* Topseed eHome Infrared Transceiver */
- { USB_DEVICE(VENDOR_TOPSEED, 0x0007) },
- /* Topseed eHome Infrared Transceiver */
- { USB_DEVICE(VENDOR_TOPSEED, 0x0008) },
- /* Ricavision internal Infrared Transceiver */
- { USB_DEVICE(VENDOR_RICAVISION, 0x0010) },
- /* Itron ione Libra Q-11 */
- { USB_DEVICE(VENDOR_ITRON, 0x7002) },
- /* FIC eHome Infrared Transceiver */
- { USB_DEVICE(VENDOR_FIC, 0x9242) },
- /* LG eHome Infrared Transceiver */
- { USB_DEVICE(VENDOR_LG, 0x9803) },
- /* Microsoft MCE Infrared Transceiver */
- { USB_DEVICE(VENDOR_MICROSOFT, 0x00a0) },
- /* Formosa eHome Infrared Transceiver */
- { USB_DEVICE(VENDOR_FORMOSA, 0xe015) },
- /* Formosa21 / eHome Infrared Receiver */
- { USB_DEVICE(VENDOR_FORMOSA, 0xe016) },
- /* Formosa aim / Trust MCE Infrared Receiver */
- { USB_DEVICE(VENDOR_FORMOSA, 0xe017) },
- /* Formosa Industrial Computing / Beanbag Emulation Device */
- { USB_DEVICE(VENDOR_FORMOSA, 0xe018) },
- /* Fintek eHome Infrared Transceiver */
- { USB_DEVICE(VENDOR_FINTEK, 0x0602) },
- /* Fintek eHome Infrared Transceiver (in the AOpen MP45) */
- { USB_DEVICE(VENDOR_FINTEK, 0x0702) },
- /* Pinnacle Remote Kit */
- { USB_DEVICE(VENDOR_PINNACLE, 0x0225) },
- /* Elitegroup Computer Systems IR */
- { USB_DEVICE(VENDOR_ECS, 0x0f38) },
- /* Wistron Corp. eHome Infrared Receiver */
- { USB_DEVICE(VENDOR_WISTRON, 0x0002) },
- /* Compro K100 */
- { USB_DEVICE(VENDOR_COMPRO, 0x3020) },
- /* Northstar Systems eHome Infrared Transceiver */
- { USB_DEVICE(VENDOR_NORTHSTAR, 0xe004) },
- /* Terminating entry */
- { }
-};
-
-static struct usb_device_id pinnacle_list[] = {
- { USB_DEVICE(VENDOR_PINNACLE, 0x0225) },
- {}
-};
-
-static struct usb_device_id transmitter_mask_list[] = {
- { USB_DEVICE(VENDOR_SMK, 0x031d) },
- { USB_DEVICE(VENDOR_SMK, 0x0322) },
- { USB_DEVICE(VENDOR_SMK, 0x0334) },
- { USB_DEVICE(VENDOR_TOPSEED, 0x0001) },
- { USB_DEVICE(VENDOR_TOPSEED, 0x0007) },
- { USB_DEVICE(VENDOR_TOPSEED, 0x0008) },
- { USB_DEVICE(VENDOR_PINNACLE, 0x0225) },
- {}
-};
-
-/* data structure for each usb transceiver */
-struct mceusb2_dev {
-
- /* usb */
- struct usb_device *usbdev;
- struct urb *urb_in;
- int devnum;
- struct usb_endpoint_descriptor *usb_ep_in;
- struct usb_endpoint_descriptor *usb_ep_out;
-
- /* buffers and dma */
- unsigned char *buf_in;
- unsigned int len_in;
- dma_addr_t dma_in;
- dma_addr_t dma_out;
-
- /* lirc */
- struct lirc_driver *d;
- lirc_t lircdata;
- unsigned char is_pulse;
- struct {
- u32 connected:1;
- u32 pinnacle:1;
- u32 transmitter_mask_inverted:1;
- u32 reserved:29;
- } flags;
-
- unsigned char transmitter_mask;
- unsigned int carrier_freq;
-
- /* handle sending (init strings) */
- int send_flags;
- wait_queue_head_t wait_out;
-
- struct mutex lock;
-};
-
-/* init strings */
-static char init1[] = {0x00, 0xff, 0xaa, 0xff, 0x0b};
-static char init2[] = {0xff, 0x18};
-
-static char pin_init1[] = { 0x9f, 0x07};
-static char pin_init2[] = { 0x9f, 0x13};
-static char pin_init3[] = { 0x9f, 0x0d};
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 11)
-static unsigned long usecs_to_jiffies(const unsigned int u)
-{
- if (u > jiffies_to_usecs(MAX_JIFFY_OFFSET))
- return MAX_JIFFY_OFFSET;
-#if HZ <= USEC_PER_SEC && !(USEC_PER_SEC % HZ)
- return (u + (USEC_PER_SEC / HZ) - 1) / (USEC_PER_SEC / HZ);
-#elif HZ > USEC_PER_SEC && !(HZ % USEC_PER_SEC)
- return u * (HZ / USEC_PER_SEC);
-#else
- return (u * HZ + USEC_PER_SEC - 1) / USEC_PER_SEC;
-#endif
-}
-#endif
-static void mceusb_dev_printdata(struct mceusb2_dev *ir, char *buf, int len)
-{
- char codes[USB_BUFLEN*3 + 1];
- int i;
-
- if (len <= 0)
- return;
-
- for (i = 0; i < len && i < USB_BUFLEN; i++)
- snprintf(codes+i*3, 4, "%02x ", buf[i] & 0xFF);
-
- printk(KERN_INFO "" DRIVER_NAME "[%d]: data received %s (length=%d)\n",
- ir->devnum, codes, len);
-}
-
-static void usb_async_callback(struct urb *urb, struct pt_regs *regs)
-{
- struct mceusb2_dev *ir;
- int len;
-
- if (!urb)
- return;
-
- ir = urb->context;
- if (ir) {
- len = urb->actual_length;
-
- dprintk(DRIVER_NAME
- "[%d]: callback called (status=%d len=%d)\n",
- ir->devnum, urb->status, len);
-
- if (debug)
- mceusb_dev_printdata(ir, urb->transfer_buffer, len);
- }
-
-}
-
-
-/* request incoming or send outgoing usb packet - used to initialize remote */
-static void request_packet_async(struct mceusb2_dev *ir,
- struct usb_endpoint_descriptor *ep,
- unsigned char *data, int size, int urb_type)
-{
- int res;
- struct urb *async_urb;
- unsigned char *async_buf;
-
- if (urb_type) {
- async_urb = usb_alloc_urb(0, GFP_KERNEL);
- if (likely(async_urb)) {
- /* alloc buffer */
- async_buf = kmalloc(size, GFP_KERNEL);
- if (async_buf) {
- if (urb_type == PHILUSB_OUTBOUND) {
- /* outbound data */
- usb_fill_int_urb(async_urb, ir->usbdev,
- usb_sndintpipe(ir->usbdev,
- ep->bEndpointAddress),
- async_buf,
- size,
- (usb_complete_t) usb_async_callback,
- ir, ep->bInterval);
-
- memcpy(async_buf, data, size);
- } else {
- /* inbound data */
- usb_fill_int_urb(async_urb, ir->usbdev,
- usb_rcvintpipe(ir->usbdev,
- ep->bEndpointAddress),
- async_buf, size,
- (usb_complete_t) usb_async_callback,
- ir, ep->bInterval);
- }
- async_urb->transfer_flags = URB_ASYNC_UNLINK;
- } else {
- usb_free_urb(async_urb);
- return;
- }
- }
- } else {
- /* standard request */
- async_urb = ir->urb_in;
- ir->send_flags = RECV_FLAG_IN_PROGRESS;
- }
- dprintk(DRIVER_NAME "[%d]: receive request called (size=%#x)\n",
- ir->devnum, size);
-
- async_urb->transfer_buffer_length = size;
- async_urb->dev = ir->usbdev;
-
- res = usb_submit_urb(async_urb, GFP_ATOMIC);
- if (res) {
- dprintk(DRIVER_NAME "[%d]: receive request FAILED! (res=%d)\n",
- ir->devnum, res);
- return;
- }
- dprintk(DRIVER_NAME "[%d]: receive request complete (res=%d)\n",
- ir->devnum, res);
-}
-
-static int unregister_from_lirc(struct mceusb2_dev *ir)
-{
- struct lirc_driver *d = ir->d;
- int devnum;
- int rtn;
-
- devnum = ir->devnum;
- dprintk(DRIVER_NAME "[%d]: unregister from lirc called\n", devnum);
-
- rtn = lirc_unregister_driver(d->minor);
- if (rtn > 0) {
- printk(DRIVER_NAME "[%d]: error in lirc_unregister minor: %d\n"
- "Trying again...\n", devnum, d->minor);
- if (rtn == -EBUSY) {
- printk(DRIVER_NAME
- "[%d]: device is opened, will unregister"
- " on close\n", devnum);
- return -EAGAIN;
- }
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(HZ);
-
- rtn = lirc_unregister_driver(d->minor);
- if (rtn > 0)
- printk(DRIVER_NAME "[%d]: lirc_unregister failed\n",
- devnum);
- }
-
- if (rtn) {
- printk(DRIVER_NAME "[%d]: didn't free resources\n", devnum);
- return -EAGAIN;
- }
-
- printk(DRIVER_NAME "[%d]: usb remote disconnected\n", devnum);
-
- lirc_buffer_free(d->rbuf);
- kfree(d->rbuf);
- kfree(d);
- kfree(ir);
- return 0;
-}
-
-static int set_use_inc(void *data)
-{
- struct mceusb2_dev *ir = data;
-
- if (!ir) {
- printk(DRIVER_NAME "[?]: set_use_inc called with no context\n");
- return -EIO;
- }
- dprintk(DRIVER_NAME "[%d]: set use inc\n", ir->devnum);
-
- MOD_INC_USE_COUNT;
- if (!ir->flags.connected) {
- if (!ir->usbdev)
- return -ENOENT;
- ir->flags.connected = 1;
- }
-
- return 0;
-}
-
-static void set_use_dec(void *data)
-{
- struct mceusb2_dev *ir = data;
-
- if (!ir) {
- printk(DRIVER_NAME "[?]: set_use_dec called with no context\n");
- return;
- }
- dprintk(DRIVER_NAME "[%d]: set use dec\n", ir->devnum);
-
- if (ir->flags.connected) {
- mutex_lock(&ir->lock);
- ir->flags.connected = 0;
- mutex_unlock(&ir->lock);
- }
- MOD_DEC_USE_COUNT;
-}
-
-static void send_packet_to_lirc(struct mceusb2_dev *ir)
-{
- if (ir->lircdata != 0) {
- lirc_buffer_write(ir->d->rbuf,
- (unsigned char *) &ir->lircdata);
- wake_up(&ir->d->rbuf->wait_poll);
- ir->lircdata = 0;
- }
-}
-
-static void mceusb_dev_recv(struct urb *urb, struct pt_regs *regs)
-{
- struct mceusb2_dev *ir;
- int buf_len, packet_len;
- int i, j;
-
- if (!urb)
- return;
-
- ir = urb->context;
- if (!ir) {
- urb->transfer_flags |= URB_ASYNC_UNLINK;
- usb_unlink_urb(urb);
- return;
- }
-
- buf_len = urb->actual_length;
- packet_len = 0;
-
- if (debug)
- mceusb_dev_printdata(ir, urb->transfer_buffer, buf_len);
-
- if (ir->send_flags == RECV_FLAG_IN_PROGRESS) {
- ir->send_flags = SEND_FLAG_COMPLETE;
- dprintk(DRIVER_NAME "[%d]: setup answer received %d bytes\n",
- ir->devnum, buf_len);
- }
-
- switch (urb->status) {
- /* success */
- case 0:
- for (i = 0; i < buf_len; i++) {
- /* decode mce packets of the form (84),AA,BB,CC,DD */
- if (ir->buf_in[i] >= 0x80 && ir->buf_in[i] <= 0x9e) {
- /* data headers */
- /* decode packet data */
- packet_len =
- ir->buf_in[i] & MCE_PACKET_LENGTH_MASK;
- for (j = 1;
- j <= packet_len && (i+j < buf_len);
- j++) {
- /* rising/falling flank */
- if (ir->is_pulse !=
- (ir->buf_in[i + j] &
- MCE_PULSE_BIT)) {
- send_packet_to_lirc(ir);
- ir->is_pulse =
- ir->buf_in[i + j] &
- MCE_PULSE_BIT;
- }
-
- /* accumulate mce pulse/space values */
- ir->lircdata +=
- (ir->buf_in[i + j] &
- MCE_PULSE_MASK)*MCE_TIME_UNIT;
- ir->lircdata |=
- (ir->is_pulse ? PULSE_BIT : 0);
- }
-
- i += packet_len;
- } else if (ir->buf_in[i] == MCE_CONTROL_HEADER) {
- /* status header (0x9F) */
- /*
- * A transmission containing one or
- * more consecutive ir commands always
- * ends with a GAP of 100ms followed by the
- * sequence 0x9F 0x01 0x01 0x9F 0x15
- * 0x00 0x00 0x80
- */
-
- /*
- Uncomment this if the last 100ms
- "infinity"-space should be transmitted
- to lirc directly instead of at the beginning
- of the next transmission. Changes pulse/space order.
-
- if (++i < buf_len && ir->buf_in[i]==0x01)
- send_packet_to_lirc(ir);
-
- */
-
- /* end decode loop */
- i = buf_len;
- }
- }
-
- break;
-
- case -ECONNRESET:
- case -ENOENT:
- case -ESHUTDOWN:
- urb->transfer_flags |= URB_ASYNC_UNLINK;
- usb_unlink_urb(urb);
- return;
-
- case -EPIPE:
- default:
- break;
- }
-
- usb_submit_urb(urb, GFP_ATOMIC);
-}
-
-
-static ssize_t lirc_write(struct file *file, const char *buf,
- size_t n, loff_t *ppos)
-{
- int i, count = 0, cmdcount = 0;
- struct mceusb2_dev *ir = NULL;
- lirc_t wbuf[LIRCBUF_SIZE]; /* Workbuffer with values from lirc */
- unsigned char cmdbuf[MCE_CMDBUF_SIZE]; /* MCE command buffer */
- unsigned long signal_duration = 0; /* Singnal length in us */
- struct timeval start_time, end_time;
-
- do_gettimeofday(&start_time);
-
- /* Retrieve lirc_driver data for the device */
- ir = lirc_get_pdata(file);
- if (!ir || !ir->usb_ep_out)
- return -EFAULT;
-
- if (n % sizeof(lirc_t))
- return -EINVAL;
- count = n / sizeof(lirc_t);
-
- /* Check if command is within limits */
- if (count > LIRCBUF_SIZE || count%2 == 0)
- return -EINVAL;
- if (copy_from_user(wbuf, buf, n))
- return -EFAULT;
-
- /* MCE tx init header */
- cmdbuf[cmdcount++] = MCE_CONTROL_HEADER;
- cmdbuf[cmdcount++] = 0x08;
- cmdbuf[cmdcount++] = ir->transmitter_mask;
-
- /* Generate mce packet data */
- for (i = 0; (i < count) && (cmdcount < MCE_CMDBUF_SIZE); i++) {
- signal_duration += wbuf[i];
- wbuf[i] = wbuf[i] / MCE_TIME_UNIT;
-
- do { /* loop to support long pulses/spaces > 127*50us=6.35ms */
-
- /* Insert mce packet header every 4th entry */
- if ((cmdcount < MCE_CMDBUF_SIZE) &&
- (cmdcount - MCE_TX_HEADER_LENGTH) %
- MCE_CODE_LENGTH == 0)
- cmdbuf[cmdcount++] = MCE_PACKET_HEADER;
-
- /* Insert mce packet data */
- if (cmdcount < MCE_CMDBUF_SIZE)
- cmdbuf[cmdcount++] =
- (wbuf[i] < MCE_PULSE_BIT ?
- wbuf[i] : MCE_MAX_PULSE_LENGTH) |
- (i & 1 ? 0x00 : MCE_PULSE_BIT);
- else
- return -EINVAL;
- } while ((wbuf[i] > MCE_MAX_PULSE_LENGTH) &&
- (wbuf[i] -= MCE_MAX_PULSE_LENGTH));
- }
-
- /* Fix packet length in last header */
- cmdbuf[cmdcount - (cmdcount - MCE_TX_HEADER_LENGTH) % MCE_CODE_LENGTH] =
- 0x80 + (cmdcount - MCE_TX_HEADER_LENGTH) % MCE_CODE_LENGTH - 1;
-
- /* Check if we have room for the empty packet at the end */
- if (cmdcount >= MCE_CMDBUF_SIZE)
- return -EINVAL;
-
- /* All mce commands end with an empty packet (0x80) */
- cmdbuf[cmdcount++] = 0x80;
-
- /* Transmit the command to the mce device */
- request_packet_async(ir, ir->usb_ep_out, cmdbuf,
- cmdcount, PHILUSB_OUTBOUND);
-
- /*
- * The lircd gap calculation expects the write function to
- * wait the time it takes for the ircommand to be sent before
- * it returns.
- */
- do_gettimeofday(&end_time);
- signal_duration -= (end_time.tv_usec - start_time.tv_usec) +
- (end_time.tv_sec - start_time.tv_sec) * 1000000;
-
- /* delay with the closest number of ticks */
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(usecs_to_jiffies(signal_duration));
-
- return n;
-}
-
-static void set_transmitter_mask(struct mceusb2_dev *ir, unsigned int mask)
-{
- if (ir->flags.transmitter_mask_inverted)
- /*
- * The mask begins at 0x02 and has an inverted
- * numbering scheme
- */
- ir->transmitter_mask =
- (mask != 0x03 ? mask ^ 0x03 : mask) << 1;
- else
- ir->transmitter_mask = mask;
-}
-
-
-/* Sets the send carrier frequency */
-static int set_send_carrier(struct mceusb2_dev *ir, int carrier)
-{
- int clk = 10000000;
- int prescaler = 0, divisor = 0;
- unsigned char cmdbuf[] = { 0x9F, 0x06, 0x01, 0x80 };
-
- /* Carrier is changed */
- if (ir->carrier_freq != carrier) {
-
- if (carrier <= 0) {
- ir->carrier_freq = carrier;
- dprintk(DRIVER_NAME "[%d]: SET_CARRIER disabling "
- "carrier modulation\n", ir->devnum);
- request_packet_async(ir, ir->usb_ep_out,
- cmdbuf, sizeof(cmdbuf),
- PHILUSB_OUTBOUND);
- return carrier;
- }
-
- for (prescaler = 0; prescaler < 4; ++prescaler) {
- divisor = (clk >> (2 * prescaler)) / carrier;
- if (divisor <= 0xFF) {
- ir->carrier_freq = carrier;
- cmdbuf[2] = prescaler;
- cmdbuf[3] = divisor;
- dprintk(DRIVER_NAME "[%d]: SET_CARRIER "
- "requesting %d Hz\n",
- ir->devnum, carrier);
-
- /* Transmit new carrier to mce device */
- request_packet_async(ir, ir->usb_ep_out,
- cmdbuf, sizeof(cmdbuf),
- PHILUSB_OUTBOUND);
- return carrier;
- }
- }
-
- return -EINVAL;
-
- }
-
- return carrier;
-}
-
-
-static int lirc_ioctl(struct inode *node, struct file *filep,
- unsigned int cmd, unsigned long arg)
-{
- int result;
- unsigned int ivalue;
- unsigned long lvalue;
- struct mceusb2_dev *ir = NULL;
-
- /* Retrieve lirc_driver data for the device */
- ir = lirc_get_pdata(filep);
- if (!ir || !ir->usb_ep_out)
- return -EFAULT;
-
-
- switch (cmd) {
- case LIRC_SET_TRANSMITTER_MASK:
-
- result = get_user(ivalue, (unsigned int *) arg);
- if (result)
- return result;
- switch (ivalue) {
- case 0x01: /* Transmitter 1 => 0x04 */
- case 0x02: /* Transmitter 2 => 0x02 */
- case 0x03: /* Transmitter 1 & 2 => 0x06 */
- set_transmitter_mask(ir, ivalue);
- break;
-
- default: /* Unsupported transmitter mask */
- return MCE_MAX_CHANNELS;
- }
-
- dprintk(DRIVER_NAME ": SET_TRANSMITTERS mask=%d\n", ivalue);
- break;
-
- case LIRC_GET_SEND_MODE:
-
- result = put_user(LIRC_SEND2MODE(LIRC_CAN_SEND_PULSE &
- LIRC_CAN_SEND_MASK),
- (unsigned long *) arg);
-
- if (result)
- return result;
- break;
-
- case LIRC_SET_SEND_MODE:
-
- result = get_user(lvalue, (unsigned long *) arg);
-
- if (result)
- return result;
- if (lvalue != (LIRC_MODE_PULSE&LIRC_CAN_SEND_MASK))
- return -EINVAL;
- break;
-
- case LIRC_SET_SEND_CARRIER:
-
- result = get_user(ivalue, (unsigned int *) arg);
- if (result)
- return result;
-
- set_send_carrier(ir, ivalue);
- break;
-
- default:
- return -ENOIOCTLCMD;
- }
-
- return 0;
-}
-
-static struct file_operations lirc_fops = {
- .owner = THIS_MODULE,
- .write = lirc_write,
- .ioctl = lirc_ioctl,
-};
-
-
-static int mceusb_dev_probe(struct usb_interface *intf,
- const struct usb_device_id *id)
-{
- struct usb_device *dev = interface_to_usbdev(intf);
- struct usb_host_interface *idesc;
- struct usb_endpoint_descriptor *ep = NULL;
- struct usb_endpoint_descriptor *ep_in = NULL;
- struct usb_endpoint_descriptor *ep_out = NULL;
- struct usb_host_config *config;
- struct mceusb2_dev *ir = NULL;
- struct lirc_driver *driver = NULL;
- struct lirc_buffer *rbuf = NULL;
- int devnum, pipe, maxp;
- int minor = 0;
- int i;
- char buf[63], name[128] = "";
- int mem_failure = 0;
- int is_pinnacle;
-
- dprintk(DRIVER_NAME ": usb probe called\n");
-
- usb_reset_device(dev);
-
- config = dev->actconfig;
-
- idesc = intf->cur_altsetting;
-
- is_pinnacle = usb_match_id(intf, pinnacle_list) ? 1 : 0;
-
- /* step through the endpoints to find first bulk in and out endpoint */
- for (i = 0; i < idesc->desc.bNumEndpoints; ++i) {
- ep = &idesc->endpoint[i].desc;
-
- if ((ep_in == NULL)
- && ((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
- == USB_DIR_IN)
- && (((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
- == USB_ENDPOINT_XFER_BULK)
- || ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
- == USB_ENDPOINT_XFER_INT))) {
-
- dprintk(DRIVER_NAME ": acceptable inbound endpoint "
- "found\n");
- ep_in = ep;
- ep_in->bmAttributes = USB_ENDPOINT_XFER_INT;
- if (is_pinnacle)
- /*
- * setting seems to 1 seem to cause issues with
- * Pinnacle timing out on transfer.
- */
- ep_in->bInterval = ep->bInterval;
- else
- ep_in->bInterval = 1;
- }
-
- if ((ep_out == NULL)
- && ((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
- == USB_DIR_OUT)
- && (((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
- == USB_ENDPOINT_XFER_BULK)
- || ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
- == USB_ENDPOINT_XFER_INT))) {
-
- dprintk(DRIVER_NAME ": acceptable outbound endpoint "
- "found\n");
- ep_out = ep;
- ep_out->bmAttributes = USB_ENDPOINT_XFER_INT;
- if (is_pinnacle)
- /*
- * setting seems to 1 seem to cause issues with
- * Pinnacle timing out on transfer.
- */
- ep_out->bInterval = ep->bInterval;
- else
- ep_out->bInterval = 1;
- }
- }
- if (ep_in == NULL || ep_out == NULL) {
- dprintk(DRIVER_NAME ": inbound and/or "
- "outbound endpoint not found\n");
- return -ENODEV;
- }
-
- devnum = dev->devnum;
- pipe = usb_rcvintpipe(dev, ep_in->bEndpointAddress);
- maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
-
- mem_failure = 0;
- ir = kzalloc(sizeof(struct mceusb2_dev), 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;
- }
-
- rbuf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
- if (!rbuf) {
- mem_failure = 3;
- goto mem_failure_switch;
- }
-
- if (lirc_buffer_init(rbuf, sizeof(lirc_t), LIRCBUF_SIZE)) {
- mem_failure = 4;
- goto mem_failure_switch;
- }
-
- ir->buf_in = usb_buffer_alloc(dev, maxp, GFP_ATOMIC, &ir->dma_in);
- if (!ir->buf_in) {
- mem_failure = 5;
- goto mem_failure_switch;
- }
-
- ir->urb_in = usb_alloc_urb(0, GFP_KERNEL);
- if (!ir->urb_in) {
- mem_failure = 7;
- goto mem_failure_switch;
- }
-
- strcpy(driver->name, DRIVER_NAME " ");
- driver->minor = -1;
- driver->features = LIRC_CAN_SEND_PULSE |
- LIRC_CAN_SET_TRANSMITTER_MASK |
- LIRC_CAN_REC_MODE2 |
- LIRC_CAN_SET_SEND_CARRIER;
- driver->data = ir;
- driver->rbuf = rbuf;
- driver->set_use_inc = &set_use_inc;
- driver->set_use_dec = &set_use_dec;
- driver->code_length = sizeof(lirc_t) * 8;
- driver->fops = &lirc_fops;
- driver->dev = &intf->dev;
- driver->owner = THIS_MODULE;
-
- mutex_init(&ir->lock);
- 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_urb(ir->urb_in);
- case 7:
- usb_buffer_free(dev, maxp, ir->buf_in, ir->dma_in);
- case 5:
- lirc_buffer_free(rbuf);
- case 4:
- kfree(rbuf);
- 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 = maxp;
- ir->flags.connected = 0;
- ir->flags.pinnacle = is_pinnacle;
- ir->flags.transmitter_mask_inverted =
- usb_match_id(intf, transmitter_mask_list) ? 0 : 1;
-
- ir->lircdata = PULSE_MASK;
- ir->is_pulse = 0;
-
- /* ir->flags.transmitter_mask_inverted must be set */
- set_transmitter_mask(ir, MCE_DEFAULT_TX_MASK);
- /* Saving usb interface data for use by the transmitter routine */
- ir->usb_ep_in = ep_in;
- ir->usb_ep_out = ep_out;
-
- 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);
-
- /* inbound data */
- usb_fill_int_urb(ir->urb_in, dev, pipe, ir->buf_in,
- maxp, (usb_complete_t) mceusb_dev_recv, ir, ep_in->bInterval);
- ir->urb_in->transfer_dma = ir->dma_in;
- ir->urb_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
-
- /* initialize device */
- if (ir->flags.pinnacle) {
- int usbret;
-
- /*
- * I have no idea why but this reset seems to be crucial to
- * getting the device to do outbound IO correctly - without
- * this the device seems to hang, ignoring all input - although
- * IR signals are correctly sent from the device, no input is
- * interpreted by the device and the host never does the
- * completion routine
- */
-
- usbret = usb_reset_configuration(dev);
- printk(DRIVER_NAME "[%d]: usb reset config ret %x\n",
- devnum, usbret);
-
- /*
- * its possible we really should wait for a return
- * for each of these...
- */
- request_packet_async(ir, ep_in, NULL, maxp, PHILUSB_INBOUND);
- request_packet_async(ir, ep_out, pin_init1, sizeof(pin_init1),
- PHILUSB_OUTBOUND);
- request_packet_async(ir, ep_in, NULL, maxp, PHILUSB_INBOUND);
- request_packet_async(ir, ep_out, pin_init2, sizeof(pin_init2),
- PHILUSB_OUTBOUND);
- request_packet_async(ir, ep_in, NULL, maxp, PHILUSB_INBOUND);
- request_packet_async(ir, ep_out, pin_init3, sizeof(pin_init3),
- PHILUSB_OUTBOUND);
- /* if we don't issue the correct number of receives
- * (PHILUSB_INBOUND) for each outbound, then the first few ir
- * pulses will be interpreted by the usb_async_callback routine
- * - we should ensure we have the right amount OR less - as the
- * mceusb_dev_recv routine will handle the control packets OK -
- * they start with 0x9f - but the async callback doesn't handle
- * ir pulse packets
- */
- request_packet_async(ir, ep_in, NULL, maxp, 0);
- } else {
- request_packet_async(ir, ep_in, NULL, maxp, PHILUSB_INBOUND);
- request_packet_async(ir, ep_in, NULL, maxp, PHILUSB_INBOUND);
- request_packet_async(ir, ep_out, init1,
- sizeof(init1), PHILUSB_OUTBOUND);
- request_packet_async(ir, ep_in, NULL, maxp, PHILUSB_INBOUND);
- request_packet_async(ir, ep_out, init2,
- sizeof(init2), PHILUSB_OUTBOUND);
- request_packet_async(ir, ep_in, NULL, maxp, 0);
- }
-
- usb_set_intfdata(intf, ir);
-
- return 0;
-}
-
-
-static void mceusb_dev_disconnect(struct usb_interface *intf)
-{
- struct usb_device *dev = interface_to_usbdev(intf);
- struct mceusb2_dev *ir = usb_get_intfdata(intf);
-
- usb_set_intfdata(intf, NULL);
-
- if (!ir || !ir->d)
- return;
-
- ir->usbdev = NULL;
- wake_up_all(&ir->wait_out);
-
- mutex_lock(&ir->lock);
- usb_kill_urb(ir->urb_in);
- usb_free_urb(ir->urb_in);
- usb_buffer_free(dev, ir->len_in, ir->buf_in, ir->dma_in);
- mutex_unlock(&ir->lock);
-
- unregister_from_lirc(ir);
-}
-
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
-static int mceusb_dev_suspend(struct usb_interface *intf, pm_message_t message)
-{
- struct mceusb2_dev *ir = usb_get_intfdata(intf);
- printk(DRIVER_NAME "[%d]: suspend\n", ir->devnum);
- usb_kill_urb(ir->urb_in);
- return 0;
-}
-
-static int mceusb_dev_resume(struct usb_interface *intf)
-{
- struct mceusb2_dev *ir = usb_get_intfdata(intf);
- printk(DRIVER_NAME "[%d]: resume\n", ir->devnum);
- if (usb_submit_urb(ir->urb_in, GFP_ATOMIC))
- return -EIO;
- return 0;
-}
-#endif
-
-static struct usb_driver mceusb_dev_driver = {
- LIRC_THIS_MODULE(.owner = THIS_MODULE)
- .name = DRIVER_NAME,
- .probe = mceusb_dev_probe,
- .disconnect = mceusb_dev_disconnect,
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
- .suspend = mceusb_dev_suspend,
- .resume = mceusb_dev_resume,
-#endif
- .id_table = mceusb_dev_table
-};
-
-static int __init mceusb_dev_init(void)
-{
- int i;
-
- printk(KERN_INFO "\n");
- printk(KERN_INFO DRIVER_NAME ": " DRIVER_DESC " " DRIVER_VERSION "\n");
- printk(KERN_INFO DRIVER_NAME ": " DRIVER_AUTHOR "\n");
- dprintk(DRIVER_NAME ": debug mode enabled\n");
-
- i = usb_register(&mceusb_dev_driver);
- if (i < 0) {
- printk(DRIVER_NAME ": usb register failed, result = %d\n", i);
- return -ENODEV;
- }
-
- return 0;
-}
-
-static void __exit mceusb_dev_exit(void)
-{
- usb_deregister(&mceusb_dev_driver);
-}
-
-module_init(mceusb_dev_init);
-module_exit(mceusb_dev_exit);
-
-MODULE_DESCRIPTION(DRIVER_DESC);
-MODULE_AUTHOR(DRIVER_AUTHOR);
-MODULE_LICENSE("GPL");
-MODULE_DEVICE_TABLE(usb, mceusb_dev_table);
-
-module_param(debug, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "Debug enabled or not");
-
-EXPORT_NO_SYMBOLS;
diff --git a/ubuntu/lirc/lirc_sir/lirc_sir.c b/ubuntu/lirc/lirc_sir/lirc_sir.c
index d7c789a9989..ac91ec7cc4c 100644
--- a/ubuntu/lirc/lirc_sir/lirc_sir.c
+++ b/ubuntu/lirc/lirc_sir/lirc_sir.c
@@ -163,8 +163,16 @@ static unsigned int duty_cycle = 50; /* duty cycle of 50% */
#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;
diff --git a/ubuntu/lirc/lirc_wpc8769l/lirc_wpc8769l.h b/ubuntu/lirc/lirc_wpc8769l/lirc_wpc8769l.h
index 464f3c016c9..d250bdc7a54 100644
--- a/ubuntu/lirc/lirc_wpc8769l/lirc_wpc8769l.h
+++ b/ubuntu/lirc/lirc_wpc8769l/lirc_wpc8769l.h
@@ -1,4 +1,4 @@
-/* $Id: lirc_wpc8769l.h,v 1.4 2009/02/05 20:55:20 lirc Exp $ */
+/* $Id: lirc_wpc8769l.h,v 1.5 2009/06/15 15:11:39 jarodwilson Exp $ */
/****************************************************************************
** lirc_wpc8769l.h ****************************************************
@@ -67,12 +67,12 @@
/* WPC8769L register set definitions. Note that these are all wild guesses.*/
/* Registers for I/O range 1. */
-#define WPC8769L_SELECT_REG 0x03
+#define WPC8769L_SELECT_REG 0x03
/*------------*/
-#define WPC8769L_BANK_00 0x00
+#define WPC8769L_BANK_00 0x00
-#define WPC8769L_DATA_REG 0x00
+#define WPC8769L_DATA_REG 0x00
#define WPC8769L_INTERRUPT_REG 0x01
#define WPC8769L_INTERRUPT_1_MASK 0x01
@@ -83,7 +83,7 @@
#define WPC8769L_DATA_STATUS_MASK_1 0x02
#define WPC8769L_DATA_STATUS_MASK_2 0xd0
-#define WPC8769L_CONFIG_REG 0x04
+#define WPC8769L_CONFIG_REG 0x04
#define WPC8769L_CONFIG_OFF_MASK 0xe0
#define WPC8769L_CONFIG_ON_MASK 0xc0
@@ -94,43 +94,43 @@
#define WPC8769L_TIMEOUT_RESET_MASK 0x20
/*------------*/
-#define WPC8769L_BANK_E0 0xe0
+#define WPC8769L_BANK_E0 0xe0
#define WPC8769L_CONFIG6_REG 0x00
#define WPC8769L_CONFIG6_MASK 0x4b
#define WPC8769L_CONFIG7_REG 0x01
-#define WPC8769L_HARDWARE_ENABLE1_REG 0x02
-#define WPC8769L_HARDWARE_ENABLE1_MASK 0x01
+#define WPC8769L_HARDWARE_ENABLE1_REG 0x02
+#define WPC8769L_HARDWARE_ENABLE1_MASK 0x01
#define WPC8769L_CONFIG5_REG 0x04
#define WPC8769L_CONFIG5_ON_MASK 0x30
-#define WPC8769L_REMAINING_RX_DATA_REG 0x07
+#define WPC8769L_REMAINING_RX_DATA_REG 0x07
/*------------*/
-#define WPC8769L_BANK_E4 0xe4
+#define WPC8769L_BANK_E4 0xe4
-#define WPC8769L_READ_ON_STARTUP_REG 0x00
+#define WPC8769L_READ_ON_STARTUP_REG 0x00
/*------------*/
-#define WPC8769L_BANK_EC 0xec
+#define WPC8769L_BANK_EC 0xec
#define WPC8769L_CONFIG3_REG 0x04
#define WPC8769L_CONFIG3_ON_MASK 0x01
#define WPC8769L_CONFIG3_MASK_1 0x10
/*------------*/
-#define WPC8769L_BANK_F0 0xf0
+#define WPC8769L_BANK_F0 0xf0
-#define WPC8769L_WAKEUP_STATUS_LEG_REG 0x02
-#define WPC8769L_WAKEUP_STATUS_LEG_MASK 0x04
+#define WPC8769L_WAKEUP_STATUS_LEG_REG 0x02
+#define WPC8769L_WAKEUP_STATUS_LEG_MASK 0x04
#define WPC8769L_WAKEUP_STATUS_LEG_MASK_A 0x02
#define WPC8769L_WAKEUP_STATUS_LEG_MASK_B 0x08
/*------------*/
-#define WPC8769L_BANK_F4 0xf4
+#define WPC8769L_BANK_F4 0xf4
#define WPC8769L_CONFIG9_REG 0x01
@@ -157,27 +157,27 @@
#define WPC8769L_CLOCK_ON_MASK 0x01
#define WPC8769L_WAKEUP_CONFIG_REG 0x1a
-#define WPC8769L_WAKEUP_CONFIG_PRE_MASK 0x80
+#define WPC8769L_WAKEUP_CONFIG_PRE_MASK 0x80
#define WPC8769L_MAX_INFO_BITS_BIAS 0x0e
-#define WPC8769L_MAX_INFO_BITS_SHIFT 0x01
+#define WPC8769L_MAX_INFO_BITS_SHIFT 0x01
#define WPC8769L_WAKEUP_CONFIG3_REG 0x13
#define WPC8769L_WAKEUP_CONFIG3_OFF_MASK 0x10
-#define WPC8769L_WAKEUP_CONFIG3_ON_MASK 0x21
-#define WPC8769L_WAKEUP_CONFIG3_A_SHIFT 0x01
-#define WPC8769L_WAKEUP_CONFIG3_A_MASK 0x03
-#define WPC8769L_WAKEUP_CONFIG3_B_SHIFT 0x03
-#define WPC8769L_WAKEUP_CONFIG3_B_MASK 0x01
+#define WPC8769L_WAKEUP_CONFIG3_ON_MASK 0x21
+#define WPC8769L_WAKEUP_CONFIG3_A_SHIFT 0x01
+#define WPC8769L_WAKEUP_CONFIG3_A_MASK 0x03
+#define WPC8769L_WAKEUP_CONFIG3_B_SHIFT 0x03
+#define WPC8769L_WAKEUP_CONFIG3_B_MASK 0x01
#define WPC8769L_WAKEUP_STATUS_REG 0x14
-#define WPC8769L_WAKEUP_WOKE_UP_MASK 0x01
+#define WPC8769L_WAKEUP_WOKE_UP_MASK 0x01
#define WPC8769L_WAKEUP_CONFIGURING_MASK 0x17
#define WPC8769L_WAKEUP_CONFIG2_REG 0x15
#define WPC8769L_WAKEUP_CONFIG2_AND_MASK 0xf9
-#define WPC8769L_WAKEUP_CONFIG2_OR_MASK 0x01
+#define WPC8769L_WAKEUP_CONFIG2_OR_MASK 0x01
-#define WPC8769L_WAKEUP_DATA_PTR_REG 0x18
+#define WPC8769L_WAKEUP_DATA_PTR_REG 0x18
#define WPC8769L_WAKEUP_DATA_BITS 0x20
#define WPC8769L_WAKEUP_DATA_BASE 0x10
#define WPC8769L_WAKEUP_MASK_BASE 0x20