diff options
Diffstat (limited to 'drivers/media/rc')
-rw-r--r-- | drivers/media/rc/Kconfig | 23 | ||||
-rw-r--r-- | drivers/media/rc/Makefile | 2 | ||||
-rw-r--r-- | drivers/media/rc/fintek-cir.c | 684 | ||||
-rw-r--r-- | drivers/media/rc/fintek-cir.h | 243 | ||||
-rw-r--r-- | drivers/media/rc/imon.c | 36 | ||||
-rw-r--r-- | drivers/media/rc/ite-cir.c | 60 | ||||
-rw-r--r-- | drivers/media/rc/keymaps/Makefile | 1 | ||||
-rw-r--r-- | drivers/media/rc/keymaps/rc-avermedia-cardbus.c | 2 | ||||
-rw-r--r-- | drivers/media/rc/keymaps/rc-imon-mce.c | 2 | ||||
-rw-r--r-- | drivers/media/rc/keymaps/rc-imon-pad.c | 6 | ||||
-rw-r--r-- | drivers/media/rc/keymaps/rc-kworld-plus-tv-analog.c | 2 | ||||
-rw-r--r-- | drivers/media/rc/keymaps/rc-lme2510.c | 134 | ||||
-rw-r--r-- | drivers/media/rc/keymaps/rc-rc6-mce.c | 4 | ||||
-rw-r--r-- | drivers/media/rc/keymaps/rc-tivo.c | 98 | ||||
-rw-r--r-- | drivers/media/rc/keymaps/rc-winfast.c | 4 | ||||
-rw-r--r-- | drivers/media/rc/mceusb.c | 34 | ||||
-rw-r--r-- | drivers/media/rc/nuvoton-cir.c | 75 | ||||
-rw-r--r-- | drivers/media/rc/nuvoton-cir.h | 17 | ||||
-rw-r--r-- | drivers/media/rc/rc-loopback.c | 6 | ||||
-rw-r--r-- | drivers/media/rc/rc-main.c | 54 | ||||
-rw-r--r-- | drivers/media/rc/redrat3.c | 1344 | ||||
-rw-r--r-- | drivers/media/rc/winbond-cir.c | 447 |
22 files changed, 3005 insertions, 273 deletions
diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig index 7f03142a329f..7d4bbc226d06 100644 --- a/drivers/media/rc/Kconfig +++ b/drivers/media/rc/Kconfig @@ -148,6 +148,18 @@ config IR_ITE_CIR To compile this driver as a module, choose M here: the module will be called ite-cir. +config IR_FINTEK + tristate "Fintek Consumer Infrared Transceiver" + depends on PNP + depends on RC_CORE + ---help--- + Say Y here to enable support for integrated infrared receiver + /transciever made by Fintek. This chip is found on assorted + Jetway motherboards (and of course, possibly others). + + To compile this driver as a module, choose M here: the + module will be called fintek-cir. + config IR_NUVOTON tristate "Nuvoton w836x7hg Consumer Infrared Transceiver" depends on PNP @@ -161,6 +173,17 @@ config IR_NUVOTON To compile this driver as a module, choose M here: the module will be called nuvoton-cir. +config IR_REDRAT3 + tristate "RedRat3 IR Transceiver" + depends on USB_ARCH_HAS_HCD + depends on RC_CORE + select USB + ---help--- + Say Y here if you want to use a RedRat3 Infrared Transceiver. + + To compile this driver as a module, choose M here: the + module will be called redrat3. + config IR_STREAMZAP tristate "Streamzap PC Remote IR Receiver" depends on USB_ARCH_HAS_HCD diff --git a/drivers/media/rc/Makefile b/drivers/media/rc/Makefile index c6cfe70d862f..52830e5f4eaa 100644 --- a/drivers/media/rc/Makefile +++ b/drivers/media/rc/Makefile @@ -16,8 +16,10 @@ obj-$(CONFIG_IR_LIRC_CODEC) += ir-lirc-codec.o obj-$(CONFIG_IR_IMON) += imon.o obj-$(CONFIG_IR_ITE_CIR) += ite-cir.o obj-$(CONFIG_IR_MCEUSB) += mceusb.o +obj-$(CONFIG_IR_FINTEK) += fintek-cir.o obj-$(CONFIG_IR_NUVOTON) += nuvoton-cir.o obj-$(CONFIG_IR_ENE) += ene_ir.o +obj-$(CONFIG_IR_REDRAT3) += redrat3.o obj-$(CONFIG_IR_STREAMZAP) += streamzap.o obj-$(CONFIG_IR_WINBOND_CIR) += winbond-cir.o obj-$(CONFIG_RC_LOOPBACK) += rc-loopback.o diff --git a/drivers/media/rc/fintek-cir.c b/drivers/media/rc/fintek-cir.c new file mode 100644 index 000000000000..8fa539dde1b4 --- /dev/null +++ b/drivers/media/rc/fintek-cir.c @@ -0,0 +1,684 @@ +/* + * Driver for Feature Integration Technology Inc. (aka Fintek) LPC CIR + * + * Copyright (C) 2011 Jarod Wilson <jarod@redhat.com> + * + * Special thanks to Fintek for providing hardware and spec sheets. + * This driver is based upon the nuvoton, ite and ene drivers for + * similar hardware. + * + * 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 <media/rc-core.h> +#include <linux/pci_ids.h> + +#include "fintek-cir.h" + +/* write val to config reg */ +static inline void fintek_cr_write(struct fintek_dev *fintek, u8 val, u8 reg) +{ + fit_dbg("%s: reg 0x%02x, val 0x%02x (ip/dp: %02x/%02x)", + __func__, reg, val, fintek->cr_ip, fintek->cr_dp); + outb(reg, fintek->cr_ip); + outb(val, fintek->cr_dp); +} + +/* read val from config reg */ +static inline u8 fintek_cr_read(struct fintek_dev *fintek, u8 reg) +{ + u8 val; + + outb(reg, fintek->cr_ip); + val = inb(fintek->cr_dp); + + fit_dbg("%s: reg 0x%02x, val 0x%02x (ip/dp: %02x/%02x)", + __func__, reg, val, fintek->cr_ip, fintek->cr_dp); + return val; +} + +/* update config register bit without changing other bits */ +static inline void fintek_set_reg_bit(struct fintek_dev *fintek, u8 val, u8 reg) +{ + u8 tmp = fintek_cr_read(fintek, reg) | val; + fintek_cr_write(fintek, tmp, reg); +} + +/* clear config register bit without changing other bits */ +static inline void fintek_clear_reg_bit(struct fintek_dev *fintek, u8 val, u8 reg) +{ + u8 tmp = fintek_cr_read(fintek, reg) & ~val; + fintek_cr_write(fintek, tmp, reg); +} + +/* enter config mode */ +static inline void fintek_config_mode_enable(struct fintek_dev *fintek) +{ + /* Enabling Config Mode explicitly requires writing 2x */ + outb(CONFIG_REG_ENABLE, fintek->cr_ip); + outb(CONFIG_REG_ENABLE, fintek->cr_ip); +} + +/* exit config mode */ +static inline void fintek_config_mode_disable(struct fintek_dev *fintek) +{ + outb(CONFIG_REG_DISABLE, fintek->cr_ip); +} + +/* + * When you want to address a specific logical device, write its logical + * device number to GCR_LOGICAL_DEV_NO + */ +static inline void fintek_select_logical_dev(struct fintek_dev *fintek, u8 ldev) +{ + fintek_cr_write(fintek, ldev, GCR_LOGICAL_DEV_NO); +} + +/* write val to cir config register */ +static inline void fintek_cir_reg_write(struct fintek_dev *fintek, u8 val, u8 offset) +{ + outb(val, fintek->cir_addr + offset); +} + +/* read val from cir config register */ +static u8 fintek_cir_reg_read(struct fintek_dev *fintek, u8 offset) +{ + u8 val; + + val = inb(fintek->cir_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 fintek_dev *fintek) +{ + fintek_config_mode_enable(fintek); + fintek_select_logical_dev(fintek, LOGICAL_DEV_CIR); + + pr_reg("%s: Dump CIR logical device registers:\n", FINTEK_DRIVER_NAME); + pr_reg(" * CR CIR BASE ADDR: 0x%x\n", + (fintek_cr_read(fintek, CIR_CR_BASE_ADDR_HI) << 8) | + fintek_cr_read(fintek, CIR_CR_BASE_ADDR_LO)); + pr_reg(" * CR CIR IRQ NUM: 0x%x\n", + fintek_cr_read(fintek, CIR_CR_IRQ_SEL)); + + fintek_config_mode_disable(fintek); + + pr_reg("%s: Dump CIR registers:\n", FINTEK_DRIVER_NAME); + pr_reg(" * STATUS: 0x%x\n", fintek_cir_reg_read(fintek, CIR_STATUS)); + pr_reg(" * CONTROL: 0x%x\n", fintek_cir_reg_read(fintek, CIR_CONTROL)); + pr_reg(" * RX_DATA: 0x%x\n", fintek_cir_reg_read(fintek, CIR_RX_DATA)); + pr_reg(" * TX_CONTROL: 0x%x\n", fintek_cir_reg_read(fintek, CIR_TX_CONTROL)); + pr_reg(" * TX_DATA: 0x%x\n", fintek_cir_reg_read(fintek, CIR_TX_DATA)); +} + +/* detect hardware features */ +static int fintek_hw_detect(struct fintek_dev *fintek) +{ + unsigned long flags; + u8 chip_major, chip_minor; + u8 vendor_major, vendor_minor; + u8 portsel, ir_class; + u16 vendor; + int ret = 0; + + fintek_config_mode_enable(fintek); + + /* Check if we're using config port 0x4e or 0x2e */ + portsel = fintek_cr_read(fintek, GCR_CONFIG_PORT_SEL); + if (portsel == 0xff) { + fit_pr(KERN_INFO, "first portsel read was bunk, trying alt"); + fintek_config_mode_disable(fintek); + fintek->cr_ip = CR_INDEX_PORT2; + fintek->cr_dp = CR_DATA_PORT2; + fintek_config_mode_enable(fintek); + portsel = fintek_cr_read(fintek, GCR_CONFIG_PORT_SEL); + } + fit_dbg("portsel reg: 0x%02x", portsel); + + ir_class = fintek_cir_reg_read(fintek, CIR_CR_CLASS); + fit_dbg("ir_class reg: 0x%02x", ir_class); + + switch (ir_class) { + case CLASS_RX_2TX: + case CLASS_RX_1TX: + fintek->hw_tx_capable = true; + break; + case CLASS_RX_ONLY: + default: + fintek->hw_tx_capable = false; + break; + } + + chip_major = fintek_cr_read(fintek, GCR_CHIP_ID_HI); + chip_minor = fintek_cr_read(fintek, GCR_CHIP_ID_LO); + + vendor_major = fintek_cr_read(fintek, GCR_VENDOR_ID_HI); + vendor_minor = fintek_cr_read(fintek, GCR_VENDOR_ID_LO); + vendor = vendor_major << 8 | vendor_minor; + + if (vendor != VENDOR_ID_FINTEK) + fit_pr(KERN_WARNING, "Unknown vendor ID: 0x%04x", vendor); + else + fit_dbg("Read Fintek vendor ID from chip"); + + fintek_config_mode_disable(fintek); + + spin_lock_irqsave(&fintek->fintek_lock, flags); + fintek->chip_major = chip_major; + fintek->chip_minor = chip_minor; + fintek->chip_vendor = vendor; + spin_unlock_irqrestore(&fintek->fintek_lock, flags); + + return ret; +} + +static void fintek_cir_ldev_init(struct fintek_dev *fintek) +{ + /* Select CIR logical device and enable */ + fintek_select_logical_dev(fintek, LOGICAL_DEV_CIR); + fintek_cr_write(fintek, LOGICAL_DEV_ENABLE, CIR_CR_DEV_EN); + + /* Write allocated CIR address and IRQ information to hardware */ + fintek_cr_write(fintek, fintek->cir_addr >> 8, CIR_CR_BASE_ADDR_HI); + fintek_cr_write(fintek, fintek->cir_addr & 0xff, CIR_CR_BASE_ADDR_LO); + + fintek_cr_write(fintek, fintek->cir_irq, CIR_CR_IRQ_SEL); + + fit_dbg("CIR initialized, base io address: 0x%lx, irq: %d (len: %d)", + fintek->cir_addr, fintek->cir_irq, fintek->cir_port_len); +} + +/* enable CIR interrupts */ +static void fintek_enable_cir_irq(struct fintek_dev *fintek) +{ + fintek_cir_reg_write(fintek, CIR_STATUS_IRQ_EN, CIR_STATUS); +} + +static void fintek_cir_regs_init(struct fintek_dev *fintek) +{ + /* clear any and all stray interrupts */ + fintek_cir_reg_write(fintek, CIR_STATUS_IRQ_MASK, CIR_STATUS); + + /* and finally, enable interrupts */ + fintek_enable_cir_irq(fintek); +} + +static void fintek_enable_wake(struct fintek_dev *fintek) +{ + fintek_config_mode_enable(fintek); + fintek_select_logical_dev(fintek, LOGICAL_DEV_ACPI); + + /* Allow CIR PME's to wake system */ + fintek_set_reg_bit(fintek, ACPI_WAKE_EN_CIR_BIT, LDEV_ACPI_WAKE_EN_REG); + /* Enable CIR PME's */ + fintek_set_reg_bit(fintek, ACPI_PME_CIR_BIT, LDEV_ACPI_PME_EN_REG); + /* Clear CIR PME status register */ + fintek_set_reg_bit(fintek, ACPI_PME_CIR_BIT, LDEV_ACPI_PME_CLR_REG); + /* Save state */ + fintek_set_reg_bit(fintek, ACPI_STATE_CIR_BIT, LDEV_ACPI_STATE_REG); + + fintek_config_mode_disable(fintek); +} + +static int fintek_cmdsize(u8 cmd, u8 subcmd) +{ + int datasize = 0; + + switch (cmd) { + case BUF_COMMAND_NULL: + if (subcmd == BUF_HW_CMD_HEADER) + datasize = 1; + break; + case BUF_HW_CMD_HEADER: + if (subcmd == BUF_CMD_G_REVISION) + datasize = 2; + break; + case BUF_COMMAND_HEADER: + switch (subcmd) { + case BUF_CMD_S_CARRIER: + case BUF_CMD_S_TIMEOUT: + case BUF_RSP_PULSE_COUNT: + datasize = 2; + break; + case BUF_CMD_SIG_END: + case BUF_CMD_S_TXMASK: + case BUF_CMD_S_RXSENSOR: + datasize = 1; + break; + } + } + + return datasize; +} + +/* process ir data stored in driver buffer */ +static void fintek_process_rx_ir_data(struct fintek_dev *fintek) +{ + DEFINE_IR_RAW_EVENT(rawir); + u8 sample; + int i; + + for (i = 0; i < fintek->pkts; i++) { + sample = fintek->buf[i]; + switch (fintek->parser_state) { + case CMD_HEADER: + fintek->cmd = sample; + if ((fintek->cmd == BUF_COMMAND_HEADER) || + ((fintek->cmd & BUF_COMMAND_MASK) != + BUF_PULSE_BIT)) { + fintek->parser_state = SUBCMD; + continue; + } + fintek->rem = (fintek->cmd & BUF_LEN_MASK); + fit_dbg("%s: rem: 0x%02x", __func__, fintek->rem); + if (fintek->rem) + fintek->parser_state = PARSE_IRDATA; + else + ir_raw_event_reset(fintek->rdev); + break; + case SUBCMD: + fintek->rem = fintek_cmdsize(fintek->cmd, sample); + fintek->parser_state = CMD_DATA; + break; + case CMD_DATA: + fintek->rem--; + break; + case PARSE_IRDATA: + fintek->rem--; + init_ir_raw_event(&rawir); + rawir.pulse = ((sample & BUF_PULSE_BIT) != 0); + rawir.duration = US_TO_NS((sample & BUF_SAMPLE_MASK) + * CIR_SAMPLE_PERIOD); + + fit_dbg("Storing %s with duration %d", + rawir.pulse ? "pulse" : "space", + rawir.duration); + ir_raw_event_store_with_filter(fintek->rdev, &rawir); + break; + } + + if ((fintek->parser_state != CMD_HEADER) && !fintek->rem) + fintek->parser_state = CMD_HEADER; + } + + fintek->pkts = 0; + + fit_dbg("Calling ir_raw_event_handle"); + ir_raw_event_handle(fintek->rdev); +} + +/* copy data from hardware rx register into driver buffer */ +static void fintek_get_rx_ir_data(struct fintek_dev *fintek, u8 rx_irqs) +{ + unsigned long flags; + u8 sample, status; + + spin_lock_irqsave(&fintek->fintek_lock, flags); + + /* + * We must read data from CIR_RX_DATA until the hardware IR buffer + * is empty and clears the RX_TIMEOUT and/or RX_RECEIVE flags in + * the CIR_STATUS register + */ + do { + sample = fintek_cir_reg_read(fintek, CIR_RX_DATA); + fit_dbg("%s: sample: 0x%02x", __func__, sample); + + fintek->buf[fintek->pkts] = sample; + fintek->pkts++; + + status = fintek_cir_reg_read(fintek, CIR_STATUS); + if (!(status & CIR_STATUS_IRQ_EN)) + break; + } while (status & rx_irqs); + + fintek_process_rx_ir_data(fintek); + + spin_unlock_irqrestore(&fintek->fintek_lock, flags); +} + +static void fintek_cir_log_irqs(u8 status) +{ + fit_pr(KERN_INFO, "IRQ 0x%02x:%s%s%s%s%s", status, + status & CIR_STATUS_IRQ_EN ? " IRQEN" : "", + status & CIR_STATUS_TX_FINISH ? " TXF" : "", + status & CIR_STATUS_TX_UNDERRUN ? " TXU" : "", + status & CIR_STATUS_RX_TIMEOUT ? " RXTO" : "", + status & CIR_STATUS_RX_RECEIVE ? " RXOK" : ""); +} + +/* interrupt service routine for incoming and outgoing CIR data */ +static irqreturn_t fintek_cir_isr(int irq, void *data) +{ + struct fintek_dev *fintek = data; + u8 status, rx_irqs; + + fit_dbg_verbose("%s firing", __func__); + + fintek_config_mode_enable(fintek); + fintek_select_logical_dev(fintek, LOGICAL_DEV_CIR); + fintek_config_mode_disable(fintek); + + /* + * Get IR Status register contents. Write 1 to ack/clear + * + * bit: reg name - description + * 3: TX_FINISH - TX is finished + * 2: TX_UNDERRUN - TX underrun + * 1: RX_TIMEOUT - RX data timeout + * 0: RX_RECEIVE - RX data received + */ + status = fintek_cir_reg_read(fintek, CIR_STATUS); + if (!(status & CIR_STATUS_IRQ_MASK) || status == 0xff) { + fit_dbg_verbose("%s exiting, IRSTS 0x%02x", __func__, status); + fintek_cir_reg_write(fintek, CIR_STATUS_IRQ_MASK, CIR_STATUS); + return IRQ_RETVAL(IRQ_NONE); + } + + if (debug) + fintek_cir_log_irqs(status); + + rx_irqs = status & (CIR_STATUS_RX_RECEIVE | CIR_STATUS_RX_TIMEOUT); + if (rx_irqs) + fintek_get_rx_ir_data(fintek, rx_irqs); + + /* ack/clear all irq flags we've got */ + fintek_cir_reg_write(fintek, status, CIR_STATUS); + + fit_dbg_verbose("%s done", __func__); + return IRQ_RETVAL(IRQ_HANDLED); +} + +static void fintek_enable_cir(struct fintek_dev *fintek) +{ + /* set IRQ enabled */ + fintek_cir_reg_write(fintek, CIR_STATUS_IRQ_EN, CIR_STATUS); + + fintek_config_mode_enable(fintek); + + /* enable the CIR logical device */ + fintek_select_logical_dev(fintek, LOGICAL_DEV_CIR); + fintek_cr_write(fintek, LOGICAL_DEV_ENABLE, CIR_CR_DEV_EN); + + fintek_config_mode_disable(fintek); + + /* clear all pending interrupts */ + fintek_cir_reg_write(fintek, CIR_STATUS_IRQ_MASK, CIR_STATUS); + + /* enable interrupts */ + fintek_enable_cir_irq(fintek); +} + +static void fintek_disable_cir(struct fintek_dev *fintek) +{ + fintek_config_mode_enable(fintek); + + /* disable the CIR logical device */ + fintek_select_logical_dev(fintek, LOGICAL_DEV_CIR); + fintek_cr_write(fintek, LOGICAL_DEV_DISABLE, CIR_CR_DEV_EN); + + fintek_config_mode_disable(fintek); +} + +static int fintek_open(struct rc_dev *dev) +{ + struct fintek_dev *fintek = dev->priv; + unsigned long flags; + + spin_lock_irqsave(&fintek->fintek_lock, flags); + fintek_enable_cir(fintek); + spin_unlock_irqrestore(&fintek->fintek_lock, flags); + + return 0; +} + +static void fintek_close(struct rc_dev *dev) +{ + struct fintek_dev *fintek = dev->priv; + unsigned long flags; + + spin_lock_irqsave(&fintek->fintek_lock, flags); + fintek_disable_cir(fintek); + spin_unlock_irqrestore(&fintek->fintek_lock, flags); +} + +/* Allocate memory, probe hardware, and initialize everything */ +static int fintek_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id) +{ + struct fintek_dev *fintek; + struct rc_dev *rdev; + int ret = -ENOMEM; + + fintek = kzalloc(sizeof(struct fintek_dev), GFP_KERNEL); + if (!fintek) + return ret; + + /* input device for IR remote (and tx) */ + rdev = rc_allocate_device(); + if (!rdev) + goto failure; + + ret = -ENODEV; + /* validate pnp resources */ + if (!pnp_port_valid(pdev, 0)) { + dev_err(&pdev->dev, "IR PNP Port not valid!\n"); + goto failure; + } + + if (!pnp_irq_valid(pdev, 0)) { + dev_err(&pdev->dev, "IR PNP IRQ not valid!\n"); + goto failure; + } + + fintek->cir_addr = pnp_port_start(pdev, 0); + fintek->cir_irq = pnp_irq(pdev, 0); + fintek->cir_port_len = pnp_port_len(pdev, 0); + + fintek->cr_ip = CR_INDEX_PORT; + fintek->cr_dp = CR_DATA_PORT; + + spin_lock_init(&fintek->fintek_lock); + + ret = -EBUSY; + /* now claim resources */ + if (!request_region(fintek->cir_addr, + fintek->cir_port_len, FINTEK_DRIVER_NAME)) + goto failure; + + if (request_irq(fintek->cir_irq, fintek_cir_isr, IRQF_SHARED, + FINTEK_DRIVER_NAME, (void *)fintek)) + goto failure; + + pnp_set_drvdata(pdev, fintek); + fintek->pdev = pdev; + + ret = fintek_hw_detect(fintek); + if (ret) + goto failure; + + /* Initialize CIR & CIR Wake Logical Devices */ + fintek_config_mode_enable(fintek); + fintek_cir_ldev_init(fintek); + fintek_config_mode_disable(fintek); + + /* Initialize CIR & CIR Wake Config Registers */ + fintek_cir_regs_init(fintek); + + /* Set up the rc device */ + rdev->priv = fintek; + rdev->driver_type = RC_DRIVER_IR_RAW; + rdev->allowed_protos = RC_TYPE_ALL; + rdev->open = fintek_open; + rdev->close = fintek_close; + rdev->input_name = FINTEK_DESCRIPTION; + rdev->input_phys = "fintek/cir0"; + rdev->input_id.bustype = BUS_HOST; + rdev->input_id.vendor = VENDOR_ID_FINTEK; + rdev->input_id.product = fintek->chip_major; + rdev->input_id.version = fintek->chip_minor; + rdev->dev.parent = &pdev->dev; + rdev->driver_name = FINTEK_DRIVER_NAME; + rdev->map_name = RC_MAP_RC6_MCE; + rdev->timeout = US_TO_NS(1000); + /* rx resolution is hardwired to 50us atm, 1, 25, 100 also possible */ + rdev->rx_resolution = US_TO_NS(CIR_SAMPLE_PERIOD); + + ret = rc_register_device(rdev); + if (ret) + goto failure; + + device_init_wakeup(&pdev->dev, true); + fintek->rdev = rdev; + fit_pr(KERN_NOTICE, "driver has been successfully loaded\n"); + if (debug) + cir_dump_regs(fintek); + + return 0; + +failure: + if (fintek->cir_irq) + free_irq(fintek->cir_irq, fintek); + if (fintek->cir_addr) + release_region(fintek->cir_addr, fintek->cir_port_len); + + rc_free_device(rdev); + kfree(fintek); + + return ret; +} + +static void __devexit fintek_remove(struct pnp_dev *pdev) +{ + struct fintek_dev *fintek = pnp_get_drvdata(pdev); + unsigned long flags; + + spin_lock_irqsave(&fintek->fintek_lock, flags); + /* disable CIR */ + fintek_disable_cir(fintek); + fintek_cir_reg_write(fintek, CIR_STATUS_IRQ_MASK, CIR_STATUS); + /* enable CIR Wake (for IR power-on) */ + fintek_enable_wake(fintek); + spin_unlock_irqrestore(&fintek->fintek_lock, flags); + + /* free resources */ + free_irq(fintek->cir_irq, fintek); + release_region(fintek->cir_addr, fintek->cir_port_len); + + rc_unregister_device(fintek->rdev); + + kfree(fintek); +} + +static int fintek_suspend(struct pnp_dev *pdev, pm_message_t state) +{ + struct fintek_dev *fintek = pnp_get_drvdata(pdev); + + fit_dbg("%s called", __func__); + + /* disable all CIR interrupts */ + fintek_cir_reg_write(fintek, CIR_STATUS_IRQ_MASK, CIR_STATUS); + + fintek_config_mode_enable(fintek); + + /* disable cir logical dev */ + fintek_select_logical_dev(fintek, LOGICAL_DEV_CIR); + fintek_cr_write(fintek, LOGICAL_DEV_DISABLE, CIR_CR_DEV_EN); + + fintek_config_mode_disable(fintek); + + /* make sure wake is enabled */ + fintek_enable_wake(fintek); + + return 0; +} + +static int fintek_resume(struct pnp_dev *pdev) +{ + int ret = 0; + struct fintek_dev *fintek = pnp_get_drvdata(pdev); + + fit_dbg("%s called", __func__); + + /* open interrupt */ + fintek_enable_cir_irq(fintek); + + /* Enable CIR logical device */ + fintek_config_mode_enable(fintek); + fintek_select_logical_dev(fintek, LOGICAL_DEV_CIR); + fintek_cr_write(fintek, LOGICAL_DEV_ENABLE, CIR_CR_DEV_EN); + + fintek_config_mode_disable(fintek); + + fintek_cir_regs_init(fintek); + + return ret; +} + +static void fintek_shutdown(struct pnp_dev *pdev) +{ + struct fintek_dev *fintek = pnp_get_drvdata(pdev); + fintek_enable_wake(fintek); +} + +static const struct pnp_device_id fintek_ids[] = { + { "FIT0002", 0 }, /* CIR */ + { "", 0 }, +}; + +static struct pnp_driver fintek_driver = { + .name = FINTEK_DRIVER_NAME, + .id_table = fintek_ids, + .flags = PNP_DRIVER_RES_DO_NOT_CHANGE, + .probe = fintek_probe, + .remove = __devexit_p(fintek_remove), + .suspend = fintek_suspend, + .resume = fintek_resume, + .shutdown = fintek_shutdown, +}; + +int fintek_init(void) +{ + return pnp_register_driver(&fintek_driver); +} + +void fintek_exit(void) +{ + pnp_unregister_driver(&fintek_driver); +} + +module_param(debug, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "Enable debugging output"); + +MODULE_DEVICE_TABLE(pnp, fintek_ids); +MODULE_DESCRIPTION(FINTEK_DESCRIPTION " driver"); + +MODULE_AUTHOR("Jarod Wilson <jarod@redhat.com>"); +MODULE_LICENSE("GPL"); + +module_init(fintek_init); +module_exit(fintek_exit); diff --git a/drivers/media/rc/fintek-cir.h b/drivers/media/rc/fintek-cir.h new file mode 100644 index 000000000000..1b10b2011f5e --- /dev/null +++ b/drivers/media/rc/fintek-cir.h @@ -0,0 +1,243 @@ +/* + * Driver for Feature Integration Technology Inc. (aka Fintek) LPC CIR + * + * Copyright (C) 2011 Jarod Wilson <jarod@redhat.com> + * + * Special thanks to Fintek for providing hardware and spec sheets. + * This driver is based upon the nuvoton, ite and ene drivers for + * similar hardware. + * + * 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 FINTEK_DRIVER_NAME "fintek-cir" +#define FINTEK_DESCRIPTION "Fintek LPC SuperIO Consumer IR Transceiver" +#define VENDOR_ID_FINTEK 0x1934 + + +/* debugging module parameter */ +static int debug; + +#define fit_pr(level, text, ...) \ + printk(level KBUILD_MODNAME ": " text, ## __VA_ARGS__) + +#define fit_dbg(text, ...) \ + if (debug) \ + printk(KERN_DEBUG \ + KBUILD_MODNAME ": " text "\n" , ## __VA_ARGS__) + +#define fit_dbg_verbose(text, ...) \ + if (debug > 1) \ + printk(KERN_DEBUG \ + KBUILD_MODNAME ": " text "\n" , ## __VA_ARGS__) + +#define fit_dbg_wake(text, ...) \ + if (debug > 2) \ + printk(KERN_DEBUG \ + KBUILD_MODNAME ": " text "\n" , ## __VA_ARGS__) + + +#define TX_BUF_LEN 256 +#define RX_BUF_LEN 32 + +struct fintek_dev { + struct pnp_dev *pdev; + struct rc_dev *rdev; + + spinlock_t fintek_lock; + + /* 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; + } tx; + + /* Config register index/data port pair */ + u8 cr_ip; + u8 cr_dp; + + /* hardware I/O settings */ + unsigned long cir_addr; + int cir_irq; + int cir_port_len; + + /* hardware id */ + u8 chip_major; + u8 chip_minor; + u16 chip_vendor; + + /* hardware features */ + bool hw_learning_capable; + bool hw_tx_capable; + + /* rx settings */ + bool learning_enabled; + bool carrier_detect_enabled; + + enum { + CMD_HEADER = 0, + SUBCMD, + CMD_DATA, + PARSE_IRDATA, + } parser_state; + + u8 cmd, rem; + + /* carrier period = 1 / frequency */ + u32 carrier; +}; + +/* buffer packet constants, largely identical to mceusb.c */ +#define BUF_PULSE_BIT 0x80 +#define BUF_LEN_MASK 0x1f +#define BUF_SAMPLE_MASK 0x7f + +#define BUF_COMMAND_HEADER 0x9f +#define BUF_COMMAND_MASK 0xe0 +#define BUF_COMMAND_NULL 0x00 +#define BUF_HW_CMD_HEADER 0xff +#define BUF_CMD_G_REVISION 0x0b +#define BUF_CMD_S_CARRIER 0x06 +#define BUF_CMD_S_TIMEOUT 0x0c +#define BUF_CMD_SIG_END 0x01 +#define BUF_CMD_S_TXMASK 0x08 +#define BUF_CMD_S_RXSENSOR 0x14 +#define BUF_RSP_PULSE_COUNT 0x15 + +#define CIR_SAMPLE_PERIOD 50 + +/* + * Configuration Register: + * Index Port + * Data Port + */ +#define CR_INDEX_PORT 0x2e +#define CR_DATA_PORT 0x2f + +/* Possible alternate values, depends on how the chip is wired */ +#define CR_INDEX_PORT2 0x4e +#define CR_DATA_PORT2 0x4f + +/* + * GCR_CONFIG_PORT_SEL bit 4 specifies which Index Port value is + * active. 1 = 0x4e, 0 = 0x2e + */ +#define PORT_SEL_PORT_4E_EN 0x10 + +/* Extended Function Mode enable/disable magic values */ +#define CONFIG_REG_ENABLE 0x87 +#define CONFIG_REG_DISABLE 0xaa + +/* Chip IDs found in CR_CHIP_ID_{HI,LO} */ +#define CHIP_ID_HIGH_F71809U 0x04 +#define CHIP_ID_LOW_F71809U 0x08 + +/* + * Global control regs we need to care about: + * Global Control def. + * Register name addr val. */ +#define GCR_SOFTWARE_RESET 0x02 /* 0x00 */ +#define GCR_LOGICAL_DEV_NO 0x07 /* 0x00 */ +#define GCR_CHIP_ID_HI 0x20 /* 0x04 */ +#define GCR_CHIP_ID_LO 0x21 /* 0x08 */ +#define GCR_VENDOR_ID_HI 0x23 /* 0x19 */ +#define GCR_VENDOR_ID_LO 0x24 /* 0x34 */ +#define GCR_CONFIG_PORT_SEL 0x25 /* 0x01 */ +#define GCR_KBMOUSE_WAKEUP 0x27 + +#define LOGICAL_DEV_DISABLE 0x00 +#define LOGICAL_DEV_ENABLE 0x01 + +/* Logical device number of the CIR function */ +#define LOGICAL_DEV_CIR 0x05 + +/* CIR Logical Device (LDN 0x08) config registers */ +#define CIR_CR_COMMAND_INDEX 0x04 +#define CIR_CR_IRCS 0x05 /* Before host writes command to IR, host + must set to 1. When host finshes write + command to IR, host must clear to 0. */ +#define CIR_CR_COMMAND_DATA 0x06 /* Host read or write comand data */ +#define CIR_CR_CLASS 0x07 /* 0xff = rx-only, 0x66 = rx + 2 tx, + 0x33 = rx + 1 tx */ +#define CIR_CR_DEV_EN 0x30 /* bit0 = 1 enables CIR */ +#define CIR_CR_BASE_ADDR_HI 0x60 /* MSB of CIR IO base addr */ +#define CIR_CR_BASE_ADDR_LO 0x61 /* LSB of CIR IO base addr */ +#define CIR_CR_IRQ_SEL 0x70 /* bits3-0 store CIR IRQ */ +#define CIR_CR_PSOUT_STATUS 0xf1 +#define CIR_CR_WAKE_KEY3_ADDR 0xf8 +#define CIR_CR_WAKE_KEY3_CODE 0xf9 +#define CIR_CR_WAKE_KEY3_DC 0xfa +#define CIR_CR_WAKE_CONTROL 0xfb +#define CIR_CR_WAKE_KEY12_ADDR 0xfc +#define CIR_CR_WAKE_KEY4_ADDR 0xfd +#define CIR_CR_WAKE_KEY5_ADDR 0xfe + +#define CLASS_RX_ONLY 0xff +#define CLASS_RX_2TX 0x66 +#define CLASS_RX_1TX 0x33 + +/* CIR device registers */ +#define CIR_STATUS 0x00 +#define CIR_RX_DATA 0x01 +#define CIR_TX_CONTROL 0x02 +#define CIR_TX_DATA 0x03 +#define CIR_CONTROL 0x04 + +/* Bits to enable CIR wake */ +#define LOGICAL_DEV_ACPI 0x01 +#define LDEV_ACPI_WAKE_EN_REG 0xe8 +#define ACPI_WAKE_EN_CIR_BIT 0x04 + +#define LDEV_ACPI_PME_EN_REG 0xf0 +#define LDEV_ACPI_PME_CLR_REG 0xf1 +#define ACPI_PME_CIR_BIT 0x02 + +#define LDEV_ACPI_STATE_REG 0xf4 +#define ACPI_STATE_CIR_BIT 0x20 + +/* + * CIR status register (0x00): + * 7 - CIR_IRQ_EN (1 = enable CIR IRQ, 0 = disable) + * 3 - TX_FINISH (1 when TX finished, write 1 to clear) + * 2 - TX_UNDERRUN (1 on TX underrun, write 1 to clear) + * 1 - RX_TIMEOUT (1 on RX timeout, write 1 to clear) + * 0 - RX_RECEIVE (1 on RX receive, write 1 to clear) + */ +#define CIR_STATUS_IRQ_EN 0x80 +#define CIR_STATUS_TX_FINISH 0x08 +#define CIR_STATUS_TX_UNDERRUN 0x04 +#define CIR_STATUS_RX_TIMEOUT 0x02 +#define CIR_STATUS_RX_RECEIVE 0x01 +#define CIR_STATUS_IRQ_MASK 0x0f + +/* + * CIR TX control register (0x02): + * 7 - TX_START (1 to indicate TX start, auto-cleared when done) + * 6 - TX_END (1 to indicate TX data written to TX fifo) + */ +#define CIR_TX_CONTROL_TX_START 0x80 +#define CIR_TX_CONTROL_TX_END 0x40 + diff --git a/drivers/media/rc/imon.c b/drivers/media/rc/imon.c index 8fc0f081b470..3f3c70716268 100644 --- a/drivers/media/rc/imon.c +++ b/drivers/media/rc/imon.c @@ -443,16 +443,6 @@ static int display_close(struct inode *inode, struct file *file) } else { ictx->display_isopen = false; dev_dbg(ictx->dev, "display port closed\n"); - if (!ictx->dev_present_intf0) { - /* - * Device disconnected before close and IR port is not - * open. If IR port is open, context will be deleted by - * ir_close. - */ - mutex_unlock(&ictx->lock); - free_imon_context(ictx); - return retval; - } } mutex_unlock(&ictx->lock); @@ -1492,7 +1482,6 @@ static void imon_incoming_packet(struct imon_context *ictx, struct device *dev = ictx->dev; unsigned long flags; u32 kc; - bool norelease = false; int i; u64 scancode; int press_type = 0; @@ -1560,7 +1549,6 @@ static void imon_incoming_packet(struct imon_context *ictx, !(buf[1] & 0x1 || buf[1] >> 2 & 0x1))) { len = 8; imon_pad_to_keys(ictx, buf); - norelease = true; } if (debug) { @@ -1982,7 +1970,7 @@ static struct input_dev *imon_init_touch(struct imon_context *ictx) return touch; touch_register_failed: - input_free_device(ictx->touch); + input_free_device(touch); touch_alloc_failed: return NULL; @@ -2274,14 +2262,12 @@ static int __devinit imon_probe(struct usb_interface *interface, struct usb_host_interface *iface_desc = NULL; struct usb_interface *first_if; struct device *dev = &interface->dev; - int ifnum, code_length, sysfs_err; + int ifnum, sysfs_err; int ret = 0; struct imon_context *ictx = NULL; struct imon_context *first_if_ctx = NULL; u16 vendor, product; - code_length = BUF_CHUNK_SIZE * 8; - usbdev = usb_get_dev(interface_to_usbdev(interface)); iface_desc = interface->cur_altsetting; ifnum = iface_desc->desc.bInterfaceNumber; @@ -2366,8 +2352,6 @@ static void __devexit imon_disconnect(struct usb_interface *interface) dev = ictx->dev; ifnum = interface->cur_altsetting->desc.bInterfaceNumber; - mutex_lock(&ictx->lock); - /* * sysfs_remove_group is safe to call even if sysfs_create_group * hasn't been called @@ -2391,24 +2375,20 @@ static void __devexit imon_disconnect(struct usb_interface *interface) if (ictx->display_supported) { if (ictx->display_type == IMON_DISPLAY_TYPE_LCD) usb_deregister_dev(interface, &imon_lcd_class); - else + else if (ictx->display_type == IMON_DISPLAY_TYPE_VFD) usb_deregister_dev(interface, &imon_vfd_class); } } else { ictx->dev_present_intf1 = false; usb_kill_urb(ictx->rx_urb_intf1); - if (ictx->display_type == IMON_DISPLAY_TYPE_VGA) + if (ictx->display_type == IMON_DISPLAY_TYPE_VGA) { input_unregister_device(ictx->touch); + del_timer_sync(&ictx->ttimer); + } } - if (!ictx->dev_present_intf0 && !ictx->dev_present_intf1) { - if (ictx->display_type == IMON_DISPLAY_TYPE_VGA) - del_timer_sync(&ictx->ttimer); - mutex_unlock(&ictx->lock); - if (!ictx->display_isopen) - free_imon_context(ictx); - } else - mutex_unlock(&ictx->lock); + if (!ictx->dev_present_intf0 && !ictx->dev_present_intf1) + free_imon_context(ictx); mutex_unlock(&driver_lock); diff --git a/drivers/media/rc/ite-cir.c b/drivers/media/rc/ite-cir.c index 43908a70bd8b..e716b931cf7e 100644 --- a/drivers/media/rc/ite-cir.c +++ b/drivers/media/rc/ite-cir.c @@ -1250,11 +1250,9 @@ static void it8709_disable(struct ite_dev *dev) ite_dbg("%s called", __func__); /* clear out all interrupt enable flags */ - it8709_wr(dev, - it8709_rr(dev, - IT85_C0IER) & ~(IT85_IEC | IT85_RFOIE | - IT85_RDAIE | - IT85_TLDLIE), IT85_C0IER); + it8709_wr(dev, it8709_rr(dev, IT85_C0IER) & + ~(IT85_IEC | IT85_RFOIE | IT85_RDAIE | IT85_TLDLIE), + IT85_C0IER); /* disable the receiver */ it8709_disable_rx(dev); @@ -1270,11 +1268,9 @@ static void it8709_init_hardware(struct ite_dev *dev) ite_dbg("%s called", __func__); /* disable all the interrupts */ - it8709_wr(dev, - it8709_rr(dev, - IT85_C0IER) & ~(IT85_IEC | IT85_RFOIE | - IT85_RDAIE | - IT85_TLDLIE), IT85_C0IER); + it8709_wr(dev, it8709_rr(dev, IT85_C0IER) & + ~(IT85_IEC | IT85_RFOIE | IT85_RDAIE | IT85_TLDLIE), + IT85_C0IER); /* program the baud rate divisor */ it8709_wr(dev, ITE_BAUDRATE_DIVISOR & 0xff, IT85_C0BDLR); @@ -1282,28 +1278,22 @@ static void it8709_init_hardware(struct ite_dev *dev) IT85_C0BDHR); /* program the C0MSTCR register defaults */ - it8709_wr(dev, (it8709_rr(dev, IT85_C0MSTCR) & ~(IT85_ILSEL | - IT85_ILE - | IT85_FIFOTL - | - IT85_FIFOCLR - | - IT85_RESET)) - | IT85_FIFOTL_DEFAULT, IT85_C0MSTCR); + it8709_wr(dev, (it8709_rr(dev, IT85_C0MSTCR) & + ~(IT85_ILSEL | IT85_ILE | IT85_FIFOTL + | IT85_FIFOCLR | IT85_RESET)) | IT85_FIFOTL_DEFAULT, + IT85_C0MSTCR); /* program the C0RCR register defaults */ - it8709_wr(dev, - (it8709_rr(dev, IT85_C0RCR) & - ~(IT85_RXEN | IT85_RDWOS | IT85_RXEND - | IT85_RXACT | IT85_RXDCR)) | - ITE_RXDCR_DEFAULT, IT85_C0RCR); + it8709_wr(dev, (it8709_rr(dev, IT85_C0RCR) & + ~(IT85_RXEN | IT85_RDWOS | IT85_RXEND | IT85_RXACT + | IT85_RXDCR)) | ITE_RXDCR_DEFAULT, + IT85_C0RCR); /* program the C0TCR register defaults */ - it8709_wr(dev, (it8709_rr(dev, IT85_C0TCR) - &~(IT85_TXMPM | IT85_TXMPW)) - |IT85_TXRLE | IT85_TXENDF | - IT85_TXMPM_DEFAULT | - IT85_TXMPW_DEFAULT, IT85_C0TCR); + it8709_wr(dev, (it8709_rr(dev, IT85_C0TCR) & ~(IT85_TXMPM | IT85_TXMPW)) + | IT85_TXRLE | IT85_TXENDF | IT85_TXMPM_DEFAULT + | IT85_TXMPW_DEFAULT, + IT85_C0TCR); /* program the carrier parameters */ ite_set_carrier_params(dev); @@ -1660,6 +1650,9 @@ static int ite_suspend(struct pnp_dev *pdev, pm_message_t state) ite_dbg("%s called", __func__); + /* wait for any transmission to end */ + wait_event_interruptible(dev->tx_ended, !dev->transmitting); + spin_lock_irqsave(&dev->lock, flags); /* disable all interrupts */ @@ -1680,13 +1673,10 @@ static int ite_resume(struct pnp_dev *pdev) spin_lock_irqsave(&dev->lock, flags); - if (dev->transmitting) { - /* wake up the transmitter */ - wake_up_interruptible(&dev->tx_queue); - } else { - /* enable the receiver */ - dev->params.enable_rx(dev); - } + /* reinitialize hardware config registers */ + dev->params.init_hardware(dev); + /* enable the receiver */ + dev->params.enable_rx(dev); spin_unlock_irqrestore(&dev->lock, flags); diff --git a/drivers/media/rc/keymaps/Makefile b/drivers/media/rc/keymaps/Makefile index 85cac7ddbcec..b57fc83fb4d2 100644 --- a/drivers/media/rc/keymaps/Makefile +++ b/drivers/media/rc/keymaps/Makefile @@ -77,6 +77,7 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \ rc-terratec-slim.o \ rc-terratec-slim-2.o \ rc-tevii-nec.o \ + rc-tivo.o \ rc-total-media-in-hand.o \ rc-trekstor.o \ rc-tt-1500.o \ diff --git a/drivers/media/rc/keymaps/rc-avermedia-cardbus.c b/drivers/media/rc/keymaps/rc-avermedia-cardbus.c index bdf97b74cf90..22f54d413a35 100644 --- a/drivers/media/rc/keymaps/rc-avermedia-cardbus.c +++ b/drivers/media/rc/keymaps/rc-avermedia-cardbus.c @@ -52,7 +52,7 @@ static struct rc_map_table avermedia_cardbus[] = { { 0x28, KEY_SELECT }, /* Select */ { 0x29, KEY_BLUE }, /* Blue/Picture */ { 0x2a, KEY_BACKSPACE }, /* Back */ - { 0x2b, KEY_MEDIA }, /* PIP (Picture-in-picture) */ + { 0x2b, KEY_VIDEO }, /* PIP (Picture-in-picture) */ { 0x2c, KEY_DOWN }, { 0x2e, KEY_DOT }, { 0x2f, KEY_TV }, /* Live TV */ diff --git a/drivers/media/rc/keymaps/rc-imon-mce.c b/drivers/media/rc/keymaps/rc-imon-mce.c index 937a81989f00..0ea2aa190d81 100644 --- a/drivers/media/rc/keymaps/rc-imon-mce.c +++ b/drivers/media/rc/keymaps/rc-imon-mce.c @@ -111,7 +111,7 @@ static struct rc_map_table imon_mce[] = { { 0x800ff44d, KEY_TITLE }, { 0x800ff40c, KEY_POWER }, - { 0x800ff40d, KEY_LEFTMETA }, /* Windows MCE button */ + { 0x800ff40d, KEY_MEDIA }, /* Windows MCE button */ }; diff --git a/drivers/media/rc/keymaps/rc-imon-pad.c b/drivers/media/rc/keymaps/rc-imon-pad.c index 63d42bd24c9e..75d3843fdc30 100644 --- a/drivers/media/rc/keymaps/rc-imon-pad.c +++ b/drivers/media/rc/keymaps/rc-imon-pad.c @@ -87,7 +87,7 @@ static struct rc_map_table imon_pad[] = { { 0x2b8515b7, KEY_VIDEO }, { 0x299195b7, KEY_AUDIO }, - { 0x2ba115b7, KEY_CAMERA }, + { 0x2ba115b7, KEY_IMAGES }, { 0x28a515b7, KEY_TV }, { 0x29a395b7, KEY_DVD }, { 0x29a295b7, KEY_DVD }, @@ -97,7 +97,7 @@ static struct rc_map_table imon_pad[] = { { 0x2ba395b7, KEY_MENU }, { 0x288515b7, KEY_BOOKMARKS }, - { 0x2ab715b7, KEY_MEDIA }, /* Thumbnail */ + { 0x2ab715b7, KEY_CAMERA }, /* Thumbnail */ { 0x298595b7, KEY_SUBTITLE }, { 0x2b8595b7, KEY_LANGUAGE }, @@ -125,7 +125,7 @@ static struct rc_map_table imon_pad[] = { { 0x2b8195b7, KEY_CONTEXT_MENU }, /* Left Menu*/ { 0x02000065, KEY_COMPOSE }, /* RightMenu */ { 0x28b715b7, KEY_COMPOSE }, /* RightMenu */ - { 0x2ab195b7, KEY_LEFTMETA }, /* Go or MultiMon */ + { 0x2ab195b7, KEY_MEDIA }, /* Go or MultiMon */ { 0x29b715b7, KEY_DASHBOARD }, /* AppLauncher */ }; diff --git a/drivers/media/rc/keymaps/rc-kworld-plus-tv-analog.c b/drivers/media/rc/keymaps/rc-kworld-plus-tv-analog.c index 08d183120e41..7fa17a369f2d 100644 --- a/drivers/media/rc/keymaps/rc-kworld-plus-tv-analog.c +++ b/drivers/media/rc/keymaps/rc-kworld-plus-tv-analog.c @@ -17,7 +17,7 @@ */ static struct rc_map_table kworld_plus_tv_analog[] = { - { 0x0c, KEY_LEFTMETA }, /* Kworld key */ + { 0x0c, KEY_MEDIA }, /* Kworld key */ { 0x16, KEY_CLOSECD }, /* -> ) */ { 0x1d, KEY_POWER2 }, diff --git a/drivers/media/rc/keymaps/rc-lme2510.c b/drivers/media/rc/keymaps/rc-lme2510.c index afae14fd152e..129d3f9a461d 100644 --- a/drivers/media/rc/keymaps/rc-lme2510.c +++ b/drivers/media/rc/keymaps/rc-lme2510.c @@ -14,81 +14,81 @@ static struct rc_map_table lme2510_rc[] = { /* Type 1 - 26 buttons */ - { 0xef12ba45, KEY_0 }, - { 0xef12a05f, KEY_1 }, - { 0xef12af50, KEY_2 }, - { 0xef12a25d, KEY_3 }, - { 0xef12be41, KEY_4 }, - { 0xef12f50a, KEY_5 }, - { 0xef12bd42, KEY_6 }, - { 0xef12b847, KEY_7 }, - { 0xef12b649, KEY_8 }, - { 0xef12fa05, KEY_9 }, - { 0xef12bc43, KEY_POWER }, - { 0xef12b946, KEY_SUBTITLE }, - { 0xef12f906, KEY_PAUSE }, - { 0xef12fc03, KEY_MEDIA_REPEAT}, - { 0xef12fd02, KEY_PAUSE }, - { 0xef12a15e, KEY_VOLUMEUP }, - { 0xef12a35c, KEY_VOLUMEDOWN }, - { 0xef12f609, KEY_CHANNELUP }, - { 0xef12e51a, KEY_CHANNELDOWN }, - { 0xef12e11e, KEY_PLAY }, - { 0xef12e41b, KEY_ZOOM }, - { 0xef12a659, KEY_MUTE }, - { 0xef12a55a, KEY_TV }, - { 0xef12e718, KEY_RECORD }, - { 0xef12f807, KEY_EPG }, - { 0xef12fe01, KEY_STOP }, + { 0x10ed45, KEY_0 }, + { 0x10ed5f, KEY_1 }, + { 0x10ed50, KEY_2 }, + { 0x10ed5d, KEY_3 }, + { 0x10ed41, KEY_4 }, + { 0x10ed0a, KEY_5 }, + { 0x10ed42, KEY_6 }, + { 0x10ed47, KEY_7 }, + { 0x10ed49, KEY_8 }, + { 0x10ed05, KEY_9 }, + { 0x10ed43, KEY_POWER }, + { 0x10ed46, KEY_SUBTITLE }, + { 0x10ed06, KEY_PAUSE }, + { 0x10ed03, KEY_MEDIA_REPEAT}, + { 0x10ed02, KEY_PAUSE }, + { 0x10ed5e, KEY_VOLUMEUP }, + { 0x10ed5c, KEY_VOLUMEDOWN }, + { 0x10ed09, KEY_CHANNELUP }, + { 0x10ed1a, KEY_CHANNELDOWN }, + { 0x10ed1e, KEY_PLAY }, + { 0x10ed1b, KEY_ZOOM }, + { 0x10ed59, KEY_MUTE }, + { 0x10ed5a, KEY_TV }, + { 0x10ed18, KEY_RECORD }, + { 0x10ed07, KEY_EPG }, + { 0x10ed01, KEY_STOP }, /* Type 2 - 20 buttons */ - { 0xff40ea15, KEY_0 }, - { 0xff40f708, KEY_1 }, - { 0xff40f609, KEY_2 }, - { 0xff40f50a, KEY_3 }, - { 0xff40f30c, KEY_4 }, - { 0xff40f20d, KEY_5 }, - { 0xff40f10e, KEY_6 }, - { 0xff40ef10, KEY_7 }, - { 0xff40ee11, KEY_8 }, - { 0xff40ed12, KEY_9 }, - { 0xff40ff00, KEY_POWER }, - { 0xff40fb04, KEY_MEDIA_REPEAT}, /* Recall */ - { 0xff40e51a, KEY_PAUSE }, /* Timeshift */ - { 0xff40fd02, KEY_VOLUMEUP }, /* 2 x -/+ Keys not marked */ - { 0xff40f906, KEY_VOLUMEDOWN }, /* Volume defined as right hand*/ - { 0xff40fe01, KEY_CHANNELUP }, - { 0xff40fa05, KEY_CHANNELDOWN }, - { 0xff40eb14, KEY_ZOOM }, - { 0xff40e718, KEY_RECORD }, - { 0xff40e916, KEY_STOP }, + { 0xbf15, KEY_0 }, + { 0xbf08, KEY_1 }, + { 0xbf09, KEY_2 }, + { 0xbf0a, KEY_3 }, + { 0xbf0c, KEY_4 }, + { 0xbf0d, KEY_5 }, + { 0xbf0e, KEY_6 }, + { 0xbf10, KEY_7 }, + { 0xbf11, KEY_8 }, + { 0xbf12, KEY_9 }, + { 0xbf00, KEY_POWER }, + { 0xbf04, KEY_MEDIA_REPEAT}, /* Recall */ + { 0xbf1a, KEY_PAUSE }, /* Timeshift */ + { 0xbf02, KEY_VOLUMEUP }, /* 2 x -/+ Keys not marked */ + { 0xbf06, KEY_VOLUMEDOWN }, /* Volume defined as right hand*/ + { 0xbf01, KEY_CHANNELUP }, + { 0xbf05, KEY_CHANNELDOWN }, + { 0xbf14, KEY_ZOOM }, + { 0xbf18, KEY_RECORD }, + { 0xbf16, KEY_STOP }, /* Type 3 - 20 buttons */ - { 0xff00e31c, KEY_0 }, - { 0xff00f807, KEY_1 }, - { 0xff00ea15, KEY_2 }, - { 0xff00f609, KEY_3 }, - { 0xff00e916, KEY_4 }, - { 0xff00e619, KEY_5 }, - { 0xff00f20d, KEY_6 }, - { 0xff00f30c, KEY_7 }, - { 0xff00e718, KEY_8 }, - { 0xff00a15e, KEY_9 }, - { 0xff00ba45, KEY_POWER }, - { 0xff00bb44, KEY_MEDIA_REPEAT}, /* Recall */ - { 0xff00b54a, KEY_PAUSE }, /* Timeshift */ - { 0xff00b847, KEY_VOLUMEUP }, /* 2 x -/+ Keys not marked */ - { 0xff00bc43, KEY_VOLUMEDOWN }, /* Volume defined as right hand*/ - { 0xff00b946, KEY_CHANNELUP }, - { 0xff00bf40, KEY_CHANNELDOWN }, - { 0xff00f708, KEY_ZOOM }, - { 0xff00bd42, KEY_RECORD }, - { 0xff00a55a, KEY_STOP }, + { 0x1c, KEY_0 }, + { 0x07, KEY_1 }, + { 0x15, KEY_2 }, + { 0x09, KEY_3 }, + { 0x16, KEY_4 }, + { 0x19, KEY_5 }, + { 0x0d, KEY_6 }, + { 0x0c, KEY_7 }, + { 0x18, KEY_8 }, + { 0x5e, KEY_9 }, + { 0x45, KEY_POWER }, + { 0x44, KEY_MEDIA_REPEAT}, /* Recall */ + { 0x4a, KEY_PAUSE }, /* Timeshift */ + { 0x47, KEY_VOLUMEUP }, /* 2 x -/+ Keys not marked */ + { 0x43, KEY_VOLUMEDOWN }, /* Volume defined as right hand*/ + { 0x46, KEY_CHANNELUP }, + { 0x40, KEY_CHANNELDOWN }, + { 0x08, KEY_ZOOM }, + { 0x42, KEY_RECORD }, + { 0x5a, KEY_STOP }, }; static struct rc_map_list lme2510_map = { .map = { .scan = lme2510_rc, .size = ARRAY_SIZE(lme2510_rc), - .rc_type = RC_TYPE_UNKNOWN, + .rc_type = RC_TYPE_NEC, .name = RC_MAP_LME2510, } }; diff --git a/drivers/media/rc/keymaps/rc-rc6-mce.c b/drivers/media/rc/keymaps/rc-rc6-mce.c index 8dd519ecc58e..01b69bcc8666 100644 --- a/drivers/media/rc/keymaps/rc-rc6-mce.c +++ b/drivers/media/rc/keymaps/rc-rc6-mce.c @@ -30,7 +30,7 @@ static struct rc_map_table rc6_mce[] = { { 0x800f040a, KEY_DELETE }, { 0x800f040b, KEY_ENTER }, { 0x800f040c, KEY_POWER }, /* PC Power */ - { 0x800f040d, KEY_LEFTMETA }, /* Windows MCE button */ + { 0x800f040d, KEY_MEDIA }, /* Windows MCE button */ { 0x800f040e, KEY_MUTE }, { 0x800f040f, KEY_INFO }, @@ -87,7 +87,7 @@ static struct rc_map_table rc6_mce[] = { { 0x800f0465, KEY_POWER2 }, /* TV Power */ { 0x800f046e, KEY_PLAYPAUSE }, - { 0x800f046f, KEY_MEDIA }, /* Start media application (NEW) */ + { 0x800f046f, KEY_PLAYER }, /* Start media application (NEW) */ { 0x800f0480, KEY_BRIGHTNESSDOWN }, { 0x800f0481, KEY_PLAYPAUSE }, diff --git a/drivers/media/rc/keymaps/rc-tivo.c b/drivers/media/rc/keymaps/rc-tivo.c new file mode 100644 index 000000000000..98ad085531fd --- /dev/null +++ b/drivers/media/rc/keymaps/rc-tivo.c @@ -0,0 +1,98 @@ +/* rc-tivo.c - Keytable for TiVo remotes + * + * Copyright (c) 2011 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> + +/* + * Initial mapping is for the TiVo remote included in the Nero LiquidTV bundle, + * which also ships with a TiVo-branded IR transceiver, supported by the mceusb + * driver. Note that the remote uses an NEC-ish protocol, but instead of having + * a command/not_command pair, it has a vendor ID of 0xa10c, but some keys, the + * NEC extended checksums do pass, so the table presently has the intended + * values and the checksum-passed versions for those keys. + */ +static struct rc_map_table tivo[] = { + { 0xa10c900f, KEY_MEDIA }, /* TiVo Button */ + { 0xa10c0807, KEY_POWER2 }, /* TV Power */ + { 0xa10c8807, KEY_TV }, /* Live TV/Swap */ + { 0xa10c2c03, KEY_VIDEO_NEXT }, /* TV Input */ + { 0xa10cc807, KEY_INFO }, + { 0xa10cfa05, KEY_CYCLEWINDOWS }, /* Window */ + { 0x0085305f, KEY_CYCLEWINDOWS }, + { 0xa10c6c03, KEY_EPG }, /* Guide */ + + { 0xa10c2807, KEY_UP }, + { 0xa10c6807, KEY_DOWN }, + { 0xa10ce807, KEY_LEFT }, + { 0xa10ca807, KEY_RIGHT }, + + { 0xa10c1807, KEY_SCROLLDOWN }, /* Red Thumbs Down */ + { 0xa10c9807, KEY_SELECT }, + { 0xa10c5807, KEY_SCROLLUP }, /* Green Thumbs Up */ + + { 0xa10c3807, KEY_VOLUMEUP }, + { 0xa10cb807, KEY_VOLUMEDOWN }, + { 0xa10cd807, KEY_MUTE }, + { 0xa10c040b, KEY_RECORD }, + { 0xa10c7807, KEY_CHANNELUP }, + { 0xa10cf807, KEY_CHANNELDOWN }, + { 0x0085301f, KEY_CHANNELDOWN }, + + { 0xa10c840b, KEY_PLAY }, + { 0xa10cc40b, KEY_PAUSE }, + { 0xa10ca40b, KEY_SLOW }, + { 0xa10c440b, KEY_REWIND }, + { 0xa10c240b, KEY_FASTFORWARD }, + { 0xa10c640b, KEY_PREVIOUS }, + { 0xa10ce40b, KEY_NEXT }, /* ->| */ + + { 0xa10c220d, KEY_ZOOM }, /* Aspect */ + { 0xa10c120d, KEY_STOP }, + { 0xa10c520d, KEY_DVD }, /* DVD Menu */ + + { 0xa10c140b, KEY_NUMERIC_1 }, + { 0xa10c940b, KEY_NUMERIC_2 }, + { 0xa10c540b, KEY_NUMERIC_3 }, + { 0xa10cd40b, KEY_NUMERIC_4 }, + { 0xa10c340b, KEY_NUMERIC_5 }, + { 0xa10cb40b, KEY_NUMERIC_6 }, + { 0xa10c740b, KEY_NUMERIC_7 }, + { 0xa10cf40b, KEY_NUMERIC_8 }, + { 0x0085302f, KEY_NUMERIC_8 }, + { 0xa10c0c03, KEY_NUMERIC_9 }, + { 0xa10c8c03, KEY_NUMERIC_0 }, + { 0xa10ccc03, KEY_ENTER }, + { 0xa10c4c03, KEY_CLEAR }, +}; + +static struct rc_map_list tivo_map = { + .map = { + .scan = tivo, + .size = ARRAY_SIZE(tivo), + .rc_type = RC_TYPE_NEC, + .name = RC_MAP_TIVO, + } +}; + +static int __init init_rc_map_tivo(void) +{ + return rc_map_register(&tivo_map); +} + +static void __exit exit_rc_map_tivo(void) +{ + rc_map_unregister(&tivo_map); +} + +module_init(init_rc_map_tivo) +module_exit(exit_rc_map_tivo) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jarod Wilson <jarod@redhat.com>"); diff --git a/drivers/media/rc/keymaps/rc-winfast.c b/drivers/media/rc/keymaps/rc-winfast.c index 0062ca291959..d8a34c14676a 100644 --- a/drivers/media/rc/keymaps/rc-winfast.c +++ b/drivers/media/rc/keymaps/rc-winfast.c @@ -32,8 +32,8 @@ static struct rc_map_table winfast[] = { { 0x02, KEY_TUNER }, /* TV/FM, not on Y0400052 */ { 0x1e, KEY_VIDEO }, /* Video Source */ { 0x16, KEY_INFO }, /* Display information */ - { 0x04, KEY_LEFT }, - { 0x08, KEY_RIGHT }, + { 0x04, KEY_RIGHT }, + { 0x08, KEY_LEFT }, { 0x0c, KEY_UP }, { 0x10, KEY_DOWN }, { 0x03, KEY_ZOOM }, /* fullscreen */ diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c index 0c273ec465c9..ad927fcaa020 100644 --- a/drivers/media/rc/mceusb.c +++ b/drivers/media/rc/mceusb.c @@ -149,6 +149,8 @@ enum mceusb_model_type { POLARIS_EVK, CX_HYBRID_TV, MULTIFUNCTION, + TIVO_KIT, + MCE_GEN2_NO_TX, }; struct mceusb_model { @@ -172,6 +174,10 @@ static const struct mceusb_model mceusb_model[] = { [MCE_GEN2] = { .mce_gen2 = 1, }, + [MCE_GEN2_NO_TX] = { + .mce_gen2 = 1, + .no_tx = 1, + }, [MCE_GEN2_TX_INV] = { .mce_gen2 = 1, .tx_mask_normal = 1, @@ -197,6 +203,10 @@ static const struct mceusb_model mceusb_model[] = { .mce_gen2 = 1, .ir_intfnum = 2, }, + [TIVO_KIT] = { + .mce_gen2 = 1, + .rc_map = RC_MAP_TIVO, + }, }; static struct usb_device_id mceusb_dev_table[] = { @@ -279,7 +289,8 @@ static struct usb_device_id mceusb_dev_table[] = { /* Formosa21 / eHome Infrared Receiver */ { USB_DEVICE(VENDOR_FORMOSA, 0xe016) }, /* Formosa aim / Trust MCE Infrared Receiver */ - { USB_DEVICE(VENDOR_FORMOSA, 0xe017) }, + { USB_DEVICE(VENDOR_FORMOSA, 0xe017), + .driver_info = MCE_GEN2_NO_TX }, /* Formosa Industrial Computing / Beanbag Emulation Device */ { USB_DEVICE(VENDOR_FORMOSA, 0xe018) }, /* Formosa21 / eHome Infrared Receiver */ @@ -308,7 +319,8 @@ static struct usb_device_id mceusb_dev_table[] = { /* Northstar Systems, Inc. eHome Infrared Transceiver */ { USB_DEVICE(VENDOR_NORTHSTAR, 0xe004) }, /* TiVo PC IR Receiver */ - { USB_DEVICE(VENDOR_TIVO, 0x2000) }, + { USB_DEVICE(VENDOR_TIVO, 0x2000), + .driver_info = TIVO_KIT }, /* Conexant Hybrid TV "Shelby" Polaris SDK */ { USB_DEVICE(VENDOR_CONEXANT, 0x58a1), .driver_info = POLARIS_EVK }, @@ -603,11 +615,10 @@ static void mce_async_callback(struct urb *urb, struct pt_regs *regs) } /* request incoming or send outgoing usb packet - used to initialize remote */ -static void mce_request_packet(struct mceusb_dev *ir, - struct usb_endpoint_descriptor *ep, - unsigned char *data, int size, int urb_type) +static void mce_request_packet(struct mceusb_dev *ir, unsigned char *data, + int size, int urb_type) { - int res; + int res, pipe; struct urb *async_urb; struct device *dev = ir->dev; unsigned char *async_buf; @@ -627,10 +638,11 @@ static void mce_request_packet(struct mceusb_dev *ir, } /* outbound data */ - usb_fill_int_urb(async_urb, ir->usbdev, - usb_sndintpipe(ir->usbdev, ep->bEndpointAddress), + pipe = usb_sndintpipe(ir->usbdev, + ir->usb_ep_out->bEndpointAddress); + usb_fill_int_urb(async_urb, ir->usbdev, pipe, async_buf, size, (usb_complete_t)mce_async_callback, - ir, ep->bInterval); + ir, ir->usb_ep_out->bInterval); memcpy(async_buf, data, size); } else if (urb_type == MCEUSB_RX) { @@ -658,12 +670,12 @@ static void mce_request_packet(struct mceusb_dev *ir, static void mce_async_out(struct mceusb_dev *ir, unsigned char *data, int size) { - mce_request_packet(ir, ir->usb_ep_out, data, size, MCEUSB_TX); + mce_request_packet(ir, data, size, MCEUSB_TX); } static void mce_sync_in(struct mceusb_dev *ir, unsigned char *data, int size) { - mce_request_packet(ir, ir->usb_ep_in, data, size, MCEUSB_RX); + mce_request_packet(ir, data, size, MCEUSB_RX); } /* Send data out the IR blaster port(s) */ diff --git a/drivers/media/rc/nuvoton-cir.c b/drivers/media/rc/nuvoton-cir.c index d4d64492a057..bf3060ea6107 100644 --- a/drivers/media/rc/nuvoton-cir.c +++ b/drivers/media/rc/nuvoton-cir.c @@ -37,8 +37,6 @@ #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) { @@ -233,6 +231,8 @@ static int nvt_hw_detect(struct nvt_dev *nvt) unsigned long flags; u8 chip_major, chip_minor; int ret = 0; + char chip_id[12]; + bool chip_unknown = false; nvt_efm_enable(nvt); @@ -246,15 +246,39 @@ static int nvt_hw_detect(struct nvt_dev *nvt) } 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)) { - nvt_pr(KERN_ERR, "%s: unsupported chip, id: 0x%02x 0x%02x", - chip_id, chip_major, chip_minor); - ret = -ENODEV; + /* these are the known working chip revisions... */ + switch (chip_major) { + case CHIP_ID_HIGH_667: + strcpy(chip_id, "w83667hg\0"); + if (chip_minor != CHIP_ID_LOW_667) + chip_unknown = true; + break; + case CHIP_ID_HIGH_677B: + strcpy(chip_id, "w83677hg\0"); + if (chip_minor != CHIP_ID_LOW_677B2 && + chip_minor != CHIP_ID_LOW_677B3) + chip_unknown = true; + break; + case CHIP_ID_HIGH_677C: + strcpy(chip_id, "w83677hg-c\0"); + if (chip_minor != CHIP_ID_LOW_677C) + chip_unknown = true; + break; + default: + strcpy(chip_id, "w836x7hg\0"); + chip_unknown = true; + break; } + /* warn, but still let the driver load, if we don't know this chip */ + if (chip_unknown) + nvt_pr(KERN_WARNING, "%s: unknown chip, id: 0x%02x 0x%02x, " + "it may not work...", chip_id, chip_major, chip_minor); + else + nvt_dbg("%s: chip id: 0x%02x 0x%02x", + chip_id, chip_major, chip_minor); + nvt_efm_disable(nvt); spin_lock_irqsave(&nvt->nvt_lock, flags); @@ -267,13 +291,23 @@ static int nvt_hw_detect(struct nvt_dev *nvt) static void nvt_cir_ldev_init(struct nvt_dev *nvt) { - u8 val; + u8 val, psreg, psmask, psval; + + if (nvt->chip_major == CHIP_ID_HIGH_667) { + psreg = CR_MULTIFUNC_PIN_SEL; + psmask = MULTIFUNC_PIN_SEL_MASK; + psval = MULTIFUNC_ENABLE_CIR | MULTIFUNC_ENABLE_CIRWB; + } else { + psreg = CR_OUTPUT_PIN_SEL; + psmask = OUTPUT_PIN_SEL_MASK; + psval = OUTPUT_ENABLE_CIR | OUTPUT_ENABLE_CIRWB; + } - /* 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); + /* output pin selection: enable CIR, with WB sensor enabled */ + val = nvt_cr_read(nvt, psreg); + val &= psmask; + val |= psval; + nvt_cr_write(nvt, val, psreg); /* Select CIR logical device and enable */ nvt_select_logical_dev(nvt, LOGICAL_DEV_CIR); @@ -640,7 +674,7 @@ static void nvt_process_rx_ir_data(struct nvt_dev *nvt) rawir.pulse ? "pulse" : "space", rawir.duration); - ir_raw_event_store(nvt->rdev, &rawir); + ir_raw_event_store_with_filter(nvt->rdev, &rawir); } /* @@ -1070,18 +1104,20 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id) rdev->tx_ir = nvt_tx_ir; rdev->s_tx_carrier = nvt_set_tx_carrier; rdev->input_name = "Nuvoton w836x7hg Infrared Remote Transceiver"; + rdev->input_phys = "nuvoton/cir0"; rdev->input_id.bustype = BUS_HOST; rdev->input_id.vendor = PCI_VENDOR_ID_WINBOND2; rdev->input_id.product = nvt->chip_major; rdev->input_id.version = nvt->chip_minor; + rdev->dev.parent = &pdev->dev; rdev->driver_name = NVT_DRIVER_NAME; rdev->map_name = RC_MAP_RC6_MCE; + rdev->timeout = US_TO_NS(1000); + /* rx resolution is hardwired to 50us atm, 1, 25, 100 also possible */ + rdev->rx_resolution = US_TO_NS(CIR_SAMPLE_PERIOD); #if 0 rdev->min_timeout = XYZ; rdev->max_timeout = XYZ; - rdev->timeout = XYZ; - /* rx resolution is hardwired to 50us atm, 1, 25, 100 also possible */ - rdev->rx_resolution = XYZ; /* tx bits */ rdev->tx_resolution = XYZ; #endif @@ -1090,8 +1126,7 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id) if (ret) goto failure; - device_set_wakeup_capable(&pdev->dev, 1); - device_set_wakeup_enable(&pdev->dev, 1); + device_init_wakeup(&pdev->dev, true); nvt->rdev = rdev; nvt_pr(KERN_NOTICE, "driver has been successfully loaded\n"); if (debug) { diff --git a/drivers/media/rc/nuvoton-cir.h b/drivers/media/rc/nuvoton-cir.h index 048135eea702..379795d61ea7 100644 --- a/drivers/media/rc/nuvoton-cir.h +++ b/drivers/media/rc/nuvoton-cir.h @@ -330,9 +330,13 @@ struct nvt_dev { #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 +#define CHIP_ID_HIGH_667 0xa5 +#define CHIP_ID_HIGH_677B 0xb4 +#define CHIP_ID_HIGH_677C 0xc3 +#define CHIP_ID_LOW_667 0x13 +#define CHIP_ID_LOW_677B2 0x72 +#define CHIP_ID_LOW_677B3 0x73 +#define CHIP_ID_LOW_677C 0x33 /* Config regs we need to care about */ #define CR_SOFTWARE_RESET 0x02 @@ -341,6 +345,7 @@ struct nvt_dev { #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_MULTIFUNC_PIN_SEL 0x2c #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 @@ -364,10 +369,16 @@ struct nvt_dev { #define CIR_INTR_MOUSE_IRQ_BIT 0x80 #define PME_INTR_CIR_PASS_BIT 0x08 +/* w83677hg CIR pin config */ #define OUTPUT_PIN_SEL_MASK 0xbc #define OUTPUT_ENABLE_CIR 0x01 /* Pin95=CIRRX, Pin96=CIRTX1 */ #define OUTPUT_ENABLE_CIRWB 0x40 /* enable wide-band sensor */ +/* w83667hg CIR pin config */ +#define MULTIFUNC_PIN_SEL_MASK 0x1f +#define MULTIFUNC_ENABLE_CIR 0x80 /* Pin75=CIRRX, Pin76=CIRTX1 */ +#define MULTIFUNC_ENABLE_CIRWB 0x20 /* enable wide-band sensor */ + /* MCE CIR signal length, related on sample period */ /* MCE CIR controller signal length: about 43ms diff --git a/drivers/media/rc/rc-loopback.c b/drivers/media/rc/rc-loopback.c index 49cee61d79c6..cc846b2619cf 100644 --- a/drivers/media/rc/rc-loopback.c +++ b/drivers/media/rc/rc-loopback.c @@ -146,6 +146,12 @@ static int loop_tx_ir(struct rc_dev *dev, int *txbuf, u32 n) if (rawir.duration) ir_raw_event_store_with_filter(dev, &rawir); } + + /* Fake a silence long enough to cause us to go idle */ + rawir.pulse = false; + rawir.duration = dev->timeout; + ir_raw_event_store_with_filter(dev, &rawir); + ir_raw_event_handle(dev); out: diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index a2706648e365..f57cd5677ac2 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -749,6 +749,9 @@ static struct { * it is trigged by reading /sys/class/rc/rc?/protocols. * It returns the protocol names of supported protocols. * Enabled protocols are printed in brackets. + * + * dev->lock is taken to guard against races between device + * registration, store_protocols and show_protocols. */ static ssize_t show_protocols(struct device *device, struct device_attribute *mattr, char *buf) @@ -762,6 +765,8 @@ static ssize_t show_protocols(struct device *device, if (!dev) return -EINVAL; + mutex_lock(&dev->lock); + if (dev->driver_type == RC_DRIVER_SCANCODE) { enabled = dev->rc_map.rc_type; allowed = dev->allowed_protos; @@ -784,6 +789,9 @@ static ssize_t show_protocols(struct device *device, if (tmp != buf) tmp--; *tmp = '\n'; + + mutex_unlock(&dev->lock); + return tmp + 1 - buf; } @@ -802,6 +810,9 @@ static ssize_t show_protocols(struct device *device, * Writing "none" will disable all protocols. * Returns -EINVAL if an invalid protocol combination or unknown protocol name * is used, otherwise @len. + * + * dev->lock is taken to guard against races between device + * registration, store_protocols and show_protocols. */ static ssize_t store_protocols(struct device *device, struct device_attribute *mattr, @@ -815,18 +826,22 @@ static ssize_t store_protocols(struct device *device, u64 mask; int rc, i, count = 0; unsigned long flags; + ssize_t ret; /* Device is being removed */ if (!dev) return -EINVAL; + mutex_lock(&dev->lock); + if (dev->driver_type == RC_DRIVER_SCANCODE) type = dev->rc_map.rc_type; else if (dev->raw) type = dev->raw->enabled_protocols; else { IR_dprintk(1, "Protocol switching not supported\n"); - return -EINVAL; + ret = -EINVAL; + goto out; } while ((tmp = strsep((char **) &data, " \n")) != NULL) { @@ -860,7 +875,8 @@ static ssize_t store_protocols(struct device *device, } if (i == ARRAY_SIZE(proto_names)) { IR_dprintk(1, "Unknown protocol: '%s'\n", tmp); - return -EINVAL; + ret = -EINVAL; + goto out; } count++; } @@ -875,7 +891,8 @@ static ssize_t store_protocols(struct device *device, if (!count) { IR_dprintk(1, "Protocol not specified\n"); - return -EINVAL; + ret = -EINVAL; + goto out; } if (dev->change_protocol) { @@ -883,7 +900,8 @@ static ssize_t store_protocols(struct device *device, if (rc < 0) { IR_dprintk(1, "Error setting protocols to 0x%llx\n", (long long)type); - return -EINVAL; + ret = -EINVAL; + goto out; } } @@ -898,7 +916,11 @@ static ssize_t store_protocols(struct device *device, IR_dprintk(1, "Current protocol(s): 0x%llx\n", (long long)type); - return len; + ret = len; + +out: + mutex_unlock(&dev->lock); + return ret; } static void rc_dev_release(struct device *device) @@ -974,6 +996,7 @@ struct rc_dev *rc_allocate_device(void) spin_lock_init(&dev->rc_map.lock); spin_lock_init(&dev->keylock); + mutex_init(&dev->lock); setup_timer(&dev->timer_keyup, ir_timer_keyup, (unsigned long)dev); dev->dev.type = &rc_dev_type; @@ -1019,12 +1042,21 @@ int rc_register_device(struct rc_dev *dev) if (dev->close) dev->input_dev->close = ir_close; + /* + * Take the lock here, as the device sysfs node will appear + * when device_add() is called, which may trigger an ir-keytable udev + * rule, which will in turn call show_protocols and access either + * dev->rc_map.rc_type or dev->raw->enabled_protocols before it has + * been initialized. + */ + mutex_lock(&dev->lock); + dev->devno = (unsigned long)(atomic_inc_return(&devno) - 1); dev_set_name(&dev->dev, "rc%ld", dev->devno); dev_set_drvdata(&dev->dev, dev); rc = device_add(&dev->dev); if (rc) - return rc; + goto out_unlock; rc = ir_setkeytable(dev, rc_map); if (rc) @@ -1046,6 +1078,13 @@ int rc_register_device(struct rc_dev *dev) */ dev->input_dev->rep[REP_DELAY] = 500; + /* + * As a repeat event on protocols like RC-5 and NEC take as long as + * 110/114ms, using 33ms as a repeat period is not the right thing + * to do. + */ + dev->input_dev->rep[REP_PERIOD] = 125; + path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL); printk(KERN_INFO "%s: %s as %s\n", dev_name(&dev->dev), @@ -1058,6 +1097,7 @@ int rc_register_device(struct rc_dev *dev) if (rc < 0) goto out_input; } + mutex_unlock(&dev->lock); if (dev->change_protocol) { rc = dev->change_protocol(dev, rc_map->rc_type); @@ -1083,6 +1123,8 @@ out_table: ir_free_table(&dev->rc_map); out_dev: device_del(&dev->dev); +out_unlock: + mutex_unlock(&dev->lock); return rc; } EXPORT_SYMBOL_GPL(rc_register_device); diff --git a/drivers/media/rc/redrat3.c b/drivers/media/rc/redrat3.c new file mode 100644 index 000000000000..5147767ccb78 --- /dev/null +++ b/drivers/media/rc/redrat3.c @@ -0,0 +1,1344 @@ +/* + * USB RedRat3 IR Transceiver rc-core driver + * + * Copyright (c) 2011 by Jarod Wilson <jarod@redhat.com> + * based heavily on the work of Stephen Cox, with additional + * help from RedRat Ltd. + * + * This driver began life based an an old version of the first-generation + * lirc_mceusb driver from the lirc 0.7.2 distribution. It was then + * significantly rewritten by Stephen Cox with the aid of RedRat Ltd's + * Chris Dodge. + * + * The driver was then ported to rc-core and significantly rewritten again, + * by Jarod, using the in-kernel mceusb driver as a guide, after an initial + * port effort was started by Stephen. + * + * TODO LIST: + * - fix lirc not showing repeats properly + * -- + * + * The RedRat3 is a USB transceiver with both send & receive, + * with 2 separate sensors available for receive to enable + * both good long range reception for general use, and good + * short range reception when required for learning a signal. + * + * http://www.redrat.co.uk/ + * + * It uses its own little protocol to communicate, the required + * parts of which are embedded within this driver. + * -- + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <linux/device.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/usb.h> +#include <linux/usb/input.h> +#include <media/rc-core.h> + +/* Driver Information */ +#define DRIVER_VERSION "0.70" +#define DRIVER_AUTHOR "Jarod Wilson <jarod@redhat.com>" +#define DRIVER_AUTHOR2 "The Dweller, Stephen Cox" +#define DRIVER_DESC "RedRat3 USB IR Transceiver Driver" +#define DRIVER_NAME "redrat3" + +/* module parameters */ +#ifdef CONFIG_USB_DEBUG +static int debug = 1; +#else +static int debug; +#endif + +#define RR3_DEBUG_STANDARD 0x1 +#define RR3_DEBUG_FUNCTION_TRACE 0x2 + +#define rr3_dbg(dev, fmt, ...) \ + do { \ + if (debug & RR3_DEBUG_STANDARD) \ + dev_info(dev, fmt, ## __VA_ARGS__); \ + } while (0) + +#define rr3_ftr(dev, fmt, ...) \ + do { \ + if (debug & RR3_DEBUG_FUNCTION_TRACE) \ + dev_info(dev, fmt, ## __VA_ARGS__); \ + } while (0) + +/* bulk data transfer types */ +#define RR3_ERROR 0x01 +#define RR3_MOD_SIGNAL_IN 0x20 +#define RR3_MOD_SIGNAL_OUT 0x21 + +/* Get the RR firmware version */ +#define RR3_FW_VERSION 0xb1 +#define RR3_FW_VERSION_LEN 64 +/* Send encoded signal bulk-sent earlier*/ +#define RR3_TX_SEND_SIGNAL 0xb3 +#define RR3_SET_IR_PARAM 0xb7 +#define RR3_GET_IR_PARAM 0xb8 +/* Blink the red LED on the device */ +#define RR3_BLINK_LED 0xb9 +/* Read serial number of device */ +#define RR3_READ_SER_NO 0xba +#define RR3_SER_NO_LEN 4 +/* Start capture with the RC receiver */ +#define RR3_RC_DET_ENABLE 0xbb +/* Stop capture with the RC receiver */ +#define RR3_RC_DET_DISABLE 0xbc +/* Return the status of RC detector capture */ +#define RR3_RC_DET_STATUS 0xbd +/* Reset redrat */ +#define RR3_RESET 0xa0 + +/* Max number of lengths in the signal. */ +#define RR3_IR_IO_MAX_LENGTHS 0x01 +/* Periods to measure mod. freq. */ +#define RR3_IR_IO_PERIODS_MF 0x02 +/* Size of memory for main signal data */ +#define RR3_IR_IO_SIG_MEM_SIZE 0x03 +/* Delta value when measuring lengths */ +#define RR3_IR_IO_LENGTH_FUZZ 0x04 +/* Timeout for end of signal detection */ +#define RR3_IR_IO_SIG_TIMEOUT 0x05 +/* Minumum value for pause recognition. */ +#define RR3_IR_IO_MIN_PAUSE 0x06 + +/* Clock freq. of EZ-USB chip */ +#define RR3_CLK 24000000 +/* Clock periods per timer count */ +#define RR3_CLK_PER_COUNT 12 +/* (RR3_CLK / RR3_CLK_PER_COUNT) */ +#define RR3_CLK_CONV_FACTOR 2000000 +/* USB bulk-in IR data endpoint address */ +#define RR3_BULK_IN_EP_ADDR 0x82 + +/* Raw Modulated signal data value offsets */ +#define RR3_PAUSE_OFFSET 0 +#define RR3_FREQ_COUNT_OFFSET 4 +#define RR3_NUM_PERIOD_OFFSET 6 +#define RR3_MAX_LENGTHS_OFFSET 8 +#define RR3_NUM_LENGTHS_OFFSET 9 +#define RR3_MAX_SIGS_OFFSET 10 +#define RR3_NUM_SIGS_OFFSET 12 +#define RR3_REPEATS_OFFSET 14 + +/* Size of the fixed-length portion of the signal */ +#define RR3_HEADER_LENGTH 15 +#define RR3_DRIVER_MAXLENS 128 +#define RR3_MAX_SIG_SIZE 512 +#define RR3_MAX_BUF_SIZE \ + ((2 * RR3_HEADER_LENGTH) + RR3_DRIVER_MAXLENS + RR3_MAX_SIG_SIZE) +#define RR3_TIME_UNIT 50 +#define RR3_END_OF_SIGNAL 0x7f +#define RR3_TX_HEADER_OFFSET 4 +#define RR3_TX_TRAILER_LEN 2 +#define RR3_RX_MIN_TIMEOUT 5 +#define RR3_RX_MAX_TIMEOUT 2000 + +/* The 8051's CPUCS Register address */ +#define RR3_CPUCS_REG_ADDR 0x7f92 + +#define USB_RR3USB_VENDOR_ID 0x112a +#define USB_RR3USB_PRODUCT_ID 0x0001 +#define USB_RR3IIUSB_PRODUCT_ID 0x0005 + +/* table of devices that work with this driver */ +static struct usb_device_id redrat3_dev_table[] = { + /* Original version of the RedRat3 */ + {USB_DEVICE(USB_RR3USB_VENDOR_ID, USB_RR3USB_PRODUCT_ID)}, + /* Second Version/release of the RedRat3 - RetRat3-II */ + {USB_DEVICE(USB_RR3USB_VENDOR_ID, USB_RR3IIUSB_PRODUCT_ID)}, + {} /* Terminating entry */ +}; + +/* Structure to hold all of our device specific stuff */ +struct redrat3_dev { + /* core device bits */ + struct rc_dev *rc; + struct device *dev; + + /* save off the usb device pointer */ + struct usb_device *udev; + + /* the receive endpoint */ + struct usb_endpoint_descriptor *ep_in; + /* the buffer to receive data */ + unsigned char *bulk_in_buf; + /* urb used to read ir data */ + struct urb *read_urb; + + /* the send endpoint */ + struct usb_endpoint_descriptor *ep_out; + /* the buffer to send data */ + unsigned char *bulk_out_buf; + /* the urb used to send data */ + struct urb *write_urb; + + /* usb dma */ + dma_addr_t dma_in; + dma_addr_t dma_out; + + /* true if write urb is busy */ + bool write_busy; + /* wait for the write to finish */ + struct completion write_finished; + + /* locks this structure */ + struct mutex lock; + + /* rx signal timeout timer */ + struct timer_list rx_timeout; + + /* Is the device currently receiving? */ + bool recv_in_progress; + /* is the detector enabled*/ + bool det_enabled; + /* Is the device currently transmitting?*/ + bool transmitting; + + /* store for current packet */ + char pbuf[RR3_MAX_BUF_SIZE]; + u16 pktlen; + u16 pkttype; + u16 bytes_read; + /* indicate whether we are going to reprocess + * the USB callback with a bigger buffer */ + int buftoosmall; + char *datap; + + u32 carrier; + + char name[128]; + char phys[64]; +}; + +/* All incoming data buffers adhere to a very specific data format */ +struct redrat3_signal_header { + u16 length; /* Length of data being transferred */ + u16 transfer_type; /* Type of data transferred */ + u32 pause; /* Pause between main and repeat signals */ + u16 mod_freq_count; /* Value of timer on mod. freq. measurement */ + u16 no_periods; /* No. of periods over which mod. freq. is measured */ + u8 max_lengths; /* Max no. of lengths (i.e. size of array) */ + u8 no_lengths; /* Actual no. of elements in lengths array */ + u16 max_sig_size; /* Max no. of values in signal data array */ + u16 sig_size; /* Acuto no. of values in signal data array */ + u8 no_repeats; /* No. of repeats of repeat signal section */ + /* Here forward is the lengths and signal data */ +}; + +static void redrat3_dump_signal_header(struct redrat3_signal_header *header) +{ + pr_info("%s:\n", __func__); + pr_info(" * length: %u, transfer_type: 0x%02x\n", + header->length, header->transfer_type); + pr_info(" * pause: %u, freq_count: %u, no_periods: %u\n", + header->pause, header->mod_freq_count, header->no_periods); + pr_info(" * lengths: %u (max: %u)\n", + header->no_lengths, header->max_lengths); + pr_info(" * sig_size: %u (max: %u)\n", + header->sig_size, header->max_sig_size); + pr_info(" * repeats: %u\n", header->no_repeats); +} + +static void redrat3_dump_signal_data(char *buffer, u16 len) +{ + int offset, i; + char *data_vals; + + pr_info("%s:", __func__); + + offset = RR3_TX_HEADER_OFFSET + RR3_HEADER_LENGTH + + (RR3_DRIVER_MAXLENS * sizeof(u16)); + + /* read RR3_DRIVER_MAXLENS from ctrl msg */ + data_vals = buffer + offset; + + for (i = 0; i < len; i++) { + if (i % 10 == 0) + pr_cont("\n * "); + pr_cont("%02x ", *data_vals++); + } + + pr_cont("\n"); +} + +/* + * redrat3_issue_async + * + * Issues an async read to the ir data in port.. + * sets the callback to be redrat3_handle_async + */ +static void redrat3_issue_async(struct redrat3_dev *rr3) +{ + int res; + + rr3_ftr(rr3->dev, "Entering %s\n", __func__); + + if (!rr3->det_enabled) { + dev_warn(rr3->dev, "not issuing async read, " + "detector not enabled\n"); + return; + } + + memset(rr3->bulk_in_buf, 0, rr3->ep_in->wMaxPacketSize); + res = usb_submit_urb(rr3->read_urb, GFP_ATOMIC); + if (res) + rr3_dbg(rr3->dev, "%s: receive request FAILED! " + "(res %d, len %d)\n", __func__, res, + rr3->read_urb->transfer_buffer_length); +} + +static void redrat3_dump_fw_error(struct redrat3_dev *rr3, int code) +{ + if (!rr3->transmitting && (code != 0x40)) + dev_info(rr3->dev, "fw error code 0x%02x: ", code); + + switch (code) { + case 0x00: + pr_cont("No Error\n"); + break; + + /* Codes 0x20 through 0x2f are IR Firmware Errors */ + case 0x20: + pr_cont("Initial signal pulse not long enough " + "to measure carrier frequency\n"); + break; + case 0x21: + pr_cont("Not enough length values allocated for signal\n"); + break; + case 0x22: + pr_cont("Not enough memory allocated for signal data\n"); + break; + case 0x23: + pr_cont("Too many signal repeats\n"); + break; + case 0x28: + pr_cont("Insufficient memory available for IR signal " + "data memory allocation\n"); + break; + case 0x29: + pr_cont("Insufficient memory available " + "for IrDa signal data memory allocation\n"); + break; + + /* Codes 0x30 through 0x3f are USB Firmware Errors */ + case 0x30: + pr_cont("Insufficient memory available for bulk " + "transfer structure\n"); + break; + + /* + * Other error codes... These are primarily errors that can occur in + * the control messages sent to the redrat + */ + case 0x40: + if (!rr3->transmitting) + pr_cont("Signal capture has been terminated\n"); + break; + case 0x41: + pr_cont("Attempt to set/get and unknown signal I/O " + "algorithm parameter\n"); + break; + case 0x42: + pr_cont("Signal capture already started\n"); + break; + + default: + pr_cont("Unknown Error\n"); + break; + } +} + +static u32 redrat3_val_to_mod_freq(struct redrat3_signal_header *ph) +{ + u32 mod_freq = 0; + + if (ph->mod_freq_count != 0) + mod_freq = (RR3_CLK * ph->no_periods) / + (ph->mod_freq_count * RR3_CLK_PER_COUNT); + + return mod_freq; +} + +/* this function scales down the figures for the same result... */ +static u32 redrat3_len_to_us(u32 length) +{ + u32 biglen = length * 1000; + u32 divisor = (RR3_CLK_CONV_FACTOR) / 1000; + u32 result = (u32) (biglen / divisor); + + /* don't allow zero lengths to go back, breaks lirc */ + return result ? result : 1; +} + +/* + * convert us back into redrat3 lengths + * + * length * 1000 length * 1000000 + * ------------- = ---------------- = micro + * rr3clk / 1000 rr3clk + + * 6 * 2 4 * 3 micro * rr3clk micro * rr3clk / 1000 + * ----- = 4 ----- = 6 -------------- = len --------------------- + * 3 2 1000000 1000 + */ +static u32 redrat3_us_to_len(u32 microsec) +{ + u32 result; + u32 divisor; + + microsec &= IR_MAX_DURATION; + divisor = (RR3_CLK_CONV_FACTOR / 1000); + result = (u32)(microsec * divisor) / 1000; + + /* don't allow zero lengths to go back, breaks lirc */ + return result ? result : 1; + +} + +/* timer callback to send long trailing space on receive timeout */ +static void redrat3_rx_timeout(unsigned long data) +{ + struct redrat3_dev *rr3 = (struct redrat3_dev *)data; + DEFINE_IR_RAW_EVENT(rawir); + + rawir.pulse = false; + rawir.duration = rr3->rc->timeout; + rr3_dbg(rr3->dev, "storing trailing space with duration %d\n", + rawir.duration); + ir_raw_event_store_with_filter(rr3->rc, &rawir); + + rr3_dbg(rr3->dev, "calling ir_raw_event_handle\n"); + ir_raw_event_handle(rr3->rc); + + rr3_dbg(rr3->dev, "calling ir_raw_event_reset\n"); + ir_raw_event_reset(rr3->rc); +} + +static void redrat3_process_ir_data(struct redrat3_dev *rr3) +{ + DEFINE_IR_RAW_EVENT(rawir); + struct redrat3_signal_header header; + struct device *dev; + int i; + unsigned long delay; + u32 mod_freq, single_len; + u16 *len_vals; + u8 *data_vals; + u32 tmp32; + u16 tmp16; + char *sig_data; + + if (!rr3) { + pr_err("%s called with no context!\n", __func__); + return; + } + + rr3_ftr(rr3->dev, "Entered %s\n", __func__); + + dev = rr3->dev; + sig_data = rr3->pbuf; + + header.length = rr3->pktlen; + header.transfer_type = rr3->pkttype; + + /* Sanity check */ + if (!(header.length >= RR3_HEADER_LENGTH)) + dev_warn(dev, "read returned less than rr3 header len\n"); + + delay = usecs_to_jiffies(rr3->rc->timeout / 1000); + mod_timer(&rr3->rx_timeout, jiffies + delay); + + memcpy(&tmp32, sig_data + RR3_PAUSE_OFFSET, sizeof(tmp32)); + header.pause = be32_to_cpu(tmp32); + + memcpy(&tmp16, sig_data + RR3_FREQ_COUNT_OFFSET, sizeof(tmp16)); + header.mod_freq_count = be16_to_cpu(tmp16); + + memcpy(&tmp16, sig_data + RR3_NUM_PERIOD_OFFSET, sizeof(tmp16)); + header.no_periods = be16_to_cpu(tmp16); + + header.max_lengths = sig_data[RR3_MAX_LENGTHS_OFFSET]; + header.no_lengths = sig_data[RR3_NUM_LENGTHS_OFFSET]; + + memcpy(&tmp16, sig_data + RR3_MAX_SIGS_OFFSET, sizeof(tmp16)); + header.max_sig_size = be16_to_cpu(tmp16); + + memcpy(&tmp16, sig_data + RR3_NUM_SIGS_OFFSET, sizeof(tmp16)); + header.sig_size = be16_to_cpu(tmp16); + + header.no_repeats= sig_data[RR3_REPEATS_OFFSET]; + + if (debug) { + redrat3_dump_signal_header(&header); + redrat3_dump_signal_data(sig_data, header.sig_size); + } + + mod_freq = redrat3_val_to_mod_freq(&header); + rr3_dbg(dev, "Got mod_freq of %u\n", mod_freq); + + /* Here we pull out the 'length' values from the signal */ + len_vals = (u16 *)(sig_data + RR3_HEADER_LENGTH); + + data_vals = sig_data + RR3_HEADER_LENGTH + + (header.max_lengths * sizeof(u16)); + + /* process each rr3 encoded byte into an int */ + for (i = 0; i < header.sig_size; i++) { + u16 val = len_vals[data_vals[i]]; + single_len = redrat3_len_to_us((u32)be16_to_cpu(val)); + + /* cap the value to IR_MAX_DURATION */ + single_len &= IR_MAX_DURATION; + + /* we should always get pulse/space/pulse/space samples */ + if (i % 2) + rawir.pulse = false; + else + rawir.pulse = true; + + rawir.duration = US_TO_NS(single_len); + rr3_dbg(dev, "storing %s with duration %d (i: %d)\n", + rawir.pulse ? "pulse" : "space", rawir.duration, i); + ir_raw_event_store_with_filter(rr3->rc, &rawir); + } + + /* add a trailing space, if need be */ + if (i % 2) { + rawir.pulse = false; + /* this duration is made up, and may not be ideal... */ + rawir.duration = rr3->rc->timeout / 2; + rr3_dbg(dev, "storing trailing space with duration %d\n", + rawir.duration); + ir_raw_event_store_with_filter(rr3->rc, &rawir); + } + + rr3_dbg(dev, "calling ir_raw_event_handle\n"); + ir_raw_event_handle(rr3->rc); + + return; +} + +/* Util fn to send rr3 cmds */ +static u8 redrat3_send_cmd(int cmd, struct redrat3_dev *rr3) +{ + struct usb_device *udev; + u8 *data; + int res; + + data = kzalloc(sizeof(u8), GFP_KERNEL); + if (!data) + return -ENOMEM; + + udev = rr3->udev; + res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), cmd, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, + 0x0000, 0x0000, data, sizeof(u8), HZ * 10); + + if (res < 0) { + dev_err(rr3->dev, "%s: Error sending rr3 cmd res %d, data %d", + __func__, res, *data); + res = -EIO; + } else + res = (u8)data[0]; + + kfree(data); + + return res; +} + +/* Enables the long range detector and starts async receive */ +static int redrat3_enable_detector(struct redrat3_dev *rr3) +{ + struct device *dev = rr3->dev; + u8 ret; + + rr3_ftr(dev, "Entering %s\n", __func__); + + ret = redrat3_send_cmd(RR3_RC_DET_ENABLE, rr3); + if (ret != 0) + dev_dbg(dev, "%s: unexpected ret of %d\n", + __func__, ret); + + ret = redrat3_send_cmd(RR3_RC_DET_STATUS, rr3); + if (ret != 1) { + dev_err(dev, "%s: detector status: %d, should be 1\n", + __func__, ret); + return -EIO; + } + + rr3->det_enabled = true; + redrat3_issue_async(rr3); + + return 0; +} + +/* Disables the rr3 long range detector */ +static void redrat3_disable_detector(struct redrat3_dev *rr3) +{ + struct device *dev = rr3->dev; + u8 ret; + + rr3_ftr(dev, "Entering %s\n", __func__); + + ret = redrat3_send_cmd(RR3_RC_DET_DISABLE, rr3); + if (ret != 0) + dev_err(dev, "%s: failure!\n", __func__); + + ret = redrat3_send_cmd(RR3_RC_DET_STATUS, rr3); + if (ret != 0) + dev_warn(dev, "%s: detector status: %d, should be 0\n", + __func__, ret); + + rr3->det_enabled = false; +} + +static inline void redrat3_delete(struct redrat3_dev *rr3, + struct usb_device *udev) +{ + rr3_ftr(rr3->dev, "%s cleaning up\n", __func__); + usb_kill_urb(rr3->read_urb); + usb_kill_urb(rr3->write_urb); + + usb_free_urb(rr3->read_urb); + usb_free_urb(rr3->write_urb); + + usb_free_coherent(udev, rr3->ep_in->wMaxPacketSize, + rr3->bulk_in_buf, rr3->dma_in); + usb_free_coherent(udev, rr3->ep_out->wMaxPacketSize, + rr3->bulk_out_buf, rr3->dma_out); + + kfree(rr3); +} + +static u32 redrat3_get_timeout(struct device *dev, + struct rc_dev *rc, struct usb_device *udev) +{ + u32 *tmp; + u32 timeout = MS_TO_NS(150); /* a sane default, if things go haywire */ + int len, ret, pipe; + + len = sizeof(*tmp); + tmp = kzalloc(len, GFP_KERNEL); + if (!tmp) { + dev_warn(dev, "Memory allocation faillure\n"); + return timeout; + } + + pipe = usb_rcvctrlpipe(udev, 0); + ret = usb_control_msg(udev, pipe, RR3_GET_IR_PARAM, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, + RR3_IR_IO_SIG_TIMEOUT, 0, tmp, len, HZ * 5); + if (ret != len) { + dev_warn(dev, "Failed to read timeout from hardware\n"); + return timeout; + } + + timeout = US_TO_NS(redrat3_len_to_us(be32_to_cpu(*tmp))); + if (timeout < rc->min_timeout) + timeout = rc->min_timeout; + else if (timeout > rc->max_timeout) + timeout = rc->max_timeout; + + rr3_dbg(dev, "Got timeout of %d ms\n", timeout / (1000 * 1000)); + return timeout; +} + +static void redrat3_reset(struct redrat3_dev *rr3) +{ + struct usb_device *udev = rr3->udev; + struct device *dev = rr3->dev; + int rc, rxpipe, txpipe; + u8 *val; + int len = sizeof(u8); + + rr3_ftr(dev, "Entering %s\n", __func__); + + rxpipe = usb_rcvctrlpipe(udev, 0); + txpipe = usb_sndctrlpipe(udev, 0); + + val = kzalloc(len, GFP_KERNEL); + if (!val) { + dev_err(dev, "Memory allocation failure\n"); + return; + } + + *val = 0x01; + rc = usb_control_msg(udev, rxpipe, RR3_RESET, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, + RR3_CPUCS_REG_ADDR, 0, val, len, HZ * 25); + rr3_dbg(dev, "reset returned 0x%02x\n", rc); + + *val = 5; + rc = usb_control_msg(udev, txpipe, RR3_SET_IR_PARAM, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, + RR3_IR_IO_LENGTH_FUZZ, 0, val, len, HZ * 25); + rr3_dbg(dev, "set ir parm len fuzz %d rc 0x%02x\n", *val, rc); + + *val = RR3_DRIVER_MAXLENS; + rc = usb_control_msg(udev, txpipe, RR3_SET_IR_PARAM, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, + RR3_IR_IO_MAX_LENGTHS, 0, val, len, HZ * 25); + rr3_dbg(dev, "set ir parm max lens %d rc 0x%02x\n", *val, rc); + + kfree(val); +} + +static void redrat3_get_firmware_rev(struct redrat3_dev *rr3) +{ + int rc = 0; + char *buffer; + + rr3_ftr(rr3->dev, "Entering %s\n", __func__); + + buffer = kzalloc(sizeof(char) * (RR3_FW_VERSION_LEN + 1), GFP_KERNEL); + if (!buffer) { + dev_err(rr3->dev, "Memory allocation failure\n"); + return; + } + + rc = usb_control_msg(rr3->udev, usb_rcvctrlpipe(rr3->udev, 0), + RR3_FW_VERSION, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, + 0, 0, buffer, RR3_FW_VERSION_LEN, HZ * 5); + + if (rc >= 0) + dev_info(rr3->dev, "Firmware rev: %s", buffer); + else + dev_err(rr3->dev, "Problem fetching firmware ID\n"); + + kfree(buffer); + rr3_ftr(rr3->dev, "Exiting %s\n", __func__); +} + +static void redrat3_read_packet_start(struct redrat3_dev *rr3, int len) +{ + u16 tx_error; + u16 hdrlen; + + rr3_ftr(rr3->dev, "Entering %s\n", __func__); + + /* grab the Length and type of transfer */ + memcpy(&(rr3->pktlen), (unsigned char *) rr3->bulk_in_buf, + sizeof(rr3->pktlen)); + memcpy(&(rr3->pkttype), ((unsigned char *) rr3->bulk_in_buf + + sizeof(rr3->pktlen)), + sizeof(rr3->pkttype)); + + /*data needs conversion to know what its real values are*/ + rr3->pktlen = be16_to_cpu(rr3->pktlen); + rr3->pkttype = be16_to_cpu(rr3->pkttype); + + switch (rr3->pkttype) { + case RR3_ERROR: + memcpy(&tx_error, ((unsigned char *)rr3->bulk_in_buf + + (sizeof(rr3->pktlen) + sizeof(rr3->pkttype))), + sizeof(tx_error)); + tx_error = be16_to_cpu(tx_error); + redrat3_dump_fw_error(rr3, tx_error); + break; + + case RR3_MOD_SIGNAL_IN: + hdrlen = sizeof(rr3->pktlen) + sizeof(rr3->pkttype); + rr3->bytes_read = len; + rr3->bytes_read -= hdrlen; + rr3->datap = &(rr3->pbuf[0]); + + memcpy(rr3->datap, ((unsigned char *)rr3->bulk_in_buf + hdrlen), + rr3->bytes_read); + rr3->datap += rr3->bytes_read; + rr3_dbg(rr3->dev, "bytes_read %d, pktlen %d\n", + rr3->bytes_read, rr3->pktlen); + break; + + default: + rr3_dbg(rr3->dev, "ignoring packet with type 0x%02x, " + "len of %d, 0x%02x\n", rr3->pkttype, len, rr3->pktlen); + break; + } +} + +static void redrat3_read_packet_continue(struct redrat3_dev *rr3, int len) +{ + + rr3_ftr(rr3->dev, "Entering %s\n", __func__); + + memcpy(rr3->datap, (unsigned char *)rr3->bulk_in_buf, len); + rr3->datap += len; + + rr3->bytes_read += len; + rr3_dbg(rr3->dev, "bytes_read %d, pktlen %d\n", + rr3->bytes_read, rr3->pktlen); +} + +/* gather IR data from incoming urb, process it when we have enough */ +static int redrat3_get_ir_data(struct redrat3_dev *rr3, int len) +{ + struct device *dev = rr3->dev; + int ret = 0; + + rr3_ftr(dev, "Entering %s\n", __func__); + + if (rr3->pktlen > RR3_MAX_BUF_SIZE) { + dev_err(rr3->dev, "error: packet larger than buffer\n"); + ret = -EINVAL; + goto out; + } + + if ((rr3->bytes_read == 0) && + (len >= (sizeof(rr3->pkttype) + sizeof(rr3->pktlen)))) { + redrat3_read_packet_start(rr3, len); + } else if (rr3->bytes_read != 0) { + redrat3_read_packet_continue(rr3, len); + } else if (rr3->bytes_read == 0) { + dev_err(dev, "error: no packet data read\n"); + ret = -ENODATA; + goto out; + } + + if (rr3->bytes_read > rr3->pktlen) { + dev_err(dev, "bytes_read (%d) greater than pktlen (%d)\n", + rr3->bytes_read, rr3->pktlen); + ret = -EINVAL; + goto out; + } else if (rr3->bytes_read < rr3->pktlen) + /* we're still accumulating data */ + return 0; + + /* if we get here, we've got IR data to decode */ + if (rr3->pkttype == RR3_MOD_SIGNAL_IN) + redrat3_process_ir_data(rr3); + else + rr3_dbg(dev, "discarding non-signal data packet " + "(type 0x%02x)\n", rr3->pkttype); + +out: + rr3->bytes_read = 0; + rr3->pktlen = 0; + rr3->pkttype = 0; + return ret; +} + +/* callback function from USB when async USB request has completed */ +static void redrat3_handle_async(struct urb *urb, struct pt_regs *regs) +{ + struct redrat3_dev *rr3; + + if (!urb) + return; + + rr3 = urb->context; + if (!rr3) { + pr_err("%s called with invalid context!\n", __func__); + usb_unlink_urb(urb); + return; + } + + rr3_ftr(rr3->dev, "Entering %s\n", __func__); + + if (!rr3->det_enabled) { + rr3_dbg(rr3->dev, "received a read callback but detector " + "disabled - ignoring\n"); + return; + } + + switch (urb->status) { + case 0: + redrat3_get_ir_data(rr3, urb->actual_length); + break; + + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + usb_unlink_urb(urb); + return; + + case -EPIPE: + default: + dev_warn(rr3->dev, "Error: urb status = %d\n", urb->status); + rr3->bytes_read = 0; + rr3->pktlen = 0; + rr3->pkttype = 0; + break; + } + + if (!rr3->transmitting) + redrat3_issue_async(rr3); + else + rr3_dbg(rr3->dev, "IR transmit in progress\n"); +} + +static void redrat3_write_bulk_callback(struct urb *urb, struct pt_regs *regs) +{ + struct redrat3_dev *rr3; + int len; + + if (!urb) + return; + + rr3 = urb->context; + if (rr3) { + len = urb->actual_length; + rr3_ftr(rr3->dev, "%s: called (status=%d len=%d)\n", + __func__, urb->status, len); + } +} + +static u16 mod_freq_to_val(unsigned int mod_freq) +{ + int mult = 6000000; + + /* Clk used in mod. freq. generation is CLK24/4. */ + return (u16)(65536 - (mult / mod_freq)); +} + +static int redrat3_set_tx_carrier(struct rc_dev *dev, u32 carrier) +{ + struct redrat3_dev *rr3 = dev->priv; + + rr3->carrier = carrier; + + return carrier; +} + +static int redrat3_transmit_ir(struct rc_dev *rcdev, int *txbuf, u32 n) +{ + struct redrat3_dev *rr3 = rcdev->priv; + struct device *dev = rr3->dev; + struct redrat3_signal_header header; + int i, j, count, ret, ret_len, offset; + int lencheck, cur_sample_len, pipe; + char *buffer = NULL, *sigdata = NULL; + int *sample_lens = NULL; + u32 tmpi; + u16 tmps; + u8 *datap; + u8 curlencheck = 0; + u16 *lengths_ptr; + int sendbuf_len; + + rr3_ftr(dev, "Entering %s\n", __func__); + + if (rr3->transmitting) { + dev_warn(dev, "%s: transmitter already in use\n", __func__); + return -EAGAIN; + } + + count = n / sizeof(int); + if (count > (RR3_DRIVER_MAXLENS * 2)) + return -EINVAL; + + rr3->transmitting = true; + + redrat3_disable_detector(rr3); + + if (rr3->det_enabled) { + dev_err(dev, "%s: cannot tx while rx is enabled\n", __func__); + ret = -EIO; + goto out; + } + + sample_lens = kzalloc(sizeof(int) * RR3_DRIVER_MAXLENS, GFP_KERNEL); + if (!sample_lens) { + ret = -ENOMEM; + goto out; + } + + for (i = 0; i < count; i++) { + for (lencheck = 0; lencheck < curlencheck; lencheck++) { + cur_sample_len = redrat3_us_to_len(txbuf[i]); + if (sample_lens[lencheck] == cur_sample_len) + break; + } + if (lencheck == curlencheck) { + cur_sample_len = redrat3_us_to_len(txbuf[i]); + rr3_dbg(dev, "txbuf[%d]=%u, pos %d, enc %u\n", + i, txbuf[i], curlencheck, cur_sample_len); + if (curlencheck < 255) { + /* now convert the value to a proper + * rr3 value.. */ + sample_lens[curlencheck] = cur_sample_len; + curlencheck++; + } else { + dev_err(dev, "signal too long\n"); + ret = -EINVAL; + goto out; + } + } + } + + sigdata = kzalloc((count + RR3_TX_TRAILER_LEN), GFP_KERNEL); + if (!sigdata) { + ret = -ENOMEM; + goto out; + } + + sigdata[count] = RR3_END_OF_SIGNAL; + sigdata[count + 1] = RR3_END_OF_SIGNAL; + for (i = 0; i < count; i++) { + for (j = 0; j < curlencheck; j++) { + if (sample_lens[j] == redrat3_us_to_len(txbuf[i])) + sigdata[i] = j; + } + } + + offset = RR3_TX_HEADER_OFFSET; + sendbuf_len = RR3_HEADER_LENGTH + (sizeof(u16) * RR3_DRIVER_MAXLENS) + + count + RR3_TX_TRAILER_LEN + offset; + + buffer = kzalloc(sendbuf_len, GFP_KERNEL); + if (!buffer) { + ret = -ENOMEM; + goto out; + } + + /* fill in our packet header */ + header.length = sendbuf_len - offset; + header.transfer_type = RR3_MOD_SIGNAL_OUT; + header.pause = redrat3_len_to_us(100); + header.mod_freq_count = mod_freq_to_val(rr3->carrier); + header.no_periods = 0; /* n/a to transmit */ + header.max_lengths = RR3_DRIVER_MAXLENS; + header.no_lengths = curlencheck; + header.max_sig_size = RR3_MAX_SIG_SIZE; + header.sig_size = count + RR3_TX_TRAILER_LEN; + /* we currently rely on repeat handling in the IR encoding source */ + header.no_repeats = 0; + + tmps = cpu_to_be16(header.length); + memcpy(buffer, &tmps, 2); + + tmps = cpu_to_be16(header.transfer_type); + memcpy(buffer + 2, &tmps, 2); + + tmpi = cpu_to_be32(header.pause); + memcpy(buffer + offset, &tmpi, sizeof(tmpi)); + + tmps = cpu_to_be16(header.mod_freq_count); + memcpy(buffer + offset + RR3_FREQ_COUNT_OFFSET, &tmps, 2); + + buffer[offset + RR3_NUM_LENGTHS_OFFSET] = header.no_lengths; + + tmps = cpu_to_be16(header.sig_size); + memcpy(buffer + offset + RR3_NUM_SIGS_OFFSET, &tmps, 2); + + buffer[offset + RR3_REPEATS_OFFSET] = header.no_repeats; + + lengths_ptr = (u16 *)(buffer + offset + RR3_HEADER_LENGTH); + for (i = 0; i < curlencheck; ++i) + lengths_ptr[i] = cpu_to_be16(sample_lens[i]); + + datap = (u8 *)(buffer + offset + RR3_HEADER_LENGTH + + (sizeof(u16) * RR3_DRIVER_MAXLENS)); + memcpy(datap, sigdata, (count + RR3_TX_TRAILER_LEN)); + + if (debug) { + redrat3_dump_signal_header(&header); + redrat3_dump_signal_data(buffer, header.sig_size); + } + + pipe = usb_sndbulkpipe(rr3->udev, rr3->ep_out->bEndpointAddress); + tmps = usb_bulk_msg(rr3->udev, pipe, buffer, + sendbuf_len, &ret_len, 10 * HZ); + rr3_dbg(dev, "sent %d bytes, (ret %d)\n", ret_len, tmps); + + /* now tell the hardware to transmit what we sent it */ + pipe = usb_rcvctrlpipe(rr3->udev, 0); + ret = usb_control_msg(rr3->udev, pipe, RR3_TX_SEND_SIGNAL, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, + 0, 0, buffer, 2, HZ * 10); + + if (ret < 0) + dev_err(dev, "Error: control msg send failed, rc %d\n", ret); + else + ret = n; + +out: + kfree(sample_lens); + kfree(buffer); + kfree(sigdata); + + rr3->transmitting = false; + + redrat3_enable_detector(rr3); + + return ret; +} + +static struct rc_dev *redrat3_init_rc_dev(struct redrat3_dev *rr3) +{ + struct device *dev = rr3->dev; + struct rc_dev *rc; + int ret = -ENODEV; + u16 prod = le16_to_cpu(rr3->udev->descriptor.idProduct); + + rc = rc_allocate_device(); + if (!rc) { + dev_err(dev, "remote input dev allocation failed\n"); + goto out; + } + + snprintf(rr3->name, sizeof(rr3->name), "RedRat3%s " + "Infrared Remote Transceiver (%04x:%04x)", + prod == USB_RR3IIUSB_PRODUCT_ID ? "-II" : "", + le16_to_cpu(rr3->udev->descriptor.idVendor), prod); + + usb_make_path(rr3->udev, rr3->phys, sizeof(rr3->phys)); + + rc->input_name = rr3->name; + rc->input_phys = rr3->phys; + usb_to_input_id(rr3->udev, &rc->input_id); + rc->dev.parent = dev; + rc->priv = rr3; + rc->driver_type = RC_DRIVER_IR_RAW; + rc->allowed_protos = RC_TYPE_ALL; + rc->min_timeout = MS_TO_NS(RR3_RX_MIN_TIMEOUT); + rc->max_timeout = MS_TO_NS(RR3_RX_MAX_TIMEOUT); + rc->timeout = redrat3_get_timeout(dev, rc, rr3->udev); + rc->tx_ir = redrat3_transmit_ir; + rc->s_tx_carrier = redrat3_set_tx_carrier; + rc->driver_name = DRIVER_NAME; + rc->map_name = RC_MAP_HAUPPAUGE; + + ret = rc_register_device(rc); + if (ret < 0) { + dev_err(dev, "remote dev registration failed\n"); + goto out; + } + + return rc; + +out: + rc_free_device(rc); + return NULL; +} + +static int __devinit redrat3_dev_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct device *dev = &intf->dev; + struct usb_host_interface *uhi; + struct redrat3_dev *rr3; + struct usb_endpoint_descriptor *ep; + struct usb_endpoint_descriptor *ep_in = NULL; + struct usb_endpoint_descriptor *ep_out = NULL; + u8 addr, attrs; + int pipe, i; + int retval = -ENOMEM; + + rr3_ftr(dev, "%s called\n", __func__); + + uhi = intf->cur_altsetting; + + /* find our bulk-in and bulk-out endpoints */ + for (i = 0; i < uhi->desc.bNumEndpoints; ++i) { + ep = &uhi->endpoint[i].desc; + addr = ep->bEndpointAddress; + attrs = ep->bmAttributes; + + if ((ep_in == NULL) && + ((addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) && + ((attrs & USB_ENDPOINT_XFERTYPE_MASK) == + USB_ENDPOINT_XFER_BULK)) { + rr3_dbg(dev, "found bulk-in endpoint at 0x%02x\n", + ep->bEndpointAddress); + /* data comes in on 0x82, 0x81 is for other data... */ + if (ep->bEndpointAddress == RR3_BULK_IN_EP_ADDR) + ep_in = ep; + } + + if ((ep_out == NULL) && + ((addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT) && + ((attrs & USB_ENDPOINT_XFERTYPE_MASK) == + USB_ENDPOINT_XFER_BULK)) { + rr3_dbg(dev, "found bulk-out endpoint at 0x%02x\n", + ep->bEndpointAddress); + ep_out = ep; + } + } + + if (!ep_in || !ep_out) { + dev_err(dev, "Couldn't find both in and out endpoints\n"); + retval = -ENODEV; + goto no_endpoints; + } + + /* allocate memory for our device state and initialize it */ + rr3 = kzalloc(sizeof(*rr3), GFP_KERNEL); + if (rr3 == NULL) { + dev_err(dev, "Memory allocation failure\n"); + goto error; + } + + rr3->dev = &intf->dev; + + /* set up bulk-in endpoint */ + rr3->read_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!rr3->read_urb) { + dev_err(dev, "Read urb allocation failure\n"); + goto error; + } + + rr3->ep_in = ep_in; + rr3->bulk_in_buf = usb_alloc_coherent(udev, ep_in->wMaxPacketSize, + GFP_ATOMIC, &rr3->dma_in); + if (!rr3->bulk_in_buf) { + dev_err(dev, "Read buffer allocation failure\n"); + goto error; + } + + pipe = usb_rcvbulkpipe(udev, ep_in->bEndpointAddress); + usb_fill_bulk_urb(rr3->read_urb, udev, pipe, + rr3->bulk_in_buf, ep_in->wMaxPacketSize, + (usb_complete_t)redrat3_handle_async, rr3); + + /* set up bulk-out endpoint*/ + rr3->write_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!rr3->write_urb) { + dev_err(dev, "Write urb allocation failure\n"); + goto error; + } + + rr3->ep_out = ep_out; + rr3->bulk_out_buf = usb_alloc_coherent(udev, ep_out->wMaxPacketSize, + GFP_ATOMIC, &rr3->dma_out); + if (!rr3->bulk_out_buf) { + dev_err(dev, "Write buffer allocation failure\n"); + goto error; + } + + pipe = usb_sndbulkpipe(udev, ep_out->bEndpointAddress); + usb_fill_bulk_urb(rr3->write_urb, udev, pipe, + rr3->bulk_out_buf, ep_out->wMaxPacketSize, + (usb_complete_t)redrat3_write_bulk_callback, rr3); + + mutex_init(&rr3->lock); + rr3->udev = udev; + + redrat3_reset(rr3); + redrat3_get_firmware_rev(rr3); + + /* might be all we need to do? */ + retval = redrat3_enable_detector(rr3); + if (retval < 0) + goto error; + + /* default.. will get overridden by any sends with a freq defined */ + rr3->carrier = 38000; + + rr3->rc = redrat3_init_rc_dev(rr3); + if (!rr3->rc) + goto error; + + setup_timer(&rr3->rx_timeout, redrat3_rx_timeout, (unsigned long)rr3); + + /* we can register the device now, as it is ready */ + usb_set_intfdata(intf, rr3); + + rr3_ftr(dev, "Exiting %s\n", __func__); + return 0; + +error: + redrat3_delete(rr3, rr3->udev); + +no_endpoints: + dev_err(dev, "%s: retval = %x", __func__, retval); + + return retval; +} + +static void __devexit redrat3_dev_disconnect(struct usb_interface *intf) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct redrat3_dev *rr3 = usb_get_intfdata(intf); + + rr3_ftr(&intf->dev, "Entering %s\n", __func__); + + if (!rr3) + return; + + redrat3_disable_detector(rr3); + + usb_set_intfdata(intf, NULL); + rc_unregister_device(rr3->rc); + redrat3_delete(rr3, udev); + + rr3_ftr(&intf->dev, "RedRat3 IR Transceiver now disconnected\n"); +} + +static int redrat3_dev_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct redrat3_dev *rr3 = usb_get_intfdata(intf); + rr3_ftr(rr3->dev, "suspend\n"); + usb_kill_urb(rr3->read_urb); + return 0; +} + +static int redrat3_dev_resume(struct usb_interface *intf) +{ + struct redrat3_dev *rr3 = usb_get_intfdata(intf); + rr3_ftr(rr3->dev, "resume\n"); + if (usb_submit_urb(rr3->read_urb, GFP_ATOMIC)) + return -EIO; + return 0; +} + +static struct usb_driver redrat3_dev_driver = { + .name = DRIVER_NAME, + .probe = redrat3_dev_probe, + .disconnect = redrat3_dev_disconnect, + .suspend = redrat3_dev_suspend, + .resume = redrat3_dev_resume, + .reset_resume = redrat3_dev_resume, + .id_table = redrat3_dev_table +}; + +static int __init redrat3_dev_init(void) +{ + int ret; + + ret = usb_register(&redrat3_dev_driver); + if (ret < 0) + pr_err(DRIVER_NAME + ": usb register failed, result = %d\n", ret); + + return ret; +} + +static void __exit redrat3_dev_exit(void) +{ + usb_deregister(&redrat3_dev_driver); +} + +module_init(redrat3_dev_init); +module_exit(redrat3_dev_exit); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_AUTHOR(DRIVER_AUTHOR2); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(usb, redrat3_dev_table); + +module_param(debug, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "Enable module debug spew. 0 = no debugging (default) " + "0x1 = standard debug messages, 0x2 = function tracing debug. " + "Flag bits are addative (i.e., 0x3 for both debug types)."); diff --git a/drivers/media/rc/winbond-cir.c b/drivers/media/rc/winbond-cir.c index 186de5522001..5d06b899e859 100644 --- a/drivers/media/rc/winbond-cir.c +++ b/drivers/media/rc/winbond-cir.c @@ -19,11 +19,12 @@ * o DSDT dumps * * Supported features: + * o IR Receive + * o IR Transmit * o Wake-On-CIR functionality * * To do: * o Learning - * o IR Transmit * * 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 @@ -50,6 +51,8 @@ #include <linux/io.h> #include <linux/bitrev.h> #include <linux/slab.h> +#include <linux/wait.h> +#include <linux/sched.h> #include <media/rc-core.h> #define DRVNAME "winbond-cir" @@ -118,14 +121,24 @@ #define WBCIR_IRQ_NONE 0x00 /* RX data bit for WBCIR_REG_SP3_IER and WBCIR_REG_SP3_EIR */ #define WBCIR_IRQ_RX 0x01 +/* TX data low bit for WBCIR_REG_SP3_IER and WBCIR_REG_SP3_EIR */ +#define WBCIR_IRQ_TX_LOW 0x02 /* Over/Under-flow bit for WBCIR_REG_SP3_IER and WBCIR_REG_SP3_EIR */ #define WBCIR_IRQ_ERR 0x04 +/* TX data empty bit for WBCEIR_REG_SP3_IER and WBCIR_REG_SP3_EIR */ +#define WBCIR_IRQ_TX_EMPTY 0x20 /* Led enable/disable bit for WBCIR_REG_ECEIR_CTS */ #define WBCIR_LED_ENABLE 0x80 /* RX data available bit for WBCIR_REG_SP3_LSR */ #define WBCIR_RX_AVAIL 0x01 +/* RX data overrun error bit for WBCIR_REG_SP3_LSR */ +#define WBCIR_RX_OVERRUN 0x02 +/* TX End-Of-Transmission bit for WBCIR_REG_SP3_ASCR */ +#define WBCIR_TX_EOT 0x04 /* RX disable bit for WBCIR_REG_SP3_ASCR */ #define WBCIR_RX_DISABLE 0x20 +/* TX data underrun error bit for WBCIR_REG_SP3_ASCR */ +#define WBCIR_TX_UNDERRUN 0x40 /* Extended mode enable bit for WBCIR_REG_SP3_EXCR1 */ #define WBCIR_EXT_ENABLE 0x01 /* Select compare register in WBCIR_REG_WCEIR_INDEX (bits 5 & 6) */ @@ -154,6 +167,21 @@ enum wbcir_protocol { IR_PROTOCOL_RC6 = 0x2, }; +/* Possible states for IR reception */ +enum wbcir_rxstate { + WBCIR_RXSTATE_INACTIVE = 0, + WBCIR_RXSTATE_ACTIVE, + WBCIR_RXSTATE_ERROR +}; + +/* Possible states for IR transmission */ +enum wbcir_txstate { + WBCIR_TXSTATE_INACTIVE = 0, + WBCIR_TXSTATE_ACTIVE, + WBCIR_TXSTATE_DONE, + WBCIR_TXSTATE_ERROR +}; + /* Misc */ #define WBCIR_NAME "Winbond CIR" #define WBCIR_ID_FAMILY 0xF1 /* Family ID for the WPCD376I */ @@ -166,22 +194,29 @@ enum wbcir_protocol { /* Per-device data */ struct wbcir_data { spinlock_t spinlock; + struct rc_dev *dev; + struct led_classdev led; unsigned long wbase; /* Wake-Up Baseaddr */ unsigned long ebase; /* Enhanced Func. Baseaddr */ unsigned long sbase; /* Serial Port Baseaddr */ unsigned int irq; /* Serial Port IRQ */ + u8 irqmask; - struct rc_dev *dev; - + /* RX state */ + enum wbcir_rxstate rxstate; struct led_trigger *rxtrigger; - struct led_trigger *txtrigger; - struct led_classdev led; + struct ir_raw_event rxev; - /* RX irdata state */ - bool irdata_active; - bool irdata_error; - struct ir_raw_event ev; + /* TX state */ + enum wbcir_txstate txstate; + struct led_trigger *txtrigger; + u32 txlen; + u32 txoff; + u32 *txbuf; + wait_queue_head_t txwaitq; + u8 txmask; + u32 txcarrier; }; static enum wbcir_protocol protocol = IR_PROTOCOL_RC6; @@ -193,6 +228,10 @@ static int invert; /* default = 0 */ module_param(invert, bool, 0444); MODULE_PARM_DESC(invert, "Invert the signal from the IR receiver"); +static int txandrx; /* default = 0 */ +module_param(txandrx, bool, 0444); +MODULE_PARM_DESC(invert, "Allow simultaneous TX and RX"); + static unsigned int wake_sc = 0x800F040C; module_param(wake_sc, uint, 0644); MODULE_PARM_DESC(wake_sc, "Scancode of the power-on IR command"); @@ -228,6 +267,17 @@ wbcir_select_bank(struct wbcir_data *data, enum wbcir_bank bank) outb(bank, data->sbase + WBCIR_REG_SP3_BSR); } +static inline void +wbcir_set_irqmask(struct wbcir_data *data, u8 irqmask) +{ + if (data->irqmask == irqmask) + return; + + wbcir_select_bank(data, WBCIR_BANK_0); + outb(irqmask, data->sbase + WBCIR_REG_SP3_IER); + data->irqmask = irqmask; +} + static enum led_brightness wbcir_led_brightness_get(struct led_classdev *led_cdev) { @@ -279,97 +329,297 @@ wbcir_to_rc6cells(u8 val) * *****************************************************************************/ +static void +wbcir_idle_rx(struct rc_dev *dev, bool idle) +{ + struct wbcir_data *data = dev->priv; + + if (!idle && data->rxstate == WBCIR_RXSTATE_INACTIVE) { + data->rxstate = WBCIR_RXSTATE_ACTIVE; + led_trigger_event(data->rxtrigger, LED_FULL); + } + + if (idle && data->rxstate != WBCIR_RXSTATE_INACTIVE) + /* Tell hardware to go idle by setting RXINACTIVE */ + outb(WBCIR_RX_DISABLE, data->sbase + WBCIR_REG_SP3_ASCR); +} + +static void +wbcir_irq_rx(struct wbcir_data *data, struct pnp_dev *device) +{ + u8 irdata; + DEFINE_IR_RAW_EVENT(rawir); + + /* Since RXHDLEV is set, at least 8 bytes are in the FIFO */ + while (inb(data->sbase + WBCIR_REG_SP3_LSR) & WBCIR_RX_AVAIL) { + irdata = inb(data->sbase + WBCIR_REG_SP3_RXDATA); + if (data->rxstate == WBCIR_RXSTATE_ERROR) + continue; + rawir.pulse = irdata & 0x80 ? false : true; + rawir.duration = US_TO_NS((irdata & 0x7F) * 10); + ir_raw_event_store_with_filter(data->dev, &rawir); + } + + /* Check if we should go idle */ + if (data->dev->idle) { + led_trigger_event(data->rxtrigger, LED_OFF); + data->rxstate = WBCIR_RXSTATE_INACTIVE; + } + + ir_raw_event_handle(data->dev); +} + +static void +wbcir_irq_tx(struct wbcir_data *data) +{ + unsigned int space; + unsigned int used; + u8 bytes[16]; + u8 byte; + + if (!data->txbuf) + return; + + switch (data->txstate) { + case WBCIR_TXSTATE_INACTIVE: + /* TX FIFO empty */ + space = 16; + led_trigger_event(data->txtrigger, LED_FULL); + break; + case WBCIR_TXSTATE_ACTIVE: + /* TX FIFO low (3 bytes or less) */ + space = 13; + break; + case WBCIR_TXSTATE_ERROR: + space = 0; + break; + default: + return; + } + + /* + * TX data is run-length coded in bytes: YXXXXXXX + * Y = space (1) or pulse (0) + * X = duration, encoded as (X + 1) * 10us (i.e 10 to 1280 us) + */ + for (used = 0; used < space && data->txoff != data->txlen; used++) { + if (data->txbuf[data->txoff] == 0) { + data->txoff++; + continue; + } + byte = min((u32)0x80, data->txbuf[data->txoff]); + data->txbuf[data->txoff] -= byte; + byte--; + byte |= (data->txoff % 2 ? 0x80 : 0x00); /* pulse/space */ + bytes[used] = byte; + } + + while (data->txbuf[data->txoff] == 0 && data->txoff != data->txlen) + data->txoff++; + + if (used == 0) { + /* Finished */ + if (data->txstate == WBCIR_TXSTATE_ERROR) + /* Clear TX underrun bit */ + outb(WBCIR_TX_UNDERRUN, data->sbase + WBCIR_REG_SP3_ASCR); + else + data->txstate = WBCIR_TXSTATE_DONE; + wbcir_set_irqmask(data, WBCIR_IRQ_RX | WBCIR_IRQ_ERR); + led_trigger_event(data->txtrigger, LED_OFF); + wake_up(&data->txwaitq); + } else if (data->txoff == data->txlen) { + /* At the end of transmission, tell the hw before last byte */ + outsb(data->sbase + WBCIR_REG_SP3_TXDATA, bytes, used - 1); + outb(WBCIR_TX_EOT, data->sbase + WBCIR_REG_SP3_ASCR); + outb(bytes[used - 1], data->sbase + WBCIR_REG_SP3_TXDATA); + wbcir_set_irqmask(data, WBCIR_IRQ_RX | WBCIR_IRQ_ERR | + WBCIR_IRQ_TX_EMPTY); + } else { + /* More data to follow... */ + outsb(data->sbase + WBCIR_REG_SP3_RXDATA, bytes, used); + if (data->txstate == WBCIR_TXSTATE_INACTIVE) { + wbcir_set_irqmask(data, WBCIR_IRQ_RX | WBCIR_IRQ_ERR | + WBCIR_IRQ_TX_LOW); + data->txstate = WBCIR_TXSTATE_ACTIVE; + } + } +} + static irqreturn_t wbcir_irq_handler(int irqno, void *cookie) { struct pnp_dev *device = cookie; struct wbcir_data *data = pnp_get_drvdata(device); unsigned long flags; - u8 irdata[8]; - u8 disable = true; u8 status; - int i; spin_lock_irqsave(&data->spinlock, flags); - wbcir_select_bank(data, WBCIR_BANK_0); - status = inb(data->sbase + WBCIR_REG_SP3_EIR); + status &= data->irqmask; - if (!(status & (WBCIR_IRQ_RX | WBCIR_IRQ_ERR))) { + if (!status) { spin_unlock_irqrestore(&data->spinlock, flags); return IRQ_NONE; } - /* Check for e.g. buffer overflow */ if (status & WBCIR_IRQ_ERR) { - data->irdata_error = true; - ir_raw_event_reset(data->dev); - } - - if (!(status & WBCIR_IRQ_RX)) - goto out; + /* RX overflow? (read clears bit) */ + if (inb(data->sbase + WBCIR_REG_SP3_LSR) & WBCIR_RX_OVERRUN) { + data->rxstate = WBCIR_RXSTATE_ERROR; + ir_raw_event_reset(data->dev); + } - if (!data->irdata_active) { - data->irdata_active = true; - led_trigger_event(data->rxtrigger, LED_FULL); + /* TX underflow? */ + if (inb(data->sbase + WBCIR_REG_SP3_ASCR) & WBCIR_TX_UNDERRUN) + data->txstate = WBCIR_TXSTATE_ERROR; } - /* Since RXHDLEV is set, at least 8 bytes are in the FIFO */ - insb(data->sbase + WBCIR_REG_SP3_RXDATA, &irdata[0], 8); + if (status & WBCIR_IRQ_RX) + wbcir_irq_rx(data, device); - for (i = 0; i < 8; i++) { - u8 pulse; - u32 duration; + if (status & (WBCIR_IRQ_TX_LOW | WBCIR_IRQ_TX_EMPTY)) + wbcir_irq_tx(data); - if (irdata[i] != 0xFF && irdata[i] != 0x00) - disable = false; - - if (data->irdata_error) - continue; + spin_unlock_irqrestore(&data->spinlock, flags); + return IRQ_HANDLED; +} - pulse = irdata[i] & 0x80 ? false : true; - duration = (irdata[i] & 0x7F) * 10000; /* ns */ +/***************************************************************************** + * + * RC-CORE INTERFACE FUNCTIONS + * + *****************************************************************************/ - if (data->ev.pulse != pulse) { - if (data->ev.duration != 0) { - ir_raw_event_store(data->dev, &data->ev); - data->ev.duration = 0; - } +static int +wbcir_txcarrier(struct rc_dev *dev, u32 carrier) +{ + struct wbcir_data *data = dev->priv; + unsigned long flags; + u8 val; + u32 freq; + + freq = DIV_ROUND_CLOSEST(carrier, 1000); + if (freq < 30 || freq > 60) + return -EINVAL; + + switch (freq) { + case 58: + case 59: + case 60: + val = freq - 58; + freq *= 1000; + break; + case 57: + val = freq - 27; + freq = 56900; + break; + default: + val = freq - 27; + freq *= 1000; + break; + } - data->ev.pulse = pulse; - } + spin_lock_irqsave(&data->spinlock, flags); + if (data->txstate != WBCIR_TXSTATE_INACTIVE) { + spin_unlock_irqrestore(&data->spinlock, flags); + return -EBUSY; + } - data->ev.duration += duration; + if (data->txcarrier != freq) { + wbcir_select_bank(data, WBCIR_BANK_7); + wbcir_set_bits(data->sbase + WBCIR_REG_SP3_IRTXMC, val, 0x1F); + data->txcarrier = freq; } - if (disable) { - if (data->ev.duration != 0 && !data->irdata_error) { - ir_raw_event_store(data->dev, &data->ev); - data->ev.duration = 0; - } + spin_unlock_irqrestore(&data->spinlock, flags); + return 0; +} - /* Set RXINACTIVE */ - outb(WBCIR_RX_DISABLE, data->sbase + WBCIR_REG_SP3_ASCR); +static int +wbcir_txmask(struct rc_dev *dev, u32 mask) +{ + struct wbcir_data *data = dev->priv; + unsigned long flags; + u8 val; - /* Drain the FIFO */ - while (inb(data->sbase + WBCIR_REG_SP3_LSR) & WBCIR_RX_AVAIL) - inb(data->sbase + WBCIR_REG_SP3_RXDATA); + /* Four outputs, only one output can be enabled at a time */ + switch (mask) { + case 0x1: + val = 0x0; + break; + case 0x2: + val = 0x1; + break; + case 0x4: + val = 0x2; + break; + case 0x8: + val = 0x3; + break; + default: + return -EINVAL; + } - ir_raw_event_reset(data->dev); - data->irdata_error = false; - data->irdata_active = false; - led_trigger_event(data->rxtrigger, LED_OFF); + spin_lock_irqsave(&data->spinlock, flags); + if (data->txstate != WBCIR_TXSTATE_INACTIVE) { + spin_unlock_irqrestore(&data->spinlock, flags); + return -EBUSY; } - ir_raw_event_handle(data->dev); + if (data->txmask != mask) { + wbcir_set_bits(data->ebase + WBCIR_REG_ECEIR_CTS, val, 0x0c); + data->txmask = mask; + } -out: spin_unlock_irqrestore(&data->spinlock, flags); - return IRQ_HANDLED; + return 0; } +static int +wbcir_tx(struct rc_dev *dev, int *buf, u32 bufsize) +{ + struct wbcir_data *data = dev->priv; + u32 count; + unsigned i; + unsigned long flags; + + /* bufsize has been sanity checked by the caller */ + count = bufsize / sizeof(int); + /* Not sure if this is possible, but better safe than sorry */ + spin_lock_irqsave(&data->spinlock, flags); + if (data->txstate != WBCIR_TXSTATE_INACTIVE) { + spin_unlock_irqrestore(&data->spinlock, flags); + return -EBUSY; + } + + /* Convert values to multiples of 10us */ + for (i = 0; i < count; i++) + buf[i] = DIV_ROUND_CLOSEST(buf[i], 10); + + /* Fill the TX fifo once, the irq handler will do the rest */ + data->txbuf = buf; + data->txlen = count; + data->txoff = 0; + wbcir_irq_tx(data); + + /* Wait for the TX to complete */ + while (data->txstate == WBCIR_TXSTATE_ACTIVE) { + spin_unlock_irqrestore(&data->spinlock, flags); + wait_event(data->txwaitq, data->txstate != WBCIR_TXSTATE_ACTIVE); + spin_lock_irqsave(&data->spinlock, flags); + } + + /* We're done */ + if (data->txstate == WBCIR_TXSTATE_ERROR) + count = -EAGAIN; + data->txstate = WBCIR_TXSTATE_INACTIVE; + data->txbuf = NULL; + spin_unlock_irqrestore(&data->spinlock, flags); + + return count; +} /***************************************************************************** * @@ -382,7 +632,7 @@ wbcir_shutdown(struct pnp_dev *device) { struct device *dev = &device->dev; struct wbcir_data *data = pnp_get_drvdata(device); - int do_wake = 1; + bool do_wake = true; u8 match[11]; u8 mask[11]; u8 rc6_csl = 0; @@ -392,14 +642,14 @@ wbcir_shutdown(struct pnp_dev *device) memset(mask, 0, sizeof(mask)); if (wake_sc == INVALID_SCANCODE || !device_may_wakeup(dev)) { - do_wake = 0; + do_wake = false; goto finish; } switch (protocol) { case IR_PROTOCOL_RC5: if (wake_sc > 0xFFF) { - do_wake = 0; + do_wake = false; dev_err(dev, "RC5 - Invalid wake scancode\n"); break; } @@ -418,7 +668,7 @@ wbcir_shutdown(struct pnp_dev *device) case IR_PROTOCOL_NEC: if (wake_sc > 0xFFFFFF) { - do_wake = 0; + do_wake = false; dev_err(dev, "NEC - Invalid wake scancode\n"); break; } @@ -440,7 +690,7 @@ wbcir_shutdown(struct pnp_dev *device) if (wake_rc6mode == 0) { if (wake_sc > 0xFFFF) { - do_wake = 0; + do_wake = false; dev_err(dev, "RC6 - Invalid wake scancode\n"); break; } @@ -496,7 +746,7 @@ wbcir_shutdown(struct pnp_dev *device) } else if (wake_sc <= 0x007FFFFF) { rc6_csl = 60; } else { - do_wake = 0; + do_wake = false; dev_err(dev, "RC6 - Invalid wake scancode\n"); break; } @@ -508,14 +758,14 @@ wbcir_shutdown(struct pnp_dev *device) mask[i++] = 0x0F; } else { - do_wake = 0; + do_wake = false; dev_err(dev, "RC6 - Invalid wake mode\n"); } break; default: - do_wake = 0; + do_wake = false; break; } @@ -551,21 +801,18 @@ finish: wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_CTL, 0x00, 0x01); } - /* Disable interrupts */ - wbcir_select_bank(data, WBCIR_BANK_0); - outb(WBCIR_IRQ_NONE, data->sbase + WBCIR_REG_SP3_IER); - - /* Disable LED */ - data->irdata_active = false; - led_trigger_event(data->rxtrigger, LED_OFF); - /* * ACPI will set the HW disable bit for SP3 which means that the * output signals are left in an undefined state which may cause * spurious interrupts which we need to ignore until the hardware * is reinitialized. */ + wbcir_set_irqmask(data, WBCIR_IRQ_NONE); disable_irq(data->irq); + + /* Disable LED */ + led_trigger_event(data->rxtrigger, LED_OFF); + led_trigger_event(data->txtrigger, LED_OFF); } static int @@ -581,8 +828,7 @@ wbcir_init_hw(struct wbcir_data *data) u8 tmp; /* Disable interrupts */ - wbcir_select_bank(data, WBCIR_BANK_0); - outb(WBCIR_IRQ_NONE, data->sbase + WBCIR_REG_SP3_IER); + wbcir_set_irqmask(data, WBCIR_IRQ_NONE); /* Set PROT_SEL, RX_INV, Clear CEIR_EN (needed for the led) */ tmp = protocol << 4; @@ -606,10 +852,11 @@ wbcir_init_hw(struct wbcir_data *data) outb(0x00, data->ebase + WBCIR_REG_ECEIR_CCTL); /* - * Clear IR LED, set SP3 clock to 24Mhz + * Clear IR LED, set SP3 clock to 24Mhz, set TX mask to IRTX1, * set SP3_IRRX_SW to binary 01, helpfully not documented */ outb(0x10, data->ebase + WBCIR_REG_ECEIR_CTS); + data->txmask = 0x1; /* Enable extended mode */ wbcir_select_bank(data, WBCIR_BANK_2); @@ -657,18 +904,21 @@ wbcir_init_hw(struct wbcir_data *data) wbcir_select_bank(data, WBCIR_BANK_4); outb(0x00, data->sbase + WBCIR_REG_SP3_IRCR1); - /* Enable MSR interrupt, Clear AUX_IRX */ + /* Disable MSR interrupt, clear AUX_IRX, mask RX during TX? */ wbcir_select_bank(data, WBCIR_BANK_5); - outb(0x00, data->sbase + WBCIR_REG_SP3_IRCR2); + outb(txandrx ? 0x03 : 0x02, data->sbase + WBCIR_REG_SP3_IRCR2); /* Disable CRC */ wbcir_select_bank(data, WBCIR_BANK_6); outb(0x20, data->sbase + WBCIR_REG_SP3_IRCR3); - /* Set RX/TX (de)modulation freq, not really used */ + /* Set RX demodulation freq, not really used */ wbcir_select_bank(data, WBCIR_BANK_7); outb(0xF2, data->sbase + WBCIR_REG_SP3_IRRXDC); + + /* Set TX modulation, 36kHz, 7us pulse width */ outb(0x69, data->sbase + WBCIR_REG_SP3_IRTXMC); + data->txcarrier = 36000; /* Set invert and pin direction */ if (invert) @@ -683,16 +933,23 @@ wbcir_init_hw(struct wbcir_data *data) /* Clear AUX status bits */ outb(0xE0, data->sbase + WBCIR_REG_SP3_ASCR); - /* Clear IR decoding state */ - data->irdata_active = false; - led_trigger_event(data->rxtrigger, LED_OFF); - data->irdata_error = false; - data->ev.duration = 0; + /* Clear RX state */ + data->rxstate = WBCIR_RXSTATE_INACTIVE; + data->rxev.duration = 0; ir_raw_event_reset(data->dev); ir_raw_event_handle(data->dev); + /* + * Check TX state, if we did a suspend/resume cycle while TX was + * active, we will have a process waiting in txwaitq. + */ + if (data->txstate == WBCIR_TXSTATE_ACTIVE) { + data->txstate = WBCIR_TXSTATE_ERROR; + wake_up(&data->txwaitq); + } + /* Enable interrupts */ - outb(WBCIR_IRQ_RX | WBCIR_IRQ_ERR, data->sbase + WBCIR_REG_SP3_IER); + wbcir_set_irqmask(data, WBCIR_IRQ_RX | WBCIR_IRQ_ERR); } static int @@ -729,6 +986,7 @@ wbcir_probe(struct pnp_dev *device, const struct pnp_device_id *dev_id) pnp_set_drvdata(device, data); spin_lock_init(&data->spinlock); + init_waitqueue_head(&data->txwaitq); data->ebase = pnp_port_start(device, 0); data->wbase = pnp_port_start(device, 1); data->sbase = pnp_port_start(device, 2); @@ -807,6 +1065,11 @@ wbcir_probe(struct pnp_dev *device, const struct pnp_device_id *dev_id) data->dev->input_id.vendor = PCI_VENDOR_ID_WINBOND; data->dev->input_id.product = WBCIR_ID_FAMILY; data->dev->input_id.version = WBCIR_ID_CHIP; + data->dev->map_name = RC_MAP_RC6_MCE; + data->dev->s_idle = wbcir_idle_rx; + data->dev->s_tx_mask = wbcir_txmask; + data->dev->s_tx_carrier = wbcir_txcarrier; + data->dev->tx_ir = wbcir_tx; data->dev->priv = data; data->dev->dev.parent = &device->dev; @@ -849,9 +1112,7 @@ wbcir_remove(struct pnp_dev *device) struct wbcir_data *data = pnp_get_drvdata(device); /* Disable interrupts */ - wbcir_select_bank(data, WBCIR_BANK_0); - outb(WBCIR_IRQ_NONE, data->sbase + WBCIR_REG_SP3_IER); - + wbcir_set_irqmask(data, WBCIR_IRQ_NONE); free_irq(data->irq, device); /* Clear status bits NEC_REP, BUFF, MSG_END, MATCH */ |