/*
* 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@thp.uni-koeln.de>
* Copyright (C) 1998 Trent Piepho <xyzzy@u.washington.edu>
* Copyright (C) 1998 Ben Pfaff <blp@gnu.org>
* Copyright (C) 1999 Christoph Bartelmus <lirc@bartelmus.de>
* Copyright (C) 2007 Andrei Tanas <andrei@tanas.ca> (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@daviesfam.org> 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/time.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 bool debug;
static int sense = -1; /* -1 = auto, 0 = active high, 1 = active low */
static bool txsense; /* 0 = active high, 1 = active low */
#define dprintk(fmt, args...) \
do { \
if (debug) \
printk(KERN_DEBUG LIRC_DRIVER_NAME ": " \
fmt, ## args); \
} while (0)
/* 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,