On Mon, Nov 21, 2016 at 02:28:08PM -0200, Mauro Carvalho Chehab wrote: > Em Fri, 11 Nov 2016 23:13:06 +0000 > Sean Young <sean@xxxxxxxx> escreveu: > > > Tested with a homebrew serial ir. Remove last remmants of the nslu2 > > which could not be enabled. > > Thanks for this! It is really nice to see any efforts on moving the drivers > under staging. Unfortunately some of the hardware is hard to track down. I've got my eyes on ebay, but if anyone can help me out with: - Sasem OnAir VFD/IR (Dign HV5 or D.Vine 5 HTPC IR/VFD) (lirc_sasem) - lirc parallel (lirc_parallel) - Original iMon SoundGraph IR receiver/display (lirc_imon) I would appreciate it and I can work on getting the drivers out of staging. > IMHO, it is almost ready for upstream merge, but I would prefer if you > could do all changes, in incremental patches, on the driver at > drivers/staging/media/lirc/lirc_serial.c, doing the module rename > (together with Kconfig/Makefile/MAINTAINERS change) on the last one. That is better, I've reworked it. > That makes my life a way easier, as I'll only need to review the final > patch, not caring about the intermediate ones (except for compilation test). New patch series ready to be sent and tested at each commit. > I have a few nitpicks here, plus one more serious issue: don't use > udelay() for very long times. See below. Thanks! > > > > > Signed-off-by: Sean Young <sean@xxxxxxxx> > > --- > > MAINTAINERS | 6 + > > drivers/media/rc/Kconfig | 17 + > > drivers/media/rc/Makefile | 1 + > > drivers/media/rc/serial_ir.c | 1003 ++++++++++++++++++++++++++ > > drivers/staging/media/lirc/Kconfig | 13 - > > drivers/staging/media/lirc/Makefile | 1 - > > drivers/staging/media/lirc/lirc_serial.c | 1130 ------------------------------ > > 7 files changed, 1027 insertions(+), 1144 deletions(-) > > create mode 100644 drivers/media/rc/serial_ir.c > > delete mode 100644 drivers/staging/media/lirc/lirc_serial.c > > > > diff --git a/MAINTAINERS b/MAINTAINERS > > index 93e9f42..cd8fd50 100644 > > --- a/MAINTAINERS > > +++ b/MAINTAINERS > > @@ -10604,6 +10604,12 @@ S: Maintained > > F: Documentation/devicetree/bindings/serial/ > > F: drivers/tty/serial/ > > > > +SERIAL IR RECEIVER > > +M: Sean Young <sean@xxxxxxxx> > > +L: linux-media@xxxxxxxxxxxxxxx > > +S: Maintained > > +F: drivers/media/rc/serial_ir.c > > + > > STI CEC DRIVER > > M: Benjamin Gaignard <benjamin.gaignard@xxxxxxxxxx> > > L: kernel@xxxxxxxxxxx > > diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig > > index 370e16e..629e8ca 100644 > > --- a/drivers/media/rc/Kconfig > > +++ b/drivers/media/rc/Kconfig > > @@ -389,4 +389,21 @@ config IR_SUNXI > > To compile this driver as a module, choose M here: the module will > > be called sunxi-ir. > > > > +config IR_SERIAL > > + tristate "Homebrew Serial Port Receiver" > > + depends on RC_CORE > > + ---help--- > > + Say Y if you want to use Homebrew Serial Port Receivers and > > + Transceivers. > > + > > + To compile this driver as a module, choose M here: the module will > > + be called serial-ir. > > + > > +config IR_SERIAL_TRANSMITTER > > + bool "Serial Port Transmitter" > > + default y > > + depends on IR_SERIAL > > + ---help--- > > + Serial Port Transmitter support > > + > > endif #RC_DEVICES > > diff --git a/drivers/media/rc/Makefile b/drivers/media/rc/Makefile > > index 379a5c0..3a984ee 100644 > > --- a/drivers/media/rc/Makefile > > +++ b/drivers/media/rc/Makefile > > @@ -37,3 +37,4 @@ obj-$(CONFIG_IR_TTUSBIR) += ttusbir.o > > obj-$(CONFIG_RC_ST) += st_rc.o > > obj-$(CONFIG_IR_SUNXI) += sunxi-cir.o > > obj-$(CONFIG_IR_IMG) += img-ir/ > > +obj-$(CONFIG_IR_SERIAL) += serial_ir.o > > diff --git a/drivers/media/rc/serial_ir.c b/drivers/media/rc/serial_ir.c > > new file mode 100644 > > index 0000000..378ae09 > > --- /dev/null > > +++ b/drivers/media/rc/serial_ir.c > > @@ -0,0 +1,1003 @@ > > +/* > > + * serial-ir.c > > + * > > + * serial-ir - Device driver that records pulse- and pause-lengths > > + * (space-lengths) between DDCD event on a serial port. > > + * > > + * Copyright (C) 1996,97 Ralph Metzler <rjkm@xxxxxxxxxxxxxxxx> > > + * Copyright (C) 1998 Trent Piepho <xyzzy@xxxxxxxxxxxxxxxx> > > + * Copyright (C) 1998 Ben Pfaff <blp@xxxxxxx> > > + * Copyright (C) 1999 Christoph Bartelmus <lirc@xxxxxxxxxxxx> > > + * Copyright (C) 2007 Andrei Tanas <andrei@xxxxxxxx> (suspend/resume support) > > + * Copyright (C) 2016 Sean Young <sean@xxxxxxxx> (port to rc-core) > > + * 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. > > + */ > > + > > +/* > > + * Steve's changes to improve transmission fidelity: > > + * - for systems with the rdtsc instruction and the clock counter, a > > + * send_pule that times the pulses directly using the counter. > > + * This means that the IR_SERIAL_TRANSMITTER_LATENCY fudge is > > + * not needed. Measurement shows very stable waveform, even where > > + * PCI activity slows the access to the UART, which trips up other > > + * versions. > > + * - For other system, non-integer-microsecond pulse/space lengths, > > + * done using fixed point binary. So, much more accurate carrier > > + * frequency. > > + * - fine tuned transmitter latency, taking advantage of fractional > > + * microseconds in previous change > > + * - Fixed bug in the way transmitter latency was accounted for by > > + * tuning the pulse lengths down - the send_pulse routine ignored > > + * this overhead as it timed the overall pulse length - so the > > + * pulse frequency was right but overall pulse length was too > > + * long. Fixed by accounting for latency on each pulse/space > > + * iteration. > > + * > > + * Steve Davies <steve@xxxxxxxxxxxxx> July 2001 > > + */ > > + > > +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt > > + > > +#include <linux/module.h> > > +#include <linux/errno.h> > > +#include <linux/interrupt.h> > > +#include <linux/kernel.h> > > +#include <linux/serial_reg.h> > > +#include <linux/types.h> > > +#include <linux/delay.h> > > +#include <linux/platform_device.h> > > +#include <linux/spinlock.h> > > +#include <media/rc-core.h> > > + > > +#define DRIVER_NAME "serial_ir" > > Why not use KBUILD_MODNAME instead? Good point. > > > + > > +struct serial_ir_hw { > > + int signal_pin; > > + int signal_pin_change; > > + u8 on; > > + u8 off; > > + unsigned set_send_carrier:1; > > + unsigned set_duty_cycle:1; > > + long (*send_pulse)(unsigned long length); > > + void (*send_space)(long length); > > + spinlock_t lock; > > +}; > > + > > +#define IR_HOMEBREW 0 > > +#define IR_IRDEO 1 > > +#define IR_IRDEO_REMOTE 2 > > +#define IR_ANIMAX 3 > > +#define IR_IGOR 4 > > + > > +/*** module parameters ***/ > > Nitpick: please use: > /* module parameters */ > > too fit at the Kernel's coding style. > > Also, this could cause kernel-doc to do something wrong, if we ever > add this file to Documentation. I'll fix it. > > +static int type; > > +static int io; > > +static int irq; > > +static bool iommap; > > +static int ioshift; > > +static bool softcarrier = true; > > +static bool share_irq; > > +static int sense = -1; /* -1 = auto, 0 = active high, 1 = active low */ > > +static bool txsense; /* 0 = active high, 1 = active low */ > > + > > +/* forward declarations */ > > +static long send_pulse_irdeo(unsigned long length); > > +static void send_space_irdeo(long length); > > +#ifdef CONFIG_IR_SERIAL_TRANSMITTER > > +static long send_pulse_homebrew(unsigned long length); > > +static void send_space_homebrew(long length); > > +#endif > > + > > +static struct serial_ir_hw hardware[] = { > > + [IR_HOMEBREW] = { > > + .lock = __SPIN_LOCK_UNLOCKED(hardware[IR_HOMEBREW].lock), > > + .signal_pin = UART_MSR_DCD, > > + .signal_pin_change = UART_MSR_DDCD, > > + .on = (UART_MCR_RTS | UART_MCR_OUT2 | UART_MCR_DTR), > > + .off = (UART_MCR_RTS | UART_MCR_OUT2), > > +#ifdef CONFIG_IR_SERIAL_TRANSMITTER > > + .send_pulse = send_pulse_homebrew, > > + .send_space = send_space_homebrew, > > + .set_send_carrier = true, > > + .set_duty_cycle = true, > > +#endif > > + }, > > + > > + [IR_IRDEO] = { > > + .lock = __SPIN_LOCK_UNLOCKED(hardware[IR_IRDEO].lock), > > + .signal_pin = UART_MSR_DSR, > > + .signal_pin_change = UART_MSR_DDSR, > > + .on = UART_MCR_OUT2, > > + .off = (UART_MCR_RTS | UART_MCR_DTR | UART_MCR_OUT2), > > + .send_pulse = send_pulse_irdeo, > > + .send_space = send_space_irdeo, > > + .set_duty_cycle = true, > > + }, > > + > > + [IR_IRDEO_REMOTE] = { > > + .lock = __SPIN_LOCK_UNLOCKED(hardware[IR_IRDEO_REMOTE].lock), > > + .signal_pin = UART_MSR_DSR, > > + .signal_pin_change = UART_MSR_DDSR, > > + .on = (UART_MCR_RTS | UART_MCR_DTR | UART_MCR_OUT2), > > + .off = (UART_MCR_RTS | UART_MCR_DTR | UART_MCR_OUT2), > > + .send_pulse = send_pulse_irdeo, > > + .send_space = send_space_irdeo, > > + .set_duty_cycle = true, > > + }, > > + > > + [IR_ANIMAX] = { > > + .lock = __SPIN_LOCK_UNLOCKED(hardware[IR_ANIMAX].lock), > > + .signal_pin = UART_MSR_DCD, > > + .signal_pin_change = UART_MSR_DDCD, > > + .on = 0, > > + .off = (UART_MCR_RTS | UART_MCR_DTR | UART_MCR_OUT2), > > + .send_pulse = NULL, > > + .send_space = NULL, > > + }, > > + > > + [IR_IGOR] = { > > + .lock = __SPIN_LOCK_UNLOCKED(hardware[IR_IGOR].lock), > > + .signal_pin = UART_MSR_DSR, > > + .signal_pin_change = UART_MSR_DDSR, > > + .on = (UART_MCR_RTS | UART_MCR_OUT2 | UART_MCR_DTR), > > + .off = (UART_MCR_RTS | UART_MCR_OUT2), > > +#ifdef CONFIG_IR_SERIAL_TRANSMITTER > > + .send_pulse = send_pulse_homebrew, > > + .send_space = send_space_homebrew, > > + .set_send_carrier = true, > > + .set_duty_cycle = true, > > +#endif > > + }, > > +}; > > + > > +#define RS_ISR_PASS_LIMIT 256 > > + > > +struct serial_ir { > > + ktime_t lastkt; > > + struct rc_dev *rcdev; > > + struct platform_device *pdev; > > + > > + unsigned int freq; > > + unsigned int duty_cycle; > > + > > + unsigned long period; > > + unsigned long pulse_width, space_width; > > +}; > > + > > +static struct serial_ir serial_ir; > > + > > +#if defined(__i386__) > > +/* > > + * From: > > + * Linux I/O port programming mini-HOWTO > > + * Author: Riku Saikkonen <Riku.Saikkonen@xxxxxx> > > + * v, 28 December 1997 > > + * > > + * [...] > > + * Actually, a port I/O instruction on most ports in the 0-0x3ff range > > + * takes almost exactly 1 microsecond, so if you're, for example, using > > + * the parallel port directly, just do additional inb()s from that port > > + * to delay. > > + * [...] > > + */ > > +/* transmitter latency 1.5625us 0x1.90 - this figure arrived at from > > + * comment above plus trimming to match actual measured frequency. > > + * This will be sensitive to cpu speed, though hopefully most of the 1.5us > > + * is spent in the uart access. Still - for reference test machine was a > > + * 1.13GHz Athlon system - Steve > > + */ > > + > > +/* > > + * changed from 400 to 450 as this works better on slower machines; > > + * faster machines will use the rdtsc code anyway > > + */ > > +#define IR_SERIAL_TRANSMITTER_LATENCY 450 > > + > > +#else > > + > > +/* does anybody have information on other platforms ? */ > > +/* 256 = 1<<8 */ > > +#define IR_SERIAL_TRANSMITTER_LATENCY 256 > > + > > +#endif /* __i386__ */ > > +/* > > + * FIXME: should we be using hrtimers instead of this > > + * IR_SERIAL_TRANSMITTER_LATENCY nonsense? > > + */ > > This all above is hacky, but you fixed on patch 2, so, I'm OK. In the next series this will be patched in staging as you suggested. > > + > > +/* fetch serial input packet (1 byte) from register offset */ > > +static u8 sinp(int offset) > > +{ > > + if (iommap) > > + /* the register is memory-mapped */ > > + offset <<= ioshift; > > + > > + return inb(io + offset); > > +} > > + > > +/* write serial output packet (1 byte) of value to register offset */ > > +static void soutp(int offset, u8 value) > > +{ > > + if (iommap) > > + /* the register is memory-mapped */ > > + offset <<= ioshift; > > + > > + outb(value, io + offset); > > +} > > + > > +static void on(void) > > +{ > > + if (txsense) > > + soutp(UART_MCR, hardware[type].off); > > + else > > + soutp(UART_MCR, hardware[type].on); > > +} > > + > > +static void off(void) > > +{ > > + if (txsense) > > + soutp(UART_MCR, hardware[type].on); > > + else > > + soutp(UART_MCR, hardware[type].off); > > +} > > + > > +#ifndef MAX_UDELAY_MS > > +#define MAX_UDELAY_US 5000 > > +#else > > +#define MAX_UDELAY_US (MAX_UDELAY_MS*1000) > > +#endif > > + > > +static void safe_udelay(unsigned long usecs) > > +{ > > + while (usecs > MAX_UDELAY_US) { > > + udelay(MAX_UDELAY_US); > > + usecs -= MAX_UDELAY_US; > > + } > > + udelay(usecs); > > +} > > That seems a hack to avoid warnings. Instead, use usleep_range() > when you need to wait for more than MAX_UDELAY_US, as the high > precision clock is OK when the delay is bigger than 5 ms. > > Actually, it could make sense to use usleep_range() when the > delay is bigger than 10 us, according with: > > Documentation/timers/timers-howto.txt Aha! I thought about using hrtimers, and kept that as "something to do in the future". Using usleep_range() is much nicer and simpler indeed. > > > + > > +#ifdef USE_RDTSC > > +/* > > + * This is an overflow/precision juggle, complicated in that we can't > > + * do long long divide in the kernel > > + */ > > + > > +/* > > + * When we use the rdtsc instruction to measure clocks, we keep the > > + * pulse and space widths as clock cycles. As this is CPU speed > > + * dependent, the widths must be calculated in init_port and ioctl > > + * time > > + */ > > + > > +static int init_timing_params(unsigned int new_duty_cycle, > > + unsigned int new_freq) > > +{ > > + __u64 loops_per_sec, work; > > + > > + serial_ir.duty_cycle = new_duty_cycle; > > + serial_ir.freq = new_freq; > > + > > + loops_per_sec = __this_cpu_read(cpu.info.loops_per_jiffy); > > + loops_per_sec *= HZ; > > + > > + /* How many clocks in a microsecond?, avoiding long long divide */ > > + work = loops_per_sec; > > + work *= 4295; /* 4295 = 2^32 / 1e6 */ > > + > > + /* > > + * Carrier period in clocks, approach good up to 32GHz clock, > > + * gets carrier frequency within 8Hz > > + */ > > + serial_ir.period = loops_per_sec >> 3; > > + serial_ir.pperiod /= (freq >> 3); > > + > > + /* Derive pulse and space from the period */ > > + serial_ir.ppulse_width = serial_ir.period * serial.ir.duty_cycle / 100; > > + serial_ir.pspace_width = serial_ir.period - serial_ir.pulse_width; > > + pr_debug("in init_timing_params, freq=%d, duty_cycle=%d, clk/jiffy=%ld, pulse=%ld, space=%ld, conv_us_to_clocks=%ld\n", > > + freq, duty_cycle, __this_cpu_read(cpu_info.loops_per_jiffy), > > + pulse_width, space_width, conv_us_to_clocks); > > + return 0; > > +} > > +#else /* ! USE_RDTSC */ > > +static int init_timing_params(unsigned int new_duty_cycle, > > + unsigned int new_freq) > > +{ > > +/* > > + * period, pulse/space width are kept with 8 binary places - > > + * IE multiplied by 256. > > + */ > > + if (256 * 1000000L / new_freq * new_duty_cycle / 100 <= > > + IR_SERIAL_TRANSMITTER_LATENCY) > > + return -EINVAL; > > + if (256 * 1000000L / new_freq * (100 - new_duty_cycle) / 100 <= > > + IR_SERIAL_TRANSMITTER_LATENCY) > > + return -EINVAL; > > + serial_ir.duty_cycle = new_duty_cycle; > > + serial_ir.freq = new_freq; > > + serial_ir.period = 256 * 1000000L / serial_ir.freq; > > + serial_ir.pulse_width = serial_ir.period * serial_ir.duty_cycle / 100; > > + serial_ir.space_width = serial_ir.period - serial_ir.pulse_width; > > + pr_debug("in init_timing_params, freq=%d pulse=%ld, space=%ld\n", > > + serial_ir.freq, serial_ir.pulse_width, > > + serial_ir.space_width); > > + return 0; > > +} > > +#endif /* USE_RDTSC */ > > + > > + > > +/* return value: space length delta */ > > + > > +static long send_pulse_irdeo(unsigned long length) > > +{ > > + long rawbits, ret; > > + int i; > > + unsigned char output; > > + unsigned char chunk, shifted; > > + > > + /* how many bits have to be sent ? */ > > + rawbits = length * 1152 / 10000; > > + if (serial_ir.duty_cycle > 50) > > + chunk = 3; > > + else > > + chunk = 1; > > + for (i = 0, output = 0x7f; rawbits > 0; rawbits -= 3) { > > + shifted = chunk << (i * 3); > > + shifted >>= 1; > > + output &= (~shifted); > > + i++; > > + if (i == 3) { > > + soutp(UART_TX, output); > > + while (!(sinp(UART_LSR) & UART_LSR_THRE)) > > + ; > > + output = 0x7f; > > + i = 0; > > + } > > + } > > + if (i != 0) { > > + soutp(UART_TX, output); > > + while (!(sinp(UART_LSR) & UART_LSR_TEMT)) > > + ; > > + } > > + > > + if (i == 0) > > + ret = (-rawbits) * 10000 / 1152; > > + else > > + ret = (3 - i) * 3 * 10000 / 1152 + (-rawbits) * 10000 / 1152; > > + > > + return ret; > > +} > > + > > +/* Version using udelay() */ > > + > > +/* > > + * here we use fixed point arithmetic, with 8 > > + * fractional bits. that gets us within 0.1% or so of the right average > > + * frequency, albeit with some jitter in pulse length - Steve > > + * > > + * This should use ndelay instead. > > + */ > > + > > +/* To match 8 fractional bits used for pulse/space length */ > > + > > +static void send_space_irdeo(long length) > > +{ > > + if (length <= 0) > > + return; > > + > > + safe_udelay(length); > > +} > > + > > +#ifdef CONFIG_IR_SERIAL_TRANSMITTER > > +static long send_pulse_homebrew_softcarrier(unsigned long length) > > +{ > > + int flag; > > + unsigned long actual, target, d; > > + > > + length <<= 8; > > + > > + actual = 0; target = 0; flag = 0; > > + while (actual < length) { > > + if (flag) { > > + off(); > > + target += serial_ir.space_width; > > + } else { > > + on(); > > + target += serial_ir.pulse_width; > > + } > > + d = (target - actual - > > + IR_SERIAL_TRANSMITTER_LATENCY + 128) >> 8; > > + /* > > + * Note - we've checked in ioctl that the pulse/space > > + * widths are big enough so that d is > 0 > > + */ > > + udelay(d); > > + actual += (d << 8) + IR_SERIAL_TRANSMITTER_LATENCY; > > + flag = !flag; > > + } > > + return (actual-length) >> 8; > > +} > > + > > +static long send_pulse_homebrew(unsigned long length) > > +{ > > + if (length <= 0) > > + return 0; > > + > > + if (softcarrier) > > + return send_pulse_homebrew_softcarrier(length); > > + > > + on(); > > + safe_udelay(length); > > + return 0; > > +} > > + > > +static void send_space_homebrew(long length) > > +{ > > + off(); > > + if (length <= 0) > > + return; > > + safe_udelay(length); > > +} > > +#endif > > + > > +static void frbwrite(unsigned int l, bool is_pulse) > > +{ > > + /* simple noise filter */ > > + static unsigned int ptr, pulse, space; > > + DEFINE_IR_RAW_EVENT(ev); > > + > > + if (ptr > 0 && is_pulse) { > > + pulse += l; > > + if (pulse > 250000) { > > + ev.duration = space; > > + ev.pulse = false; > > + ir_raw_event_store_with_filter(serial_ir.rcdev, &ev); > > + ev.duration = pulse; > > + ev.pulse = true; > > + ir_raw_event_store_with_filter(serial_ir.rcdev, &ev); > > + ptr = 0; > > + pulse = 0; > > + } > > + return; > > + } > > + if (!is_pulse) { > > + if (ptr == 0) { > > + if (l > 20000000) { > > + space = l; > > + ptr++; > > + return; > > + } > > + } else { > > + if (l > 20000000) { > > + space += pulse; > > + if (space > IR_MAX_DURATION) > > + space = IR_MAX_DURATION; > > + space += l; > > + if (space > IR_MAX_DURATION) > > + space = IR_MAX_DURATION; > > + pulse = 0; > > + return; > > + } > > + > > + ev.duration = space; > > + ev.pulse = false; > > + ir_raw_event_store_with_filter(serial_ir.rcdev, &ev); > > + ev.duration = pulse; > > + ev.pulse = true; > > + ir_raw_event_store_with_filter(serial_ir.rcdev, &ev); > > + ptr = 0; > > + pulse = 0; > > + } > > + } > > + > > + ev.duration = l; > > + ev.pulse = is_pulse; > > + ir_raw_event_store_with_filter(serial_ir.rcdev, &ev); > > +} > > + > > +static irqreturn_t serial_ir_irq_handler(int i, void *blah) > > +{ > > + ktime_t kt; > > + int counter, dcd; > > + u8 status; > > + ktime_t delkt; > > + unsigned int data; > > + static int last_dcd = -1; > > + > > + if ((sinp(UART_IIR) & UART_IIR_NO_INT)) { > > + /* not our interrupt */ > > + return IRQ_NONE; > > + } > > + > > + counter = 0; > > + do { > > + counter++; > > + status = sinp(UART_MSR); > > + if (counter > RS_ISR_PASS_LIMIT) { > > + dev_err(&serial_ir.pdev->dev, "Trapped in interrupt"); > > + break; > > + } > > + if ((status & hardware[type].signal_pin_change) > > + && sense != -1) { > > + /* get current time */ > > + kt = ktime_get(); > > + > > + /* > > + * The driver needs to know if your receiver is > > + * active high or active low, or the space/pulse > > + * sense could be inverted. > > + */ > > + > > + /* calc time since last interrupt in nanoseconds */ > > + dcd = (status & hardware[type].signal_pin) ? 1 : 0; > > + > > + if (dcd == last_dcd) { > > + dev_err(&serial_ir.pdev->dev, > > + "ignoring spike: %d %d %lldns %lldns\n", > > + dcd, sense, ktime_to_ns(kt), > > + ktime_to_ns(serial_ir.lastkt)); > > + continue; > > + } > > + > > + delkt = ktime_sub(kt, serial_ir.lastkt); > > + if (ktime_compare(delkt, ktime_set(15, 0)) > 0) { > > + data = IR_MAX_DURATION; /* really long time */ > > + if (!(dcd^sense)) { > > + /* sanity check */ > > + dev_err(&serial_ir.pdev->dev, > > + "dcd unexpected: %d %d %lldns %lldns\n", > > + dcd, sense, ktime_to_ns(kt), > > + ktime_to_ns(serial_ir.lastkt)); > > + /* > > + * detecting pulse while this > > + * MUST be a space! > > + */ > > + sense = sense ? 0 : 1; > > + } > > + } else > > + data = ktime_to_ns(delkt); > > + frbwrite(data, !(dcd^sense)); > > + serial_ir.lastkt = kt; > > + last_dcd = dcd; > > + ir_raw_event_handle(serial_ir.rcdev); > > + } > > + } while (!(sinp(UART_IIR) & UART_IIR_NO_INT)); /* still pending ? */ > > + return IRQ_HANDLED; > > +} > > + > > + > > +static int hardware_init_port(void) > > +{ > > + u8 scratch, scratch2, scratch3; > > + > > + /* > > + * This is a simple port existence test, borrowed from the autoconfig > > + * function in drivers/serial/8250.c > > + */ > > + scratch = sinp(UART_IER); > > + soutp(UART_IER, 0); > > +#ifdef __i386__ > > + outb(0xff, 0x080); > > +#endif > > + scratch2 = sinp(UART_IER) & 0x0f; > > + soutp(UART_IER, 0x0f); > > +#ifdef __i386__ > > + outb(0x00, 0x080); > > +#endif > > + scratch3 = sinp(UART_IER) & 0x0f; > > + soutp(UART_IER, scratch); > > + if (scratch2 != 0 || scratch3 != 0x0f) { > > + /* we fail, there's nothing here */ > > + pr_err("port existence test failed, cannot continue\n"); > > + return -ENODEV; > > + } > > + > > + /* Set DLAB 0. */ > > + soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB)); > > + > > + /* First of all, disable all interrupts */ > > + soutp(UART_IER, sinp(UART_IER) & > > + (~(UART_IER_MSI|UART_IER_RLSI|UART_IER_THRI|UART_IER_RDI))); > > + > > + /* Clear registers. */ > > + sinp(UART_LSR); > > + sinp(UART_RX); > > + sinp(UART_IIR); > > + sinp(UART_MSR); > > + > > + /* Set line for power source */ > > + off(); > > + > > + /* Clear registers again to be sure. */ > > + sinp(UART_LSR); > > + sinp(UART_RX); > > + sinp(UART_IIR); > > + sinp(UART_MSR); > > + > > + switch (type) { > > + case IR_IRDEO: > > + case IR_IRDEO_REMOTE: > > + /* setup port to 7N1 @ 115200 Baud */ > > + /* 7N1+start = 9 bits at 115200 ~ 3 bits at 38kHz */ > > + > > + /* Set DLAB 1. */ > > + soutp(UART_LCR, sinp(UART_LCR) | UART_LCR_DLAB); > > + /* Set divisor to 1 => 115200 Baud */ > > + soutp(UART_DLM, 0); > > + soutp(UART_DLL, 1); > > + /* Set DLAB 0 + 7N1 */ > > + soutp(UART_LCR, UART_LCR_WLEN7); > > + /* THR interrupt already disabled at this point */ > > + break; > > + default: > > + break; > > + } > > + > > + return 0; > > +} > > + > > +static int serial_ir_probe(struct platform_device *dev) > > +{ > > + int i, nlow, nhigh, result; > > + > > + result = devm_request_irq(&dev->dev, irq, serial_ir_irq_handler, > > + (share_irq ? IRQF_SHARED : 0), > > + DRIVER_NAME, &hardware); > > + if (result < 0) { > > + if (result == -EBUSY) > > + dev_err(&dev->dev, "IRQ %d busy\n", irq); > > + else if (result == -EINVAL) > > + dev_err(&dev->dev, "Bad irq number or handler\n"); > > + return result; > > + } > > + > > + /* Reserve io region. */ > > + if (((iommap) > > + && (devm_request_mem_region(&dev->dev, iommap, 8 << ioshift, > > + DRIVER_NAME) == NULL)) > > + || ((!iommap) > > + && (devm_request_region(&dev->dev, io, 8, > > + DRIVER_NAME) == NULL))) { > > + dev_err(&dev->dev, "port %04x already in use\n", io); > > + dev_warn(&dev->dev, "use 'setserial /dev/ttySX uart none'\n"); > > + dev_warn(&dev->dev, > > + "or compile the serial port driver as module and\n"); > > + dev_warn(&dev->dev, "make sure this module is loaded first\n"); > > + return -EBUSY; > > + } > > + > > + result = hardware_init_port(); > > + if (result < 0) > > + return result; > > + > > + /* Initialize pulse/space widths */ > > + init_timing_params(50, 38000); > > + > > + /* If pin is high, then this must be an active low receiver. */ > > + if (sense == -1) { > > + /* wait 1/2 sec for the power supply */ > > + msleep(500); > > + > > + /* > > + * probe 9 times every 0.04s, collect "votes" for > > + * active high/low > > + */ > > + nlow = 0; > > + nhigh = 0; > > + for (i = 0; i < 9; i++) { > > + if (sinp(UART_MSR) & hardware[type].signal_pin) > > + nlow++; > > + else > > + nhigh++; > > + msleep(40); > > + } > > + sense = nlow >= nhigh ? 1 : 0; > > + dev_info(&dev->dev, "auto-detected active %s receiver\n", > > + sense ? "low" : "high"); > > + } else > > + dev_info(&dev->dev, "Manually using active %s receiver\n", > > + sense ? "low" : "high"); > > + > > + dev_dbg(&dev->dev, "Interrupt %d, port %04x obtained\n", irq, io); > > + return 0; > > +} > > + > > +static int serial_ir_open(struct rc_dev *rcdev) > > +{ > > + unsigned long flags; > > + > > + /* initialize timestamp */ > > + serial_ir.lastkt = ktime_get(); > > + > > + spin_lock_irqsave(&hardware[type].lock, flags); > > + > > + /* Set DLAB 0. */ > > + soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB)); > > + > > + soutp(UART_IER, sinp(UART_IER)|UART_IER_MSI); > > + > > + spin_unlock_irqrestore(&hardware[type].lock, flags); > > + > > + return 0; > > +} > > + > > +static void serial_ir_close(struct rc_dev *rcdev) > > +{ > > + unsigned long flags; > > + > > + spin_lock_irqsave(&hardware[type].lock, flags); > > + > > + /* Set DLAB 0. */ > > + soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB)); > > + > > + /* First of all, disable all interrupts */ > > + soutp(UART_IER, sinp(UART_IER) & > > + (~(UART_IER_MSI|UART_IER_RLSI|UART_IER_THRI|UART_IER_RDI))); > > + spin_unlock_irqrestore(&hardware[type].lock, flags); > > +} > > + > > +static int serial_ir_tx(struct rc_dev *dev, unsigned int *txbuf, > > + unsigned int count) > > +{ > > + unsigned long flags; > > + long delta = 0; > > + int i; > > + > > + spin_lock_irqsave(&hardware[type].lock, flags); > > + if (type == IR_IRDEO) { > > + /* DTR, RTS down */ > > + on(); > > + } > > + for (i = 0; i < count; i++) { > > + if (i%2) > > + hardware[type].send_space(txbuf[i] - delta); > > + else > > + delta = hardware[type].send_pulse(txbuf[i]); > > + } > > + off(); > > + spin_unlock_irqrestore(&hardware[type].lock, flags); > > + return count; > > +} > > + > > +static int serial_ir_tx_duty_cycle(struct rc_dev *dev, u32 cycle) > > +{ > > + return init_timing_params(cycle, serial_ir.freq); > > +} > > + > > +static int serial_ir_tx_carrier(struct rc_dev *dev, u32 carrier) > > +{ > > + if (carrier > 500000 || carrier < 20000) > > + return -EINVAL; > > + > > + return init_timing_params(serial_ir.duty_cycle, carrier); > > +} > > + > > +static int serial_ir_suspend(struct platform_device *dev, > > + pm_message_t state) > > +{ > > + /* Set DLAB 0. */ > > + soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB)); > > + > > + /* Disable all interrupts */ > > + soutp(UART_IER, sinp(UART_IER) & > > + (~(UART_IER_MSI|UART_IER_RLSI|UART_IER_THRI|UART_IER_RDI))); > > + > > + /* Clear registers. */ > > + sinp(UART_LSR); > > + sinp(UART_RX); > > + sinp(UART_IIR); > > + sinp(UART_MSR); > > + > > + return 0; > > +} > > + > > +static int serial_ir_resume(struct platform_device *dev) > > +{ > > + unsigned long flags; > > + int result; > > + > > + result = hardware_init_port(); > > + if (result < 0) > > + return result; > > + > > + spin_lock_irqsave(&hardware[type].lock, flags); > > + /* Enable Interrupt */ > > + serial_ir.lastkt = ktime_get(); > > + soutp(UART_IER, sinp(UART_IER)|UART_IER_MSI); > > + off(); > > + > > + spin_unlock_irqrestore(&hardware[type].lock, flags); > > + > > + return 0; > > +} > > + > > +static struct platform_driver serial_ir_driver = { > > + .probe = serial_ir_probe, > > + .suspend = serial_ir_suspend, > > + .resume = serial_ir_resume, > > + .driver = { > > + .name = "serial_ir", > > + }, > > +}; > > + > > +static int __init serial_ir_init(void) > > +{ > > + int result; > > + > > + result = platform_driver_register(&serial_ir_driver); > > + if (result) > > + return result; > > + > > + serial_ir.pdev = platform_device_alloc("serial_ir", 0); > > + if (!serial_ir.pdev) { > > + result = -ENOMEM; > > + goto exit_driver_unregister; > > + } > > + > > + result = platform_device_add(serial_ir.pdev); > > + if (result) > > + goto exit_device_put; > > + > > + return 0; > > + > > +exit_device_put: > > + platform_device_put(serial_ir.pdev); > > +exit_driver_unregister: > > + platform_driver_unregister(&serial_ir_driver); > > + return result; > > +} > > + > > +static void serial_ir_exit(void) > > +{ > > + platform_device_unregister(serial_ir.pdev); > > + platform_driver_unregister(&serial_ir_driver); > > +} > > + > > +static int __init serial_ir_init_module(void) > > +{ > > + struct rc_dev *rcdev; > > + int result; > > + > > + switch (type) { > > + case IR_HOMEBREW: > > + case IR_IRDEO: > > + case IR_IRDEO_REMOTE: > > + case IR_ANIMAX: > > + case IR_IGOR: > > + /* if nothing specified, use ttyS0/com1 and irq 4 */ > > + io = io ? io : 0x3f8; > > + irq = irq ? irq : 4; > > + break; > > + default: > > + return -EINVAL; > > + } > > + if (!softcarrier) { > > + switch (type) { > > + case IR_HOMEBREW: > > + case IR_IGOR: > > + hardware[type].set_send_carrier = false; > > + hardware[type].set_duty_cycle = false; > > + break; > > + } > > + } > > + > > + /* make sure sense is either -1, 0, or 1 */ > > + if (sense != -1) > > + sense = !!sense; > > + > > + result = serial_ir_init(); > > + if (result) > > + return result; > > + > > + rcdev = devm_rc_allocate_device(&serial_ir.pdev->dev); > > + if (!rcdev) { > > + result = -ENOMEM; > > + goto serial_cleanup; > > + } > > + > > + if (hardware[type].send_pulse && hardware[type].send_space) > > + rcdev->tx_ir = serial_ir_tx; > > + if (hardware[type].set_send_carrier) > > + rcdev->s_tx_carrier = serial_ir_tx_carrier; > > + if (hardware[type].set_duty_cycle) > > + rcdev->s_tx_duty_cycle = serial_ir_tx_duty_cycle; > > + > > + switch (type) { > > + case IR_HOMEBREW: > > + rcdev->input_name = "Serial IR type home-brew"; > > + break; > > + case IR_IRDEO: > > + rcdev->input_name = "Serial IR type IRdeo"; > > + break; > > + case IR_IRDEO_REMOTE: > > + rcdev->input_name = "Serial IR type IRdeo remote"; > > + break; > > + case IR_ANIMAX: > > + rcdev->input_name = "Serial IR type AnimaX"; > > + break; > > + case IR_IGOR: > > + rcdev->input_name = "Serial IR type IgorPlug"; > > + break; > > + } > > + > > + rcdev->input_phys = DRIVER_NAME "/input0"; > > + rcdev->input_id.bustype = BUS_HOST; > > + rcdev->input_id.vendor = 0x0001; > > + rcdev->input_id.product = 0x0001; > > + rcdev->input_id.version = 0x0100; > > + rcdev->open = serial_ir_open; > > + rcdev->close = serial_ir_close; > > + rcdev->dev.parent = &serial_ir.pdev->dev; > > + rcdev->driver_type = RC_DRIVER_IR_RAW; > > + rcdev->allowed_protocols = RC_BIT_ALL; > > + rcdev->driver_name = DRIVER_NAME; > > + rcdev->map_name = RC_MAP_RC6_MCE; > > + rcdev->timeout = IR_DEFAULT_TIMEOUT; > > + rcdev->rx_resolution = 250000; > > + > > + serial_ir.rcdev = rcdev; > > + > > + result = rc_register_device(rcdev); > > + > > + if (!result) > > + return 0; > > +serial_cleanup: > > + serial_ir_exit(); > > + return result; > > +} > > + > > +static void __exit serial_ir_exit_module(void) > > +{ > > + rc_unregister_device(serial_ir.rcdev); > > + serial_ir_exit(); > > +} > > + > > +module_init(serial_ir_init_module); > > +module_exit(serial_ir_exit_module); > > + > > +MODULE_DESCRIPTION("Infra-red receiver driver for serial ports."); > > +MODULE_AUTHOR("Ralph Metzler, Trent Piepho, Ben Pfaff, Christoph Bartelmus, Andrei Tanas"); > > +MODULE_LICENSE("GPL"); > > + > > +module_param(type, int, 0444); > > +MODULE_PARM_DESC(type, "Hardware type (0 = home-brew, 1 = IRdeo, 2 = IRdeo Remote, 3 = AnimaX, 4 = IgorPlug"); > > + > > +module_param(io, int, 0444); > > +MODULE_PARM_DESC(io, "I/O address base (0x3f8 or 0x2f8)"); > > + > > +/* some architectures (e.g. intel xscale) have memory mapped registers */ > > +module_param(iommap, bool, 0444); > > +MODULE_PARM_DESC(iommap, "physical base for memory mapped I/O (0 = no memory mapped io)"); > > + > > +/* > > + * some architectures (e.g. intel xscale) align the 8bit serial registers > > + * on 32bit word boundaries. > > + * See linux-kernel/drivers/tty/serial/8250/8250.c serial_in()/out() > > + */ > > +module_param(ioshift, int, 0444); > > +MODULE_PARM_DESC(ioshift, "shift I/O register offset (0 = no shift)"); > > + > > +module_param(irq, int, 0444); > > +MODULE_PARM_DESC(irq, "Interrupt (4 or 3)"); > > + > > +module_param(share_irq, bool, 0444); > > +MODULE_PARM_DESC(share_irq, "Share interrupts (0 = off, 1 = on)"); > > + > > +module_param(sense, int, 0444); > > +MODULE_PARM_DESC(sense, "Override autodetection of IR receiver circuit (0 = active high, 1 = active low )"); > > + > > +#ifdef CONFIG_IR_SERIAL_TRANSMITTER > > +module_param(txsense, bool, 0444); > > +MODULE_PARM_DESC(txsense, "Sense of transmitter circuit (0 = active high, 1 = active low )"); > > +#endif > > + > > +module_param(softcarrier, bool, 0444); > > +MODULE_PARM_DESC(softcarrier, "Software carrier (0 = off, 1 = on, default on)"); > > diff --git a/drivers/staging/media/lirc/Kconfig b/drivers/staging/media/lirc/Kconfig > > index 6879c46..25b7e7c 100644 > > --- a/drivers/staging/media/lirc/Kconfig > > +++ b/drivers/staging/media/lirc/Kconfig > > @@ -38,19 +38,6 @@ config LIRC_SASEM > > help > > Driver for the Sasem OnAir Remocon-V or Dign HV5 HTPC IR/VFD Module > > > > -config LIRC_SERIAL > > - tristate "Homebrew Serial Port Receiver" > > - depends on LIRC > > - help > > - Driver for Homebrew Serial Port Receivers > > - > > -config LIRC_SERIAL_TRANSMITTER > > - bool "Serial Port Transmitter" > > - default y > > - depends on LIRC_SERIAL > > - help > > - Serial Port Transmitter support > > - > > config LIRC_SIR > > tristate "Built-in SIR IrDA port" > > depends on LIRC > > diff --git a/drivers/staging/media/lirc/Makefile b/drivers/staging/media/lirc/Makefile > > index 5430adf..7f919ea 100644 > > --- a/drivers/staging/media/lirc/Makefile > > +++ b/drivers/staging/media/lirc/Makefile > > @@ -7,6 +7,5 @@ obj-$(CONFIG_LIRC_BT829) += lirc_bt829.o > > obj-$(CONFIG_LIRC_IMON) += lirc_imon.o > > obj-$(CONFIG_LIRC_PARALLEL) += lirc_parallel.o > > obj-$(CONFIG_LIRC_SASEM) += lirc_sasem.o > > -obj-$(CONFIG_LIRC_SERIAL) += lirc_serial.o > > obj-$(CONFIG_LIRC_SIR) += lirc_sir.o > > obj-$(CONFIG_LIRC_ZILOG) += lirc_zilog.o > > diff --git a/drivers/staging/media/lirc/lirc_serial.c b/drivers/staging/media/lirc/lirc_serial.c > > deleted file mode 100644 > > index b798b31..0000000 > > --- a/drivers/staging/media/lirc/lirc_serial.c > > +++ /dev/null > > @@ -1,1130 +0,0 @@ > > -/* > > - * lirc_serial.c > > - * > > - * lirc_serial - Device driver that records pulse- and pause-lengths > > - * (space-lengths) between DDCD event on a serial port. > > - * > > - * Copyright (C) 1996,97 Ralph Metzler <rjkm@xxxxxxxxxxxxxxxx> > > - * Copyright (C) 1998 Trent Piepho <xyzzy@xxxxxxxxxxxxxxxx> > > - * Copyright (C) 1998 Ben Pfaff <blp@xxxxxxx> > > - * Copyright (C) 1999 Christoph Bartelmus <lirc@xxxxxxxxxxxx> > > - * Copyright (C) 2007 Andrei Tanas <andrei@xxxxxxxx> (suspend/resume support) > > - * 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 > > - * > > - */ > > - > > -/* > > - * Steve's changes to improve transmission fidelity: > > - * - for systems with the rdtsc instruction and the clock counter, a > > - * send_pule that times the pulses directly using the counter. > > - * This means that the LIRC_SERIAL_TRANSMITTER_LATENCY fudge is > > - * not needed. Measurement shows very stable waveform, even where > > - * PCI activity slows the access to the UART, which trips up other > > - * versions. > > - * - For other system, non-integer-microsecond pulse/space lengths, > > - * done using fixed point binary. So, much more accurate carrier > > - * frequency. > > - * - fine tuned transmitter latency, taking advantage of fractional > > - * microseconds in previous change > > - * - Fixed bug in the way transmitter latency was accounted for by > > - * tuning the pulse lengths down - the send_pulse routine ignored > > - * this overhead as it timed the overall pulse length - so the > > - * pulse frequency was right but overall pulse length was too > > - * long. Fixed by accounting for latency on each pulse/space > > - * iteration. > > - * > > - * Steve Davies <steve@xxxxxxxxxxxxx> July 2001 > > - */ > > - > > -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt > > - > > -#include <linux/module.h> > > -#include <linux/errno.h> > > -#include <linux/signal.h> > > -#include <linux/sched.h> > > -#include <linux/fs.h> > > -#include <linux/interrupt.h> > > -#include <linux/ioport.h> > > -#include <linux/kernel.h> > > -#include <linux/serial_reg.h> > > -#include <linux/ktime.h> > > -#include <linux/string.h> > > -#include <linux/types.h> > > -#include <linux/wait.h> > > -#include <linux/mm.h> > > -#include <linux/delay.h> > > -#include <linux/poll.h> > > -#include <linux/platform_device.h> > > -#include <linux/gpio.h> > > -#include <linux/io.h> > > -#include <linux/irq.h> > > -#include <linux/fcntl.h> > > -#include <linux/spinlock.h> > > - > > -/* From Intel IXP42X Developer's Manual (#252480-005): */ > > -/* ftp://download.intel.com/design/network/manuals/25248005.pdf */ > > -#define UART_IE_IXP42X_UUE 0x40 /* IXP42X UART Unit enable */ > > -#define UART_IE_IXP42X_RTOIE 0x10 /* IXP42X Receiver Data Timeout int.enable */ > > - > > -#include <media/lirc.h> > > -#include <media/lirc_dev.h> > > - > > -#define LIRC_DRIVER_NAME "lirc_serial" > > - > > -struct lirc_serial { > > - int signal_pin; > > - int signal_pin_change; > > - u8 on; > > - u8 off; > > - long (*send_pulse)(unsigned long length); > > - void (*send_space)(long length); > > - int features; > > - spinlock_t lock; > > -}; > > - > > -#define LIRC_HOMEBREW 0 > > -#define LIRC_IRDEO 1 > > -#define LIRC_IRDEO_REMOTE 2 > > -#define LIRC_ANIMAX 3 > > -#define LIRC_IGOR 4 > > -#define LIRC_NSLU2 5 > > - > > -/*** module parameters ***/ > > -static int type; > > -static int io; > > -static int irq; > > -static bool iommap; > > -static int ioshift; > > -static bool softcarrier = true; > > -static bool share_irq; > > -static int sense = -1; /* -1 = auto, 0 = active high, 1 = active low */ > > -static bool txsense; /* 0 = active high, 1 = active low */ > > - > > -/* forward declarations */ > > -static long send_pulse_irdeo(unsigned long length); > > -static long send_pulse_homebrew(unsigned long length); > > -static void send_space_irdeo(long length); > > -static void send_space_homebrew(long length); > > - > > -static struct lirc_serial hardware[] = { > > - [LIRC_HOMEBREW] = { > > - .lock = __SPIN_LOCK_UNLOCKED(hardware[LIRC_HOMEBREW].lock), > > - .signal_pin = UART_MSR_DCD, > > - .signal_pin_change = UART_MSR_DDCD, > > - .on = (UART_MCR_RTS | UART_MCR_OUT2 | UART_MCR_DTR), > > - .off = (UART_MCR_RTS | UART_MCR_OUT2), > > - .send_pulse = send_pulse_homebrew, > > - .send_space = send_space_homebrew, > > -#ifdef CONFIG_LIRC_SERIAL_TRANSMITTER > > - .features = (LIRC_CAN_SET_SEND_DUTY_CYCLE | > > - LIRC_CAN_SET_SEND_CARRIER | > > - LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2) > > -#else > > - .features = LIRC_CAN_REC_MODE2 > > -#endif > > - }, > > - > > - [LIRC_IRDEO] = { > > - .lock = __SPIN_LOCK_UNLOCKED(hardware[LIRC_IRDEO].lock), > > - .signal_pin = UART_MSR_DSR, > > - .signal_pin_change = UART_MSR_DDSR, > > - .on = UART_MCR_OUT2, > > - .off = (UART_MCR_RTS | UART_MCR_DTR | UART_MCR_OUT2), > > - .send_pulse = send_pulse_irdeo, > > - .send_space = send_space_irdeo, > > - .features = (LIRC_CAN_SET_SEND_DUTY_CYCLE | > > - LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2) > > - }, > > - > > - [LIRC_IRDEO_REMOTE] = { > > - .lock = __SPIN_LOCK_UNLOCKED(hardware[LIRC_IRDEO_REMOTE].lock), > > - .signal_pin = UART_MSR_DSR, > > - .signal_pin_change = UART_MSR_DDSR, > > - .on = (UART_MCR_RTS | UART_MCR_DTR | UART_MCR_OUT2), > > - .off = (UART_MCR_RTS | UART_MCR_DTR | UART_MCR_OUT2), > > - .send_pulse = send_pulse_irdeo, > > - .send_space = send_space_irdeo, > > - .features = (LIRC_CAN_SET_SEND_DUTY_CYCLE | > > - LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2) > > - }, > > - > > - [LIRC_ANIMAX] = { > > - .lock = __SPIN_LOCK_UNLOCKED(hardware[LIRC_ANIMAX].lock), > > - .signal_pin = UART_MSR_DCD, > > - .signal_pin_change = UART_MSR_DDCD, > > - .on = 0, > > - .off = (UART_MCR_RTS | UART_MCR_DTR | UART_MCR_OUT2), > > - .send_pulse = NULL, > > - .send_space = NULL, > > - .features = LIRC_CAN_REC_MODE2 > > - }, > > - > > - [LIRC_IGOR] = { > > - .lock = __SPIN_LOCK_UNLOCKED(hardware[LIRC_IGOR].lock), > > - .signal_pin = UART_MSR_DSR, > > - .signal_pin_change = UART_MSR_DDSR, > > - .on = (UART_MCR_RTS | UART_MCR_OUT2 | UART_MCR_DTR), > > - .off = (UART_MCR_RTS | UART_MCR_OUT2), > > - .send_pulse = send_pulse_homebrew, > > - .send_space = send_space_homebrew, > > -#ifdef CONFIG_LIRC_SERIAL_TRANSMITTER > > - .features = (LIRC_CAN_SET_SEND_DUTY_CYCLE | > > - LIRC_CAN_SET_SEND_CARRIER | > > - LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2) > > -#else > > - .features = LIRC_CAN_REC_MODE2 > > -#endif > > - }, > > -}; > > - > > -#define RS_ISR_PASS_LIMIT 256 > > - > > -/* > > - * A long pulse code from a remote might take up to 300 bytes. The > > - * daemon should read the bytes as soon as they are generated, so take > > - * the number of keys you think you can push before the daemon runs > > - * and multiply by 300. The driver will warn you if you overrun this > > - * buffer. If you have a slow computer or non-busmastering IDE disks, > > - * maybe you will need to increase this. > > - */ > > - > > -/* This MUST be a power of two! It has to be larger than 1 as well. */ > > - > > -#define RBUF_LEN 256 > > - > > -static ktime_t lastkt; > > - > > -static struct lirc_buffer rbuf; > > - > > -static unsigned int freq = 38000; > > -static unsigned int duty_cycle = 50; > > - > > -/* Initialized in init_timing_params() */ > > -static unsigned long period; > > -static unsigned long pulse_width; > > -static unsigned long space_width; > > - > > -#if defined(__i386__) > > -/* > > - * From: > > - * Linux I/O port programming mini-HOWTO > > - * Author: Riku Saikkonen <Riku.Saikkonen@xxxxxx> > > - * v, 28 December 1997 > > - * > > - * [...] > > - * Actually, a port I/O instruction on most ports in the 0-0x3ff range > > - * takes almost exactly 1 microsecond, so if you're, for example, using > > - * the parallel port directly, just do additional inb()s from that port > > - * to delay. > > - * [...] > > - */ > > -/* transmitter latency 1.5625us 0x1.90 - this figure arrived at from > > - * comment above plus trimming to match actual measured frequency. > > - * This will be sensitive to cpu speed, though hopefully most of the 1.5us > > - * is spent in the uart access. Still - for reference test machine was a > > - * 1.13GHz Athlon system - Steve > > - */ > > - > > -/* > > - * changed from 400 to 450 as this works better on slower machines; > > - * faster machines will use the rdtsc code anyway > > - */ > > -#define LIRC_SERIAL_TRANSMITTER_LATENCY 450 > > - > > -#else > > - > > -/* does anybody have information on other platforms ? */ > > -/* 256 = 1<<8 */ > > -#define LIRC_SERIAL_TRANSMITTER_LATENCY 256 > > - > > -#endif /* __i386__ */ > > -/* > > - * FIXME: should we be using hrtimers instead of this > > - * LIRC_SERIAL_TRANSMITTER_LATENCY nonsense? > > - */ > > - > > -/* fetch serial input packet (1 byte) from register offset */ > > -static u8 sinp(int offset) > > -{ > > - if (iommap) > > - /* the register is memory-mapped */ > > - offset <<= ioshift; > > - > > - return inb(io + offset); > > -} > > - > > -/* write serial output packet (1 byte) of value to register offset */ > > -static void soutp(int offset, u8 value) > > -{ > > - if (iommap) > > - /* the register is memory-mapped */ > > - offset <<= ioshift; > > - > > - outb(value, io + offset); > > -} > > - > > -static void on(void) > > -{ > > - if (txsense) > > - soutp(UART_MCR, hardware[type].off); > > - else > > - soutp(UART_MCR, hardware[type].on); > > -} > > - > > -static void off(void) > > -{ > > - if (txsense) > > - soutp(UART_MCR, hardware[type].on); > > - else > > - soutp(UART_MCR, hardware[type].off); > > -} > > - > > -#ifndef MAX_UDELAY_MS > > -#define MAX_UDELAY_US 5000 > > -#else > > -#define MAX_UDELAY_US (MAX_UDELAY_MS*1000) > > -#endif > > - > > -static void safe_udelay(unsigned long usecs) > > -{ > > - while (usecs > MAX_UDELAY_US) { > > - udelay(MAX_UDELAY_US); > > - usecs -= MAX_UDELAY_US; > > - } > > - udelay(usecs); > > -} > > - > > -#ifdef USE_RDTSC > > -/* > > - * This is an overflow/precision juggle, complicated in that we can't > > - * do long long divide in the kernel > > - */ > > - > > -/* > > - * When we use the rdtsc instruction to measure clocks, we keep the > > - * pulse and space widths as clock cycles. As this is CPU speed > > - * dependent, the widths must be calculated in init_port and ioctl > > - * time > > - */ > > - > > -static int init_timing_params(unsigned int new_duty_cycle, > > - unsigned int new_freq) > > -{ > > - __u64 loops_per_sec, work; > > - > > - duty_cycle = new_duty_cycle; > > - freq = new_freq; > > - > > - loops_per_sec = __this_cpu_read(cpu.info.loops_per_jiffy); > > - loops_per_sec *= HZ; > > - > > - /* How many clocks in a microsecond?, avoiding long long divide */ > > - work = loops_per_sec; > > - work *= 4295; /* 4295 = 2^32 / 1e6 */ > > - > > - /* > > - * Carrier period in clocks, approach good up to 32GHz clock, > > - * gets carrier frequency within 8Hz > > - */ > > - period = loops_per_sec >> 3; > > - period /= (freq >> 3); > > - > > - /* Derive pulse and space from the period */ > > - pulse_width = period * duty_cycle / 100; > > - space_width = period - pulse_width; > > - pr_debug("in init_timing_params, freq=%d, duty_cycle=%d, clk/jiffy=%ld, pulse=%ld, space=%ld, conv_us_to_clocks=%ld\n", > > - freq, duty_cycle, __this_cpu_read(cpu_info.loops_per_jiffy), > > - pulse_width, space_width, conv_us_to_clocks); > > - return 0; > > -} > > -#else /* ! USE_RDTSC */ > > -static int init_timing_params(unsigned int new_duty_cycle, > > - unsigned int new_freq) > > -{ > > -/* > > - * period, pulse/space width are kept with 8 binary places - > > - * IE multiplied by 256. > > - */ > > - if (256 * 1000000L / new_freq * new_duty_cycle / 100 <= > > - LIRC_SERIAL_TRANSMITTER_LATENCY) > > - return -EINVAL; > > - if (256 * 1000000L / new_freq * (100 - new_duty_cycle) / 100 <= > > - LIRC_SERIAL_TRANSMITTER_LATENCY) > > - return -EINVAL; > > - duty_cycle = new_duty_cycle; > > - freq = new_freq; > > - period = 256 * 1000000L / freq; > > - pulse_width = period * duty_cycle / 100; > > - space_width = period - pulse_width; > > - pr_debug("in init_timing_params, freq=%d pulse=%ld, space=%ld\n", > > - freq, pulse_width, space_width); > > - return 0; > > -} > > -#endif /* USE_RDTSC */ > > - > > - > > -/* return value: space length delta */ > > - > > -static long send_pulse_irdeo(unsigned long length) > > -{ > > - long rawbits, ret; > > - int i; > > - unsigned char output; > > - unsigned char chunk, shifted; > > - > > - /* how many bits have to be sent ? */ > > - rawbits = length * 1152 / 10000; > > - if (duty_cycle > 50) > > - chunk = 3; > > - else > > - chunk = 1; > > - for (i = 0, output = 0x7f; rawbits > 0; rawbits -= 3) { > > - shifted = chunk << (i * 3); > > - shifted >>= 1; > > - output &= (~shifted); > > - i++; > > - if (i == 3) { > > - soutp(UART_TX, output); > > - while (!(sinp(UART_LSR) & UART_LSR_THRE)) > > - ; > > - output = 0x7f; > > - i = 0; > > - } > > - } > > - if (i != 0) { > > - soutp(UART_TX, output); > > - while (!(sinp(UART_LSR) & UART_LSR_TEMT)) > > - ; > > - } > > - > > - if (i == 0) > > - ret = (-rawbits) * 10000 / 1152; > > - else > > - ret = (3 - i) * 3 * 10000 / 1152 + (-rawbits) * 10000 / 1152; > > - > > - return ret; > > -} > > - > > -/* Version using udelay() */ > > - > > -/* > > - * here we use fixed point arithmetic, with 8 > > - * fractional bits. that gets us within 0.1% or so of the right average > > - * frequency, albeit with some jitter in pulse length - Steve > > - * > > - * This should use ndelay instead. > > - */ > > - > > -/* To match 8 fractional bits used for pulse/space length */ > > - > > -static long send_pulse_homebrew_softcarrier(unsigned long length) > > -{ > > - int flag; > > - unsigned long actual, target, d; > > - > > - length <<= 8; > > - > > - actual = 0; target = 0; flag = 0; > > - while (actual < length) { > > - if (flag) { > > - off(); > > - target += space_width; > > - } else { > > - on(); > > - target += pulse_width; > > - } > > - d = (target - actual - > > - LIRC_SERIAL_TRANSMITTER_LATENCY + 128) >> 8; > > - /* > > - * Note - we've checked in ioctl that the pulse/space > > - * widths are big enough so that d is > 0 > > - */ > > - udelay(d); > > - actual += (d << 8) + LIRC_SERIAL_TRANSMITTER_LATENCY; > > - flag = !flag; > > - } > > - return (actual-length) >> 8; > > -} > > - > > -static long send_pulse_homebrew(unsigned long length) > > -{ > > - if (length <= 0) > > - return 0; > > - > > - if (softcarrier) > > - return send_pulse_homebrew_softcarrier(length); > > - > > - on(); > > - safe_udelay(length); > > - return 0; > > -} > > - > > -static void send_space_irdeo(long length) > > -{ > > - if (length <= 0) > > - return; > > - > > - safe_udelay(length); > > -} > > - > > -static void send_space_homebrew(long length) > > -{ > > - off(); > > - if (length <= 0) > > - return; > > - safe_udelay(length); > > -} > > - > > -static void rbwrite(int l) > > -{ > > - if (lirc_buffer_full(&rbuf)) { > > - /* no new signals will be accepted */ > > - pr_debug("Buffer overrun\n"); > > - return; > > - } > > - lirc_buffer_write(&rbuf, (void *)&l); > > -} > > - > > -static void frbwrite(int l) > > -{ > > - /* simple noise filter */ > > - static int pulse, space; > > - static unsigned int ptr; > > - > > - if (ptr > 0 && (l & PULSE_BIT)) { > > - pulse += l & PULSE_MASK; > > - if (pulse > 250) { > > - rbwrite(space); > > - rbwrite(pulse | PULSE_BIT); > > - ptr = 0; > > - pulse = 0; > > - } > > - return; > > - } > > - if (!(l & PULSE_BIT)) { > > - if (ptr == 0) { > > - if (l > 20000) { > > - space = l; > > - ptr++; > > - return; > > - } > > - } else { > > - if (l > 20000) { > > - space += pulse; > > - if (space > PULSE_MASK) > > - space = PULSE_MASK; > > - space += l; > > - if (space > PULSE_MASK) > > - space = PULSE_MASK; > > - pulse = 0; > > - return; > > - } > > - rbwrite(space); > > - rbwrite(pulse | PULSE_BIT); > > - ptr = 0; > > - pulse = 0; > > - } > > - } > > - rbwrite(l); > > -} > > - > > -static irqreturn_t lirc_irq_handler(int i, void *blah) > > -{ > > - ktime_t kt; > > - int counter, dcd; > > - u8 status; > > - ktime_t delkt; > > - int data; > > - static int last_dcd = -1; > > - > > - if ((sinp(UART_IIR) & UART_IIR_NO_INT)) { > > - /* not our interrupt */ > > - return IRQ_NONE; > > - } > > - > > - counter = 0; > > - do { > > - counter++; > > - status = sinp(UART_MSR); > > - if (counter > RS_ISR_PASS_LIMIT) { > > - pr_warn("AIEEEE: We're caught!\n"); > > - break; > > - } > > - if ((status & hardware[type].signal_pin_change) > > - && sense != -1) { > > - /* get current time */ > > - kt = ktime_get(); > > - > > - /* New mode, written by Trent Piepho > > - <xyzzy@xxxxxxxxxxxxxxxx>. */ > > - > > - /* > > - * The old format was not very portable. > > - * We now use an int to pass pulses > > - * and spaces to user space. > > - * > > - * If PULSE_BIT is set a pulse has been > > - * received, otherwise a space has been > > - * received. The driver needs to know if your > > - * receiver is active high or active low, or > > - * the space/pulse sense could be > > - * inverted. The bits denoted by PULSE_MASK are > > - * the length in microseconds. Lengths greater > > - * than or equal to 16 seconds are clamped to > > - * PULSE_MASK. All other bits are unused. > > - * This is a much simpler interface for user > > - * programs, as well as eliminating "out of > > - * phase" errors with space/pulse > > - * autodetection. > > - */ > > - > > - /* calc time since last interrupt in microseconds */ > > - dcd = (status & hardware[type].signal_pin) ? 1 : 0; > > - > > - if (dcd == last_dcd) { > > - pr_warn("ignoring spike: %d %d %llx %llx\n", > > - dcd, sense, ktime_to_us(kt), > > - ktime_to_us(lastkt)); > > - continue; > > - } > > - > > - delkt = ktime_sub(kt, lastkt); > > - if (ktime_compare(delkt, ktime_set(15, 0)) > 0) { > > - data = PULSE_MASK; /* really long time */ > > - if (!(dcd^sense)) { > > - /* sanity check */ > > - pr_warn("AIEEEE: %d %d %llx %llx\n", > > - dcd, sense, ktime_to_us(kt), > > - ktime_to_us(lastkt)); > > - /* > > - * detecting pulse while this > > - * MUST be a space! > > - */ > > - sense = sense ? 0 : 1; > > - } > > - } else > > - data = (int) ktime_to_us(delkt); > > - frbwrite(dcd^sense ? data : (data|PULSE_BIT)); > > - lastkt = kt; > > - last_dcd = dcd; > > - wake_up_interruptible(&rbuf.wait_poll); > > - } > > - } while (!(sinp(UART_IIR) & UART_IIR_NO_INT)); /* still pending ? */ > > - return IRQ_HANDLED; > > -} > > - > > - > > -static int hardware_init_port(void) > > -{ > > - u8 scratch, scratch2, scratch3; > > - > > - /* > > - * This is a simple port existence test, borrowed from the autoconfig > > - * function in drivers/serial/8250.c > > - */ > > - scratch = sinp(UART_IER); > > - soutp(UART_IER, 0); > > -#ifdef __i386__ > > - outb(0xff, 0x080); > > -#endif > > - scratch2 = sinp(UART_IER) & 0x0f; > > - soutp(UART_IER, 0x0f); > > -#ifdef __i386__ > > - outb(0x00, 0x080); > > -#endif > > - scratch3 = sinp(UART_IER) & 0x0f; > > - soutp(UART_IER, scratch); > > - if (scratch2 != 0 || scratch3 != 0x0f) { > > - /* we fail, there's nothing here */ > > - pr_err("port existence test failed, cannot continue\n"); > > - return -ENODEV; > > - } > > - > > - > > - > > - /* Set DLAB 0. */ > > - soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB)); > > - > > - /* First of all, disable all interrupts */ > > - soutp(UART_IER, sinp(UART_IER) & > > - (~(UART_IER_MSI|UART_IER_RLSI|UART_IER_THRI|UART_IER_RDI))); > > - > > - /* Clear registers. */ > > - sinp(UART_LSR); > > - sinp(UART_RX); > > - sinp(UART_IIR); > > - sinp(UART_MSR); > > - > > - /* Set line for power source */ > > - off(); > > - > > - /* Clear registers again to be sure. */ > > - sinp(UART_LSR); > > - sinp(UART_RX); > > - sinp(UART_IIR); > > - sinp(UART_MSR); > > - > > - switch (type) { > > - case LIRC_IRDEO: > > - case LIRC_IRDEO_REMOTE: > > - /* setup port to 7N1 @ 115200 Baud */ > > - /* 7N1+start = 9 bits at 115200 ~ 3 bits at 38kHz */ > > - > > - /* Set DLAB 1. */ > > - soutp(UART_LCR, sinp(UART_LCR) | UART_LCR_DLAB); > > - /* Set divisor to 1 => 115200 Baud */ > > - soutp(UART_DLM, 0); > > - soutp(UART_DLL, 1); > > - /* Set DLAB 0 + 7N1 */ > > - soutp(UART_LCR, UART_LCR_WLEN7); > > - /* THR interrupt already disabled at this point */ > > - break; > > - default: > > - break; > > - } > > - > > - return 0; > > -} > > - > > -static int lirc_serial_probe(struct platform_device *dev) > > -{ > > - int i, nlow, nhigh, result; > > - > > - result = devm_request_irq(&dev->dev, irq, lirc_irq_handler, > > - (share_irq ? IRQF_SHARED : 0), > > - LIRC_DRIVER_NAME, &hardware); > > - if (result < 0) { > > - if (result == -EBUSY) > > - dev_err(&dev->dev, "IRQ %d busy\n", irq); > > - else if (result == -EINVAL) > > - dev_err(&dev->dev, "Bad irq number or handler\n"); > > - return result; > > - } > > - > > - /* Reserve io region. */ > > - /* > > - * Future MMAP-Developers: Attention! > > - * For memory mapped I/O you *might* need to use ioremap() first, > > - * for the NSLU2 it's done in boot code. > > - */ > > - if (((iommap) > > - && (devm_request_mem_region(&dev->dev, iommap, 8 << ioshift, > > - LIRC_DRIVER_NAME) == NULL)) > > - || ((!iommap) > > - && (devm_request_region(&dev->dev, io, 8, > > - LIRC_DRIVER_NAME) == NULL))) { > > - dev_err(&dev->dev, "port %04x already in use\n", io); > > - dev_warn(&dev->dev, "use 'setserial /dev/ttySX uart none'\n"); > > - dev_warn(&dev->dev, > > - "or compile the serial port driver as module and\n"); > > - dev_warn(&dev->dev, "make sure this module is loaded first\n"); > > - return -EBUSY; > > - } > > - > > - result = hardware_init_port(); > > - if (result < 0) > > - return result; > > - > > - /* Initialize pulse/space widths */ > > - init_timing_params(duty_cycle, freq); > > - > > - /* If pin is high, then this must be an active low receiver. */ > > - if (sense == -1) { > > - /* wait 1/2 sec for the power supply */ > > - msleep(500); > > - > > - /* > > - * probe 9 times every 0.04s, collect "votes" for > > - * active high/low > > - */ > > - nlow = 0; > > - nhigh = 0; > > - for (i = 0; i < 9; i++) { > > - if (sinp(UART_MSR) & hardware[type].signal_pin) > > - nlow++; > > - else > > - nhigh++; > > - msleep(40); > > - } > > - sense = nlow >= nhigh ? 1 : 0; > > - dev_info(&dev->dev, "auto-detected active %s receiver\n", > > - sense ? "low" : "high"); > > - } else > > - dev_info(&dev->dev, "Manually using active %s receiver\n", > > - sense ? "low" : "high"); > > - > > - dev_dbg(&dev->dev, "Interrupt %d, port %04x obtained\n", irq, io); > > - return 0; > > -} > > - > > -static int set_use_inc(void *data) > > -{ > > - unsigned long flags; > > - > > - /* initialize timestamp */ > > - lastkt = ktime_get(); > > - > > - spin_lock_irqsave(&hardware[type].lock, flags); > > - > > - /* Set DLAB 0. */ > > - soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB)); > > - > > - soutp(UART_IER, sinp(UART_IER)|UART_IER_MSI); > > - > > - spin_unlock_irqrestore(&hardware[type].lock, flags); > > - > > - return 0; > > -} > > - > > -static void set_use_dec(void *data) > > -{ unsigned long flags; > > - > > - spin_lock_irqsave(&hardware[type].lock, flags); > > - > > - /* Set DLAB 0. */ > > - soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB)); > > - > > - /* First of all, disable all interrupts */ > > - soutp(UART_IER, sinp(UART_IER) & > > - (~(UART_IER_MSI|UART_IER_RLSI|UART_IER_THRI|UART_IER_RDI))); > > - spin_unlock_irqrestore(&hardware[type].lock, flags); > > -} > > - > > -static ssize_t lirc_write(struct file *file, const char __user *buf, > > - size_t n, loff_t *ppos) > > -{ > > - int i, count; > > - unsigned long flags; > > - long delta = 0; > > - int *wbuf; > > - > > - if (!(hardware[type].features & LIRC_CAN_SEND_PULSE)) > > - return -EPERM; > > - > > - count = n / sizeof(int); > > - if (n % sizeof(int) || count % 2 == 0) > > - return -EINVAL; > > - wbuf = memdup_user(buf, n); > > - if (IS_ERR(wbuf)) > > - return PTR_ERR(wbuf); > > - spin_lock_irqsave(&hardware[type].lock, flags); > > - if (type == LIRC_IRDEO) { > > - /* DTR, RTS down */ > > - on(); > > - } > > - for (i = 0; i < count; i++) { > > - if (i%2) > > - hardware[type].send_space(wbuf[i] - delta); > > - else > > - delta = hardware[type].send_pulse(wbuf[i]); > > - } > > - off(); > > - spin_unlock_irqrestore(&hardware[type].lock, flags); > > - kfree(wbuf); > > - return n; > > -} > > - > > -static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) > > -{ > > - int result; > > - u32 __user *uptr = (u32 __user *)arg; > > - u32 value; > > - > > - switch (cmd) { > > - case LIRC_GET_SEND_MODE: > > - if (!(hardware[type].features&LIRC_CAN_SEND_MASK)) > > - return -ENOIOCTLCMD; > > - > > - result = put_user(LIRC_SEND2MODE > > - (hardware[type].features&LIRC_CAN_SEND_MASK), > > - uptr); > > - if (result) > > - return result; > > - break; > > - > > - case LIRC_SET_SEND_MODE: > > - if (!(hardware[type].features&LIRC_CAN_SEND_MASK)) > > - return -ENOIOCTLCMD; > > - > > - result = get_user(value, uptr); > > - if (result) > > - return result; > > - /* only LIRC_MODE_PULSE supported */ > > - if (value != LIRC_MODE_PULSE) > > - return -EINVAL; > > - break; > > - > > - case LIRC_GET_LENGTH: > > - return -ENOIOCTLCMD; > > - > > - case LIRC_SET_SEND_DUTY_CYCLE: > > - pr_debug("SET_SEND_DUTY_CYCLE\n"); > > - if (!(hardware[type].features&LIRC_CAN_SET_SEND_DUTY_CYCLE)) > > - return -ENOIOCTLCMD; > > - > > - result = get_user(value, uptr); > > - if (result) > > - return result; > > - if (value <= 0 || value > 100) > > - return -EINVAL; > > - return init_timing_params(value, freq); > > - > > - case LIRC_SET_SEND_CARRIER: > > - pr_debug("SET_SEND_CARRIER\n"); > > - if (!(hardware[type].features&LIRC_CAN_SET_SEND_CARRIER)) > > - return -ENOIOCTLCMD; > > - > > - result = get_user(value, uptr); > > - if (result) > > - return result; > > - if (value > 500000 || value < 20000) > > - return -EINVAL; > > - return init_timing_params(duty_cycle, value); > > - > > - default: > > - return lirc_dev_fop_ioctl(filep, cmd, arg); > > - } > > - return 0; > > -} > > - > > -static const struct file_operations lirc_fops = { > > - .owner = THIS_MODULE, > > - .write = lirc_write, > > - .unlocked_ioctl = lirc_ioctl, > > -#ifdef CONFIG_COMPAT > > - .compat_ioctl = lirc_ioctl, > > -#endif > > - .read = lirc_dev_fop_read, > > - .poll = lirc_dev_fop_poll, > > - .open = lirc_dev_fop_open, > > - .release = lirc_dev_fop_close, > > - .llseek = no_llseek, > > -}; > > - > > -static struct lirc_driver driver = { > > - .name = LIRC_DRIVER_NAME, > > - .minor = -1, > > - .code_length = 1, > > - .sample_rate = 0, > > - .data = NULL, > > - .add_to_buf = NULL, > > - .rbuf = &rbuf, > > - .set_use_inc = set_use_inc, > > - .set_use_dec = set_use_dec, > > - .fops = &lirc_fops, > > - .dev = NULL, > > - .owner = THIS_MODULE, > > -}; > > - > > -static struct platform_device *lirc_serial_dev; > > - > > -static int lirc_serial_suspend(struct platform_device *dev, > > - pm_message_t state) > > -{ > > - /* Set DLAB 0. */ > > - soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB)); > > - > > - /* Disable all interrupts */ > > - soutp(UART_IER, sinp(UART_IER) & > > - (~(UART_IER_MSI|UART_IER_RLSI|UART_IER_THRI|UART_IER_RDI))); > > - > > - /* Clear registers. */ > > - sinp(UART_LSR); > > - sinp(UART_RX); > > - sinp(UART_IIR); > > - sinp(UART_MSR); > > - > > - return 0; > > -} > > - > > -/* twisty maze... need a forward-declaration here... */ > > -static void lirc_serial_exit(void); > > - > > -static int lirc_serial_resume(struct platform_device *dev) > > -{ > > - unsigned long flags; > > - int result; > > - > > - result = hardware_init_port(); > > - if (result < 0) > > - return result; > > - > > - spin_lock_irqsave(&hardware[type].lock, flags); > > - /* Enable Interrupt */ > > - lastkt = ktime_get(); > > - soutp(UART_IER, sinp(UART_IER)|UART_IER_MSI); > > - off(); > > - > > - lirc_buffer_clear(&rbuf); > > - > > - spin_unlock_irqrestore(&hardware[type].lock, flags); > > - > > - return 0; > > -} > > - > > -static struct platform_driver lirc_serial_driver = { > > - .probe = lirc_serial_probe, > > - .suspend = lirc_serial_suspend, > > - .resume = lirc_serial_resume, > > - .driver = { > > - .name = "lirc_serial", > > - }, > > -}; > > - > > -static int __init lirc_serial_init(void) > > -{ > > - int result; > > - > > - /* Init read buffer. */ > > - result = lirc_buffer_init(&rbuf, sizeof(int), RBUF_LEN); > > - if (result < 0) > > - return result; > > - > > - result = platform_driver_register(&lirc_serial_driver); > > - if (result) { > > - printk("lirc register returned %d\n", result); > > - goto exit_buffer_free; > > - } > > - > > - lirc_serial_dev = platform_device_alloc("lirc_serial", 0); > > - if (!lirc_serial_dev) { > > - result = -ENOMEM; > > - goto exit_driver_unregister; > > - } > > - > > - result = platform_device_add(lirc_serial_dev); > > - if (result) > > - goto exit_device_put; > > - > > - return 0; > > - > > -exit_device_put: > > - platform_device_put(lirc_serial_dev); > > -exit_driver_unregister: > > - platform_driver_unregister(&lirc_serial_driver); > > -exit_buffer_free: > > - lirc_buffer_free(&rbuf); > > - return result; > > -} > > - > > -static void lirc_serial_exit(void) > > -{ > > - platform_device_unregister(lirc_serial_dev); > > - platform_driver_unregister(&lirc_serial_driver); > > - lirc_buffer_free(&rbuf); > > -} > > - > > -static int __init lirc_serial_init_module(void) > > -{ > > - int result; > > - > > - switch (type) { > > - case LIRC_HOMEBREW: > > - case LIRC_IRDEO: > > - case LIRC_IRDEO_REMOTE: > > - case LIRC_ANIMAX: > > - case LIRC_IGOR: > > - /* if nothing specified, use ttyS0/com1 and irq 4 */ > > - io = io ? io : 0x3f8; > > - irq = irq ? irq : 4; > > - break; > > - default: > > - return -EINVAL; > > - } > > - if (!softcarrier) { > > - switch (type) { > > - case LIRC_HOMEBREW: > > - case LIRC_IGOR: > > - hardware[type].features &= > > - ~(LIRC_CAN_SET_SEND_DUTY_CYCLE| > > - LIRC_CAN_SET_SEND_CARRIER); > > - break; > > - } > > - } > > - > > - /* make sure sense is either -1, 0, or 1 */ > > - if (sense != -1) > > - sense = !!sense; > > - > > - result = lirc_serial_init(); > > - if (result) > > - return result; > > - > > - driver.features = hardware[type].features; > > - driver.dev = &lirc_serial_dev->dev; > > - driver.minor = lirc_register_driver(&driver); > > - if (driver.minor < 0) { > > - pr_err("register_chrdev failed!\n"); > > - lirc_serial_exit(); > > - return driver.minor; > > - } > > - return 0; > > -} > > - > > -static void __exit lirc_serial_exit_module(void) > > -{ > > - lirc_unregister_driver(driver.minor); > > - lirc_serial_exit(); > > - pr_debug("cleaned up module\n"); > > -} > > - > > - > > -module_init(lirc_serial_init_module); > > -module_exit(lirc_serial_exit_module); > > - > > -MODULE_DESCRIPTION("Infra-red receiver driver for serial ports."); > > -MODULE_AUTHOR("Ralph Metzler, Trent Piepho, Ben Pfaff, " > > - "Christoph Bartelmus, Andrei Tanas"); > > -MODULE_LICENSE("GPL"); > > - > > -module_param(type, int, S_IRUGO); > > -MODULE_PARM_DESC(type, "Hardware type (0 = home-brew, 1 = IRdeo," > > - " 2 = IRdeo Remote, 3 = AnimaX, 4 = IgorPlug," > > - " 5 = NSLU2 RX:CTS2/TX:GreenLED)"); > > - > > -module_param(io, int, S_IRUGO); > > -MODULE_PARM_DESC(io, "I/O address base (0x3f8 or 0x2f8)"); > > - > > -/* some architectures (e.g. intel xscale) have memory mapped registers */ > > -module_param(iommap, bool, S_IRUGO); > > -MODULE_PARM_DESC(iommap, "physical base for memory mapped I/O" > > - " (0 = no memory mapped io)"); > > - > > -/* > > - * some architectures (e.g. intel xscale) align the 8bit serial registers > > - * on 32bit word boundaries. > > - * See linux-kernel/drivers/tty/serial/8250/8250.c serial_in()/out() > > - */ > > -module_param(ioshift, int, S_IRUGO); > > -MODULE_PARM_DESC(ioshift, "shift I/O register offset (0 = no shift)"); > > - > > -module_param(irq, int, S_IRUGO); > > -MODULE_PARM_DESC(irq, "Interrupt (4 or 3)"); > > - > > -module_param(share_irq, bool, S_IRUGO); > > -MODULE_PARM_DESC(share_irq, "Share interrupts (0 = off, 1 = on)"); > > - > > -module_param(sense, int, S_IRUGO); > > -MODULE_PARM_DESC(sense, "Override autodetection of IR receiver circuit" > > - " (0 = active high, 1 = active low )"); > > - > > -#ifdef CONFIG_LIRC_SERIAL_TRANSMITTER > > -module_param(txsense, bool, S_IRUGO); > > -MODULE_PARM_DESC(txsense, "Sense of transmitter circuit" > > - " (0 = active high, 1 = active low )"); > > -#endif > > - > > -module_param(softcarrier, bool, S_IRUGO); > > -MODULE_PARM_DESC(softcarrier, "Software carrier (0 = off, 1 = on, default on)"); Thanks Sean -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html