diff options
author | 2010-10-28 09:35:11 -0700 | |
---|---|---|
committer | 2010-10-28 09:35:11 -0700 | |
commit | 0851668fdd97e526b2a41f794b785c204dd3d3e0 (patch) | |
tree | 4ef7c20a8be8393006c6fe9627eb29dd30877d61 /drivers/media/IR | |
parent | Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/cjb... (diff) | |
parent | [media] af9015: Fix max I2C message size when used with tda18271 (diff) | |
download | linux-0851668fdd97e526b2a41f794b785c204dd3d3e0.tar.gz linux-0851668fdd97e526b2a41f794b785c204dd3d3e0.tar.bz2 linux-0851668fdd97e526b2a41f794b785c204dd3d3e0.zip |
Merge branch 'v4l_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-2.6
* 'v4l_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-2.6: (505 commits)
[media] af9015: Fix max I2C message size when used with tda18271
[media] IR: initialize ir_raw_event in few more drivers
[media] Guard a divide in v4l1 compat layer
[media] imon: fix nomouse modprobe option
[media] imon: remove redundant change_protocol call
[media] imon: fix my egregious brown paper bag w/rdev/idev split
[media] cafe_ccic: Configure ov7670 correctly
[media] ov7670: allow configuration of image size, clock speed, and I/O method
[media] af9015: support for DigitalNow TinyTwin v3 [1f4d:9016]
[media] af9015: map DigitalNow TinyTwin v2 remote
[media] DigitalNow TinyTwin remote controller
[media] af9015: RC fixes and improvements
videodev2.h.xml: Update to reflect the latest changes at videodev2.h
[media] v4l: document new Bayer and monochrome pixel formats
[media] DocBook/v4l: Add missing formats used on gspca cpia1 and sn9c2028
[media] firedtv: add parameter to fake ca_system_ids in CA_INFO
[media] tm6000: fix a macro coding style issue
tm6000: Remove some ugly debug code
[media] Nova-S-Plus audio line input
[media] [RFC,1/1] V4L2: Use new CAP bits in existing RDS capable drivers
...
Diffstat (limited to 'drivers/media/IR')
40 files changed, 5066 insertions, 1604 deletions
diff --git a/drivers/media/IR/Kconfig b/drivers/media/IR/Kconfig index 490c57cc4cfe..aa4163eb7a83 100644 --- a/drivers/media/IR/Kconfig +++ b/drivers/media/IR/Kconfig @@ -79,6 +79,18 @@ config IR_SONY_DECODER Enable this option if you have an infrared remote control which uses the Sony protocol, and you need software decoding support. +config IR_RC5_SZ_DECODER + tristate "Enable IR raw decoder for the RC-5 (streamzap) protocol" + depends on IR_CORE + select BITREVERSE + default y + + ---help--- + Enable this option if you have IR with RC-5 (streamzap) protocol, + and if the IR is decoded in software. (The Streamzap PC Remote + uses an IR protocol that is almost standard RC-5, but not quite, + as it uses an additional bit). + config IR_LIRC_CODEC tristate "Enable IR to LIRC bridge" depends on IR_CORE @@ -89,6 +101,20 @@ config IR_LIRC_CODEC Enable this option to pass raw IR to and from userspace via the LIRC interface. +config IR_ENE + tristate "ENE eHome Receiver/Transceiver (pnp id: ENE0100/ENE02xxx)" + depends on PNP + depends on IR_CORE + ---help--- + Say Y here to enable support for integrated infrared receiver + /transceiver made by ENE. + + You can see if you have it by looking at lspnp output. + Output should include ENE0100 ENE0200 or something similar. + + To compile this driver as a module, choose M here: the + module will be called ene_ir. + config IR_IMON tristate "SoundGraph iMON Receiver and Display" depends on USB_ARCH_HAS_HCD @@ -113,19 +139,18 @@ 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)" +config IR_NUVOTON + tristate "Nuvoton w836x7hg Consumer Infrared Transceiver" 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. + /transciever made by Nuvoton (formerly Winbond). This chip is + found in the ASRock ION 330HT, as well as assorted Intel + DP55-series motherboards (and of course, possibly others). To compile this driver as a module, choose M here: the - module will be called ene_ir. + module will be called nuvoton-cir. config IR_STREAMZAP tristate "Streamzap PC Remote IR Receiver" diff --git a/drivers/media/IR/Makefile b/drivers/media/IR/Makefile index 53676838fe97..f9574adab82a 100644 --- a/drivers/media/IR/Makefile +++ b/drivers/media/IR/Makefile @@ -11,10 +11,12 @@ obj-$(CONFIG_IR_RC5_DECODER) += ir-rc5-decoder.o obj-$(CONFIG_IR_RC6_DECODER) += ir-rc6-decoder.o obj-$(CONFIG_IR_JVC_DECODER) += ir-jvc-decoder.o obj-$(CONFIG_IR_SONY_DECODER) += ir-sony-decoder.o +obj-$(CONFIG_IR_RC5_SZ_DECODER) += ir-rc5-sz-decoder.o 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_NUVOTON) += nuvoton-cir.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 index 5447750f5e38..7637babcd262 100644 --- a/drivers/media/IR/ene_ir.c +++ b/drivers/media/IR/ene_ir.c @@ -1,5 +1,5 @@ /* - * driver for ENE KB3926 B/C/D CIR (pnp id: ENE0XXX) + * driver for ENE KB3926 B/C/D/E/F CIR (pnp id: ENE0XXX) * * Copyright (C) 2010 Maxim Levitsky <maximlevitsky@gmail.com> * @@ -17,6 +17,17 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA + * + * Special thanks to: + * Sami R. <maesesami@gmail.com> for lot of help in debugging and therefore + * bringing to life support for transmission & learning mode. + * + * Charlie Andrews <charliethepilot@googlemail.com> for lots of help in + * bringing up the support of new firmware buffer that is popular + * on latest notebooks + * + * ENE for partial device documentation + * */ #include <linux/kernel.h> @@ -31,51 +42,59 @@ #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 sample_period; +static bool learning_mode_force; static int debug; -static int txsim; +static bool txsim; -static int ene_irq_status(struct ene_device *dev); +static void ene_set_reg_addr(struct ene_device *dev, u16 reg) +{ + outb(reg >> 8, dev->hw_io + ENE_ADDR_HI); + outb(reg & 0xFF, dev->hw_io + ENE_ADDR_LO); +} /* read a hardware register */ -static u8 ene_hw_read_reg(struct ene_device *dev, u16 reg) +static u8 ene_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); + ene_set_reg_addr(dev, reg); retval = inb(dev->hw_io + ENE_IO); - - ene_dbg_verbose("reg %04x == %02x", reg, retval); + dbg_regs("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) +static void ene_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); + dbg_regs("reg %04x <- %02x", reg, value); + ene_set_reg_addr(dev, reg); 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) +/* Set bits in hardware register */ +static void ene_set_reg_mask(struct ene_device *dev, u16 reg, u8 mask) { - u8 regvalue; - - outb(reg >> 8, dev->hw_io + ENE_ADDR_HI); - outb(reg & 0xFF, dev->hw_io + ENE_ADDR_LO); + dbg_regs("reg %04x |= %02x", reg, mask); + ene_set_reg_addr(dev, reg); + outb(inb(dev->hw_io + ENE_IO) | mask, dev->hw_io + ENE_IO); +} - regvalue = inb(dev->hw_io + ENE_IO) & ~mask; - regvalue |= (value & mask); - outb(regvalue, dev->hw_io + ENE_IO); +/* Clear bits in hardware register */ +static void ene_clear_reg_mask(struct ene_device *dev, u16 reg, u8 mask) +{ + dbg_regs("reg %04x &= ~%02x ", reg, mask); + ene_set_reg_addr(dev, reg); + outb(inb(dev->hw_io + ENE_IO) & ~mask, dev->hw_io + ENE_IO); +} - ene_dbg_verbose("reg %04x <- %02x (mask=%02x)", reg, value, mask); +/* A helper to set/clear a bit in register according to boolean variable */ +static void ene_set_clear_reg_mask(struct ene_device *dev, u16 reg, u8 mask, + bool set) +{ + if (set) + ene_set_reg_mask(dev, reg, mask); + else + ene_clear_reg_mask(dev, reg, mask); } /* detect hardware features */ @@ -83,194 +102,378 @@ 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; + u8 fw_reg2, fw_reg1; - tmp = ene_hw_read_reg(dev, ENE_HW_UNK); - ene_hw_write_reg(dev, ENE_HW_UNK, tmp & ~ENE_HW_UNK_CLR); + ene_clear_reg_mask(dev, ENE_ECSTS, ENE_ECSTS_RSRVD); + chip_major = ene_read_reg(dev, ENE_ECVER_MAJOR); + chip_minor = ene_read_reg(dev, ENE_ECVER_MINOR); + ene_set_reg_mask(dev, ENE_ECSTS, ENE_ECSTS_RSRVD); - chip_major = ene_hw_read_reg(dev, ENE_HW_VER_MAJOR); - chip_minor = ene_hw_read_reg(dev, ENE_HW_VER_MINOR); + hw_revision = ene_read_reg(dev, ENE_ECHV); + old_ver = ene_read_reg(dev, ENE_HW_VER_OLD); - 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); + dev->pll_freq = (ene_read_reg(dev, ENE_PLLFRH) << 4) + + (ene_read_reg(dev, ENE_PLLFRL) >> 4); - 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 (sample_period != ENE_DEFAULT_SAMPLE_PERIOD) + dev->rx_period_adjust = + dev->pll_freq == ENE_DEFAULT_PLL_FREQ ? 2 : 4; 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"); + ene_warn("device seems to be disabled"); + ene_warn("send a mail to lirc-list@lists.sourceforge.net"); + ene_warn("please attach output of acpidump and dmidecode"); return -ENODEV; } + ene_notice("chip is 0x%02x%02x - kbver = 0x%02x, rev = 0x%02x", + chip_major, chip_minor, old_ver, hw_revision); + + ene_notice("PLL freq = %d", dev->pll_freq); + if (chip_major == 0x33) { - ene_printk(KERN_WARNING, "chips 0x33xx aren't supported\n"); + ene_warn("chips 0x33xx aren't supported"); return -ENODEV; } if (chip_major == 0x39 && chip_minor == 0x26 && hw_revision == 0xC0) { dev->hw_revision = ENE_HW_C; + ene_notice("KB3926C detected"); } else if (old_ver == 0x24 && hw_revision == 0xC0) { dev->hw_revision = ENE_HW_B; - ene_printk(KERN_NOTICE, "KB3926B detected\n"); + ene_notice("KB3926B detected"); } 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_notice("KB3926D or higher detected"); } - 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); + fw_reg1 = ene_read_reg(dev, ENE_FW1); + fw_reg2 = ene_read_reg(dev, ENE_FW2); + + ene_notice("Firmware regs: %02x %02x", fw_reg1, fw_reg2); - dev->hw_gpio40_learning = fw_capabilities & ENE_FW2_GP40_AS_LEARN; - dev->hw_learning_and_tx_capable = fw_capabilities & ENE_FW2_LEARNING; + dev->hw_use_gpio_0a = !!(fw_reg2 & ENE_FW2_GP0A); + dev->hw_learning_and_tx_capable = !!(fw_reg2 & ENE_FW2_LEARNING); + dev->hw_extra_buffer = !!(fw_reg1 & ENE_FW1_HAS_EXTRA_BUF); - dev->hw_fan_as_normal_input = dev->hw_learning_and_tx_capable && - (fw_capabilities & ENE_FW2_FAN_AS_NRML_IN); + if (dev->hw_learning_and_tx_capable) + dev->hw_fan_input = !!(fw_reg2 & ENE_FW2_FAN_INPUT); - 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"); + ene_notice("Hardware features:"); 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"); + ene_notice("* Supports transmitting & learning mode"); + ene_notice(" This feature is rare and therefore,"); + ene_notice(" you are welcome to test it,"); + ene_notice(" and/or contact the author via:"); + ene_notice(" lirc-list@lists.sourceforge.net"); + ene_notice(" or maximlevitsky@gmail.com"); + + ene_notice("* Uses GPIO %s for IR raw input", + dev->hw_use_gpio_0a ? "40" : "0A"); + + if (dev->hw_fan_input) + ene_notice("* Uses unused fan feedback input as source" + " of demodulated IR data"); } + + if (!dev->hw_fan_input) + ene_notice("* Uses GPIO %s for IR demodulated input", + dev->hw_use_gpio_0a ? "0A" : "40"); + + if (dev->hw_extra_buffer) + ene_notice("* Uses new style input buffer"); return 0; } -/* this enables/disables IR input via gpio40*/ -static void ene_enable_gpio40_receive(struct ene_device *dev, int enable) +/* Read properities of hw sample buffer */ +static void ene_rx_setup_hw_buffer(struct ene_device *dev) { - ene_hw_write_reg_mask(dev, ENE_CIR_CONF2, enable ? - 0 : ENE_CIR_CONF2_GPIO40DIS, - ENE_CIR_CONF2_GPIO40DIS); + u16 tmp; + + ene_rx_read_hw_pointer(dev); + dev->r_pointer = dev->w_pointer; + + if (!dev->hw_extra_buffer) { + dev->buffer_len = ENE_FW_PACKET_SIZE * 2; + return; + } + + tmp = ene_read_reg(dev, ENE_FW_SAMPLE_BUFFER); + tmp |= ene_read_reg(dev, ENE_FW_SAMPLE_BUFFER+1) << 8; + dev->extra_buf1_address = tmp; + + dev->extra_buf1_len = ene_read_reg(dev, ENE_FW_SAMPLE_BUFFER + 2); + + tmp = ene_read_reg(dev, ENE_FW_SAMPLE_BUFFER + 3); + tmp |= ene_read_reg(dev, ENE_FW_SAMPLE_BUFFER + 4) << 8; + dev->extra_buf2_address = tmp; + + dev->extra_buf2_len = ene_read_reg(dev, ENE_FW_SAMPLE_BUFFER + 5); + + dev->buffer_len = dev->extra_buf1_len + dev->extra_buf2_len + 8; + + ene_notice("Hardware uses 2 extended buffers:"); + ene_notice(" 0x%04x - len : %d", dev->extra_buf1_address, + dev->extra_buf1_len); + ene_notice(" 0x%04x - len : %d", dev->extra_buf2_address, + dev->extra_buf2_len); + + ene_notice("Total buffer len = %d", dev->buffer_len); + + if (dev->buffer_len > 64 || dev->buffer_len < 16) + goto error; + + if (dev->extra_buf1_address > 0xFBFC || + dev->extra_buf1_address < 0xEC00) + goto error; + + if (dev->extra_buf2_address > 0xFBFC || + dev->extra_buf2_address < 0xEC00) + goto error; + + if (dev->r_pointer > dev->buffer_len) + goto error; + + ene_set_reg_mask(dev, ENE_FW1, ENE_FW1_EXTRA_BUF_HND); + return; +error: + ene_warn("Error validating extra buffers, device probably won't work"); + dev->hw_extra_buffer = false; + ene_clear_reg_mask(dev, ENE_FW1, ENE_FW1_EXTRA_BUF_HND); } -/* this enables/disables IR via standard input */ -static void ene_enable_normal_receive(struct ene_device *dev, int enable) + +/* Restore the pointers to extra buffers - to make module reload work*/ +static void ene_rx_restore_hw_buffer(struct ene_device *dev) { - ene_hw_write_reg(dev, ENE_CIR_CONF1, enable ? ENE_CIR_CONF1_RX_ON : 0); + if (!dev->hw_extra_buffer) + return; + + ene_write_reg(dev, ENE_FW_SAMPLE_BUFFER + 0, + dev->extra_buf1_address & 0xFF); + ene_write_reg(dev, ENE_FW_SAMPLE_BUFFER + 1, + dev->extra_buf1_address >> 8); + ene_write_reg(dev, ENE_FW_SAMPLE_BUFFER + 2, dev->extra_buf1_len); + + ene_write_reg(dev, ENE_FW_SAMPLE_BUFFER + 3, + dev->extra_buf2_address & 0xFF); + ene_write_reg(dev, ENE_FW_SAMPLE_BUFFER + 4, + dev->extra_buf2_address >> 8); + ene_write_reg(dev, ENE_FW_SAMPLE_BUFFER + 5, + dev->extra_buf2_len); + ene_clear_reg_mask(dev, ENE_FW1, ENE_FW1_EXTRA_BUF_HND); } -/* this enables/disables IR input via unused fan tachtometer input */ -static void ene_enable_fan_receive(struct ene_device *dev, int enable) +/* Read hardware write pointer */ +static void ene_rx_read_hw_pointer(struct ene_device *dev) { - 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; + if (dev->hw_extra_buffer) + dev->w_pointer = ene_read_reg(dev, ENE_FW_RX_POINTER); + else + dev->w_pointer = ene_read_reg(dev, ENE_FW2) + & ENE_FW2_BUF_WPTR ? 0 : ENE_FW_PACKET_SIZE; + + dbg_verbose("RB: HW write pointer: %02x, driver read pointer: %02x", + dev->w_pointer, dev->r_pointer); } +/* Gets address of next sample from HW ring buffer */ +static int ene_rx_get_sample_reg(struct ene_device *dev) +{ + int r_pointer; + + if (dev->r_pointer == dev->w_pointer) { + dbg_verbose("RB: hit end, try update w_pointer"); + ene_rx_read_hw_pointer(dev); + } + + if (dev->r_pointer == dev->w_pointer) { + dbg_verbose("RB: end of data at %d", dev->r_pointer); + return 0; + } + + dbg_verbose("RB: reading at offset %d", dev->r_pointer); + r_pointer = dev->r_pointer; + + dev->r_pointer++; + if (dev->r_pointer == dev->buffer_len) + dev->r_pointer = 0; + + dbg_verbose("RB: next read will be from offset %d", dev->r_pointer); + + if (r_pointer < 8) { + dbg_verbose("RB: read at main buffer at %d", r_pointer); + return ENE_FW_SAMPLE_BUFFER + r_pointer; + } + + r_pointer -= 8; + + if (r_pointer < dev->extra_buf1_len) { + dbg_verbose("RB: read at 1st extra buffer at %d", r_pointer); + return dev->extra_buf1_address + r_pointer; + } + + r_pointer -= dev->extra_buf1_len; + + if (r_pointer < dev->extra_buf2_len) { + dbg_verbose("RB: read at 2nd extra buffer at %d", r_pointer); + return dev->extra_buf2_address + r_pointer; + } + + dbg("attempt to read beyong ring bufer end"); + return 0; +} /* Sense current received carrier */ -static int ene_rx_sense_carrier(struct ene_device *dev) +void 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); + DEFINE_IR_RAW_EVENT(ev); - if (!(period & ENE_RX_CARRIER_VALID)) - return 0; + int carrier, duty_cycle; + int period = ene_read_reg(dev, ENE_CIRCAR_PRD); + int hperiod = ene_read_reg(dev, ENE_CIRCAR_HPRD); + + if (!(period & ENE_CIRCAR_PRD_VALID)) + return; - period &= ~ENE_RX_CARRIER_VALID; + period &= ~ENE_CIRCAR_PRD_VALID; if (!period) - return 0; + return; + + dbg("RX: hardware carrier period = %02x", period); + dbg("RX: hardware carrier pulse period = %02x", hperiod); carrier = 2000000 / period; - ene_dbg("RX: sensed carrier = %d Hz", carrier); - return carrier; + duty_cycle = (hperiod * 100) / period; + dbg("RX: sensed carrier = %d Hz, duty cycle %d%%", + carrier, duty_cycle); + if (dev->carrier_detect_enabled) { + ev.carrier_report = true; + ev.carrier = carrier; + ev.duty_cycle = duty_cycle; + ir_raw_event_store(dev->idev, &ev); + } } -/* determine which input to use*/ -static void ene_rx_set_inputs(struct ene_device *dev) +/* this enables/disables the CIR RX engine */ +static void ene_rx_enable_cir_engine(struct ene_device *dev, bool enable) { - int learning_mode = dev->learning_enabled; - - ene_dbg("RX: setup receiver, learning mode = %d", learning_mode); + ene_set_clear_reg_mask(dev, ENE_CIRCFG, + ENE_CIRCFG_RX_EN | ENE_CIRCFG_RX_IRQ, enable); +} - ene_enable_normal_receive(dev, 1); +/* this selects input for CIR engine. Ether GPIO 0A or GPIO40*/ +static void ene_rx_select_input(struct ene_device *dev, bool gpio_0a) +{ + ene_set_clear_reg_mask(dev, ENE_CIRCFG2, ENE_CIRCFG2_GPIO0A, gpio_0a); +} - /* old hardware doesn't support learning mode for sure */ - if (dev->hw_revision <= ENE_HW_B) +/* + * this enables alternative input via fan tachometer sensor and bypasses + * the hw CIR engine + */ +static void ene_rx_enable_fan_input(struct ene_device *dev, bool enable) +{ + if (!dev->hw_fan_input) 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; + if (!enable) + ene_write_reg(dev, ENE_FAN_AS_IN1, 0); + else { + ene_write_reg(dev, ENE_FAN_AS_IN1, ENE_FAN_AS_IN1_EN); + ene_write_reg(dev, ENE_FAN_AS_IN2, ENE_FAN_AS_IN2_EN); } +} + +/* setup the receiver for RX*/ +static void ene_rx_setup(struct ene_device *dev) +{ + bool learning_mode = dev->learning_mode_enabled || + dev->carrier_detect_enabled; + int sample_period_adjust = 0; + + dbg("RX: setup receiver, learning mode = %d", learning_mode); + + + /* This selects RLC input and clears CFG2 settings */ + ene_write_reg(dev, ENE_CIRCFG2, 0x00); + + /* set sample period*/ + if (sample_period == ENE_DEFAULT_SAMPLE_PERIOD) + sample_period_adjust = + dev->pll_freq == ENE_DEFAULT_PLL_FREQ ? 1 : 2; + + ene_write_reg(dev, ENE_CIRRLC_CFG, + (sample_period + sample_period_adjust) | + ENE_CIRRLC_CFG_OVERFLOW); + /* revB doesn't support inputs */ + if (dev->hw_revision < ENE_HW_C) + goto select_timeout; - /* 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); + WARN_ON(!dev->hw_learning_and_tx_capable); - /* 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); - } + /* Enable the opposite of the normal input + That means that if GPIO40 is normally used, use GPIO0A + and vice versa. + This input will carry non demodulated + signal, and we will tell the hw to demodulate it itself */ + ene_rx_select_input(dev, !dev->hw_use_gpio_0a); + dev->rx_fan_input_inuse = false; - /* 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); + /* Enable carrier demodulation */ + ene_set_reg_mask(dev, ENE_CIRCFG, ENE_CIRCFG_CARR_DEMOD); - ene_hw_write_reg_mask(dev, ENE_CIR_CONF2, learning_mode ? - ENE_CIR_CONF2_LEARN2 : 0, ENE_CIR_CONF2_LEARN2); + /* Enable carrier detection */ + ene_write_reg(dev, ENE_CIRCAR_PULS, 0x63); + ene_set_clear_reg_mask(dev, ENE_CIRCFG2, ENE_CIRCFG2_CARR_DETECT, + dev->carrier_detect_enabled || debug); + } else { + if (dev->hw_fan_input) + dev->rx_fan_input_inuse = true; + else + ene_rx_select_input(dev, dev->hw_use_gpio_0a); + + /* Disable carrier detection & demodulation */ + ene_clear_reg_mask(dev, ENE_CIRCFG, ENE_CIRCFG_CARR_DEMOD); + ene_clear_reg_mask(dev, ENE_CIRCFG2, ENE_CIRCFG2_CARR_DETECT); + } +select_timeout: if (dev->rx_fan_input_inuse) { - dev->props->rx_resolution = ENE_SAMPLE_PERIOD_FAN * 1000; + dev->props->rx_resolution = MS_TO_NS(ENE_FW_SAMPLE_PERIOD_FAN); - dev->props->timeout = - ENE_FAN_VALUE_MASK * ENE_SAMPLE_PERIOD_FAN * 1000; + /* Fan input doesn't support timeouts, it just ends the + input with a maximum sample */ + dev->props->min_timeout = dev->props->max_timeout = + MS_TO_NS(ENE_FW_SMPL_BUF_FAN_MSK * + ENE_FW_SAMPLE_PERIOD_FAN); } else { - dev->props->rx_resolution = sample_period * 1000; - dev->props->timeout = ENE_MAXGAP * 1000; + dev->props->rx_resolution = MS_TO_NS(sample_period); + + /* Theoreticly timeout is unlimited, but we cap it + * because it was seen that on one device, it + * would stop sending spaces after around 250 msec. + * Besides, this is close to 2^32 anyway and timeout is u32. + */ + dev->props->min_timeout = MS_TO_NS(127 * sample_period); + dev->props->max_timeout = MS_TO_NS(200000); } + + if (dev->hw_learning_and_tx_capable) + dev->props->tx_resolution = MS_TO_NS(sample_period); + + if (dev->props->timeout > dev->props->max_timeout) + dev->props->timeout = dev->props->max_timeout; + if (dev->props->timeout < dev->props->min_timeout) + dev->props->timeout = dev->props->min_timeout; } /* Enable the device for receive */ @@ -278,145 +481,157 @@ static void ene_rx_enable(struct ene_device *dev) { u8 reg_value; + /* Enable system interrupt */ 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); + ene_write_reg(dev, ENEB_IRQ, dev->irq << 1); + ene_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); + reg_value = ene_read_reg(dev, ENE_IRQ) & 0xF0; + reg_value |= ENE_IRQ_UNK_EN; + reg_value &= ~ENE_IRQ_STATUS; + reg_value |= (dev->irq & ENE_IRQ_MASK); + ene_write_reg(dev, ENE_IRQ, reg_value); } - 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); + /* Enable inputs */ + ene_rx_enable_fan_input(dev, dev->rx_fan_input_inuse); + ene_rx_enable_cir_engine(dev, !dev->rx_fan_input_inuse); /* 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); + ene_set_reg_mask(dev, ENE_FW1, ENE_FW1_ENABLE | ENE_FW1_IRQ); /* enter idle mode */ - ir_raw_event_set_idle(dev->idev, 1); - ir_raw_event_reset(dev->idev); - + ir_raw_event_set_idle(dev->idev, true); + dev->rx_enabled = true; } /* 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); + ene_rx_enable_cir_engine(dev, false); + ene_rx_enable_fan_input(dev, false); /* disable hardware IRQ and firmware flag */ - ene_hw_write_reg_mask(dev, ENE_FW1, 0, ENE_FW1_ENABLE | ENE_FW1_IRQ); + ene_clear_reg_mask(dev, ENE_FW1, ENE_FW1_ENABLE | ENE_FW1_IRQ); - ir_raw_event_set_idle(dev->idev, 1); - ir_raw_event_reset(dev->idev); + ir_raw_event_set_idle(dev->idev, true); + dev->rx_enabled = false; } +/* This resets the receiver. Usefull to stop stream of spaces at end of + * transmission + */ +static void ene_rx_reset(struct ene_device *dev) +{ + ene_clear_reg_mask(dev, ENE_CIRCFG, ENE_CIRCFG_RX_EN); + ene_set_reg_mask(dev, ENE_CIRCFG, ENE_CIRCFG_RX_EN); +} -/* prepare transmission */ -static void ene_tx_prepare(struct ene_device *dev) +/* Set up the TX carrier frequency and duty cycle */ +static void ene_tx_set_carrier(struct ene_device *dev) { - u8 conf1; + u8 tx_puls_width; + unsigned long flags; - conf1 = ene_hw_read_reg(dev, ENE_CIR_CONF1); - dev->saved_conf1 = conf1; + spin_lock_irqsave(&dev->hw_lock, flags); - if (dev->hw_revision == ENE_HW_C) - conf1 &= ~ENE_CIR_CONF1_TX_CLEAR; + ene_set_clear_reg_mask(dev, ENE_CIRCFG, + ENE_CIRCFG_TX_CARR, dev->tx_period > 0); - /* Enable TX engine */ - conf1 |= ENE_CIR_CONF1_TX_ON; + if (!dev->tx_period) + goto unlock; - /* Set carrier */ - if (dev->tx_period) { + BUG_ON(dev->tx_duty_cycle >= 100 || dev->tx_duty_cycle <= 0); - /* 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; + tx_puls_width = dev->tx_period / (100 / dev->tx_duty_cycle); - int tx_pulse_width_in_500ns = - tx_period_in500ns / (100 / dev->tx_duty_cycle); + if (!tx_puls_width) + tx_puls_width = 1; - if (!tx_pulse_width_in_500ns) - tx_pulse_width_in_500ns = 1; + dbg("TX: pulse distance = %d * 500 ns", dev->tx_period); + dbg("TX: pulse width = %d * 500 ns", tx_puls_width); - 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_write_reg(dev, ENE_CIRMOD_PRD, dev->tx_period | ENE_CIRMOD_PRD_POL); + ene_write_reg(dev, ENE_CIRMOD_HPRD, tx_puls_width); +unlock: + spin_unlock_irqrestore(&dev->hw_lock, flags); +} - ene_hw_write_reg(dev, ENE_TX_PERIOD, ENE_TX_PERIOD_UNKBIT | - tx_period_in500ns); +/* Enable/disable transmitters */ +static void ene_tx_set_transmitters(struct ene_device *dev) +{ + unsigned long flags; - ene_hw_write_reg(dev, ENE_TX_PERIOD_PULSE, - tx_pulse_width_in_500ns); + spin_lock_irqsave(&dev->hw_lock, flags); + ene_set_clear_reg_mask(dev, ENE_GPIOFS8, ENE_GPIOFS8_GPIO41, + !!(dev->transmitter_mask & 0x01)); + ene_set_clear_reg_mask(dev, ENE_GPIOFS1, ENE_GPIOFS1_GPIO0D, + !!(dev->transmitter_mask & 0x02)); + spin_unlock_irqrestore(&dev->hw_lock, flags); +} - conf1 |= ENE_CIR_CONF1_TX_CARR; - } else - conf1 &= ~ENE_CIR_CONF1_TX_CARR; +/* prepare transmission */ +static void ene_tx_enable(struct ene_device *dev) +{ + u8 conf1 = ene_read_reg(dev, ENE_CIRCFG); + u8 fwreg2 = ene_read_reg(dev, ENE_FW2); + + dev->saved_conf1 = conf1; + + /* Show information about currently connected transmitter jacks */ + if (fwreg2 & ENE_FW2_EMMITER1_CONN) + dbg("TX: Transmitter #1 is connected"); + + if (fwreg2 & ENE_FW2_EMMITER2_CONN) + dbg("TX: Transmitter #2 is connected"); - ene_hw_write_reg(dev, ENE_CIR_CONF1, conf1); + if (!(fwreg2 & (ENE_FW2_EMMITER1_CONN | ENE_FW2_EMMITER2_CONN))) + ene_warn("TX: transmitter cable isn't connected!"); + /* disable receive on revc */ + if (dev->hw_revision == ENE_HW_C) + conf1 &= ~ENE_CIRCFG_RX_EN; + + /* Enable TX engine */ + conf1 |= ENE_CIRCFG_TX_EN | ENE_CIRCFG_TX_IRQ; + ene_write_reg(dev, ENE_CIRCFG, conf1); } /* end transmission */ -static void ene_tx_complete(struct ene_device *dev) +static void ene_tx_disable(struct ene_device *dev) { - ene_hw_write_reg(dev, ENE_CIR_CONF1, dev->saved_conf1); + ene_write_reg(dev, ENE_CIRCFG, 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; + bool pulse = dev->tx_sample_pulse; if (!dev->tx_buffer) { - ene_dbg("TX: attempt to transmit NULL buffer"); + ene_warn("TX: BUG: 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_pos == dev->tx_len) { if (!dev->tx_done) { - ene_dbg("TX: no more data to send"); - dev->tx_done = 1; + dbg("TX: no more data to send"); + dev->tx_done = true; goto exit; } else { - ene_dbg("TX: last sample sent by hardware"); - ene_tx_complete(dev); + dbg("TX: last sample sent by hardware"); + ene_tx_disable(dev); complete(&dev->tx_complete); return; } @@ -425,23 +640,23 @@ again: 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, sample_period); - dev->tx_sample = DIV_ROUND_CLOSEST(sample, ENE_TX_SMPL_PERIOD); - - /* guard against too short samples */ if (!dev->tx_sample) - goto again; + dev->tx_sample = 1; } - raw_tx = min(dev->tx_sample , (unsigned int)ENE_TX_SMLP_MASK); + raw_tx = min(dev->tx_sample , (unsigned int)ENE_CIRRLC_OUT_MASK); dev->tx_sample -= raw_tx; - if (dev->tx_sample_pulse) - raw_tx |= ENE_TX_PULSE_MASK; + dbg("TX: sample %8d (%s)", raw_tx * sample_period, + pulse ? "pulse" : "space"); + if (pulse) + raw_tx |= ENE_CIRRLC_OUT_PULSE; + + ene_write_reg(dev, + dev->tx_reg ? ENE_CIRRLC_OUT1 : ENE_CIRRLC_OUT0, raw_tx); - ene_hw_write_reg(dev, ENE_TX_INPUT1 + dev->tx_reg, raw_tx); dev->tx_reg = !dev->tx_reg; exit: /* simulate TX done interrupt */ @@ -466,76 +681,59 @@ 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); + fw_flags2 = ene_read_reg(dev, ENE_FW2); if (dev->hw_revision < ENE_HW_C) { - irq_status = ene_hw_read_reg(dev, ENEB_IRQ_STATUS); + irq_status = ene_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; + ene_clear_reg_mask(dev, ENEB_IRQ_STATUS, ENEB_IRQ_STATUS_IR); return ENE_IRQ_RX; } - irq_status = ene_hw_read_reg(dev, ENEC_IRQ); - - if (!(irq_status & ENEC_IRQ_STATUS)) + irq_status = ene_read_reg(dev, ENE_IRQ); + if (!(irq_status & ENE_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); + ene_write_reg(dev, ENE_IRQ, irq_status & ~ENE_IRQ_STATUS); + ene_write_reg(dev, ENE_IRQ, irq_status & ~ENE_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 RX interrupt */ + if (fw_flags2 & ENE_FW2_RXIRQ) { + retval |= ENE_IRQ_RX; + ene_write_reg(dev, ENE_FW2, fw_flags2 & ~ENE_FW2_RXIRQ); + } - /* check if this is a TX interrupt */ - fw_flags1 = ene_hw_read_reg(dev, ENE_FW1); + /* check TX interrupt */ + fw_flags1 = ene_read_reg(dev, ENE_FW1); if (fw_flags1 & ENE_FW1_TXIRQ) { - ene_hw_write_reg(dev, ENE_FW1, fw_flags1 & ~ENE_FW1_TXIRQ); + ene_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; + u16 hw_value, reg; + int hw_sample, irq_status; + bool pulse; unsigned long flags; - int carrier = 0; irqreturn_t retval = IRQ_NONE; struct ene_device *dev = (struct ene_device *)data; - struct ir_raw_event ev; - + DEFINE_IR_RAW_EVENT(ev); spin_lock_irqsave(&dev->hw_lock, flags); + + dbg_verbose("ISR called"); + ene_rx_read_hw_pointer(dev); irq_status = ene_irq_status(dev); if (!irq_status) @@ -544,9 +742,9 @@ static irqreturn_t ene_isr(int irq, void *data) retval = IRQ_HANDLED; if (irq_status & ENE_IRQ_TX) { - + dbg_verbose("TX interrupt"); if (!dev->hw_learning_and_tx_capable) { - ene_dbg("TX interrupt on unsupported device!"); + dbg("TX interrupt on unsupported device!"); goto unlock; } ene_tx_sample(dev); @@ -555,48 +753,57 @@ static irqreturn_t ene_isr(int irq, void *data) if (!(irq_status & ENE_IRQ_RX)) goto unlock; + dbg_verbose("RX interrupt"); - 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 + if (dev->hw_learning_and_tx_capable) + ene_rx_sense_carrier(dev); + + /* On hardware that don't support extra buffer we need to trust + the interrupt and not track the read pointer */ + if (!dev->hw_extra_buffer) + dev->r_pointer = dev->w_pointer == 0 ? ENE_FW_PACKET_SIZE : 0; + + while (1) { + + reg = ene_rx_get_sample_reg(dev); + + dbg_verbose("next sample to read at: %04x", reg); + if (!reg) + break; - for (i = 0; i < ENE_SAMPLES_SIZE; i++) { - hw_value = ene_hw_read_reg(dev, - ENE_SAMPLE_BUFFER + dev->rx_pointer * 4 + i); + hw_value = ene_read_reg(dev, reg); if (dev->rx_fan_input_inuse) { + + int offset = ENE_FW_SMPL_BUF_FAN - ENE_FW_SAMPLE_BUFFER; + /* 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; + hw_value |= ene_read_reg(dev, reg + offset) << 8; + pulse = hw_value & ENE_FW_SMPL_BUF_FAN_PLS; /* clear space bit, and other unused bits */ - hw_value &= ENE_FAN_VALUE_MASK; - hw_sample = hw_value * ENE_SAMPLE_PERIOD_FAN; + hw_value &= ENE_FW_SMPL_BUF_FAN_MSK; + hw_sample = hw_value * ENE_FW_SAMPLE_PERIOD_FAN; } else { - pulse = !(hw_value & ENE_SAMPLE_SPC_MASK); - hw_value &= ENE_SAMPLE_VALUE_MASK; + pulse = !(hw_value & ENE_FW_SAMPLE_SPACE); + hw_value &= ~ENE_FW_SAMPLE_SPACE; hw_sample = hw_value * sample_period; if (dev->rx_period_adjust) { - hw_sample *= (100 - dev->rx_period_adjust); - hw_sample /= 100; + hw_sample *= 100; + hw_sample /= (100 + dev->rx_period_adjust); } } - /* no more data */ - if (!(hw_value)) - break; - ene_dbg("RX: %d (%s)", hw_sample, pulse ? "pulse" : "space"); + if (!dev->hw_extra_buffer && !hw_sample) { + dev->r_pointer = dev->w_pointer; + continue; + } + dbg("RX: %d (%s)", hw_sample, pulse ? "pulse" : "space"); - ev.duration = hw_sample * 1000; + ev.duration = MS_TO_NS(hw_sample); ev.pulse = pulse; ir_raw_event_store_with_filter(dev->idev, &ev); } @@ -608,19 +815,26 @@ unlock: } /* Initialize default settings */ -static void ene_setup_settings(struct ene_device *dev) +static void ene_setup_default_settings(struct ene_device *dev) { dev->tx_period = 32; - dev->tx_duty_cycle = 25; /*%*/ - dev->transmitter_mask = 3; + dev->tx_duty_cycle = 50; /*%*/ + dev->transmitter_mask = 0x03; + dev->learning_mode_enabled = learning_mode_force; - /* 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); + /* Set reasonable default timeout */ + dev->props->timeout = MS_TO_NS(150000); +} - dev->rx_pointer = -1; +/* Upload all hardware settings at once. Used at load and resume time */ +static void ene_setup_hw_settings(struct ene_device *dev) +{ + if (dev->hw_learning_and_tx_capable) { + ene_tx_set_carrier(dev); + ene_tx_set_transmitters(dev); + } + ene_rx_setup(dev); } /* outside interface: called on first open*/ @@ -630,8 +844,6 @@ static int ene_open(void *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; @@ -645,7 +857,6 @@ static void ene_close(void *data) spin_lock_irqsave(&dev->hw_lock, flags); ene_rx_disable(dev); - dev->in_use = 0; spin_unlock_irqrestore(&dev->hw_lock, flags); } @@ -653,19 +864,17 @@ static void ene_close(void *data) 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); + dbg("TX: attempt to set transmitter mask %02x", tx_mask); /* invalid txmask */ - if (!tx_mask || tx_mask & ~0x3) { - ene_dbg("TX: invalid mask"); + if (!tx_mask || tx_mask & ~0x03) { + 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); + ene_tx_set_transmitters(dev); return 0; } @@ -673,66 +882,76 @@ static int ene_set_tx_mask(void *data, u32 tx_mask) 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); + u32 period = 2000000 / carrier; - if (period && (period > ENE_TX_PERIOD_MAX || - period < ENE_TX_PERIOD_MIN)) { + dbg("TX: attempt to set tx carrier to %d kHz", carrier); - ene_dbg("TX: out of range %d-%d carrier, " - "falling back to 32 kHz", - 1000 / ENE_TX_PERIOD_MIN, - 1000 / ENE_TX_PERIOD_MAX); + if (period && (period > ENE_CIRMOD_PRD_MAX || + period < ENE_CIRMOD_PRD_MIN)) { - period = 32; /* this is just a coincidence!!! */ + dbg("TX: out of range %d-%d kHz carrier", + 2000 / ENE_CIRMOD_PRD_MIN, 2000 / ENE_CIRMOD_PRD_MAX); + return -1; } - 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); + ene_tx_set_carrier(dev); return 0; } +/*outside interface : set tx duty cycle */ +static int ene_set_tx_duty_cycle(void *data, u32 duty_cycle) +{ + struct ene_device *dev = (struct ene_device *)data; + dbg("TX: setting duty cycle to %d%%", duty_cycle); + dev->tx_duty_cycle = duty_cycle; + ene_tx_set_carrier(dev); + 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) + if (enable == dev->learning_mode_enabled) return 0; spin_lock_irqsave(&dev->hw_lock, flags); - dev->learning_enabled = enable; - ene_rx_set_inputs(dev); + dev->learning_mode_enabled = enable; + ene_rx_disable(dev); + ene_rx_setup(dev); + ene_rx_enable(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) +static int ene_set_carrier_report(void *data, int enable) { struct ene_device *dev = (struct ene_device *)data; - ene_set_learning_mode(dev, - max > ENE_NORMAL_RX_HI || min < ENE_NORMAL_RX_LOW); + unsigned long flags; + + if (enable == dev->carrier_detect_enabled) + return 0; + + spin_lock_irqsave(&dev->hw_lock, flags); + dev->carrier_detect_enabled = enable; + ene_rx_disable(dev); + ene_rx_setup(dev); + ene_rx_enable(dev); + spin_unlock_irqrestore(&dev->hw_lock, flags); return 0; } /* outside interface: enable or disable idle mode */ -static void ene_rx_set_idle(void *data, int idle) +static void ene_set_idle(void *data, bool 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); + if (idle) { + ene_rx_reset((struct ene_device *)data); + dbg("RX: end of data"); + } } - /* outside interface: transmit */ static int ene_transmit(void *data, int *buf, u32 n) { @@ -747,12 +966,11 @@ static int ene_transmit(void *data, int *buf, u32 n) dev->tx_sample = 0; dev->tx_sample_pulse = 0; - ene_dbg("TX: %d samples", dev->tx_len); + 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); + ene_tx_enable(dev); /* Transmit first two samples */ ene_tx_sample(dev); @@ -761,16 +979,15 @@ static int ene_transmit(void *data, int *buf, u32 n) spin_unlock_irqrestore(&dev->hw_lock, flags); if (wait_for_completion_timeout(&dev->tx_complete, 2 * HZ) == 0) { - ene_dbg("TX: timeout"); + dbg("TX: timeout"); spin_lock_irqsave(&dev->hw_lock, flags); - ene_tx_complete(dev); + ene_tx_disable(dev); spin_unlock_irqrestore(&dev->hw_lock, flags); } else - ene_dbg("TX: done"); + dbg("TX: done"); return n; } - /* probe entry */ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id) { @@ -785,121 +1002,103 @@ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id) dev = kzalloc(sizeof(struct ene_device), GFP_KERNEL); if (!input_dev || !ir_props || !dev) - goto error; + goto error1; /* validate resources */ error = -ENODEV; if (!pnp_port_valid(pnp_dev, 0) || - pnp_port_len(pnp_dev, 0) < ENE_MAX_IO) + pnp_port_len(pnp_dev, 0) < ENE_IO_SIZE) 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)) + dev->hw_io = pnp_port_start(pnp_dev, 0); + if (!request_region(dev->hw_io, ENE_IO_SIZE, ENE_DRIVER_NAME)) { + dev->hw_io = -1; + dev->irq = -1; goto error; + } + dev->irq = pnp_irq(pnp_dev, 0); if (request_irq(dev->irq, ene_isr, - IRQF_SHARED, ENE_DRIVER_NAME, (void *)dev)) + IRQF_SHARED, ENE_DRIVER_NAME, (void *)dev)) { + dev->irq = -1; goto error; + } pnp_set_drvdata(pnp_dev, dev); dev->pnp_dev = pnp_dev; + /* don't allow too short/long sample periods */ + if (sample_period < 5 || sample_period > 0x7F) + sample_period = ENE_DEFAULT_SAMPLE_PERIOD; + /* 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; + dev->hw_learning_and_tx_capable = true; setup_timer(&dev->tx_sim_timer, ene_tx_irqsim, (long unsigned int)dev); - ene_printk(KERN_WARNING, - "Simulation of TX activated\n"); + ene_warn("Simulation of TX activated"); } + if (!dev->hw_learning_and_tx_capable) + learning_mode_force = false; + 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; - + ir_props->s_idle = ene_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; */ + ir_props->s_tx_duty_cycle = ene_set_tx_duty_cycle; + ir_props->s_carrier_report = ene_set_carrier_report; } + ene_rx_setup_hw_buffer(dev); + ene_setup_default_settings(dev); + ene_setup_hw_settings(dev); - device_set_wakeup_capable(&pnp_dev->dev, 1); - device_set_wakeup_enable(&pnp_dev->dev, 1); + device_set_wakeup_capable(&pnp_dev->dev, true); + device_set_wakeup_enable(&pnp_dev->dev, true); 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"); + ene_notice("driver has been succesfully loaded"); return 0; error: - if (dev->irq) + if (dev && dev->irq >= 0) free_irq(dev->irq, dev); - if (dev->hw_io) - release_region(dev->hw_io, ENE_MAX_IO); - + if (dev && dev->hw_io >= 0) + release_region(dev->hw_io, ENE_IO_SIZE); +error1: input_free_device(input_dev); kfree(ir_props); kfree(dev); @@ -914,10 +1113,11 @@ static void ene_remove(struct pnp_dev *pnp_dev) spin_lock_irqsave(&dev->hw_lock, flags); ene_rx_disable(dev); + ene_rx_restore_hw_buffer(dev); spin_unlock_irqrestore(&dev->hw_lock, flags); free_irq(dev->irq, dev); - release_region(dev->hw_io, ENE_MAX_IO); + release_region(dev->hw_io, ENE_IO_SIZE); ir_input_unregister(dev->idev); kfree(dev->props); kfree(dev); @@ -927,28 +1127,29 @@ static void ene_remove(struct pnp_dev *pnp_dev) 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); + dbg("wake on IR %s", enable ? "enabled" : "disabled"); + ene_set_clear_reg_mask(dev, ENE_FW1, ENE_FW1_WAKE, enable); } #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); + ene_enable_wake(dev, true); + + /* TODO: add support for wake pattern */ 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_setup_hw_settings(dev); + + if (dev->rx_enabled) ene_rx_enable(dev); - ene_enable_wake(dev, 0); + ene_enable_wake(dev, false); return 0; } #endif @@ -956,7 +1157,7 @@ static int ene_resume(struct pnp_dev *pnp_dev) static void ene_shutdown(struct pnp_dev *pnp_dev) { struct ene_device *dev = pnp_get_drvdata(pnp_dev); - ene_enable_wake(dev, 1); + ene_enable_wake(dev, true); } static const struct pnp_device_id ene_ids[] = { @@ -994,18 +1195,11 @@ static void ene_exit(void) 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(learning_mode_force, bool, S_IRUGO); +MODULE_PARM_DESC(learning_mode_force, "Enable learning mode by default"); module_param(debug, int, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(debug, "Enable debug (debug=2 verbose debug output)"); +MODULE_PARM_DESC(debug, "Debug level"); module_param(txsim, bool, S_IRUGO); MODULE_PARM_DESC(txsim, @@ -1013,8 +1207,8 @@ MODULE_PARM_DESC(txsim, MODULE_DEVICE_TABLE(pnp, ene_ids); MODULE_DESCRIPTION - ("Infrared input driver for KB3926B/KB3926C/KB3926D " - "(aka ENE0100/ENE0200/ENE0201) CIR port"); + ("Infrared input driver for KB3926B/C/D/E/F " + "(aka ENE0100/ENE0200/ENE0201/ENE0202) CIR port"); MODULE_AUTHOR("Maxim Levitsky"); MODULE_LICENSE("GPL"); diff --git a/drivers/media/IR/ene_ir.h b/drivers/media/IR/ene_ir.h index 54c76af0d033..f5870667a433 100644 --- a/drivers/media/IR/ene_ir.h +++ b/drivers/media/IR/ene_ir.h @@ -1,5 +1,5 @@ /* - * driver for ENE KB3926 B/C/D CIR (also known as ENE0XXX) + * driver for ENE KB3926 B/C/D/E/F CIR (also known as ENE0XXX) * * Copyright (C) 2010 Maxim Levitsky <maximlevitsky@gmail.com> * @@ -26,43 +26,50 @@ #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_IO_SIZE 4 + +/* 8 bytes of samples, divided in 2 packets*/ +#define ENE_FW_SAMPLE_BUFFER 0xF8F0 /* sample buffer */ +#define ENE_FW_SAMPLE_SPACE 0x80 /* sample is space */ +#define ENE_FW_PACKET_SIZE 4 + +/* first firmware flag register */ +#define ENE_FW1 0xF8F8 /* flagr */ #define ENE_FW1_ENABLE 0x01 /* enable fw processing */ #define ENE_FW1_TXIRQ 0x02 /* TX interrupt pending */ +#define ENE_FW1_HAS_EXTRA_BUF 0x04 /* fw uses extra buffer*/ +#define ENE_FW1_EXTRA_BUF_HND 0x08 /* extra buffer handshake bit*/ +#define ENE_FW1_LED_ON 0x10 /* turn on a led */ + +#define ENE_FW1_WPATTERN 0x20 /* enable wake pattern */ #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 */ +/* second firmware flag register */ +#define ENE_FW2 0xF8F9 /* flagw */ +#define ENE_FW2_BUF_WPTR 0x01 /* which half of the buffer to read */ +#define ENE_FW2_RXIRQ 0x04 /* RX IRQ pending*/ +#define ENE_FW2_GP0A 0x08 /* Use GPIO0A for demodulated input */ +#define ENE_FW2_EMMITER1_CONN 0x10 /* TX emmiter 1 connected */ +#define ENE_FW2_EMMITER2_CONN 0x20 /* TX emmiter 2 connected */ + +#define ENE_FW2_FAN_INPUT 0x40 /* fan input used for demodulated data*/ #define ENE_FW2_LEARNING 0x80 /* hardware supports learning and TX */ +/* firmware RX pointer for new style buffer */ +#define ENE_FW_RX_POINTER 0xF8FA + +/* high parts of samples for fan input (8 samples)*/ +#define ENE_FW_SMPL_BUF_FAN 0xF8FB +#define ENE_FW_SMPL_BUF_FAN_PLS 0x8000 /* combined sample is pulse */ +#define ENE_FW_SMPL_BUF_FAN_MSK 0x0FFF /* combined sample maximum value */ +#define ENE_FW_SAMPLE_PERIOD_FAN 61 /* fan input has fixed sample period */ + /* 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 +#define ENE_GPIOFS1 0xFC01 +#define ENE_GPIOFS1_GPIO0D 0x20 /* enable tx output on GPIO0D */ +#define ENE_GPIOFS8 0xFC08 +#define ENE_GPIOFS8_GPIO41 0x02 /* enable tx output on GPIO40 */ /* IRQ registers block (for revision B) */ #define ENEB_IRQ 0xFD09 /* IRQ number */ @@ -70,97 +77,99 @@ #define ENEB_IRQ_STATUS 0xFD80 /* irq status */ #define ENEB_IRQ_STATUS_IR 0x20 /* IR irq */ -/* fan as input settings - only if learning capable */ +/* fan as input settings */ #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 */ +#define ENE_IRQ 0xFE9B /* new irq settings register */ +#define ENE_IRQ_MASK 0x0F /* irq number mask */ +#define ENE_IRQ_UNK_EN 0x10 /* always enabled */ +#define ENE_IRQ_STATUS 0x20 /* irq status and ACK */ + +/* CIR Config register #1 */ +#define ENE_CIRCFG 0xFEC0 +#define ENE_CIRCFG_RX_EN 0x01 /* RX enable */ +#define ENE_CIRCFG_RX_IRQ 0x02 /* Enable hardware interrupt */ +#define ENE_CIRCFG_REV_POL 0x04 /* Input polarity reversed */ +#define ENE_CIRCFG_CARR_DEMOD 0x08 /* Enable carrier demodulator */ + +#define ENE_CIRCFG_TX_EN 0x10 /* TX enable */ +#define ENE_CIRCFG_TX_IRQ 0x20 /* Send interrupt on TX done */ +#define ENE_CIRCFG_TX_POL_REV 0x40 /* TX polarity reversed */ +#define ENE_CIRCFG_TX_CARR 0x80 /* send TX carrier or not */ + +/* CIR config register #2 */ +#define ENE_CIRCFG2 0xFEC1 +#define ENE_CIRCFG2_RLC 0x00 +#define ENE_CIRCFG2_RC5 0x01 +#define ENE_CIRCFG2_RC6 0x02 +#define ENE_CIRCFG2_NEC 0x03 +#define ENE_CIRCFG2_CARR_DETECT 0x10 /* Enable carrier detection */ +#define ENE_CIRCFG2_GPIO0A 0x20 /* Use GPIO0A instead of GPIO40 for input */ +#define ENE_CIRCFG2_FAST_SAMPL1 0x40 /* Fast leading pulse detection for RC6 */ +#define ENE_CIRCFG2_FAST_SAMPL2 0x80 /* Fast data detection for RC6 */ + +/* Knobs for protocol decoding - will document when/if will use them */ +#define ENE_CIRPF 0xFEC2 +#define ENE_CIRHIGH 0xFEC3 +#define ENE_CIRBIT 0xFEC4 +#define ENE_CIRSTART 0xFEC5 +#define ENE_CIRSTART2 0xFEC6 + +/* Actual register which contains RLC RX data - read by firmware */ +#define ENE_CIRDAT_IN 0xFEC7 + + +/* RLC configuration - sample period (1us resulution) + idle mode */ +#define ENE_CIRRLC_CFG 0xFEC8 +#define ENE_CIRRLC_CFG_OVERFLOW 0x80 /* interrupt on overflows if set */ +#define ENE_DEFAULT_SAMPLE_PERIOD 50 + +/* Two byte RLC TX buffer */ +#define ENE_CIRRLC_OUT0 0xFEC9 +#define ENE_CIRRLC_OUT1 0xFECA +#define ENE_CIRRLC_OUT_PULSE 0x80 /* Transmitted sample is pulse */ +#define ENE_CIRRLC_OUT_MASK 0x7F + + +/* Carrier detect setting + * Low nibble - number of carrier pulses to average + * High nibble - number of initial carrier pulses to discard + */ +#define ENE_CIRCAR_PULS 0xFECB +/* detected RX carrier period (resolution: 500 ns) */ +#define ENE_CIRCAR_PRD 0xFECC +#define ENE_CIRCAR_PRD_VALID 0x80 /* data valid content valid */ -/* Unknown TX setting - TX sample period ??? */ -#define ENE_TX_UNK1 0xFECB /* set to 0x63 */ +/* detected RX carrier pulse width (resolution: 500 ns) */ +#define ENE_CIRCAR_HPRD 0xFECD -/* Current received carrier period */ -#define ENE_RX_CARRIER 0xFECC /* RX period (500 ns) */ -#define ENE_RX_CARRIER_VALID 0x80 /* Register content valid */ +/* TX period (resolution: 500 ns, minimum 2)*/ +#define ENE_CIRMOD_PRD 0xFECE +#define ENE_CIRMOD_PRD_POL 0x80 /* TX carrier polarity*/ +#define ENE_CIRMOD_PRD_MAX 0x7F /* 15.87 kHz */ +#define ENE_CIRMOD_PRD_MIN 0x02 /* 1 Mhz */ -/* 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)*/ +/* TX pulse width (resolution: 500 ns)*/ +#define ENE_CIRMOD_HPRD 0xFECF /* Hardware versions */ -#define ENE_HW_VERSION 0xFF00 /* hardware revision */ +#define ENE_ECHV 0xFF00 /* hardware revision */ #define ENE_PLLFRH 0xFF16 #define ENE_PLLFRL 0xFF17 +#define ENE_DEFAULT_PLL_FREQ 1000 -#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 +#define ENE_ECSTS 0xFF1D +#define ENE_ECSTS_RSRVD 0x04 -/* 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_ECVER_MAJOR 0xFF1E /* chip version */ +#define ENE_ECVER_MINOR 0xFF1F +#define ENE_HW_VER_OLD 0xFD00 /******************************************************************************/ @@ -171,46 +180,60 @@ #define ENE_HW_B 1 /* 3926B */ #define ENE_HW_C 2 /* 3926C */ -#define ENE_HW_D 3 /* 3926D */ +#define ENE_HW_D 3 /* 3926D or later */ #define ene_printk(level, text, ...) \ - printk(level ENE_DRIVER_NAME ": " text, ## __VA_ARGS__) + printk(level ENE_DRIVER_NAME ": " text "\n", ## __VA_ARGS__) -#define ene_dbg(text, ...) \ - if (debug) \ - printk(KERN_DEBUG \ - ENE_DRIVER_NAME ": " text "\n" , ## __VA_ARGS__) +#define ene_notice(text, ...) ene_printk(KERN_NOTICE, text, ## __VA_ARGS__) +#define ene_warn(text, ...) ene_printk(KERN_WARNING, text, ## __VA_ARGS__) -#define ene_dbg_verbose(text, ...) \ - if (debug > 1) \ - printk(KERN_DEBUG \ - ENE_DRIVER_NAME ": " text "\n" , ## __VA_ARGS__) +#define __dbg(level, format, ...) \ + do { \ + if (debug >= level) \ + printk(KERN_DEBUG ENE_DRIVER_NAME \ + ": " format "\n", ## __VA_ARGS__); \ + } while (0) + + +#define dbg(format, ...) __dbg(1, format, ## __VA_ARGS__) +#define dbg_verbose(format, ...) __dbg(2, format, ## __VA_ARGS__) +#define dbg_regs(format, ...) __dbg(3, format, ## __VA_ARGS__) + +#define MS_TO_NS(msec) ((msec) * 1000) 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; + 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 */ + bool hw_use_gpio_0a; /* gpio0a is demodulated input*/ + bool hw_extra_buffer; /* hardware has 'extra buffer' */ + bool hw_fan_input; /* fan input is IR data source */ + bool hw_learning_and_tx_capable; /* learning & tx capable */ + int pll_freq; + int buffer_len; + + /* Extra RX buffer location */ + int extra_buf1_address; + int extra_buf1_len; + int extra_buf2_address; + int extra_buf2_len; + /* HW state*/ - int rx_pointer; /* hw pointer to rx buffer */ + int r_pointer; /* pointer to next sample to read */ + int w_pointer; /* pointer to next sample hw will write */ 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 */ @@ -229,7 +252,11 @@ struct ene_device { int transmitter_mask; /* RX settings */ - bool learning_enabled; /* learning input enabled */ + bool learning_mode_enabled; /* learning input enabled */ bool carrier_detect_enabled; /* carrier detect enabled */ int rx_period_adjust; + bool rx_enabled; }; + +static int ene_irq_status(struct ene_device *dev); +static void ene_rx_read_hw_pointer(struct ene_device *dev); diff --git a/drivers/media/IR/imon.c b/drivers/media/IR/imon.c index faed5a332c71..bc118066bc38 100644 --- a/drivers/media/IR/imon.c +++ b/drivers/media/IR/imon.c @@ -1,7 +1,7 @@ /* * imon.c: input and display driver for SoundGraph iMON IR/VFD/LCD * - * Copyright(C) 2009 Jarod Wilson <jarod@wilsonet.com> + * Copyright(C) 2010 Jarod Wilson <jarod@wilsonet.com> * Portions based on the original lirc_imon driver, * Copyright(C) 2004 Venky Raju(dev@venky.ws) * @@ -26,6 +26,8 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__ + #include <linux/errno.h> #include <linux/init.h> #include <linux/kernel.h> @@ -44,7 +46,7 @@ #define MOD_AUTHOR "Jarod Wilson <jarod@wilsonet.com>" #define MOD_DESC "Driver for SoundGraph iMON MultiMedia IR/Display" #define MOD_NAME "imon" -#define MOD_VERSION "0.9.1" +#define MOD_VERSION "0.9.2" #define DISPLAY_MINOR_BASE 144 #define DEVICE_NAME "lcd%d" @@ -121,21 +123,26 @@ struct imon_context { u16 vendor; /* usb vendor ID */ u16 product; /* usb product ID */ - struct input_dev *idev; /* input device for remote */ + struct input_dev *rdev; /* input device for remote */ + struct input_dev *idev; /* input device for panel & IR mouse */ struct input_dev *touch; /* input device for touchscreen */ + spinlock_t kc_lock; /* make sure we get keycodes right */ u32 kc; /* current input keycode */ u32 last_keycode; /* last reported input keycode */ + u32 rc_scancode; /* the computed remote scancode */ + u8 rc_toggle; /* the computed remote toggle bit */ u64 ir_type; /* iMON or MCE (RC6) IR protocol? */ - u8 mce_toggle_bit; /* last mce toggle bit */ bool release_code; /* some keys send a release code */ u8 display_type; /* store the display type */ bool pad_mouse; /* toggle kbd(0)/mouse(1) mode */ + char name_rdev[128]; /* rc input device name */ + char phys_rdev[64]; /* rc input device phys path */ + char name_idev[128]; /* input device name */ char phys_idev[64]; /* input device phys path */ - struct timer_list itimer; /* input device timer, need for rc6 */ char name_touch[128]; /* touch screen name */ char phys_touch[64]; /* touch screen phys path */ @@ -289,6 +296,9 @@ static const struct { { 0x000100000000ffeell, KEY_VOLUMEUP }, { 0x010000000000ffeell, KEY_VOLUMEDOWN }, { 0x000000000100ffeell, KEY_MUTE }, + /* 0xffdc iMON MCE VFD */ + { 0x00010000ffffffeell, KEY_VOLUMEUP }, + { 0x01000000ffffffeell, KEY_VOLUMEDOWN }, /* iMON Knob values */ { 0x000100ffffffffeell, KEY_VOLUMEUP }, { 0x010000ffffffffeell, KEY_VOLUMEDOWN }, @@ -307,7 +317,7 @@ MODULE_DEVICE_TABLE(usb, imon_usb_id_table); static bool debug; module_param(debug, bool, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(debug, "Debug messages: 0=no, 1=yes(default: no)"); +MODULE_PARM_DESC(debug, "Debug messages: 0=no, 1=yes (default: no)"); /* lcd, vfd, vga or none? should be auto-detected, but can be overridden... */ static int display_type; @@ -365,15 +375,14 @@ static int display_open(struct inode *inode, struct file *file) subminor = iminor(inode); interface = usb_find_interface(&imon_driver, subminor); if (!interface) { - err("%s: could not find interface for minor %d", - __func__, subminor); + pr_err("could not find interface for minor %d\n", subminor); retval = -ENODEV; goto exit; } ictx = usb_get_intfdata(interface); if (!ictx) { - err("%s: no context found for minor %d", __func__, subminor); + pr_err("no context found for minor %d\n", subminor); retval = -ENODEV; goto exit; } @@ -381,10 +390,10 @@ static int display_open(struct inode *inode, struct file *file) mutex_lock(&ictx->lock); if (!ictx->display_supported) { - err("%s: display not supported by device", __func__); + pr_err("display not supported by device\n"); retval = -ENODEV; } else if (ictx->display_isopen) { - err("%s: display port is already open", __func__); + pr_err("display port is already open\n"); retval = -EBUSY; } else { ictx->display_isopen = true; @@ -411,17 +420,17 @@ static int display_close(struct inode *inode, struct file *file) ictx = file->private_data; if (!ictx) { - err("%s: no context for device", __func__); + pr_err("no context for device\n"); return -ENODEV; } mutex_lock(&ictx->lock); if (!ictx->display_supported) { - err("%s: display not supported by device", __func__); + pr_err("display not supported by device\n"); retval = -ENODEV; } else if (!ictx->display_isopen) { - err("%s: display is not open", __func__); + pr_err("display is not open\n"); retval = -EIO; } else { ictx->display_isopen = false; @@ -500,19 +509,19 @@ static int send_packet(struct imon_context *ictx) if (retval) { ictx->tx.busy = false; smp_rmb(); /* ensure later readers know we're not busy */ - err("%s: error submitting urb(%d)", __func__, retval); + pr_err("error submitting urb(%d)\n", retval); } else { /* Wait for transmission to complete (or abort) */ mutex_unlock(&ictx->lock); retval = wait_for_completion_interruptible( &ictx->tx.finished); if (retval) - err("%s: task interrupted", __func__); + pr_err("task interrupted\n"); mutex_lock(&ictx->lock); retval = ictx->tx.status; if (retval) - err("%s: packet tx failed (%d)", __func__, retval); + pr_err("packet tx failed (%d)\n", retval); } kfree(control_req); @@ -544,12 +553,12 @@ static int send_associate_24g(struct imon_context *ictx) 0x00, 0x00, 0x00, 0x20 }; if (!ictx) { - err("%s: no context for device", __func__); + pr_err("no context for device\n"); return -ENODEV; } if (!ictx->dev_present_intf0) { - err("%s: no iMON device present", __func__); + pr_err("no iMON device present\n"); return -ENODEV; } @@ -577,7 +586,7 @@ static int send_set_imon_clock(struct imon_context *ictx, int i; if (!ictx) { - err("%s: no context for device", __func__); + pr_err("no context for device\n"); return -ENODEV; } @@ -638,8 +647,7 @@ static int send_set_imon_clock(struct imon_context *ictx, memcpy(ictx->usb_tx_buf, clock_enable_pkt[i], 8); retval = send_packet(ictx); if (retval) { - err("%s: send_packet failed for packet %d", - __func__, i); + pr_err("send_packet failed for packet %d\n", i); break; } } @@ -778,7 +786,7 @@ static struct attribute *imon_display_sysfs_entries[] = { NULL }; -static struct attribute_group imon_display_attribute_group = { +static struct attribute_group imon_display_attr_group = { .attrs = imon_display_sysfs_entries }; @@ -787,7 +795,7 @@ static struct attribute *imon_rf_sysfs_entries[] = { NULL }; -static struct attribute_group imon_rf_attribute_group = { +static struct attribute_group imon_rf_attr_group = { .attrs = imon_rf_sysfs_entries }; @@ -815,20 +823,20 @@ static ssize_t vfd_write(struct file *file, const char *buf, ictx = file->private_data; if (!ictx) { - err("%s: no context for device", __func__); + pr_err("no context for device\n"); return -ENODEV; } mutex_lock(&ictx->lock); if (!ictx->dev_present_intf0) { - err("%s: no iMON device present", __func__); + pr_err("no iMON device present\n"); retval = -ENODEV; goto exit; } if (n_bytes <= 0 || n_bytes > 32) { - err("%s: invalid payload size", __func__); + pr_err("invalid payload size\n"); retval = -EINVAL; goto exit; } @@ -854,8 +862,7 @@ static ssize_t vfd_write(struct file *file, const char *buf, retval = send_packet(ictx); if (retval) { - err("%s: send packet failed for packet #%d", - __func__, seq/2); + pr_err("send packet failed for packet #%d\n", seq / 2); goto exit; } else { seq += 2; @@ -869,8 +876,7 @@ static ssize_t vfd_write(struct file *file, const char *buf, ictx->usb_tx_buf[7] = (unsigned char) seq; retval = send_packet(ictx); if (retval) - err("%s: send packet failed for packet #%d", - __func__, seq / 2); + pr_err("send packet failed for packet #%d\n", seq / 2); exit: mutex_unlock(&ictx->lock); @@ -899,21 +905,20 @@ static ssize_t lcd_write(struct file *file, const char *buf, ictx = file->private_data; if (!ictx) { - err("%s: no context for device", __func__); + pr_err("no context for device\n"); return -ENODEV; } mutex_lock(&ictx->lock); if (!ictx->display_supported) { - err("%s: no iMON display present", __func__); + pr_err("no iMON display present\n"); retval = -ENODEV; goto exit; } if (n_bytes != 8) { - err("%s: invalid payload size: %d (expecting 8)", - __func__, (int) n_bytes); + pr_err("invalid payload size: %d (expected 8)\n", (int)n_bytes); retval = -EINVAL; goto exit; } @@ -925,7 +930,7 @@ static ssize_t lcd_write(struct file *file, const char *buf, retval = send_packet(ictx); if (retval) { - err("%s: send packet failed!", __func__); + pr_err("send packet failed!\n"); goto exit; } else { dev_dbg(ictx->dev, "%s: write %d bytes to LCD\n", @@ -958,17 +963,6 @@ static void usb_tx_callback(struct urb *urb) } /** - * mce/rc6 keypresses have no distinct release code, use timer - */ -static void imon_mce_timeout(unsigned long data) -{ - struct imon_context *ictx = (struct imon_context *)data; - - input_report_key(ictx->idev, ictx->last_keycode, 0); - input_sync(ictx->idev); -} - -/** * report touchscreen input */ static void imon_touch_display_timeout(unsigned long data) @@ -1008,14 +1002,11 @@ int imon_ir_change_protocol(void *priv, u64 ir_type) dev_dbg(dev, "Configuring IR receiver for MCE protocol\n"); ir_proto_packet[0] = 0x01; pad_mouse = false; - init_timer(&ictx->itimer); - ictx->itimer.data = (unsigned long)ictx; - ictx->itimer.function = imon_mce_timeout; break; case IR_TYPE_UNKNOWN: case IR_TYPE_OTHER: dev_dbg(dev, "Configuring IR receiver for iMON protocol\n"); - if (pad_stabilize) + if (pad_stabilize && !nomouse) pad_mouse = true; else { dev_dbg(dev, "PAD stabilize functionality disabled\n"); @@ -1027,7 +1018,7 @@ int imon_ir_change_protocol(void *priv, u64 ir_type) default: dev_warn(dev, "Unsupported IR protocol specified, overriding " "to iMON IR protocol\n"); - if (pad_stabilize) + if (pad_stabilize && !nomouse) pad_mouse = true; else { dev_dbg(dev, "PAD stabilize functionality disabled\n"); @@ -1149,20 +1140,21 @@ static int stabilize(int a, int b, u16 timeout, u16 threshold) return result; } -static u32 imon_remote_key_lookup(struct imon_context *ictx, u32 hw_code) +static u32 imon_remote_key_lookup(struct imon_context *ictx, u32 scancode) { - u32 scancode = be32_to_cpu(hw_code); u32 keycode; u32 release; bool is_release_code = false; /* Look for the initial press of a button */ - keycode = ir_g_keycode_from_table(ictx->idev, scancode); + keycode = ir_g_keycode_from_table(ictx->rdev, scancode); + ictx->rc_toggle = 0x0; + ictx->rc_scancode = scancode; /* Look for the release of a button */ if (keycode == KEY_RESERVED) { release = scancode & ~0x4000; - keycode = ir_g_keycode_from_table(ictx->idev, release); + keycode = ir_g_keycode_from_table(ictx->rdev, release); if (keycode != KEY_RESERVED) is_release_code = true; } @@ -1172,9 +1164,8 @@ static u32 imon_remote_key_lookup(struct imon_context *ictx, u32 hw_code) return keycode; } -static u32 imon_mce_key_lookup(struct imon_context *ictx, u32 hw_code) +static u32 imon_mce_key_lookup(struct imon_context *ictx, u32 scancode) { - u32 scancode = be32_to_cpu(hw_code); u32 keycode; #define MCE_KEY_MASK 0x7000 @@ -1188,18 +1179,21 @@ static u32 imon_mce_key_lookup(struct imon_context *ictx, u32 hw_code) * but we can't or them into all codes, as some keys are decoded in * a different way w/o the same use of the toggle bit... */ - if ((scancode >> 24) & 0x80) + if (scancode & 0x80000000) scancode = scancode | MCE_KEY_MASK | MCE_TOGGLE_BIT; - keycode = ir_g_keycode_from_table(ictx->idev, scancode); + ictx->rc_scancode = scancode; + keycode = ir_g_keycode_from_table(ictx->rdev, scancode); + + /* not used in mce mode, but make sure we know its false */ + ictx->release_code = false; return keycode; } -static u32 imon_panel_key_lookup(u64 hw_code) +static u32 imon_panel_key_lookup(u64 code) { int i; - u64 code = be64_to_cpu(hw_code); u32 keycode = KEY_RESERVED; for (i = 0; i < ARRAY_SIZE(imon_panel_key_table); i++) { @@ -1219,6 +1213,9 @@ static bool imon_mouse_event(struct imon_context *ictx, u8 right_shift = 1; bool mouse_input = true; int dir = 0; + unsigned long flags; + + spin_lock_irqsave(&ictx->kc_lock, flags); /* newer iMON device PAD or mouse button */ if (ictx->product != 0xffdc && (buf[0] & 0x01) && len == 5) { @@ -1250,6 +1247,8 @@ static bool imon_mouse_event(struct imon_context *ictx, } else mouse_input = false; + spin_unlock_irqrestore(&ictx->kc_lock, flags); + if (mouse_input) { dev_dbg(ictx->dev, "sending mouse data via input subsystem\n"); @@ -1264,7 +1263,9 @@ static bool imon_mouse_event(struct imon_context *ictx, buf[1] >> right_shift & 0x1); } input_sync(ictx->idev); + spin_lock_irqsave(&ictx->kc_lock, flags); ictx->last_keycode = ictx->kc; + spin_unlock_irqrestore(&ictx->kc_lock, flags); } return mouse_input; @@ -1286,8 +1287,8 @@ static void imon_pad_to_keys(struct imon_context *ictx, unsigned char *buf) int dir = 0; char rel_x = 0x00, rel_y = 0x00; u16 timeout, threshold; - u64 temp_key; - u32 remote_key; + u32 scancode = KEY_RESERVED; + unsigned long flags; /* * The imon directional pad functions more like a touchpad. Bytes 3 & 4 @@ -1311,26 +1312,36 @@ static void imon_pad_to_keys(struct imon_context *ictx, unsigned char *buf) dir = stabilize((int)rel_x, (int)rel_y, timeout, threshold); if (!dir) { + spin_lock_irqsave(&ictx->kc_lock, + flags); ictx->kc = KEY_UNKNOWN; + spin_unlock_irqrestore(&ictx->kc_lock, + flags); return; } buf[2] = dir & 0xFF; buf[3] = (dir >> 8) & 0xFF; - memcpy(&temp_key, buf, sizeof(temp_key)); - remote_key = (u32) (le64_to_cpu(temp_key) - & 0xffffffff); - ictx->kc = imon_remote_key_lookup(ictx, - remote_key); + scancode = be32_to_cpu(*((u32 *)buf)); } } else { + /* + * Hack alert: instead of using keycodes, we have + * to use hard-coded scancodes here... + */ if (abs(rel_y) > abs(rel_x)) { buf[2] = (rel_y > 0) ? 0x7F : 0x80; buf[3] = 0; - ictx->kc = (rel_y > 0) ? KEY_DOWN : KEY_UP; + if (rel_y > 0) + scancode = 0x01007f00; /* KEY_DOWN */ + else + scancode = 0x01008000; /* KEY_UP */ } else { buf[2] = 0; buf[3] = (rel_x > 0) ? 0x7F : 0x80; - ictx->kc = (rel_x > 0) ? KEY_RIGHT : KEY_LEFT; + if (rel_x > 0) + scancode = 0x0100007f; /* KEY_RIGHT */ + else + scancode = 0x01000080; /* KEY_LEFT */ } } @@ -1367,34 +1378,56 @@ static void imon_pad_to_keys(struct imon_context *ictx, unsigned char *buf) dir = stabilize((int)rel_x, (int)rel_y, timeout, threshold); if (!dir) { + spin_lock_irqsave(&ictx->kc_lock, flags); ictx->kc = KEY_UNKNOWN; + spin_unlock_irqrestore(&ictx->kc_lock, flags); return; } buf[2] = dir & 0xFF; buf[3] = (dir >> 8) & 0xFF; - memcpy(&temp_key, buf, sizeof(temp_key)); - remote_key = (u32) (le64_to_cpu(temp_key) & 0xffffffff); - ictx->kc = imon_remote_key_lookup(ictx, remote_key); + scancode = be32_to_cpu(*((u32 *)buf)); } else { + /* + * Hack alert: instead of using keycodes, we have + * to use hard-coded scancodes here... + */ if (abs(rel_y) > abs(rel_x)) { buf[2] = (rel_y > 0) ? 0x7F : 0x80; buf[3] = 0; - ictx->kc = (rel_y > 0) ? KEY_DOWN : KEY_UP; + if (rel_y > 0) + scancode = 0x01007f00; /* KEY_DOWN */ + else + scancode = 0x01008000; /* KEY_UP */ } else { buf[2] = 0; buf[3] = (rel_x > 0) ? 0x7F : 0x80; - ictx->kc = (rel_x > 0) ? KEY_RIGHT : KEY_LEFT; + if (rel_x > 0) + scancode = 0x0100007f; /* KEY_RIGHT */ + else + scancode = 0x01000080; /* KEY_LEFT */ } } } + + if (scancode) { + spin_lock_irqsave(&ictx->kc_lock, flags); + ictx->kc = imon_remote_key_lookup(ictx, scancode); + spin_unlock_irqrestore(&ictx->kc_lock, flags); + } } +/** + * figure out if these is a press or a release. We don't actually + * care about repeats, as those will be auto-generated within the IR + * subsystem for repeating scancodes. + */ static int imon_parse_press_type(struct imon_context *ictx, unsigned char *buf, u8 ktype) { int press_type = 0; - int rep_delay = ictx->idev->rep[REP_DELAY]; - int rep_period = ictx->idev->rep[REP_PERIOD]; + unsigned long flags; + + spin_lock_irqsave(&ictx->kc_lock, flags); /* key release of 0x02XXXXXX key */ if (ictx->kc == KEY_RESERVED && buf[0] == 0x02 && buf[3] == 0x00) @@ -1410,22 +1443,10 @@ static int imon_parse_press_type(struct imon_context *ictx, buf[2] == 0x81 && buf[3] == 0xb7) ictx->kc = ictx->last_keycode; - /* mce-specific button handling */ + /* mce-specific button handling, no keyup events */ else if (ktype == IMON_KEY_MCE) { - /* initial press */ - if (ictx->kc != ictx->last_keycode - || buf[2] != ictx->mce_toggle_bit) { - ictx->last_keycode = ictx->kc; - ictx->mce_toggle_bit = buf[2]; - press_type = 1; - mod_timer(&ictx->itimer, - jiffies + msecs_to_jiffies(rep_delay)); - /* repeat */ - } else { - press_type = 2; - mod_timer(&ictx->itimer, - jiffies + msecs_to_jiffies(rep_period)); - } + ictx->rc_toggle = buf[2]; + press_type = 1; /* incoherent or irrelevant data */ } else if (ictx->kc == KEY_RESERVED) @@ -1439,6 +1460,8 @@ static int imon_parse_press_type(struct imon_context *ictx, else press_type = 1; + spin_unlock_irqrestore(&ictx->kc_lock, flags); + return press_type; } @@ -1451,41 +1474,45 @@ static void imon_incoming_packet(struct imon_context *ictx, int len = urb->actual_length; unsigned char *buf = urb->transfer_buffer; struct device *dev = ictx->dev; + unsigned long flags; u32 kc; bool norelease = false; int i; - u64 temp_key; - u64 panel_key = 0; - u32 remote_key = 0; - struct input_dev *idev = NULL; + u64 scancode; + struct input_dev *rdev = NULL; + struct ir_input_dev *irdev = NULL; int press_type = 0; int msec; struct timeval t; static struct timeval prev_time = { 0, 0 }; - u8 ktype = IMON_KEY_IMON; + u8 ktype; - idev = ictx->idev; + rdev = ictx->rdev; + irdev = input_get_drvdata(rdev); /* filter out junk data on the older 0xffdc imon devices */ if ((buf[0] == 0xff) && (buf[1] == 0xff) && (buf[2] == 0xff)) return; /* Figure out what key was pressed */ - memcpy(&temp_key, buf, sizeof(temp_key)); if (len == 8 && buf[7] == 0xee) { + scancode = be64_to_cpu(*((u64 *)buf)); ktype = IMON_KEY_PANEL; - panel_key = le64_to_cpu(temp_key); - kc = imon_panel_key_lookup(panel_key); + kc = imon_panel_key_lookup(scancode); } else { - remote_key = (u32) (le64_to_cpu(temp_key) & 0xffffffff); + scancode = be32_to_cpu(*((u32 *)buf)); if (ictx->ir_type == IR_TYPE_RC6) { + ktype = IMON_KEY_IMON; if (buf[0] == 0x80) ktype = IMON_KEY_MCE; - kc = imon_mce_key_lookup(ictx, remote_key); - } else - kc = imon_remote_key_lookup(ictx, remote_key); + kc = imon_mce_key_lookup(ictx, scancode); + } else { + ktype = IMON_KEY_IMON; + kc = imon_remote_key_lookup(ictx, scancode); + } } + spin_lock_irqsave(&ictx->kc_lock, flags); /* keyboard/mouse mode toggle button */ if (kc == KEY_KEYBOARD && !ictx->release_code) { ictx->last_keycode = kc; @@ -1493,6 +1520,7 @@ static void imon_incoming_packet(struct imon_context *ictx, ictx->pad_mouse = ~(ictx->pad_mouse) & 0x1; dev_dbg(dev, "toggling to %s mode\n", ictx->pad_mouse ? "mouse" : "keyboard"); + spin_unlock_irqrestore(&ictx->kc_lock, flags); return; } else { ictx->pad_mouse = 0; @@ -1501,11 +1529,13 @@ static void imon_incoming_packet(struct imon_context *ictx, } ictx->kc = kc; + spin_unlock_irqrestore(&ictx->kc_lock, flags); /* send touchscreen events through input subsystem if touchpad data */ if (ictx->display_type == IMON_DISPLAY_TYPE_VGA && len == 8 && buf[7] == 0x86) { imon_touch_event(ictx, buf); + return; /* look for mouse events with pad in mouse mode */ } else if (ictx->pad_mouse) { @@ -1533,36 +1563,55 @@ static void imon_incoming_packet(struct imon_context *ictx, if (press_type < 0) goto not_input_data; + spin_lock_irqsave(&ictx->kc_lock, flags); if (ictx->kc == KEY_UNKNOWN) goto unknown_key; + spin_unlock_irqrestore(&ictx->kc_lock, flags); + + if (ktype != IMON_KEY_PANEL) { + if (press_type == 0) + ir_keyup(irdev); + else { + ir_keydown(rdev, ictx->rc_scancode, ictx->rc_toggle); + spin_lock_irqsave(&ictx->kc_lock, flags); + ictx->last_keycode = ictx->kc; + spin_unlock_irqrestore(&ictx->kc_lock, flags); + } + return; + } - /* KEY_MUTE repeats from MCE and knob need to be suppressed */ - if ((ictx->kc == KEY_MUTE && ictx->kc == ictx->last_keycode) - && (buf[7] == 0xee || ktype == IMON_KEY_MCE)) { + /* Only panel type events left to process now */ + spin_lock_irqsave(&ictx->kc_lock, flags); + + /* KEY_MUTE repeats from knob need to be suppressed */ + if (ictx->kc == KEY_MUTE && ictx->kc == ictx->last_keycode) { do_gettimeofday(&t); msec = tv2int(&t, &prev_time); prev_time = t; - if (msec < idev->rep[REP_DELAY]) + if (msec < ictx->idev->rep[REP_DELAY]) { + spin_unlock_irqrestore(&ictx->kc_lock, flags); return; + } } + kc = ictx->kc; - input_report_key(idev, ictx->kc, press_type); - input_sync(idev); + spin_unlock_irqrestore(&ictx->kc_lock, flags); - /* panel keys and some remote keys don't generate a release */ - if (panel_key || norelease) { - input_report_key(idev, ictx->kc, 0); - input_sync(idev); - } + input_report_key(ictx->idev, kc, press_type); + input_sync(ictx->idev); - ictx->last_keycode = ictx->kc; + /* panel keys don't generate a release */ + input_report_key(ictx->idev, kc, 0); + input_sync(ictx->idev); + + ictx->last_keycode = kc; return; unknown_key: + spin_unlock_irqrestore(&ictx->kc_lock, flags); dev_info(dev, "%s: unknown keypress, code 0x%llx\n", __func__, - (panel_key ? be64_to_cpu(panel_key) : - be32_to_cpu(remote_key))); + (long long)scancode); return; not_input_data: @@ -1653,31 +1702,205 @@ static void usb_rx_callback_intf1(struct urb *urb) usb_submit_urb(ictx->rx_urb_intf1, GFP_ATOMIC); } +/* + * The 0x15c2:0xffdc device ID was used for umpteen different imon + * devices, and all of them constantly spew interrupts, even when there + * is no actual data to report. However, byte 6 of this buffer looks like + * its unique across device variants, so we're trying to key off that to + * figure out which display type (if any) and what IR protocol the device + * actually supports. These devices have their IR protocol hard-coded into + * their firmware, they can't be changed on the fly like the newer hardware. + */ +static void imon_get_ffdc_type(struct imon_context *ictx) +{ + u8 ffdc_cfg_byte = ictx->usb_rx_buf[6]; + u8 detected_display_type = IMON_DISPLAY_TYPE_NONE; + u64 allowed_protos = IR_TYPE_OTHER; + + switch (ffdc_cfg_byte) { + /* iMON Knob, no display, iMON IR + vol knob */ + case 0x21: + dev_info(ictx->dev, "0xffdc iMON Knob, iMON IR"); + ictx->display_supported = false; + break; + /* iMON 2.4G LT (usb stick), no display, iMON RF */ + case 0x4e: + dev_info(ictx->dev, "0xffdc iMON 2.4G LT, iMON RF"); + ictx->display_supported = false; + ictx->rf_device = true; + break; + /* iMON VFD, no IR (does have vol knob tho) */ + case 0x35: + dev_info(ictx->dev, "0xffdc iMON VFD + knob, no IR"); + detected_display_type = IMON_DISPLAY_TYPE_VFD; + break; + /* iMON VFD, iMON IR */ + case 0x24: + case 0x85: + dev_info(ictx->dev, "0xffdc iMON VFD, iMON IR"); + detected_display_type = IMON_DISPLAY_TYPE_VFD; + break; + /* iMON VFD, MCE IR */ + case 0x9e: + dev_info(ictx->dev, "0xffdc iMON VFD, MCE IR"); + detected_display_type = IMON_DISPLAY_TYPE_VFD; + allowed_protos = IR_TYPE_RC6; + break; + /* iMON LCD, MCE IR */ + case 0x9f: + dev_info(ictx->dev, "0xffdc iMON LCD, MCE IR"); + detected_display_type = IMON_DISPLAY_TYPE_LCD; + allowed_protos = IR_TYPE_RC6; + break; + default: + dev_info(ictx->dev, "Unknown 0xffdc device, " + "defaulting to VFD and iMON IR"); + detected_display_type = IMON_DISPLAY_TYPE_VFD; + break; + } + + printk(KERN_CONT " (id 0x%02x)\n", ffdc_cfg_byte); + + ictx->display_type = detected_display_type; + ictx->props->allowed_protos = allowed_protos; + ictx->ir_type = allowed_protos; +} + +static void imon_set_display_type(struct imon_context *ictx) +{ + u8 configured_display_type = IMON_DISPLAY_TYPE_VFD; + + /* + * 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) { + switch (ictx->product) { + case 0xffdc: + /* set in imon_get_ffdc_type() */ + configured_display_type = ictx->display_type; + break; + case 0x0034: + case 0x0035: + configured_display_type = IMON_DISPLAY_TYPE_VGA; + break; + case 0x0038: + case 0x0039: + case 0x0045: + configured_display_type = IMON_DISPLAY_TYPE_LCD; + break; + case 0x003c: + case 0x0041: + case 0x0042: + case 0x0043: + configured_display_type = IMON_DISPLAY_TYPE_NONE; + ictx->display_supported = false; + break; + case 0x0036: + case 0x0044: + default: + configured_display_type = IMON_DISPLAY_TYPE_VFD; + break; + } + } else { + configured_display_type = display_type; + if (display_type == IMON_DISPLAY_TYPE_NONE) + ictx->display_supported = false; + else + ictx->display_supported = true; + dev_info(ictx->dev, "%s: overriding display type to %d via " + "modparam\n", __func__, display_type); + } + + ictx->display_type = configured_display_type; +} + +static struct input_dev *imon_init_rdev(struct imon_context *ictx) +{ + struct input_dev *rdev; + struct ir_dev_props *props; + int ret; + char *ir_codes = NULL; + const unsigned char fp_packet[] = { 0x40, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x88 }; + + rdev = input_allocate_device(); + props = kzalloc(sizeof(*props), GFP_KERNEL); + if (!rdev || !props) { + dev_err(ictx->dev, "remote control dev allocation failed\n"); + goto out; + } + + snprintf(ictx->name_rdev, sizeof(ictx->name_rdev), + "iMON Remote (%04x:%04x)", ictx->vendor, ictx->product); + usb_make_path(ictx->usbdev_intf0, ictx->phys_rdev, + sizeof(ictx->phys_rdev)); + strlcat(ictx->phys_rdev, "/input0", sizeof(ictx->phys_rdev)); + + rdev->name = ictx->name_rdev; + rdev->phys = ictx->phys_rdev; + usb_to_input_id(ictx->usbdev_intf0, &rdev->id); + rdev->dev.parent = ictx->dev; + rdev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); + input_set_drvdata(rdev, ictx); + + props->priv = ictx; + props->driver_type = RC_DRIVER_SCANCODE; + props->allowed_protos = IR_TYPE_OTHER | IR_TYPE_RC6; /* iMON PAD or MCE */ + props->change_protocol = imon_ir_change_protocol; + ictx->props = props; + + /* Enable front-panel buttons and/or knobs */ + memcpy(ictx->usb_tx_buf, &fp_packet, sizeof(fp_packet)); + ret = send_packet(ictx); + /* Not fatal, but warn about it */ + if (ret) + dev_info(ictx->dev, "panel buttons/knobs setup failed\n"); + + if (ictx->product == 0xffdc) + imon_get_ffdc_type(ictx); + + imon_set_display_type(ictx); + + if (ictx->ir_type == IR_TYPE_RC6) + ir_codes = RC_MAP_IMON_MCE; + else + ir_codes = RC_MAP_IMON_PAD; + + ret = ir_input_register(rdev, ir_codes, props, MOD_NAME); + if (ret < 0) { + dev_err(ictx->dev, "remote input dev register failed\n"); + goto out; + } + + return rdev; + +out: + kfree(props); + input_free_device(rdev); + return NULL; +} + static struct input_dev *imon_init_idev(struct imon_context *ictx) { struct input_dev *idev; - struct ir_dev_props *props; int ret, i; idev = input_allocate_device(); if (!idev) { - dev_err(ictx->dev, "remote input dev allocation failed\n"); - goto idev_alloc_failed; - } - - props = kzalloc(sizeof(struct ir_dev_props), GFP_KERNEL); - if (!props) { - dev_err(ictx->dev, "remote ir dev props allocation failed\n"); - goto props_alloc_failed; + dev_err(ictx->dev, "input dev allocation failed\n"); + goto out; } snprintf(ictx->name_idev, sizeof(ictx->name_idev), - "iMON Remote (%04x:%04x)", ictx->vendor, ictx->product); + "iMON Panel, Knob and Mouse(%04x:%04x)", + ictx->vendor, ictx->product); idev->name = ictx->name_idev; usb_make_path(ictx->usbdev_intf0, ictx->phys_idev, sizeof(ictx->phys_idev)); - strlcat(ictx->phys_idev, "/input0", sizeof(ictx->phys_idev)); + strlcat(ictx->phys_idev, "/input1", sizeof(ictx->phys_idev)); idev->phys = ictx->phys_idev; idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP) | BIT_MASK(EV_REL); @@ -1693,30 +1916,20 @@ static struct input_dev *imon_init_idev(struct imon_context *ictx) __set_bit(kc, idev->keybit); } - props->priv = ictx; - props->driver_type = RC_DRIVER_SCANCODE; - /* IR_TYPE_OTHER maps to iMON PAD remote, IR_TYPE_RC6 to MCE remote */ - props->allowed_protos = IR_TYPE_OTHER | IR_TYPE_RC6; - props->change_protocol = imon_ir_change_protocol; - ictx->props = props; - usb_to_input_id(ictx->usbdev_intf0, &idev->id); idev->dev.parent = ictx->dev; + input_set_drvdata(idev, ictx); - ret = ir_input_register(idev, RC_MAP_IMON_PAD, props, MOD_NAME); + ret = input_register_device(idev); if (ret < 0) { - dev_err(ictx->dev, "remote input dev register failed\n"); - goto idev_register_failed; + dev_err(ictx->dev, "input dev register failed\n"); + goto out; } return idev; -idev_register_failed: - kfree(props); -props_alloc_failed: +out: input_free_device(idev); -idev_alloc_failed: - return NULL; } @@ -1738,7 +1951,7 @@ static struct input_dev *imon_init_touch(struct imon_context *ictx) usb_make_path(ictx->usbdev_intf1, ictx->phys_touch, sizeof(ictx->phys_touch)); - strlcat(ictx->phys_touch, "/input1", sizeof(ictx->phys_touch)); + strlcat(ictx->phys_touch, "/input2", sizeof(ictx->phys_touch)); touch->phys = ictx->phys_touch; touch->evbit[0] = @@ -1850,7 +2063,7 @@ static bool imon_find_endpoints(struct imon_context *ictx, /* Input endpoint is mandatory */ if (!ir_ep_found) - err("%s: no valid input (IR) endpoint found.", __func__); + pr_err("no valid input (IR) endpoint found\n"); ictx->tx_control = tx_control; @@ -1888,6 +2101,7 @@ static struct imon_context *imon_init_intf0(struct usb_interface *intf) } mutex_init(&ictx->lock); + spin_lock_init(&ictx->kc_lock); mutex_lock(&ictx->lock); @@ -1913,6 +2127,12 @@ static struct imon_context *imon_init_intf0(struct usb_interface *intf) goto idev_setup_failed; } + ictx->rdev = imon_init_rdev(ictx); + if (!ictx->rdev) { + dev_err(dev, "%s: rc device setup failed\n", __func__); + goto rdev_setup_failed; + } + usb_fill_int_urb(ictx->rx_urb_intf0, ictx->usbdev_intf0, usb_rcvintpipe(ictx->usbdev_intf0, ictx->rx_endpoint_intf0->bEndpointAddress), @@ -1922,15 +2142,16 @@ static struct imon_context *imon_init_intf0(struct usb_interface *intf) ret = usb_submit_urb(ictx->rx_urb_intf0, GFP_KERNEL); if (ret) { - err("%s: usb_submit_urb failed for intf0 (%d)", - __func__, ret); + pr_err("usb_submit_urb failed for intf0 (%d)\n", ret); goto urb_submit_failed; } return ictx; urb_submit_failed: - ir_input_unregister(ictx->idev); + ir_input_unregister(ictx->rdev); +rdev_setup_failed: + input_unregister_device(ictx->idev); idev_setup_failed: find_endpoint_failed: mutex_unlock(&ictx->lock); @@ -1954,7 +2175,7 @@ static struct imon_context *imon_init_intf1(struct usb_interface *intf, rx_urb = usb_alloc_urb(0, GFP_KERNEL); if (!rx_urb) { - err("%s: usb_alloc_urb failed for IR urb", __func__); + pr_err("usb_alloc_urb failed for IR urb\n"); goto rx_urb_alloc_failed; } @@ -1992,8 +2213,7 @@ static struct imon_context *imon_init_intf1(struct usb_interface *intf, ret = usb_submit_urb(ictx->rx_urb_intf1, GFP_KERNEL); if (ret) { - err("%s: usb_submit_urb failed for intf1 (%d)", - __func__, ret); + pr_err("usb_submit_urb failed for intf1 (%d)\n", ret); goto urb_submit_failed; } @@ -2012,116 +2232,6 @@ rx_urb_alloc_failed: return NULL; } -/* - * The 0x15c2:0xffdc device ID was used for umpteen different imon - * devices, and all of them constantly spew interrupts, even when there - * is no actual data to report. However, byte 6 of this buffer looks like - * its unique across device variants, so we're trying to key off that to - * figure out which display type (if any) and what IR protocol the device - * actually supports. These devices have their IR protocol hard-coded into - * their firmware, they can't be changed on the fly like the newer hardware. - */ -static void imon_get_ffdc_type(struct imon_context *ictx) -{ - u8 ffdc_cfg_byte = ictx->usb_rx_buf[6]; - u8 detected_display_type = IMON_DISPLAY_TYPE_NONE; - u64 allowed_protos = IR_TYPE_OTHER; - - switch (ffdc_cfg_byte) { - /* iMON Knob, no display, iMON IR + vol knob */ - case 0x21: - dev_info(ictx->dev, "0xffdc iMON Knob, iMON IR"); - ictx->display_supported = false; - break; - /* iMON 2.4G LT (usb stick), no display, iMON RF */ - case 0x4e: - dev_info(ictx->dev, "0xffdc iMON 2.4G LT, iMON RF"); - ictx->display_supported = false; - ictx->rf_device = true; - break; - /* iMON VFD, no IR (does have vol knob tho) */ - case 0x35: - dev_info(ictx->dev, "0xffdc iMON VFD + knob, no IR"); - detected_display_type = IMON_DISPLAY_TYPE_VFD; - break; - /* iMON VFD, iMON IR */ - case 0x24: - case 0x85: - dev_info(ictx->dev, "0xffdc iMON VFD, iMON IR"); - detected_display_type = IMON_DISPLAY_TYPE_VFD; - break; - /* iMON LCD, MCE IR */ - case 0x9e: - case 0x9f: - dev_info(ictx->dev, "0xffdc iMON LCD, MCE IR"); - detected_display_type = IMON_DISPLAY_TYPE_LCD; - allowed_protos = IR_TYPE_RC6; - break; - default: - dev_info(ictx->dev, "Unknown 0xffdc device, " - "defaulting to VFD and iMON IR"); - detected_display_type = IMON_DISPLAY_TYPE_VFD; - break; - } - - printk(KERN_CONT " (id 0x%02x)\n", ffdc_cfg_byte); - - ictx->display_type = detected_display_type; - ictx->props->allowed_protos = allowed_protos; - ictx->ir_type = allowed_protos; -} - -static void imon_set_display_type(struct imon_context *ictx, - struct usb_interface *intf) -{ - u8 configured_display_type = IMON_DISPLAY_TYPE_VFD; - - /* - * 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) { - switch (ictx->product) { - case 0xffdc: - /* set in imon_get_ffdc_type() */ - configured_display_type = ictx->display_type; - break; - case 0x0034: - case 0x0035: - configured_display_type = IMON_DISPLAY_TYPE_VGA; - break; - case 0x0038: - case 0x0039: - case 0x0045: - configured_display_type = IMON_DISPLAY_TYPE_LCD; - break; - case 0x003c: - case 0x0041: - case 0x0042: - case 0x0043: - configured_display_type = IMON_DISPLAY_TYPE_NONE; - ictx->display_supported = false; - break; - case 0x0036: - case 0x0044: - default: - configured_display_type = IMON_DISPLAY_TYPE_VFD; - break; - } - } else { - configured_display_type = display_type; - if (display_type == IMON_DISPLAY_TYPE_NONE) - ictx->display_supported = false; - else - ictx->display_supported = true; - dev_info(ictx->dev, "%s: overriding display type to %d via " - "modparam\n", __func__, display_type); - } - - ictx->display_type = configured_display_type; -} - static void imon_init_display(struct imon_context *ictx, struct usb_interface *intf) { @@ -2130,8 +2240,7 @@ static void imon_init_display(struct imon_context *ictx, dev_dbg(ictx->dev, "Registering iMON display with sysfs\n"); /* set up sysfs entry for built-in clock */ - ret = sysfs_create_group(&intf->dev.kobj, - &imon_display_attribute_group); + ret = sysfs_create_group(&intf->dev.kobj, &imon_display_attr_group); if (ret) dev_err(ictx->dev, "Could not create display sysfs " "entries(%d)", ret); @@ -2162,8 +2271,6 @@ static int __devinit imon_probe(struct usb_interface *interface, struct imon_context *ictx = NULL; struct imon_context *first_if_ctx = NULL; u16 vendor, product; - const unsigned char fp_packet[] = { 0x40, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x88 }; code_length = BUF_CHUNK_SIZE * 8; @@ -2185,7 +2292,7 @@ static int __devinit imon_probe(struct usb_interface *interface, if (ifnum == 0) { ictx = imon_init_intf0(interface); if (!ictx) { - err("%s: failed to initialize context!\n", __func__); + pr_err("failed to initialize context!\n"); ret = -ENODEV; goto fail; } @@ -2194,7 +2301,7 @@ static int __devinit imon_probe(struct usb_interface *interface, /* this is the secondary interface on the device */ ictx = imon_init_intf1(interface, first_if_ctx); if (!ictx) { - err("%s: failed to attach to context!\n", __func__); + pr_err("failed to attach to context!\n"); ret = -ENODEV; goto fail; } @@ -2204,39 +2311,18 @@ static int __devinit imon_probe(struct usb_interface *interface, usb_set_intfdata(interface, ictx); if (ifnum == 0) { - /* Enable front-panel buttons and/or knobs */ - memcpy(ictx->usb_tx_buf, &fp_packet, sizeof(fp_packet)); - ret = send_packet(ictx); - /* Not fatal, but warn about it */ - if (ret) - dev_info(dev, "failed to enable panel buttons " - "and/or knobs\n"); - - if (product == 0xffdc) - imon_get_ffdc_type(ictx); - - imon_set_display_type(ictx, interface); - if (product == 0xffdc && ictx->rf_device) { sysfs_err = sysfs_create_group(&interface->dev.kobj, - &imon_rf_attribute_group); + &imon_rf_attr_group); if (sysfs_err) - err("%s: Could not create RF sysfs entries(%d)", - __func__, sysfs_err); + pr_err("Could not create RF sysfs entries(%d)\n", + sysfs_err); } if (ictx->display_supported) imon_init_display(ictx, interface); } - /* set IR protocol/remote type */ - ret = imon_ir_change_protocol(ictx, ictx->ir_type); - if (ret) { - dev_warn(dev, "%s: failed to set IR protocol, falling back " - "to standard iMON protocol mode\n", __func__); - ictx->ir_type = IR_TYPE_OTHER; - } - dev_info(dev, "iMON device (%04x:%04x, intf%d) on " "usb<%d:%d> initialized\n", vendor, product, ifnum, usbdev->bus->busnum, usbdev->devnum); @@ -2275,10 +2361,8 @@ static void __devexit imon_disconnect(struct usb_interface *interface) * sysfs_remove_group is safe to call even if sysfs_create_group * hasn't been called */ - sysfs_remove_group(&interface->dev.kobj, - &imon_display_attribute_group); - sysfs_remove_group(&interface->dev.kobj, - &imon_rf_attribute_group); + sysfs_remove_group(&interface->dev.kobj, &imon_display_attr_group); + sysfs_remove_group(&interface->dev.kobj, &imon_rf_attr_group); usb_set_intfdata(interface, NULL); @@ -2291,7 +2375,8 @@ static void __devexit imon_disconnect(struct usb_interface *interface) if (ifnum == 0) { ictx->dev_present_intf0 = false; usb_kill_urb(ictx->rx_urb_intf0); - ir_input_unregister(ictx->idev); + input_unregister_device(ictx->idev); + ir_input_unregister(ictx->rdev); if (ictx->display_supported) { if (ictx->display_type == IMON_DISPLAY_TYPE_LCD) usb_deregister_dev(interface, &imon_lcd_class); @@ -2311,11 +2396,8 @@ static void __devexit imon_disconnect(struct usb_interface *interface) mutex_unlock(&ictx->lock); if (!ictx->display_isopen) free_imon_context(ictx); - } else { - if (ictx->ir_type == IR_TYPE_RC6) - del_timer_sync(&ictx->itimer); + } else mutex_unlock(&ictx->lock); - } mutex_unlock(&driver_lock); @@ -2372,7 +2454,7 @@ static int __init imon_init(void) rc = usb_register(&imon_driver); if (rc) { - err("%s: usb register failed(%d)", __func__, rc); + pr_err("usb register failed(%d)\n", rc); rc = -ENODEV; } diff --git a/drivers/media/IR/ir-core-priv.h b/drivers/media/IR/ir-core-priv.h index a85a8c7c905a..81c936bd793f 100644 --- a/drivers/media/IR/ir-core-priv.h +++ b/drivers/media/IR/ir-core-priv.h @@ -17,6 +17,7 @@ #define _IR_RAW_EVENT #include <linux/slab.h> +#include <linux/spinlock.h> #include <media/ir-core.h> struct ir_raw_handler { @@ -33,6 +34,7 @@ struct ir_raw_handler { struct ir_raw_event_ctrl { struct list_head list; /* to keep track of raw clients */ struct task_struct *thread; + spinlock_t lock; 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 */ @@ -76,10 +78,22 @@ struct ir_raw_event_ctrl { bool first; bool toggle; } jvc; + struct rc5_sz_dec { + int state; + u32 bits; + unsigned count; + unsigned wanted_bits; + } rc5_sz; struct lirc_codec { struct ir_input_dev *ir_dev; struct lirc_driver *drv; int carrier_low; + + ktime_t gap_start; + u64 gap_duration; + bool gap; + bool send_timeout_reports; + } lirc; }; @@ -107,13 +121,19 @@ static inline void decrease_duration(struct ir_raw_event *ev, unsigned duration) ev->duration -= duration; } +/* Returns true if event is normal pulse/space event */ +static inline bool is_timing_event(struct ir_raw_event ev) +{ + return !ev.carrier_report && !ev.reset; +} + #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 */ +int ir_register_input(struct input_dev *input_dev); int ir_register_class(struct input_dev *input_dev); void ir_unregister_class(struct input_dev *input_dev); diff --git a/drivers/media/IR/ir-jvc-decoder.c b/drivers/media/IR/ir-jvc-decoder.c index 77a89c4de014..63dca6e5458b 100644 --- a/drivers/media/IR/ir-jvc-decoder.c +++ b/drivers/media/IR/ir-jvc-decoder.c @@ -50,8 +50,9 @@ static int ir_jvc_decode(struct input_dev *input_dev, struct ir_raw_event ev) if (!(ir_dev->raw->enabled_protocols & IR_TYPE_JVC)) return 0; - if (IS_RESET(ev)) { - data->state = STATE_INACTIVE; + if (!is_timing_event(ev)) { + if (ev.reset) + data->state = STATE_INACTIVE; return 0; } diff --git a/drivers/media/IR/ir-keytable.c b/drivers/media/IR/ir-keytable.c index c06b4d50a3dc..9186b45132ed 100644 --- a/drivers/media/IR/ir-keytable.c +++ b/drivers/media/IR/ir-keytable.c @@ -435,7 +435,7 @@ EXPORT_SYMBOL_GPL(ir_g_keycode_from_table); * This routine is used to signal that a key has been released on the * remote control. It reports a keyup input event via input_report_key(). */ -static void ir_keyup(struct ir_input_dev *ir) +void ir_keyup(struct ir_input_dev *ir) { if (!ir->keypressed) return; @@ -445,6 +445,7 @@ static void ir_keyup(struct ir_input_dev *ir) input_sync(ir->input_dev); ir->keypressed = false; } +EXPORT_SYMBOL_GPL(ir_keyup); /** * ir_timer_keyup() - generates a keyup event after a timeout @@ -640,6 +641,10 @@ int __ir_input_register(struct input_dev *input_dev, goto out_event; } + rc = ir_register_input(input_dev); + if (rc < 0) + goto out_event; + IR_dprintk(1, "Registered input device on %s for %s remote%s.\n", driver_name, rc_tab->name, (ir_dev->props && ir_dev->props->driver_type == RC_DRIVER_IR_RAW) ? diff --git a/drivers/media/IR/ir-lirc-codec.c b/drivers/media/IR/ir-lirc-codec.c index 1983cd3f3994..9fc0db9d344d 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); + struct lirc_codec *lirc = &ir_dev->raw->lirc; int sample; if (!(ir_dev->raw->enabled_protocols & IR_TYPE_LIRC)) @@ -40,21 +41,57 @@ 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)) + /* Packet start */ + if (ev.reset) return 0; - IR_dprintk(2, "LIRC data transfer started (%uus %s)\n", - TO_US(ev.duration), TO_STR(ev.pulse)); + /* Carrier reports */ + if (ev.carrier_report) { + sample = LIRC_FREQUENCY(ev.carrier); + + /* Packet end */ + } else if (ev.timeout) { + + if (lirc->gap) + return 0; + + lirc->gap_start = ktime_get(); + lirc->gap = true; + lirc->gap_duration = ev.duration; + + if (!lirc->send_timeout_reports) + return 0; + + sample = LIRC_TIMEOUT(ev.duration / 1000); - sample = ev.duration / 1000; - if (ev.pulse) - sample |= PULSE_BIT; + /* Normal sample */ + } else { + + if (lirc->gap) { + int gap_sample; + + lirc->gap_duration += ktime_to_ns(ktime_sub(ktime_get(), + lirc->gap_start)); + + /* Convert to ms and cap by LIRC_VALUE_MASK */ + do_div(lirc->gap_duration, 1000); + lirc->gap_duration = min(lirc->gap_duration, + (u64)LIRC_VALUE_MASK); + + gap_sample = LIRC_SPACE(lirc->gap_duration); + lirc_buffer_write(ir_dev->raw->lirc.drv->rbuf, + (unsigned char *) &gap_sample); + lirc->gap = false; + } + + sample = ev.pulse ? LIRC_PULSE(ev.duration / 1000) : + LIRC_SPACE(ev.duration / 1000); + } lirc_buffer_write(ir_dev->raw->lirc.drv->rbuf, (unsigned char *) &sample); wake_up(&ir_dev->raw->lirc.drv->rbuf->wait_poll); - return 0; } @@ -102,7 +139,7 @@ static long ir_lirc_ioctl(struct file *filep, unsigned int cmd, struct ir_input_dev *ir_dev; int ret = 0; void *drv_data; - unsigned long val = 0; + __u32 val = 0, tmp; lirc = lirc_get_pdata(filep); if (!lirc) @@ -115,7 +152,7 @@ static long ir_lirc_ioctl(struct file *filep, unsigned int cmd, drv_data = ir_dev->props->priv; if (_IOC_DIR(cmd) & _IOC_WRITE) { - ret = get_user(val, (unsigned long *)arg); + ret = get_user(val, (__u32 *)arg); if (ret) return ret; } @@ -130,22 +167,20 @@ static long ir_lirc_ioctl(struct file *filep, unsigned int cmd, case LIRC_SET_SEND_MODE: if (val != (LIRC_MODE_PULSE & LIRC_CAN_SEND_MASK)) return -EINVAL; - break; + return 0; /* 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 + if (!ir_dev->props->s_tx_mask) return -EINVAL; - break; + + return ir_dev->props->s_tx_mask(drv_data, val); case LIRC_SET_SEND_CARRIER: - if (ir_dev->props->s_tx_carrier) - ir_dev->props->s_tx_carrier(drv_data, (u32)val); - else + if (!ir_dev->props->s_tx_carrier) return -EINVAL; - break; + + return ir_dev->props->s_tx_carrier(drv_data, val); case LIRC_SET_SEND_DUTY_CYCLE: if (!ir_dev->props->s_tx_duty_cycle) @@ -154,39 +189,42 @@ static long ir_lirc_ioctl(struct file *filep, unsigned int cmd, if (val <= 0 || val >= 100) return -EINVAL; - ir_dev->props->s_tx_duty_cycle(ir_dev->props->priv, val); - break; + return ir_dev->props->s_tx_duty_cycle(drv_data, val); /* 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 + if (!ir_dev->props->s_rx_carrier_range) return -ENOSYS; - if (!ret) - ir_dev->raw->lirc.carrier_low = 0; - break; + if (val <= 0) + return -EINVAL; + + return ir_dev->props->s_rx_carrier_range(drv_data, + ir_dev->raw->lirc.carrier_low, val); case LIRC_SET_REC_CARRIER_RANGE: - if (val >= 0) - ir_dev->raw->lirc.carrier_low = val; - break; + if (val <= 0) + return -EINVAL; + ir_dev->raw->lirc.carrier_low = val; + return 0; 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 + if (!ir_dev->props->s_learning_mode) return -ENOSYS; + return ir_dev->props->s_learning_mode(drv_data, !!val); + + case LIRC_SET_MEASURE_CARRIER_MODE: + if (!ir_dev->props->s_carrier_report) + return -ENOSYS; + + return ir_dev->props->s_carrier_report(drv_data, !!val); + /* Generic timeout support */ case LIRC_GET_MIN_TIMEOUT: if (!ir_dev->props->max_timeout) @@ -201,10 +239,20 @@ static long ir_lirc_ioctl(struct file *filep, unsigned int cmd, 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; + if (!ir_dev->props->max_timeout) + return -ENOSYS; + + tmp = val * 1000; + + if (tmp < ir_dev->props->min_timeout || + tmp > ir_dev->props->max_timeout) + return -EINVAL; + + ir_dev->props->timeout = tmp; + break; + + case LIRC_SET_REC_TIMEOUT_REPORTS: + lirc->send_timeout_reports = !!val; break; default: @@ -212,7 +260,7 @@ static long ir_lirc_ioctl(struct file *filep, unsigned int cmd, } if (_IOC_DIR(cmd) & _IOC_READ) - ret = put_user(val, (unsigned long *)arg); + ret = put_user(val, (__u32 *)arg); return ret; } @@ -231,6 +279,9 @@ static struct file_operations lirc_fops = { .owner = THIS_MODULE, .write = ir_lirc_transmit_ir, .unlocked_ioctl = ir_lirc_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = ir_lirc_ioctl, +#endif .read = lirc_dev_fop_read, .poll = lirc_dev_fop_poll, .open = lirc_dev_fop_open, @@ -278,6 +329,10 @@ static int ir_lirc_register(struct input_dev *input_dev) if (ir_dev->props->s_learning_mode) features |= LIRC_CAN_USE_WIDEBAND_RECEIVER; + if (ir_dev->props->s_carrier_report) + features |= LIRC_CAN_MEASURE_CARRIER; + + if (ir_dev->props->max_timeout) features |= LIRC_CAN_SET_REC_TIMEOUT; diff --git a/drivers/media/IR/ir-nec-decoder.c b/drivers/media/IR/ir-nec-decoder.c index d597421d6547..70993f79c8a2 100644 --- a/drivers/media/IR/ir-nec-decoder.c +++ b/drivers/media/IR/ir-nec-decoder.c @@ -54,8 +54,9 @@ static int ir_nec_decode(struct input_dev *input_dev, struct ir_raw_event ev) if (!(ir_dev->raw->enabled_protocols & IR_TYPE_NEC)) return 0; - if (IS_RESET(ev)) { - data->state = STATE_INACTIVE; + if (!is_timing_event(ev)) { + if (ev.reset) + 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 8e0e1b1f8c87..a06a07e4e0b1 100644 --- a/drivers/media/IR/ir-raw-event.c +++ b/drivers/media/IR/ir-raw-event.c @@ -39,22 +39,34 @@ static int ir_raw_event_thread(void *data) struct ir_raw_event ev; struct ir_raw_handler *handler; struct ir_raw_event_ctrl *raw = (struct ir_raw_event_ctrl *)data; + int retval; while (!kthread_should_stop()) { - try_to_freeze(); - mutex_lock(&ir_raw_handler_lock); + spin_lock_irq(&raw->lock); + retval = kfifo_out(&raw->kfifo, &ev, sizeof(ev)); + + if (!retval) { + set_current_state(TASK_INTERRUPTIBLE); + + if (kthread_should_stop()) + set_current_state(TASK_RUNNING); - 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; + spin_unlock_irq(&raw->lock); + schedule(); + continue; } - mutex_unlock(&ir_raw_handler_lock); + spin_unlock_irq(&raw->lock); + - set_current_state(TASK_INTERRUPTIBLE); - schedule(); + BUG_ON(retval != sizeof(ev)); + + mutex_lock(&ir_raw_handler_lock); + 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); } return 0; @@ -77,7 +89,7 @@ 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", + IR_dprintk(2, "sample: (%05dus %s)\n", TO_US(ev->duration), TO_STR(ev->pulse)); if (kfifo_in(&ir->raw->kfifo, ev, sizeof(*ev)) != sizeof(*ev)) @@ -162,7 +174,7 @@ int ir_raw_event_store_with_filter(struct input_dev *input_dev, if (ir->idle && !ev->pulse) return 0; else if (ir->idle) - ir_raw_event_set_idle(input_dev, 0); + ir_raw_event_set_idle(input_dev, false); if (!raw->this_ev.duration) { raw->this_ev = *ev; @@ -175,48 +187,35 @@ int ir_raw_event_store_with_filter(struct input_dev *input_dev, /* 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); + raw->this_ev.duration >= ir->props->timeout) { + ir_raw_event_set_idle(input_dev, true); + } return 0; } EXPORT_SYMBOL_GPL(ir_raw_event_store_with_filter); -void ir_raw_event_set_idle(struct input_dev *input_dev, int idle) +/** + * ir_raw_event_set_idle() - hint the ir core if device is receiving + * IR data or not + * @input_dev: the struct input_dev device descriptor + * @idle: the hint value + */ +void ir_raw_event_set_idle(struct input_dev *input_dev, bool 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) + if (!ir->props || !ir->raw) return; - if (!ir->raw) - goto out; + IR_dprintk(2, "%s idle mode\n", idle ? "enter" : "leave"); 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); - + raw->this_ev.timeout = true; 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; + init_ir_raw_event(&raw->this_ev); } -out: + if (ir->props->s_idle) ir->props->s_idle(ir->props->priv, idle); ir->idle = idle; @@ -232,11 +231,14 @@ EXPORT_SYMBOL_GPL(ir_raw_event_set_idle); void ir_raw_event_handle(struct input_dev *input_dev) { struct ir_input_dev *ir = input_get_drvdata(input_dev); + unsigned long flags; if (!ir->raw) return; + spin_lock_irqsave(&ir->raw->lock, flags); wake_up_process(ir->raw->thread); + spin_unlock_irqrestore(&ir->raw->lock, flags); } EXPORT_SYMBOL_GPL(ir_raw_event_handle); @@ -275,6 +277,7 @@ int ir_raw_event_register(struct input_dev *input_dev) return rc; } + spin_lock_init(&ir->raw->lock); ir->raw->thread = kthread_run(ir_raw_event_thread, ir->raw, "rc%u", (unsigned int)ir->devno); diff --git a/drivers/media/IR/ir-rc5-decoder.c b/drivers/media/IR/ir-rc5-decoder.c index df4770d978ad..572ed4ca8c68 100644 --- a/drivers/media/IR/ir-rc5-decoder.c +++ b/drivers/media/IR/ir-rc5-decoder.c @@ -55,8 +55,9 @@ static int ir_rc5_decode(struct input_dev *input_dev, struct ir_raw_event ev) if (!(ir_dev->raw->enabled_protocols & IR_TYPE_RC5)) return 0; - if (IS_RESET(ev)) { - data->state = STATE_INACTIVE; + if (!is_timing_event(ev)) { + if (ev.reset) + data->state = STATE_INACTIVE; return 0; } diff --git a/drivers/media/IR/ir-rc5-sz-decoder.c b/drivers/media/IR/ir-rc5-sz-decoder.c new file mode 100644 index 000000000000..7c413501a3f7 --- /dev/null +++ b/drivers/media/IR/ir-rc5-sz-decoder.c @@ -0,0 +1,154 @@ +/* ir-rc5-sz-decoder.c - handle RC5 Streamzap IR Pulse/Space protocol + * + * Copyright (C) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * 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 version 2 of the License. + * + * 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. + */ + +/* + * This code handles the 15 bit RC5-ish protocol used by the Streamzap + * PC Remote. + * It considers a carrier of 36 kHz, with a total of 15 bits, where + * the first two bits are start bits, and a third one is a filing bit + */ + +#include "ir-core-priv.h" + +#define RC5_SZ_NBITS 15 +#define RC5_UNIT 888888 /* ns */ +#define RC5_BIT_START (1 * RC5_UNIT) +#define RC5_BIT_END (1 * RC5_UNIT) + +enum rc5_sz_state { + STATE_INACTIVE, + STATE_BIT_START, + STATE_BIT_END, + STATE_FINISHED, +}; + +/** + * ir_rc5_sz_decode() - Decode one RC-5 Streamzap pulse or space + * @input_dev: the struct input_dev descriptor of the device + * @ev: the struct ir_raw_event descriptor of the pulse/space + * + * This function returns -EINVAL if the pulse violates the state machine + */ +static int ir_rc5_sz_decode(struct input_dev *input_dev, struct ir_raw_event ev) +{ + struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); + struct rc5_sz_dec *data = &ir_dev->raw->rc5_sz; + u8 toggle, command, system; + u32 scancode; + + if (!(ir_dev->raw->enabled_protocols & IR_TYPE_RC5_SZ)) + return 0; + + if (!is_timing_event(ev)) { + if (ev.reset) + data->state = STATE_INACTIVE; + return 0; + } + + if (!geq_margin(ev.duration, RC5_UNIT, RC5_UNIT / 2)) + goto out; + +again: + IR_dprintk(2, "RC5-sz decode started at state %i (%uus %s)\n", + data->state, TO_US(ev.duration), TO_STR(ev.pulse)); + + if (!geq_margin(ev.duration, RC5_UNIT, RC5_UNIT / 2)) + return 0; + + switch (data->state) { + + case STATE_INACTIVE: + if (!ev.pulse) + break; + + data->state = STATE_BIT_START; + data->count = 1; + data->wanted_bits = RC5_SZ_NBITS; + decrease_duration(&ev, RC5_BIT_START); + goto again; + + case STATE_BIT_START: + if (!eq_margin(ev.duration, RC5_BIT_START, RC5_UNIT / 2)) + break; + + data->bits <<= 1; + if (!ev.pulse) + data->bits |= 1; + data->count++; + data->state = STATE_BIT_END; + return 0; + + case STATE_BIT_END: + if (!is_transition(&ev, &ir_dev->raw->prev_ev)) + break; + + if (data->count == data->wanted_bits) + data->state = STATE_FINISHED; + else + data->state = STATE_BIT_START; + + decrease_duration(&ev, RC5_BIT_END); + goto again; + + case STATE_FINISHED: + if (ev.pulse) + break; + + /* RC5-sz */ + command = (data->bits & 0x0003F) >> 0; + system = (data->bits & 0x02FC0) >> 6; + toggle = (data->bits & 0x01000) ? 1 : 0; + scancode = system << 6 | command; + + IR_dprintk(1, "RC5-sz scancode 0x%04x (toggle: %u)\n", + scancode, toggle); + + ir_keydown(input_dev, scancode, toggle); + data->state = STATE_INACTIVE; + return 0; + } + +out: + IR_dprintk(1, "RC5-sz decode failed at state %i (%uus %s)\n", + data->state, TO_US(ev.duration), TO_STR(ev.pulse)); + data->state = STATE_INACTIVE; + return -EINVAL; +} + +static struct ir_raw_handler rc5_sz_handler = { + .protocols = IR_TYPE_RC5_SZ, + .decode = ir_rc5_sz_decode, +}; + +static int __init ir_rc5_sz_decode_init(void) +{ + ir_raw_handler_register(&rc5_sz_handler); + + printk(KERN_INFO "IR RC5 (streamzap) protocol handler initialized\n"); + return 0; +} + +static void __exit ir_rc5_sz_decode_exit(void) +{ + ir_raw_handler_unregister(&rc5_sz_handler); +} + +module_init(ir_rc5_sz_decode_init); +module_exit(ir_rc5_sz_decode_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jarod Wilson <jarod@redhat.com>"); +MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)"); +MODULE_DESCRIPTION("RC5 (streamzap) IR protocol decoder"); diff --git a/drivers/media/IR/ir-rc6-decoder.c b/drivers/media/IR/ir-rc6-decoder.c index f1624b8279bc..d25da91f44ff 100644 --- a/drivers/media/IR/ir-rc6-decoder.c +++ b/drivers/media/IR/ir-rc6-decoder.c @@ -85,8 +85,9 @@ static int ir_rc6_decode(struct input_dev *input_dev, struct ir_raw_event ev) if (!(ir_dev->raw->enabled_protocols & IR_TYPE_RC6)) return 0; - if (IS_RESET(ev)) { - data->state = STATE_INACTIVE; + if (!is_timing_event(ev)) { + if (ev.reset) + data->state = STATE_INACTIVE; return 0; } diff --git a/drivers/media/IR/ir-sony-decoder.c b/drivers/media/IR/ir-sony-decoder.c index b9074f07c7a0..2d15730822bc 100644 --- a/drivers/media/IR/ir-sony-decoder.c +++ b/drivers/media/IR/ir-sony-decoder.c @@ -48,8 +48,9 @@ static int ir_sony_decode(struct input_dev *input_dev, struct ir_raw_event ev) if (!(ir_dev->raw->enabled_protocols & IR_TYPE_SONY)) return 0; - if (IS_RESET(ev)) { - data->state = STATE_INACTIVE; + if (!is_timing_event(ev)) { + if (ev.reset) + data->state = STATE_INACTIVE; return 0; } diff --git a/drivers/media/IR/ir-sysfs.c b/drivers/media/IR/ir-sysfs.c index 46d42467f9b4..38423a8da871 100644 --- a/drivers/media/IR/ir-sysfs.c +++ b/drivers/media/IR/ir-sysfs.c @@ -43,6 +43,7 @@ static struct { { IR_TYPE_RC6, "rc-6" }, { IR_TYPE_JVC, "jvc" }, { IR_TYPE_SONY, "sony" }, + { IR_TYPE_RC5_SZ, "rc-5-sz" }, { IR_TYPE_LIRC, "lirc" }, }; @@ -67,6 +68,10 @@ static ssize_t show_protocols(struct device *d, char *tmp = buf; int i; + /* Device is being removed */ + if (!ir_dev) + return -EINVAL; + if (ir_dev->props && ir_dev->props->driver_type == RC_DRIVER_SCANCODE) { enabled = ir_dev->rc_tab.ir_type; allowed = ir_dev->props->allowed_protos; @@ -122,6 +127,10 @@ static ssize_t store_protocols(struct device *d, int rc, i, count = 0; unsigned long flags; + /* Device is being removed */ + if (!ir_dev) + return -EINVAL; + if (ir_dev->props && ir_dev->props->driver_type == RC_DRIVER_SCANCODE) type = ir_dev->rc_tab.ir_type; else if (ir_dev->raw) @@ -256,8 +265,6 @@ static struct device_type rc_dev_type = { */ int ir_register_class(struct input_dev *input_dev) { - int rc; - const char *path; struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); int devno = find_first_zero_bit(&ir_core_dev_number, IRRCV_NUM_DEVICES); @@ -266,17 +273,28 @@ int ir_register_class(struct input_dev *input_dev) return devno; ir_dev->dev.type = &rc_dev_type; + ir_dev->devno = devno; ir_dev->dev.class = &ir_input_class; ir_dev->dev.parent = input_dev->dev.parent; + input_dev->dev.parent = &ir_dev->dev; dev_set_name(&ir_dev->dev, "rc%d", devno); dev_set_drvdata(&ir_dev->dev, ir_dev); - rc = device_register(&ir_dev->dev); - if (rc) - return rc; + return device_register(&ir_dev->dev); +}; + +/** + * ir_register_input - registers ir input device with input subsystem + * @input_dev: the struct input_dev descriptor of the device + */ + +int ir_register_input(struct input_dev *input_dev) +{ + struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); + int rc; + const char *path; - input_dev->dev.parent = &ir_dev->dev; rc = input_register_device(input_dev); if (rc < 0) { device_del(&ir_dev->dev); @@ -292,11 +310,9 @@ int ir_register_class(struct input_dev *input_dev) path ? path : "N/A"); kfree(path); - ir_dev->devno = devno; - set_bit(devno, &ir_core_dev_number); - + set_bit(ir_dev->devno, &ir_core_dev_number); return 0; -}; +} /** * ir_unregister_class() - removes the sysfs for sysfs for @@ -309,6 +325,7 @@ void ir_unregister_class(struct input_dev *input_dev) { struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); + input_set_drvdata(input_dev, NULL); clear_bit(ir_dev->devno, &ir_core_dev_number); input_unregister_device(input_dev); device_del(&ir_dev->dev); diff --git a/drivers/media/IR/keymaps/Makefile b/drivers/media/IR/keymaps/Makefile index 950e5d953c6f..3194d391bbd4 100644 --- a/drivers/media/IR/keymaps/Makefile +++ b/drivers/media/IR/keymaps/Makefile @@ -1,4 +1,6 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \ + rc-alink-dtu-m.o \ + rc-anysee.o \ rc-apac-viewcomp.o \ rc-asus-pc39.o \ rc-ati-tv-wonder-hd-600.o \ @@ -8,7 +10,9 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \ rc-avermedia-dvbt.o \ rc-avermedia-m135a.o \ rc-avermedia-m733a-rm-k6.o \ + rc-avermedia-rm-ks.o \ rc-avertv-303.o \ + rc-azurewave-ad-tu700.o \ rc-behold.o \ rc-behold-columbus.o \ rc-budget-ci-old.o \ @@ -16,6 +20,8 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \ rc-cinergy.o \ rc-dib0700-nec.o \ rc-dib0700-rc5.o \ + rc-digitalnow-tinytwin.o \ + rc-digittrade.o \ rc-dm1105-nec.o \ rc-dntv-live-dvb-t.o \ rc-dntv-live-dvbt-pro.o \ @@ -38,8 +44,12 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \ rc-kaiomy.o \ rc-kworld-315u.o \ rc-kworld-plus-tv-analog.o \ + rc-leadtek-y04g0051.o \ rc-lirc.o \ + rc-lme2510.o \ rc-manli.o \ + rc-msi-digivox-ii.o \ + rc-msi-digivox-iii.o \ rc-msi-tvanywhere.o \ rc-msi-tvanywhere-plus.o \ rc-nebula.o \ @@ -58,14 +68,18 @@ 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 \ + rc-streamzap.o \ rc-tbs-nec.o \ rc-terratec-cinergy-xs.o \ + rc-terratec-slim.o \ rc-tevii-nec.o \ + rc-total-media-in-hand.o \ + rc-trekstor.o \ rc-tt-1500.o \ + rc-twinhan1027.o \ rc-videomate-s350.o \ rc-videomate-tv-pvr.o \ rc-winfast.o \ diff --git a/drivers/media/IR/keymaps/rc-alink-dtu-m.c b/drivers/media/IR/keymaps/rc-alink-dtu-m.c new file mode 100644 index 000000000000..ddfee7f8093d --- /dev/null +++ b/drivers/media/IR/keymaps/rc-alink-dtu-m.c @@ -0,0 +1,68 @@ +/* + * A-Link DTU(m) remote controller keytable + * + * Copyright (C) 2010 Antti Palosaari <crope@iki.fi> + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <media/rc-map.h> + +/* A-Link DTU(m) slim remote, 6 rows, 3 columns. */ +static struct ir_scancode alink_dtu_m[] = { + { 0x0800, KEY_VOLUMEUP }, + { 0x0801, KEY_1 }, + { 0x0802, KEY_3 }, + { 0x0803, KEY_7 }, + { 0x0804, KEY_9 }, + { 0x0805, KEY_NEW }, /* symbol: PIP */ + { 0x0806, KEY_0 }, + { 0x0807, KEY_CHANNEL }, /* JUMP */ + { 0x080d, KEY_5 }, + { 0x080f, KEY_2 }, + { 0x0812, KEY_POWER2 }, + { 0x0814, KEY_CHANNELUP }, + { 0x0816, KEY_VOLUMEDOWN }, + { 0x0818, KEY_6 }, + { 0x081a, KEY_MUTE }, + { 0x081b, KEY_8 }, + { 0x081c, KEY_4 }, + { 0x081d, KEY_CHANNELDOWN }, +}; + +static struct rc_keymap alink_dtu_m_map = { + .map = { + .scan = alink_dtu_m, + .size = ARRAY_SIZE(alink_dtu_m), + .ir_type = IR_TYPE_NEC, + .name = RC_MAP_ALINK_DTU_M, + } +}; + +static int __init init_rc_map_alink_dtu_m(void) +{ + return ir_register_map(&alink_dtu_m_map); +} + +static void __exit exit_rc_map_alink_dtu_m(void) +{ + ir_unregister_map(&alink_dtu_m_map); +} + +module_init(init_rc_map_alink_dtu_m) +module_exit(exit_rc_map_alink_dtu_m) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); diff --git a/drivers/media/IR/keymaps/rc-anysee.c b/drivers/media/IR/keymaps/rc-anysee.c new file mode 100644 index 000000000000..30d70498cfed --- /dev/null +++ b/drivers/media/IR/keymaps/rc-anysee.c @@ -0,0 +1,93 @@ +/* + * Anysee remote controller keytable + * + * Copyright (C) 2010 Antti Palosaari <crope@iki.fi> + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <media/rc-map.h> + +static struct ir_scancode anysee[] = { + { 0x0800, KEY_0 }, + { 0x0801, KEY_1 }, + { 0x0802, KEY_2 }, + { 0x0803, KEY_3 }, + { 0x0804, KEY_4 }, + { 0x0805, KEY_5 }, + { 0x0806, KEY_6 }, + { 0x0807, KEY_7 }, + { 0x0808, KEY_8 }, + { 0x0809, KEY_9 }, + { 0x080a, KEY_POWER2 }, /* [red power button] */ + { 0x080b, KEY_VIDEO }, /* [*] MODE */ + { 0x080c, KEY_CHANNEL }, /* [symbol counterclockwise arrow] */ + { 0x080d, KEY_NEXT }, /* [>>|] */ + { 0x080e, KEY_MENU }, /* MENU */ + { 0x080f, KEY_EPG }, /* [EPG] */ + { 0x0810, KEY_CLEAR }, /* EXIT */ + { 0x0811, KEY_CHANNELUP }, + { 0x0812, KEY_VOLUMEDOWN }, + { 0x0813, KEY_VOLUMEUP }, + { 0x0814, KEY_CHANNELDOWN }, + { 0x0815, KEY_OK }, + { 0x0816, KEY_RADIO }, /* [symbol TV/radio] */ + { 0x0817, KEY_INFO }, /* [i] */ + { 0x0818, KEY_PREVIOUS }, /* [|<<] */ + { 0x0819, KEY_FAVORITES }, /* FAV. */ + { 0x081a, KEY_SUBTITLE }, /* Subtitle */ + { 0x081b, KEY_CAMERA }, /* [symbol camera] */ + { 0x081c, KEY_YELLOW }, + { 0x081d, KEY_RED }, + { 0x081e, KEY_LANGUAGE }, /* [symbol Second Audio Program] */ + { 0x081f, KEY_GREEN }, + { 0x0820, KEY_SLEEP }, /* Sleep */ + { 0x0821, KEY_SCREEN }, /* 16:9 / 4:3 */ + { 0x0822, KEY_ZOOM }, /* SIZE */ + { 0x0824, KEY_FN }, /* [F1] */ + { 0x0825, KEY_FN }, /* [F2] */ + { 0x0842, KEY_MUTE }, /* symbol mute */ + { 0x0844, KEY_BLUE }, + { 0x0847, KEY_TEXT }, /* TEXT */ + { 0x0848, KEY_STOP }, + { 0x0849, KEY_RECORD }, + { 0x0850, KEY_PLAY }, + { 0x0851, KEY_PAUSE }, +}; + +static struct rc_keymap anysee_map = { + .map = { + .scan = anysee, + .size = ARRAY_SIZE(anysee), + .ir_type = IR_TYPE_NEC, + .name = RC_MAP_ANYSEE, + } +}; + +static int __init init_rc_map_anysee(void) +{ + return ir_register_map(&anysee_map); +} + +static void __exit exit_rc_map_anysee(void) +{ + ir_unregister_map(&anysee_map); +} + +module_init(init_rc_map_anysee) +module_exit(exit_rc_map_anysee) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); diff --git a/drivers/media/IR/keymaps/rc-asus-pc39.c b/drivers/media/IR/keymaps/rc-asus-pc39.c index 2aa068cd6c75..2996e0a3b8d5 100644 --- a/drivers/media/IR/keymaps/rc-asus-pc39.c +++ b/drivers/media/IR/keymaps/rc-asus-pc39.c @@ -20,56 +20,56 @@ static struct ir_scancode asus_pc39[] = { /* Keys 0 to 9 */ - { 0x15, KEY_0 }, - { 0x29, KEY_1 }, - { 0x2d, KEY_2 }, - { 0x2b, KEY_3 }, - { 0x09, KEY_4 }, - { 0x0d, KEY_5 }, - { 0x0b, KEY_6 }, - { 0x31, KEY_7 }, - { 0x35, KEY_8 }, - { 0x33, KEY_9 }, + { 0x082a, KEY_0 }, + { 0x0816, KEY_1 }, + { 0x0812, KEY_2 }, + { 0x0814, KEY_3 }, + { 0x0836, KEY_4 }, + { 0x0832, KEY_5 }, + { 0x0834, KEY_6 }, + { 0x080e, KEY_7 }, + { 0x080a, KEY_8 }, + { 0x080c, KEY_9 }, - { 0x3e, KEY_RADIO }, /* radio */ - { 0x03, KEY_MENU }, /* dvd/menu */ - { 0x2a, KEY_VOLUMEUP }, - { 0x19, KEY_VOLUMEDOWN }, - { 0x37, KEY_UP }, - { 0x3b, KEY_DOWN }, - { 0x27, KEY_LEFT }, - { 0x2f, KEY_RIGHT }, - { 0x25, KEY_VIDEO }, /* video */ - { 0x39, KEY_AUDIO }, /* music */ + { 0x0801, KEY_RADIO }, /* radio */ + { 0x083c, KEY_MENU }, /* dvd/menu */ + { 0x0815, KEY_VOLUMEUP }, + { 0x0826, KEY_VOLUMEDOWN }, + { 0x0808, KEY_UP }, + { 0x0804, KEY_DOWN }, + { 0x0818, KEY_LEFT }, + { 0x0810, KEY_RIGHT }, + { 0x081a, KEY_VIDEO }, /* video */ + { 0x0806, KEY_AUDIO }, /* music */ - { 0x21, KEY_TV }, /* tv */ - { 0x1d, KEY_EXIT }, /* back */ - { 0x0a, KEY_CHANNELUP }, /* channel / program + */ - { 0x1b, KEY_CHANNELDOWN }, /* channel / program - */ - { 0x1a, KEY_ENTER }, /* enter */ + { 0x081e, KEY_TV }, /* tv */ + { 0x0822, KEY_EXIT }, /* back */ + { 0x0835, KEY_CHANNELUP }, /* channel / program + */ + { 0x0824, KEY_CHANNELDOWN }, /* channel / program - */ + { 0x0825, KEY_ENTER }, /* enter */ - { 0x06, KEY_PAUSE }, /* play/pause */ - { 0x1e, KEY_PREVIOUS }, /* rew */ - { 0x26, KEY_NEXT }, /* forward */ - { 0x0e, KEY_REWIND }, /* backward << */ - { 0x3a, KEY_FASTFORWARD }, /* forward >> */ - { 0x36, KEY_STOP }, - { 0x2e, KEY_RECORD }, /* recording */ - { 0x16, KEY_POWER }, /* the button that reads "close" */ + { 0x0839, KEY_PAUSE }, /* play/pause */ + { 0x0821, KEY_PREVIOUS }, /* rew */ + { 0x0819, KEY_NEXT }, /* forward */ + { 0x0831, KEY_REWIND }, /* backward << */ + { 0x0805, KEY_FASTFORWARD }, /* forward >> */ + { 0x0809, KEY_STOP }, + { 0x0811, KEY_RECORD }, /* recording */ + { 0x0829, KEY_POWER }, /* the button that reads "close" */ - { 0x11, KEY_ZOOM }, /* full screen */ - { 0x13, KEY_MACRO }, /* recall */ - { 0x23, KEY_HOME }, /* home */ - { 0x05, KEY_PVR }, /* picture */ - { 0x3d, KEY_MUTE }, /* mute */ - { 0x01, KEY_DVD }, /* dvd */ + { 0x082e, KEY_ZOOM }, /* full screen */ + { 0x082c, KEY_MACRO }, /* recall */ + { 0x081c, KEY_HOME }, /* home */ + { 0x083a, KEY_PVR }, /* picture */ + { 0x0802, KEY_MUTE }, /* mute */ + { 0x083e, KEY_DVD }, /* dvd */ }; static struct rc_keymap asus_pc39_map = { .map = { .scan = asus_pc39, .size = ARRAY_SIZE(asus_pc39), - .ir_type = IR_TYPE_UNKNOWN, /* Legacy IR type */ + .ir_type = IR_TYPE_RC5, .name = RC_MAP_ASUS_PC39, } }; diff --git a/drivers/media/IR/keymaps/rc-avermedia-rm-ks.c b/drivers/media/IR/keymaps/rc-avermedia-rm-ks.c new file mode 100644 index 000000000000..9ee60906c861 --- /dev/null +++ b/drivers/media/IR/keymaps/rc-avermedia-rm-ks.c @@ -0,0 +1,79 @@ +/* + * AverMedia RM-KS remote controller keytable + * + * Copyright (C) 2010 Antti Palosaari <crope@iki.fi> + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <media/rc-map.h> + +/* Initial keytable is from Jose Alberto Reguero <jareguero@telefonica.net> + and Felipe Morales Moreno <felipe.morales.moreno@gmail.com> */ +/* FIXME: mappings are not 100% correct? */ +static struct ir_scancode avermedia_rm_ks[] = { + { 0x0501, KEY_POWER2 }, + { 0x0502, KEY_CHANNELUP }, + { 0x0503, KEY_CHANNELDOWN }, + { 0x0504, KEY_VOLUMEUP }, + { 0x0505, KEY_VOLUMEDOWN }, + { 0x0506, KEY_MUTE }, + { 0x0507, KEY_RIGHT }, + { 0x0508, KEY_PROG1 }, + { 0x0509, KEY_1 }, + { 0x050a, KEY_2 }, + { 0x050b, KEY_3 }, + { 0x050c, KEY_4 }, + { 0x050d, KEY_5 }, + { 0x050e, KEY_6 }, + { 0x050f, KEY_7 }, + { 0x0510, KEY_8 }, + { 0x0511, KEY_9 }, + { 0x0512, KEY_0 }, + { 0x0513, KEY_AUDIO }, + { 0x0515, KEY_EPG }, + { 0x0516, KEY_PLAY }, + { 0x0517, KEY_RECORD }, + { 0x0518, KEY_STOP }, + { 0x051c, KEY_BACK }, + { 0x051d, KEY_FORWARD }, + { 0x054d, KEY_LEFT }, + { 0x0556, KEY_ZOOM }, +}; + +static struct rc_keymap avermedia_rm_ks_map = { + .map = { + .scan = avermedia_rm_ks, + .size = ARRAY_SIZE(avermedia_rm_ks), + .ir_type = IR_TYPE_NEC, + .name = RC_MAP_AVERMEDIA_RM_KS, + } +}; + +static int __init init_rc_map_avermedia_rm_ks(void) +{ + return ir_register_map(&avermedia_rm_ks_map); +} + +static void __exit exit_rc_map_avermedia_rm_ks(void) +{ + ir_unregister_map(&avermedia_rm_ks_map); +} + +module_init(init_rc_map_avermedia_rm_ks) +module_exit(exit_rc_map_avermedia_rm_ks) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); diff --git a/drivers/media/IR/keymaps/rc-azurewave-ad-tu700.c b/drivers/media/IR/keymaps/rc-azurewave-ad-tu700.c new file mode 100644 index 000000000000..e0876147d471 --- /dev/null +++ b/drivers/media/IR/keymaps/rc-azurewave-ad-tu700.c @@ -0,0 +1,102 @@ +/* + * TwinHan AzureWave AD-TU700(704J) remote controller keytable + * + * Copyright (C) 2010 Antti Palosaari <crope@iki.fi> + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <media/rc-map.h> + +static struct ir_scancode azurewave_ad_tu700[] = { + { 0x0000, KEY_TAB }, /* Tab */ + { 0x0001, KEY_2 }, + { 0x0002, KEY_CHANNELDOWN }, + { 0x0003, KEY_1 }, + { 0x0004, KEY_MENU }, /* Record List */ + { 0x0005, KEY_CHANNELUP }, + { 0x0006, KEY_3 }, + { 0x0007, KEY_SLEEP }, /* Hibernate */ + { 0x0008, KEY_VIDEO }, /* A/V */ + { 0x0009, KEY_4 }, + { 0x000a, KEY_VOLUMEDOWN }, + { 0x000c, KEY_CANCEL }, /* Cancel */ + { 0x000d, KEY_7 }, + { 0x000e, KEY_AGAIN }, /* Recall */ + { 0x000f, KEY_TEXT }, /* Teletext */ + { 0x0010, KEY_MUTE }, + { 0x0011, KEY_RECORD }, + { 0x0012, KEY_FASTFORWARD }, /* FF >> */ + { 0x0013, KEY_BACK }, /* Back */ + { 0x0014, KEY_PLAY }, + { 0x0015, KEY_0 }, + { 0x0016, KEY_POWER2 }, /* [red power button] */ + { 0x0017, KEY_FAVORITES }, /* Favorite List */ + { 0x0018, KEY_RED }, + { 0x0019, KEY_8 }, + { 0x001a, KEY_STOP }, + { 0x001b, KEY_9 }, + { 0x001c, KEY_EPG }, /* Info/EPG */ + { 0x001d, KEY_5 }, + { 0x001e, KEY_VOLUMEUP }, + { 0x001f, KEY_6 }, + { 0x0040, KEY_REWIND }, /* FR << */ + { 0x0041, KEY_PREVIOUS }, /* Replay */ + { 0x0042, KEY_NEXT }, /* Skip */ + { 0x0043, KEY_SUBTITLE }, /* Subtitle / CC */ + { 0x0045, KEY_KPPLUS }, /* Zoom+ */ + { 0x0046, KEY_KPMINUS }, /* Zoom- */ + { 0x0047, KEY_NEW }, /* PIP */ + { 0x0048, KEY_INFO }, /* Preview */ + { 0x0049, KEY_MODE }, /* L/R */ + { 0x004a, KEY_CLEAR }, /* Clear */ + { 0x004b, KEY_UP }, /* up arrow */ + { 0x004c, KEY_PAUSE }, + { 0x004d, KEY_ZOOM }, /* Full Screen */ + { 0x004e, KEY_LEFT }, /* left arrow */ + { 0x004f, KEY_OK }, /* Enter / ok */ + { 0x0050, KEY_LANGUAGE }, /* SAP */ + { 0x0051, KEY_DOWN }, /* down arrow */ + { 0x0052, KEY_RIGHT }, /* right arrow */ + { 0x0053, KEY_GREEN }, + { 0x0054, KEY_CAMERA }, /* Capture */ + { 0x005e, KEY_YELLOW }, + { 0x005f, KEY_BLUE }, +}; + +static struct rc_keymap azurewave_ad_tu700_map = { + .map = { + .scan = azurewave_ad_tu700, + .size = ARRAY_SIZE(azurewave_ad_tu700), + .ir_type = IR_TYPE_NEC, + .name = RC_MAP_AZUREWAVE_AD_TU700, + } +}; + +static int __init init_rc_map_azurewave_ad_tu700(void) +{ + return ir_register_map(&azurewave_ad_tu700_map); +} + +static void __exit exit_rc_map_azurewave_ad_tu700(void) +{ + ir_unregister_map(&azurewave_ad_tu700_map); +} + +module_init(init_rc_map_azurewave_ad_tu700) +module_exit(exit_rc_map_azurewave_ad_tu700) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); diff --git a/drivers/media/IR/keymaps/rc-digitalnow-tinytwin.c b/drivers/media/IR/keymaps/rc-digitalnow-tinytwin.c new file mode 100644 index 000000000000..63e469e2dd21 --- /dev/null +++ b/drivers/media/IR/keymaps/rc-digitalnow-tinytwin.c @@ -0,0 +1,98 @@ +/* + * DigitalNow TinyTwin remote controller keytable + * + * Copyright (C) 2010 Antti Palosaari <crope@iki.fi> + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <media/rc-map.h> + +static struct ir_scancode digitalnow_tinytwin[] = { + { 0x0000, KEY_MUTE }, /* [symbol speaker] */ + { 0x0001, KEY_VOLUMEUP }, + { 0x0002, KEY_POWER2 }, /* TV [power button] */ + { 0x0003, KEY_2 }, + { 0x0004, KEY_3 }, + { 0x0005, KEY_4 }, + { 0x0006, KEY_6 }, + { 0x0007, KEY_7 }, + { 0x0008, KEY_8 }, + { 0x0009, KEY_NUMERIC_STAR }, /* [*] */ + { 0x000a, KEY_0 }, + { 0x000b, KEY_NUMERIC_POUND }, /* [#] */ + { 0x000c, KEY_RIGHT }, /* [right arrow] */ + { 0x000d, KEY_HOMEPAGE }, /* [symbol home] Start */ + { 0x000e, KEY_RED }, /* [red] Videos */ + { 0x0010, KEY_POWER }, /* PC [power button] */ + { 0x0011, KEY_YELLOW }, /* [yellow] Pictures */ + { 0x0012, KEY_DOWN }, /* [down arrow] */ + { 0x0013, KEY_GREEN }, /* [green] Music */ + { 0x0014, KEY_CYCLEWINDOWS }, /* BACK */ + { 0x0015, KEY_FAVORITES }, /* MORE */ + { 0x0016, KEY_UP }, /* [up arrow] */ + { 0x0017, KEY_LEFT }, /* [left arrow] */ + { 0x0018, KEY_OK }, /* OK */ + { 0x0019, KEY_BLUE }, /* [blue] MyTV */ + { 0x001a, KEY_REWIND }, /* REW [<<] */ + { 0x001b, KEY_PLAY }, /* PLAY */ + { 0x001c, KEY_5 }, + { 0x001d, KEY_9 }, + { 0x001e, KEY_VOLUMEDOWN }, + { 0x001f, KEY_1 }, + { 0x0040, KEY_STOP }, /* STOP */ + { 0x0042, KEY_PAUSE }, /* PAUSE */ + { 0x0043, KEY_SCREEN }, /* Aspect */ + { 0x0044, KEY_FORWARD }, /* FWD [>>] */ + { 0x0045, KEY_NEXT }, /* SKIP */ + { 0x0048, KEY_RECORD }, /* RECORD */ + { 0x0049, KEY_VIDEO }, /* RTV */ + { 0x004a, KEY_EPG }, /* Guide */ + { 0x004b, KEY_CHANNELUP }, + { 0x004c, KEY_HELP }, /* Help */ + { 0x004d, KEY_RADIO }, /* Radio */ + { 0x004f, KEY_CHANNELDOWN }, + { 0x0050, KEY_DVD }, /* DVD */ + { 0x0051, KEY_AUDIO }, /* Audio */ + { 0x0052, KEY_TITLE }, /* Title */ + { 0x0053, KEY_NEW }, /* [symbol PIP?] */ + { 0x0057, KEY_MENU }, /* Mouse */ + { 0x005a, KEY_PREVIOUS }, /* REPLAY */ +}; + +static struct rc_keymap digitalnow_tinytwin_map = { + .map = { + .scan = digitalnow_tinytwin, + .size = ARRAY_SIZE(digitalnow_tinytwin), + .ir_type = IR_TYPE_NEC, + .name = RC_MAP_DIGITALNOW_TINYTWIN, + } +}; + +static int __init init_rc_map_digitalnow_tinytwin(void) +{ + return ir_register_map(&digitalnow_tinytwin_map); +} + +static void __exit exit_rc_map_digitalnow_tinytwin(void) +{ + ir_unregister_map(&digitalnow_tinytwin_map); +} + +module_init(init_rc_map_digitalnow_tinytwin) +module_exit(exit_rc_map_digitalnow_tinytwin) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); diff --git a/drivers/media/IR/keymaps/rc-digittrade.c b/drivers/media/IR/keymaps/rc-digittrade.c new file mode 100644 index 000000000000..5dece78e19c5 --- /dev/null +++ b/drivers/media/IR/keymaps/rc-digittrade.c @@ -0,0 +1,82 @@ +/* + * Digittrade DVB-T USB Stick remote controller keytable + * + * Copyright (C) 2010 Antti Palosaari <crope@iki.fi> + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <media/rc-map.h> + +/* Digittrade DVB-T USB Stick remote controller. */ +/* Imported from af9015.h. + Initial keytable was from Alain Kalker <miki@dds.nl> */ + +/* Digittrade DVB-T USB Stick */ +static struct ir_scancode digittrade[] = { + { 0x0000, KEY_9 }, + { 0x0001, KEY_EPG }, /* EPG */ + { 0x0002, KEY_VOLUMEDOWN }, /* Vol Dn */ + { 0x0003, KEY_TEXT }, /* TELETEXT */ + { 0x0004, KEY_8 }, + { 0x0005, KEY_MUTE }, /* MUTE */ + { 0x0006, KEY_POWER2 }, /* POWER */ + { 0x0009, KEY_ZOOM }, /* FULLSCREEN */ + { 0x000a, KEY_RECORD }, /* RECORD */ + { 0x000d, KEY_SUBTITLE }, /* SUBTITLE */ + { 0x000e, KEY_STOP }, /* STOP */ + { 0x0010, KEY_OK }, /* RETURN */ + { 0x0011, KEY_2 }, + { 0x0012, KEY_4 }, + { 0x0015, KEY_3 }, + { 0x0016, KEY_5 }, + { 0x0017, KEY_CHANNELDOWN }, /* Ch Dn */ + { 0x0019, KEY_CHANNELUP }, /* CH Up */ + { 0x001a, KEY_PAUSE }, /* PAUSE */ + { 0x001b, KEY_1 }, + { 0x001d, KEY_AUDIO }, /* DUAL SOUND */ + { 0x001e, KEY_PLAY }, /* PLAY */ + { 0x001f, KEY_CAMERA }, /* SNAPSHOT */ + { 0x0040, KEY_VOLUMEUP }, /* Vol Up */ + { 0x0048, KEY_7 }, + { 0x004c, KEY_6 }, + { 0x004d, KEY_PLAYPAUSE }, /* TIMESHIFT */ + { 0x0054, KEY_0 }, +}; + +static struct rc_keymap digittrade_map = { + .map = { + .scan = digittrade, + .size = ARRAY_SIZE(digittrade), + .ir_type = IR_TYPE_NEC, + .name = RC_MAP_DIGITTRADE, + } +}; + +static int __init init_rc_map_digittrade(void) +{ + return ir_register_map(&digittrade_map); +} + +static void __exit exit_rc_map_digittrade(void) +{ + ir_unregister_map(&digittrade_map); +} + +module_init(init_rc_map_digittrade) +module_exit(exit_rc_map_digittrade) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); diff --git a/drivers/media/IR/keymaps/rc-leadtek-y04g0051.c b/drivers/media/IR/keymaps/rc-leadtek-y04g0051.c new file mode 100644 index 000000000000..7521315fd876 --- /dev/null +++ b/drivers/media/IR/keymaps/rc-leadtek-y04g0051.c @@ -0,0 +1,99 @@ +/* + * LeadTek Y04G0051 remote controller keytable + * + * Copyright (C) 2010 Antti Palosaari <crope@iki.fi> + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <media/rc-map.h> + +static struct ir_scancode leadtek_y04g0051[] = { + { 0x0300, KEY_POWER2 }, + { 0x0303, KEY_SCREEN }, + { 0x0304, KEY_RIGHT }, + { 0x0305, KEY_1 }, + { 0x0306, KEY_2 }, + { 0x0307, KEY_3 }, + { 0x0308, KEY_LEFT }, + { 0x0309, KEY_4 }, + { 0x030a, KEY_5 }, + { 0x030b, KEY_6 }, + { 0x030c, KEY_UP }, + { 0x030d, KEY_7 }, + { 0x030e, KEY_8 }, + { 0x030f, KEY_9 }, + { 0x0310, KEY_DOWN }, + { 0x0311, KEY_AGAIN }, + { 0x0312, KEY_0 }, + { 0x0313, KEY_OK }, /* 1st ok */ + { 0x0314, KEY_MUTE }, + { 0x0316, KEY_OK }, /* 2nd ok */ + { 0x031e, KEY_VIDEO }, /* 2nd video */ + { 0x031b, KEY_AUDIO }, + { 0x031f, KEY_TEXT }, + { 0x0340, KEY_SLEEP }, + { 0x0341, KEY_DOT }, + { 0x0342, KEY_REWIND }, + { 0x0343, KEY_PLAY }, + { 0x0344, KEY_FASTFORWARD }, + { 0x0345, KEY_TIME }, + { 0x0346, KEY_STOP }, /* 2nd stop */ + { 0x0347, KEY_RECORD }, + { 0x0348, KEY_CAMERA }, + { 0x0349, KEY_ESC }, + { 0x034a, KEY_NEW }, + { 0x034b, KEY_RED }, + { 0x034c, KEY_GREEN }, + { 0x034d, KEY_YELLOW }, + { 0x034e, KEY_BLUE }, + { 0x034f, KEY_MENU }, + { 0x0350, KEY_STOP }, /* 1st stop */ + { 0x0351, KEY_CHANNEL }, + { 0x0352, KEY_VIDEO }, /* 1st video */ + { 0x0353, KEY_EPG }, + { 0x0354, KEY_PREVIOUS }, + { 0x0355, KEY_NEXT }, + { 0x0356, KEY_TV }, + { 0x035a, KEY_VOLUMEDOWN }, + { 0x035b, KEY_CHANNELUP }, + { 0x035e, KEY_VOLUMEUP }, + { 0x035f, KEY_CHANNELDOWN }, +}; + +static struct rc_keymap leadtek_y04g0051_map = { + .map = { + .scan = leadtek_y04g0051, + .size = ARRAY_SIZE(leadtek_y04g0051), + .ir_type = IR_TYPE_NEC, + .name = RC_MAP_LEADTEK_Y04G0051, + } +}; + +static int __init init_rc_map_leadtek_y04g0051(void) +{ + return ir_register_map(&leadtek_y04g0051_map); +} + +static void __exit exit_rc_map_leadtek_y04g0051(void) +{ + ir_unregister_map(&leadtek_y04g0051_map); +} + +module_init(init_rc_map_leadtek_y04g0051) +module_exit(exit_rc_map_leadtek_y04g0051) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); diff --git a/drivers/media/IR/keymaps/rc-lme2510.c b/drivers/media/IR/keymaps/rc-lme2510.c new file mode 100644 index 000000000000..40dcf0b4e21a --- /dev/null +++ b/drivers/media/IR/keymaps/rc-lme2510.c @@ -0,0 +1,68 @@ +/* LME2510 remote control + * + * + * Copyright (C) 2010 Malcolm Priestley (tvboxspy@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. + */ + +#include <media/rc-map.h> + + +static struct ir_scancode lme2510_rc[] = { + { 0xba45, KEY_0 }, + { 0xa05f, KEY_1 }, + { 0xaf50, KEY_2 }, + { 0xa25d, KEY_3 }, + { 0xbe41, KEY_4 }, + { 0xf50a, KEY_5 }, + { 0xbd42, KEY_6 }, + { 0xb847, KEY_7 }, + { 0xb649, KEY_8 }, + { 0xfa05, KEY_9 }, + { 0xbc43, KEY_POWER }, + { 0xb946, KEY_SUBTITLE }, + { 0xf906, KEY_PAUSE }, + { 0xfc03, KEY_MEDIA_REPEAT}, + { 0xfd02, KEY_PAUSE }, + { 0xa15e, KEY_VOLUMEUP }, + { 0xa35c, KEY_VOLUMEDOWN }, + { 0xf609, KEY_CHANNELUP }, + { 0xe51a, KEY_CHANNELDOWN }, + { 0xe11e, KEY_PLAY }, + { 0xe41b, KEY_ZOOM }, + { 0xa659, KEY_MUTE }, + { 0xa55a, KEY_TV }, + { 0xe718, KEY_RECORD }, + { 0xf807, KEY_EPG }, + { 0xfe01, KEY_STOP }, + +}; + +static struct rc_keymap lme2510_map = { + .map = { + .scan = lme2510_rc, + .size = ARRAY_SIZE(lme2510_rc), + .ir_type = IR_TYPE_UNKNOWN, + .name = RC_MAP_LME2510, + } +}; + +static int __init init_rc_lme2510_map(void) +{ + return ir_register_map(&lme2510_map); +} + +static void __exit exit_rc_lme2510_map(void) +{ + ir_unregister_map(&lme2510_map); +} + +module_init(init_rc_lme2510_map) +module_exit(exit_rc_lme2510_map) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Malcolm Priestley tvboxspy@gmail.com"); diff --git a/drivers/media/IR/keymaps/rc-msi-digivox-ii.c b/drivers/media/IR/keymaps/rc-msi-digivox-ii.c new file mode 100644 index 000000000000..67237fbf9e4b --- /dev/null +++ b/drivers/media/IR/keymaps/rc-msi-digivox-ii.c @@ -0,0 +1,67 @@ +/* + * MSI DIGIVOX mini II remote controller keytable + * + * Copyright (C) 2010 Antti Palosaari <crope@iki.fi> + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <media/rc-map.h> + +static struct ir_scancode msi_digivox_ii[] = { + { 0x0002, KEY_2 }, + { 0x0003, KEY_UP }, /* up */ + { 0x0004, KEY_3 }, + { 0x0005, KEY_CHANNELDOWN }, + { 0x0008, KEY_5 }, + { 0x0009, KEY_0 }, + { 0x000b, KEY_8 }, + { 0x000d, KEY_DOWN }, /* down */ + { 0x0010, KEY_9 }, + { 0x0011, KEY_7 }, + { 0x0014, KEY_VOLUMEUP }, + { 0x0015, KEY_CHANNELUP }, + { 0x0016, KEY_OK }, + { 0x0017, KEY_POWER2 }, + { 0x001a, KEY_1 }, + { 0x001c, KEY_4 }, + { 0x001d, KEY_6 }, + { 0x001f, KEY_VOLUMEDOWN }, +}; + +static struct rc_keymap msi_digivox_ii_map = { + .map = { + .scan = msi_digivox_ii, + .size = ARRAY_SIZE(msi_digivox_ii), + .ir_type = IR_TYPE_NEC, + .name = RC_MAP_MSI_DIGIVOX_II, + } +}; + +static int __init init_rc_map_msi_digivox_ii(void) +{ + return ir_register_map(&msi_digivox_ii_map); +} + +static void __exit exit_rc_map_msi_digivox_ii(void) +{ + ir_unregister_map(&msi_digivox_ii_map); +} + +module_init(init_rc_map_msi_digivox_ii) +module_exit(exit_rc_map_msi_digivox_ii) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); diff --git a/drivers/media/IR/keymaps/rc-msi-digivox-iii.c b/drivers/media/IR/keymaps/rc-msi-digivox-iii.c new file mode 100644 index 000000000000..882056e52ef9 --- /dev/null +++ b/drivers/media/IR/keymaps/rc-msi-digivox-iii.c @@ -0,0 +1,85 @@ +/* + * MSI DIGIVOX mini III remote controller keytable + * + * Copyright (C) 2010 Antti Palosaari <crope@iki.fi> + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <media/rc-map.h> + +/* MSI DIGIVOX mini III */ +/* Uses NEC extended 0x61d6. */ +/* This remote seems to be same as rc-kworld-315u.c. Anyhow, add new remote + since rc-kworld-315u.c lacks NEC extended address byte. */ +static struct ir_scancode msi_digivox_iii[] = { + { 0x61d601, KEY_VIDEO }, /* Source */ + { 0x61d602, KEY_3 }, + { 0x61d603, KEY_POWER }, /* ShutDown */ + { 0x61d604, KEY_1 }, + { 0x61d605, KEY_5 }, + { 0x61d606, KEY_6 }, + { 0x61d607, KEY_CHANNELDOWN }, /* CH- */ + { 0x61d608, KEY_2 }, + { 0x61d609, KEY_CHANNELUP }, /* CH+ */ + { 0x61d60a, KEY_9 }, + { 0x61d60b, KEY_ZOOM }, /* Zoom */ + { 0x61d60c, KEY_7 }, + { 0x61d60d, KEY_8 }, + { 0x61d60e, KEY_VOLUMEUP }, /* Vol+ */ + { 0x61d60f, KEY_4 }, + { 0x61d610, KEY_ESC }, /* [back up arrow] */ + { 0x61d611, KEY_0 }, + { 0x61d612, KEY_OK }, /* [enter arrow] */ + { 0x61d613, KEY_VOLUMEDOWN }, /* Vol- */ + { 0x61d614, KEY_RECORD }, /* Rec */ + { 0x61d615, KEY_STOP }, /* Stop */ + { 0x61d616, KEY_PLAY }, /* Play */ + { 0x61d617, KEY_MUTE }, /* Mute */ + { 0x61d618, KEY_UP }, + { 0x61d619, KEY_DOWN }, + { 0x61d61a, KEY_LEFT }, + { 0x61d61b, KEY_RIGHT }, + { 0x61d61c, KEY_RED }, + { 0x61d61d, KEY_GREEN }, + { 0x61d61e, KEY_YELLOW }, + { 0x61d61f, KEY_BLUE }, + { 0x61d643, KEY_POWER2 }, /* [red power button] */ +}; + +static struct rc_keymap msi_digivox_iii_map = { + .map = { + .scan = msi_digivox_iii, + .size = ARRAY_SIZE(msi_digivox_iii), + .ir_type = IR_TYPE_NEC, + .name = RC_MAP_MSI_DIGIVOX_III, + } +}; + +static int __init init_rc_map_msi_digivox_iii(void) +{ + return ir_register_map(&msi_digivox_iii_map); +} + +static void __exit exit_rc_map_msi_digivox_iii(void) +{ + ir_unregister_map(&msi_digivox_iii_map); +} + +module_init(init_rc_map_msi_digivox_iii) +module_exit(exit_rc_map_msi_digivox_iii) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); diff --git a/drivers/media/IR/keymaps/rc-rc5-streamzap.c b/drivers/media/IR/keymaps/rc-rc5-streamzap.c deleted file mode 100644 index 4c19c58b46d8..000000000000 --- a/drivers/media/IR/keymaps/rc-rc5-streamzap.c +++ /dev/null @@ -1,81 +0,0 @@ -/* 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 39557ad401b6..1b7adabbcee9 100644 --- a/drivers/media/IR/keymaps/rc-rc6-mce.c +++ b/drivers/media/IR/keymaps/rc-rc6-mce.c @@ -12,76 +12,78 @@ #include <media/rc-map.h> static struct ir_scancode rc6_mce[] = { - { 0x800f0415, KEY_REWIND }, - { 0x800f0414, KEY_FASTFORWARD }, - { 0x800f041b, KEY_PREVIOUS }, - { 0x800f041a, KEY_NEXT }, + { 0x800f0400, KEY_NUMERIC_0 }, + { 0x800f0401, KEY_NUMERIC_1 }, + { 0x800f0402, KEY_NUMERIC_2 }, + { 0x800f0403, KEY_NUMERIC_3 }, + { 0x800f0404, KEY_NUMERIC_4 }, + { 0x800f0405, KEY_NUMERIC_5 }, + { 0x800f0406, KEY_NUMERIC_6 }, + { 0x800f0407, KEY_NUMERIC_7 }, + { 0x800f0408, KEY_NUMERIC_8 }, + { 0x800f0409, KEY_NUMERIC_9 }, + + { 0x800f040a, KEY_DELETE }, + { 0x800f040b, KEY_ENTER }, + { 0x800f040c, KEY_POWER }, + { 0x800f040d, KEY_PROG1 }, /* Windows MCE button */ + { 0x800f040e, KEY_MUTE }, + { 0x800f040f, KEY_INFO }, + + { 0x800f0410, KEY_VOLUMEUP }, + { 0x800f0411, KEY_VOLUMEDOWN }, + { 0x800f0412, KEY_CHANNELUP }, + { 0x800f0413, KEY_CHANNELDOWN }, + + { 0x800f0414, KEY_FASTFORWARD }, + { 0x800f0415, KEY_REWIND }, { 0x800f0416, KEY_PLAY }, + { 0x800f0417, KEY_RECORD }, { 0x800f0418, KEY_PAUSE }, { 0x800f046e, KEY_PLAYPAUSE }, { 0x800f0419, KEY_STOP }, - { 0x800f0417, KEY_RECORD }, + { 0x800f041a, KEY_NEXT }, + { 0x800f041b, KEY_PREVIOUS }, + { 0x800f041c, KEY_NUMERIC_POUND }, + { 0x800f041d, KEY_NUMERIC_STAR }, { 0x800f041e, KEY_UP }, { 0x800f041f, KEY_DOWN }, { 0x800f0420, KEY_LEFT }, { 0x800f0421, KEY_RIGHT }, - { 0x800f040b, KEY_ENTER }, { 0x800f0422, KEY_OK }, { 0x800f0423, KEY_EXIT }, - { 0x800f040a, KEY_DELETE }, + { 0x800f0424, KEY_DVD }, + { 0x800f0425, KEY_TUNER }, /* LiveTV */ + { 0x800f0426, KEY_EPG }, /* Guide */ + { 0x800f0427, KEY_ZOOM }, /* Aspect */ - { 0x800f040e, KEY_MUTE }, - { 0x800f0410, KEY_VOLUMEUP }, - { 0x800f0411, KEY_VOLUMEDOWN }, - { 0x800f0412, KEY_CHANNELUP }, - { 0x800f0413, KEY_CHANNELDOWN }, { 0x800f043a, KEY_BRIGHTNESSUP }, - { 0x800f0480, KEY_BRIGHTNESSDOWN }, - - { 0x800f0401, KEY_NUMERIC_1 }, - { 0x800f0402, KEY_NUMERIC_2 }, - { 0x800f0403, KEY_NUMERIC_3 }, - { 0x800f0404, KEY_NUMERIC_4 }, - { 0x800f0405, KEY_NUMERIC_5 }, - { 0x800f0406, KEY_NUMERIC_6 }, - { 0x800f0407, KEY_NUMERIC_7 }, - { 0x800f0408, KEY_NUMERIC_8 }, - { 0x800f0409, KEY_NUMERIC_9 }, - { 0x800f0400, KEY_NUMERIC_0 }, - - { 0x800f041d, KEY_NUMERIC_STAR }, - { 0x800f041c, KEY_NUMERIC_POUND }, { 0x800f0446, KEY_TV }, - { 0x800f0447, KEY_AUDIO }, /* My Music */ - { 0x800f0448, KEY_PVR }, /* RecordedTV */ + { 0x800f0447, KEY_AUDIO }, /* My Music */ + { 0x800f0448, KEY_PVR }, /* RecordedTV */ { 0x800f0449, KEY_CAMERA }, { 0x800f044a, KEY_VIDEO }, - { 0x800f0424, KEY_DVD }, - { 0x800f0425, KEY_TUNER }, /* LiveTV */ - { 0x800f0450, KEY_RADIO }, - { 0x800f044c, KEY_LANGUAGE }, - { 0x800f0427, KEY_ZOOM }, /* Aspect */ + { 0x800f044d, KEY_TITLE }, + { 0x800f044e, KEY_PRINT }, /* Print - HP OEM version of remote */ + { 0x800f0450, KEY_RADIO }, + + { 0x800f045a, KEY_SUBTITLE }, /* Caption/Teletext */ { 0x800f045b, KEY_RED }, { 0x800f045c, KEY_GREEN }, { 0x800f045d, KEY_YELLOW }, { 0x800f045e, KEY_BLUE }, - { 0x800f040f, KEY_INFO }, - { 0x800f0426, KEY_EPG }, /* Guide */ - { 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 */ + { 0x800f046e, KEY_PLAYPAUSE }, + { 0x800f046f, KEY_MEDIA }, /* Start media application (NEW) */ + { 0x800f0480, KEY_BRIGHTNESSDOWN }, + { 0x800f0481, KEY_PLAYPAUSE }, }; static struct rc_keymap rc6_mce_map = { diff --git a/drivers/media/IR/keymaps/rc-streamzap.c b/drivers/media/IR/keymaps/rc-streamzap.c new file mode 100644 index 000000000000..df32013a321c --- /dev/null +++ b/drivers/media/IR/keymaps/rc-streamzap.c @@ -0,0 +1,82 @@ +/* rc-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 streamzap[] = { +/* + * The Streamzap remote is almost, but not quite, RC-5, as it has an extra + * bit in it, which throws the in-kernel RC-5 decoder for a loop. Currently, + * an additional RC-5-sz decoder is being deployed to support it, but it + * may be possible to merge it back with the standard RC-5 decoder. + */ + { 0x28c0, KEY_NUMERIC_0 }, + { 0x28c1, KEY_NUMERIC_1 }, + { 0x28c2, KEY_NUMERIC_2 }, + { 0x28c3, KEY_NUMERIC_3 }, + { 0x28c4, KEY_NUMERIC_4 }, + { 0x28c5, KEY_NUMERIC_5 }, + { 0x28c6, KEY_NUMERIC_6 }, + { 0x28c7, KEY_NUMERIC_7 }, + { 0x28c8, KEY_NUMERIC_8 }, + { 0x28c9, KEY_NUMERIC_9 }, + { 0x28ca, KEY_POWER }, + { 0x28cb, KEY_MUTE }, + { 0x28cc, KEY_CHANNELUP }, + { 0x28cd, KEY_VOLUMEUP }, + { 0x28ce, KEY_CHANNELDOWN }, + { 0x28cf, KEY_VOLUMEDOWN }, + { 0x28d0, KEY_UP }, + { 0x28d1, KEY_LEFT }, + { 0x28d2, KEY_OK }, + { 0x28d3, KEY_RIGHT }, + { 0x28d4, KEY_DOWN }, + { 0x28d5, KEY_MENU }, + { 0x28d6, KEY_EXIT }, + { 0x28d7, KEY_PLAY }, + { 0x28d8, KEY_PAUSE }, + { 0x28d9, KEY_STOP }, + { 0x28da, KEY_BACK }, + { 0x28db, KEY_FORWARD }, + { 0x28dc, KEY_RECORD }, + { 0x28dd, KEY_REWIND }, + { 0x28de, KEY_FASTFORWARD }, + { 0x28e0, KEY_RED }, + { 0x28e1, KEY_GREEN }, + { 0x28e2, KEY_YELLOW }, + { 0x28e3, KEY_BLUE }, + +}; + +static struct rc_keymap streamzap_map = { + .map = { + .scan = streamzap, + .size = ARRAY_SIZE(streamzap), + .ir_type = IR_TYPE_RC5_SZ, + .name = RC_MAP_STREAMZAP, + } +}; + +static int __init init_rc_map_streamzap(void) +{ + return ir_register_map(&streamzap_map); +} + +static void __exit exit_rc_map_streamzap(void) +{ + ir_unregister_map(&streamzap_map); +} + +module_init(init_rc_map_streamzap) +module_exit(exit_rc_map_streamzap) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jarod Wilson <jarod@redhat.com>"); diff --git a/drivers/media/IR/keymaps/rc-terratec-slim.c b/drivers/media/IR/keymaps/rc-terratec-slim.c new file mode 100644 index 000000000000..10dee4c1deff --- /dev/null +++ b/drivers/media/IR/keymaps/rc-terratec-slim.c @@ -0,0 +1,79 @@ +/* + * TerraTec remote controller keytable + * + * Copyright (C) 2010 Antti Palosaari <crope@iki.fi> + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <media/rc-map.h> + +/* TerraTec slim remote, 7 rows, 4 columns. */ +/* Uses NEC extended 0x02bd. */ +static struct ir_scancode terratec_slim[] = { + { 0x02bd00, KEY_1 }, + { 0x02bd01, KEY_2 }, + { 0x02bd02, KEY_3 }, + { 0x02bd03, KEY_4 }, + { 0x02bd04, KEY_5 }, + { 0x02bd05, KEY_6 }, + { 0x02bd06, KEY_7 }, + { 0x02bd07, KEY_8 }, + { 0x02bd08, KEY_9 }, + { 0x02bd09, KEY_0 }, + { 0x02bd0a, KEY_MUTE }, + { 0x02bd0b, KEY_NEW }, /* symbol: PIP */ + { 0x02bd0e, KEY_VOLUMEDOWN }, + { 0x02bd0f, KEY_PLAYPAUSE }, + { 0x02bd10, KEY_RIGHT }, + { 0x02bd11, KEY_LEFT }, + { 0x02bd12, KEY_UP }, + { 0x02bd13, KEY_DOWN }, + { 0x02bd15, KEY_OK }, + { 0x02bd16, KEY_STOP }, + { 0x02bd17, KEY_CAMERA }, /* snapshot */ + { 0x02bd18, KEY_CHANNELUP }, + { 0x02bd19, KEY_RECORD }, + { 0x02bd1a, KEY_CHANNELDOWN }, + { 0x02bd1c, KEY_ESC }, + { 0x02bd1f, KEY_VOLUMEUP }, + { 0x02bd44, KEY_EPG }, + { 0x02bd45, KEY_POWER2 }, /* [red power button] */ +}; + +static struct rc_keymap terratec_slim_map = { + .map = { + .scan = terratec_slim, + .size = ARRAY_SIZE(terratec_slim), + .ir_type = IR_TYPE_NEC, + .name = RC_MAP_TERRATEC_SLIM, + } +}; + +static int __init init_rc_map_terratec_slim(void) +{ + return ir_register_map(&terratec_slim_map); +} + +static void __exit exit_rc_map_terratec_slim(void) +{ + ir_unregister_map(&terratec_slim_map); +} + +module_init(init_rc_map_terratec_slim) +module_exit(exit_rc_map_terratec_slim) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); diff --git a/drivers/media/IR/keymaps/rc-total-media-in-hand.c b/drivers/media/IR/keymaps/rc-total-media-in-hand.c new file mode 100644 index 000000000000..fd1985763781 --- /dev/null +++ b/drivers/media/IR/keymaps/rc-total-media-in-hand.c @@ -0,0 +1,85 @@ +/* + * Total Media In Hand remote controller keytable + * + * Copyright (C) 2010 Antti Palosaari <crope@iki.fi> + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <media/rc-map.h> + +/* Uses NEC extended 0x02bd */ +static struct ir_scancode total_media_in_hand[] = { + { 0x02bd00, KEY_1 }, + { 0x02bd01, KEY_2 }, + { 0x02bd02, KEY_3 }, + { 0x02bd03, KEY_4 }, + { 0x02bd04, KEY_5 }, + { 0x02bd05, KEY_6 }, + { 0x02bd06, KEY_7 }, + { 0x02bd07, KEY_8 }, + { 0x02bd08, KEY_9 }, + { 0x02bd09, KEY_0 }, + { 0x02bd0a, KEY_MUTE }, + { 0x02bd0b, KEY_CYCLEWINDOWS }, /* yellow, [min / max] */ + { 0x02bd0c, KEY_VIDEO }, /* TV / AV */ + { 0x02bd0e, KEY_VOLUMEDOWN }, + { 0x02bd0f, KEY_TIME }, /* TimeShift */ + { 0x02bd10, KEY_RIGHT }, /* right arrow */ + { 0x02bd11, KEY_LEFT }, /* left arrow */ + { 0x02bd12, KEY_UP }, /* up arrow */ + { 0x02bd13, KEY_DOWN }, /* down arrow */ + { 0x02bd14, KEY_POWER2 }, /* [red] */ + { 0x02bd15, KEY_OK }, /* OK */ + { 0x02bd16, KEY_STOP }, + { 0x02bd17, KEY_CAMERA }, /* Snapshot */ + { 0x02bd18, KEY_CHANNELUP }, + { 0x02bd19, KEY_RECORD }, + { 0x02bd1a, KEY_CHANNELDOWN }, + { 0x02bd1c, KEY_ESC }, /* Esc */ + { 0x02bd1e, KEY_PLAY }, + { 0x02bd1f, KEY_VOLUMEUP }, + { 0x02bd40, KEY_PAUSE }, + { 0x02bd41, KEY_FASTFORWARD }, /* FF >> */ + { 0x02bd42, KEY_REWIND }, /* FR << */ + { 0x02bd43, KEY_ZOOM }, /* [window + mouse pointer] */ + { 0x02bd44, KEY_SHUFFLE }, /* Shuffle */ + { 0x02bd45, KEY_INFO }, /* [red (I)] */ +}; + +static struct rc_keymap total_media_in_hand_map = { + .map = { + .scan = total_media_in_hand, + .size = ARRAY_SIZE(total_media_in_hand), + .ir_type = IR_TYPE_NEC, + .name = RC_MAP_TOTAL_MEDIA_IN_HAND, + } +}; + +static int __init init_rc_map_total_media_in_hand(void) +{ + return ir_register_map(&total_media_in_hand_map); +} + +static void __exit exit_rc_map_total_media_in_hand(void) +{ + ir_unregister_map(&total_media_in_hand_map); +} + +module_init(init_rc_map_total_media_in_hand) +module_exit(exit_rc_map_total_media_in_hand) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); diff --git a/drivers/media/IR/keymaps/rc-trekstor.c b/drivers/media/IR/keymaps/rc-trekstor.c new file mode 100644 index 000000000000..91092caca452 --- /dev/null +++ b/drivers/media/IR/keymaps/rc-trekstor.c @@ -0,0 +1,80 @@ +/* + * TrekStor remote controller keytable + * + * Copyright (C) 2010 Antti Palosaari <crope@iki.fi> + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <media/rc-map.h> + +/* TrekStor DVB-T USB Stick remote controller. */ +/* Imported from af9015.h. + Initial keytable was from Marc Schneider <macke@macke.org> */ +static struct ir_scancode trekstor[] = { + { 0x0084, KEY_0 }, + { 0x0085, KEY_MUTE }, /* Mute */ + { 0x0086, KEY_HOMEPAGE }, /* Home */ + { 0x0087, KEY_UP }, /* Up */ + { 0x0088, KEY_OK }, /* OK */ + { 0x0089, KEY_RIGHT }, /* Right */ + { 0x008a, KEY_FASTFORWARD }, /* Fast forward */ + { 0x008b, KEY_VOLUMEUP }, /* Volume + */ + { 0x008c, KEY_DOWN }, /* Down */ + { 0x008d, KEY_PLAY }, /* Play/Pause */ + { 0x008e, KEY_STOP }, /* Stop */ + { 0x008f, KEY_EPG }, /* Info/EPG */ + { 0x0090, KEY_7 }, + { 0x0091, KEY_4 }, + { 0x0092, KEY_1 }, + { 0x0093, KEY_CHANNELDOWN }, /* Channel - */ + { 0x0094, KEY_8 }, + { 0x0095, KEY_5 }, + { 0x0096, KEY_2 }, + { 0x0097, KEY_CHANNELUP }, /* Channel + */ + { 0x0098, KEY_9 }, + { 0x0099, KEY_6 }, + { 0x009a, KEY_3 }, + { 0x009b, KEY_VOLUMEDOWN }, /* Volume - */ + { 0x009c, KEY_TV }, /* TV */ + { 0x009d, KEY_RECORD }, /* Record */ + { 0x009e, KEY_REWIND }, /* Rewind */ + { 0x009f, KEY_LEFT }, /* Left */ +}; + +static struct rc_keymap trekstor_map = { + .map = { + .scan = trekstor, + .size = ARRAY_SIZE(trekstor), + .ir_type = IR_TYPE_NEC, + .name = RC_MAP_TREKSTOR, + } +}; + +static int __init init_rc_map_trekstor(void) +{ + return ir_register_map(&trekstor_map); +} + +static void __exit exit_rc_map_trekstor(void) +{ + ir_unregister_map(&trekstor_map); +} + +module_init(init_rc_map_trekstor) +module_exit(exit_rc_map_trekstor) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); diff --git a/drivers/media/IR/keymaps/rc-twinhan1027.c b/drivers/media/IR/keymaps/rc-twinhan1027.c new file mode 100644 index 000000000000..0b5d356c2d84 --- /dev/null +++ b/drivers/media/IR/keymaps/rc-twinhan1027.c @@ -0,0 +1,87 @@ +#include <media/rc-map.h> + +static struct ir_scancode twinhan_vp1027[] = { + { 0x16, KEY_POWER2 }, + { 0x17, KEY_FAVORITES }, + { 0x0f, KEY_TEXT }, + { 0x48, KEY_INFO}, + { 0x1c, KEY_EPG }, + { 0x04, KEY_LIST }, + + { 0x03, KEY_1 }, + { 0x01, KEY_2 }, + { 0x06, KEY_3 }, + { 0x09, KEY_4 }, + { 0x1d, KEY_5 }, + { 0x1f, KEY_6 }, + { 0x0d, KEY_7 }, + { 0x19, KEY_8 }, + { 0x1b, KEY_9 }, + { 0x15, KEY_0 }, + + { 0x0c, KEY_CANCEL }, + { 0x4a, KEY_CLEAR }, + { 0x13, KEY_BACKSPACE }, + { 0x00, KEY_TAB }, + + { 0x4b, KEY_UP }, + { 0x51, KEY_DOWN }, + { 0x4e, KEY_LEFT }, + { 0x52, KEY_RIGHT }, + { 0x4f, KEY_ENTER }, + + { 0x1e, KEY_VOLUMEUP }, + { 0x0a, KEY_VOLUMEDOWN }, + { 0x02, KEY_CHANNELDOWN }, + { 0x05, KEY_CHANNELUP }, + { 0x11, KEY_RECORD }, + + { 0x14, KEY_PLAY }, + { 0x4c, KEY_PAUSE }, + { 0x1a, KEY_STOP }, + { 0x40, KEY_REWIND }, + { 0x12, KEY_FASTFORWARD }, + { 0x41, KEY_PREVIOUSSONG }, + { 0x42, KEY_NEXTSONG }, + { 0x54, KEY_SAVE }, + { 0x50, KEY_LANGUAGE }, + { 0x47, KEY_MEDIA }, + { 0x4d, KEY_SCREEN }, + { 0x43, KEY_SUBTITLE }, + { 0x10, KEY_MUTE }, + { 0x49, KEY_AUDIO }, + { 0x07, KEY_SLEEP }, + { 0x08, KEY_VIDEO }, + { 0x0e, KEY_AGAIN }, + { 0x45, KEY_EQUAL }, + { 0x46, KEY_MINUS }, + { 0x18, KEY_RED }, + { 0x53, KEY_GREEN }, + { 0x5e, KEY_YELLOW }, + { 0x5f, KEY_BLUE }, +}; + +static struct rc_keymap twinhan_vp1027_map = { + .map = { + .scan = twinhan_vp1027, + .size = ARRAY_SIZE(twinhan_vp1027), + .ir_type = IR_TYPE_UNKNOWN, /* Legacy IR type */ + .name = RC_MAP_TWINHAN_VP1027_DVBS, + } +}; + +static int __init init_rc_map_twinhan_vp1027(void) +{ + return ir_register_map(&twinhan_vp1027_map); +} + +static void __exit exit_rc_map_twinhan_vp1027(void) +{ + ir_unregister_map(&twinhan_vp1027_map); +} + +module_init(init_rc_map_twinhan_vp1027) +module_exit(exit_rc_map_twinhan_vp1027) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Sergey Ivanov <123kash@gmail.com>"); diff --git a/drivers/media/IR/lirc_dev.c b/drivers/media/IR/lirc_dev.c index 202581808bdc..8418b14ee4d2 100644 --- a/drivers/media/IR/lirc_dev.c +++ b/drivers/media/IR/lirc_dev.c @@ -57,13 +57,12 @@ struct irctl { struct task_struct *task; long jiffies_to_wait; - - struct cdev cdev; }; static DEFINE_MUTEX(lirc_dev_lock); static struct irctl *irctls[MAX_IRCTL_DEVICES]; +static struct cdev cdevs[MAX_IRCTL_DEVICES]; /* Only used for sysfs but defined to void otherwise */ static struct class *lirc_class; @@ -71,15 +70,13 @@ static struct class *lirc_class; /* helper function * initializes the irctl structure */ -static void init_irctl(struct irctl *ir) +static void lirc_irctl_init(struct irctl *ir) { - dev_dbg(ir->d.dev, LOGHEAD "initializing irctl\n", - ir->d.name, ir->d.minor); mutex_init(&ir->irctl_lock); ir->d.minor = NOPLUG; } -static void cleanup(struct irctl *ir) +static void lirc_irctl_cleanup(struct irctl *ir) { dev_dbg(ir->d.dev, LOGHEAD "cleaning up\n", ir->d.name, ir->d.minor); @@ -96,7 +93,7 @@ static void cleanup(struct irctl *ir) * reads key codes from driver and puts them into buffer * returns 0 on success */ -static int add_to_buf(struct irctl *ir) +static int lirc_add_to_buf(struct irctl *ir) { if (ir->d.add_to_buf) { int res = -ENODATA; @@ -139,7 +136,7 @@ static int lirc_thread(void *irctl) } if (kthread_should_stop()) break; - if (!add_to_buf(ir)) + if (!lirc_add_to_buf(ir)) wake_up_interruptible(&ir->buf->wait_poll); } else { set_current_state(TASK_INTERRUPTIBLE); @@ -154,12 +151,15 @@ static int lirc_thread(void *irctl) } -static struct file_operations fops = { +static struct file_operations lirc_dev_fops = { .owner = THIS_MODULE, .read = lirc_dev_fop_read, .write = lirc_dev_fop_write, .poll = lirc_dev_fop_poll, .unlocked_ioctl = lirc_dev_fop_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = lirc_dev_fop_ioctl, +#endif .open = lirc_dev_fop_open, .release = lirc_dev_fop_close, .llseek = noop_llseek, @@ -169,19 +169,20 @@ static int lirc_cdev_add(struct irctl *ir) { int retval; struct lirc_driver *d = &ir->d; + struct cdev *cdev = &cdevs[d->minor]; if (d->fops) { - cdev_init(&ir->cdev, d->fops); - ir->cdev.owner = d->owner; + cdev_init(cdev, d->fops); + cdev->owner = d->owner; } else { - cdev_init(&ir->cdev, &fops); - ir->cdev.owner = THIS_MODULE; + cdev_init(cdev, &lirc_dev_fops); + cdev->owner = THIS_MODULE; } - kobject_set_name(&ir->cdev.kobj, "lirc%d", d->minor); + kobject_set_name(&cdev->kobj, "lirc%d", d->minor); - retval = cdev_add(&ir->cdev, MKDEV(MAJOR(lirc_base_dev), d->minor), 1); + retval = cdev_add(cdev, MKDEV(MAJOR(lirc_base_dev), d->minor), 1); if (retval) - kobject_put(&ir->cdev.kobj); + kobject_put(&cdev->kobj); return retval; } @@ -202,6 +203,12 @@ int lirc_register_driver(struct lirc_driver *d) goto out; } + if (!d->dev) { + printk(KERN_ERR "%s: dev pointer not filled in!\n", __func__); + err = -EINVAL; + goto out; + } + if (MAX_IRCTL_DEVICES <= d->minor) { dev_err(d->dev, "lirc_dev: lirc_register_driver: " "\"minor\" must be between 0 and %d (%d)!\n", @@ -277,7 +284,7 @@ int lirc_register_driver(struct lirc_driver *d) err = -ENOMEM; goto out_lock; } - init_irctl(ir); + lirc_irctl_init(ir); irctls[minor] = ir; d->minor = minor; @@ -316,7 +323,6 @@ int lirc_register_driver(struct lirc_driver *d) d->features = LIRC_CAN_REC_LIRCCODE; ir->d = *d; - ir->d.minor = minor; device_create(lirc_class, ir->d.dev, MKDEV(MAJOR(lirc_base_dev), ir->d.minor), NULL, @@ -357,21 +363,28 @@ EXPORT_SYMBOL(lirc_register_driver); int lirc_unregister_driver(int minor) { struct irctl *ir; + struct cdev *cdev; if (minor < 0 || minor >= MAX_IRCTL_DEVICES) { - printk(KERN_ERR "lirc_dev: lirc_unregister_driver: " - "\"minor (%d)\" must be between 0 and %d!\n", - minor, MAX_IRCTL_DEVICES-1); + printk(KERN_ERR "lirc_dev: %s: minor (%d) must be between " + "0 and %d!\n", __func__, minor, MAX_IRCTL_DEVICES-1); return -EBADRQC; } ir = irctls[minor]; + if (!ir) { + printk(KERN_ERR "lirc_dev: %s: failed to get irctl struct " + "for minor %d!\n", __func__, minor); + return -ENOENT; + } + + cdev = &cdevs[minor]; mutex_lock(&lirc_dev_lock); if (ir->d.minor != minor) { - printk(KERN_ERR "lirc_dev: lirc_unregister_driver: " - "minor (%d) device not registered!", minor); + printk(KERN_ERR "lirc_dev: %s: minor (%d) device not " + "registered!\n", __func__, minor); mutex_unlock(&lirc_dev_lock); return -ENOENT; } @@ -390,12 +403,11 @@ int lirc_unregister_driver(int minor) wake_up_interruptible(&ir->buf->wait_poll); mutex_lock(&ir->irctl_lock); ir->d.set_use_dec(ir->d.data); - module_put(ir->d.owner); + module_put(cdev->owner); mutex_unlock(&ir->irctl_lock); - cdev_del(&ir->cdev); } else { - cleanup(ir); - cdev_del(&ir->cdev); + lirc_irctl_cleanup(ir); + cdev_del(cdev); kfree(ir); irctls[minor] = NULL; } @@ -409,6 +421,7 @@ EXPORT_SYMBOL(lirc_unregister_driver); int lirc_dev_fop_open(struct inode *inode, struct file *file) { struct irctl *ir; + struct cdev *cdev; int retval = 0; if (iminor(inode) >= MAX_IRCTL_DEVICES) { @@ -425,7 +438,6 @@ int lirc_dev_fop_open(struct inode *inode, struct file *file) retval = -ENODEV; goto error; } - file->private_data = ir; dev_dbg(ir->d.dev, LOGHEAD "open called\n", ir->d.name, ir->d.minor); @@ -439,13 +451,14 @@ int lirc_dev_fop_open(struct inode *inode, struct file *file) goto error; } - if (try_module_get(ir->d.owner)) { - ++ir->open; + cdev = &cdevs[iminor(inode)]; + if (try_module_get(cdev->owner)) { + ir->open++; retval = ir->d.set_use_inc(ir->d.data); if (retval) { - module_put(ir->d.owner); - --ir->open; + module_put(cdev->owner); + ir->open--; } else { lirc_buffer_clear(ir->buf); } @@ -469,17 +482,24 @@ EXPORT_SYMBOL(lirc_dev_fop_open); int lirc_dev_fop_close(struct inode *inode, struct file *file) { struct irctl *ir = irctls[iminor(inode)]; + struct cdev *cdev = &cdevs[iminor(inode)]; + + if (!ir) { + printk(KERN_ERR "%s: called with invalid irctl\n", __func__); + return -EINVAL; + } dev_dbg(ir->d.dev, LOGHEAD "close called\n", ir->d.name, ir->d.minor); WARN_ON(mutex_lock_killable(&lirc_dev_lock)); - --ir->open; + ir->open--; if (ir->attached) { ir->d.set_use_dec(ir->d.data); - module_put(ir->d.owner); + module_put(cdev->owner); } else { - cleanup(ir); + lirc_irctl_cleanup(ir); + cdev_del(cdev); irctls[ir->d.minor] = NULL; kfree(ir); } @@ -495,6 +515,11 @@ unsigned int lirc_dev_fop_poll(struct file *file, poll_table *wait) struct irctl *ir = irctls[iminor(file->f_dentry->d_inode)]; unsigned int ret; + if (!ir) { + printk(KERN_ERR "%s: called with invalid irctl\n", __func__); + return POLLERR; + } + dev_dbg(ir->d.dev, LOGHEAD "poll called\n", ir->d.name, ir->d.minor); if (!ir->attached) { @@ -521,9 +546,14 @@ EXPORT_SYMBOL(lirc_dev_fop_poll); long lirc_dev_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { - unsigned long mode; + __u32 mode; int result = 0; - struct irctl *ir = file->private_data; + struct irctl *ir = irctls[iminor(file->f_dentry->d_inode)]; + + if (!ir) { + printk(KERN_ERR "lirc_dev: %s: no irctl found!\n", __func__); + return -ENODEV; + } dev_dbg(ir->d.dev, LOGHEAD "ioctl called (0x%x)\n", ir->d.name, ir->d.minor, cmd); @@ -538,7 +568,7 @@ long lirc_dev_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg) switch (cmd) { case LIRC_GET_FEATURES: - result = put_user(ir->d.features, (unsigned long *)arg); + result = put_user(ir->d.features, (__u32 *)arg); break; case LIRC_GET_REC_MODE: if (!(ir->d.features & LIRC_CAN_REC_MASK)) { @@ -548,7 +578,7 @@ long lirc_dev_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg) result = put_user(LIRC_REC2MODE (ir->d.features & LIRC_CAN_REC_MASK), - (unsigned long *)arg); + (__u32 *)arg); break; case LIRC_SET_REC_MODE: if (!(ir->d.features & LIRC_CAN_REC_MASK)) { @@ -556,7 +586,7 @@ long lirc_dev_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg) break; } - result = get_user(mode, (unsigned long *)arg); + result = get_user(mode, (__u32 *)arg); if (!result && !(LIRC_MODE2REC(mode) & ir->d.features)) result = -EINVAL; /* @@ -565,7 +595,7 @@ long lirc_dev_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg) */ break; case LIRC_GET_LENGTH: - result = put_user(ir->d.code_length, (unsigned long *)arg); + result = put_user(ir->d.code_length, (__u32 *)arg); break; case LIRC_GET_MIN_TIMEOUT: if (!(ir->d.features & LIRC_CAN_SET_REC_TIMEOUT) || @@ -574,7 +604,7 @@ long lirc_dev_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg) break; } - result = put_user(ir->d.min_timeout, (unsigned long *)arg); + result = put_user(ir->d.min_timeout, (__u32 *)arg); break; case LIRC_GET_MAX_TIMEOUT: if (!(ir->d.features & LIRC_CAN_SET_REC_TIMEOUT) || @@ -583,7 +613,7 @@ long lirc_dev_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg) break; } - result = put_user(ir->d.max_timeout, (unsigned long *)arg); + result = put_user(ir->d.max_timeout, (__u32 *)arg); break; default: result = -EINVAL; @@ -604,12 +634,21 @@ ssize_t lirc_dev_fop_read(struct file *file, loff_t *ppos) { struct irctl *ir = irctls[iminor(file->f_dentry->d_inode)]; - unsigned char buf[ir->chunk_size]; + unsigned char *buf; int ret = 0, written = 0; DECLARE_WAITQUEUE(wait, current); + if (!ir) { + printk(KERN_ERR "%s: called with invalid irctl\n", __func__); + return -ENODEV; + } + dev_dbg(ir->d.dev, LOGHEAD "read called\n", ir->d.name, ir->d.minor); + buf = kzalloc(ir->chunk_size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + if (mutex_lock_interruptible(&ir->irctl_lock)) return -ERESTARTSYS; if (!ir->attached) { @@ -681,6 +720,7 @@ ssize_t lirc_dev_fop_read(struct file *file, mutex_unlock(&ir->irctl_lock); out_unlocked: + kfree(buf); dev_dbg(ir->d.dev, LOGHEAD "read result = %s (%d)\n", ir->d.name, ir->d.minor, ret ? "-EFAULT" : "OK", ret); @@ -709,6 +749,11 @@ ssize_t lirc_dev_fop_write(struct file *file, const char *buffer, { struct irctl *ir = irctls[iminor(file->f_dentry->d_inode)]; + if (!ir) { + printk(KERN_ERR "%s: called with invalid irctl\n", __func__); + return -ENODEV; + } + dev_dbg(ir->d.dev, LOGHEAD "write called\n", ir->d.name, ir->d.minor); if (!ir->attached) diff --git a/drivers/media/IR/mceusb.c b/drivers/media/IR/mceusb.c index bc620e10ef77..9dce684fd231 100644 --- a/drivers/media/IR/mceusb.c +++ b/drivers/media/IR/mceusb.c @@ -46,24 +46,58 @@ "device driver" #define DRIVER_NAME "mceusb" -#define USB_BUFLEN 32 /* USB reception buffer length */ -#define USB_CTRL_MSG_SZ 2 /* Size of usb ctrl msg on gen1 hw */ -#define MCE_G1_INIT_MSGS 40 /* Init messages on gen1 hw to throw out */ +#define USB_BUFLEN 32 /* USB reception buffer length */ +#define USB_CTRL_MSG_SZ 2 /* Size of usb ctrl msg on gen1 hw */ +#define MCE_G1_INIT_MSGS 40 /* Init messages on gen1 hw to throw out */ /* 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 0x1F /* Packet length mask */ +#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_IRDATA_HEADER 0x84 /* Actual header format is 0x80 + num_bytes */ +#define MCE_IRDATA_TRAILER 0x80 /* End of IR data */ +#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 /* Vals: 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_HW_CMD_HEADER 0xff /* MCE hardware command header */ +#define MCE_COMMAND_HEADER 0x9f /* MCE command header */ +#define MCE_COMMAND_MASK 0xe0 /* Mask out command bits */ +#define MCE_COMMAND_NULL 0x00 /* These show up various places... */ +/* if buf[i] & MCE_COMMAND_MASK == 0x80 and buf[i] != MCE_COMMAND_HEADER, + * then we're looking at a raw IR data sample */ +#define MCE_COMMAND_IRDATA 0x80 +#define MCE_PACKET_LENGTH_MASK 0x1f /* Packet length mask */ + +/* Sub-commands, which follow MCE_COMMAND_HEADER or MCE_HW_CMD_HEADER */ +#define MCE_CMD_PING 0x03 /* Ping device */ +#define MCE_CMD_UNKNOWN 0x04 /* Unknown */ +#define MCE_CMD_UNKNOWN2 0x05 /* Unknown */ +#define MCE_CMD_S_CARRIER 0x06 /* Set TX carrier frequency */ +#define MCE_CMD_G_CARRIER 0x07 /* Get TX carrier frequency */ +#define MCE_CMD_S_TXMASK 0x08 /* Set TX port bitmask */ +#define MCE_CMD_UNKNOWN3 0x09 /* Unknown */ +#define MCE_CMD_UNKNOWN4 0x0a /* Unknown */ +#define MCE_CMD_G_REVISION 0x0b /* Get hw/sw revision */ +#define MCE_CMD_S_TIMEOUT 0x0c /* Set RX timeout value */ +#define MCE_CMD_G_TIMEOUT 0x0d /* Get RX timeout value */ +#define MCE_CMD_UNKNOWN5 0x0e /* Unknown */ +#define MCE_CMD_UNKNOWN6 0x0f /* Unknown */ +#define MCE_CMD_G_RXPORTSTS 0x11 /* Get RX port status */ +#define MCE_CMD_G_TXMASK 0x13 /* Set TX port bitmask */ +#define MCE_CMD_S_RXSENSOR 0x14 /* Set RX sensor (std/learning) */ +#define MCE_CMD_G_RXSENSOR 0x15 /* Get RX sensor (std/learning) */ +#define MCE_CMD_TX_PORTS 0x16 /* Get number of TX ports */ +#define MCE_CMD_G_WAKESRC 0x17 /* Get wake source */ +#define MCE_CMD_UNKNOWN7 0x18 /* Unknown */ +#define MCE_CMD_UNKNOWN8 0x19 /* Unknown */ +#define MCE_CMD_UNKNOWN9 0x1b /* Unknown */ +#define MCE_CMD_DEVICE_RESET 0xaa /* Reset the hardware */ +#define MCE_RSP_CMD_INVALID 0xfe /* Invalid command issued */ /* module parameters */ @@ -104,14 +138,64 @@ static int debug; #define VENDOR_NORTHSTAR 0x04eb #define VENDOR_REALTEK 0x0bda #define VENDOR_TIVO 0x105a +#define VENDOR_CONEXANT 0x0572 + +enum mceusb_model_type { + MCE_GEN2 = 0, /* Most boards */ + MCE_GEN1, + MCE_GEN3, + MCE_GEN2_TX_INV, + POLARIS_EVK, +}; + +struct mceusb_model { + u32 mce_gen1:1; + u32 mce_gen2:1; + u32 mce_gen3:1; + u32 tx_mask_inverted:1; + u32 is_polaris:1; + + const char *rc_map; /* Allow specify a per-board map */ + const char *name; /* per-board name */ +}; + +static const struct mceusb_model mceusb_model[] = { + [MCE_GEN1] = { + .mce_gen1 = 1, + .tx_mask_inverted = 1, + }, + [MCE_GEN2] = { + .mce_gen2 = 1, + }, + [MCE_GEN2_TX_INV] = { + .mce_gen2 = 1, + .tx_mask_inverted = 1, + }, + [MCE_GEN3] = { + .mce_gen3 = 1, + .tx_mask_inverted = 1, + }, + [POLARIS_EVK] = { + .is_polaris = 1, + /* + * In fact, the EVK is shipped without + * remotes, but we should have something handy, + * to allow testing it + */ + .rc_map = RC_MAP_RC5_HAUPPAUGE_NEW, + .name = "cx231xx MCE IR", + }, +}; static struct usb_device_id mceusb_dev_table[] = { /* Original Microsoft MCE IR Transceiver (often HP-branded) */ - { USB_DEVICE(VENDOR_MICROSOFT, 0x006d) }, + { USB_DEVICE(VENDOR_MICROSOFT, 0x006d), + .driver_info = MCE_GEN1 }, /* Philips Infrared Transceiver - Sahara branded */ { USB_DEVICE(VENDOR_PHILIPS, 0x0608) }, /* Philips Infrared Transceiver - HP branded */ - { USB_DEVICE(VENDOR_PHILIPS, 0x060c) }, + { USB_DEVICE(VENDOR_PHILIPS, 0x060c), + .driver_info = MCE_GEN2_TX_INV }, /* Philips SRM5100 */ { USB_DEVICE(VENDOR_PHILIPS, 0x060d) }, /* Philips Infrared Transceiver - Omaura */ @@ -127,11 +211,14 @@ static struct usb_device_id mceusb_dev_table[] = { /* Realtek MCE IR Receiver */ { USB_DEVICE(VENDOR_REALTEK, 0x0161) }, /* SMK/Toshiba G83C0004D410 */ - { USB_DEVICE(VENDOR_SMK, 0x031d) }, + { USB_DEVICE(VENDOR_SMK, 0x031d), + .driver_info = MCE_GEN2_TX_INV }, /* SMK eHome Infrared Transceiver (Sony VAIO) */ - { USB_DEVICE(VENDOR_SMK, 0x0322) }, + { USB_DEVICE(VENDOR_SMK, 0x0322), + .driver_info = MCE_GEN2_TX_INV }, /* bundled with Hauppauge PVR-150 */ - { USB_DEVICE(VENDOR_SMK, 0x0334) }, + { USB_DEVICE(VENDOR_SMK, 0x0334), + .driver_info = MCE_GEN2_TX_INV }, /* SMK eHome Infrared Transceiver */ { USB_DEVICE(VENDOR_SMK, 0x0338) }, /* Tatung eHome Infrared Transceiver */ @@ -145,17 +232,23 @@ static struct usb_device_id mceusb_dev_table[] = { /* Mitsumi */ { USB_DEVICE(VENDOR_MITSUMI, 0x2501) }, /* Topseed eHome Infrared Transceiver */ - { USB_DEVICE(VENDOR_TOPSEED, 0x0001) }, + { USB_DEVICE(VENDOR_TOPSEED, 0x0001), + .driver_info = MCE_GEN2_TX_INV }, /* Topseed HP eHome Infrared Transceiver */ - { USB_DEVICE(VENDOR_TOPSEED, 0x0006) }, + { USB_DEVICE(VENDOR_TOPSEED, 0x0006), + .driver_info = MCE_GEN2_TX_INV }, /* Topseed eHome Infrared Transceiver */ - { USB_DEVICE(VENDOR_TOPSEED, 0x0007) }, + { USB_DEVICE(VENDOR_TOPSEED, 0x0007), + .driver_info = MCE_GEN2_TX_INV }, /* Topseed eHome Infrared Transceiver */ - { USB_DEVICE(VENDOR_TOPSEED, 0x0008) }, + { USB_DEVICE(VENDOR_TOPSEED, 0x0008), + .driver_info = MCE_GEN3 }, /* Topseed eHome Infrared Transceiver */ - { USB_DEVICE(VENDOR_TOPSEED, 0x000a) }, + { USB_DEVICE(VENDOR_TOPSEED, 0x000a), + .driver_info = MCE_GEN2_TX_INV }, /* Topseed eHome Infrared Transceiver */ - { USB_DEVICE(VENDOR_TOPSEED, 0x0011) }, + { USB_DEVICE(VENDOR_TOPSEED, 0x0011), + .driver_info = MCE_GEN2_TX_INV }, /* Ricavision internal Infrared Transceiver */ { USB_DEVICE(VENDOR_RICAVISION, 0x0010) }, /* Itron ione Libra Q-11 */ @@ -185,7 +278,8 @@ static struct usb_device_id mceusb_dev_table[] = { /* Fintek eHome Infrared Transceiver (in the AOpen MP45) */ { USB_DEVICE(VENDOR_FINTEK, 0x0702) }, /* Pinnacle Remote Kit */ - { USB_DEVICE(VENDOR_PINNACLE, 0x0225) }, + { USB_DEVICE(VENDOR_PINNACLE, 0x0225), + .driver_info = MCE_GEN3 }, /* Elitegroup Computer Systems IR */ { USB_DEVICE(VENDOR_ECS, 0x0f38) }, /* Wistron Corp. eHome Infrared Receiver */ @@ -198,37 +292,13 @@ static struct usb_device_id mceusb_dev_table[] = { { USB_DEVICE(VENDOR_NORTHSTAR, 0xe004) }, /* TiVo PC IR Receiver */ { USB_DEVICE(VENDOR_TIVO, 0x2000) }, + /* Conexant SDK */ + { USB_DEVICE(VENDOR_CONEXANT, 0x58a1), + .driver_info = POLARIS_EVK }, /* Terminating entry */ { } }; -static struct usb_device_id gen3_list[] = { - { USB_DEVICE(VENDOR_PINNACLE, 0x0225) }, - { USB_DEVICE(VENDOR_TOPSEED, 0x0008) }, - {} -}; - -static struct usb_device_id microsoft_gen1_list[] = { - { USB_DEVICE(VENDOR_MICROSOFT, 0x006d) }, - {} -}; - -static struct usb_device_id std_tx_mask_list[] = { - { USB_DEVICE(VENDOR_MICROSOFT, 0x006d) }, - { USB_DEVICE(VENDOR_PHILIPS, 0x060c) }, - { 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_TOPSEED, 0x0011) }, - { USB_DEVICE(VENDOR_PINNACLE, 0x0225) }, - {} -}; - /* data structure for each usb transceiver */ struct mceusb_dev { /* ir-core bits */ @@ -248,8 +318,15 @@ struct mceusb_dev { /* buffers and dma */ unsigned char *buf_in; unsigned int len_in; - u8 cmd; /* MCE command type */ - u8 rem; /* Remaining IR data bytes in packet */ + + enum { + CMD_HEADER = 0, + SUBCMD, + CMD_DATA, + PARSE_IRDATA, + } parser_state; + u8 cmd, rem; /* Remaining IR data bytes in packet */ + dma_addr_t dma_in; dma_addr_t dma_out; @@ -257,7 +334,6 @@ struct mceusb_dev { u32 connected:1; u32 tx_mask_inverted:1; u32 microsoft_gen1:1; - u32 reserved:29; } flags; /* transmit support */ @@ -267,6 +343,7 @@ struct mceusb_dev { char name[128]; char phys[64]; + enum mceusb_model_type model; }; /* @@ -291,43 +368,81 @@ struct mceusb_dev { * - SET_RX_TIMEOUT sets the receiver timeout * - SET_RX_SENSOR sets which receiver sensor to use */ -static char DEVICE_RESET[] = {0x00, 0xff, 0xaa}; -static char GET_REVISION[] = {0xff, 0x0b}; -static char GET_UNKNOWN[] = {0xff, 0x18}; -static char GET_UNKNOWN2[] = {0x9f, 0x05}; -static char GET_CARRIER_FREQ[] = {0x9f, 0x07}; -static char GET_RX_TIMEOUT[] = {0x9f, 0x0d}; -static char GET_TX_BITMASK[] = {0x9f, 0x13}; -static char GET_RX_SENSOR[] = {0x9f, 0x15}; +static char DEVICE_RESET[] = {MCE_COMMAND_NULL, MCE_HW_CMD_HEADER, + MCE_CMD_DEVICE_RESET}; +static char GET_REVISION[] = {MCE_HW_CMD_HEADER, MCE_CMD_G_REVISION}; +static char GET_UNKNOWN[] = {MCE_HW_CMD_HEADER, MCE_CMD_UNKNOWN7}; +static char GET_UNKNOWN2[] = {MCE_COMMAND_HEADER, MCE_CMD_UNKNOWN2}; +static char GET_CARRIER_FREQ[] = {MCE_COMMAND_HEADER, MCE_CMD_G_CARRIER}; +static char GET_RX_TIMEOUT[] = {MCE_COMMAND_HEADER, MCE_CMD_G_TIMEOUT}; +static char GET_TX_BITMASK[] = {MCE_COMMAND_HEADER, MCE_CMD_G_TXMASK}; +static char GET_RX_SENSOR[] = {MCE_COMMAND_HEADER, MCE_CMD_G_RXSENSOR}; /* sub in desired values in lower byte or bytes for full command */ /* FIXME: make use of these for transmit. -static char SET_CARRIER_FREQ[] = {0x9f, 0x06, 0x00, 0x00}; -static char SET_TX_BITMASK[] = {0x9f, 0x08, 0x00}; -static char SET_RX_TIMEOUT[] = {0x9f, 0x0c, 0x00, 0x00}; -static char SET_RX_SENSOR[] = {0x9f, 0x14, 0x00}; +static char SET_CARRIER_FREQ[] = {MCE_COMMAND_HEADER, + MCE_CMD_S_CARRIER, 0x00, 0x00}; +static char SET_TX_BITMASK[] = {MCE_COMMAND_HEADER, MCE_CMD_S_TXMASK, 0x00}; +static char SET_RX_TIMEOUT[] = {MCE_COMMAND_HEADER, + MCE_CMD_S_TIMEOUT, 0x00, 0x00}; +static char SET_RX_SENSOR[] = {MCE_COMMAND_HEADER, + MCE_CMD_S_RXSENSOR, 0x00}; */ +static int mceusb_cmdsize(u8 cmd, u8 subcmd) +{ + int datasize = 0; + + switch (cmd) { + case MCE_COMMAND_NULL: + if (subcmd == MCE_HW_CMD_HEADER) + datasize = 1; + break; + case MCE_HW_CMD_HEADER: + switch (subcmd) { + case MCE_CMD_G_REVISION: + datasize = 2; + break; + } + case MCE_COMMAND_HEADER: + switch (subcmd) { + case MCE_CMD_UNKNOWN: + case MCE_CMD_S_CARRIER: + case MCE_CMD_S_TIMEOUT: + case MCE_CMD_G_RXSENSOR: + datasize = 2; + break; + case MCE_CMD_S_TXMASK: + case MCE_CMD_S_RXSENSOR: + datasize = 1; + break; + } + } + return datasize; +} + static void mceusb_dev_printdata(struct mceusb_dev *ir, char *buf, - int len, bool out) + int offset, int len, bool out) { char codes[USB_BUFLEN * 3 + 1]; char inout[9]; - int i; u8 cmd, subcmd, data1, data2; struct device *dev = ir->dev; - int idx = 0; + int i, start, skip = 0; + + if (!debug) + return; /* skip meaningless 0xb1 0x60 header bytes on orig receiver */ if (ir->flags.microsoft_gen1 && !out) - idx = 2; + skip = 2; - if (len <= idx) + if (len <= skip) return; for (i = 0; i < len && i < USB_BUFLEN; i++) - snprintf(codes + i * 3, 4, "%02x ", buf[i] & 0xFF); + snprintf(codes + i * 3, 4, "%02x ", buf[i + offset] & 0xff); - dev_info(dev, "%sx data: %s (length=%d)\n", + dev_info(dev, "%sx data: %s(length=%d)\n", (out ? "t" : "r"), codes, len); if (out) @@ -335,91 +450,93 @@ static void mceusb_dev_printdata(struct mceusb_dev *ir, char *buf, else strcpy(inout, "Got\0"); - cmd = buf[idx] & 0xff; - subcmd = buf[idx + 1] & 0xff; - data1 = buf[idx + 2] & 0xff; - data2 = buf[idx + 3] & 0xff; + start = offset + skip; + cmd = buf[start] & 0xff; + subcmd = buf[start + 1] & 0xff; + data1 = buf[start + 2] & 0xff; + data2 = buf[start + 3] & 0xff; switch (cmd) { - case 0x00: - if (subcmd == 0xff && data1 == 0xaa) + case MCE_COMMAND_NULL: + if ((subcmd == MCE_HW_CMD_HEADER) && + (data1 == MCE_CMD_DEVICE_RESET)) dev_info(dev, "Device reset requested\n"); else dev_info(dev, "Unknown command 0x%02x 0x%02x\n", cmd, subcmd); break; - case 0xff: + case MCE_HW_CMD_HEADER: switch (subcmd) { - case 0x0b: + case MCE_CMD_G_REVISION: if (len == 2) dev_info(dev, "Get hw/sw rev?\n"); else dev_info(dev, "hw/sw rev 0x%02x 0x%02x " "0x%02x 0x%02x\n", data1, data2, - buf[idx + 4], buf[idx + 5]); + buf[start + 4], buf[start + 5]); break; - case 0xaa: + case MCE_CMD_DEVICE_RESET: dev_info(dev, "Device reset requested\n"); break; - case 0xfe: + case MCE_RSP_CMD_INVALID: dev_info(dev, "Previous command not supported\n"); break; - case 0x18: - case 0x1b: + case MCE_CMD_UNKNOWN7: + case MCE_CMD_UNKNOWN9: default: dev_info(dev, "Unknown command 0x%02x 0x%02x\n", cmd, subcmd); break; } break; - case 0x9f: + case MCE_COMMAND_HEADER: switch (subcmd) { - case 0x03: + case MCE_CMD_PING: dev_info(dev, "Ping\n"); break; - case 0x04: + case MCE_CMD_UNKNOWN: dev_info(dev, "Resp to 9f 05 of 0x%02x 0x%02x\n", data1, data2); break; - case 0x06: + case MCE_CMD_S_CARRIER: dev_info(dev, "%s carrier mode and freq of " "0x%02x 0x%02x\n", inout, data1, data2); break; - case 0x07: + case MCE_CMD_G_CARRIER: dev_info(dev, "Get carrier mode and freq\n"); break; - case 0x08: + case MCE_CMD_S_TXMASK: dev_info(dev, "%s transmit blaster mask of 0x%02x\n", inout, data1); break; - case 0x0c: + case MCE_CMD_S_TIMEOUT: /* value is in units of 50us, so x*50/100 or x/2 ms */ dev_info(dev, "%s receive timeout of %d ms\n", inout, ((data1 << 8) | data2) / 2); break; - case 0x0d: + case MCE_CMD_G_TIMEOUT: dev_info(dev, "Get receive timeout\n"); break; - case 0x13: + case MCE_CMD_G_TXMASK: dev_info(dev, "Get transmit blaster mask\n"); break; - case 0x14: + case MCE_CMD_S_RXSENSOR: dev_info(dev, "%s %s-range receive sensor in use\n", inout, data1 == 0x02 ? "short" : "long"); break; - case 0x15: + case MCE_CMD_G_RXSENSOR: if (len == 2) dev_info(dev, "Get receive sensor\n"); else dev_info(dev, "Received pulse count is %d\n", ((data1 << 8) | data2)); break; - case 0xfe: + case MCE_RSP_CMD_INVALID: dev_info(dev, "Error! Hardware is likely wedged...\n"); break; - case 0x05: - case 0x09: - case 0x0f: + case MCE_CMD_UNKNOWN2: + case MCE_CMD_UNKNOWN3: + case MCE_CMD_UNKNOWN5: default: dev_info(dev, "Unknown command 0x%02x 0x%02x\n", cmd, subcmd); @@ -429,6 +546,12 @@ static void mceusb_dev_printdata(struct mceusb_dev *ir, char *buf, default: break; } + + if (cmd == MCE_IRDATA_TRAILER) + dev_info(dev, "End of raw IR data\n"); + else if ((cmd != MCE_COMMAND_HEADER) && + ((cmd & MCE_COMMAND_MASK) == MCE_COMMAND_IRDATA)) + dev_info(dev, "Raw IR data, %d pulse/space samples\n", ir->rem); } static void mce_async_callback(struct urb *urb, struct pt_regs *regs) @@ -446,9 +569,7 @@ static void mce_async_callback(struct urb *urb, struct pt_regs *regs) dev_dbg(ir->dev, "callback called (status=%d len=%d)\n", urb->status, len); - if (debug) - mceusb_dev_printdata(ir, urb->transfer_buffer, - len, true); + mceusb_dev_printdata(ir, urb->transfer_buffer, 0, len, true); } } @@ -536,8 +657,8 @@ static int mceusb_tx_ir(void *priv, int *txbuf, u32 n) return -ENOMEM; /* MCE tx init header */ - cmdbuf[cmdcount++] = MCE_CONTROL_HEADER; - cmdbuf[cmdcount++] = 0x08; + cmdbuf[cmdcount++] = MCE_COMMAND_HEADER; + cmdbuf[cmdcount++] = MCE_CMD_S_TXMASK; cmdbuf[cmdcount++] = ir->tx_mask; /* Generate mce packet data */ @@ -551,7 +672,7 @@ static int mceusb_tx_ir(void *priv, int *txbuf, u32 n) if ((cmdcount < MCE_CMDBUF_SIZE) && (cmdcount - MCE_TX_HEADER_LENGTH) % MCE_CODE_LENGTH == 0) - cmdbuf[cmdcount++] = MCE_PACKET_HEADER; + cmdbuf[cmdcount++] = MCE_IRDATA_HEADER; /* Insert mce packet data */ if (cmdcount < MCE_CMDBUF_SIZE) @@ -570,7 +691,8 @@ static int mceusb_tx_ir(void *priv, int *txbuf, u32 n) /* 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; + MCE_COMMAND_IRDATA + (cmdcount - MCE_TX_HEADER_LENGTH) % + MCE_CODE_LENGTH - 1; /* Check if we have room for the empty packet at the end */ if (cmdcount >= MCE_CMDBUF_SIZE) { @@ -579,7 +701,7 @@ static int mceusb_tx_ir(void *priv, int *txbuf, u32 n) } /* All mce commands end with an empty packet (0x80) */ - cmdbuf[cmdcount++] = 0x80; + cmdbuf[cmdcount++] = MCE_IRDATA_TRAILER; /* Transmit the command to the mce device */ mce_async_out(ir, cmdbuf, cmdcount); @@ -608,7 +730,8 @@ static int mceusb_set_tx_mask(void *priv, u32 mask) struct mceusb_dev *ir = priv; if (ir->flags.tx_mask_inverted) - ir->tx_mask = (mask != 0x03 ? mask ^ 0x03 : mask) << 1; + ir->tx_mask = (mask != MCE_DEFAULT_TX_MASK ? + mask ^ MCE_DEFAULT_TX_MASK : mask) << 1; else ir->tx_mask = mask; @@ -621,7 +744,8 @@ static int mceusb_set_tx_carrier(void *priv, u32 carrier) struct mceusb_dev *ir = priv; int clk = 10000000; int prescaler = 0, divisor = 0; - unsigned char cmdbuf[4] = { 0x9f, 0x06, 0x00, 0x00 }; + unsigned char cmdbuf[4] = { MCE_COMMAND_HEADER, + MCE_CMD_S_CARRIER, 0x00, 0x00 }; /* Carrier has changed */ if (ir->carrier != carrier) { @@ -629,7 +753,7 @@ static int mceusb_set_tx_carrier(void *priv, u32 carrier) if (carrier == 0) { ir->carrier = carrier; cmdbuf[2] = 0x01; - cmdbuf[3] = 0x80; + cmdbuf[3] = MCE_IRDATA_TRAILER; dev_dbg(ir->dev, "%s: disabling carrier " "modulation\n", __func__); mce_async_out(ir, cmdbuf, sizeof(cmdbuf)); @@ -638,7 +762,7 @@ static int mceusb_set_tx_carrier(void *priv, u32 carrier) for (prescaler = 0; prescaler < 4; ++prescaler) { divisor = (clk >> (2 * prescaler)) / carrier; - if (divisor <= 0xFF) { + if (divisor <= 0xff) { ir->carrier = carrier; cmdbuf[2] = prescaler; cmdbuf[3] = divisor; @@ -660,47 +784,36 @@ static int mceusb_set_tx_carrier(void *priv, u32 carrier) static void mceusb_process_ir_data(struct mceusb_dev *ir, int buf_len) { - struct ir_raw_event rawir = { .pulse = false, .duration = 0 }; - int i, start_index = 0; - u8 hdr = MCE_CONTROL_HEADER; + DEFINE_IR_RAW_EVENT(rawir); + int i = 0; /* skip meaningless 0xb1 0x60 header bytes on orig receiver */ if (ir->flags.microsoft_gen1) - start_index = 2; - - for (i = start_index; i < buf_len;) { - if (ir->rem == 0) { - /* decode mce packets of the form (84),AA,BB,CC,DD */ - /* IR data packets can span USB messages - rem */ - hdr = ir->buf_in[i]; - ir->rem = (hdr & MCE_PACKET_LENGTH_MASK); - ir->cmd = (hdr & ~MCE_PACKET_LENGTH_MASK); - dev_dbg(ir->dev, "New data. rem: 0x%02x, cmd: 0x%02x\n", - ir->rem, ir->cmd); - i++; - } - - /* don't process MCE commands */ - if (hdr == MCE_CONTROL_HEADER || hdr == 0xff) { - ir->rem = 0; - return; - } - - for (; (ir->rem > 0) && (i < buf_len); i++) { + i = 2; + + for (; i < buf_len; i++) { + switch (ir->parser_state) { + case SUBCMD: + ir->rem = mceusb_cmdsize(ir->cmd, ir->buf_in[i]); + mceusb_dev_printdata(ir, ir->buf_in, i - 1, + ir->rem + 2, false); + ir->parser_state = CMD_DATA; + break; + case PARSE_IRDATA: ir->rem--; - rawir.pulse = ((ir->buf_in[i] & MCE_PULSE_BIT) != 0); rawir.duration = (ir->buf_in[i] & MCE_PULSE_MASK) * MCE_TIME_UNIT * 1000; if ((ir->buf_in[i] & MCE_PULSE_MASK) == 0x7f) { - if (ir->rawir.pulse == rawir.pulse) + if (ir->rawir.pulse == rawir.pulse) { ir->rawir.duration += rawir.duration; - else { + } else { ir->rawir.duration = rawir.duration; ir->rawir.pulse = rawir.pulse; } - continue; + if (ir->rem) + break; } rawir.duration += ir->rawir.duration; ir->rawir.duration = 0; @@ -711,14 +824,40 @@ static void mceusb_process_ir_data(struct mceusb_dev *ir, int buf_len) rawir.duration); ir_raw_event_store(ir->idev, &rawir); + break; + case CMD_DATA: + ir->rem--; + break; + case CMD_HEADER: + /* decode mce packets of the form (84),AA,BB,CC,DD */ + /* IR data packets can span USB messages - rem */ + ir->cmd = ir->buf_in[i]; + if ((ir->cmd == MCE_COMMAND_HEADER) || + ((ir->cmd & MCE_COMMAND_MASK) != + MCE_COMMAND_IRDATA)) { + ir->parser_state = SUBCMD; + continue; + } + ir->rem = (ir->cmd & MCE_PACKET_LENGTH_MASK); + mceusb_dev_printdata(ir, ir->buf_in, i, ir->rem + 1, false); + if (ir->rem) { + ir->parser_state = PARSE_IRDATA; + break; + } + /* + * a package with len=0 (e. g. 0x80) means end of + * data. We could use it to do the call to + * ir_raw_event_handle(). For now, we don't need to + * use it. + */ + break; } - if (ir->buf_in[i] == 0x80 || ir->buf_in[i] == 0x9f) - ir->rem = 0; - - dev_dbg(ir->dev, "calling ir_raw_event_handle\n"); - ir_raw_event_handle(ir->idev); + if (ir->parser_state != CMD_HEADER && !ir->rem) + ir->parser_state = CMD_HEADER; } + dev_dbg(ir->dev, "processed IR data, calling ir_raw_event_handle\n"); + ir_raw_event_handle(ir->idev); } static void mceusb_dev_recv(struct urb *urb, struct pt_regs *regs) @@ -737,9 +876,6 @@ static void mceusb_dev_recv(struct urb *urb, struct pt_regs *regs) buf_len = urb->actual_length; - if (debug) - mceusb_dev_printdata(ir, urb->transfer_buffer, buf_len, false); - if (ir->send_flags == RECV_FLAG_IN_PROGRESS) { ir->send_flags = SEND_FLAG_COMPLETE; dev_dbg(ir->dev, "setup answer received %d bytes\n", @@ -760,6 +896,7 @@ static void mceusb_dev_recv(struct urb *urb, struct pt_regs *regs) case -EPIPE: default: + dev_dbg(ir->dev, "Error: urb status = %d\n", urb->status); break; } @@ -865,6 +1002,8 @@ static struct input_dev *mceusb_init_input_dev(struct mceusb_dev *ir) struct input_dev *idev; struct ir_dev_props *props; struct device *dev = ir->dev; + const char *rc_map = RC_MAP_RC6_MCE; + const char *name = "Media Center Ed. eHome Infrared Remote Transceiver"; int ret = -ENODEV; idev = input_allocate_device(); @@ -880,8 +1019,11 @@ static struct input_dev *mceusb_init_input_dev(struct mceusb_dev *ir) goto props_alloc_failed; } - snprintf(ir->name, sizeof(ir->name), "Media Center Ed. eHome " - "Infrared Remote Transceiver (%04x:%04x)", + if (mceusb_model[ir->model].name) + name = mceusb_model[ir->model].name; + + snprintf(ir->name, sizeof(ir->name), "%s (%04x:%04x)", + name, le16_to_cpu(ir->usbdev->descriptor.idVendor), le16_to_cpu(ir->usbdev->descriptor.idProduct)); @@ -899,7 +1041,10 @@ static struct input_dev *mceusb_init_input_dev(struct mceusb_dev *ir) ir->props = props; - ret = ir_input_register(idev, RC_MAP_RC6_MCE, props, DRIVER_NAME); + if (mceusb_model[ir->model].rc_map) + rc_map = mceusb_model[ir->model].rc_map; + + ret = ir_input_register(idev, rc_map, props, DRIVER_NAME); if (ret < 0) { dev_err(dev, "remote input device register failed\n"); goto irdev_failed; @@ -926,17 +1071,26 @@ static int __devinit mceusb_dev_probe(struct usb_interface *intf, struct mceusb_dev *ir = NULL; int pipe, maxp, i; char buf[63], name[128] = ""; + enum mceusb_model_type model = id->driver_info; bool is_gen3; bool is_microsoft_gen1; bool tx_mask_inverted; + bool is_polaris; dev_dbg(&intf->dev, ": %s called\n", __func__); idesc = intf->cur_altsetting; - is_gen3 = usb_match_id(intf, gen3_list) ? 1 : 0; - is_microsoft_gen1 = usb_match_id(intf, microsoft_gen1_list) ? 1 : 0; - tx_mask_inverted = usb_match_id(intf, std_tx_mask_list) ? 0 : 1; + is_gen3 = mceusb_model[model].mce_gen3; + is_microsoft_gen1 = mceusb_model[model].mce_gen1; + tx_mask_inverted = mceusb_model[model].tx_mask_inverted; + is_polaris = mceusb_model[model].is_polaris; + + if (is_polaris) { + /* Interface 0 is IR */ + if (idesc->desc.bInterfaceNumber) + return -ENODEV; + } /* step through the endpoints to find first bulk in and out endpoint */ for (i = 0; i < idesc->desc.bNumEndpoints; ++i) { @@ -997,6 +1151,9 @@ static int __devinit mceusb_dev_probe(struct usb_interface *intf, ir->len_in = maxp; ir->flags.microsoft_gen1 = is_microsoft_gen1; ir->flags.tx_mask_inverted = tx_mask_inverted; + ir->model = model; + + init_ir_raw_event(&ir->rawir); /* Saving usb interface data for use by the transmitter routine */ ir->usb_ep_in = ep_in; diff --git a/drivers/media/IR/nuvoton-cir.c b/drivers/media/IR/nuvoton-cir.c new file mode 100644 index 000000000000..301be53aee85 --- /dev/null +++ b/drivers/media/IR/nuvoton-cir.c @@ -0,0 +1,1246 @@ +/* + * Driver for Nuvoton Technology Corporation w83667hg/w83677hg-i CIR + * + * Copyright (C) 2010 Jarod Wilson <jarod@redhat.com> + * Copyright (C) 2009 Nuvoton PS Team + * + * Special thanks to Nuvoton for providing hardware, spec sheets and + * sample code upon which portions of this driver are based. Indirect + * thanks also to Maxim Levitsky, whose ene_ir driver this driver is + * modeled after. + * + * 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 <linux/pci_ids.h> + +#include "nuvoton-cir.h" + +static char *chip_id = "w836x7hg"; + +/* write val to config reg */ +static inline void nvt_cr_write(struct nvt_dev *nvt, u8 val, u8 reg) +{ + outb(reg, nvt->cr_efir); + outb(val, nvt->cr_efdr); +} + +/* read val from config reg */ +static inline u8 nvt_cr_read(struct nvt_dev *nvt, u8 reg) +{ + outb(reg, nvt->cr_efir); + return inb(nvt->cr_efdr); +} + +/* update config register bit without changing other bits */ +static inline void nvt_set_reg_bit(struct nvt_dev *nvt, u8 val, u8 reg) +{ + u8 tmp = nvt_cr_read(nvt, reg) | val; + nvt_cr_write(nvt, tmp, reg); +} + +/* clear config register bit without changing other bits */ +static inline void nvt_clear_reg_bit(struct nvt_dev *nvt, u8 val, u8 reg) +{ + u8 tmp = nvt_cr_read(nvt, reg) & ~val; + nvt_cr_write(nvt, tmp, reg); +} + +/* enter extended function mode */ +static inline void nvt_efm_enable(struct nvt_dev *nvt) +{ + /* Enabling Extended Function Mode explicitly requires writing 2x */ + outb(EFER_EFM_ENABLE, nvt->cr_efir); + outb(EFER_EFM_ENABLE, nvt->cr_efir); +} + +/* exit extended function mode */ +static inline void nvt_efm_disable(struct nvt_dev *nvt) +{ + outb(EFER_EFM_DISABLE, nvt->cr_efir); +} + +/* + * When you want to address a specific logical device, write its logical + * device number to CR_LOGICAL_DEV_SEL, then enable/disable by writing + * 0x1/0x0 respectively to CR_LOGICAL_DEV_EN. + */ +static inline void nvt_select_logical_dev(struct nvt_dev *nvt, u8 ldev) +{ + outb(CR_LOGICAL_DEV_SEL, nvt->cr_efir); + outb(ldev, nvt->cr_efdr); +} + +/* write val to cir config register */ +static inline void nvt_cir_reg_write(struct nvt_dev *nvt, u8 val, u8 offset) +{ + outb(val, nvt->cir_addr + offset); +} + +/* read val from cir config register */ +static u8 nvt_cir_reg_read(struct nvt_dev *nvt, u8 offset) +{ + u8 val; + + val = inb(nvt->cir_addr + offset); + + return val; +} + +/* write val to cir wake register */ +static inline void nvt_cir_wake_reg_write(struct nvt_dev *nvt, + u8 val, u8 offset) +{ + outb(val, nvt->cir_wake_addr + offset); +} + +/* read val from cir wake config register */ +static u8 nvt_cir_wake_reg_read(struct nvt_dev *nvt, u8 offset) +{ + u8 val; + + val = inb(nvt->cir_wake_addr + offset); + + return val; +} + +#define pr_reg(text, ...) \ + printk(KERN_INFO KBUILD_MODNAME ": " text, ## __VA_ARGS__) + +/* dump current cir register contents */ +static void cir_dump_regs(struct nvt_dev *nvt) +{ + nvt_efm_enable(nvt); + nvt_select_logical_dev(nvt, LOGICAL_DEV_CIR); + + pr_reg("%s: Dump CIR logical device registers:\n", NVT_DRIVER_NAME); + pr_reg(" * CR CIR ACTIVE : 0x%x\n", + nvt_cr_read(nvt, CR_LOGICAL_DEV_EN)); + pr_reg(" * CR CIR BASE ADDR: 0x%x\n", + (nvt_cr_read(nvt, CR_CIR_BASE_ADDR_HI) << 8) | + nvt_cr_read(nvt, CR_CIR_BASE_ADDR_LO)); + pr_reg(" * CR CIR IRQ NUM: 0x%x\n", + nvt_cr_read(nvt, CR_CIR_IRQ_RSRC)); + + nvt_efm_disable(nvt); + + pr_reg("%s: Dump CIR registers:\n", NVT_DRIVER_NAME); + pr_reg(" * IRCON: 0x%x\n", nvt_cir_reg_read(nvt, CIR_IRCON)); + pr_reg(" * IRSTS: 0x%x\n", nvt_cir_reg_read(nvt, CIR_IRSTS)); + pr_reg(" * IREN: 0x%x\n", nvt_cir_reg_read(nvt, CIR_IREN)); + pr_reg(" * RXFCONT: 0x%x\n", nvt_cir_reg_read(nvt, CIR_RXFCONT)); + pr_reg(" * CP: 0x%x\n", nvt_cir_reg_read(nvt, CIR_CP)); + pr_reg(" * CC: 0x%x\n", nvt_cir_reg_read(nvt, CIR_CC)); + pr_reg(" * SLCH: 0x%x\n", nvt_cir_reg_read(nvt, CIR_SLCH)); + pr_reg(" * SLCL: 0x%x\n", nvt_cir_reg_read(nvt, CIR_SLCL)); + pr_reg(" * FIFOCON: 0x%x\n", nvt_cir_reg_read(nvt, CIR_FIFOCON)); + pr_reg(" * IRFIFOSTS: 0x%x\n", nvt_cir_reg_read(nvt, CIR_IRFIFOSTS)); + pr_reg(" * SRXFIFO: 0x%x\n", nvt_cir_reg_read(nvt, CIR_SRXFIFO)); + pr_reg(" * TXFCONT: 0x%x\n", nvt_cir_reg_read(nvt, CIR_TXFCONT)); + pr_reg(" * STXFIFO: 0x%x\n", nvt_cir_reg_read(nvt, CIR_STXFIFO)); + pr_reg(" * FCCH: 0x%x\n", nvt_cir_reg_read(nvt, CIR_FCCH)); + pr_reg(" * FCCL: 0x%x\n", nvt_cir_reg_read(nvt, CIR_FCCL)); + pr_reg(" * IRFSM: 0x%x\n", nvt_cir_reg_read(nvt, CIR_IRFSM)); +} + +/* dump current cir wake register contents */ +static void cir_wake_dump_regs(struct nvt_dev *nvt) +{ + u8 i, fifo_len; + + nvt_efm_enable(nvt); + nvt_select_logical_dev(nvt, LOGICAL_DEV_CIR_WAKE); + + pr_reg("%s: Dump CIR WAKE logical device registers:\n", + NVT_DRIVER_NAME); + pr_reg(" * CR CIR WAKE ACTIVE : 0x%x\n", + nvt_cr_read(nvt, CR_LOGICAL_DEV_EN)); + pr_reg(" * CR CIR WAKE BASE ADDR: 0x%x\n", + (nvt_cr_read(nvt, CR_CIR_BASE_ADDR_HI) << 8) | + nvt_cr_read(nvt, CR_CIR_BASE_ADDR_LO)); + pr_reg(" * CR CIR WAKE IRQ NUM: 0x%x\n", + nvt_cr_read(nvt, CR_CIR_IRQ_RSRC)); + + nvt_efm_disable(nvt); + + pr_reg("%s: Dump CIR WAKE registers\n", NVT_DRIVER_NAME); + pr_reg(" * IRCON: 0x%x\n", + nvt_cir_wake_reg_read(nvt, CIR_WAKE_IRCON)); + pr_reg(" * IRSTS: 0x%x\n", + nvt_cir_wake_reg_read(nvt, CIR_WAKE_IRSTS)); + pr_reg(" * IREN: 0x%x\n", + nvt_cir_wake_reg_read(nvt, CIR_WAKE_IREN)); + pr_reg(" * FIFO CMP DEEP: 0x%x\n", + nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFO_CMP_DEEP)); + pr_reg(" * FIFO CMP TOL: 0x%x\n", + nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFO_CMP_TOL)); + pr_reg(" * FIFO COUNT: 0x%x\n", + nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFO_COUNT)); + pr_reg(" * SLCH: 0x%x\n", + nvt_cir_wake_reg_read(nvt, CIR_WAKE_SLCH)); + pr_reg(" * SLCL: 0x%x\n", + nvt_cir_wake_reg_read(nvt, CIR_WAKE_SLCL)); + pr_reg(" * FIFOCON: 0x%x\n", + nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFOCON)); + pr_reg(" * SRXFSTS: 0x%x\n", + nvt_cir_wake_reg_read(nvt, CIR_WAKE_SRXFSTS)); + pr_reg(" * SAMPLE RX FIFO: 0x%x\n", + nvt_cir_wake_reg_read(nvt, CIR_WAKE_SAMPLE_RX_FIFO)); + pr_reg(" * WR FIFO DATA: 0x%x\n", + nvt_cir_wake_reg_read(nvt, CIR_WAKE_WR_FIFO_DATA)); + pr_reg(" * RD FIFO ONLY: 0x%x\n", + nvt_cir_wake_reg_read(nvt, CIR_WAKE_RD_FIFO_ONLY)); + pr_reg(" * RD FIFO ONLY IDX: 0x%x\n", + nvt_cir_wake_reg_read(nvt, CIR_WAKE_RD_FIFO_ONLY_IDX)); + pr_reg(" * FIFO IGNORE: 0x%x\n", + nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFO_IGNORE)); + pr_reg(" * IRFSM: 0x%x\n", + nvt_cir_wake_reg_read(nvt, CIR_WAKE_IRFSM)); + + fifo_len = nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFO_COUNT); + pr_reg("%s: Dump CIR WAKE FIFO (len %d)\n", NVT_DRIVER_NAME, fifo_len); + pr_reg("* Contents = "); + for (i = 0; i < fifo_len; i++) + printk(KERN_CONT "%02x ", + nvt_cir_wake_reg_read(nvt, CIR_WAKE_RD_FIFO_ONLY)); + printk(KERN_CONT "\n"); +} + +/* detect hardware features */ +static int nvt_hw_detect(struct nvt_dev *nvt) +{ + unsigned long flags; + u8 chip_major, chip_minor; + int ret = 0; + + nvt_efm_enable(nvt); + + /* Check if we're wired for the alternate EFER setup */ + chip_major = nvt_cr_read(nvt, CR_CHIP_ID_HI); + if (chip_major == 0xff) { + nvt->cr_efir = CR_EFIR2; + nvt->cr_efdr = CR_EFDR2; + nvt_efm_enable(nvt); + chip_major = nvt_cr_read(nvt, CR_CHIP_ID_HI); + } + + chip_minor = nvt_cr_read(nvt, CR_CHIP_ID_LO); + nvt_dbg("%s: chip id: 0x%02x 0x%02x", chip_id, chip_major, chip_minor); + + if (chip_major != CHIP_ID_HIGH && + (chip_minor != CHIP_ID_LOW || chip_minor != CHIP_ID_LOW2)) + ret = -ENODEV; + + nvt_efm_disable(nvt); + + spin_lock_irqsave(&nvt->nvt_lock, flags); + nvt->chip_major = chip_major; + nvt->chip_minor = chip_minor; + spin_unlock_irqrestore(&nvt->nvt_lock, flags); + + return ret; +} + +static void nvt_cir_ldev_init(struct nvt_dev *nvt) +{ + u8 val; + + /* output pin selection (Pin95=CIRRX, Pin96=CIRTX1, WB enabled */ + val = nvt_cr_read(nvt, CR_OUTPUT_PIN_SEL); + val &= OUTPUT_PIN_SEL_MASK; + val |= (OUTPUT_ENABLE_CIR | OUTPUT_ENABLE_CIRWB); + nvt_cr_write(nvt, val, CR_OUTPUT_PIN_SEL); + + /* Select CIR logical device and enable */ + nvt_select_logical_dev(nvt, LOGICAL_DEV_CIR); + nvt_cr_write(nvt, LOGICAL_DEV_ENABLE, CR_LOGICAL_DEV_EN); + + nvt_cr_write(nvt, nvt->cir_addr >> 8, CR_CIR_BASE_ADDR_HI); + nvt_cr_write(nvt, nvt->cir_addr & 0xff, CR_CIR_BASE_ADDR_LO); + + nvt_cr_write(nvt, nvt->cir_irq, CR_CIR_IRQ_RSRC); + + nvt_dbg("CIR initialized, base io port address: 0x%lx, irq: %d", + nvt->cir_addr, nvt->cir_irq); +} + +static void nvt_cir_wake_ldev_init(struct nvt_dev *nvt) +{ + /* Select ACPI logical device, enable it and CIR Wake */ + nvt_select_logical_dev(nvt, LOGICAL_DEV_ACPI); + nvt_cr_write(nvt, LOGICAL_DEV_ENABLE, CR_LOGICAL_DEV_EN); + + /* Enable CIR Wake via PSOUT# (Pin60) */ + nvt_set_reg_bit(nvt, CIR_WAKE_ENABLE_BIT, CR_ACPI_CIR_WAKE); + + /* enable cir interrupt of mouse/keyboard IRQ event */ + nvt_set_reg_bit(nvt, CIR_INTR_MOUSE_IRQ_BIT, CR_ACPI_IRQ_EVENTS); + + /* enable pme interrupt of cir wakeup event */ + nvt_set_reg_bit(nvt, PME_INTR_CIR_PASS_BIT, CR_ACPI_IRQ_EVENTS2); + + /* Select CIR Wake logical device and enable */ + nvt_select_logical_dev(nvt, LOGICAL_DEV_CIR_WAKE); + nvt_cr_write(nvt, LOGICAL_DEV_ENABLE, CR_LOGICAL_DEV_EN); + + nvt_cr_write(nvt, nvt->cir_wake_addr >> 8, CR_CIR_BASE_ADDR_HI); + nvt_cr_write(nvt, nvt->cir_wake_addr & 0xff, CR_CIR_BASE_ADDR_LO); + + nvt_cr_write(nvt, nvt->cir_wake_irq, CR_CIR_IRQ_RSRC); + + nvt_dbg("CIR Wake initialized, base io port address: 0x%lx, irq: %d", + nvt->cir_wake_addr, nvt->cir_wake_irq); +} + +/* clear out the hardware's cir rx fifo */ +static void nvt_clear_cir_fifo(struct nvt_dev *nvt) +{ + u8 val; + + val = nvt_cir_reg_read(nvt, CIR_FIFOCON); + nvt_cir_reg_write(nvt, val | CIR_FIFOCON_RXFIFOCLR, CIR_FIFOCON); +} + +/* clear out the hardware's cir wake rx fifo */ +static void nvt_clear_cir_wake_fifo(struct nvt_dev *nvt) +{ + u8 val; + + val = nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFOCON); + nvt_cir_wake_reg_write(nvt, val | CIR_WAKE_FIFOCON_RXFIFOCLR, + CIR_WAKE_FIFOCON); +} + +/* clear out the hardware's cir tx fifo */ +static void nvt_clear_tx_fifo(struct nvt_dev *nvt) +{ + u8 val; + + val = nvt_cir_reg_read(nvt, CIR_FIFOCON); + nvt_cir_reg_write(nvt, val | CIR_FIFOCON_TXFIFOCLR, CIR_FIFOCON); +} + +/* enable RX Trigger Level Reach and Packet End interrupts */ +static void nvt_set_cir_iren(struct nvt_dev *nvt) +{ + u8 iren; + + iren = CIR_IREN_RTR | CIR_IREN_PE; + nvt_cir_reg_write(nvt, iren, CIR_IREN); +} + +static void nvt_cir_regs_init(struct nvt_dev *nvt) +{ + /* set sample limit count (PE interrupt raised when reached) */ + nvt_cir_reg_write(nvt, CIR_RX_LIMIT_COUNT >> 8, CIR_SLCH); + nvt_cir_reg_write(nvt, CIR_RX_LIMIT_COUNT & 0xff, CIR_SLCL); + + /* set fifo irq trigger levels */ + nvt_cir_reg_write(nvt, CIR_FIFOCON_TX_TRIGGER_LEV | + CIR_FIFOCON_RX_TRIGGER_LEV, CIR_FIFOCON); + + /* + * Enable TX and RX, specify carrier on = low, off = high, and set + * sample period (currently 50us) + */ + nvt_cir_reg_write(nvt, + CIR_IRCON_TXEN | CIR_IRCON_RXEN | + CIR_IRCON_RXINV | CIR_IRCON_SAMPLE_PERIOD_SEL, + CIR_IRCON); + + /* clear hardware rx and tx fifos */ + nvt_clear_cir_fifo(nvt); + nvt_clear_tx_fifo(nvt); + + /* clear any and all stray interrupts */ + nvt_cir_reg_write(nvt, 0xff, CIR_IRSTS); + + /* and finally, enable interrupts */ + nvt_set_cir_iren(nvt); +} + +static void nvt_cir_wake_regs_init(struct nvt_dev *nvt) +{ + /* set number of bytes needed for wake key comparison (default 67) */ + nvt_cir_wake_reg_write(nvt, CIR_WAKE_FIFO_LEN, CIR_WAKE_FIFO_CMP_DEEP); + + /* set tolerance/variance allowed per byte during wake compare */ + nvt_cir_wake_reg_write(nvt, CIR_WAKE_CMP_TOLERANCE, + CIR_WAKE_FIFO_CMP_TOL); + + /* set sample limit count (PE interrupt raised when reached) */ + nvt_cir_wake_reg_write(nvt, CIR_RX_LIMIT_COUNT >> 8, CIR_WAKE_SLCH); + nvt_cir_wake_reg_write(nvt, CIR_RX_LIMIT_COUNT & 0xff, CIR_WAKE_SLCL); + + /* set cir wake fifo rx trigger level (currently 67) */ + nvt_cir_wake_reg_write(nvt, CIR_WAKE_FIFOCON_RX_TRIGGER_LEV, + CIR_WAKE_FIFOCON); + + /* + * Enable TX and RX, specific carrier on = low, off = high, and set + * sample period (currently 50us) + */ + nvt_cir_wake_reg_write(nvt, CIR_WAKE_IRCON_MODE0 | CIR_WAKE_IRCON_RXEN | + CIR_WAKE_IRCON_R | CIR_WAKE_IRCON_RXINV | + CIR_WAKE_IRCON_SAMPLE_PERIOD_SEL, + CIR_WAKE_IRCON); + + /* clear cir wake rx fifo */ + nvt_clear_cir_wake_fifo(nvt); + + /* clear any and all stray interrupts */ + nvt_cir_wake_reg_write(nvt, 0xff, CIR_WAKE_IRSTS); +} + +static void nvt_enable_wake(struct nvt_dev *nvt) +{ + nvt_efm_enable(nvt); + + nvt_select_logical_dev(nvt, LOGICAL_DEV_ACPI); + nvt_set_reg_bit(nvt, CIR_WAKE_ENABLE_BIT, CR_ACPI_CIR_WAKE); + nvt_set_reg_bit(nvt, CIR_INTR_MOUSE_IRQ_BIT, CR_ACPI_IRQ_EVENTS); + nvt_set_reg_bit(nvt, PME_INTR_CIR_PASS_BIT, CR_ACPI_IRQ_EVENTS2); + + nvt_select_logical_dev(nvt, LOGICAL_DEV_CIR_WAKE); + nvt_cr_write(nvt, LOGICAL_DEV_ENABLE, CR_LOGICAL_DEV_EN); + + nvt_efm_disable(nvt); + + nvt_cir_wake_reg_write(nvt, CIR_WAKE_IRCON_MODE0 | CIR_WAKE_IRCON_RXEN | + CIR_WAKE_IRCON_R | CIR_WAKE_IRCON_RXINV | + CIR_WAKE_IRCON_SAMPLE_PERIOD_SEL, + CIR_WAKE_IRCON); + nvt_cir_wake_reg_write(nvt, 0xff, CIR_WAKE_IRSTS); + nvt_cir_wake_reg_write(nvt, 0, CIR_WAKE_IREN); +} + +/* rx carrier detect only works in learning mode, must be called w/nvt_lock */ +static u32 nvt_rx_carrier_detect(struct nvt_dev *nvt) +{ + u32 count, carrier, duration = 0; + int i; + + count = nvt_cir_reg_read(nvt, CIR_FCCL) | + nvt_cir_reg_read(nvt, CIR_FCCH) << 8; + + for (i = 0; i < nvt->pkts; i++) { + if (nvt->buf[i] & BUF_PULSE_BIT) + duration += nvt->buf[i] & BUF_LEN_MASK; + } + + duration *= SAMPLE_PERIOD; + + if (!count || !duration) { + nvt_pr(KERN_NOTICE, "Unable to determine carrier! (c:%u, d:%u)", + count, duration); + return 0; + } + + carrier = (count * 1000000) / duration; + + if ((carrier > MAX_CARRIER) || (carrier < MIN_CARRIER)) + nvt_dbg("WTF? Carrier frequency out of range!"); + + nvt_dbg("Carrier frequency: %u (count %u, duration %u)", + carrier, count, duration); + + return carrier; +} + +/* + * set carrier frequency + * + * set carrier on 2 registers: CP & CC + * always set CP as 0x81 + * set CC by SPEC, CC = 3MHz/carrier - 1 + */ +static int nvt_set_tx_carrier(void *data, u32 carrier) +{ + struct nvt_dev *nvt = data; + u16 val; + + nvt_cir_reg_write(nvt, 1, CIR_CP); + val = 3000000 / (carrier) - 1; + nvt_cir_reg_write(nvt, val & 0xff, CIR_CC); + + nvt_dbg("cp: 0x%x cc: 0x%x\n", + nvt_cir_reg_read(nvt, CIR_CP), nvt_cir_reg_read(nvt, CIR_CC)); + + return 0; +} + +/* + * nvt_tx_ir + * + * 1) clean TX fifo first (handled by AP) + * 2) copy data from user space + * 3) disable RX interrupts, enable TX interrupts: TTR & TFU + * 4) send 9 packets to TX FIFO to open TTR + * in interrupt_handler: + * 5) send all data out + * go back to write(): + * 6) disable TX interrupts, re-enable RX interupts + * + * The key problem of this function is user space data may larger than + * driver's data buf length. So nvt_tx_ir() will only copy TX_BUF_LEN data to + * buf, and keep current copied data buf num in cur_buf_num. But driver's buf + * number may larger than TXFCONT (0xff). So in interrupt_handler, it has to + * set TXFCONT as 0xff, until buf_count less than 0xff. + */ +static int nvt_tx_ir(void *priv, int *txbuf, u32 n) +{ + struct nvt_dev *nvt = priv; + unsigned long flags; + size_t cur_count; + unsigned int i; + u8 iren; + int ret; + + spin_lock_irqsave(&nvt->tx.lock, flags); + + if (n >= TX_BUF_LEN) { + nvt->tx.buf_count = cur_count = TX_BUF_LEN; + ret = TX_BUF_LEN; + } else { + nvt->tx.buf_count = cur_count = n; + ret = n; + } + + memcpy(nvt->tx.buf, txbuf, nvt->tx.buf_count); + + nvt->tx.cur_buf_num = 0; + + /* save currently enabled interrupts */ + iren = nvt_cir_reg_read(nvt, CIR_IREN); + + /* now disable all interrupts, save TFU & TTR */ + nvt_cir_reg_write(nvt, CIR_IREN_TFU | CIR_IREN_TTR, CIR_IREN); + + nvt->tx.tx_state = ST_TX_REPLY; + + nvt_cir_reg_write(nvt, CIR_FIFOCON_TX_TRIGGER_LEV_8 | + CIR_FIFOCON_RXFIFOCLR, CIR_FIFOCON); + + /* trigger TTR interrupt by writing out ones, (yes, it's ugly) */ + for (i = 0; i < 9; i++) + nvt_cir_reg_write(nvt, 0x01, CIR_STXFIFO); + + spin_unlock_irqrestore(&nvt->tx.lock, flags); + + wait_event(nvt->tx.queue, nvt->tx.tx_state == ST_TX_REQUEST); + + spin_lock_irqsave(&nvt->tx.lock, flags); + nvt->tx.tx_state = ST_TX_NONE; + spin_unlock_irqrestore(&nvt->tx.lock, flags); + + /* restore enabled interrupts to prior state */ + nvt_cir_reg_write(nvt, iren, CIR_IREN); + + return ret; +} + +/* dump contents of the last rx buffer we got from the hw rx fifo */ +static void nvt_dump_rx_buf(struct nvt_dev *nvt) +{ + int i; + + printk(KERN_DEBUG "%s (len %d): ", __func__, nvt->pkts); + for (i = 0; (i < nvt->pkts) && (i < RX_BUF_LEN); i++) + printk(KERN_CONT "0x%02x ", nvt->buf[i]); + printk(KERN_CONT "\n"); +} + +/* + * Process raw data in rx driver buffer, store it in raw IR event kfifo, + * trigger decode when appropriate. + * + * We get IR data samples one byte at a time. If the msb is set, its a pulse, + * otherwise its a space. The lower 7 bits are the count of SAMPLE_PERIOD + * (default 50us) intervals for that pulse/space. A discrete signal is + * followed by a series of 0x7f packets, then either 0x7<something> or 0x80 + * to signal more IR coming (repeats) or end of IR, respectively. We store + * sample data in the raw event kfifo until we see 0x7<something> (except f) + * or 0x80, at which time, we trigger a decode operation. + */ +static void nvt_process_rx_ir_data(struct nvt_dev *nvt) +{ + DEFINE_IR_RAW_EVENT(rawir); + unsigned int count; + u32 carrier; + u8 sample; + int i; + + nvt_dbg_verbose("%s firing", __func__); + + if (debug) + nvt_dump_rx_buf(nvt); + + if (nvt->carrier_detect_enabled) + carrier = nvt_rx_carrier_detect(nvt); + + count = nvt->pkts; + nvt_dbg_verbose("Processing buffer of len %d", count); + + for (i = 0; i < count; i++) { + nvt->pkts--; + sample = nvt->buf[i]; + + rawir.pulse = ((sample & BUF_PULSE_BIT) != 0); + rawir.duration = (sample & BUF_LEN_MASK) + * SAMPLE_PERIOD * 1000; + + if ((sample & BUF_LEN_MASK) == BUF_LEN_MASK) { + if (nvt->rawir.pulse == rawir.pulse) + nvt->rawir.duration += rawir.duration; + else { + nvt->rawir.duration = rawir.duration; + nvt->rawir.pulse = rawir.pulse; + } + continue; + } + + rawir.duration += nvt->rawir.duration; + + init_ir_raw_event(&nvt->rawir); + nvt->rawir.duration = 0; + nvt->rawir.pulse = rawir.pulse; + + if (sample == BUF_PULSE_BIT) + rawir.pulse = false; + + if (rawir.duration) { + nvt_dbg("Storing %s with duration %d", + rawir.pulse ? "pulse" : "space", + rawir.duration); + + ir_raw_event_store(nvt->rdev, &rawir); + } + + /* + * BUF_PULSE_BIT indicates end of IR data, BUF_REPEAT_BYTE + * indicates end of IR signal, but new data incoming. In both + * cases, it means we're ready to call ir_raw_event_handle + */ + if (sample == BUF_PULSE_BIT || ((sample != BUF_LEN_MASK) && + (sample & BUF_REPEAT_MASK) == BUF_REPEAT_BYTE)) + ir_raw_event_handle(nvt->rdev); + } + + if (nvt->pkts) { + nvt_dbg("Odd, pkts should be 0 now... (its %u)", nvt->pkts); + nvt->pkts = 0; + } + + nvt_dbg_verbose("%s done", __func__); +} + +static void nvt_handle_rx_fifo_overrun(struct nvt_dev *nvt) +{ + nvt_pr(KERN_WARNING, "RX FIFO overrun detected, flushing data!"); + + nvt->pkts = 0; + nvt_clear_cir_fifo(nvt); + ir_raw_event_reset(nvt->rdev); +} + +/* copy data from hardware rx fifo into driver buffer */ +static void nvt_get_rx_ir_data(struct nvt_dev *nvt) +{ + unsigned long flags; + u8 fifocount, val; + unsigned int b_idx; + bool overrun = false; + int i; + + /* Get count of how many bytes to read from RX FIFO */ + fifocount = nvt_cir_reg_read(nvt, CIR_RXFCONT); + /* if we get 0xff, probably means the logical dev is disabled */ + if (fifocount == 0xff) + return; + /* watch out for a fifo overrun condition */ + else if (fifocount > RX_BUF_LEN) { + overrun = true; + fifocount = RX_BUF_LEN; + } + + nvt_dbg("attempting to fetch %u bytes from hw rx fifo", fifocount); + + spin_lock_irqsave(&nvt->nvt_lock, flags); + + b_idx = nvt->pkts; + + /* This should never happen, but lets check anyway... */ + if (b_idx + fifocount > RX_BUF_LEN) { + nvt_process_rx_ir_data(nvt); + b_idx = 0; + } + + /* Read fifocount bytes from CIR Sample RX FIFO register */ + for (i = 0; i < fifocount; i++) { + val = nvt_cir_reg_read(nvt, CIR_SRXFIFO); + nvt->buf[b_idx + i] = val; + } + + nvt->pkts += fifocount; + nvt_dbg("%s: pkts now %d", __func__, nvt->pkts); + + nvt_process_rx_ir_data(nvt); + + if (overrun) + nvt_handle_rx_fifo_overrun(nvt); + + spin_unlock_irqrestore(&nvt->nvt_lock, flags); +} + +static void nvt_cir_log_irqs(u8 status, u8 iren) +{ + nvt_pr(KERN_INFO, "IRQ 0x%02x (IREN 0x%02x) :%s%s%s%s%s%s%s%s%s", + status, iren, + status & CIR_IRSTS_RDR ? " RDR" : "", + status & CIR_IRSTS_RTR ? " RTR" : "", + status & CIR_IRSTS_PE ? " PE" : "", + status & CIR_IRSTS_RFO ? " RFO" : "", + status & CIR_IRSTS_TE ? " TE" : "", + status & CIR_IRSTS_TTR ? " TTR" : "", + status & CIR_IRSTS_TFU ? " TFU" : "", + status & CIR_IRSTS_GH ? " GH" : "", + status & ~(CIR_IRSTS_RDR | CIR_IRSTS_RTR | CIR_IRSTS_PE | + CIR_IRSTS_RFO | CIR_IRSTS_TE | CIR_IRSTS_TTR | + CIR_IRSTS_TFU | CIR_IRSTS_GH) ? " ?" : ""); +} + +static bool nvt_cir_tx_inactive(struct nvt_dev *nvt) +{ + unsigned long flags; + bool tx_inactive; + u8 tx_state; + + spin_lock_irqsave(&nvt->tx.lock, flags); + tx_state = nvt->tx.tx_state; + spin_unlock_irqrestore(&nvt->tx.lock, flags); + + tx_inactive = (tx_state == ST_TX_NONE); + + return tx_inactive; +} + +/* interrupt service routine for incoming and outgoing CIR data */ +static irqreturn_t nvt_cir_isr(int irq, void *data) +{ + struct nvt_dev *nvt = data; + u8 status, iren, cur_state; + unsigned long flags; + + nvt_dbg_verbose("%s firing", __func__); + + nvt_efm_enable(nvt); + nvt_select_logical_dev(nvt, LOGICAL_DEV_CIR); + nvt_efm_disable(nvt); + + /* + * Get IR Status register contents. Write 1 to ack/clear + * + * bit: reg name - description + * 7: CIR_IRSTS_RDR - RX Data Ready + * 6: CIR_IRSTS_RTR - RX FIFO Trigger Level Reach + * 5: CIR_IRSTS_PE - Packet End + * 4: CIR_IRSTS_RFO - RX FIFO Overrun (RDR will also be set) + * 3: CIR_IRSTS_TE - TX FIFO Empty + * 2: CIR_IRSTS_TTR - TX FIFO Trigger Level Reach + * 1: CIR_IRSTS_TFU - TX FIFO Underrun + * 0: CIR_IRSTS_GH - Min Length Detected + */ + status = nvt_cir_reg_read(nvt, CIR_IRSTS); + if (!status) { + nvt_dbg_verbose("%s exiting, IRSTS 0x0", __func__); + nvt_cir_reg_write(nvt, 0xff, CIR_IRSTS); + return IRQ_RETVAL(IRQ_NONE); + } + + /* ack/clear all irq flags we've got */ + nvt_cir_reg_write(nvt, status, CIR_IRSTS); + nvt_cir_reg_write(nvt, 0, CIR_IRSTS); + + /* Interrupt may be shared with CIR Wake, bail if CIR not enabled */ + iren = nvt_cir_reg_read(nvt, CIR_IREN); + if (!iren) { + nvt_dbg_verbose("%s exiting, CIR not enabled", __func__); + return IRQ_RETVAL(IRQ_NONE); + } + + if (debug) + nvt_cir_log_irqs(status, iren); + + if (status & CIR_IRSTS_RTR) { + /* FIXME: add code for study/learn mode */ + /* We only do rx if not tx'ing */ + if (nvt_cir_tx_inactive(nvt)) + nvt_get_rx_ir_data(nvt); + } + + if (status & CIR_IRSTS_PE) { + if (nvt_cir_tx_inactive(nvt)) + nvt_get_rx_ir_data(nvt); + + spin_lock_irqsave(&nvt->nvt_lock, flags); + + cur_state = nvt->study_state; + + spin_unlock_irqrestore(&nvt->nvt_lock, flags); + + if (cur_state == ST_STUDY_NONE) + nvt_clear_cir_fifo(nvt); + } + + if (status & CIR_IRSTS_TE) + nvt_clear_tx_fifo(nvt); + + if (status & CIR_IRSTS_TTR) { + unsigned int pos, count; + u8 tmp; + + spin_lock_irqsave(&nvt->tx.lock, flags); + + pos = nvt->tx.cur_buf_num; + count = nvt->tx.buf_count; + + /* Write data into the hardware tx fifo while pos < count */ + if (pos < count) { + nvt_cir_reg_write(nvt, nvt->tx.buf[pos], CIR_STXFIFO); + nvt->tx.cur_buf_num++; + /* Disable TX FIFO Trigger Level Reach (TTR) interrupt */ + } else { + tmp = nvt_cir_reg_read(nvt, CIR_IREN); + nvt_cir_reg_write(nvt, tmp & ~CIR_IREN_TTR, CIR_IREN); + } + + spin_unlock_irqrestore(&nvt->tx.lock, flags); + + } + + if (status & CIR_IRSTS_TFU) { + spin_lock_irqsave(&nvt->tx.lock, flags); + if (nvt->tx.tx_state == ST_TX_REPLY) { + nvt->tx.tx_state = ST_TX_REQUEST; + wake_up(&nvt->tx.queue); + } + spin_unlock_irqrestore(&nvt->tx.lock, flags); + } + + nvt_dbg_verbose("%s done", __func__); + return IRQ_RETVAL(IRQ_HANDLED); +} + +/* Interrupt service routine for CIR Wake */ +static irqreturn_t nvt_cir_wake_isr(int irq, void *data) +{ + u8 status, iren, val; + struct nvt_dev *nvt = data; + unsigned long flags; + + nvt_dbg_wake("%s firing", __func__); + + status = nvt_cir_wake_reg_read(nvt, CIR_WAKE_IRSTS); + if (!status) + return IRQ_RETVAL(IRQ_NONE); + + if (status & CIR_WAKE_IRSTS_IR_PENDING) + nvt_clear_cir_wake_fifo(nvt); + + nvt_cir_wake_reg_write(nvt, status, CIR_WAKE_IRSTS); + nvt_cir_wake_reg_write(nvt, 0, CIR_WAKE_IRSTS); + + /* Interrupt may be shared with CIR, bail if Wake not enabled */ + iren = nvt_cir_wake_reg_read(nvt, CIR_WAKE_IREN); + if (!iren) { + nvt_dbg_wake("%s exiting, wake not enabled", __func__); + return IRQ_RETVAL(IRQ_HANDLED); + } + + if ((status & CIR_WAKE_IRSTS_PE) && + (nvt->wake_state == ST_WAKE_START)) { + while (nvt_cir_wake_reg_read(nvt, CIR_WAKE_RD_FIFO_ONLY_IDX)) { + val = nvt_cir_wake_reg_read(nvt, CIR_WAKE_RD_FIFO_ONLY); + nvt_dbg("setting wake up key: 0x%x", val); + } + + nvt_cir_wake_reg_write(nvt, 0, CIR_WAKE_IREN); + spin_lock_irqsave(&nvt->nvt_lock, flags); + nvt->wake_state = ST_WAKE_FINISH; + spin_unlock_irqrestore(&nvt->nvt_lock, flags); + } + + nvt_dbg_wake("%s done", __func__); + return IRQ_RETVAL(IRQ_HANDLED); +} + +static void nvt_enable_cir(struct nvt_dev *nvt) +{ + /* set function enable flags */ + nvt_cir_reg_write(nvt, CIR_IRCON_TXEN | CIR_IRCON_RXEN | + CIR_IRCON_RXINV | CIR_IRCON_SAMPLE_PERIOD_SEL, + CIR_IRCON); + + nvt_efm_enable(nvt); + + /* enable the CIR logical device */ + nvt_select_logical_dev(nvt, LOGICAL_DEV_CIR); + nvt_cr_write(nvt, LOGICAL_DEV_ENABLE, CR_LOGICAL_DEV_EN); + + nvt_efm_disable(nvt); + + /* clear all pending interrupts */ + nvt_cir_reg_write(nvt, 0xff, CIR_IRSTS); + + /* enable interrupts */ + nvt_set_cir_iren(nvt); +} + +static void nvt_disable_cir(struct nvt_dev *nvt) +{ + /* disable CIR interrupts */ + nvt_cir_reg_write(nvt, 0, CIR_IREN); + + /* clear any and all pending interrupts */ + nvt_cir_reg_write(nvt, 0xff, CIR_IRSTS); + + /* clear all function enable flags */ + nvt_cir_reg_write(nvt, 0, CIR_IRCON); + + /* clear hardware rx and tx fifos */ + nvt_clear_cir_fifo(nvt); + nvt_clear_tx_fifo(nvt); + + nvt_efm_enable(nvt); + + /* disable the CIR logical device */ + nvt_select_logical_dev(nvt, LOGICAL_DEV_CIR); + nvt_cr_write(nvt, LOGICAL_DEV_DISABLE, CR_LOGICAL_DEV_EN); + + nvt_efm_disable(nvt); +} + +static int nvt_open(void *data) +{ + struct nvt_dev *nvt = (struct nvt_dev *)data; + unsigned long flags; + + spin_lock_irqsave(&nvt->nvt_lock, flags); + nvt->in_use = true; + nvt_enable_cir(nvt); + spin_unlock_irqrestore(&nvt->nvt_lock, flags); + + return 0; +} + +static void nvt_close(void *data) +{ + struct nvt_dev *nvt = (struct nvt_dev *)data; + unsigned long flags; + + spin_lock_irqsave(&nvt->nvt_lock, flags); + nvt->in_use = false; + nvt_disable_cir(nvt); + spin_unlock_irqrestore(&nvt->nvt_lock, flags); +} + +/* Allocate memory, probe hardware, and initialize everything */ +static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id) +{ + struct nvt_dev *nvt = NULL; + struct input_dev *rdev = NULL; + struct ir_dev_props *props = NULL; + int ret = -ENOMEM; + + nvt = kzalloc(sizeof(struct nvt_dev), GFP_KERNEL); + if (!nvt) + return ret; + + props = kzalloc(sizeof(struct ir_dev_props), GFP_KERNEL); + if (!props) + goto failure; + + /* input device for IR remote (and tx) */ + rdev = input_allocate_device(); + if (!rdev) + goto failure; + + ret = -ENODEV; + /* validate pnp resources */ + if (!pnp_port_valid(pdev, 0) || + pnp_port_len(pdev, 0) < CIR_IOREG_LENGTH) { + dev_err(&pdev->dev, "IR PNP Port not valid!\n"); + goto failure; + } + + if (!pnp_irq_valid(pdev, 0)) { + dev_err(&pdev->dev, "PNP IRQ not valid!\n"); + goto failure; + } + + if (!pnp_port_valid(pdev, 1) || + pnp_port_len(pdev, 1) < CIR_IOREG_LENGTH) { + dev_err(&pdev->dev, "Wake PNP Port not valid!\n"); + goto failure; + } + + nvt->cir_addr = pnp_port_start(pdev, 0); + nvt->cir_irq = pnp_irq(pdev, 0); + + nvt->cir_wake_addr = pnp_port_start(pdev, 1); + /* irq is always shared between cir and cir wake */ + nvt->cir_wake_irq = nvt->cir_irq; + + nvt->cr_efir = CR_EFIR; + nvt->cr_efdr = CR_EFDR; + + spin_lock_init(&nvt->nvt_lock); + spin_lock_init(&nvt->tx.lock); + init_ir_raw_event(&nvt->rawir); + + ret = -EBUSY; + /* now claim resources */ + if (!request_region(nvt->cir_addr, + CIR_IOREG_LENGTH, NVT_DRIVER_NAME)) + goto failure; + + if (request_irq(nvt->cir_irq, nvt_cir_isr, IRQF_SHARED, + NVT_DRIVER_NAME, (void *)nvt)) + goto failure; + + if (!request_region(nvt->cir_wake_addr, + CIR_IOREG_LENGTH, NVT_DRIVER_NAME)) + goto failure; + + if (request_irq(nvt->cir_wake_irq, nvt_cir_wake_isr, IRQF_SHARED, + NVT_DRIVER_NAME, (void *)nvt)) + goto failure; + + pnp_set_drvdata(pdev, nvt); + nvt->pdev = pdev; + + init_waitqueue_head(&nvt->tx.queue); + + ret = nvt_hw_detect(nvt); + if (ret) + goto failure; + + /* Initialize CIR & CIR Wake Logical Devices */ + nvt_efm_enable(nvt); + nvt_cir_ldev_init(nvt); + nvt_cir_wake_ldev_init(nvt); + nvt_efm_disable(nvt); + + /* Initialize CIR & CIR Wake Config Registers */ + nvt_cir_regs_init(nvt); + nvt_cir_wake_regs_init(nvt); + + /* Set up ir-core props */ + props->priv = nvt; + props->driver_type = RC_DRIVER_IR_RAW; + props->allowed_protos = IR_TYPE_ALL; + props->open = nvt_open; + props->close = nvt_close; +#if 0 + props->min_timeout = XYZ; + props->max_timeout = XYZ; + props->timeout = XYZ; + /* rx resolution is hardwired to 50us atm, 1, 25, 100 also possible */ + props->rx_resolution = XYZ; + + /* tx bits */ + props->tx_resolution = XYZ; +#endif + props->tx_ir = nvt_tx_ir; + props->s_tx_carrier = nvt_set_tx_carrier; + + rdev->name = "Nuvoton w836x7hg Infrared Remote Transceiver"; + rdev->id.bustype = BUS_HOST; + rdev->id.vendor = PCI_VENDOR_ID_WINBOND2; + rdev->id.product = nvt->chip_major; + rdev->id.version = nvt->chip_minor; + + nvt->props = props; + nvt->rdev = rdev; + + device_set_wakeup_capable(&pdev->dev, 1); + device_set_wakeup_enable(&pdev->dev, 1); + + ret = ir_input_register(rdev, RC_MAP_RC6_MCE, props, NVT_DRIVER_NAME); + if (ret) + goto failure; + + nvt_pr(KERN_NOTICE, "driver has been successfully loaded\n"); + if (debug) { + cir_dump_regs(nvt); + cir_wake_dump_regs(nvt); + } + + return 0; + +failure: + if (nvt->cir_irq) + free_irq(nvt->cir_irq, nvt); + if (nvt->cir_addr) + release_region(nvt->cir_addr, CIR_IOREG_LENGTH); + + if (nvt->cir_wake_irq) + free_irq(nvt->cir_wake_irq, nvt); + if (nvt->cir_wake_addr) + release_region(nvt->cir_wake_addr, CIR_IOREG_LENGTH); + + input_free_device(rdev); + kfree(props); + kfree(nvt); + + return ret; +} + +static void __devexit nvt_remove(struct pnp_dev *pdev) +{ + struct nvt_dev *nvt = pnp_get_drvdata(pdev); + unsigned long flags; + + spin_lock_irqsave(&nvt->nvt_lock, flags); + /* disable CIR */ + nvt_cir_reg_write(nvt, 0, CIR_IREN); + nvt_disable_cir(nvt); + /* enable CIR Wake (for IR power-on) */ + nvt_enable_wake(nvt); + spin_unlock_irqrestore(&nvt->nvt_lock, flags); + + /* free resources */ + free_irq(nvt->cir_irq, nvt); + free_irq(nvt->cir_wake_irq, nvt); + release_region(nvt->cir_addr, CIR_IOREG_LENGTH); + release_region(nvt->cir_wake_addr, CIR_IOREG_LENGTH); + + ir_input_unregister(nvt->rdev); + + kfree(nvt->props); + kfree(nvt); +} + +static int nvt_suspend(struct pnp_dev *pdev, pm_message_t state) +{ + struct nvt_dev *nvt = pnp_get_drvdata(pdev); + unsigned long flags; + + nvt_dbg("%s called", __func__); + + /* zero out misc state tracking */ + spin_lock_irqsave(&nvt->nvt_lock, flags); + nvt->study_state = ST_STUDY_NONE; + nvt->wake_state = ST_WAKE_NONE; + spin_unlock_irqrestore(&nvt->nvt_lock, flags); + + spin_lock_irqsave(&nvt->tx.lock, flags); + nvt->tx.tx_state = ST_TX_NONE; + spin_unlock_irqrestore(&nvt->tx.lock, flags); + + /* disable all CIR interrupts */ + nvt_cir_reg_write(nvt, 0, CIR_IREN); + + nvt_efm_enable(nvt); + + /* disable cir logical dev */ + nvt_select_logical_dev(nvt, LOGICAL_DEV_CIR); + nvt_cr_write(nvt, LOGICAL_DEV_DISABLE, CR_LOGICAL_DEV_EN); + + nvt_efm_disable(nvt); + + /* make sure wake is enabled */ + nvt_enable_wake(nvt); + + return 0; +} + +static int nvt_resume(struct pnp_dev *pdev) +{ + int ret = 0; + struct nvt_dev *nvt = pnp_get_drvdata(pdev); + + nvt_dbg("%s called", __func__); + + /* open interrupt */ + nvt_set_cir_iren(nvt); + + /* Enable CIR logical device */ + nvt_efm_enable(nvt); + nvt_select_logical_dev(nvt, LOGICAL_DEV_CIR); + nvt_cr_write(nvt, LOGICAL_DEV_ENABLE, CR_LOGICAL_DEV_EN); + + nvt_efm_disable(nvt); + + nvt_cir_regs_init(nvt); + nvt_cir_wake_regs_init(nvt); + + return ret; +} + +static void nvt_shutdown(struct pnp_dev *pdev) +{ + struct nvt_dev *nvt = pnp_get_drvdata(pdev); + nvt_enable_wake(nvt); +} + +static const struct pnp_device_id nvt_ids[] = { + { "WEC0530", 0 }, /* CIR */ + { "NTN0530", 0 }, /* CIR for new chip's pnp id*/ + { "", 0 }, +}; + +static struct pnp_driver nvt_driver = { + .name = NVT_DRIVER_NAME, + .id_table = nvt_ids, + .flags = PNP_DRIVER_RES_DO_NOT_CHANGE, + .probe = nvt_probe, + .remove = __devexit_p(nvt_remove), + .suspend = nvt_suspend, + .resume = nvt_resume, + .shutdown = nvt_shutdown, +}; + +int nvt_init(void) +{ + return pnp_register_driver(&nvt_driver); +} + +void nvt_exit(void) +{ + pnp_unregister_driver(&nvt_driver); +} + +module_param(debug, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "Enable debugging output"); + +MODULE_DEVICE_TABLE(pnp, nvt_ids); +MODULE_DESCRIPTION("Nuvoton W83667HG-A & W83677HG-I CIR driver"); + +MODULE_AUTHOR("Jarod Wilson <jarod@redhat.com>"); +MODULE_LICENSE("GPL"); + +module_init(nvt_init); +module_exit(nvt_exit); diff --git a/drivers/media/IR/nuvoton-cir.h b/drivers/media/IR/nuvoton-cir.h new file mode 100644 index 000000000000..62dc53017c8e --- /dev/null +++ b/drivers/media/IR/nuvoton-cir.h @@ -0,0 +1,408 @@ +/* + * Driver for Nuvoton Technology Corporation w83667hg/w83677hg-i CIR + * + * Copyright (C) 2010 Jarod Wilson <jarod@redhat.com> + * Copyright (C) 2009 Nuvoton PS Team + * + * Special thanks to Nuvoton for providing hardware, spec sheets and + * sample code upon which portions of this driver are based. Indirect + * thanks also to Maxim Levitsky, whose ene_ir driver this driver is + * modeled after. + * + * 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> +#include <linux/ioctl.h> + +/* platform driver name to register */ +#define NVT_DRIVER_NAME "nuvoton-cir" + +/* debugging module parameter */ +static int debug; + + +#define nvt_pr(level, text, ...) \ + printk(level KBUILD_MODNAME ": " text, ## __VA_ARGS__) + +#define nvt_dbg(text, ...) \ + if (debug) \ + printk(KERN_DEBUG \ + KBUILD_MODNAME ": " text "\n" , ## __VA_ARGS__) + +#define nvt_dbg_verbose(text, ...) \ + if (debug > 1) \ + printk(KERN_DEBUG \ + KBUILD_MODNAME ": " text "\n" , ## __VA_ARGS__) + +#define nvt_dbg_wake(text, ...) \ + if (debug > 2) \ + printk(KERN_DEBUG \ + KBUILD_MODNAME ": " text "\n" , ## __VA_ARGS__) + + +/* + * Original lirc driver said min value of 76, and recommended value of 256 + * for the buffer length, but then used 2048. Never mind that the size of the + * RX FIFO is 32 bytes... So I'm using 32 for RX and 256 for TX atm, but I'm + * not sure if maybe that TX value is off by a factor of 8 (bits vs. bytes), + * and I don't have TX-capable hardware to test/debug on... + */ +#define TX_BUF_LEN 256 +#define RX_BUF_LEN 32 + +struct nvt_dev { + struct pnp_dev *pdev; + struct input_dev *rdev; + struct ir_dev_props *props; + struct ir_raw_event rawir; + + spinlock_t nvt_lock; + bool in_use; + + /* for rx */ + u8 buf[RX_BUF_LEN]; + unsigned int pkts; + + struct { + spinlock_t lock; + u8 buf[TX_BUF_LEN]; + unsigned int buf_count; + unsigned int cur_buf_num; + wait_queue_head_t queue; + u8 tx_state; + } tx; + + /* EFER Config register index/data pair */ + u8 cr_efir; + u8 cr_efdr; + + /* hardware I/O settings */ + unsigned long cir_addr; + unsigned long cir_wake_addr; + int cir_irq; + int cir_wake_irq; + + /* hardware id */ + u8 chip_major; + u8 chip_minor; + + /* hardware features */ + bool hw_learning_capable; + bool hw_tx_capable; + + /* rx settings */ + bool learning_enabled; + bool carrier_detect_enabled; + + /* track cir wake state */ + u8 wake_state; + /* for study */ + u8 study_state; + /* carrier period = 1 / frequency */ + u32 carrier; +}; + +/* study states */ +#define ST_STUDY_NONE 0x0 +#define ST_STUDY_START 0x1 +#define ST_STUDY_CARRIER 0x2 +#define ST_STUDY_ALL_RECV 0x4 + +/* wake states */ +#define ST_WAKE_NONE 0x0 +#define ST_WAKE_START 0x1 +#define ST_WAKE_FINISH 0x2 + +/* receive states */ +#define ST_RX_WAIT_7F 0x1 +#define ST_RX_WAIT_HEAD 0x2 +#define ST_RX_WAIT_SILENT_END 0x4 + +/* send states */ +#define ST_TX_NONE 0x0 +#define ST_TX_REQUEST 0x2 +#define ST_TX_REPLY 0x4 + +/* buffer packet constants */ +#define BUF_PULSE_BIT 0x80 +#define BUF_LEN_MASK 0x7f +#define BUF_REPEAT_BYTE 0x70 +#define BUF_REPEAT_MASK 0xf0 + +/* CIR settings */ + +/* total length of CIR and CIR WAKE */ +#define CIR_IOREG_LENGTH 0x0f + +/* RX limit length, 8 high bits for SLCH, 8 low bits for SLCL (0x7d0 = 2000) */ +#define CIR_RX_LIMIT_COUNT 0x7d0 + +/* CIR Regs */ +#define CIR_IRCON 0x00 +#define CIR_IRSTS 0x01 +#define CIR_IREN 0x02 +#define CIR_RXFCONT 0x03 +#define CIR_CP 0x04 +#define CIR_CC 0x05 +#define CIR_SLCH 0x06 +#define CIR_SLCL 0x07 +#define CIR_FIFOCON 0x08 +#define CIR_IRFIFOSTS 0x09 +#define CIR_SRXFIFO 0x0a +#define CIR_TXFCONT 0x0b +#define CIR_STXFIFO 0x0c +#define CIR_FCCH 0x0d +#define CIR_FCCL 0x0e +#define CIR_IRFSM 0x0f + +/* CIR IRCON settings */ +#define CIR_IRCON_RECV 0x80 +#define CIR_IRCON_WIREN 0x40 +#define CIR_IRCON_TXEN 0x20 +#define CIR_IRCON_RXEN 0x10 +#define CIR_IRCON_WRXINV 0x08 +#define CIR_IRCON_RXINV 0x04 + +#define CIR_IRCON_SAMPLE_PERIOD_SEL_1 0x00 +#define CIR_IRCON_SAMPLE_PERIOD_SEL_25 0x01 +#define CIR_IRCON_SAMPLE_PERIOD_SEL_50 0x02 +#define CIR_IRCON_SAMPLE_PERIOD_SEL_100 0x03 + +/* FIXME: make this a runtime option */ +/* select sample period as 50us */ +#define CIR_IRCON_SAMPLE_PERIOD_SEL CIR_IRCON_SAMPLE_PERIOD_SEL_50 + +/* CIR IRSTS settings */ +#define CIR_IRSTS_RDR 0x80 +#define CIR_IRSTS_RTR 0x40 +#define CIR_IRSTS_PE 0x20 +#define CIR_IRSTS_RFO 0x10 +#define CIR_IRSTS_TE 0x08 +#define CIR_IRSTS_TTR 0x04 +#define CIR_IRSTS_TFU 0x02 +#define CIR_IRSTS_GH 0x01 + +/* CIR IREN settings */ +#define CIR_IREN_RDR 0x80 +#define CIR_IREN_RTR 0x40 +#define CIR_IREN_PE 0x20 +#define CIR_IREN_RFO 0x10 +#define CIR_IREN_TE 0x08 +#define CIR_IREN_TTR 0x04 +#define CIR_IREN_TFU 0x02 +#define CIR_IREN_GH 0x01 + +/* CIR FIFOCON settings */ +#define CIR_FIFOCON_TXFIFOCLR 0x80 + +#define CIR_FIFOCON_TX_TRIGGER_LEV_31 0x00 +#define CIR_FIFOCON_TX_TRIGGER_LEV_24 0x10 +#define CIR_FIFOCON_TX_TRIGGER_LEV_16 0x20 +#define CIR_FIFOCON_TX_TRIGGER_LEV_8 0x30 + +/* FIXME: make this a runtime option */ +/* select TX trigger level as 16 */ +#define CIR_FIFOCON_TX_TRIGGER_LEV CIR_FIFOCON_TX_TRIGGER_LEV_16 + +#define CIR_FIFOCON_RXFIFOCLR 0x08 + +#define CIR_FIFOCON_RX_TRIGGER_LEV_1 0x00 +#define CIR_FIFOCON_RX_TRIGGER_LEV_8 0x01 +#define CIR_FIFOCON_RX_TRIGGER_LEV_16 0x02 +#define CIR_FIFOCON_RX_TRIGGER_LEV_24 0x03 + +/* FIXME: make this a runtime option */ +/* select RX trigger level as 24 */ +#define CIR_FIFOCON_RX_TRIGGER_LEV CIR_FIFOCON_RX_TRIGGER_LEV_24 + +/* CIR IRFIFOSTS settings */ +#define CIR_IRFIFOSTS_IR_PENDING 0x80 +#define CIR_IRFIFOSTS_RX_GS 0x40 +#define CIR_IRFIFOSTS_RX_FTA 0x20 +#define CIR_IRFIFOSTS_RX_EMPTY 0x10 +#define CIR_IRFIFOSTS_RX_FULL 0x08 +#define CIR_IRFIFOSTS_TX_FTA 0x04 +#define CIR_IRFIFOSTS_TX_EMPTY 0x02 +#define CIR_IRFIFOSTS_TX_FULL 0x01 + + +/* CIR WAKE UP Regs */ +#define CIR_WAKE_IRCON 0x00 +#define CIR_WAKE_IRSTS 0x01 +#define CIR_WAKE_IREN 0x02 +#define CIR_WAKE_FIFO_CMP_DEEP 0x03 +#define CIR_WAKE_FIFO_CMP_TOL 0x04 +#define CIR_WAKE_FIFO_COUNT 0x05 +#define CIR_WAKE_SLCH 0x06 +#define CIR_WAKE_SLCL 0x07 +#define CIR_WAKE_FIFOCON 0x08 +#define CIR_WAKE_SRXFSTS 0x09 +#define CIR_WAKE_SAMPLE_RX_FIFO 0x0a +#define CIR_WAKE_WR_FIFO_DATA 0x0b +#define CIR_WAKE_RD_FIFO_ONLY 0x0c +#define CIR_WAKE_RD_FIFO_ONLY_IDX 0x0d +#define CIR_WAKE_FIFO_IGNORE 0x0e +#define CIR_WAKE_IRFSM 0x0f + +/* CIR WAKE UP IRCON settings */ +#define CIR_WAKE_IRCON_DEC_RST 0x80 +#define CIR_WAKE_IRCON_MODE1 0x40 +#define CIR_WAKE_IRCON_MODE0 0x20 +#define CIR_WAKE_IRCON_RXEN 0x10 +#define CIR_WAKE_IRCON_R 0x08 +#define CIR_WAKE_IRCON_RXINV 0x04 + +/* FIXME/jarod: make this a runtime option */ +/* select a same sample period like cir register */ +#define CIR_WAKE_IRCON_SAMPLE_PERIOD_SEL CIR_IRCON_SAMPLE_PERIOD_SEL_50 + +/* CIR WAKE IRSTS Bits */ +#define CIR_WAKE_IRSTS_RDR 0x80 +#define CIR_WAKE_IRSTS_RTR 0x40 +#define CIR_WAKE_IRSTS_PE 0x20 +#define CIR_WAKE_IRSTS_RFO 0x10 +#define CIR_WAKE_IRSTS_GH 0x08 +#define CIR_WAKE_IRSTS_IR_PENDING 0x01 + +/* CIR WAKE UP IREN Bits */ +#define CIR_WAKE_IREN_RDR 0x80 +#define CIR_WAKE_IREN_RTR 0x40 +#define CIR_WAKE_IREN_PE 0x20 +#define CIR_WAKE_IREN_RFO 0x10 +#define CIR_WAKE_IREN_TE 0x08 +#define CIR_WAKE_IREN_TTR 0x04 +#define CIR_WAKE_IREN_TFU 0x02 +#define CIR_WAKE_IREN_GH 0x01 + +/* CIR WAKE FIFOCON settings */ +#define CIR_WAKE_FIFOCON_RXFIFOCLR 0x08 + +#define CIR_WAKE_FIFOCON_RX_TRIGGER_LEV_67 0x00 +#define CIR_WAKE_FIFOCON_RX_TRIGGER_LEV_66 0x01 +#define CIR_WAKE_FIFOCON_RX_TRIGGER_LEV_65 0x02 +#define CIR_WAKE_FIFOCON_RX_TRIGGER_LEV_64 0x03 + +/* FIXME: make this a runtime option */ +/* select WAKE UP RX trigger level as 67 */ +#define CIR_WAKE_FIFOCON_RX_TRIGGER_LEV CIR_WAKE_FIFOCON_RX_TRIGGER_LEV_67 + +/* CIR WAKE SRXFSTS settings */ +#define CIR_WAKE_IRFIFOSTS_RX_GS 0x80 +#define CIR_WAKE_IRFIFOSTS_RX_FTA 0x40 +#define CIR_WAKE_IRFIFOSTS_RX_EMPTY 0x20 +#define CIR_WAKE_IRFIFOSTS_RX_FULL 0x10 + +/* CIR Wake FIFO buffer is 67 bytes long */ +#define CIR_WAKE_FIFO_LEN 67 +/* CIR Wake byte comparison tolerance */ +#define CIR_WAKE_CMP_TOLERANCE 5 + +/* + * Extended Function Enable Registers: + * Extended Function Index Register + * Extended Function Data Register + */ +#define CR_EFIR 0x2e +#define CR_EFDR 0x2f + +/* Possible alternate EFER values, depends on how the chip is wired */ +#define CR_EFIR2 0x4e +#define CR_EFDR2 0x4f + +/* Extended Function Mode enable/disable magic values */ +#define EFER_EFM_ENABLE 0x87 +#define EFER_EFM_DISABLE 0xaa + +/* Chip IDs found in CR_CHIP_ID_{HI,LO} */ +#define CHIP_ID_HIGH 0xb4 +#define CHIP_ID_LOW 0x72 +#define CHIP_ID_LOW2 0x73 + +/* Config regs we need to care about */ +#define CR_SOFTWARE_RESET 0x02 +#define CR_LOGICAL_DEV_SEL 0x07 +#define CR_CHIP_ID_HI 0x20 +#define CR_CHIP_ID_LO 0x21 +#define CR_DEV_POWER_DOWN 0x22 /* bit 2 is CIR power, default power on */ +#define CR_OUTPUT_PIN_SEL 0x27 +#define CR_LOGICAL_DEV_EN 0x30 /* valid for all logical devices */ +/* next three regs valid for both the CIR and CIR_WAKE logical devices */ +#define CR_CIR_BASE_ADDR_HI 0x60 +#define CR_CIR_BASE_ADDR_LO 0x61 +#define CR_CIR_IRQ_RSRC 0x70 +/* next three regs valid only for ACPI logical dev */ +#define CR_ACPI_CIR_WAKE 0xe0 +#define CR_ACPI_IRQ_EVENTS 0xf6 +#define CR_ACPI_IRQ_EVENTS2 0xf7 + +/* Logical devices that we need to care about */ +#define LOGICAL_DEV_LPT 0x01 +#define LOGICAL_DEV_CIR 0x06 +#define LOGICAL_DEV_ACPI 0x0a +#define LOGICAL_DEV_CIR_WAKE 0x0e + +#define LOGICAL_DEV_DISABLE 0x00 +#define LOGICAL_DEV_ENABLE 0x01 + +#define CIR_WAKE_ENABLE_BIT 0x08 +#define CIR_INTR_MOUSE_IRQ_BIT 0x80 +#define PME_INTR_CIR_PASS_BIT 0x08 + +#define OUTPUT_PIN_SEL_MASK 0xbc +#define OUTPUT_ENABLE_CIR 0x01 /* Pin95=CIRRX, Pin96=CIRTX1 */ +#define OUTPUT_ENABLE_CIRWB 0x40 /* enable wide-band sensor */ + +/* MCE CIR signal length, related on sample period */ + +/* MCE CIR controller signal length: about 43ms + * 43ms / 50us (sample period) * 0.85 (inaccuracy) + */ +#define CONTROLLER_BUF_LEN_MIN 830 + +/* MCE CIR keyboard signal length: about 26ms + * 26ms / 50us (sample period) * 0.85 (inaccuracy) + */ +#define KEYBOARD_BUF_LEN_MAX 650 +#define KEYBOARD_BUF_LEN_MIN 610 + +/* MCE CIR mouse signal length: about 24ms + * 24ms / 50us (sample period) * 0.85 (inaccuracy) + */ +#define MOUSE_BUF_LEN_MIN 565 + +#define CIR_SAMPLE_PERIOD 50 +#define CIR_SAMPLE_LOW_INACCURACY 0.85 + +/* MAX silence time that driver will sent to lirc */ +#define MAX_SILENCE_TIME 60000 + +#if CIR_IRCON_SAMPLE_PERIOD_SEL == CIR_IRCON_SAMPLE_PERIOD_SEL_100 +#define SAMPLE_PERIOD 100 + +#elif CIR_IRCON_SAMPLE_PERIOD_SEL == CIR_IRCON_SAMPLE_PERIOD_SEL_50 +#define SAMPLE_PERIOD 50 + +#elif CIR_IRCON_SAMPLE_PERIOD_SEL == CIR_IRCON_SAMPLE_PERIOD_SEL_25 +#define SAMPLE_PERIOD 25 + +#else +#define SAMPLE_PERIOD 1 +#endif + +/* as VISTA MCE definition, valid carrier value */ +#define MAX_CARRIER 60000 +#define MIN_CARRIER 30000 diff --git a/drivers/media/IR/streamzap.c b/drivers/media/IR/streamzap.c index 058e29fd478c..548381c35bfd 100644 --- a/drivers/media/IR/streamzap.c +++ b/drivers/media/IR/streamzap.c @@ -38,7 +38,7 @@ #include <linux/input.h> #include <media/ir-core.h> -#define DRIVER_VERSION "1.60" +#define DRIVER_VERSION "1.61" #define DRIVER_NAME "streamzap" #define DRIVER_DESC "Streamzap Remote Control driver" @@ -61,14 +61,21 @@ static struct usb_device_id streamzap_table[] = { MODULE_DEVICE_TABLE(usb, streamzap_table); -#define STREAMZAP_PULSE_MASK 0xf0 -#define STREAMZAP_SPACE_MASK 0x0f -#define STREAMZAP_TIMEOUT 0xff -#define STREAMZAP_RESOLUTION 256 +#define SZ_PULSE_MASK 0xf0 +#define SZ_SPACE_MASK 0x0f +#define SZ_TIMEOUT 0xff +#define SZ_RESOLUTION 256 /* number of samples buffered */ #define SZ_BUF_LEN 128 +/* from ir-rc5-sz-decoder.c */ +#ifdef CONFIG_IR_RC5_SZ_DECODER_MODULE +#define load_rc5_sz_decode() request_module("ir-rc5-sz-decoder") +#else +#define load_rc5_sz_decode() 0 +#endif + enum StreamzapDecoderState { PulseSpace, FullPulse, @@ -81,7 +88,6 @@ struct streamzap_ir { /* ir-core */ struct ir_dev_props *props; - struct ir_raw_event rawir; /* core device info */ struct device *dev; @@ -98,17 +104,6 @@ struct streamzap_ir { 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 */ @@ -118,7 +113,7 @@ struct streamzap_ir { /* start time of signal; necessary for gap tracking */ struct timeval signal_last; struct timeval signal_start; - /* bool timeout_enabled; */ + bool timeout_enabled; char name[128]; char phys[64]; @@ -143,122 +138,16 @@ static struct usb_driver streamzap_driver = { .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) +static void sz_push(struct streamzap_ir *sz, struct ir_raw_event rawir) { - 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); + ir_raw_event_store(sz->idev, &rawir); } static void sz_push_full_pulse(struct streamzap_ir *sz, unsigned char value) { + DEFINE_IR_RAW_EVENT(rawir); + if (sz->idle) { long deltv; @@ -266,57 +155,59 @@ static void sz_push_full_pulse(struct streamzap_ir *sz, do_gettimeofday(&sz->signal_start); deltv = sz->signal_start.tv_sec - sz->signal_last.tv_sec; - sz->rawir.pulse = false; + rawir.pulse = false; if (deltv > 15) { /* really long time */ - sz->rawir.duration = IR_MAX_DURATION; + rawir.duration = IR_MAX_DURATION; } else { - sz->rawir.duration = (int)(deltv * 1000000 + + 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; + rawir.duration -= sz->sum; + rawir.duration *= 1000; + rawir.duration &= IR_MAX_DURATION; } - dev_dbg(sz->dev, "ls %u\n", sz->rawir.duration); - sz_push(sz); + dev_dbg(sz->dev, "ls %u\n", rawir.duration); + sz_push(sz, rawir); - sz->idle = 0; + sz->idle = false; 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); + rawir.pulse = true; + rawir.duration = ((int) value) * SZ_RESOLUTION; + rawir.duration += SZ_RESOLUTION / 2; + sz->sum += rawir.duration; + rawir.duration *= 1000; + rawir.duration &= IR_MAX_DURATION; + dev_dbg(sz->dev, "p %u\n", rawir.duration); + sz_push(sz, rawir); } static void sz_push_half_pulse(struct streamzap_ir *sz, unsigned char value) { - sz_push_full_pulse(sz, (value & STREAMZAP_PULSE_MASK) >> 4); + sz_push_full_pulse(sz, (value & SZ_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); + DEFINE_IR_RAW_EVENT(rawir); + + rawir.pulse = false; + rawir.duration = ((int) value) * SZ_RESOLUTION; + rawir.duration += SZ_RESOLUTION / 2; + sz->sum += rawir.duration; + rawir.duration *= 1000; + dev_dbg(sz->dev, "s %u\n", rawir.duration); + sz_push(sz, rawir); } static void sz_push_half_space(struct streamzap_ir *sz, unsigned long value) { - sz_push_full_space(sz, value & STREAMZAP_SPACE_MASK); + sz_push_full_space(sz, value & SZ_SPACE_MASK); } /** @@ -330,10 +221,8 @@ 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) & + static int timeout = (((SZ_TIMEOUT * SZ_RESOLUTION * 1000) & IR_MAX_DURATION) | 0x03000000); - #endif if (!urb) return; @@ -356,57 +245,53 @@ static void streamzap_callback(struct urb *urb) } 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; - } + for (i = 0; i < len; i++) { + dev_dbg(sz->dev, "sz idx %d: %x\n", + i, (unsigned char)sz->buf_in[i]); + switch (sz->decoder_state) { + case PulseSpace: + if ((sz->buf_in[i] & SZ_PULSE_MASK) == + SZ_PULSE_MASK) { + sz->decoder_state = FullPulse; + continue; + } else if ((sz->buf_in[i] & SZ_SPACE_MASK) + == SZ_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]); - sz->decoder_state = PulseSpace; - break; } + break; + case FullPulse: + sz_push_full_pulse(sz, sz->buf_in[i]); + sz->decoder_state = IgnorePulse; + break; + case FullSpace: + if (sz->buf_in[i] == SZ_TIMEOUT) { + DEFINE_IR_RAW_EVENT(rawir); + + rawir.pulse = false; + rawir.duration = timeout; + sz->idle = true; + if (sz->timeout_enabled) + sz_push(sz, rawir); + ir_raw_event_handle(sz->idev); + } else { + sz_push_full_space(sz, sz->buf_in[i]); + } + sz->decoder_state = PulseSpace; + break; + case IgnorePulse: + if ((sz->buf_in[i] & SZ_SPACE_MASK) == + SZ_SPACE_MASK) { + sz->decoder_state = FullSpace; + continue; + } + sz_push_half_space(sz, sz->buf_in[i]); + sz->decoder_state = PulseSpace; + break; } } @@ -446,12 +331,11 @@ static struct input_dev *streamzap_init_input_dev(struct streamzap_ir *sz) 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; + props->allowed_protos = IR_TYPE_ALL; sz->props = props; - ret = ir_input_register(idev, RC_MAP_RC5_STREAMZAP, props, DRIVER_NAME); + ret = ir_input_register(idev, RC_MAP_STREAMZAP, props, DRIVER_NAME); if (ret < 0) { dev_err(dev, "remote input device register failed\n"); goto irdev_failed; @@ -467,29 +351,6 @@ 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 * @@ -575,35 +436,21 @@ static int __devinit streamzap_probe(struct usb_interface *intf, 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; + /* FIXME: don't yet have a way to set this */ + sz->timeout_enabled = true; #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; + sz->min_timeout = SZ_TIMEOUT * SZ_RESOLUTION * 1000; + sz->max_timeout = SZ_TIMEOUT * SZ_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 */ @@ -615,16 +462,18 @@ static int __devinit streamzap_probe(struct usb_interface *intf, usb_set_intfdata(intf, sz); - streamzap_start_flush_timer(sz); + if (usb_submit_urb(sz->urb_in, GFP_ATOMIC)) + dev_err(sz->dev, "urb submit failed\n"); dev_info(sz->dev, "Registered %s on usb%d:%d\n", name, usbdev->bus->busnum, usbdev->devnum); + /* Load the streamzap not-quite-rc5 decoder too */ + load_rc5_sz_decode(); + 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); @@ -654,13 +503,6 @@ static void streamzap_disconnect(struct usb_interface *interface) 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); @@ -674,13 +516,6 @@ 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; @@ -690,13 +525,6 @@ 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; |