From patchwork Sat Oct 10 04:39:51 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Mario Limonciello X-Patchwork-Id: 35663 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from chlorine.canonical.com (chlorine.canonical.com [91.189.94.204]) by ozlabs.org (Postfix) with ESMTP id B280FB7B69 for ; Sat, 10 Oct 2009 15:40:22 +1100 (EST) Received: from localhost ([127.0.0.1] helo=chlorine.canonical.com) by chlorine.canonical.com with esmtp (Exim 4.60) (envelope-from ) id 1MwTkN-0008Qs-UN; Sat, 10 Oct 2009 05:40:00 +0100 Received: from mail-gx0-f225.google.com ([209.85.217.225]) by chlorine.canonical.com with esmtp (Exim 4.60) (envelope-from ) id 1MwTkG-0008PE-Ed for kernel-team@lists.ubuntu.com; Sat, 10 Oct 2009 05:39:53 +0100 Received: by gxk25 with SMTP id 25so8455157gxk.16 for ; Fri, 09 Oct 2009 21:39:51 -0700 (PDT) MIME-Version: 1.0 Received: by 10.101.95.6 with SMTP id x6mr3897077anl.108.1255149591586; Fri, 09 Oct 2009 21:39:51 -0700 (PDT) Date: Fri, 9 Oct 2009 23:39:51 -0500 X-Google-Sender-Auth: 07153d28ced84d58 Message-ID: <76715d500910092139v38fa9512x8aba4ceed0f3138e@mail.gmail.com> Subject: Update LIRC to 0.8.6 From: Mario Limonciello To: Ubuntu Kernel Team Cc: Jeremy Yoder X-BeenThere: kernel-team@lists.ubuntu.com X-Mailman-Version: 2.1.8 Precedence: list List-Id: Kernel team discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: kernel-team-bounces@lists.ubuntu.com Errors-To: kernel-team-bounces@lists.ubuntu.com Hi: We have an open bug for bumping LIRC to 0.8.6 ( https://bugs.edge.launchpad.net/ubuntu/+source/lirc/+bug/432678). The release team has a tentative ack for it, but this requires a kernel change too. I'm attaching the delta for the kernel change for review. These two pieces need to be put in synchronicity. Thanks, From 8131120cacd0bec6523e5ef58ead5edb16f162e9 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Fri, 9 Oct 2009 22:55:56 -0500 Subject: [PATCH] UBUNTU: Update LIRC to 0.8.6 release OriginalLocation: www.lirc.org BugLink: http://bugs.launchpad.net/bugs/432678 Ignore: no Signed-off-by: Mario Limonciello --- debian.master/config/config.common.ports | 2 +- debian.master/config/config.common.ubuntu | 2 +- ubuntu/LIST | 2 +- ubuntu/lirc/Kconfig | 10 +- ubuntu/lirc/Makefile | 2 +- ubuntu/lirc/lirc.h | 9 +- ubuntu/lirc/lirc_dev/lirc_dev.c | 44 +- ubuntu/lirc/lirc_ene0100/Makefile | 3 + ubuntu/lirc/lirc_ene0100/lirc_ene0100.c | 653 +++++++++ ubuntu/lirc/lirc_ene0100/lirc_ene0100.h | 170 +++ ubuntu/lirc/lirc_i2c/lirc_i2c.c | 254 ++++- ubuntu/lirc/lirc_igorplugusb/lirc_igorplugusb.c | 61 +- ubuntu/lirc/lirc_imon/lirc_imon.c | 1747 +++++++++++++++------- ubuntu/lirc/lirc_it87/lirc_it87.c | 20 +- ubuntu/lirc/lirc_mceusb/lirc_mceusb.c | 1770 ++++++++++++++--------- ubuntu/lirc/lirc_sir/lirc_sir.c | 8 + ubuntu/lirc/lirc_wpc8769l/lirc_wpc8769l.h | 52 +- 17 files changed, 3484 insertions(+), 1325 deletions(-) create mode 100644 ubuntu/lirc/lirc_ene0100/Makefile create mode 100644 ubuntu/lirc/lirc_ene0100/lirc_ene0100.c create mode 100644 ubuntu/lirc/lirc_ene0100/lirc_ene0100.h diff --git a/debian.master/config/config.common.ports b/debian.master/config/config.common.ports index 429bcbd..6ffb81a 100644 --- a/debian.master/config/config.common.ports +++ b/debian.master/config/config.common.ports @@ -1585,6 +1585,7 @@ CONFIG_LIBIPW=m # CONFIG_LIBIPW_DEBUG is not set CONFIG_LIRC_ATIUSB=m CONFIG_LIRC_BT829=m +CONFIG_LIRC_ENE0100=m CONFIG_LIRC_CONFIG_LIRC_WPC8769L=m CONFIG_LIRC_DEV=m # CONFIG_LIRC_GPIO is not set @@ -1594,7 +1595,6 @@ CONFIG_LIRC_IMON=m CONFIG_LIRC_IT87=m CONFIG_LIRC_ITE8709=m CONFIG_LIRC_MCEUSB=m -CONFIG_LIRC_MCEUSB2=m # CONFIG_LIRC_PARALLEL is not set CONFIG_LIRC_SASEM=m CONFIG_LIRC_SERIAL=m diff --git a/debian.master/config/config.common.ubuntu b/debian.master/config/config.common.ubuntu index a43cbab..53f0183 100644 --- a/debian.master/config/config.common.ubuntu +++ b/debian.master/config/config.common.ubuntu @@ -1892,6 +1892,7 @@ CONFIG_LIBIPW_DEBUG=y CONFIG_LINE6_USB=m CONFIG_LIRC_ATIUSB=m CONFIG_LIRC_BT829=m +CONFIG_LIRC_ENE0100=m CONFIG_LIRC_CONFIG_LIRC_WPC8769L=m CONFIG_LIRC_DEV=m # CONFIG_LIRC_GPIO is not set @@ -1901,7 +1902,6 @@ CONFIG_LIRC_IMON=m CONFIG_LIRC_IT87=m CONFIG_LIRC_ITE8709=m CONFIG_LIRC_MCEUSB=m -CONFIG_LIRC_MCEUSB2=m # CONFIG_LIRC_PARALLEL is not set CONFIG_LIRC_SASEM=m CONFIG_LIRC_SERIAL=m diff --git a/ubuntu/LIST b/ubuntu/LIST index 126c711..85b8099 100644 --- a/ubuntu/LIST +++ b/ubuntu/LIST @@ -23,6 +23,7 @@ FSAM7400 LENOVO_SL_LAPTOP LIRC_ATIUSB LIRC_BT829 +LIRC_ENE0100 LIRC_CONFIG_LIRC_WPC8769L LIRC_DEV LIRC_GPIO @@ -32,7 +33,6 @@ LIRC_IMON LIRC_IT87 LIRC_ITE8709 LIRC_MCEUSB -LIRC_MCEUSB2 LIRC_PARALLEL LIRC_SASEM LIRC_SERIAL diff --git a/ubuntu/lirc/Kconfig b/ubuntu/lirc/Kconfig index f709f78..21dcc40 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 ca80518..006a6c8 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 20495e6..7c4d09e 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 -#else -#if defined(__NetBSD__) +#elif defined(_NetBSD_) +#include +#elif defined(_CYGWIN_) +#define __USE_LINUX_IOCTL_DEFS #include #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 4e1e380..7618c0a 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_ene0100/Makefile b/ubuntu/lirc/lirc_ene0100/Makefile new file mode 100644 index 0000000..b5f2e6f --- /dev/null +++ b/ubuntu/lirc/lirc_ene0100/Makefile @@ -0,0 +1,3 @@ +EXTRA_CFLAGS =-DIRCTL_DEV_MAJOR=61 -DLIRC_SERIAL_TRANSMITTER -DLIRC_SERIAL_SOFTCARRIER -I$(src)/.. + +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 0000000..da9519f --- /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 + * + * 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 +#include +#include +#include +#include +#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 0000000..ae19753 --- /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 + * + * 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 baf04fa..5acf079 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 +#endif + #include #include #include @@ -55,6 +61,7 @@ #include #include +#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 4ff2725..6705570 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 " #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 52abbb3..fcaa45f 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 #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 @@ -43,7 +43,13 @@ #include #endif #include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18) +#include +#else +#include +#endif #include +#include #include "../kcompat.h" #include "../lirc.h" @@ -51,44 +57,47 @@ #define MOD_AUTHOR "Venky Raju " -#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 9fc1793..256406b 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 + * Copyright (C) 2001 Hans-Günter Lütke Uphues * * 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 df92dd3..0851769 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 * - * 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 * - * All trademarks property of their respective owners. + * Original lirc_mceusb driver for 1st-gen device: + * Copyright (c) 2003-2004 Dan Conti * - * 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 + * + * Initial transmission support for 1st-gen device added August 2009, + * by Patrick Calhoun + * + * 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 +#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 #include #include #include #include #include +#include #include -#include -#ifdef KERNEL_2_5 #include -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18) #include #else #include #endif -#else -#include -#include -#include -#include -#include -#include -#endif +#include +#include +#include + +#include "../lirc.h" +#include "../kcompat.h" +#include "../lirc_dev/lirc_dev.h" +#define DRIVER_VERSION "1.90" +#define DRIVER_AUTHOR "Daniel Melander , " \ + "Martin Blatter , " \ + "Dan Conti " +#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_sir/lirc_sir.c b/ubuntu/lirc/lirc_sir/lirc_sir.c index d7c789a..ac91ec7 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 464f3c0..d250bdc 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 -- 1.6.3.3