summaryrefslogtreecommitdiffstats
path: root/drivers/media/IR
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/IR')
-rw-r--r--drivers/media/IR/Kconfig43
-rw-r--r--drivers/media/IR/Makefile2
-rw-r--r--drivers/media/IR/ene_ir.c1023
-rw-r--r--drivers/media/IR/ene_ir.h235
-rw-r--r--drivers/media/IR/imon.c20
-rw-r--r--drivers/media/IR/ir-core-priv.h13
-rw-r--r--drivers/media/IR/ir-jvc-decoder.c14
-rw-r--r--drivers/media/IR/ir-keytable.c13
-rw-r--r--drivers/media/IR/ir-lirc-codec.c124
-rw-r--r--drivers/media/IR/ir-nec-decoder.c25
-rw-r--r--drivers/media/IR/ir-raw-event.c159
-rw-r--r--drivers/media/IR/ir-sysfs.c2
-rw-r--r--drivers/media/IR/keymaps/Makefile2
-rw-r--r--drivers/media/IR/keymaps/rc-empty.c44
-rw-r--r--drivers/media/IR/keymaps/rc-rc5-streamzap.c81
-rw-r--r--drivers/media/IR/keymaps/rc-rc6-mce.c2
-rw-r--r--drivers/media/IR/mceusb.c21
-rw-r--r--drivers/media/IR/rc-map.c23
-rw-r--r--drivers/media/IR/streamzap.c741
19 files changed, 2441 insertions, 146 deletions
diff --git a/drivers/media/IR/Kconfig b/drivers/media/IR/Kconfig
index 999a8250b3c..490c57cc4cf 100644
--- a/drivers/media/IR/Kconfig
+++ b/drivers/media/IR/Kconfig
@@ -1,13 +1,22 @@
-config IR_CORE
- tristate
+menuconfig IR_CORE
+ tristate "Infrared remote controller adapters"
depends on INPUT
default INPUT
+ ---help---
+ Enable support for Remote Controllers on Linux. This is
+ needed in order to support several video capture adapters.
+
+ Enable this option if you have a video capture board even
+ if you don't need IR, as otherwise, you may not be able to
+ compile the driver for your adapter.
config VIDEO_IR
tristate
depends on IR_CORE
default IR_CORE
+if IR_CORE
+
config LIRC
tristate
default y
@@ -16,7 +25,7 @@ config LIRC
Enable this option to build the Linux Infrared Remote
Control (LIRC) core device interface driver. The LIRC
interface passes raw IR to and from userspace, where the
- LIRC daemon handles protocol decoding for IR reception ann
+ LIRC daemon handles protocol decoding for IR reception and
encoding for IR transmitting (aka "blasting").
source "drivers/media/IR/keymaps/Kconfig"
@@ -103,3 +112,31 @@ config IR_MCEUSB
To compile this driver as a module, choose M here: the
module will be called mceusb.
+
+config IR_ENE
+ tristate "ENE eHome Receiver/Transciever (pnp id: ENE0100/ENE02xxx)"
+ depends on PNP
+ depends on IR_CORE
+ ---help---
+ Say Y here to enable support for integrated infrared receiver
+ /transciever made by ENE.
+
+ You can see if you have it by looking at lspnp output.
+ Output should include ENE0100 ENE0200 or something similiar.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ene_ir.
+
+config IR_STREAMZAP
+ tristate "Streamzap PC Remote IR Receiver"
+ depends on USB_ARCH_HAS_HCD
+ depends on IR_CORE
+ select USB
+ ---help---
+ Say Y here if you want to use a Streamzap PC Remote
+ Infrared Receiver.
+
+ To compile this driver as a module, choose M here: the
+ module will be called streamzap.
+
+endif #IR_CORE
diff --git a/drivers/media/IR/Makefile b/drivers/media/IR/Makefile
index 2ae4f3abfdb..53676838fe9 100644
--- a/drivers/media/IR/Makefile
+++ b/drivers/media/IR/Makefile
@@ -16,3 +16,5 @@ obj-$(CONFIG_IR_LIRC_CODEC) += ir-lirc-codec.o
# stand-alone IR receivers/transmitters
obj-$(CONFIG_IR_IMON) += imon.o
obj-$(CONFIG_IR_MCEUSB) += mceusb.o
+obj-$(CONFIG_IR_ENE) += ene_ir.o
+obj-$(CONFIG_IR_STREAMZAP) += streamzap.o
diff --git a/drivers/media/IR/ene_ir.c b/drivers/media/IR/ene_ir.c
new file mode 100644
index 00000000000..5447750f5e3
--- /dev/null
+++ b/drivers/media/IR/ene_ir.c
@@ -0,0 +1,1023 @@
+/*
+ * driver for ENE KB3926 B/C/D CIR (pnp id: ENE0XXX)
+ *
+ * Copyright (C) 2010 Maxim Levitsky <maximlevitsky@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pnp.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <media/ir-core.h>
+#include <media/ir-common.h>
+#include "ene_ir.h"
+
+
+static int sample_period = -1;
+static int enable_idle = 1;
+static int input = 1;
+static int debug;
+static int txsim;
+
+static int ene_irq_status(struct ene_device *dev);
+
+/* read a hardware register */
+static u8 ene_hw_read_reg(struct ene_device *dev, u16 reg)
+{
+ u8 retval;
+ outb(reg >> 8, dev->hw_io + ENE_ADDR_HI);
+ outb(reg & 0xFF, dev->hw_io + ENE_ADDR_LO);
+ retval = inb(dev->hw_io + ENE_IO);
+
+ ene_dbg_verbose("reg %04x == %02x", reg, retval);
+ return retval;
+}
+
+/* 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);
+
+ ene_dbg_verbose("reg %04x <- %02x", reg, value);
+}
+
+/* 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);
+
+ ene_dbg_verbose("reg %04x <- %02x (mask=%02x)", reg, value, mask);
+}
+
+/* detect hardware features */
+static int ene_hw_detect(struct ene_device *dev)
+{
+ u8 chip_major, chip_minor;
+ u8 hw_revision, old_ver;
+ u8 tmp;
+ u8 fw_capabilities;
+ int pll_freq;
+
+ 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);
+
+ pll_freq = (ene_hw_read_reg(dev, ENE_PLLFRH) << 4) +
+ (ene_hw_read_reg(dev, ENE_PLLFRL) >> 4);
+
+ if (pll_freq != 1000)
+ dev->rx_period_adjust = 4;
+ else
+ dev->rx_period_adjust = 2;
+
+
+ ene_printk(KERN_NOTICE, "PLL freq = %d\n", pll_freq);
+
+ 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\n");
+ return -ENODEV;
+ }
+
+ if (chip_major == 0x39 && chip_minor == 0x26 && hw_revision == 0xC0) {
+ dev->hw_revision = ENE_HW_C;
+ } 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 might be not complete");
+
+ }
+
+ ene_printk(KERN_DEBUG,
+ "chip is 0x%02x%02x - kbver = 0x%02x, rev = 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);
+ ene_dbg("Firmware capabilities: %02x", fw_capabilities);
+
+ 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 transmit %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) {
+ ene_printk(KERN_WARNING,
+ "Device supports transmitting, but that support is\n");
+ ene_printk(KERN_WARNING,
+ "lightly tested. Please test it and mail\n");
+ ene_printk(KERN_WARNING,
+ "lirc-list@lists.sourceforge.net\n");
+ }
+ return 0;
+}
+
+/* this enables/disables IR input via gpio40*/
+static void ene_enable_gpio40_receive(struct ene_device *dev, int enable)
+{
+ ene_hw_write_reg_mask(dev, ENE_CIR_CONF2, enable ?
+ 0 : ENE_CIR_CONF2_GPIO40DIS,
+ ENE_CIR_CONF2_GPIO40DIS);
+}
+
+/* this enables/disables IR via standard input */
+static void ene_enable_normal_receive(struct ene_device *dev, int enable)
+{
+ ene_hw_write_reg(dev, ENE_CIR_CONF1, enable ? ENE_CIR_CONF1_RX_ON : 0);
+}
+
+/* this enables/disables IR input via unused fan tachtometer input */
+static void ene_enable_fan_receive(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->rx_fan_input_inuse = enable;
+}
+
+
+/* Sense current received carrier */
+static int ene_rx_sense_carrier(struct ene_device *dev)
+{
+ int period = ene_hw_read_reg(dev, ENE_RX_CARRIER);
+ int carrier;
+ ene_dbg("RX: hardware carrier period = %02x", period);
+
+ if (!(period & ENE_RX_CARRIER_VALID))
+ return 0;
+
+ period &= ~ENE_RX_CARRIER_VALID;
+
+ if (!period)
+ return 0;
+
+ carrier = 2000000 / period;
+ ene_dbg("RX: sensed carrier = %d Hz", carrier);
+ return carrier;
+}
+
+/* determine which input to use*/
+static void ene_rx_set_inputs(struct ene_device *dev)
+{
+ int learning_mode = dev->learning_enabled;
+
+ ene_dbg("RX: setup receiver, learning mode = %d", learning_mode);
+
+ ene_enable_normal_receive(dev, 1);
+
+ /* old hardware doesn't support learning mode for sure */
+ if (dev->hw_revision <= ENE_HW_B)
+ return;
+
+ /* receiver not learning capable, still set gpio40 correctly */
+ if (!dev->hw_learning_and_tx_capable) {
+ ene_enable_gpio40_receive(dev, !dev->hw_gpio40_learning);
+ return;
+ }
+
+ /* enable learning mode */
+ if (learning_mode) {
+ ene_enable_gpio40_receive(dev, dev->hw_gpio40_learning);
+
+ /* fan input is not used for learning */
+ if (dev->hw_fan_as_normal_input)
+ ene_enable_fan_receive(dev, 0);
+
+ /* disable learning mode */
+ } else {
+ if (dev->hw_fan_as_normal_input) {
+ ene_enable_fan_receive(dev, 1);
+ ene_enable_normal_receive(dev, 0);
+ } else
+ ene_enable_gpio40_receive(dev,
+ !dev->hw_gpio40_learning);
+ }
+
+ /* set few additional settings for this mode */
+ ene_hw_write_reg_mask(dev, ENE_CIR_CONF1, learning_mode ?
+ ENE_CIR_CONF1_LEARN1 : 0, ENE_CIR_CONF1_LEARN1);
+
+ ene_hw_write_reg_mask(dev, ENE_CIR_CONF2, learning_mode ?
+ ENE_CIR_CONF2_LEARN2 : 0, ENE_CIR_CONF2_LEARN2);
+
+ if (dev->rx_fan_input_inuse) {
+ dev->props->rx_resolution = ENE_SAMPLE_PERIOD_FAN * 1000;
+
+ dev->props->timeout =
+ ENE_FAN_VALUE_MASK * ENE_SAMPLE_PERIOD_FAN * 1000;
+ } else {
+ dev->props->rx_resolution = sample_period * 1000;
+ dev->props->timeout = ENE_MAXGAP * 1000;
+ }
+}
+
+/* Enable the device for receive */
+static void ene_rx_enable(struct ene_device *dev)
+{
+ u8 reg_value;
+
+ 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_rx_set_inputs(dev);
+
+ /* set sampling period */
+ ene_hw_write_reg(dev, ENE_CIR_SAMPLE_PERIOD, sample_period);
+
+ /* ack any pending irqs - just in case */
+ ene_irq_status(dev);
+
+ /* enable firmware bits */
+ ene_hw_write_reg_mask(dev, ENE_FW1,
+ ENE_FW1_ENABLE | ENE_FW1_IRQ,
+ ENE_FW1_ENABLE | ENE_FW1_IRQ);
+
+ /* enter idle mode */
+ ir_raw_event_set_idle(dev->idev, 1);
+ ir_raw_event_reset(dev->idev);
+
+}
+
+/* Disable the device receiver */
+static void ene_rx_disable(struct ene_device *dev)
+{
+ /* disable inputs */
+ ene_enable_normal_receive(dev, 0);
+
+ if (dev->hw_fan_as_normal_input)
+ ene_enable_fan_receive(dev, 0);
+
+ /* disable hardware IRQ and firmware flag */
+ ene_hw_write_reg_mask(dev, ENE_FW1, 0, ENE_FW1_ENABLE | ENE_FW1_IRQ);
+
+ ir_raw_event_set_idle(dev->idev, 1);
+ ir_raw_event_reset(dev->idev);
+}
+
+
+/* prepare transmission */
+static void ene_tx_prepare(struct ene_device *dev)
+{
+ u8 conf1;
+
+ conf1 = ene_hw_read_reg(dev, ENE_CIR_CONF1);
+ dev->saved_conf1 = conf1;
+
+ if (dev->hw_revision == ENE_HW_C)
+ conf1 &= ~ENE_CIR_CONF1_TX_CLEAR;
+
+ /* Enable TX engine */
+ conf1 |= ENE_CIR_CONF1_TX_ON;
+
+ /* Set carrier */
+ if (dev->tx_period) {
+
+ /* NOTE: duty cycle handling is just a guess, it might
+ not be aviable. Default values were tested */
+ int tx_period_in500ns = dev->tx_period * 2;
+
+ int tx_pulse_width_in_500ns =
+ tx_period_in500ns / (100 / dev->tx_duty_cycle);
+
+ if (!tx_pulse_width_in_500ns)
+ tx_pulse_width_in_500ns = 1;
+
+ ene_dbg("TX: pulse distance = %d * 500 ns", tx_period_in500ns);
+ ene_dbg("TX: pulse width = %d * 500 ns",
+ tx_pulse_width_in_500ns);
+
+ ene_hw_write_reg(dev, ENE_TX_PERIOD, ENE_TX_PERIOD_UNKBIT |
+ tx_period_in500ns);
+
+ ene_hw_write_reg(dev, ENE_TX_PERIOD_PULSE,
+ tx_pulse_width_in_500ns);
+
+ conf1 |= ENE_CIR_CONF1_TX_CARR;
+ } else
+ conf1 &= ~ENE_CIR_CONF1_TX_CARR;
+
+ ene_hw_write_reg(dev, ENE_CIR_CONF1, conf1);
+
+}
+
+/* end transmission */
+static void ene_tx_complete(struct ene_device *dev)
+{
+ ene_hw_write_reg(dev, ENE_CIR_CONF1, dev->saved_conf1);
+ dev->tx_buffer = NULL;
+}
+
+/* set transmit mask */
+static void ene_tx_hw_set_transmiter_mask(struct ene_device *dev)
+{
+ u8 txport1 = ene_hw_read_reg(dev, ENE_TX_PORT1) & ~ENE_TX_PORT1_EN;
+ u8 txport2 = ene_hw_read_reg(dev, ENE_TX_PORT2) & ~ENE_TX_PORT2_EN;
+
+ if (dev->transmitter_mask & 0x01)
+ txport1 |= ENE_TX_PORT1_EN;
+
+ if (dev->transmitter_mask & 0x02)
+ txport2 |= ENE_TX_PORT2_EN;
+
+ ene_hw_write_reg(dev, ENE_TX_PORT1, txport1);
+ ene_hw_write_reg(dev, ENE_TX_PORT2, txport2);
+}
+
+/* TX one sample - must be called with dev->hw_lock*/
+static void ene_tx_sample(struct ene_device *dev)
+{
+ u8 raw_tx;
+ u32 sample;
+
+ if (!dev->tx_buffer) {
+ ene_dbg("TX: attempt to transmit NULL buffer");
+ return;
+ }
+
+ /* Grab next TX sample */
+ if (!dev->tx_sample) {
+again:
+ if (dev->tx_pos == dev->tx_len + 1) {
+ if (!dev->tx_done) {
+ ene_dbg("TX: no more data to send");
+ dev->tx_done = 1;
+ goto exit;
+ } else {
+ ene_dbg("TX: last sample sent by hardware");
+ ene_tx_complete(dev);
+ complete(&dev->tx_complete);
+ return;
+ }
+ }
+
+ sample = dev->tx_buffer[dev->tx_pos++];
+ dev->tx_sample_pulse = !dev->tx_sample_pulse;
+
+ ene_dbg("TX: sample %8d (%s)", sample, dev->tx_sample_pulse ?
+ "pulse" : "space");
+
+ dev->tx_sample = DIV_ROUND_CLOSEST(sample, ENE_TX_SMPL_PERIOD);
+
+ /* guard against too short samples */
+ if (!dev->tx_sample)
+ goto again;
+ }
+
+ raw_tx = min(dev->tx_sample , (unsigned int)ENE_TX_SMLP_MASK);
+ dev->tx_sample -= raw_tx;
+
+ if (dev->tx_sample_pulse)
+ raw_tx |= ENE_TX_PULSE_MASK;
+
+ ene_hw_write_reg(dev, ENE_TX_INPUT1 + dev->tx_reg, raw_tx);
+ dev->tx_reg = !dev->tx_reg;
+exit:
+ /* simulate TX done interrupt */
+ if (txsim)
+ mod_timer(&dev->tx_sim_timer, jiffies + HZ / 500);
+}
+
+/* timer to simulate tx done interrupt */
+static void ene_tx_irqsim(unsigned long data)
+{
+ struct ene_device *dev = (struct ene_device *)data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->hw_lock, flags);
+ ene_tx_sample(dev);
+ spin_unlock_irqrestore(&dev->hw_lock, flags);
+}
+
+
+/* read irq status and ack it */
+static int ene_irq_status(struct ene_device *dev)
+{
+ u8 irq_status;
+ u8 fw_flags1, fw_flags2;
+ int cur_rx_pointer;
+ int retval = 0;
+
+ fw_flags2 = ene_hw_read_reg(dev, ENE_FW2);
+ cur_rx_pointer = !!(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);
+ dev->rx_pointer = cur_rx_pointer;
+ 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);
+ retval |= ENE_IRQ_TX;
+ }
+
+ /* Check if this is RX interrupt */
+ if (dev->rx_pointer != cur_rx_pointer) {
+ retval |= ENE_IRQ_RX;
+ dev->rx_pointer = cur_rx_pointer;
+
+ } else if (!(retval & ENE_IRQ_TX)) {
+ ene_dbg("RX: interrupt without change in RX pointer(%d)",
+ dev->rx_pointer);
+ retval |= ENE_IRQ_RX;
+ }
+
+ if ((retval & ENE_IRQ_RX) && (retval & ENE_IRQ_TX))
+ ene_dbg("both RX and TX interrupt at same time");
+
+ return retval;
+}
+
+/* interrupt handler */
+static irqreturn_t ene_isr(int irq, void *data)
+{
+ u16 hw_value;
+ int i, hw_sample;
+ int pulse;
+ int irq_status;
+ unsigned long flags;
+ int carrier = 0;
+ irqreturn_t retval = IRQ_NONE;
+ struct ene_device *dev = (struct ene_device *)data;
+ struct ir_raw_event ev;
+
+
+ spin_lock_irqsave(&dev->hw_lock, flags);
+ irq_status = ene_irq_status(dev);
+
+ if (!irq_status)
+ goto unlock;
+
+ retval = IRQ_HANDLED;
+
+ if (irq_status & ENE_IRQ_TX) {
+
+ if (!dev->hw_learning_and_tx_capable) {
+ ene_dbg("TX interrupt on unsupported device!");
+ goto unlock;
+ }
+ ene_tx_sample(dev);
+ }
+
+ if (!(irq_status & ENE_IRQ_RX))
+ goto unlock;
+
+
+ if (dev->carrier_detect_enabled || debug)
+ carrier = ene_rx_sense_carrier(dev);
+#if 0
+ /* TODO */
+ if (dev->carrier_detect_enabled && carrier)
+ ir_raw_event_report_frequency(dev->idev, carrier);
+#endif
+
+ for (i = 0; i < ENE_SAMPLES_SIZE; i++) {
+ hw_value = ene_hw_read_reg(dev,
+ ENE_SAMPLE_BUFFER + dev->rx_pointer * 4 + i);
+
+ if (dev->rx_fan_input_inuse) {
+ /* read high part of the sample */
+ hw_value |= ene_hw_read_reg(dev,
+ ENE_SAMPLE_BUFFER_FAN +
+ dev->rx_pointer * 4 + i) << 8;
+ pulse = 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 {
+ pulse = !(hw_value & ENE_SAMPLE_SPC_MASK);
+ hw_value &= ENE_SAMPLE_VALUE_MASK;
+ hw_sample = hw_value * sample_period;
+
+ if (dev->rx_period_adjust) {
+ hw_sample *= (100 - dev->rx_period_adjust);
+ hw_sample /= 100;
+ }
+ }
+ /* no more data */
+ if (!(hw_value))
+ break;
+
+ ene_dbg("RX: %d (%s)", hw_sample, pulse ? "pulse" : "space");
+
+
+ ev.duration = hw_sample * 1000;
+ ev.pulse = pulse;
+ ir_raw_event_store_with_filter(dev->idev, &ev);
+ }
+
+ ir_raw_event_handle(dev->idev);
+unlock:
+ spin_unlock_irqrestore(&dev->hw_lock, flags);
+ return retval;
+}
+
+/* Initialize default settings */
+static void ene_setup_settings(struct ene_device *dev)
+{
+ dev->tx_period = 32;
+ dev->tx_duty_cycle = 25; /*%*/
+ dev->transmitter_mask = 3;
+
+ /* Force learning mode if (input == 2), otherwise
+ let user set it with LIRC_SET_REC_CARRIER */
+ dev->learning_enabled =
+ (input == 2 && dev->hw_learning_and_tx_capable);
+
+ dev->rx_pointer = -1;
+
+}
+
+/* outside interface: called on first open*/
+static int ene_open(void *data)
+{
+ struct ene_device *dev = (struct ene_device *)data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->hw_lock, flags);
+ dev->in_use = 1;
+ ene_setup_settings(dev);
+ ene_rx_enable(dev);
+ spin_unlock_irqrestore(&dev->hw_lock, flags);
+ return 0;
+}
+
+/* outside interface: called on device close*/
+static void ene_close(void *data)
+{
+ struct ene_device *dev = (struct ene_device *)data;
+ unsigned long flags;
+ spin_lock_irqsave(&dev->hw_lock, flags);
+
+ ene_rx_disable(dev);
+ dev->in_use = 0;
+ spin_unlock_irqrestore(&dev->hw_lock, flags);
+}
+
+/* outside interface: set transmitter mask */
+static int ene_set_tx_mask(void *data, u32 tx_mask)
+{
+ struct ene_device *dev = (struct ene_device *)data;
+ unsigned long flags;
+ ene_dbg("TX: attempt to set transmitter mask %02x", tx_mask);
+
+ /* invalid txmask */
+ if (!tx_mask || tx_mask & ~0x3) {
+ ene_dbg("TX: invalid mask");
+ /* return count of transmitters */
+ return 2;
+ }
+
+ spin_lock_irqsave(&dev->hw_lock, flags);
+ dev->transmitter_mask = tx_mask;
+ spin_unlock_irqrestore(&dev->hw_lock, flags);
+ return 0;
+}
+
+/* outside interface : set tx carrier */
+static int ene_set_tx_carrier(void *data, u32 carrier)
+{
+ struct ene_device *dev = (struct ene_device *)data;
+ unsigned long flags;
+ u32 period = 1000000 / carrier; /* (1 / freq) (* # usec in 1 sec) */
+
+ ene_dbg("TX: attempt to set tx carrier to %d kHz", carrier);
+
+ if (period && (period > ENE_TX_PERIOD_MAX ||
+ period < ENE_TX_PERIOD_MIN)) {
+
+ ene_dbg("TX: out of range %d-%d carrier, "
+ "falling back to 32 kHz",
+ 1000 / ENE_TX_PERIOD_MIN,
+ 1000 / ENE_TX_PERIOD_MAX);
+
+ period = 32; /* this is just a coincidence!!! */
+ }
+ ene_dbg("TX: set carrier to %d kHz", carrier);
+
+ spin_lock_irqsave(&dev->hw_lock, flags);
+ dev->tx_period = period;
+ spin_unlock_irqrestore(&dev->hw_lock, flags);
+ return 0;
+}
+
+
+/* outside interface: enable learning mode */
+static int ene_set_learning_mode(void *data, int enable)
+{
+ struct ene_device *dev = (struct ene_device *)data;
+ unsigned long flags;
+ if (enable == dev->learning_enabled)
+ return 0;
+
+ spin_lock_irqsave(&dev->hw_lock, flags);
+ dev->learning_enabled = enable;
+ ene_rx_set_inputs(dev);
+ spin_unlock_irqrestore(&dev->hw_lock, flags);
+ return 0;
+}
+
+/* outside interface: set rec carrier */
+static int ene_set_rec_carrier(void *data, u32 min, u32 max)
+{
+ struct ene_device *dev = (struct ene_device *)data;
+ ene_set_learning_mode(dev,
+ max > ENE_NORMAL_RX_HI || min < ENE_NORMAL_RX_LOW);
+ return 0;
+}
+
+/* outside interface: enable or disable idle mode */
+static void ene_rx_set_idle(void *data, int idle)
+{
+ struct ene_device *dev = (struct ene_device *)data;
+ ene_dbg("%sabling idle mode", idle ? "en" : "dis");
+
+ ene_hw_write_reg_mask(dev, ENE_CIR_SAMPLE_PERIOD,
+ (enable_idle && idle) ? 0 : ENE_CIR_SAMPLE_OVERFLOW,
+ ENE_CIR_SAMPLE_OVERFLOW);
+}
+
+
+/* outside interface: transmit */
+static int ene_transmit(void *data, int *buf, u32 n)
+{
+ struct ene_device *dev = (struct ene_device *)data;
+ unsigned long flags;
+
+ dev->tx_buffer = buf;
+ dev->tx_len = n / sizeof(int);
+ dev->tx_pos = 0;
+ dev->tx_reg = 0;
+ dev->tx_done = 0;
+ dev->tx_sample = 0;
+ dev->tx_sample_pulse = 0;
+
+ ene_dbg("TX: %d samples", dev->tx_len);
+
+ spin_lock_irqsave(&dev->hw_lock, flags);
+
+ ene_tx_hw_set_transmiter_mask(dev);
+ ene_tx_prepare(dev);
+
+ /* Transmit first two samples */
+ ene_tx_sample(dev);
+ ene_tx_sample(dev);
+
+ spin_unlock_irqrestore(&dev->hw_lock, flags);
+
+ if (wait_for_completion_timeout(&dev->tx_complete, 2 * HZ) == 0) {
+ ene_dbg("TX: timeout");
+ spin_lock_irqsave(&dev->hw_lock, flags);
+ ene_tx_complete(dev);
+ spin_unlock_irqrestore(&dev->hw_lock, flags);
+ } else
+ ene_dbg("TX: done");
+ return n;
+}
+
+
+/* probe entry */
+static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id)
+{
+ int error = -ENOMEM;
+ struct ir_dev_props *ir_props;
+ struct input_dev *input_dev;
+ struct ene_device *dev;
+
+ /* allocate memory */
+ input_dev = input_allocate_device();
+ ir_props = kzalloc(sizeof(struct ir_dev_props), GFP_KERNEL);
+ dev = kzalloc(sizeof(struct ene_device), GFP_KERNEL);
+
+ if (!input_dev || !ir_props || !dev)
+ goto error;
+
+ /* validate resources */
+ error = -ENODEV;
+
+ if (!pnp_port_valid(pnp_dev, 0) ||
+ pnp_port_len(pnp_dev, 0) < ENE_MAX_IO)
+ goto error;
+
+ if (!pnp_irq_valid(pnp_dev, 0))
+ goto error;
+
+ dev->hw_io = pnp_port_start(pnp_dev, 0);
+ dev->irq = pnp_irq(pnp_dev, 0);
+ spin_lock_init(&dev->hw_lock);
+
+ /* claim the resources */
+ error = -EBUSY;
+ if (!request_region(dev->hw_io, ENE_MAX_IO, ENE_DRIVER_NAME))
+ goto error;
+
+ if (request_irq(dev->irq, ene_isr,
+ IRQF_SHARED, ENE_DRIVER_NAME, (void *)dev))
+ goto error;
+
+ pnp_set_drvdata(pnp_dev, dev);
+ dev->pnp_dev = pnp_dev;
+
+ /* detect hardware version and features */
+ error = ene_hw_detect(dev);
+ if (error)
+ goto error;
+
+ ene_setup_settings(dev);
+
+ if (!dev->hw_learning_and_tx_capable && txsim) {
+ dev->hw_learning_and_tx_capable = 1;
+ setup_timer(&dev->tx_sim_timer, ene_tx_irqsim,
+ (long unsigned int)dev);
+ ene_printk(KERN_WARNING,
+ "Simulation of TX activated\n");
+ }
+
+ ir_props->driver_type = RC_DRIVER_IR_RAW;
+ ir_props->allowed_protos = IR_TYPE_ALL;
+ ir_props->priv = dev;
+ ir_props->open = ene_open;
+ ir_props->close = ene_close;
+ ir_props->min_timeout = ENE_MINGAP * 1000;
+ ir_props->max_timeout = ENE_MAXGAP * 1000;
+ ir_props->timeout = ENE_MAXGAP * 1000;
+
+ if (dev->hw_revision == ENE_HW_B)
+ ir_props->s_idle = ene_rx_set_idle;
+
+
+ dev->props = ir_props;
+ dev->idev = input_dev;
+
+ /* don't allow too short/long sample periods */
+ if (sample_period < 5 || sample_period > 0x7F)
+ sample_period = -1;
+
+ /* choose default sample period */
+ if (sample_period == -1) {
+
+ sample_period = 50;
+
+ /* on revB, hardware idle mode eats first sample
+ if we set too low sample period */
+ if (dev->hw_revision == ENE_HW_B && enable_idle)
+ sample_period = 75;
+ }
+
+ ir_props->rx_resolution = sample_period * 1000;
+
+ if (dev->hw_learning_and_tx_capable) {
+
+ ir_props->s_learning_mode = ene_set_learning_mode;
+
+ if (input == 0)
+ ir_props->s_rx_carrier_range = ene_set_rec_carrier;
+
+ init_completion(&dev->tx_complete);
+ ir_props->tx_ir = ene_transmit;
+ ir_props->s_tx_mask = ene_set_tx_mask;
+ ir_props->s_tx_carrier = ene_set_tx_carrier;
+ ir_props->tx_resolution = ENE_TX_SMPL_PERIOD * 1000;
+ /* ir_props->s_carrier_report = ene_set_carrier_report; */
+ }
+
+
+ device_set_wakeup_capable(&pnp_dev->dev, 1);
+ device_set_wakeup_enable(&pnp_dev->dev, 1);
+
+ if (dev->hw_learning_and_tx_capable)
+ input_dev->name = "ENE eHome Infrared Remote Transceiver";
+ else
+ input_dev->name = "ENE eHome Infrared Remote Receiver";
+
+
+ error = -ENODEV;
+ if (ir_input_register(input_dev, RC_MAP_RC6_MCE, ir_props,
+ ENE_DRIVER_NAME))
+ goto error;
+
+
+ ene_printk(KERN_NOTICE, "driver has been succesfully loaded\n");
+ return 0;
+error:
+ if (dev->irq)
+ free_irq(dev->irq, dev);
+ if (dev->hw_io)
+ release_region(dev->hw_io, ENE_MAX_IO);
+
+ input_free_device(input_dev);
+ kfree(ir_props);
+ kfree(dev);
+ return error;
+}
+
+/* main unload function */
+static void ene_remove(struct pnp_dev *pnp_dev)
+{
+ struct ene_device *dev = pnp_get_drvdata(pnp_dev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->hw_lock, flags);
+ ene_rx_disable(dev);
+ spin_unlock_irqrestore(&dev->hw_lock, flags);
+
+ free_irq(dev->irq, dev);
+ release_region(dev->hw_io, ENE_MAX_IO);
+ ir_input_unregister(dev->idev);
+ kfree(dev->props);
+ kfree(dev);
+}
+
+/* enable wake on IR (wakes on specific button on original remote) */
+static void ene_enable_wake(struct ene_device *dev, int enable)
+{
+ enable = enable && device_may_wakeup(&dev->pnp_dev->dev);
+
+ ene_dbg("wake on IR %s", enable ? "enabled" : "disabled");
+
+ ene_hw_write_reg_mask(dev, ENE_FW1, enable ?
+ ENE_FW1_WAKE : 0, ENE_FW1_WAKE);
+}
+
+#ifdef CONFIG_PM
+static int ene_suspend(struct pnp_dev *pnp_dev, pm_message_t state)
+{
+ struct ene_device *dev = pnp_get_drvdata(pnp_dev);
+ ene_enable_wake(dev, 1);
+ 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_rx_enable(dev);
+
+ ene_enable_wake(dev, 0);
+ return 0;
+}
+#endif
+
+static void ene_shutdown(struct pnp_dev *pnp_dev)
+{
+ struct ene_device *dev = pnp_get_drvdata(pnp_dev);
+ ene_enable_wake(dev, 1);
+}
+
+static const struct pnp_device_id ene_ids[] = {
+ {.id = "ENE0100",},
+ {.id = "ENE0200",},
+ {.id = "ENE0201",},
+ {.id = "ENE0202",},
+ {},
+};
+
+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
+ .shutdown = ene_shutdown,
+};
+
+static int __init ene_init(void)
+{
+ 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 (50 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)"
+ " (KB3926B only)");
+
+module_param(input, bool, S_IRUGO);
+MODULE_PARM_DESC(input, "select which input to use "
+ "0 - auto, 1 - standard, 2 - wideband(KB3926C+)");
+
+module_param(debug, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Enable debug (debug=2 verbose debug output)");
+
+module_param(txsim, bool, S_IRUGO);
+MODULE_PARM_DESC(txsim,
+ "Simulate TX features on unsupported hardware (dangerous)");
+
+MODULE_DEVICE_TABLE(pnp, ene_ids);
+MODULE_DESCRIPTION
+ ("Infrared input driver for KB3926B/KB3926C/KB3926D "
+ "(aka ENE0100/ENE0200/ENE0201) CIR port");
+
+MODULE_AUTHOR("Maxim Levitsky");
+MODULE_LICENSE("GPL");
+
+module_init(ene_init);
+module_exit(ene_exit);
diff --git a/drivers/media/IR/ene_ir.h b/drivers/media/IR/ene_ir.h
new file mode 100644
index 00000000000..54c76af0d03
--- /dev/null
+++ b/drivers/media/IR/ene_ir.h
@@ -0,0 +1,235 @@
+/*
+ * driver for ENE KB3926 B/C/D CIR (also known as ENE0XXX)
+ *
+ * Copyright (C) 2010 Maxim Levitsky <maximlevitsky@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#include <linux/spinlock.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 0x80 /* 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 0x01 /* enable fw processing */
+#define ENE_FW1_TXIRQ 0x02 /* TX interrupt pending */
+#define ENE_FW1_WAKE 0x40 /* enable wake from S3 */
+#define ENE_FW1_IRQ 0x80 /* enable interrupt */
+
+/* second firmware register */
+#define ENE_FW2 0xF8F9
+#define ENE_FW2_BUF_HIGH 0x01 /* which half of the buffer to read */
+#define ENE_FW2_IRQ_CLR 0x04 /* clear this on IRQ */
+#define ENE_FW2_GP40_AS_LEARN 0x08 /* normal input is used as */
+ /* learning input */
+#define ENE_FW2_FAN_AS_NRML_IN 0x40 /* fan is used as normal input */
+#define ENE_FW2_LEARNING 0x80 /* hardware supports learning and TX */
+
+/* transmitter ports */
+#define ENE_TX_PORT2 0xFC01 /* this enables one or both */
+#define ENE_TX_PORT2_EN 0x20 /* TX ports */
+#define ENE_TX_PORT1 0xFC08
+#define ENE_TX_PORT1_EN 0x02
+
+/* 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 0x20 /* IR irq */
+
+/* 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 C,D) */
+#define ENEC_IRQ 0xFE9B /* new irq settings register */
+#define ENEC_IRQ_MASK 0x0F /* irq number mask */
+#define ENEC_IRQ_UNK_EN 0x10 /* always enabled */
+#define ENEC_IRQ_STATUS 0x20 /* irq status and ACK */
+
+/* CIR block settings */
+#define ENE_CIR_CONF1 0xFEC0
+#define ENE_CIR_CONF1_TX_CLEAR 0x01 /* clear that on revC */
+ /* while transmitting */
+#define ENE_CIR_CONF1_RX_ON 0x07 /* normal receiver enabled */
+#define ENE_CIR_CONF1_LEARN1 0x08 /* enabled on learning mode */
+#define ENE_CIR_CONF1_TX_ON 0x30 /* enabled on transmit */
+#define ENE_CIR_CONF1_TX_CARR 0x80 /* send TX carrier or not */
+
+#define ENE_CIR_CONF2 0xFEC1 /* unknown setting = 0 */
+#define ENE_CIR_CONF2_LEARN2 0x10 /* set on enable learning */
+#define ENE_CIR_CONF2_GPIO40DIS 0x20 /* disable input via gpio40 */
+
+#define ENE_CIR_SAMPLE_PERIOD 0xFEC8 /* sample period in us */
+#define ENE_CIR_SAMPLE_OVERFLOW 0x80 /* interrupt on overflows if set */
+
+
+/* Two byte tx buffer */
+#define ENE_TX_INPUT1 0xFEC9
+#define ENE_TX_INPUT2 0xFECA
+#define ENE_TX_PULSE_MASK 0x80 /* Transmitted sample is pulse */
+#define ENE_TX_SMLP_MASK 0x7F
+#define ENE_TX_SMPL_PERIOD 50 /* transmit sample period - fixed */
+
+
+/* Unknown TX setting - TX sample period ??? */
+#define ENE_TX_UNK1 0xFECB /* set to 0x63 */
+
+/* Current received carrier period */
+#define ENE_RX_CARRIER 0xFECC /* RX period (500 ns) */
+#define ENE_RX_CARRIER_VALID 0x80 /* Register content valid */
+
+
+/* TX period (1/carrier) */
+#define ENE_TX_PERIOD 0xFECE /* TX period (500 ns) */
+#define ENE_TX_PERIOD_UNKBIT 0x80 /* This bit set on transmit*/
+#define ENE_TX_PERIOD_PULSE 0xFECF /* TX pulse period (500 ns)*/
+
+/* Hardware versions */
+#define ENE_HW_VERSION 0xFF00 /* hardware revision */
+#define ENE_PLLFRH 0xFF16
+#define ENE_PLLFRL 0xFF17
+
+#define ENE_HW_UNK 0xFF1D
+#define ENE_HW_UNK_CLR 0x04
+#define ENE_HW_VER_MAJOR 0xFF1E /* chip version */
+#define ENE_HW_VER_MINOR 0xFF1F
+#define ENE_HW_VER_OLD 0xFD00
+
+/* Normal/Learning carrier ranges - only valid if we have learning input*/
+/* TODO: test */
+#define ENE_NORMAL_RX_LOW 34
+#define ENE_NORMAL_RX_HI 38
+
+/* Tx carrier range */
+/* Hardware might be able to do more, but this range is enough for
+ all purposes */
+#define ENE_TX_PERIOD_MAX 32 /* corresponds to 29.4 kHz */
+#define ENE_TX_PERIOD_MIN 16 /* corrsponds to 62.5 kHz */
+
+
+
+/* Minimal and maximal gaps */
+
+/* Normal case:
+ Minimal gap is 0x7F * sample period
+ Maximum gap depends on hardware.
+ For KB3926B, it is unlimited, for newer models its around
+ 250000, after which HW stops sending samples, and that is
+ not possible to change */
+
+/* Fan case:
+ Both minimal and maximal gaps are same, and equal to 0xFFF * 0x61
+ And there is nothing to change this setting
+*/
+
+#define ENE_MAXGAP 250000
+#define ENE_MINGAP (127 * sample_period)
+
+/******************************************************************************/
+
+#define ENE_DRIVER_NAME "ene_ir"
+
+#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__)
+
+#define ene_dbg(text, ...) \
+ if (debug) \
+ printk(KERN_DEBUG \
+ ENE_DRIVER_NAME ": " text "\n" , ## __VA_ARGS__)
+
+#define ene_dbg_verbose(text, ...) \
+ if (debug > 1) \
+ printk(KERN_DEBUG \
+ ENE_DRIVER_NAME ": " text "\n" , ## __VA_ARGS__)
+
+
+struct ene_device {
+ struct pnp_dev *pnp_dev;
+ struct input_dev *idev;
+ struct ir_dev_props *props;
+ int in_use;
+
+ /* hw IO settings */
+ unsigned long hw_io;
+ int irq;
+ spinlock_t hw_lock;
+
+ /* HW features */
+ int hw_revision; /* hardware revision */
+ bool hw_learning_and_tx_capable; /* learning capable */
+ bool hw_gpio40_learning; /* gpio40 is learning */
+ bool hw_fan_as_normal_input; /* fan input is used as */
+ /* regular input */
+ /* HW state*/
+ int rx_pointer; /* hw pointer to rx buffer */
+ bool rx_fan_input_inuse; /* is fan input in use for rx*/
+ int tx_reg; /* current reg used for TX */
+ u8 saved_conf1; /* saved FEC0 reg */
+
+ /* TX sample handling */
+ unsigned int tx_sample; /* current sample for TX */
+ bool tx_sample_pulse; /* current sample is pulse */
+
+ /* TX buffer */
+ int *tx_buffer; /* input samples buffer*/
+ int tx_pos; /* position in that bufer */
+ int tx_len; /* current len of tx buffer */
+ int tx_done; /* done transmitting */
+ /* one more sample pending*/
+ struct completion tx_complete; /* TX completion */
+ struct timer_list tx_sim_timer;
+
+ /* TX settings */
+ int tx_period;
+ int tx_duty_cycle;
+ int transmitter_mask;
+
+ /* RX settings */
+ bool learning_enabled; /* learning input enabled */
+ bool carrier_detect_enabled; /* carrier detect enabled */
+ int rx_period_adjust;
+};
diff --git a/drivers/media/IR/imon.c b/drivers/media/IR/imon.c
index 65c125e44e9..c185422ef28 100644
--- a/drivers/media/IR/imon.c
+++ b/drivers/media/IR/imon.c
@@ -87,7 +87,6 @@ static ssize_t lcd_write(struct file *file, const char *buf,
struct imon_context {
struct device *dev;
struct ir_dev_props *props;
- struct ir_input_dev *ir;
/* Newer devices have two interfaces */
struct usb_device *usbdev_intf0;
struct usb_device *usbdev_intf1;
@@ -1656,7 +1655,6 @@ static struct input_dev *imon_init_idev(struct imon_context *ictx)
{
struct input_dev *idev;
struct ir_dev_props *props;
- struct ir_input_dev *ir;
int ret, i;
idev = input_allocate_device();
@@ -1671,12 +1669,6 @@ static struct input_dev *imon_init_idev(struct imon_context *ictx)
goto props_alloc_failed;
}
- ir = kzalloc(sizeof(struct ir_input_dev), GFP_KERNEL);
- if (!ir) {
- dev_err(ictx->dev, "remote ir input dev allocation failed\n");
- goto ir_dev_alloc_failed;
- }
-
snprintf(ictx->name_idev, sizeof(ictx->name_idev),
"iMON Remote (%04x:%04x)", ictx->vendor, ictx->product);
idev->name = ictx->name_idev;
@@ -1706,14 +1698,9 @@ static struct input_dev *imon_init_idev(struct imon_context *ictx)
props->change_protocol = imon_ir_change_protocol;
ictx->props = props;
- ictx->ir = ir;
- memcpy(&ir->dev, ictx->dev, sizeof(struct device));
-
usb_to_input_id(ictx->usbdev_intf0, &idev->id);
idev->dev.parent = ictx->dev;
- input_set_drvdata(idev, ir);
-
ret = ir_input_register(idev, RC_MAP_IMON_PAD, props, MOD_NAME);
if (ret < 0) {
dev_err(ictx->dev, "remote input dev register failed\n");
@@ -1723,8 +1710,6 @@ static struct input_dev *imon_init_idev(struct imon_context *ictx)
return idev;
idev_register_failed:
- kfree(ir);
-ir_dev_alloc_failed:
kfree(props);
props_alloc_failed:
input_free_device(idev);
@@ -1944,7 +1929,6 @@ static struct imon_context *imon_init_intf0(struct usb_interface *intf)
urb_submit_failed:
ir_input_unregister(ictx->idev);
- input_free_device(ictx->idev);
idev_setup_failed:
find_endpoint_failed:
mutex_unlock(&ictx->lock);
@@ -2014,10 +1998,8 @@ static struct imon_context *imon_init_intf1(struct usb_interface *intf,
return ictx;
urb_submit_failed:
- if (ictx->touch) {
+ if (ictx->touch)
input_unregister_device(ictx->touch);
- input_free_device(ictx->touch);
- }
touch_setup_failed:
find_endpoint_failed:
mutex_unlock(&ictx->lock);
diff --git a/drivers/media/IR/ir-core-priv.h b/drivers/media/IR/ir-core-priv.h
index babd52061bc..a85a8c7c905 100644
--- a/drivers/media/IR/ir-core-priv.h
+++ b/drivers/media/IR/ir-core-priv.h
@@ -32,7 +32,7 @@ struct ir_raw_handler {
struct ir_raw_event_ctrl {
struct list_head list; /* to keep track of raw clients */
- struct work_struct rx_work; /* for the rx decoding workqueue */
+ struct task_struct *thread;
struct kfifo kfifo; /* fifo for the pulse/space durations */
ktime_t last_event; /* when last event occurred */
enum raw_event_type last_type; /* last event type */
@@ -41,10 +41,13 @@ struct ir_raw_event_ctrl {
/* raw decoder state follows */
struct ir_raw_event prev_ev;
+ struct ir_raw_event this_ev;
struct nec_dec {
int state;
unsigned count;
u32 bits;
+ bool is_nec_x;
+ bool necx_repeat;
} nec;
struct rc5_dec {
int state;
@@ -76,7 +79,7 @@ struct ir_raw_event_ctrl {
struct lirc_codec {
struct ir_input_dev *ir_dev;
struct lirc_driver *drv;
- int lircdata;
+ int carrier_low;
} lirc;
};
@@ -104,10 +107,9 @@ static inline void decrease_duration(struct ir_raw_event *ev, unsigned duration)
ev->duration -= duration;
}
-#define TO_US(duration) (((duration) + 500) / 1000)
+#define TO_US(duration) DIV_ROUND_CLOSEST((duration), 1000)
#define TO_STR(is_pulse) ((is_pulse) ? "pulse" : "space")
#define IS_RESET(ev) (ev.duration == 0)
-
/*
* Routines from ir-sysfs.c - Meant to be called only internally inside
* ir-core
@@ -126,7 +128,8 @@ int ir_raw_handler_register(struct ir_raw_handler *ir_raw_handler);
void ir_raw_handler_unregister(struct ir_raw_handler *ir_raw_handler);
void ir_raw_init(void);
-
+int ir_rcmap_init(void);
+void ir_rcmap_cleanup(void);
/*
* Decoder initialization code
*
diff --git a/drivers/media/IR/ir-jvc-decoder.c b/drivers/media/IR/ir-jvc-decoder.c
index 8894d8b3604..77a89c4de01 100644
--- a/drivers/media/IR/ir-jvc-decoder.c
+++ b/drivers/media/IR/ir-jvc-decoder.c
@@ -32,6 +32,7 @@ enum jvc_state {
STATE_BIT_SPACE,
STATE_TRAILER_PULSE,
STATE_TRAILER_SPACE,
+ STATE_CHECK_REPEAT,
};
/**
@@ -60,6 +61,7 @@ static int ir_jvc_decode(struct input_dev *input_dev, struct ir_raw_event ev)
IR_dprintk(2, "JVC decode started at state %d (%uus %s)\n",
data->state, TO_US(ev.duration), TO_STR(ev.pulse));
+again:
switch (data->state) {
case STATE_INACTIVE:
@@ -149,8 +151,18 @@ static int ir_jvc_decode(struct input_dev *input_dev, struct ir_raw_event ev)
}
data->count = 0;
- data->state = STATE_BIT_PULSE;
+ data->state = STATE_CHECK_REPEAT;
return 0;
+
+ case STATE_CHECK_REPEAT:
+ if (!ev.pulse)
+ break;
+
+ if (eq_margin(ev.duration, JVC_HEADER_PULSE, JVC_UNIT / 2))
+ data->state = STATE_INACTIVE;
+ else
+ data->state = STATE_BIT_PULSE;
+ goto again;
}
out:
diff --git a/drivers/media/IR/ir-keytable.c b/drivers/media/IR/ir-keytable.c
index 15a0f192d41..7e82a9df726 100644
--- a/drivers/media/IR/ir-keytable.c
+++ b/drivers/media/IR/ir-keytable.c
@@ -339,6 +339,8 @@ void ir_repeat(struct input_dev *dev)
spin_lock_irqsave(&ir->keylock, flags);
+ input_event(dev, EV_MSC, MSC_SCAN, ir->last_scancode);
+
if (!ir->keypressed)
goto out;
@@ -370,6 +372,8 @@ void ir_keydown(struct input_dev *dev, int scancode, u8 toggle)
spin_lock_irqsave(&ir->keylock, flags);
+ input_event(dev, EV_MSC, MSC_SCAN, scancode);
+
/* Repeat event? */
if (ir->keypressed &&
ir->last_scancode == scancode &&
@@ -383,9 +387,11 @@ void ir_keydown(struct input_dev *dev, int scancode, u8 toggle)
ir->last_toggle = toggle;
ir->last_keycode = keycode;
+
if (keycode == KEY_RESERVED)
goto out;
+
/* Register a keypress */
ir->keypressed = true;
IR_dprintk(1, "%s: key down event, key 0x%04x, scancode 0x%04x\n",
@@ -428,7 +434,7 @@ static void ir_close(struct input_dev *input_dev)
*/
int __ir_input_register(struct input_dev *input_dev,
const struct ir_scancode_table *rc_tab,
- const struct ir_dev_props *props,
+ struct ir_dev_props *props,
const char *driver_name)
{
struct ir_input_dev *ir_dev;
@@ -480,6 +486,8 @@ int __ir_input_register(struct input_dev *input_dev,
set_bit(EV_KEY, input_dev->evbit);
set_bit(EV_REP, input_dev->evbit);
+ set_bit(EV_MSC, input_dev->evbit);
+ set_bit(MSC_SCAN, input_dev->mscbit);
if (ir_setkeytable(input_dev, &ir_dev->rc_tab, rc_tab)) {
rc = -ENOMEM;
@@ -499,7 +507,8 @@ int __ir_input_register(struct input_dev *input_dev,
IR_dprintk(1, "Registered input device on %s for %s remote%s.\n",
driver_name, rc_tab->name,
- ir_dev->props->driver_type == RC_DRIVER_IR_RAW ? " in raw mode" : "");
+ (ir_dev->props && ir_dev->props->driver_type == RC_DRIVER_IR_RAW) ?
+ " in raw mode" : "");
return 0;
diff --git a/drivers/media/IR/ir-lirc-codec.c b/drivers/media/IR/ir-lirc-codec.c
index 3ba482d96c4..77b5946413c 100644
--- a/drivers/media/IR/ir-lirc-codec.c
+++ b/drivers/media/IR/ir-lirc-codec.c
@@ -32,6 +32,7 @@
static int ir_lirc_decode(struct input_dev *input_dev, struct ir_raw_event ev)
{
struct ir_input_dev *ir_dev = input_get_drvdata(input_dev);
+ int sample;
if (!(ir_dev->raw->enabled_protocols & IR_TYPE_LIRC))
return 0;
@@ -39,18 +40,20 @@ static int ir_lirc_decode(struct input_dev *input_dev, struct ir_raw_event ev)
if (!ir_dev->raw->lirc.drv || !ir_dev->raw->lirc.drv->rbuf)
return -EINVAL;
+ if (IS_RESET(ev))
+ return 0;
+
IR_dprintk(2, "LIRC data transfer started (%uus %s)\n",
TO_US(ev.duration), TO_STR(ev.pulse));
- ir_dev->raw->lirc.lircdata += ev.duration / 1000;
+ sample = ev.duration / 1000;
if (ev.pulse)
- ir_dev->raw->lirc.lircdata |= PULSE_BIT;
+ sample |= PULSE_BIT;
lirc_buffer_write(ir_dev->raw->lirc.drv->rbuf,
- (unsigned char *) &ir_dev->raw->lirc.lircdata);
+ (unsigned char *) &sample);
wake_up(&ir_dev->raw->lirc.drv->rbuf->wait_poll);
- ir_dev->raw->lirc.lircdata = 0;
return 0;
}
@@ -92,13 +95,14 @@ out:
return ret;
}
-static long ir_lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
+static long ir_lirc_ioctl(struct file *filep, unsigned int cmd,
+ unsigned long __user arg)
{
struct lirc_codec *lirc;
struct ir_input_dev *ir_dev;
int ret = 0;
void *drv_data;
- unsigned long val;
+ unsigned long val = 0;
lirc = lirc_get_pdata(filep);
if (!lirc)
@@ -110,47 +114,106 @@ static long ir_lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long ar
drv_data = ir_dev->props->priv;
- switch (cmd) {
- case LIRC_SET_TRANSMITTER_MASK:
+ if (_IOC_DIR(cmd) & _IOC_WRITE) {
ret = get_user(val, (unsigned long *)arg);
if (ret)
return ret;
+ }
+
+ switch (cmd) {
- if (ir_dev->props && ir_dev->props->s_tx_mask)
+ /* legacy support */
+ case LIRC_GET_SEND_MODE:
+ val = LIRC_CAN_SEND_PULSE & LIRC_CAN_SEND_MASK;
+ break;
+
+ case LIRC_SET_SEND_MODE:
+ if (val != (LIRC_MODE_PULSE & LIRC_CAN_SEND_MASK))
+ return -EINVAL;
+ break;
+
+ /* TX settings */
+ case LIRC_SET_TRANSMITTER_MASK:
+ if (ir_dev->props->s_tx_mask)
ret = ir_dev->props->s_tx_mask(drv_data, (u32)val);
else
return -EINVAL;
break;
case LIRC_SET_SEND_CARRIER:
- ret = get_user(val, (unsigned long *)arg);
- if (ret)
- return ret;
-
- if (ir_dev->props && ir_dev->props->s_tx_carrier)
+ if (ir_dev->props->s_tx_carrier)
ir_dev->props->s_tx_carrier(drv_data, (u32)val);
else
return -EINVAL;
break;
- case LIRC_GET_SEND_MODE:
- val = LIRC_CAN_SEND_PULSE & LIRC_CAN_SEND_MASK;
- ret = put_user(val, (unsigned long *)arg);
+ case LIRC_SET_SEND_DUTY_CYCLE:
+ if (!ir_dev->props->s_tx_duty_cycle)
+ return -ENOSYS;
+
+ if (val <= 0 || val >= 100)
+ return -EINVAL;
+
+ ir_dev->props->s_tx_duty_cycle(ir_dev->props->priv, val);
break;
- case LIRC_SET_SEND_MODE:
- ret = get_user(val, (unsigned long *)arg);
- if (ret)
- return ret;
+ /* RX settings */
+ case LIRC_SET_REC_CARRIER:
+ if (ir_dev->props->s_rx_carrier_range)
+ ret = ir_dev->props->s_rx_carrier_range(
+ ir_dev->props->priv,
+ ir_dev->raw->lirc.carrier_low, val);
+ else
+ return -ENOSYS;
- if (val != (LIRC_MODE_PULSE & LIRC_CAN_SEND_MASK))
+ if (!ret)
+ ir_dev->raw->lirc.carrier_low = 0;
+ break;
+
+ case LIRC_SET_REC_CARRIER_RANGE:
+ if (val >= 0)
+ ir_dev->raw->lirc.carrier_low = val;
+ break;
+
+
+ case LIRC_GET_REC_RESOLUTION:
+ val = ir_dev->props->rx_resolution;
+ break;
+
+ case LIRC_SET_WIDEBAND_RECEIVER:
+ if (ir_dev->props->s_learning_mode)
+ return ir_dev->props->s_learning_mode(
+ ir_dev->props->priv, !!val);
+ else
+ return -ENOSYS;
+
+ /* Generic timeout support */
+ case LIRC_GET_MIN_TIMEOUT:
+ if (!ir_dev->props->max_timeout)
+ return -ENOSYS;
+ val = ir_dev->props->min_timeout / 1000;
+ break;
+
+ case LIRC_GET_MAX_TIMEOUT:
+ if (!ir_dev->props->max_timeout)
+ return -ENOSYS;
+ val = ir_dev->props->max_timeout / 1000;
+ break;
+
+ case LIRC_SET_REC_TIMEOUT:
+ if (val < ir_dev->props->min_timeout ||
+ val > ir_dev->props->max_timeout)
return -EINVAL;
+ ir_dev->props->timeout = val * 1000;
break;
default:
return lirc_dev_fop_ioctl(filep, cmd, arg);
}
+ if (_IOC_DIR(cmd) & _IOC_READ)
+ ret = put_user(val, (unsigned long *)arg);
+
return ret;
}
@@ -196,13 +259,28 @@ static int ir_lirc_register(struct input_dev *input_dev)
features = LIRC_CAN_REC_MODE2;
if (ir_dev->props->tx_ir) {
+
features |= LIRC_CAN_SEND_PULSE;
if (ir_dev->props->s_tx_mask)
features |= LIRC_CAN_SET_TRANSMITTER_MASK;
if (ir_dev->props->s_tx_carrier)
features |= LIRC_CAN_SET_SEND_CARRIER;
+
+ if (ir_dev->props->s_tx_duty_cycle)
+ features |= LIRC_CAN_SET_REC_DUTY_CYCLE;
}
+ if (ir_dev->props->s_rx_carrier_range)
+ features |= LIRC_CAN_SET_REC_CARRIER |
+ LIRC_CAN_SET_REC_CARRIER_RANGE;
+
+ if (ir_dev->props->s_learning_mode)
+ features |= LIRC_CAN_USE_WIDEBAND_RECEIVER;
+
+ if (ir_dev->props->max_timeout)
+ features |= LIRC_CAN_SET_REC_TIMEOUT;
+
+
snprintf(drv->name, sizeof(drv->name), "ir-lirc-codec (%s)",
ir_dev->driver_name);
drv->minor = -1;
@@ -224,8 +302,6 @@ static int ir_lirc_register(struct input_dev *input_dev)
ir_dev->raw->lirc.drv = drv;
ir_dev->raw->lirc.ir_dev = ir_dev;
- ir_dev->raw->lirc.lircdata = PULSE_MASK;
-
return 0;
lirc_register_failed:
diff --git a/drivers/media/IR/ir-nec-decoder.c b/drivers/media/IR/ir-nec-decoder.c
index 52e0f378ae3..d597421d654 100644
--- a/drivers/media/IR/ir-nec-decoder.c
+++ b/drivers/media/IR/ir-nec-decoder.c
@@ -20,12 +20,13 @@
#define NEC_HEADER_PULSE (16 * NEC_UNIT)
#define NECX_HEADER_PULSE (8 * NEC_UNIT) /* Less common NEC variant */
#define NEC_HEADER_SPACE (8 * NEC_UNIT)
-#define NEC_REPEAT_SPACE (8 * NEC_UNIT)
+#define NEC_REPEAT_SPACE (4 * NEC_UNIT)
#define NEC_BIT_PULSE (1 * NEC_UNIT)
#define NEC_BIT_0_SPACE (1 * NEC_UNIT)
#define NEC_BIT_1_SPACE (3 * NEC_UNIT)
#define NEC_TRAILER_PULSE (1 * NEC_UNIT)
#define NEC_TRAILER_SPACE (10 * NEC_UNIT) /* even longer in reality */
+#define NECX_REPEAT_BITS 1
enum nec_state {
STATE_INACTIVE,
@@ -67,8 +68,12 @@ static int ir_nec_decode(struct input_dev *input_dev, struct ir_raw_event ev)
if (!ev.pulse)
break;
- if (!eq_margin(ev.duration, NEC_HEADER_PULSE, NEC_UNIT / 2) &&
- !eq_margin(ev.duration, NECX_HEADER_PULSE, NEC_UNIT / 2))
+ if (eq_margin(ev.duration, NEC_HEADER_PULSE, NEC_UNIT / 2)) {
+ data->is_nec_x = false;
+ data->necx_repeat = false;
+ } else if (eq_margin(ev.duration, NECX_HEADER_PULSE, NEC_UNIT / 2))
+ data->is_nec_x = true;
+ else
break;
data->count = 0;
@@ -105,6 +110,17 @@ static int ir_nec_decode(struct input_dev *input_dev, struct ir_raw_event ev)
if (ev.pulse)
break;
+ if (data->necx_repeat && data->count == NECX_REPEAT_BITS &&
+ geq_margin(ev.duration,
+ NEC_TRAILER_SPACE, NEC_UNIT / 2)) {
+ IR_dprintk(1, "Repeat last key\n");
+ ir_repeat(input_dev);
+ data->state = STATE_INACTIVE;
+ return 0;
+
+ } else if (data->count > NECX_REPEAT_BITS)
+ data->necx_repeat = false;
+
data->bits <<= 1;
if (eq_margin(ev.duration, NEC_BIT_1_SPACE, NEC_UNIT / 2))
data->bits |= 1;
@@ -159,6 +175,9 @@ static int ir_nec_decode(struct input_dev *input_dev, struct ir_raw_event ev)
IR_dprintk(1, "NEC scancode 0x%04x\n", scancode);
}
+ if (data->is_nec_x)
+ data->necx_repeat = true;
+
ir_keydown(input_dev, scancode, 0);
data->state = STATE_INACTIVE;
return 0;
diff --git a/drivers/media/IR/ir-raw-event.c b/drivers/media/IR/ir-raw-event.c
index 6f192ef31db..43094e7eccf 100644
--- a/drivers/media/IR/ir-raw-event.c
+++ b/drivers/media/IR/ir-raw-event.c
@@ -12,9 +12,10 @@
* GNU General Public License for more details.
*/
-#include <linux/workqueue.h>
-#include <linux/spinlock.h>
+#include <linux/kthread.h>
+#include <linux/mutex.h>
#include <linux/sched.h>
+#include <linux/freezer.h>
#include "ir-core-priv.h"
/* Define the max number of pulse/space transitions to buffer */
@@ -24,7 +25,7 @@
static LIST_HEAD(ir_raw_client_list);
/* Used to handle IR raw handler extensions */
-static DEFINE_SPINLOCK(ir_raw_handler_lock);
+static DEFINE_MUTEX(ir_raw_handler_lock);
static LIST_HEAD(ir_raw_handler_list);
static u64 available_protocols;
@@ -33,20 +34,30 @@ static u64 available_protocols;
static struct work_struct wq_load;
#endif
-static void ir_raw_event_work(struct work_struct *work)
+static int ir_raw_event_thread(void *data)
{
struct ir_raw_event ev;
struct ir_raw_handler *handler;
- struct ir_raw_event_ctrl *raw =
- container_of(work, struct ir_raw_event_ctrl, rx_work);
-
- while (kfifo_out(&raw->kfifo, &ev, sizeof(ev)) == sizeof(ev)) {
- spin_lock(&ir_raw_handler_lock);
- list_for_each_entry(handler, &ir_raw_handler_list, list)
- handler->decode(raw->input_dev, ev);
- spin_unlock(&ir_raw_handler_lock);
- raw->prev_ev = ev;
+ struct ir_raw_event_ctrl *raw = (struct ir_raw_event_ctrl *)data;
+
+ while (!kthread_should_stop()) {
+ try_to_freeze();
+
+ mutex_lock(&ir_raw_handler_lock);
+
+ while (kfifo_out(&raw->kfifo, &ev, sizeof(ev)) == sizeof(ev)) {
+ list_for_each_entry(handler, &ir_raw_handler_list, list)
+ handler->decode(raw->input_dev, ev);
+ raw->prev_ev = ev;
+ }
+
+ mutex_unlock(&ir_raw_handler_lock);
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule();
}
+
+ return 0;
}
/**
@@ -66,6 +77,9 @@ int ir_raw_event_store(struct input_dev *input_dev, struct ir_raw_event *ev)
if (!ir->raw)
return -EINVAL;
+ IR_dprintk(2, "sample: (05%dus %s)\n",
+ TO_US(ev->duration), TO_STR(ev->pulse));
+
if (kfifo_in(&ir->raw->kfifo, ev, sizeof(*ev)) != sizeof(*ev))
return -ENOMEM;
@@ -126,6 +140,90 @@ int ir_raw_event_store_edge(struct input_dev *input_dev, enum raw_event_type typ
EXPORT_SYMBOL_GPL(ir_raw_event_store_edge);
/**
+ * ir_raw_event_store_with_filter() - pass next pulse/space to decoders with some processing
+ * @input_dev: the struct input_dev device descriptor
+ * @type: the type of the event that has occurred
+ *
+ * This routine (which may be called from an interrupt context) works
+ * in similiar manner to ir_raw_event_store_edge.
+ * This routine is intended for devices with limited internal buffer
+ * It automerges samples of same type, and handles timeouts
+ */
+int ir_raw_event_store_with_filter(struct input_dev *input_dev,
+ struct ir_raw_event *ev)
+{
+ struct ir_input_dev *ir = input_get_drvdata(input_dev);
+ struct ir_raw_event_ctrl *raw = ir->raw;
+
+ if (!raw || !ir->props)
+ return -EINVAL;
+
+ /* Ignore spaces in idle mode */
+ if (ir->idle && !ev->pulse)
+ return 0;
+ else if (ir->idle)
+ ir_raw_event_set_idle(input_dev, 0);
+
+ if (!raw->this_ev.duration) {
+ raw->this_ev = *ev;
+ } else if (ev->pulse == raw->this_ev.pulse) {
+ raw->this_ev.duration += ev->duration;
+ } else {
+ ir_raw_event_store(input_dev, &raw->this_ev);
+ raw->this_ev = *ev;
+ }
+
+ /* Enter idle mode if nessesary */
+ if (!ev->pulse && ir->props->timeout &&
+ raw->this_ev.duration >= ir->props->timeout)
+ ir_raw_event_set_idle(input_dev, 1);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ir_raw_event_store_with_filter);
+
+void ir_raw_event_set_idle(struct input_dev *input_dev, int idle)
+{
+ struct ir_input_dev *ir = input_get_drvdata(input_dev);
+ struct ir_raw_event_ctrl *raw = ir->raw;
+ ktime_t now;
+ u64 delta;
+
+ if (!ir->props)
+ return;
+
+ if (!ir->raw)
+ goto out;
+
+ if (idle) {
+ IR_dprintk(2, "enter idle mode\n");
+ raw->last_event = ktime_get();
+ } else {
+ IR_dprintk(2, "exit idle mode\n");
+
+ now = ktime_get();
+ delta = ktime_to_ns(ktime_sub(now, ir->raw->last_event));
+
+ WARN_ON(raw->this_ev.pulse);
+
+ raw->this_ev.duration =
+ min(raw->this_ev.duration + delta,
+ (u64)IR_MAX_DURATION);
+
+ ir_raw_event_store(input_dev, &raw->this_ev);
+
+ if (raw->this_ev.duration == IR_MAX_DURATION)
+ ir_raw_event_reset(input_dev);
+
+ raw->this_ev.duration = 0;
+ }
+out:
+ if (ir->props->s_idle)
+ ir->props->s_idle(ir->props->priv, idle);
+ ir->idle = idle;
+}
+EXPORT_SYMBOL_GPL(ir_raw_event_set_idle);
+
+/**
* ir_raw_event_handle() - schedules the decoding of stored ir data
* @input_dev: the struct input_dev device descriptor
*
@@ -138,7 +236,7 @@ void ir_raw_event_handle(struct input_dev *input_dev)
if (!ir->raw)
return;
- schedule_work(&ir->raw->rx_work);
+ wake_up_process(ir->raw->thread);
}
EXPORT_SYMBOL_GPL(ir_raw_event_handle);
@@ -147,9 +245,9 @@ u64
ir_raw_get_allowed_protocols()
{
u64 protocols;
- spin_lock(&ir_raw_handler_lock);
+ mutex_lock(&ir_raw_handler_lock);
protocols = available_protocols;
- spin_unlock(&ir_raw_handler_lock);
+ mutex_unlock(&ir_raw_handler_lock);
return protocols;
}
@@ -167,7 +265,7 @@ int ir_raw_event_register(struct input_dev *input_dev)
return -ENOMEM;
ir->raw->input_dev = input_dev;
- INIT_WORK(&ir->raw->rx_work, ir_raw_event_work);
+
ir->raw->enabled_protocols = ~0;
rc = kfifo_alloc(&ir->raw->kfifo, sizeof(s64) * MAX_IR_EVENT_SIZE,
GFP_KERNEL);
@@ -177,12 +275,21 @@ int ir_raw_event_register(struct input_dev *input_dev)
return rc;
}
- spin_lock(&ir_raw_handler_lock);
+ ir->raw->thread = kthread_run(ir_raw_event_thread, ir->raw,
+ "rc%u", (unsigned int)ir->devno);
+
+ if (IS_ERR(ir->raw->thread)) {
+ kfree(ir->raw);
+ ir->raw = NULL;
+ return PTR_ERR(ir->raw->thread);
+ }
+
+ mutex_lock(&ir_raw_handler_lock);
list_add_tail(&ir->raw->list, &ir_raw_client_list);
list_for_each_entry(handler, &ir_raw_handler_list, list)
if (handler->raw_register)
handler->raw_register(ir->raw->input_dev);
- spin_unlock(&ir_raw_handler_lock);
+ mutex_unlock(&ir_raw_handler_lock);
return 0;
}
@@ -195,14 +302,14 @@ void ir_raw_event_unregister(struct input_dev *input_dev)
if (!ir->raw)
return;
- cancel_work_sync(&ir->raw->rx_work);
+ kthread_stop(ir->raw->thread);
- spin_lock(&ir_raw_handler_lock);
+ mutex_lock(&ir_raw_handler_lock);
list_del(&ir->raw->list);
list_for_each_entry(handler, &ir_raw_handler_list, list)
if (handler->raw_unregister)
handler->raw_unregister(ir->raw->input_dev);
- spin_unlock(&ir_raw_handler_lock);
+ mutex_unlock(&ir_raw_handler_lock);
kfifo_free(&ir->raw->kfifo);
kfree(ir->raw);
@@ -217,13 +324,13 @@ int ir_raw_handler_register(struct ir_raw_handler *ir_raw_handler)
{
struct ir_raw_event_ctrl *raw;
- spin_lock(&ir_raw_handler_lock);
+ mutex_lock(&ir_raw_handler_lock);
list_add_tail(&ir_raw_handler->list, &ir_raw_handler_list);
if (ir_raw_handler->raw_register)
list_for_each_entry(raw, &ir_raw_client_list, list)
ir_raw_handler->raw_register(raw->input_dev);
available_protocols |= ir_raw_handler->protocols;
- spin_unlock(&ir_raw_handler_lock);
+ mutex_unlock(&ir_raw_handler_lock);
return 0;
}
@@ -233,13 +340,13 @@ void ir_raw_handler_unregister(struct ir_raw_handler *ir_raw_handler)
{
struct ir_raw_event_ctrl *raw;
- spin_lock(&ir_raw_handler_lock);
+ mutex_lock(&ir_raw_handler_lock);
list_del(&ir_raw_handler->list);
if (ir_raw_handler->raw_unregister)
list_for_each_entry(raw, &ir_raw_client_list, list)
ir_raw_handler->raw_unregister(raw->input_dev);
available_protocols &= ~ir_raw_handler->protocols;
- spin_unlock(&ir_raw_handler_lock);
+ mutex_unlock(&ir_raw_handler_lock);
}
EXPORT_SYMBOL(ir_raw_handler_unregister);
diff --git a/drivers/media/IR/ir-sysfs.c b/drivers/media/IR/ir-sysfs.c
index 6273047e915..96dafc425c8 100644
--- a/drivers/media/IR/ir-sysfs.c
+++ b/drivers/media/IR/ir-sysfs.c
@@ -325,6 +325,7 @@ static int __init ir_core_init(void)
/* Initialize/load the decoders/keymap code that will be used */
ir_raw_init();
+ ir_rcmap_init();
return 0;
}
@@ -332,6 +333,7 @@ static int __init ir_core_init(void)
static void __exit ir_core_exit(void)
{
class_unregister(&ir_input_class);
+ ir_rcmap_cleanup();
}
module_init(ir_core_init);
diff --git a/drivers/media/IR/keymaps/Makefile b/drivers/media/IR/keymaps/Makefile
index cbee06243b5..950e5d953c6 100644
--- a/drivers/media/IR/keymaps/Makefile
+++ b/drivers/media/IR/keymaps/Makefile
@@ -19,7 +19,6 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \
rc-dm1105-nec.o \
rc-dntv-live-dvb-t.o \
rc-dntv-live-dvbt-pro.o \
- rc-empty.o \
rc-em-terratec.o \
rc-encore-enltv2.o \
rc-encore-enltv.o \
@@ -59,6 +58,7 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \
rc-purpletv.o \
rc-pv951.o \
rc-rc5-hauppauge-new.o \
+ rc-rc5-streamzap.o \
rc-rc5-tv.o \
rc-rc6-mce.o \
rc-real-audio-220-32-keys.o \
diff --git a/drivers/media/IR/keymaps/rc-empty.c b/drivers/media/IR/keymaps/rc-empty.c
deleted file mode 100644
index 3b338d84b47..00000000000
--- a/drivers/media/IR/keymaps/rc-empty.c
+++ /dev/null
@@ -1,44 +0,0 @@
-/* empty.h - Keytable for empty Remote Controller
- *
- * keymap imported from ir-keymaps.c
- *
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- */
-
-#include <media/rc-map.h>
-
-/* empty keytable, can be used as placeholder for not-yet created keytables */
-
-static struct ir_scancode empty[] = {
- { 0x2a, KEY_COFFEE },
-};
-
-static struct rc_keymap empty_map = {
- .map = {
- .scan = empty,
- .size = ARRAY_SIZE(empty),
- .ir_type = IR_TYPE_UNKNOWN, /* Legacy IR type */
- .name = RC_MAP_EMPTY,
- }
-};
-
-static int __init init_rc_map_empty(void)
-{
- return ir_register_map(&empty_map);
-}
-
-static void __exit exit_rc_map_empty(void)
-{
- ir_unregister_map(&empty_map);
-}
-
-module_init(init_rc_map_empty)
-module_exit(exit_rc_map_empty)
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
diff --git a/drivers/media/IR/keymaps/rc-rc5-streamzap.c b/drivers/media/IR/keymaps/rc-rc5-streamzap.c
new file mode 100644
index 00000000000..4c19c58b46d
--- /dev/null
+++ b/drivers/media/IR/keymaps/rc-rc5-streamzap.c
@@ -0,0 +1,81 @@
+/* rc-rc5-streamzap.c - Keytable for Streamzap PC Remote, for use
+ * with the Streamzap PC Remote IR Receiver.
+ *
+ * Copyright (c) 2010 by Jarod Wilson <jarod@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+
+static struct ir_scancode rc5_streamzap[] = {
+/*
+ * FIXME: The Streamzap remote isn't actually true RC-5, it has an extra
+ * bit in it, which presently throws the in-kernel RC-5 decoder for a loop.
+ * We either have to enhance the decoder to support it, add a new decoder,
+ * or just rely on lirc userspace decoding.
+ */
+ { 0x00, KEY_NUMERIC_0 },
+ { 0x01, KEY_NUMERIC_1 },
+ { 0x02, KEY_NUMERIC_2 },
+ { 0x03, KEY_NUMERIC_3 },
+ { 0x04, KEY_NUMERIC_4 },
+ { 0x05, KEY_NUMERIC_5 },
+ { 0x06, KEY_NUMERIC_6 },
+ { 0x07, KEY_NUMERIC_7 },
+ { 0x08, KEY_NUMERIC_8 },
+ { 0x0a, KEY_POWER },
+ { 0x0b, KEY_MUTE },
+ { 0x0c, KEY_CHANNELUP },
+ { 0x0d, KEY_VOLUMEUP },
+ { 0x0e, KEY_CHANNELDOWN },
+ { 0x0f, KEY_VOLUMEDOWN },
+ { 0x10, KEY_UP },
+ { 0x11, KEY_LEFT },
+ { 0x12, KEY_OK },
+ { 0x13, KEY_RIGHT },
+ { 0x14, KEY_DOWN },
+ { 0x15, KEY_MENU },
+ { 0x16, KEY_EXIT },
+ { 0x17, KEY_PLAY },
+ { 0x18, KEY_PAUSE },
+ { 0x19, KEY_STOP },
+ { 0x1a, KEY_BACK },
+ { 0x1b, KEY_FORWARD },
+ { 0x1c, KEY_RECORD },
+ { 0x1d, KEY_REWIND },
+ { 0x1e, KEY_FASTFORWARD },
+ { 0x20, KEY_RED },
+ { 0x21, KEY_GREEN },
+ { 0x22, KEY_YELLOW },
+ { 0x23, KEY_BLUE },
+
+};
+
+static struct rc_keymap rc5_streamzap_map = {
+ .map = {
+ .scan = rc5_streamzap,
+ .size = ARRAY_SIZE(rc5_streamzap),
+ .ir_type = IR_TYPE_RC5,
+ .name = RC_MAP_RC5_STREAMZAP,
+ }
+};
+
+static int __init init_rc_map_rc5_streamzap(void)
+{
+ return ir_register_map(&rc5_streamzap_map);
+}
+
+static void __exit exit_rc_map_rc5_streamzap(void)
+{
+ ir_unregister_map(&rc5_streamzap_map);
+}
+
+module_init(init_rc_map_rc5_streamzap)
+module_exit(exit_rc_map_rc5_streamzap)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jarod Wilson <jarod@redhat.com>");
diff --git a/drivers/media/IR/keymaps/rc-rc6-mce.c b/drivers/media/IR/keymaps/rc-rc6-mce.c
index c6726a8039b..64264f7f838 100644
--- a/drivers/media/IR/keymaps/rc-rc6-mce.c
+++ b/drivers/media/IR/keymaps/rc-rc6-mce.c
@@ -74,6 +74,8 @@ static struct ir_scancode rc6_mce[] = {
{ 0x800f045a, KEY_SUBTITLE }, /* Caption/Teletext */
{ 0x800f044d, KEY_TITLE },
+ { 0x800f044e, KEY_PRINT }, /* Print - HP OEM version of remote */
+
{ 0x800f040c, KEY_POWER },
{ 0x800f040d, KEY_PROG1 }, /* Windows MCE button */
diff --git a/drivers/media/IR/mceusb.c b/drivers/media/IR/mceusb.c
index 78bf7f77a1a..ac6bb2c01a4 100644
--- a/drivers/media/IR/mceusb.c
+++ b/drivers/media/IR/mceusb.c
@@ -228,7 +228,6 @@ static struct usb_device_id std_tx_mask_list[] = {
/* data structure for each usb transceiver */
struct mceusb_dev {
/* ir-core bits */
- struct ir_input_dev *irdev;
struct ir_dev_props *props;
struct ir_raw_event rawir;
@@ -428,7 +427,7 @@ static void mceusb_dev_printdata(struct mceusb_dev *ir, char *buf,
}
}
-static void usb_async_callback(struct urb *urb, struct pt_regs *regs)
+static void mce_async_callback(struct urb *urb, struct pt_regs *regs)
{
struct mceusb_dev *ir;
int len;
@@ -477,7 +476,7 @@ static void mce_request_packet(struct mceusb_dev *ir,
/* 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,
+ async_buf, size, (usb_complete_t)mce_async_callback,
ir, ep->bInterval);
memcpy(async_buf, data, size);
@@ -739,7 +738,7 @@ static void mceusb_dev_recv(struct urb *urb, struct pt_regs *regs)
if (ir->send_flags == RECV_FLAG_IN_PROGRESS) {
ir->send_flags = SEND_FLAG_COMPLETE;
- dev_dbg(&ir->irdev->dev, "setup answer received %d bytes\n",
+ dev_dbg(ir->dev, "setup answer received %d bytes\n",
buf_len);
}
@@ -861,7 +860,6 @@ static struct input_dev *mceusb_init_input_dev(struct mceusb_dev *ir)
{
struct input_dev *idev;
struct ir_dev_props *props;
- struct ir_input_dev *irdev;
struct device *dev = ir->dev;
int ret = -ENODEV;
@@ -878,12 +876,6 @@ static struct input_dev *mceusb_init_input_dev(struct mceusb_dev *ir)
goto props_alloc_failed;
}
- irdev = kzalloc(sizeof(struct ir_input_dev), GFP_KERNEL);
- if (!irdev) {
- dev_err(dev, "remote ir input dev allocation failed\n");
- goto ir_dev_alloc_failed;
- }
-
snprintf(ir->name, sizeof(ir->name), "Media Center Ed. eHome "
"Infrared Remote Transceiver (%04x:%04x)",
le16_to_cpu(ir->usbdev->descriptor.idVendor),
@@ -902,9 +894,6 @@ static struct input_dev *mceusb_init_input_dev(struct mceusb_dev *ir)
props->tx_ir = mceusb_tx_ir;
ir->props = props;
- ir->irdev = irdev;
-
- input_set_drvdata(idev, irdev);
ret = ir_input_register(idev, RC_MAP_RC6_MCE, props, DRIVER_NAME);
if (ret < 0) {
@@ -915,8 +904,6 @@ static struct input_dev *mceusb_init_input_dev(struct mceusb_dev *ir)
return idev;
irdev_failed:
- kfree(irdev);
-ir_dev_alloc_failed:
kfree(props);
props_alloc_failed:
input_free_device(idev);
@@ -932,7 +919,6 @@ static int __devinit mceusb_dev_probe(struct usb_interface *intf,
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;
int pipe, maxp, i;
char buf[63], name[128] = "";
@@ -942,7 +928,6 @@ static int __devinit mceusb_dev_probe(struct usb_interface *intf,
dev_dbg(&intf->dev, ": %s called\n", __func__);
- config = dev->actconfig;
idesc = intf->cur_altsetting;
is_gen3 = usb_match_id(intf, gen3_list) ? 1 : 0;
diff --git a/drivers/media/IR/rc-map.c b/drivers/media/IR/rc-map.c
index 46a8f1524b5..689143f2fff 100644
--- a/drivers/media/IR/rc-map.c
+++ b/drivers/media/IR/rc-map.c
@@ -82,3 +82,26 @@ void ir_unregister_map(struct rc_keymap *map)
}
EXPORT_SYMBOL_GPL(ir_unregister_map);
+
+static struct ir_scancode empty[] = {
+ { 0x2a, KEY_COFFEE },
+};
+
+static struct rc_keymap empty_map = {
+ .map = {
+ .scan = empty,
+ .size = ARRAY_SIZE(empty),
+ .ir_type = IR_TYPE_UNKNOWN, /* Legacy IR type */
+ .name = RC_MAP_EMPTY,
+ }
+};
+
+int ir_rcmap_init(void)
+{
+ return ir_register_map(&empty_map);
+}
+
+void ir_rcmap_cleanup(void)
+{
+ ir_unregister_map(&empty_map);
+}
diff --git a/drivers/media/IR/streamzap.c b/drivers/media/IR/streamzap.c
new file mode 100644
index 00000000000..058e29fd478
--- /dev/null
+++ b/drivers/media/IR/streamzap.c
@@ -0,0 +1,741 @@
+/*
+ * Streamzap Remote Control driver
+ *
+ * Copyright (c) 2005 Christoph Bartelmus <lirc@bartelmus.de>
+ * Copyright (c) 2010 Jarod Wilson <jarod@wilsonet.com>
+ *
+ * This driver was based on the work of Greg Wickham and Adrian
+ * Dewhurst. It was substantially rewritten to support correct signal
+ * gaps and now maintains a delay buffer, which is used to present
+ * consistent timing behaviour to user space applications. Without the
+ * delay buffer an ugly hack would be required in lircd, which can
+ * cause sluggish signal decoding in certain situations.
+ *
+ * Ported to in-kernel ir-core interface by Jarod Wilson
+ *
+ * This driver is based on the USB skeleton driver packaged with the
+ * kernel; copyright (C) 2001-2003 Greg Kroah-Hartman (greg@kroah.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/input.h>
+#include <media/ir-core.h>
+
+#define DRIVER_VERSION "1.60"
+#define DRIVER_NAME "streamzap"
+#define DRIVER_DESC "Streamzap Remote Control driver"
+
+#ifdef CONFIG_USB_DEBUG
+static int debug = 1;
+#else
+static int debug;
+#endif
+
+#define USB_STREAMZAP_VENDOR_ID 0x0e9c
+#define USB_STREAMZAP_PRODUCT_ID 0x0000
+
+/* table of devices that work with this driver */
+static struct usb_device_id streamzap_table[] = {
+ /* Streamzap Remote Control */
+ { USB_DEVICE(USB_STREAMZAP_VENDOR_ID, USB_STREAMZAP_PRODUCT_ID) },
+ /* Terminating entry */
+ { }
+};
+
+MODULE_DEVICE_TABLE(usb, streamzap_table);
+
+#define STREAMZAP_PULSE_MASK 0xf0
+#define STREAMZAP_SPACE_MASK 0x0f
+#define STREAMZAP_TIMEOUT 0xff
+#define STREAMZAP_RESOLUTION 256
+
+/* number of samples buffered */
+#define SZ_BUF_LEN 128
+
+enum StreamzapDecoderState {
+ PulseSpace,
+ FullPulse,
+ FullSpace,
+ IgnorePulse
+};
+
+/* structure to hold our device specific stuff */
+struct streamzap_ir {
+
+ /* ir-core */
+ struct ir_dev_props *props;
+ struct ir_raw_event rawir;
+
+ /* core device info */
+ struct device *dev;
+ struct input_dev *idev;
+
+ /* usb */
+ struct usb_device *usbdev;
+ struct usb_interface *interface;
+ struct usb_endpoint_descriptor *endpoint;
+ struct urb *urb_in;
+
+ /* buffer & dma */
+ unsigned char *buf_in;
+ dma_addr_t dma_in;
+ unsigned int buf_in_len;
+
+ /* timer used to support delay buffering */
+ struct timer_list delay_timer;
+ bool timer_running;
+ spinlock_t timer_lock;
+ struct timer_list flush_timer;
+ bool flush;
+
+ /* delay buffer */
+ struct kfifo fifo;
+ bool fifo_initialized;
+
+ /* track what state we're in */
+ enum StreamzapDecoderState decoder_state;
+ /* tracks whether we are currently receiving some signal */
+ bool idle;
+ /* sum of signal lengths received since signal start */
+ unsigned long sum;
+ /* start time of signal; necessary for gap tracking */
+ struct timeval signal_last;
+ struct timeval signal_start;
+ /* bool timeout_enabled; */
+
+ char name[128];
+ char phys[64];
+};
+
+
+/* local function prototypes */
+static int streamzap_probe(struct usb_interface *interface,
+ const struct usb_device_id *id);
+static void streamzap_disconnect(struct usb_interface *interface);
+static void streamzap_callback(struct urb *urb);
+static int streamzap_suspend(struct usb_interface *intf, pm_message_t message);
+static int streamzap_resume(struct usb_interface *intf);
+
+/* usb specific object needed to register this driver with the usb subsystem */
+static struct usb_driver streamzap_driver = {
+ .name = DRIVER_NAME,
+ .probe = streamzap_probe,
+ .disconnect = streamzap_disconnect,
+ .suspend = streamzap_suspend,
+ .resume = streamzap_resume,
+ .id_table = streamzap_table,
+};
+
+static void streamzap_stop_timer(struct streamzap_ir *sz)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&sz->timer_lock, flags);
+ if (sz->timer_running) {
+ sz->timer_running = false;
+ spin_unlock_irqrestore(&sz->timer_lock, flags);
+ del_timer_sync(&sz->delay_timer);
+ } else {
+ spin_unlock_irqrestore(&sz->timer_lock, flags);
+ }
+}
+
+static void streamzap_flush_timeout(unsigned long arg)
+{
+ struct streamzap_ir *sz = (struct streamzap_ir *)arg;
+
+ dev_info(sz->dev, "%s: callback firing\n", __func__);
+
+ /* finally start accepting data */
+ sz->flush = false;
+}
+
+static void streamzap_delay_timeout(unsigned long arg)
+{
+ struct streamzap_ir *sz = (struct streamzap_ir *)arg;
+ struct ir_raw_event rawir = { .pulse = false, .duration = 0 };
+ unsigned long flags;
+ int len, ret;
+ static unsigned long delay;
+ bool wake = false;
+
+ /* deliver data every 10 ms */
+ delay = msecs_to_jiffies(10);
+
+ spin_lock_irqsave(&sz->timer_lock, flags);
+
+ if (kfifo_len(&sz->fifo) > 0) {
+ ret = kfifo_out(&sz->fifo, &rawir, sizeof(rawir));
+ if (ret != sizeof(rawir))
+ dev_err(sz->dev, "Problem w/kfifo_out...\n");
+ ir_raw_event_store(sz->idev, &rawir);
+ wake = true;
+ }
+
+ len = kfifo_len(&sz->fifo);
+ if (len > 0) {
+ while ((len < SZ_BUF_LEN / 2) &&
+ (len < SZ_BUF_LEN * sizeof(int))) {
+ ret = kfifo_out(&sz->fifo, &rawir, sizeof(rawir));
+ if (ret != sizeof(rawir))
+ dev_err(sz->dev, "Problem w/kfifo_out...\n");
+ ir_raw_event_store(sz->idev, &rawir);
+ wake = true;
+ len = kfifo_len(&sz->fifo);
+ }
+ if (sz->timer_running)
+ mod_timer(&sz->delay_timer, jiffies + delay);
+
+ } else {
+ sz->timer_running = false;
+ }
+
+ if (wake)
+ ir_raw_event_handle(sz->idev);
+
+ spin_unlock_irqrestore(&sz->timer_lock, flags);
+}
+
+static void streamzap_flush_delay_buffer(struct streamzap_ir *sz)
+{
+ struct ir_raw_event rawir = { .pulse = false, .duration = 0 };
+ bool wake = false;
+ int ret;
+
+ while (kfifo_len(&sz->fifo) > 0) {
+ ret = kfifo_out(&sz->fifo, &rawir, sizeof(rawir));
+ if (ret != sizeof(rawir))
+ dev_err(sz->dev, "Problem w/kfifo_out...\n");
+ ir_raw_event_store(sz->idev, &rawir);
+ wake = true;
+ }
+
+ if (wake)
+ ir_raw_event_handle(sz->idev);
+}
+
+static void sz_push(struct streamzap_ir *sz)
+{
+ struct ir_raw_event rawir = { .pulse = false, .duration = 0 };
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&sz->timer_lock, flags);
+ if (kfifo_len(&sz->fifo) >= sizeof(int) * SZ_BUF_LEN) {
+ ret = kfifo_out(&sz->fifo, &rawir, sizeof(rawir));
+ if (ret != sizeof(rawir))
+ dev_err(sz->dev, "Problem w/kfifo_out...\n");
+ ir_raw_event_store(sz->idev, &rawir);
+ }
+
+ kfifo_in(&sz->fifo, &sz->rawir, sizeof(rawir));
+
+ if (!sz->timer_running) {
+ sz->delay_timer.expires = jiffies + (HZ / 10);
+ add_timer(&sz->delay_timer);
+ sz->timer_running = true;
+ }
+
+ spin_unlock_irqrestore(&sz->timer_lock, flags);
+}
+
+static void sz_push_full_pulse(struct streamzap_ir *sz,
+ unsigned char value)
+{
+ if (sz->idle) {
+ long deltv;
+
+ sz->signal_last = sz->signal_start;
+ do_gettimeofday(&sz->signal_start);
+
+ deltv = sz->signal_start.tv_sec - sz->signal_last.tv_sec;
+ sz->rawir.pulse = false;
+ if (deltv > 15) {
+ /* really long time */
+ sz->rawir.duration = IR_MAX_DURATION;
+ } else {
+ sz->rawir.duration = (int)(deltv * 1000000 +
+ sz->signal_start.tv_usec -
+ sz->signal_last.tv_usec);
+ sz->rawir.duration -= sz->sum;
+ sz->rawir.duration *= 1000;
+ sz->rawir.duration &= IR_MAX_DURATION;
+ }
+ dev_dbg(sz->dev, "ls %u\n", sz->rawir.duration);
+ sz_push(sz);
+
+ sz->idle = 0;
+ sz->sum = 0;
+ }
+
+ sz->rawir.pulse = true;
+ sz->rawir.duration = ((int) value) * STREAMZAP_RESOLUTION;
+ sz->rawir.duration += STREAMZAP_RESOLUTION / 2;
+ sz->sum += sz->rawir.duration;
+ sz->rawir.duration *= 1000;
+ sz->rawir.duration &= IR_MAX_DURATION;
+ dev_dbg(sz->dev, "p %u\n", sz->rawir.duration);
+ sz_push(sz);
+}
+
+static void sz_push_half_pulse(struct streamzap_ir *sz,
+ unsigned char value)
+{
+ sz_push_full_pulse(sz, (value & STREAMZAP_PULSE_MASK) >> 4);
+}
+
+static void sz_push_full_space(struct streamzap_ir *sz,
+ unsigned char value)
+{
+ sz->rawir.pulse = false;
+ sz->rawir.duration = ((int) value) * STREAMZAP_RESOLUTION;
+ sz->rawir.duration += STREAMZAP_RESOLUTION / 2;
+ sz->sum += sz->rawir.duration;
+ sz->rawir.duration *= 1000;
+ dev_dbg(sz->dev, "s %u\n", sz->rawir.duration);
+ sz_push(sz);
+}
+
+static void sz_push_half_space(struct streamzap_ir *sz,
+ unsigned long value)
+{
+ sz_push_full_space(sz, value & STREAMZAP_SPACE_MASK);
+}
+
+/**
+ * streamzap_callback - usb IRQ handler callback
+ *
+ * This procedure is invoked on reception of data from
+ * the usb remote.
+ */
+static void streamzap_callback(struct urb *urb)
+{
+ struct streamzap_ir *sz;
+ unsigned int i;
+ int len;
+ #if 0
+ static int timeout = (((STREAMZAP_TIMEOUT * STREAMZAP_RESOLUTION) &
+ IR_MAX_DURATION) | 0x03000000);
+ #endif
+
+ if (!urb)
+ return;
+
+ sz = urb->context;
+ len = urb->actual_length;
+
+ switch (urb->status) {
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ /*
+ * this urb is terminated, clean up.
+ * sz might already be invalid at this point
+ */
+ dev_err(sz->dev, "urb terminated, status: %d\n", urb->status);
+ return;
+ default:
+ break;
+ }
+
+ dev_dbg(sz->dev, "%s: received urb, len %d\n", __func__, len);
+ if (!sz->flush) {
+ for (i = 0; i < urb->actual_length; i++) {
+ dev_dbg(sz->dev, "%d: %x\n", i,
+ (unsigned char)sz->buf_in[i]);
+ switch (sz->decoder_state) {
+ case PulseSpace:
+ if ((sz->buf_in[i] & STREAMZAP_PULSE_MASK) ==
+ STREAMZAP_PULSE_MASK) {
+ sz->decoder_state = FullPulse;
+ continue;
+ } else if ((sz->buf_in[i] & STREAMZAP_SPACE_MASK)
+ == STREAMZAP_SPACE_MASK) {
+ sz_push_half_pulse(sz, sz->buf_in[i]);
+ sz->decoder_state = FullSpace;
+ continue;
+ } else {
+ sz_push_half_pulse(sz, sz->buf_in[i]);
+ sz_push_half_space(sz, sz->buf_in[i]);
+ }
+ break;
+ case FullPulse:
+ sz_push_full_pulse(sz, sz->buf_in[i]);
+ sz->decoder_state = IgnorePulse;
+ break;
+ case FullSpace:
+ if (sz->buf_in[i] == STREAMZAP_TIMEOUT) {
+ sz->idle = 1;
+ streamzap_stop_timer(sz);
+ #if 0
+ if (sz->timeout_enabled) {
+ sz->rawir.pulse = false;
+ sz->rawir.duration = timeout;
+ sz->rawir.duration *= 1000;
+ sz_push(sz);
+ }
+ #endif
+ streamzap_flush_delay_buffer(sz);
+ } else
+ sz_push_full_space(sz, sz->buf_in[i]);
+ sz->decoder_state = PulseSpace;
+ break;
+ case IgnorePulse:
+ if ((sz->buf_in[i]&STREAMZAP_SPACE_MASK) ==
+ STREAMZAP_SPACE_MASK) {
+ sz->decoder_state = FullSpace;
+ continue;
+ }
+ sz_push_half_space(sz, sz->buf_in[i]);
+ sz->decoder_state = PulseSpace;
+ break;
+ }
+ }
+ }
+
+ usb_submit_urb(urb, GFP_ATOMIC);
+
+ return;
+}
+
+static struct input_dev *streamzap_init_input_dev(struct streamzap_ir *sz)
+{
+ struct input_dev *idev;
+ struct ir_dev_props *props;
+ struct device *dev = sz->dev;
+ int ret;
+
+ idev = input_allocate_device();
+ if (!idev) {
+ dev_err(dev, "remote input dev allocation failed\n");
+ goto idev_alloc_failed;
+ }
+
+ props = kzalloc(sizeof(struct ir_dev_props), GFP_KERNEL);
+ if (!props) {
+ dev_err(dev, "remote ir dev props allocation failed\n");
+ goto props_alloc_failed;
+ }
+
+ snprintf(sz->name, sizeof(sz->name), "Streamzap PC Remote Infrared "
+ "Receiver (%04x:%04x)",
+ le16_to_cpu(sz->usbdev->descriptor.idVendor),
+ le16_to_cpu(sz->usbdev->descriptor.idProduct));
+
+ idev->name = sz->name;
+ usb_make_path(sz->usbdev, sz->phys, sizeof(sz->phys));
+ strlcat(sz->phys, "/input0", sizeof(sz->phys));
+ idev->phys = sz->phys;
+
+ props->priv = sz;
+ props->driver_type = RC_DRIVER_IR_RAW;
+ /* FIXME: not sure about supported protocols, check on this */
+ props->allowed_protos = IR_TYPE_RC5 | IR_TYPE_RC6;
+
+ sz->props = props;
+
+ ret = ir_input_register(idev, RC_MAP_RC5_STREAMZAP, props, DRIVER_NAME);
+ if (ret < 0) {
+ dev_err(dev, "remote input device register failed\n");
+ goto irdev_failed;
+ }
+
+ return idev;
+
+irdev_failed:
+ kfree(props);
+props_alloc_failed:
+ input_free_device(idev);
+idev_alloc_failed:
+ return NULL;
+}
+
+static int streamzap_delay_buf_init(struct streamzap_ir *sz)
+{
+ int ret;
+
+ ret = kfifo_alloc(&sz->fifo, sizeof(int) * SZ_BUF_LEN,
+ GFP_KERNEL);
+ if (ret == 0)
+ sz->fifo_initialized = 1;
+
+ return ret;
+}
+
+static void streamzap_start_flush_timer(struct streamzap_ir *sz)
+{
+ sz->flush_timer.expires = jiffies + HZ;
+ sz->flush = true;
+ add_timer(&sz->flush_timer);
+
+ sz->urb_in->dev = sz->usbdev;
+ if (usb_submit_urb(sz->urb_in, GFP_ATOMIC))
+ dev_err(sz->dev, "urb submit failed\n");
+}
+
+/**
+ * streamzap_probe
+ *
+ * Called by usb-core to associated with a candidate device
+ * On any failure the return value is the ERROR
+ * On success return 0
+ */
+static int __devinit streamzap_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usb_device *usbdev = interface_to_usbdev(intf);
+ struct usb_host_interface *iface_host;
+ struct streamzap_ir *sz = NULL;
+ char buf[63], name[128] = "";
+ int retval = -ENOMEM;
+ int pipe, maxp;
+
+ /* Allocate space for device driver specific data */
+ sz = kzalloc(sizeof(struct streamzap_ir), GFP_KERNEL);
+ if (!sz)
+ return -ENOMEM;
+
+ sz->usbdev = usbdev;
+ sz->interface = intf;
+
+ /* Check to ensure endpoint information matches requirements */
+ iface_host = intf->cur_altsetting;
+
+ if (iface_host->desc.bNumEndpoints != 1) {
+ dev_err(&intf->dev, "%s: Unexpected desc.bNumEndpoints (%d)\n",
+ __func__, iface_host->desc.bNumEndpoints);
+ retval = -ENODEV;
+ goto free_sz;
+ }
+
+ sz->endpoint = &(iface_host->endpoint[0].desc);
+ if ((sz->endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
+ != USB_DIR_IN) {
+ dev_err(&intf->dev, "%s: endpoint doesn't match input device "
+ "02%02x\n", __func__, sz->endpoint->bEndpointAddress);
+ retval = -ENODEV;
+ goto free_sz;
+ }
+
+ if ((sz->endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+ != USB_ENDPOINT_XFER_INT) {
+ dev_err(&intf->dev, "%s: endpoint attributes don't match xfer "
+ "02%02x\n", __func__, sz->endpoint->bmAttributes);
+ retval = -ENODEV;
+ goto free_sz;
+ }
+
+ pipe = usb_rcvintpipe(usbdev, sz->endpoint->bEndpointAddress);
+ maxp = usb_maxpacket(usbdev, pipe, usb_pipeout(pipe));
+
+ if (maxp == 0) {
+ dev_err(&intf->dev, "%s: endpoint Max Packet Size is 0!?!\n",
+ __func__);
+ retval = -ENODEV;
+ goto free_sz;
+ }
+
+ /* Allocate the USB buffer and IRQ URB */
+ sz->buf_in = usb_alloc_coherent(usbdev, maxp, GFP_ATOMIC, &sz->dma_in);
+ if (!sz->buf_in)
+ goto free_sz;
+
+ sz->urb_in = usb_alloc_urb(0, GFP_KERNEL);
+ if (!sz->urb_in)
+ goto free_buf_in;
+
+ sz->dev = &intf->dev;
+ sz->buf_in_len = maxp;
+
+ if (usbdev->descriptor.iManufacturer
+ && usb_string(usbdev, usbdev->descriptor.iManufacturer,
+ buf, sizeof(buf)) > 0)
+ strlcpy(name, buf, sizeof(name));
+
+ if (usbdev->descriptor.iProduct
+ && usb_string(usbdev, usbdev->descriptor.iProduct,
+ buf, sizeof(buf)) > 0)
+ snprintf(name + strlen(name), sizeof(name) - strlen(name),
+ " %s", buf);
+
+ retval = streamzap_delay_buf_init(sz);
+ if (retval) {
+ dev_err(&intf->dev, "%s: delay buffer init failed\n", __func__);
+ goto free_urb_in;
+ }
+
+ sz->idev = streamzap_init_input_dev(sz);
+ if (!sz->idev)
+ goto input_dev_fail;
+
+ sz->idle = true;
+ sz->decoder_state = PulseSpace;
+ #if 0
+ /* not yet supported, depends on patches from maxim */
+ /* see also: LIRC_GET_REC_RESOLUTION and LIRC_SET_REC_TIMEOUT */
+ sz->timeout_enabled = false;
+ sz->min_timeout = STREAMZAP_TIMEOUT * STREAMZAP_RESOLUTION * 1000;
+ sz->max_timeout = STREAMZAP_TIMEOUT * STREAMZAP_RESOLUTION * 1000;
+ #endif
+
+ init_timer(&sz->delay_timer);
+ sz->delay_timer.function = streamzap_delay_timeout;
+ sz->delay_timer.data = (unsigned long)sz;
+ spin_lock_init(&sz->timer_lock);
+
+ init_timer(&sz->flush_timer);
+ sz->flush_timer.function = streamzap_flush_timeout;
+ sz->flush_timer.data = (unsigned long)sz;
+
+ do_gettimeofday(&sz->signal_start);
+
+ /* Complete final initialisations */
+ usb_fill_int_urb(sz->urb_in, usbdev, pipe, sz->buf_in,
+ maxp, (usb_complete_t)streamzap_callback,
+ sz, sz->endpoint->bInterval);
+ sz->urb_in->transfer_dma = sz->dma_in;
+ sz->urb_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ usb_set_intfdata(intf, sz);
+
+ streamzap_start_flush_timer(sz);
+
+ dev_info(sz->dev, "Registered %s on usb%d:%d\n", name,
+ usbdev->bus->busnum, usbdev->devnum);
+
+ return 0;
+
+input_dev_fail:
+ kfifo_free(&sz->fifo);
+free_urb_in:
+ usb_free_urb(sz->urb_in);
+free_buf_in:
+ usb_free_coherent(usbdev, maxp, sz->buf_in, sz->dma_in);
+free_sz:
+ kfree(sz);
+
+ return retval;
+}
+
+/**
+ * streamzap_disconnect
+ *
+ * Called by the usb core when the device is removed from the system.
+ *
+ * This routine guarantees that the driver will not submit any more urbs
+ * by clearing dev->usbdev. It is also supposed to terminate any currently
+ * active urbs. Unfortunately, usb_bulk_msg(), used in streamzap_read(),
+ * does not provide any way to do this.
+ */
+static void streamzap_disconnect(struct usb_interface *interface)
+{
+ struct streamzap_ir *sz = usb_get_intfdata(interface);
+ struct usb_device *usbdev = interface_to_usbdev(interface);
+
+ usb_set_intfdata(interface, NULL);
+
+ if (!sz)
+ return;
+
+ if (sz->flush) {
+ sz->flush = false;
+ del_timer_sync(&sz->flush_timer);
+ }
+
+ streamzap_stop_timer(sz);
+
+ sz->usbdev = NULL;
+ ir_input_unregister(sz->idev);
+ usb_kill_urb(sz->urb_in);
+ usb_free_urb(sz->urb_in);
+ usb_free_coherent(usbdev, sz->buf_in_len, sz->buf_in, sz->dma_in);
+
+ kfree(sz);
+}
+
+static int streamzap_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ struct streamzap_ir *sz = usb_get_intfdata(intf);
+
+ if (sz->flush) {
+ sz->flush = false;
+ del_timer_sync(&sz->flush_timer);
+ }
+
+ streamzap_stop_timer(sz);
+
+ usb_kill_urb(sz->urb_in);
+
+ return 0;
+}
+
+static int streamzap_resume(struct usb_interface *intf)
+{
+ struct streamzap_ir *sz = usb_get_intfdata(intf);
+
+ if (sz->fifo_initialized)
+ kfifo_reset(&sz->fifo);
+
+ sz->flush_timer.expires = jiffies + HZ;
+ sz->flush = true;
+ add_timer(&sz->flush_timer);
+
+ if (usb_submit_urb(sz->urb_in, GFP_ATOMIC)) {
+ dev_err(sz->dev, "Error sumbiting urb\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/**
+ * streamzap_init
+ */
+static int __init streamzap_init(void)
+{
+ int ret;
+
+ /* register this driver with the USB subsystem */
+ ret = usb_register(&streamzap_driver);
+ if (ret < 0)
+ printk(KERN_ERR DRIVER_NAME ": usb register failed, "
+ "result = %d\n", ret);
+
+ return ret;
+}
+
+/**
+ * streamzap_exit
+ */
+static void __exit streamzap_exit(void)
+{
+ usb_deregister(&streamzap_driver);
+}
+
+
+module_init(streamzap_init);
+module_exit(streamzap_exit);
+
+MODULE_AUTHOR("Jarod Wilson <jarod@wilsonet.com>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Enable debugging messages");