View | Details | Raw Unified
Collapse All | Expand All

(-) linux-2.6.5.orig/Documentation/lirc/lirc_it87 (+54 lines)
Line 0    Link Here 
This is the README using the ITE IT8705 and IT8712 CIR port for LIRC.
The IT8705 for example can be found on the ECS K7S5A.
The driver supports receiving (MODE2) and sending (PULSE). It seems
sending 'LIRC_CAN_SEND_PULSE' isn't optimal for this type of hardware.
But because I don't know how to implement 'LIRC_CAN_SEND_CODE', I did
it this way.
Attention:
Because of missing hardware, the following hasn't been tested:
a) receiving with demodulator enabled,
b) sending (debugging output looks good) and
c) using IT8712
Any help and/or additions etc. is welcome.
lirc_it87 knows about the following module-parameters:
MODULE_DESCRIPTION("LIRC driver for ITE IT8712/IT8705 CIR port");
MODULE_PARM(io, "i");
MODULE_PARM_DESC(io, "I/O base address (default: 0x310)");
MODULE_PARM(irq, "i");
MODULE_PARM_DESC(irq, "Interrupt (1,3-12) (default: 7)");
MODULE_PARM(it87_enable_demodulator, "i");
MODULE_PARM_DESC(it87_enable_demodulator, "Receiver demodulator
	enable/disable (1/0), default: 0");
Usage:
a) io and irq:
If the driver finds the IT8705/12-CIR port initialized, io and irq of
the preinitialized hardware is used by the driver. If both values are
read 0x0 from the hardware, the default or given value is used.
Note: I experienced using irq=3. The driver initialized without any
problems, but no irqs are recognized by the system. I had to switch
back to default, irq 7.
b) it87_enable_demodulator:
The demodulator for the receiver can be switched off (default within
the driver). If you need the demodulator simple enable it by the
following way: it87_enable_demodulator=1.
Hans-Günter Lütke Uphues
TODO
This is my todo-list for lirc_it87:
1. enabling/using shared IRQ
2. init/drop IRQ-usage in lirc_open/lirc_close
(-) linux-2.6.5.orig/drivers/char/Kconfig (+2 lines)
 Lines 632-637    Link Here 
comment "metalab.unc.edu or ftp://titus.cfw.com/pub/Linux/util/"
comment "metalab.unc.edu or ftp://titus.cfw.com/pub/Linux/util/"
	depends on QIC02_TAPE && QIC02_DYNCONF
	depends on QIC02_TAPE && QIC02_DYNCONF
source "drivers/char/lirc/Kconfig"
source "drivers/char/ipmi/Kconfig"
source "drivers/char/ipmi/Kconfig"
source "drivers/char/watchdog/Kconfig"
source "drivers/char/watchdog/Kconfig"
(-) linux-2.6.5.orig/drivers/char/Makefile (-1 / +1 lines)
 Lines 7-13    Link Here 
#
#
FONTMAPFILE = cp437.uni
FONTMAPFILE = cp437.uni
obj-y	 += mem.o random.o tty_io.o n_tty.o tty_ioctl.o pty.o misc.o
obj-y	 += mem.o random.o tty_io.o n_tty.o tty_ioctl.o pty.o misc.o lirc/
obj-$(CONFIG_VT)		+= vt_ioctl.o vc_screen.o consolemap.o \
obj-$(CONFIG_VT)		+= vt_ioctl.o vc_screen.o consolemap.o \
				   consolemap_deftbl.o selection.o keyboard.o
				   consolemap_deftbl.o selection.o keyboard.o
(-) linux-2.6.5.orig/drivers/char/lirc/Kconfig (+208 lines)
Line 0    Link Here 
# LIRC http://lirc.sf.net/
# Kernel patch by Flameeyes <dgp85@users.sf.net>
# Check for new patch at http://flameeyes.web.ctonet.it
#
# Thanks to Koos Vriezen <koos.vriezen@xs4all.nl> for the Homebrew support.
# Thanks to Jeff Clark <jeff@tmtrading.com> for support when I wasn't able
# to update it and for his patch (found at http://www.clarkmania.com/~jclark/
# Thanks to Bernhard Rosenkraenzer <bero@arklinux.org> for SMP patch.
# Thanks to Vince <fuzzy77@free.fr> for the temporary lirc_atiusb driver.
# Thanks to Paul Miller <pmiller9@users.sourceforge.net> for the new working
# lirc_atiusb driver.
menu "Linux InfraRed Controller"
config LIRC_SUPPORT
	tristate "Linux InfraRed Controller"
	config LIRC_MAX_DEV
		int "Maximum LIRC devices"
		default "2"
		depends on LIRC_SUPPORT
	config LIRC_I2C
		tristate "I2C Driver"
		depends on LIRC_SUPPORT && VIDEO_BT848 && I2C && I2C_ALGOBIT
		help
		  Say Y here if you need support for the following cards:
		  Pixelview IR
		  Hauppauage IR
		  PV951 IR
		  TV-Box IR
		  KNC ONE IR
		  If these dont make sense to you, then dont use the module.
	config LIRC_GPIO
		tristate "GPIO Driver"
		depends on LIRC_SUPPORT && VIDEO_BT848
	config LIRC_BT829
		tristate "BT829 Driver"
		depends on LIRC_SUPPORT
	config LIRC_IT87
		tristate "IT87 Driver"
		depends on LIRC_SUPPORT
	config LIRC_ATIUSB
		tristate "ATI USB Driver"
		depends on LIRC_SUPPORT && USB
	config LIRC_MCEUSB
		tristate "MCE USB Driver"
		depends on LIRC_SUPPORT && USB
	config LIRC_PARALLEL
		tristate "Parallel Driver"
		depends on LIRC_SUPPORT && !SMP && PARPORT
		choice
			prompt "Parallel Port"
			depends on LIRC_PARALLEL
			config LIRC_PARALLEL_LPT1
				bool "LPT1 (0x378, 7)"
			config LIRC_PARALLEL_LPT2
				bool "LPT2 (0x278, 5)"
			config LIRC_PARALLEL_LPT3
				bool "COM3 (0x3bc, none)"
			config LIRC_PARALLEL_OTHER
				bool "Other (custom values)"
		endchoice
		config LIRC_PORT_PARALLEL
			hex "I/O Port"
				default "0x378" if LIRC_PARALLEL_LPT1
				default "0x278" if LIRC_PARALLEL_LPT2
				default "0x3bc" if LIRC_PARALLEL_LPT3
			depends on LIRC_PARALLEL
		config LIRC_IRQ_PARALLEL
			hex "IRQ"
				default "7" if LIRC_PARALLEL_LPT1
				default "5" if LIRC_PARALLEL_LPT2
			depends on LIRC_PARALLEL
		config LIRC_TIMER
			int "Timer"
			default "65535"
			depends on LIRC_PARALLEL
	config LIRC_SERIAL
		tristate "Serial Driver"
		depends on LIRC_SUPPORT && SERIAL_8250
		choice
			prompt "Serial Receiver Type"
			depends on LIRC_SERIAL
			config LIRC_HOMEBREW
				bool "Homebrew"
			config LIRC_SERIAL_ANIMAX
				bool "Animax"
			config LIRC_SERIAL_IRDEO
				bool "IRdeo"
			config LIRC_SERIAL_IRDEO_REMOTE
				bool "IRdeo Remote"
		endchoice
		config LIRC_SERIAL_TRANSMITTER
			bool "With transmitter diode"
			depends on LIRC_SERIAL && !LIRC_SERIAL_ANIMAX
		config LIRC_SERIAL_SOFTCARRIER
			bool "With software carrier"
			depends on LIRC_SERIAL_TRANSMITTER
		config LIRC_SERIAL_IGOR
			bool "Igor Ceska's variation"
			depends on LIRC_SERIAL
		choice
			prompt "Serial Port"
			depends on LIRC_SERIAL
			config LIRC_SERIAL_COM1
				bool "COM1 (0x3f8, 4)"
			config LIRC_SERIAL_COM2
				bool "COM2 (0x2f8, 3)"
			config LIRC_SERIAL_COM3
				bool "COM3 (0x3e8, 4)"
			config LIRC_SERIAL_COM4
				bool "COM4 (0x2e8, 3)"
			config LIRC_SERIAL_OTHER
				bool "Other (custom values)"
		endchoice
		config LIRC_PORT_SERIAL
			hex "I/O Port"
				default "0x3f8" if LIRC_SERIAL_COM1
				default "0x2f8" if LIRC_SERIAL_COM2
				default "0x3e8" if LIRC_SERIAL_COM3
				default "0x2e8" if LIRC_SERIAL_COM4
			depends on LIRC_SERIAL
		config LIRC_IRQ_SERIAL
			hex "IRQ"
				default "4" if LIRC_SERIAL_COM1 || LIRC_SERIAL_COM3
				default "3" if LIRC_SERIAL_COM2 || LIRC_SERIAL_COM4
			depends on LIRC_SERIAL
	config LIRC_SIR
		tristate "SIR Driver"
		depends on LIRC_SUPPORT
		config LIRC_ON_SA1100
			bool "LIRC driver for StrongARM SA1100 embedded microprocessor"
			depends on LIRC_SIR
		choice
			prompt "SIR Type"
			depends on LIRC_SIR && !LIRC_ON_SA1100
			config LIRC_SIR_IRDA
				bool "SIR IrDA (built-in IR ports)"
			config LIRC_SIR_TEKRAM
				bool "Tekram Irmate 210 (16x50 UART compatible serial port)"
			config LIRC_SIR_ACTISYS_ACT200L
				bool "Actisys Act200L SIR driver support"
		endchoice
		choice
			prompt "Serial Port"
			depends on LIRC_SIR
			config LIRC_SIR_COM1
				bool "COM1 (0x3f8, 4)"
			config LIRC_SIR_COM2
				bool "COM2 (0x2f8, 3)"
			config LIRC_SIR_COM3
				bool "COM3 (0x3e8, 4)"
			config LIRC_SIR_COM4
				bool "COM4 (0x2e8, 3)"
			config LIRC_SIR_OTHER
				bool "Other (custom values)"
		endchoice
		config LIRC_PORT_SIR
			hex "I/O Port"
				default "0x3f8" if LIRC_SIR_COM1
				default "0x2f8" if LIRC_SIR_COM2
				default "0x3e8" if LIRC_SIR_COM3
				default "0x2e8" if LIRC_SIR_COM4
			depends on LIRC_SIR
		config LIRC_IRQ_SIR
			hex "IRQ"
				default "4" if LIRC_SIR_COM1 || LIRC_SIR_COM3
				default "3" if LIRC_SIR_COM2 || LIRC_SIR_COM4
			depends on LIRC_SIR
endmenu
(-) linux-2.6.5.orig/drivers/char/lirc/Makefile (+14 lines)
Line 0    Link Here 
#
# Makefile for the lirc drivers
#
obj-$(CONFIG_LIRC_SUPPORT) += lirc_dev.o
obj-$(CONFIG_LIRC_GPIO) += lirc_gpio.o
obj-$(CONFIG_LIRC_BT829) += lirc_bt829.o
obj-$(CONFIG_LIRC_IT87) += lirc_it87.o
obj-$(CONFIG_LIRC_PARALLEL) += lirc_parallel.o
obj-$(CONFIG_LIRC_SERIAL) += lirc_serial.o
obj-$(CONFIG_LIRC_SIR) += lirc_sir.o
obj-$(CONFIG_LIRC_ATIUSB) += lirc_atiusb.o
obj-$(CONFIG_LIRC_MCEUSB) += lirc_mceusb.o
obj-$(CONFIG_LIRC_I2C) += lirc_i2c.o
(-) linux-2.6.5.orig/drivers/char/lirc/lirc_atiusb.c (+629 lines)
Line 0    Link Here 
/* lirc_atiusb - USB remote support for LIRC
 * (currently only supports X10 USB remotes)
 * Version 0.3  [beta status]
 *
 * Copyright (C) 2003-2004 Paul Miller <pmiller9@users.sourceforge.net>
 *
 * This driver was derived from:
 *   Vladimir Dergachev <volodya@minspring.com>'s 2002
 *      "USB ATI Remote support" (input device)
 *   Adrian Dewhurst <sailor-lk@sailorfrag.net>'s 2002
 *      "USB StreamZap remote driver" (LIRC)
 *   Artur Lipowski <alipowski@kki.net.pl>'s 2002
 *      "lirc_dev" and "lirc_gpio" LIRC modules
 *
 * $Id: lirc_atiusb.c,v 1.21 2004/01/31 03:38:58 pmiller9 Exp $
 */
/*
 * 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/version.h>
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 2, 4)
#error "*******************************************************"
#error "Sorry, this driver needs kernel version 2.2.4 or higher"
#error "*******************************************************"
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
#define KERNEL26		1
#else
#define KERNEL26		0
#endif
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/kmod.h>
#include <linux/smp_lock.h>
#include <linux/completion.h>
#include <asm/uaccess.h>
#include <linux/usb.h>
#include <linux/poll.h>
#include <linux/wait.h>
#if KERNEL26
#include <linux/lirc.h>
#include "lirc_dev.h"
#else
#include "drivers/lirc.h"
#include "drivers/lirc_dev/lirc_dev.h"
#endif
#define DRIVER_VERSION		"0.3"
#define DRIVER_AUTHOR		"Paul Miller <pmiller9@users.sourceforge.net>"
#define DRIVER_DESC		"USB remote driver for LIRC"
#define DRIVER_NAME		"lirc_atiusb"
#define CODE_LENGTH		5
#define CODE_MIN_LENGTH		4
#define USB_BUFLEN		(CODE_LENGTH*4)
#ifdef CONFIG_USB_DEBUG
	static int debug = 1;
#else
	static int debug = 0;
#endif
#define dprintk			if (debug) printk
/* get hi and low bytes of a 16-bits int */
#define HI(a)			((unsigned char)((a) >> 8))
#define LO(a)			((unsigned char)((a) & 0xff))
/* lock irctl structure */
#define IRLOCK			down_interruptible(&ir->lock)
#define IRUNLOCK		up(&ir->lock)
/* general constants */
#define SUCCESS			0
#define SEND_FLAG_IN_PROGRESS	1
#define SEND_FLAG_COMPLETE	2
/* data structure for each usb remote */
struct irctl {
	/* usb */
	struct usb_device *usbdev;
	struct urb *urb_in;
	struct urb *urb_out;
	int devnum;
	/* buffers and dma */
	unsigned char *buf_in;
	unsigned char *buf_out;
	unsigned int len_in;
#if KERNEL26
	dma_addr_t dma_in;
	dma_addr_t dma_out;
#endif
	/* lirc */
	struct lirc_plugin *p;
	int connected;
	/* handle sending (init strings) */
	int send_flags;
	wait_queue_head_t wait_out;
	struct semaphore lock;
};
/* init strings */
static char init1[] = {0x01, 0x00, 0x20, 0x14};
static char init2[] = {0x01, 0x00, 0x20, 0x14, 0x20, 0x20, 0x20};
/* send packet - used to initialize remote */
static void send_packet(struct irctl *ir, u16 cmd, unsigned char *data)
{
	DECLARE_WAITQUEUE(wait, current);
	int timeout = HZ; /* 1 second */
	unsigned char buf[USB_BUFLEN];
	dprintk(DRIVER_NAME "[%d]: send called (%#x)\n", ir->devnum, cmd);
	IRLOCK;
	ir->urb_out->transfer_buffer_length = LO(cmd) + 1;
	ir->urb_out->dev = ir->usbdev;
	ir->send_flags = SEND_FLAG_IN_PROGRESS;
	memcpy(buf+1, data, LO(cmd));
	buf[0] = HI(cmd);
	memcpy(ir->buf_out, buf, LO(cmd)+1);
	set_current_state(TASK_INTERRUPTIBLE);
	add_wait_queue(&ir->wait_out, &wait);
#if KERNEL26
	if (usb_submit_urb(ir->urb_out, SLAB_ATOMIC)) {
#else
	if (usb_submit_urb(ir->urb_out)) {
#endif
		set_current_state(TASK_RUNNING);
		remove_wait_queue(&ir->wait_out, &wait);
		IRUNLOCK;
		return;
	}
	IRUNLOCK;
	while (timeout && (ir->urb_out->status == -EINPROGRESS)
		&& !(ir->send_flags & SEND_FLAG_COMPLETE)) {
		timeout = schedule_timeout(timeout);
		rmb();
	}
	dprintk(DRIVER_NAME "[%d]: send complete (%#x)\n", ir->devnum, cmd);
	set_current_state(TASK_RUNNING);
	remove_wait_queue(&ir->wait_out, &wait);
	usb_unlink_urb(ir->urb_out);
}
static int unregister_from_lirc(struct irctl *ir)
{
	struct lirc_plugin *p = ir->p;
	int devnum;
	int rtn;
	devnum = ir->devnum;
	dprintk(DRIVER_NAME "[%d]: unregister from lirc called\n", devnum);
	if ((rtn = lirc_unregister_plugin(p->minor)) > 0) {
		printk(DRIVER_NAME "[%d]: error in lirc_unregister minor: %d\n"
			"Trying again...\n", devnum, p->minor);
		if (rtn == -EBUSY) {
			printk(DRIVER_NAME
				"[%d]: device is opened, will unregister"
				" on close\n", devnum);
			return -EAGAIN;
		}
		set_current_state(TASK_INTERRUPTIBLE);
		schedule_timeout(HZ);
		if ((rtn = lirc_unregister_plugin(p->minor)) > 0) {
			printk(DRIVER_NAME "[%d]: lirc_unregister failed\n",
			devnum);
		}
	}
	if (rtn != SUCCESS) {
		printk(DRIVER_NAME "[%d]: didn't free resources\n", devnum);
		return -EAGAIN;
	}
	printk(DRIVER_NAME "[%d]: usb remote disconnected\n", devnum);
	lirc_buffer_free(p->rbuf);
	kfree(p->rbuf);
	kfree(p);
	kfree(ir);
	return SUCCESS;
}
static int set_use_inc(void *data)
{
	struct irctl *ir = data;
	if (!ir) {
		printk(DRIVER_NAME "[?]: set_use_inc called with no context\n");
		return -EIO;
	}
	dprintk(DRIVER_NAME "[%d]: set use inc\n", ir->devnum);
	if (!ir->connected) {
		if (!ir->usbdev)
			return -ENOENT;
		ir->urb_in->dev = ir->usbdev;
#if KERNEL26
		if (usb_submit_urb(ir->urb_in, SLAB_ATOMIC)) {
#else
		if (usb_submit_urb(ir->urb_in)) {
#endif
			printk(DRIVER_NAME "[%d]: open result = -EIO error "
				"submitting urb\n", ir->devnum);
			return -EIO;
		}
		ir->connected = 1;
	}
	return SUCCESS;
}
static void set_use_dec(void *data)
{
	struct irctl *ir = data;
	if (!ir) {
		printk(DRIVER_NAME "[?]: set_use_dec called with no context\n");
		return;
	}
	dprintk(DRIVER_NAME "[%d]: set use dec\n", ir->devnum);
	if (ir->connected) {
		IRLOCK;
		usb_unlink_urb(ir->urb_in);
		ir->connected = 0;
		IRUNLOCK;
	}
}
#if KERNEL26
static void usb_remote_recv(struct urb *urb, struct pt_regs *regs)
#else
static void usb_remote_recv(struct urb *urb)
#endif
{
	struct irctl *ir;
	char buf[CODE_LENGTH];
	int i, len;
	if (!urb)
		return;
	if (!(ir = urb->context)) {
		usb_unlink_urb(urb);
		return;
	}
	dprintk(DRIVER_NAME "[%d]: data received (length %d)\n",
		ir->devnum, urb->actual_length);
	switch (urb->status) {
	/* success */
	case SUCCESS:
		/* some remotes emit both 4 and 5 byte length codes. */
		len = urb->actual_length;
		if (len < CODE_MIN_LENGTH || len > CODE_LENGTH) return;
		memcpy(buf,urb->transfer_buffer,len);
		for (i = len; i < CODE_LENGTH; i++) buf[i] = 0;
		lirc_buffer_write_1(ir->p->rbuf, buf);
		wake_up(&ir->p->rbuf->wait_poll);
		break;
	/* unlink */
	case -ECONNRESET:
	case -ENOENT:
	case -ESHUTDOWN:
		usb_unlink_urb(urb);
		return;
	}
	/* resubmit urb */
#if KERNEL26
	usb_submit_urb(urb, SLAB_ATOMIC);
#else
	usb_submit_urb(urb);
#endif
}
#if KERNEL26
static void usb_remote_send(struct urb *urb, struct pt_regs *regs)
#else
static void usb_remote_send(struct urb *urb)
#endif
{
	struct irctl *ir;
	if (!urb)
		return;
	if (!(ir = urb->context)) {
		usb_unlink_urb(urb);
		return;
	}
	dprintk(DRIVER_NAME "[%d]: usb out called\n", ir->devnum);
	if (urb->status)
		return;
	ir->send_flags |= SEND_FLAG_COMPLETE;
	wmb();
	if (waitqueue_active(&ir->wait_out))
		wake_up(&ir->wait_out);
}
#if KERNEL26
static int usb_remote_probe(struct usb_interface *intf,
				const struct usb_device_id *id)
{
	struct usb_device *dev = NULL;
	struct usb_host_interface *idesc = NULL;
#else
static void *usb_remote_probe(struct usb_device *dev, unsigned int ifnum,
				const struct usb_device_id *id)
{
	struct usb_interface *intf;
	struct usb_interface_descriptor *idesc;
#endif
	struct usb_endpoint_descriptor *ep_in, *ep_out;
	struct irctl *ir = NULL;
	struct lirc_plugin *plugin = NULL;
	struct lirc_buffer *rbuf = NULL;
	int devnum, pipe, maxp, len, buf_len, bytes_in_key;
	int minor = 0;
	char buf[63], name[128]="";
	int mem_failure = 0;
	dprintk(DRIVER_NAME ": usb probe called\n");
#if KERNEL26
	dev = interface_to_usbdev(intf);
	idesc = &intf->altsetting[intf->act_altsetting];
	if (idesc->desc.bNumEndpoints != 2)
		return -ENODEV;
	ep_in = &idesc->endpoint[0].desc;
	ep_out = &idesc->endpoint[1].desc;
	if (((ep_in->bEndpointAddress & USB_ENDPOINT_DIR_MASK) != USB_DIR_IN)
		|| (ep_in->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
		!= USB_ENDPOINT_XFER_INT)
		return -ENODEV;
#else
	intf = &dev->actconfig->interface[ifnum];
	idesc = &intf->altsetting[intf->act_altsetting];
	if (idesc->bNumEndpoints != 2)
		return NULL;
	ep_in = idesc->endpoint + 0;
	ep_out = idesc->endpoint + 1;
	if (((ep_in->bEndpointAddress & USB_ENDPOINT_DIR_MASK) != USB_DIR_IN)
		|| (ep_in->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
		!= USB_ENDPOINT_XFER_INT)
		return NULL;
#endif
	devnum = dev->devnum;
	pipe = usb_rcvintpipe(dev, ep_in->bEndpointAddress);
	maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
	bytes_in_key = CODE_LENGTH;
	len = (maxp > USB_BUFLEN) ? USB_BUFLEN : maxp;
	buf_len = len - (len % bytes_in_key);
	dprintk(DRIVER_NAME "[%d]: bytes_in_key=%d len=%d maxp=%d buf_len=%d\n",
		devnum, bytes_in_key, len, maxp, buf_len);
	/* allocate kernel memory */
	mem_failure = 0;
	if (!(ir = kmalloc(sizeof(struct irctl), GFP_KERNEL))) {
		mem_failure = 1;
	} else {
		memset(ir, 0, sizeof(struct irctl));
		if (!(plugin = kmalloc(sizeof(struct lirc_plugin), GFP_KERNEL))) {
			mem_failure = 2;
		} else if (!(rbuf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL))) {
			mem_failure = 3;
		} else if (lirc_buffer_init(rbuf, bytes_in_key, USB_BUFLEN/bytes_in_key)) {
			mem_failure = 4;
#if KERNEL26
		} else if (!(ir->buf_in = usb_buffer_alloc(dev, buf_len, SLAB_ATOMIC, &ir->dma_in))) {
			mem_failure = 5;
		} else if (!(ir->buf_out = usb_buffer_alloc(dev, USB_BUFLEN, SLAB_ATOMIC, &ir->dma_out))) {
			mem_failure = 6;
		} else if (!(ir->urb_in = usb_alloc_urb(0, GFP_KERNEL))) {
			mem_failure = 7;
		} else if (!(ir->urb_out = usb_alloc_urb(0, GFP_KERNEL))) {
			mem_failure = 8;
#else
		} else if (!(ir->buf_in = kmalloc(buf_len, GFP_KERNEL))) {
			mem_failure = 5;
		} else if (!(ir->buf_out = kmalloc(USB_BUFLEN, GFP_KERNEL))) {
			mem_failure = 6;
		} else if (!(ir->urb_in = usb_alloc_urb(0))) {
			mem_failure = 7;
		} else if (!(ir->urb_out = usb_alloc_urb(0))) {
			mem_failure = 8;
#endif
		} else {
			memset(plugin, 0, sizeof(struct lirc_plugin));
			strcpy(plugin->name, DRIVER_NAME " ");
			plugin->minor = -1;
			plugin->code_length = bytes_in_key*8;
			plugin->features = LIRC_CAN_REC_LIRCCODE;
			plugin->data = ir;
			plugin->rbuf = rbuf;
			plugin->set_use_inc = &set_use_inc;
			plugin->set_use_dec = &set_use_dec;
			init_MUTEX(&ir->lock);
			init_waitqueue_head(&ir->wait_out);
			if ((minor = lirc_register_plugin(plugin)) < 0) {
				mem_failure = 9;
			}
		}
	}
	/* free allocated memory incase of failure */
	switch (mem_failure) {
	case 9:
		lirc_buffer_free(rbuf);
	case 8:
		usb_free_urb(ir->urb_out);
	case 7:
		usb_free_urb(ir->urb_in);
#if KERNEL26
	case 6:
		usb_buffer_free(dev, USB_BUFLEN, ir->buf_out, ir->dma_out);
	case 5:
		usb_buffer_free(dev, buf_len, ir->buf_in, ir->dma_in);
#else
	case 6:
		kfree(ir->buf_out);
	case 5:
		kfree(ir->buf_in);
#endif
	case 4:
		kfree(rbuf);
	case 3:
		kfree(plugin);
	case 2:
		kfree(ir);
	case 1:
		printk(DRIVER_NAME "[%d]: out of memory (code=%d)\n",
			devnum, mem_failure);
#if KERNEL26
		return -ENOMEM;
#else
		return NULL;
#endif
	}
	plugin->minor = minor;
	ir->p = plugin;
	ir->devnum = devnum;
	ir->usbdev = dev;
	ir->len_in = buf_len;
	ir->connected = 0;
	usb_fill_int_urb(ir->urb_in, dev, pipe, ir->buf_in,
		buf_len, usb_remote_recv, ir, ep_in->bInterval);
	usb_fill_int_urb(ir->urb_out, dev,
		usb_sndintpipe(dev, ep_out->bEndpointAddress), ir->buf_out,
		USB_BUFLEN, usb_remote_send, ir, ep_out->bInterval);
	if (dev->descriptor.iManufacturer
		&& usb_string(dev, dev->descriptor.iManufacturer, buf, 63) > 0)
		strncpy(name, buf, 128);
	if (dev->descriptor.iProduct
		&& usb_string(dev, dev->descriptor.iProduct, buf, 63) > 0)
		snprintf(name, 128, "%s %s", name, buf);
	printk(DRIVER_NAME "[%d]: %s on usb%d:%d\n", devnum, name,
	       dev->bus->busnum, devnum);
	send_packet(ir, 0x8004, init1);
	send_packet(ir, 0x8007, init2);
#if KERNEL26
	usb_set_intfdata(intf, ir);
	return SUCCESS;
#else
	return ir;
#endif
}
#if KERNEL26
static void usb_remote_disconnect(struct usb_interface *intf)
{
	struct usb_device *dev = interface_to_usbdev(intf);
	struct irctl *ir = usb_get_intfdata(intf);
	usb_set_intfdata(intf, NULL);
#else
static void usb_remote_disconnect(struct usb_device *dev, void *ptr)
{
	struct irctl *ir = ptr;
#endif
	if (!ir || !ir->p)
		return;
	ir->usbdev = NULL;
	wake_up_all(&ir->wait_out);
	IRLOCK;
	usb_unlink_urb(ir->urb_in);
	usb_unlink_urb(ir->urb_out);
	usb_free_urb(ir->urb_in);
	usb_free_urb(ir->urb_out);
#if KERNEL26
	usb_buffer_free(dev, ir->len_in, ir->buf_in, ir->dma_in);
	usb_buffer_free(dev, USB_BUFLEN, ir->buf_out, ir->dma_out);
#else
	kfree(ir->buf_in);
	kfree(ir->buf_out);
#endif
	IRUNLOCK;
	unregister_from_lirc(ir);
}
static struct usb_device_id usb_remote_id_table [] = {
	{ USB_DEVICE(0x0bc7, 0x0002) },		/* X10 USB Firecracker Interface */
	{ USB_DEVICE(0x0bc7, 0x0003) },		/* X10 VGA Video Sender */
	{ USB_DEVICE(0x0bc7, 0x0004) },		/* ATI Wireless Remote Receiver */
	{ USB_DEVICE(0x0bc7, 0x0005) },		/* NVIDIA Wireless Remote Receiver */
	{ USB_DEVICE(0x0bc7, 0x0006) },		/* ATI Wireless Remote Receiver */
	{ USB_DEVICE(0x0bc7, 0x0007) },		/* X10 USB Wireless Transceiver */
	{ USB_DEVICE(0x0bc7, 0x0008) },		/* X10 USB Wireless Transceiver */
	{ USB_DEVICE(0x0bc7, 0x0009) },		/* X10 USB Wireless Transceiver */
	{ USB_DEVICE(0x0bc7, 0x000A) },		/* X10 USB Wireless Transceiver */
	{ USB_DEVICE(0x0bc7, 0x000B) },		/* X10 USB Transceiver */
	{ USB_DEVICE(0x0bc7, 0x000C) },		/* X10 USB Transceiver */
	{ USB_DEVICE(0x0bc7, 0x000D) },		/* X10 USB Transceiver */
	{ USB_DEVICE(0x0bc7, 0x000E) },		/* X10 USB Transceiver */
	{ USB_DEVICE(0x0bc7, 0x000F) },		/* X10 USB Transceiver */
	{ }					/* Terminating entry */
};
static struct usb_driver usb_remote_driver = {
	.owner =	THIS_MODULE,
	.name =		DRIVER_NAME,
	.probe =	usb_remote_probe,
	.disconnect =	usb_remote_disconnect,
	.id_table =	usb_remote_id_table
};
static int __init usb_remote_init(void)
{
	int i;
	printk("\n" DRIVER_NAME ": " DRIVER_DESC " v" DRIVER_VERSION "\n");
	printk(DRIVER_NAME ": " DRIVER_AUTHOR "\n");
	dprintk(DRIVER_NAME ": debug mode enabled\n");
	request_module("lirc_dev");
	if ((i = usb_register(&usb_remote_driver)) < 0) {
		printk(DRIVER_NAME ": usb register failed, result = %d\n", i);
		return -ENODEV;
	}
	return SUCCESS;
}
static void __exit usb_remote_exit(void)
{
	usb_deregister(&usb_remote_driver);
}
module_init(usb_remote_init);
module_exit(usb_remote_exit);
MODULE_AUTHOR (DRIVER_AUTHOR);
MODULE_DESCRIPTION (DRIVER_DESC);
MODULE_LICENSE ("GPL");
MODULE_DEVICE_TABLE (usb, usb_remote_id_table);
MODULE_PARM(debug, "i");
MODULE_PARM_DESC(debug, "enable driver debug mode");
#if !KERNEL26
EXPORT_NO_SYMBOLS;
#endif
(-) linux-2.6.5.orig/drivers/char/lirc/lirc_bt829.c (+368 lines)
Line 0    Link Here 
/*
 * Remote control driver for the TV-card based on bt829
 *
 *  by Leonid Froenchenko <lfroen@galileo.co.il>
 *
 *  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/version.h>
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/threads.h>
#include <linux/sched.h>
#include <linux/ioport.h>
#include <linux/pci.h>
#include <linux/delay.h>
#include <linux/init.h>
#include "lirc_dev.h"
int poll_main(void);
int atir_init_start(void);
void write_index(unsigned char index,unsigned int value);
unsigned int read_index(unsigned char index);
void do_i2c_start(void);
void do_i2c_stop(void);
void seems_wr_byte(unsigned char al);
unsigned char seems_rd_byte(void);
unsigned int read_index(unsigned char al);
void write_index(unsigned char ah,unsigned int edx);
void cycle_delay(int cycle);
void do_set_bits(unsigned char bl);
unsigned char do_get_bits(void);
#define DATA_PCI_OFF 0x7FFC00
#define WAIT_CYCLE   20
int atir_minor;
unsigned long pci_addr_phys, pci_addr_lin;
struct lirc_plugin atir_plugin;
int do_pci_probe(void)
{
	struct pci_dev *my_dev;
	my_dev = (struct pci_dev *)pci_find_device(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_264VT,NULL);
	if ( my_dev ) {
		printk(KERN_ERR "ATIR: Using device: %s\n",my_dev->slot_name);
		pci_addr_phys = 0;
		if ( my_dev->resource[0].flags & IORESOURCE_MEM ) {
			pci_addr_phys = my_dev->resource[0].start;
			printk(KERN_INFO "ATIR memory at 0x%08X \n",(unsigned int)pci_addr_phys);
		}
		if ( pci_addr_phys == 0 ) {
			printk(KERN_ERR "ATIR no memory resource ?\n");
			return 0;
		}
	} else {
		printk(KERN_ERR "ATIR: pci_prob failed\n");
		return 0;
	}
	return 1;
}
int atir_add_to_buf (void* data, struct lirc_buffer* buf)
{
	unsigned char key;
	int status;
	status = poll_main();
	key = (status >> 8) & 0xFF;
	if( status & 0xFF )
	{
	//    printk(KERN_INFO "ATIR reading key %02X\n",*key);
		lirc_buffer_write_1( buf, &key );
		return 0;
	}
	return -ENODATA;
}
int atir_set_use_inc(void* data)
{
	printk(KERN_DEBUG "ATIR driver is opened\n");
	return 0;
}
void atir_set_use_dec(void* data)
{
	printk(KERN_DEBUG "ATIR driver is closed\n");
}
static int __init lirc_bt829_init(void)
{
	if ( !do_pci_probe() ) {
		return 1;
	}
	if ( !atir_init_start() ) {
		return 1;
	}
	strcpy(atir_plugin.name,"ATIR");
	atir_plugin.minor       = -1;
	atir_plugin.code_length = 8;
	atir_plugin.sample_rate = 10;
	atir_plugin.data        = 0;
	atir_plugin.add_to_buf  = atir_add_to_buf;
	atir_plugin.set_use_inc = atir_set_use_inc;
	atir_plugin.set_use_dec = atir_set_use_dec;
	atir_minor = lirc_register_plugin(&atir_plugin);
	printk(KERN_DEBUG "ATIR driver is registered on minor %d\n",atir_minor);
	return 0;
}
static void __exit lirc_bt829_exit(void)
{
	lirc_unregister_plugin(atir_minor);
}
int atir_init_start(void)
{
	pci_addr_lin = (unsigned long)ioremap(pci_addr_phys + DATA_PCI_OFF,0x400);
	if ( pci_addr_lin == 0 ) {
		printk(KERN_INFO "atir: pci mem must be mapped\n");
		return 0;
	}
	return 1;
}
void cycle_delay(int cycle)
{
	udelay(WAIT_CYCLE*cycle);
}
int poll_main()
{
	unsigned char status_high, status_low;
	do_i2c_start();
	seems_wr_byte(0xAA);
	seems_wr_byte(0x01);
	do_i2c_start();
	seems_wr_byte(0xAB);
	status_low = seems_rd_byte();
	status_high = seems_rd_byte();
	do_i2c_stop();
	return (status_high << 8) | status_low;
}
void do_i2c_start(void)
{
	do_set_bits(3);
	cycle_delay(4);
	do_set_bits(1);
	cycle_delay(7);
	do_set_bits(0);
	cycle_delay(2);
}
void do_i2c_stop(void)
{
	unsigned char bits;
	bits =  do_get_bits() & 0xFD;
	do_set_bits(bits);
	cycle_delay(1);
	bits |= 1;
	do_set_bits(bits);
	cycle_delay(2);
	bits |= 2;
	do_set_bits(bits);
	bits = 3;
	do_set_bits(bits);
	cycle_delay(2);
}
void seems_wr_byte(unsigned char value)
{
	int i;
	unsigned char reg;
	reg = do_get_bits();
	for(i = 0;i < 8;i++) {
		if ( value & 0x80 ) {
			reg |= 0x02;
		} else {
			reg &= 0xFD;
		}
		do_set_bits(reg);
		cycle_delay(1);
		reg |= 1;
		do_set_bits(reg);
		cycle_delay(1);
		reg &= 0xFE;
		do_set_bits(reg);
		cycle_delay(1);
		value <<= 1;
	}
	cycle_delay(2);
	reg |= 2;
	do_set_bits(reg);
	reg |= 1;
	do_set_bits(reg);
	cycle_delay(1);
	do_get_bits();
	reg &= 0xFE;
	do_set_bits(reg);
	cycle_delay(3);
}
unsigned char seems_rd_byte(void)
{
	int i;
	int rd_byte;
	unsigned char bits_2, bits_1;
	bits_1 = do_get_bits() | 2;
	do_set_bits(bits_1);
	rd_byte = 0;
	for(i = 0;i < 8;i++) {
		bits_1 &= 0xFE;
		do_set_bits(bits_1);
		cycle_delay(2);
		bits_1 |= 1;
		do_set_bits(bits_1);
		cycle_delay(1);
		if ( (bits_2 = do_get_bits()) & 2 ) {
			rd_byte |= 1;
		}
		rd_byte <<= 1;
	}
	bits_1 = 0;
	if ( bits_2 == 0 ) {
		bits_1 |= 2;
	}
	do_set_bits(bits_1);
	cycle_delay(2);
	bits_1 |= 1;
	do_set_bits(bits_1);
	cycle_delay(3);
	bits_1 &= 0xFE;
	do_set_bits(bits_1);
	cycle_delay(2);
	rd_byte >>= 1;
	rd_byte &= 0xFF;
	return rd_byte;
}
void do_set_bits(unsigned char new_bits)
{
	int reg_val;
	reg_val = read_index(0x34);
	if ( new_bits & 2 ) {
		reg_val &= 0xFFFFFFDF;
		reg_val |= 1;
	} else {
		reg_val &= 0xFFFFFFFE;
		reg_val |= 0x20;
	}
	reg_val |= 0x10;
	write_index(0x34,reg_val);
	reg_val = read_index(0x31);
	if ( new_bits & 1 ) {
		reg_val |= 0x1000000;
	} else {
		reg_val &= 0xFEFFFFFF;
	}
	reg_val |= 0x8000000;
	write_index(0x31,reg_val);
}
unsigned char do_get_bits(void)
{
	unsigned char bits;
	int reg_val;
	reg_val = read_index(0x34);
	reg_val |= 0x10;
	reg_val &= 0xFFFFFFDF;
	write_index(0x34,reg_val);
	reg_val = read_index(0x34);
	bits = 0;
	if ( reg_val & 8 ) {
		bits |= 2;
	} else {
		bits &= 0xFD;
	}
	reg_val = read_index(0x31);
	if ( reg_val & 0x1000000 ) {
		bits |= 1;
	} else {
		bits &= 0xFE;
	}
	return bits;
}
unsigned int read_index(unsigned char index)
{
	unsigned int addr, value;
	//  addr = pci_addr_lin + DATA_PCI_OFF + ((index & 0xFF) << 2);
	addr = pci_addr_lin + ((index & 0xFF) << 2);
	value = readl(addr);
	return value;
}
void write_index(unsigned char index,unsigned int reg_val)
{
	unsigned int addr;
	addr = pci_addr_lin + ((index & 0xFF) << 2);
	writel(reg_val,addr);
}
MODULE_AUTHOR("Froenchenko Leonid");
MODULE_DESCRIPTION("IR remote driver for bt829 based TV cards");
MODULE_LICENSE("GPL");
module_init(lirc_bt829_init);
module_exit(lirc_bt829_exit);
(-) linux-2.6.5.orig/drivers/char/lirc/lirc_dev.c (+727 lines)
Line 0    Link Here 
/*
 * LIRC base driver
 *
 * (L) by Artur Lipowski <alipowski@interia.pl>
 *
 *  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
 *
 * $Id: lirc_dev.c,v 1.27 2004/01/13 13:59:48 lirc Exp $
 *
 */
#include <linux/version.h>
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/ioctl.h>
#include <linux/fs.h>
#include <linux/poll.h>
#include <linux/smp_lock.h>
#include <asm/uaccess.h>
#include <asm/semaphore.h>
#include <asm/errno.h>
#define __KERNEL_SYSCALLS__
#include <linux/unistd.h>
#include <linux/init.h>
#include <linux/devfs_fs_kernel.h>
#include <linux/lirc.h>
#include "lirc_dev.h"
static int debug = 0;
MODULE_PARM(debug,"i");
#define IRCTL_DEV_NAME    "BaseRemoteCtl"
#define SUCCESS           0
#define NOPLUG            -1
#define dprintk           if (debug) printk
#define LOGHEAD           "lirc_dev (%s[%d]): "
struct irctl
{
	struct lirc_plugin p;
	int open;
	struct lirc_buffer *buf;
	int t_pid;
	struct semaphore *t_notify;
	struct semaphore *t_notify2;
	int shutdown;
	long jiffies_to_wait;
};
DECLARE_MUTEX(plugin_lock);
static struct irctl irctls[CONFIG_LIRC_MAX_DEV];
static struct file_operations fops;
/*  helper function
 *  initializes the irctl structure
 */
static inline void init_irctl(struct irctl *ir)
{
	memset(&ir->p, 0, sizeof(struct lirc_plugin));
	ir->p.minor = NOPLUG;
	ir->t_pid = -1;
	ir->t_notify = NULL;
	ir->t_notify2 = NULL;
	ir->shutdown = 0;
	ir->jiffies_to_wait = 0;
	ir->open = 0;
}
/*  helper function
 *  reads key codes from plugin and puts them into buffer
 *  buffer free space is checked and locking performed
 *  returns 0 on success
 */
inline static int add_to_buf(struct irctl *ir)
{
	if (lirc_buffer_full(ir->buf)) {
		dprintk(LOGHEAD "buffer overflow\n",
			ir->p.name, ir->p.minor);
		return -EOVERFLOW;
	}
    if(ir->p.add_to_buf) {
        int res = -ENODATA;
        int got_data = 0;
        
        /* Service the device as long as it is returning
         * data and we have space
         */
        while( !lirc_buffer_full(ir->buf) )
        {
            res = ir->p.add_to_buf( ir->p.data, ir->buf );
            if( res == SUCCESS )
                got_data++;
            else
                break;
        }
        if( res == -ENODEV )
        {
            ir->shutdown = 1;
        }
        return (got_data ? SUCCESS : res);
    }
	return SUCCESS;
}
/* main function of the polling thread
 */
static int lirc_thread(void *irctl)
{
	struct irctl *ir = irctl;
	daemonize("lirc_dev");
	if (ir->t_notify != NULL) {
		up(ir->t_notify);
	}
	dprintk(LOGHEAD "poll thread started\n", ir->p.name, ir->p.minor);
	do {
		if (ir->open) {
			if (ir->jiffies_to_wait) {
				current->state = TASK_INTERRUPTIBLE;
				schedule_timeout(ir->jiffies_to_wait);
			} else {
				interruptible_sleep_on(ir->p.get_queue(ir->p.data));
			}
			if (ir->shutdown || !ir->open) {
				break;
			}
			if (!add_to_buf(ir)) {
				wake_up_interruptible(&ir->buf->wait_poll);
			}
		} else {
			/* if device not opened so we can sleep half a second */
			current->state = TASK_INTERRUPTIBLE;
			schedule_timeout(HZ/2);
		}
	} while (!ir->shutdown);
	dprintk(LOGHEAD "poll thread ended\n", ir->p.name, ir->p.minor);
	if (ir->t_notify2 != NULL) {
		down(ir->t_notify2);
	}
	ir->t_pid = -1;
	if (ir->t_notify != NULL) {
		up(ir->t_notify);
	}
	return 0;
}
/*
 *
 */
int lirc_register_plugin(struct lirc_plugin *p)
{
	struct irctl *ir;
	int minor;
	int bytes_in_key;
	DECLARE_MUTEX_LOCKED(tn);
	if (!p) {
		printk("lirc_dev: lirc_register_plugin:"
		       "plugin pointer must be not NULL!\n");
		return -EBADRQC;
	}
	if (CONFIG_LIRC_MAX_DEV <= p->minor) {
		printk("lirc_dev: lirc_register_plugin:"
		       "\" minor\" must be beetween 0 and %d (%d)!\n",
		       CONFIG_LIRC_MAX_DEV-1, p->minor);
		return -EBADRQC;
	}
	if (1 > p->code_length || (BUFLEN*8) < p->code_length) {
		printk("lirc_dev: lirc_register_plugin:"
		       "code length in bits for minor (%d) "
		       "must be less than %d!\n",
		       p->minor, BUFLEN*8);
		return -EBADRQC;
	}
	printk("lirc_dev: lirc_register_plugin:"
	       "sample_rate: %d\n",p->sample_rate);
	if (p->sample_rate) {
		if (2 > p->sample_rate || 100 < p->sample_rate) {
			printk("lirc_dev: lirc_register_plugin:"
			       "sample_rate must be beetween 2 and 100!\n");
			return -EBADRQC;
		}
        if (!p->add_to_buf) {
            printk("lirc_dev: lirc_register_plugin:"
                   "add_to_buf cannot be NULL when sample_rate is set\n");
            return -EBADRQC;
        }
	} else if (!(p->fops && p->fops->read)
			&& !p->get_queue && !p->rbuf) {
		printk("lirc_dev: lirc_register_plugin:"
		       "fops->read, get_queue and rbuf cannot all be NULL!\n");
		return -EBADRQC;
	} else if (!p->get_queue && !p->rbuf) {
		if (!(p->fops && p->fops->read && p->fops->poll)
				|| (!p->fops->ioctl && !p->ioctl)) {
			printk("lirc_dev: lirc_register_plugin:"
			       "neither read, poll nor ioctl can be NULL!\n");
			return -EBADRQC;
		}
	}
	down_interruptible(&plugin_lock);
	minor = p->minor;
	if (0 > minor) {
		/* find first free slot for plugin */
		for (minor=0; minor<CONFIG_LIRC_MAX_DEV; minor++)
			if (irctls[minor].p.minor == NOPLUG)
				break;
		if (CONFIG_LIRC_MAX_DEV == minor) {
			printk("lirc_dev: lirc_register_plugin: "
			       "no free slots for plugins!\n");
			up(&plugin_lock);
			return -ENOMEM;
		}
	} else if (irctls[minor].p.minor != NOPLUG) {
		printk("lirc_dev: lirc_register_plugin:"
		       "minor (%d) just registerd!\n", minor);
		up(&plugin_lock);
		return -EBUSY;
	}
	ir = &irctls[minor];
	if (p->sample_rate) {
		ir->jiffies_to_wait = HZ / p->sample_rate;
	} else {
                /* it means - wait for externeal event in task queue */
		ir->jiffies_to_wait = 0;
	}
	/* some safety check 8-) */
	p->name[sizeof(p->name)-1] = '\0';
	bytes_in_key = p->code_length/8 + (p->code_length%8 ? 1 : 0);
	if (p->rbuf) {
		ir->buf = p->rbuf;
	} else {
		ir->buf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
		lirc_buffer_init(ir->buf, bytes_in_key, BUFLEN/bytes_in_key);
	}
	if (p->features==0)
		p->features = (p->code_length > 8) ?
			LIRC_CAN_REC_LIRCCODE : LIRC_CAN_REC_CODE;
	ir->p = *p;
	ir->p.minor = minor;
#ifdef CONFIG_DEVFS_FS
	devfs_mk_cdev(MKDEV(IRCTL_DEV_MAJOR, ir->p.minor), S_IFCHR | S_IRUSR | S_IWUSR, "lirc/lirc%d", ir->p.minor);
#endif
	if(p->sample_rate || p->get_queue) {
		/* try to fire up polling thread */
		ir->t_notify = &tn;
		ir->t_pid = kernel_thread(lirc_thread, (void*)ir, 0);
		if (ir->t_pid < 0) {
			up(&plugin_lock);
			printk("lirc_dev: lirc_register_plugin:"
			       "cannot run poll thread for minor = %d\n",
			       p->minor);
			return -ECHILD;
		}
		down(&tn);
		ir->t_notify = NULL;
	}
	up(&plugin_lock);
	try_module_get(THIS_MODULE);
	dprintk("lirc_dev: plugin %s registered at minor number = %d\n",
		ir->p.name, ir->p.minor);
	return minor;
}
/*
 *
 */
int lirc_unregister_plugin(int minor)
{
	struct irctl *ir;
	DECLARE_MUTEX_LOCKED(tn);
	DECLARE_MUTEX_LOCKED(tn2);
	if (minor < 0 || minor >= CONFIG_LIRC_MAX_DEV) {
		printk("lirc_dev: lirc_unregister_plugin:"
		       "\" minor\" must be beetween 0 and %d!\n",
		       CONFIG_LIRC_MAX_DEV-1);
		return -EBADRQC;
	}
	ir = &irctls[minor];
	down_interruptible(&plugin_lock);
	if (ir->p.minor != minor) {
		printk("lirc_dev: lirc_unregister_plugin:"
		       "minor (%d) device not registered!", minor);
		up(&plugin_lock);
		return -ENOENT;
	}
	if (ir->open) {
		printk("lirc_dev: lirc_unregister_plugin:"
		       "plugin %s[%d] in use!", ir->p.name, ir->p.minor);
		up(&plugin_lock);
		return -EBUSY;
	}
	/* end up polling thread */
	if (ir->t_pid >= 0) {
		ir->t_notify = &tn;
		ir->t_notify2 = &tn2;
		ir->shutdown = 1;
		{
			struct task_struct *p;
			p = find_task_by_pid(ir->t_pid);
			wake_up_process(p);
		}
		up(&tn2);
		down(&tn);
		ir->t_notify = NULL;
		ir->t_notify2 = NULL;
	}
	dprintk("lirc_dev: plugin %s unregistered from minor number = %d\n",
		ir->p.name, ir->p.minor);
#ifdef CONFIG_DEVFS_FS
	devfs_remove("lirc/lirc%d", ir->p.minor);
#endif
	if (ir->buf != ir->p.rbuf){
		lirc_buffer_free(ir->buf);
		kfree(ir->buf);
	}
	ir->buf = NULL;
	init_irctl(ir);
	up(&plugin_lock);
	module_put(THIS_MODULE);
	return SUCCESS;
}
/*
 *
 */
static int irctl_open(struct inode *inode, struct file *file)
{
	struct irctl *ir;
	int retval;
	if (MINOR(inode->i_rdev) >= CONFIG_LIRC_MAX_DEV) {
		dprintk("lirc_dev [%d]: open result = -ENODEV\n",
			MINOR(inode->i_rdev));
		return -ENODEV;
	}
	ir = &irctls[MINOR(inode->i_rdev)];
	dprintk(LOGHEAD "open called\n", ir->p.name, ir->p.minor);
	/* if the plugin has an open function use it instead */
	if(ir->p.fops && ir->p.fops->open)
		return ir->p.fops->open(inode, file);
	down_interruptible(&plugin_lock);
	if (ir->p.minor == NOPLUG) {
		up(&plugin_lock);
		dprintk(LOGHEAD "open result = -ENODEV\n",
			ir->p.name, ir->p.minor);
		return -ENODEV;
	}
	if (ir->open) {
		up(&plugin_lock);
		dprintk(LOGHEAD "open result = -EBUSY\n",
			ir->p.name, ir->p.minor);
		return -EBUSY;
	}
	/* there is no need for locking here because ir->open is 0
         * and lirc_thread isn't using buffer
	 * plugins which use irq's should allocate them on set_use_inc,
	 * so there should be no problem with those either.
         */
	ir->buf->head = ir->buf->tail;
	ir->buf->fill = 0;
	++ir->open;
	retval = ir->p.set_use_inc(ir->p.data);
	up(&plugin_lock);
	if (retval != SUCCESS) {
		--ir->open;
		return retval;
	}
	dprintk(LOGHEAD "open result = %d\n", ir->p.name, ir->p.minor, SUCCESS);
	return SUCCESS;
}
/*
 *
 */
static int irctl_close(struct inode *inode, struct file *file)
{
	struct irctl *ir = &irctls[MINOR(inode->i_rdev)];
	dprintk(LOGHEAD "close called\n", ir->p.name, ir->p.minor);
	/* if the plugin has a close function use it instead */
	if(ir->p.fops && ir->p.fops->release)
		return ir->p.fops->release(inode, file);
	down_interruptible(&plugin_lock);
	--ir->open;
	ir->p.set_use_dec(ir->p.data);
	up(&plugin_lock);
	return SUCCESS;
}
/*
 *
 */
static unsigned int irctl_poll(struct file *file, poll_table *wait)
{
	struct irctl *ir = &irctls[MINOR(file->f_dentry->d_inode->i_rdev)];
	dprintk(LOGHEAD "poll called\n", ir->p.name, ir->p.minor);
	/* if the plugin has a poll function use it instead */
	if(ir->p.fops && ir->p.fops->poll)
		return ir->p.fops->poll(file, wait);
	poll_wait(file, &ir->buf->wait_poll, wait);
	dprintk(LOGHEAD "poll result = %s\n",
		ir->p.name, ir->p.minor,
		lirc_buffer_empty(ir->buf) ? "0" : "POLLIN|POLLRDNORM");
	return lirc_buffer_empty(ir->buf) ? 0 : (POLLIN|POLLRDNORM);
}
/*
 *
 */
static int irctl_ioctl(struct inode *inode, struct file *file,
                       unsigned int cmd, unsigned long arg)
{
	unsigned long mode;
	int result;
	struct irctl *ir = &irctls[MINOR(inode->i_rdev)];
	dprintk(LOGHEAD "ioctl called (%u)\n",
		ir->p.name, ir->p.minor, cmd);
	/* if the plugin has a ioctl function use it instead */
	if(ir->p.fops && ir->p.fops->ioctl)
		return ir->p.fops->ioctl(inode, file, cmd, arg);
	if (ir->p.minor == NOPLUG) {
		dprintk(LOGHEAD "ioctl result = -ENODEV\n",
			ir->p.name, ir->p.minor);
		return -ENODEV;
	}
	/* Give the plugin a chance to handle the ioctl */
	if(ir->p.ioctl){
		result = ir->p.ioctl(inode, file, cmd, arg);
		if (result != -ENOIOCTLCMD)
			return result;
	}
	/* The plugin can't handle cmd */
	result = SUCCESS;
	switch(cmd)
	{
	case LIRC_GET_FEATURES:
		result = put_user(ir->p.features, (unsigned long*)arg);
		break;
	case LIRC_GET_REC_MODE:
		if(!(ir->p.features&LIRC_CAN_REC_MASK))
			return(-ENOSYS);
		result = put_user(LIRC_REC2MODE
				  (ir->p.features&LIRC_CAN_REC_MASK),
				  (unsigned long*)arg);
		break;
	case LIRC_SET_REC_MODE:
		if(!(ir->p.features&LIRC_CAN_REC_MASK))
			return(-ENOSYS);
		result = get_user(mode, (unsigned long*)arg);
		if(!result && !(LIRC_MODE2REC(mode) & ir->p.features)) {
			result = -EINVAL;
		}
		/* FIXME: We should actually set the mode somehow
		 * but for now, lirc_serial doesn't support mode changin
		 * eighter */
		break;
	case LIRC_GET_LENGTH:
		result = put_user((unsigned long)ir->p.code_length,
				  (unsigned long *)arg);
		break;
	default:
		result = -ENOIOCTLCMD;
	}
	dprintk(LOGHEAD "ioctl result = %d\n",
		ir->p.name, ir->p.minor, result);
	return result;
}
/*
 *
 */
static ssize_t irctl_read(struct file *file,
			  char *buffer,
			  size_t length,
			  loff_t *ppos)
{
	struct irctl *ir = &irctls[MINOR(file->f_dentry->d_inode->i_rdev)];
	unsigned char buf[ir->buf->chunk_size];
	int ret=0, written=0;
	DECLARE_WAITQUEUE(wait, current);
	dprintk(LOGHEAD "read called\n", ir->p.name, ir->p.minor);
	/* if the plugin has a specific read function use it instead */
	if(ir->p.fops && ir->p.fops->read)
		return ir->p.fops->read(file, buffer, length, ppos);
	if (length % ir->buf->chunk_size) {
		dprintk(LOGHEAD "read result = -EINVAL\n",
			ir->p.name, ir->p.minor);
		return -EINVAL;
	}
	/* we add ourselves to the task queue before buffer check
         * to avoid losing scan code (in case when queue is awaken somewhere
	 * beetwen while condition checking and scheduling)
	 */
	add_wait_queue(&ir->buf->wait_poll, &wait);
	current->state = TASK_INTERRUPTIBLE;
	/* while we did't provide 'length' bytes, device is opened in blocking
	 * mode and 'copy_to_user' is happy, wait for data.
	 */
	while (written < length && ret == 0) {
		if (lirc_buffer_empty(ir->buf)) {
			/* According to the read(2) man page, 'written' can be
			 * returned as less than 'length', instead of blocking
			 * again, returning -EWOULDBLOCK, or returning
			 * -ERESTARTSYS */
			if (written) break;
			if (file->f_flags & O_NONBLOCK) {
				dprintk(LOGHEAD "read result = -EWOULDBLOCK\n",
						ir->p.name, ir->p.minor);
				remove_wait_queue(&ir->buf->wait_poll, &wait);
				current->state = TASK_RUNNING;
				return -EWOULDBLOCK;
			}
			if (signal_pending(current)) {
				dprintk(LOGHEAD "read result = -ERESTARTSYS\n",
						ir->p.name, ir->p.minor);
				remove_wait_queue(&ir->buf->wait_poll, &wait);
				current->state = TASK_RUNNING;
				return -ERESTARTSYS;
			}
			schedule();
			current->state = TASK_INTERRUPTIBLE;
		} else {
			lirc_buffer_read_1(ir->buf, buf);
			ret = copy_to_user((void *)buffer+written, buf,
					   ir->buf->chunk_size);
			written += ir->buf->chunk_size;
		}
	}
	remove_wait_queue(&ir->buf->wait_poll, &wait);
	current->state = TASK_RUNNING;
	dprintk(LOGHEAD "read result = %s (%d)\n",
		ir->p.name, ir->p.minor, ret ? "-EFAULT" : "OK", ret);
	return ret ? -EFAULT : written;
}
static ssize_t irctl_write(struct file *file, const char *buffer,
			   size_t length, loff_t * ppos)
{
	struct irctl *ir = &irctls[MINOR(file->f_dentry->d_inode->i_rdev)];
	dprintk(LOGHEAD "write called\n", ir->p.name, ir->p.minor);
	/* if the plugin has a specific read function use it instead */
	if(ir->p.fops && ir->p.fops->write)
		return ir->p.fops->write(file, buffer, length, ppos);
	return -EINVAL;
}
static struct file_operations fops = {
	read:    irctl_read,
	write:   irctl_write,
	poll:    irctl_poll,
	ioctl:   irctl_ioctl,
	open:    irctl_open,
	release: irctl_close
};
static int __init lirc_dev_init(void)
{
	int i;
	for (i=0; i < CONFIG_LIRC_MAX_DEV; ++i) {
		init_irctl(&irctls[i]);
	}
	i = register_chrdev(IRCTL_DEV_MAJOR,
				   IRCTL_DEV_NAME,
				   &fops);
	if (i < 0) {
		printk ("lirc_dev: device registration failed with %d\n", i);
		return i;
	}
	printk("lirc_dev: IR Remote Control driver registered, at major %d \n",
	       IRCTL_DEV_MAJOR);
	return SUCCESS;
}
static void __exit lirc_dev_exit(void)
{
	int ret;
	ret = unregister_chrdev(IRCTL_DEV_MAJOR, IRCTL_DEV_NAME);
	if (0 > ret){
		printk("lirc_dev: error in module_unregister_chrdev: %d\n",
		       ret);
	} else {
		dprintk("lirc_dev: module successfully unloaded\n");
	}
}
/* ---------------------------------------------------------------------- */
/* For now dont try to use it as a static version !  */
MODULE_DESCRIPTION("LIRC base driver module");
MODULE_AUTHOR("Artur Lipowski");
MODULE_LICENSE("GPL");
EXPORT_SYMBOL(lirc_register_plugin);
EXPORT_SYMBOL(lirc_unregister_plugin);
module_init(lirc_dev_init);
module_exit(lirc_dev_exit);
/*
 * Overrides for Emacs so that we follow Linus's tabbing style.
 * ---------------------------------------------------------------------------
 * Local variables:
 * c-basic-offset: 4
 * End:
 */
(-) linux-2.6.5.orig/drivers/char/lirc/lirc_dev.h (+237 lines)
Line 0    Link Here 
/*
 * LIRC base driver
 *
 * (L) by Artur Lipowski <alipowski@interia.pl>
 *        This code is licensed under GNU GPL
 *
 * $Id: lirc_dev.h,v 1.11 2003/05/02 18:56:44 ranty Exp $
 *
 */
#ifndef _LINUX_LIRC_DEV_H
#define _LINUX_LIRC_DEV_H
#define BUFLEN            16
//#define LIRC_BUFF_POWER_OF_2
#ifdef LIRC_BUFF_POWER_OF_2
#define mod(n, div) ((n) & ((div) -1))
#else
#define mod(n, div) ((n) % (div))
#endif
#include <linux/slab.h>
#include <linux/fs.h>
struct lirc_buffer
{
        wait_queue_head_t wait_poll;
	spinlock_t lock;
	unsigned char *data;
	unsigned int chunk_size;
	unsigned int size; /* in chunks */
	unsigned int fill; /* in chunks */
	int head, tail;    /* in chunks */
	/* Using chunks instead of bytes pretends to simplify boundary checking
	 * And should allow for some performance fine tunning later */
};
static inline int lirc_buffer_init(struct lirc_buffer *buf,
				    unsigned int chunk_size,
				    unsigned int size)
{
	/* Adjusting size to the next power of 2 would allow for
	 * inconditional LIRC_BUFF_POWER_OF_2 optimization */
	init_waitqueue_head(&buf->wait_poll);
	spin_lock_init(&buf->lock);
	buf->head = buf->tail = buf->fill = 0;
	buf->chunk_size = chunk_size;
	buf->size = size;
	buf->data = kmalloc(size*chunk_size, GFP_KERNEL);
	if (buf->data == NULL)
		return -1;
	memset(buf->data, 0, size*chunk_size);
	return 0;
}
static inline void lirc_buffer_free(struct lirc_buffer *buf)
{
	kfree(buf->data);
	buf->data = NULL;
	buf->head = buf->tail = buf->fill = 0;
	buf->chunk_size = 0;
	buf->size = 0;
}
static inline int  lirc_buffer_full(struct lirc_buffer *buf)
{
	return (buf->fill >= buf->size);
}
static inline int  lirc_buffer_empty(struct lirc_buffer *buf)
{
	return !(buf->fill);
}
static inline int  lirc_buffer_available(struct lirc_buffer *buf)
{
    return (buf->size - buf->fill);
}
extern inline void lirc_buffer_lock(struct lirc_buffer *buf, unsigned long *flags)
{
	spin_lock_irqsave(&buf->lock, *flags);
}
extern inline void lirc_buffer_unlock(struct lirc_buffer *buf, unsigned long *flags)
{
	spin_unlock_irqrestore(&buf->lock, *flags);
}
static inline void _lirc_buffer_remove_1(struct lirc_buffer *buf)
{
	buf->head = mod(buf->head+1, buf->size);
	buf->fill -= 1;
}
static inline void lirc_buffer_remove_1(struct lirc_buffer *buf)
{
	unsigned long flags;
	lirc_buffer_lock(buf, &flags);
	_lirc_buffer_remove_1(buf);
	lirc_buffer_unlock(buf, &flags);
}
static inline void _lirc_buffer_read_1(struct lirc_buffer *buf,
				     unsigned char *dest)
{
	memcpy(dest, &buf->data[buf->head*buf->chunk_size], buf->chunk_size);
	buf->head = mod(buf->head+1, buf->size);
	buf->fill -= 1;
}
static inline void lirc_buffer_read_1(struct lirc_buffer *buf,
				      unsigned char *dest)
{
	unsigned long flags;
	lirc_buffer_lock(buf, &flags);
	_lirc_buffer_read_1(buf, dest);
	lirc_buffer_unlock(buf, &flags);
}
static inline void _lirc_buffer_write_1(struct lirc_buffer *buf,
				      unsigned char *orig)
{
	memcpy(&buf->data[buf->tail*buf->chunk_size], orig, buf->chunk_size);
	buf->tail = mod(buf->tail+1, buf->size);
	buf->fill++;
}
static inline void lirc_buffer_write_1(struct lirc_buffer *buf,
				       unsigned char *orig)
{
	unsigned long flags;
	lirc_buffer_lock(buf, &flags);
	_lirc_buffer_write_1(buf, orig);
	lirc_buffer_unlock(buf, &flags);
}
static inline void _lirc_buffer_write_n(struct lirc_buffer *buf,
                       unsigned char* orig, int count)
{
    memcpy(&buf->data[buf->tail*buf->chunk_size], orig, count*buf->chunk_size);
    buf->tail = mod(buf->tail+count, buf->size);
    buf->fill += count;
}
static inline void lirc_buffer_write_n(struct lirc_buffer *buf,
                       unsigned char* orig, int count)
{
    unsigned long flags;
    int space1;
    lirc_buffer_lock(buf,&flags);
    if( buf->head > buf->tail ) space1 = buf->head - buf->tail;
    else space1 = buf->size - buf->tail;
    
    if( count > space1 )
    {
        _lirc_buffer_write_n(buf, orig, space1);
        _lirc_buffer_write_n(buf, orig+(space1*buf->chunk_size), count-space1);
    }
    else
        _lirc_buffer_write_n(buf, orig, count);
    lirc_buffer_unlock(buf, &flags);
}
struct lirc_plugin
{
     char name[40];
     int minor;
     int code_length;
     int sample_rate;
     unsigned long features;
     void* data;
     int (*add_to_buf) (void* data, struct lirc_buffer* buf);
     wait_queue_head_t* (*get_queue) (void* data);
     struct lirc_buffer *rbuf;
     int (*set_use_inc) (void* data);
     void (*set_use_dec) (void* data);
     int (*ioctl) (struct inode *,struct file *,unsigned int, unsigned long);
     struct file_operations *fops;
};
/* name:
 * this string will be used for logs
 *
 * minor:
 * indicates minor device (/dev/lircd) number for registered plugin
 * if caller fills it with negative value, then the first free minor
 * number will be used (if available)
 *
 * code_length:
 * length of the remote control key code expressed in bits
 * if code_length > 8 then many bytes are returned through the device read
 * in such situation get_key should return key code values starting
 * from most significant byte (device read will preseve this order)
 * in addition if code_length > 8 then get_key will be called
 * several (ceil(code_length/8)) times in one pool pass (or after task queue
 * awake) key_no parameter denotes number of the requested byte (0 means first
 * byte)
 *
 * sample_rate:
 * sample_rate equal to 0 means that no polling will be performed and add_to_buf
 * will be triggered by external events (through task queue returned by
 * get_queue)
 *
 * data:
 * it may point to any plugin data and this pointer will be passed to all
 * callback functions
 *
 * add_to_buf:
 * add_to_buf will be called after specified period of the time or triggered
 * by the external event, this behavior depends on value of the sample_rate
 * this function will be called in user context. this routine should return 0
 * if data was added to the buffer and -ENODATA if none was available. this should
 * add some number of bits evenly divisible by code_length to the buffer
 *
 * get_queue:
 * this callback should return a pointer to the task queue which will be used
 * for external event waiting
 *
 * rbuf:
 * if not NULL, it will be used as a read buffer, you will have to write to
 * the buffer by other means, like irq's (see also lirc_serial.c).
 *
 * set_use_inc:
 * set_use_inc will be called after device is opened
 *
 * set_use_dec:
 * set_use_dec will be called after device is closed
 *
 * ioctl:
 * Some ioctl's can be directly handled by lirc_dev but will be forwared here
 * if not NULL and only handled if it returns -ENOIOCTLCMD (see also
 * lirc_serial.c).
 *
 * fops:
 * file_operations for drivers which don't fit the current plugin model.
 */
/* following functions can be called ONLY from user context
 *
 * returns negative value on error or minor number
 * of the registered device if success
 * contens of the structure pointed by p is copied
 */
extern int lirc_register_plugin(struct lirc_plugin *p);
/* returns negative value on error or 0 if success
*/
extern int lirc_unregister_plugin(int minor);
#endif
(-) linux-2.6.5.orig/drivers/char/lirc/lirc_gpio.c (+546 lines)
Line 0    Link Here 
/*
 * Remote control driver for the TV-card
 * key codes are obtained from GPIO port
 *
 * (L) by Artur Lipowski <alipowski@interia.pl>
 *     patch for the AverMedia by Santiago Garcia Mantinan <manty@i.am>
 *                            and Christoph Bartelmus <lirc@bartelmus.de>
 *     patch for the BestBuy by Miguel Angel Alvarez <maacruz@navegalia.com>
 *     patch for the Winfast TV2000 by Juan Toledo
 *     <toledo@users.sourceforge.net>
 *     patch for the I-O Data GV-BCTV5/PCI by Jens C. Rasmussen
 *     <jens.rasmussen@ieee.org>
 *
 *  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
 *
 * $Id: lirc_gpio.c,v 1.34 2003/11/02 15:38:34 lirc Exp $
 *
 */
#include <linux/version.h>
#include <linux/module.h>
#include <linux/kmod.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/moduleparam.h>
#include "../../media/video/bttv.h"
#include "../../media/video/bttvp.h"
#include "lirc_dev.h"
static int debug = 0;
static int card = 0;
static int minor = -1;
static int bttv_id = BTTV_UNKNOWN;
static unsigned long gpio_mask = 0;
static unsigned long gpio_enable = 0;
static unsigned long gpio_lock_mask = 0;
static unsigned long gpio_xor_mask = 0;
static unsigned int soft_gap = 0;
static unsigned char sample_rate = 10;
MODULE_PARM(debug,"i");
MODULE_PARM(card,"i");
MODULE_PARM(minor,"i");
MODULE_PARM(gpio_mask,"l");
MODULE_PARM(gpio_lock_mask,"l");
MODULE_PARM(gpio_xor_mask,"l");
MODULE_PARM(soft_gap,"i");
MODULE_PARM(sample_rate,"b");
MODULE_PARM(bttv_id,"i");
#undef dprintk
#define dprintk  if (debug) printk
struct rcv_info {
	int bttv_id;
	int card_id;
	unsigned long gpio_mask;
	unsigned long gpio_enable;
	unsigned long gpio_lock_mask;
	unsigned long gpio_xor_mask;
	unsigned int soft_gap;
	unsigned char sample_rate;
	unsigned char code_length;
};
static struct rcv_info rcv_infos[] = {
	{BTTV_UNKNOWN,                0,          0,          0,         0,          0,   0,  1,  0},
	{BTTV_PXELVWPLTVPAK,          0, 0x00003e00,          0, 0x0010000,          0,   0, 15, 32},
	{BTTV_PXELVWPLTVPRO,          0, 0x00001f00,          0, 0x0008000,          0, 500, 12, 32},
	{BTTV_PV_BT878P_9B,           0, 0x00001f00,          0, 0x0008000,          0, 500, 12, 32},
	{BTTV_PV_BT878P_PLUS,         0, 0x00001f00,          0, 0x0008000,          0, 500, 12, 32},
	{BTTV_AVERMEDIA,              0, 0x00f88000,          0, 0x0010000, 0x00010000,   0, 10, 32},
	{BTTV_AVPHONE98,     0x00011461, 0x003b8000, 0x00004000, 0x0800000, 0x00800000,   0, 10,  0}, /*mapped to Capture98*/
	{BTTV_AVERMEDIA98,   0x00021461, 0x003b8000, 0x00004000, 0x0800000, 0x00800000,   0, 10,  0}, /*mapped to Capture98*/
	{BTTV_AVPHONE98,     0x00031461, 0x00f88000,          0, 0x0010000, 0x00010000,   0, 10, 32}, /*mapped to Phone98*/
	/* is this one correct? */
	{BTTV_AVERMEDIA98,   0x00041461, 0x00f88000,          0, 0x0010000, 0x00010000,   0, 10, 32}, /*mapped to Phone98*/
	/* work-around for VDOMATE */
	{BTTV_AVERMEDIA98,   0x03001461, 0x00f88000,          0, 0x0010000, 0x00010000,   0, 10, 32}, /*mapped to Phone98*/
	/* reported by Danijel Korzinek, AVerTV GOw/FM */
	{BTTV_AVERMEDIA98,   0x00000000, 0x00f88000,          0, 0x0010000, 0x00010000,   0, 10, 32}, /*mapped to Phone98*/
	{BTTV_CHRONOS_VS2,            0, 0x000000f8,          0, 0x0000100,          0,   0, 20,  0},
	/* CPH031 and CPH033 cards (?) */
	/* MIRO was just a work-around */
	{BTTV_MIRO,                   0, 0x00001f00,          0, 0x0004000,          0,   0, 10, 32},
	{BTTV_DYNALINK,               0, 0x00001f00,          0, 0x0004000,          0,   0, 10, 32},
	{BTTV_WINVIEW_601,            0, 0x00001f00,          0, 0x0004000,          0,   0,  0, 32},
#ifdef BTTV_KWORLD
	{BTTV_KWORLD,                 0, 0x00007f00,          0, 0x0004000,          0,   0, 12, 32},
#endif
	/* just a guess */
	{BTTV_MAGICTVIEW061,          0, 0x0028e000,          0, 0x0020000,          0,   0, 20, 32},
 	{BTTV_MAGICTVIEW063,          0, 0x0028e000,          0, 0x0020000,          0,   0, 20, 32},
 	{BTTV_PHOEBE_TVMAS,           0, 0x0028e000,          0, 0x0020000,          0,   0, 20, 32},
#ifdef BTTV_BESTBUY_EASYTV2
        {BTTV_BESTBUY_EASYTV,         0, 0x00007F00,          0, 0x0004000,          0,   0, 10,  8},
        {BTTV_BESTBUY_EASYTV2,        0, 0x00007F00,          0, 0x0008000,          0,   0, 10,  8},
#endif
	/* lock_mask probably also 0x100, or maybe it is 0x0 for all others !?! */
	{BTTV_FLYVIDEO,               0, 0x000000f8,          0,         0,          0,   0,  0, 42},
 	{BTTV_FLYVIDEO_98,            0, 0x000000f8,          0, 0x0000100,          0,   0,  0, 42},
 	{BTTV_TYPHOON_TVIEW,          0, 0x000000f8,          0, 0x0000100,          0,   0,  0, 42},
#ifdef BTTV_FLYVIDEO_98FM
	/* smorar@alfonzo.smuts.uct.ac.za */
	{BTTV_FLYVIDEO_98FM,          0, 0x000000f8,          0, 0x0000100,          0,   0,  0, 42},
#endif
	/* The Leadtek WinFast TV 2000 XP card (id 0x6606107d) uses an
	 * extra gpio bit compared to the original TV 2000 card (id
	 * 0x217d6606); as the bttv-0.7.100 driver does not
	 * distinguish between the two cards, we enable the extra bit
	 * based on the card id: */
	{BTTV_WINFAST2000,   0x6606107d, 0x000008f8,          0, 0x0000100,          0,   0,  0, 32},
	/* default: */
	{BTTV_WINFAST2000,            0, 0x000000f8,          0, 0x0000100,          0,   0,  0, 32},
#ifdef BTTV_GVBCTV5PCI
	{BTTV_GVBCTV5PCI,             0, 0x00f0b000,          0,         0,          0,   0, 20,  8},
#endif
};
static unsigned char code_length = 0;
static unsigned char code_bytes = 1;
#define MAX_BYTES 8
#define SUCCESS 0
#define LOGHEAD "lirc_gpio (%d): "
/* how many bits GPIO value can be shifted right before processing
 * it is computed from the value of gpio_mask_parameter
 */
static unsigned char gpio_pre_shift = 0;
static inline int reverse(int data, int bits)
{
	int i;
	int c;
	for (c=0,i=0; i<bits; i++) {
		c |= (((data & (1<<i)) ? 1:0)) << (bits-1-i);
	}
	return c;
}
static int build_key(unsigned long gpio_val, unsigned char codes[MAX_BYTES])
{
	unsigned long mask = gpio_mask;
	unsigned char shift = 0;
	dprintk(LOGHEAD "gpio_val is %lx\n",card,(unsigned long) gpio_val);
	gpio_val ^= gpio_xor_mask;
	if (gpio_lock_mask && (gpio_val & gpio_lock_mask)) {
		return -EBUSY;
	}
	switch (bttv_id)
	{
	case BTTV_AVERMEDIA98:
		if (bttv_write_gpio(card, gpio_enable, gpio_enable)) {
			dprintk(LOGHEAD "cannot write to GPIO\n", card);
			return -EIO;
		}
		if (bttv_read_gpio(card, &gpio_val)) {
			dprintk(LOGHEAD "cannot read GPIO\n", card);
			return -EIO;
		}
		if (bttv_write_gpio(card, gpio_enable, 0)) {
			dprintk(LOGHEAD "cannot write to GPIO\n", card);
			return -EIO;
		}
		break;
	default:
		break;
	}
	/* extract bits from "raw" GPIO value using gpio_mask */
	codes[0] = 0;
	gpio_val >>= gpio_pre_shift;
	while (mask) {
		if (mask & 1u) {
			codes[0] |= (gpio_val & 1u) << shift++;
		}
		mask >>= 1;
		gpio_val >>= 1;
	}
	dprintk(LOGHEAD "code is %lx\n",card,(unsigned long) codes[0]);
	switch (bttv_id)
	{
	case BTTV_AVERMEDIA:
		codes[2] = (codes[0]<<2)&0xff;
		codes[3] = (~codes[2])&0xff;
		codes[0] = 0x02;
		codes[1] = 0xFD;
		break;
	case BTTV_AVPHONE98:
		codes[2] = ((codes[0]&(~0x1))<<2)&0xff;
		codes[3] = (~codes[2])&0xff;
		if (codes[0]&0x1) {
			codes[0] = 0xc0;
			codes[1] = 0x3f;
		} else {
			codes[0] = 0x40;
			codes[1] = 0xbf;
		}
		break;
	case BTTV_AVERMEDIA98:
		break;
	case BTTV_FLYVIDEO:
	case BTTV_FLYVIDEO_98:
	case BTTV_TYPHOON_TVIEW:
#ifdef BTTV_FLYVIDEO_98FM
	case BTTV_FLYVIDEO_98FM:
#endif
		codes[4]=codes[0]<<3;
		codes[5]=((~codes[4])&0xff);
		codes[0]=0x00;
		codes[1]=0x1A;
		codes[2]=0x1F;
		codes[3]=0x2F;
		break;
        case BTTV_MAGICTVIEW061:
        case BTTV_MAGICTVIEW063:
	case BTTV_PHOEBE_TVMAS:
		codes[0] = (codes[0]&0x01)
			|((codes[0]&0x02)<<1)
			|((codes[0]&0x04)<<2)
			|((codes[0]&0x08)>>2)
			|((codes[0]&0x10)>>1);
		/* FALLTHROUGH */
	case BTTV_MIRO:
	case BTTV_DYNALINK:
	case BTTV_PXELVWPLTVPAK:
	case BTTV_PXELVWPLTVPRO:
	case BTTV_PV_BT878P_9B:
	case BTTV_PV_BT878P_PLUS:
#ifdef BTTV_KWORLD
	case BTTV_KWORLD:
#endif
		codes[2] = reverse(codes[0],8);
		codes[3] = (~codes[2])&0xff;
		codes[0] = 0x61;
		codes[1] = 0xD6;
		break;
#if 0
		/* derived from e-tech config file */
		/* 26 + 16 bits */
		/* won't apply it until it's confirmed with a fly98 */
 	case BTTV_FLYVIDEO_98:
	case BTTV_FLYVIDEO_98FM:
		codes[4]=codes[0]<<3;
		codes[5]=(~codes[4])&0xff;
		codes[0]=0x00;
		codes[1]=0x1A;
		codes[2]=0x1F;
		codes[3]=0x2F;
		break;
#endif
	case BTTV_WINFAST2000:
		/* shift extra bit */
		codes[0] = (codes[0]&0x1f) | ((codes[0]&0x20) << 1);
	case BTTV_WINVIEW_601:
		codes[2] = reverse(codes[0],8);
		codes[3] = (~codes[2])&0xff;
		codes[0] = 0xC0;
		codes[1] = 0x3F;
		break;
	default:
		break;
	}
	return SUCCESS;
}
/* add_to_buf - copy a code to the buffer */
static int add_to_buf(void* data, struct lirc_buffer* buf)
{
	static unsigned long next_time = 0;
	static unsigned char prev_codes[MAX_BYTES];
	unsigned long code = 0;
	unsigned char cur_codes[MAX_BYTES];
	if (bttv_read_gpio(card, &code)) {
		dprintk(LOGHEAD "cannot read GPIO\n", card);
		return -EIO;
	}
	if (build_key(code, cur_codes)) {
		return -EFAULT;
	}
		
	/* XXX this should be double checked; i think the soft_gap
	 * here is supposed to track repeats, which is why i save off
	 * the prev_codes, but i'm not certain this is correct
	 */
	if (soft_gap) {
		if (!memcmp(prev_codes, cur_codes, code_bytes) &&
			jiffies < next_time) {
			return -EAGAIN;
		}
		next_time = jiffies + soft_gap;
	}
	memcpy( prev_codes, cur_codes, code_bytes );
	lirc_buffer_write_1( buf, cur_codes );
	return SUCCESS;
}
static int set_use_inc(void* data)
{
	try_module_get(THIS_MODULE);
	return 0;
}
static void set_use_dec(void* data)
{
	module_put(THIS_MODULE);
}
static wait_queue_head_t* get_queue(void* data)
{
	return bttv_get_gpio_queue(card);
}
static struct lirc_plugin plugin = {
	.name		= "lirc_gpio  ",
	.add_to_buf	= add_to_buf,
	.get_queue	= get_queue,
	.set_use_inc	= set_use_inc,
	.set_use_dec	= set_use_dec,
};
/*
 *
 */
int gpio_remote_init(void)
{
	int ret;
	unsigned int mask;
	/* "normalize" gpio_mask
	 * this means shift it right until first bit is set
	 */
	while (!(gpio_mask & 1u)) {
		gpio_pre_shift++;
		gpio_mask >>= 1;
	}
	if (code_length) {
		plugin.code_length = code_length;
	} else {
		/* calculate scan code length in bits if needed */
		plugin.code_length = 1;
		mask = gpio_mask >> 1;
		while (mask) {
			if (mask & 1u) {
				plugin.code_length++;
			}
			mask >>= 1;
		}
	}
	code_bytes = (plugin.code_length/8) + (plugin.code_length%8 ? 1 : 0);
	if (MAX_BYTES < code_bytes) {
		printk (LOGHEAD "scan code too long (%d bytes)\n",
			minor, code_bytes);
		return -EBADRQC;
	}
	if (gpio_enable) {
		if(bttv_gpio_enable(card, gpio_enable, gpio_enable)) {
			printk(LOGHEAD "gpio_enable failure\n", minor);
			return -EIO;
		}
	}
	/* translate ms to jiffies */
	soft_gap = (soft_gap*HZ) / 1000;
	plugin.minor = minor;
	plugin.sample_rate = sample_rate;
	ret = lirc_register_plugin(&plugin);
	if (0 > ret) {
		printk (LOGHEAD "device registration failed with %d\n",
			minor, ret);
		return ret;
	}
	minor = ret;
	printk(LOGHEAD "driver registered\n", minor);
	return SUCCESS;
}
static int __init lirc_gpio_init(void)
{
	int type,cardid,card_type;
	if (CONFIG_LIRC_MAX_DEV < minor) {
		printk("lirc_gpio: parameter minor (%d) must be less than %d!\n",
		       minor, CONFIG_LIRC_MAX_DEV-1);
		return -EBADRQC;
	}
	request_module("bttv");
	/* if gpio_mask not zero then use module parameters
	 * instead of autodetecting TV card
	 */
	if (gpio_mask) {
		if (sample_rate!=0 && (2 > sample_rate || 50 < sample_rate)) {
			printk(LOGHEAD "parameter sample_rate "
			       "must be beetween 2 and 50!\n", minor);
			return -EBADRQC;
		}
		if (sample_rate!=0 && soft_gap &&
		    ((2000/sample_rate) > soft_gap || 1000 < soft_gap)) {
			printk(LOGHEAD "parameter soft_gap "
			       "must be beetween %d and 1000!\n",
			       minor, 2000/sample_rate);
			return -EBADRQC;
		}
	} else {
		if(bttv_get_cardinfo(card,&type,&cardid)==-1) {
			printk(LOGHEAD "could not get card type\n", minor);
		}
		printk(LOGHEAD "card type 0x%x, id 0x%x\n",minor,
		       type,cardid);
		if (type == BTTV_UNKNOWN) {
			printk(LOGHEAD "cannot detect TV card nr %d!\n",
			       minor, card);
			return -EBADRQC;
		}
		for (card_type = 1;
		     card_type < sizeof(rcv_infos)/sizeof(struct rcv_info);
		     card_type++) {
			if (rcv_infos[card_type].bttv_id == type &&
			    (rcv_infos[card_type].card_id == 0 ||
			     rcv_infos[card_type].card_id == cardid)) {
				bttv_id = rcv_infos[card_type].bttv_id;
				gpio_mask = rcv_infos[card_type].gpio_mask;
				gpio_enable = rcv_infos[card_type].gpio_enable;
				gpio_lock_mask = rcv_infos[card_type].gpio_lock_mask;
				gpio_xor_mask = rcv_infos[card_type].gpio_xor_mask;
				soft_gap = rcv_infos[card_type].soft_gap;
				sample_rate = rcv_infos[card_type].sample_rate;
				code_length = rcv_infos[card_type].code_length;
				break;
			}
		}
		if (type==BTTV_AVPHONE98 && cardid==0x00011461)	{
			bttv_id = BTTV_AVERMEDIA98;
		}
		if (type==BTTV_AVERMEDIA98 && cardid==0x00041461) {
			bttv_id = BTTV_AVPHONE98;
		}
		if (type==BTTV_AVERMEDIA98 && cardid==0x03001461) {
			bttv_id = BTTV_AVPHONE98;
		}
		if (type==BTTV_AVERMEDIA98 && cardid==0x00000000) {
			bttv_id = BTTV_AVPHONE98;
		}
		if (card_type == sizeof(rcv_infos)/sizeof(struct rcv_info)) {
			printk(LOGHEAD "TV card type 0x%x not supported!\n",
			       minor, type);
			return -EBADRQC;
		}
	}
	request_module("lirc_dev");
	return gpio_remote_init();
}
void __exit lirc_gpio_exit(void)
{
	int ret;
	ret = lirc_unregister_plugin(minor);
	if (0 > ret) {
		printk(LOGHEAD "error in lirc_unregister_minor: %d\n"
		       "Trying again...\n",
		       minor, ret);
		current->state = TASK_INTERRUPTIBLE;
		schedule_timeout(HZ);
		ret = lirc_unregister_plugin(minor);
		if (0 > ret) {
			printk(LOGHEAD "error in lirc_unregister_minor: %d!!!\n",
			       minor, ret);
			return;
		}
	}
	dprintk(LOGHEAD "module successfully unloaded\n", minor);
}
MODULE_DESCRIPTION("Driver module for remote control (data from bt848 GPIO port)");
MODULE_AUTHOR("Artur Lipowski");
MODULE_LICENSE("GPL");
#ifdef MODULE
	module_init(lirc_gpio_init);
#else
	late_initcall(lirc_gpio_init);
#endif
module_exit(lirc_gpio_exit);
/*
 * Overrides for Emacs so that we follow Linus's tabbing style.
 * ---------------------------------------------------------------------------
 * Local variables:
 * c-basic-offset: 8
 * End:
 */
(-) linux-2.6.5.orig/drivers/char/lirc/lirc_i2c.c (+440 lines)
Line 0    Link Here 
/*      $Id: lirc_i2c.c,v 1.22 2004/03/02 19:34:21 lirc Exp $      */
/*
 * i2c IR lirc plugin for Hauppauge and Pixelview cards - new 2.8.x i2c stack
 *
 * Copyright (c) 2000 Gerd Knorr <kraxel@goldbach.in-berlin.de>
 * modified for PixelView (BT878P+W/FM) by
 *      Michal Kochanowicz <mkochano@pld.org.pl>
 *      Christoph Bartelmus <lirc@bartelmus.de>
 * modified for KNC ONE TV Station/Anubis Typhoon TView Tuner by
 *      Ulrich Mueller <ulrich.mueller42@web.de>
 * modified for Asus TV-Box and Creative/VisionTek BreakOut-Box by
 *      Stefan Jahn <stefan@lkcc.org>
 * modified for Linux 2.6 by
 * 	Jeffrey Clark <jeff@clarkmania.com>
 *
 * parts are cut&pasted from the old lirc_haup.c 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/module.h>
#include <linux/kmod.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/string.h>
#include <linux/timer.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/moduleparam.h>
#include <linux/i2c.h>
#include <linux/i2c-algo-bit.h>
#include <asm/semaphore.h>
#include "../../media/video/bttv.h"
#include "lirc_dev.h"
static unsigned short normal_i2c[] = { 0x1a, 0x18, 0x4b, 0x64, 0x30, 0x21, 0x23, I2C_CLIENT_END };
static unsigned short normal_i2c_range[] = { I2C_CLIENT_END };
static unsigned short probe[2] = { I2C_CLIENT_END, I2C_CLIENT_END };
static unsigned short probe_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END };
static unsigned short ignore[2] = { I2C_CLIENT_END, I2C_CLIENT_END };
static unsigned short ignore_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END };
static unsigned short force[2] = { I2C_CLIENT_END, I2C_CLIENT_END };
static struct i2c_client_address_data addr_data = {
	.normal_i2c             = normal_i2c,
	.normal_i2c_range       = normal_i2c_range,
	.probe                  = probe,
	.probe_range            = probe_range,
	.ignore                 = ignore,
	.ignore_range           = ignore_range,
	.force                  = force
};
struct i2c_ir {
	struct lirc_plugin lirc;
	struct i2c_client client;
	int nextkey;
	unsigned char b[3];
	unsigned char bits;
	unsigned char flag;
};
/* ----------------------------------------------------------------------- */
/* insmod parameters                                                       */
static int debug   = 0;    /* debug output */
static int minor   = -1;   /* minor number */
MODULE_PARM(debug,"i");
MODULE_PARM(minor,"i");
MODULE_DESCRIPTION("Infrared receiver driver for Hauppauge and Pixelview cards (i2c stack)");
MODULE_AUTHOR("Gerd Knorr, Michal Kochanowicz, Christoph Bartelmus, Ulrich Mueller, Stefan Jahn, Jeffrey Clark");
MODULE_LICENSE("GPL");
#define dprintk if (debug) printk
/* ----------------------------------------------------------------------- */
#define DRIVER_NAME "lirc_i2c"
#define DEVICE_NAME DRIVER_NAME
/* ----------------------------------------------------------------------- */
static inline int reverse(int data, int bits)
{
	int i;
	int c;
	
	for (c=0,i=0; i<bits; i++) {
		c |= (((data & (1<<i)) ? 1:0)) << (bits-1-i);
	}
	return c;
}
static int add_to_buf_pcf8574(void* data, struct lirc_buffer* buf)
{
	struct i2c_ir *ir = data;
	int rc;
	unsigned char all, mask;
	unsigned char key;
	/* compute all valid bits (key code + pressed/release flag) */
	all = ir->bits | ir->flag;
	/* save IR writable mask bits */
	mask = i2c_smbus_read_byte(&ir->client) & ~all;
	/* send bit mask */
	rc = i2c_smbus_write_byte(&ir->client, (0xff & all) | mask);
	/* receive scan code */
	rc = i2c_smbus_read_byte(&ir->client);
	if (rc == -1) {
		/* should this return -EIO? */	
		dprintk(DEVICE_NAME ": %s read error\n", ir->client.name);
		return -ENODATA;
	}
	/* drop duplicate polls */
	if (ir->b[0] == (rc & all)) {
		return -ENODATA;
	}
	ir->b[0] = rc & all;
	dprintk(DRIVER_NAME ": %s key 0x%02X %s\n",
		ir->client.name, rc & ir->bits,
		(rc & ir->flag) ? "released" : "pressed");
	if (rc & ir->flag) {
		/* ignore released buttons */
		return -ENODATA;
	}
	/* set valid key code */
	key  = rc & ir->bits;
	lirc_buffer_write_1( buf, &key );
	return 0;
}
static int add_to_buf_haup(void* data, struct lirc_buffer* buf)
{
	struct i2c_ir *ir = data;
        unsigned char keybuf[3];
	__u16 code;
	unsigned char codes[2];
	/* poll IR chip */
	if (3 == i2c_master_recv(&ir->client,keybuf,3)) {
		ir->b[0] = keybuf[0];
		ir->b[1] = keybuf[1];
		ir->b[2] = keybuf[2];
	} else {
		dprintk(DRIVER_NAME ": read error\n");
		/* keep last successfull read buffer */
	}
	/* key pressed ? */
	if ((ir->b[0] & 0x80) == 0)
		return -ENODATA;
	
	dprintk(DRIVER_NAME ": key (0x%02x/0x%02x)\n",
	ir->b[0], ir->b[1]);
	/* look what we have */
	code = (((__u16)ir->b[0]&0x7f)<<6) | (ir->b[1]>>2);
	codes[0] = (code >> 8) & 0xff;
	codes[1] = code & 0xff;
	/* return it */
	lirc_buffer_write_1( buf, codes );
	return 0;
}
static int add_to_buf_pixelview(void* data, struct lirc_buffer* buf)
{
	struct i2c_ir *ir = data;
	unsigned char key;
	
	/* poll IR chip */
	if (1 != i2c_master_recv(&ir->client,&key,1)) {
		dprintk(DRIVER_NAME ": read error\n");
		return -1;
	}
	dprintk(KERN_DEBUG DEVICE_NAME ": key %02x\n", key);
	/* return it */
	lirc_buffer_write_1( buf, &key );
/* not sure where b came from
 	*key = b;
*/
	return 0;
}
static int add_to_buf_pv951(void* data, struct lirc_buffer* buf)
{
	struct i2c_ir *ir = data;
	unsigned char key;
	unsigned char codes[4];
	
	/* poll IR chip */
	if (1 != i2c_master_recv(&ir->client,&key,1)) {
		dprintk(DRIVER_NAME ": read error\n");
		return -ENODATA;
	}
	/* ignore 0xaa */
	if (key==0xaa)
		return -ENODATA;
	dprintk(KERN_DEBUG DEVICE_NAME ": key %02x\n", key);
	codes[3] = 0x61;
	codes[2] = 0xD6;
	codes[1] = reverse(key,8);
	codes[0] = (~codes[2])&0xff;
	
	lirc_buffer_write_1( buf, codes );
	return 0;
}
static int add_to_buf_knc1(void *data, struct lirc_buffer* buf)
{
	static unsigned char last_key = 0xFF;
	struct i2c_ir *ir = data;
	unsigned char key;
	
	/* poll IR chip */
	if (1 != i2c_master_recv(&ir->client,&key,1)) {
		dprintk(DRIVER_NAME ": read error\n");
		return -ENODATA;
	}
	
	/* it seems that 0xFE indicates that a button is still hold
	   down, while 0xFF indicates that no button is hold
	   down. 0xFE sequences are sometimes interrupted by 0xFF */
	
	dprintk(KERN_DEBUG DEVICE_NAME ": key %02x\n", key);
	
	if( key == 0xFF )
		return -ENODATA;
	if ( key == 0xFE )
		key = last_key;
	last_key = key;
	lirc_buffer_write_1( buf, &key );
	return 0;
}
static int set_use_inc(void* data)
{
	try_module_get(THIS_MODULE);
	return 0;
}
static void set_use_dec(void* data)
{
	module_put(THIS_MODULE);
}
static struct lirc_plugin lirc_template = {
	.name =        "lirc_i2c",
	.set_use_inc = set_use_inc,
	.set_use_dec = set_use_dec
};
/* ----------------------------------------------------------------------- */
static int lirc_i2c_attach(struct i2c_adapter *adap, int addr, int kind);
static int lirc_i2c_detach(struct i2c_client *client);
static int lirc_i2c_probe(struct i2c_adapter *adap);
static struct i2c_driver driver = {
	.owner          = THIS_MODULE,
        .name           = DRIVER_NAME,
        .id             = I2C_DRIVERID_EXP3, /* FIXME */
        .flags          = I2C_DF_NOTIFY,
        .attach_adapter = lirc_i2c_probe,
        .detach_client  = lirc_i2c_detach,
};
static struct i2c_client client_template = 
{
        I2C_DEVNAME("(unset)"),
	.flags  = I2C_CLIENT_ALLOW_USE,
        .driver = &driver
};
static int lirc_i2c_attach(struct i2c_adapter *adap, int addr, int kind)
{
        struct i2c_ir *ir;
	int ret;
	
        client_template.adapter = adap;
        client_template.addr = addr;
	
	if (NULL == (ir = kmalloc(sizeof(struct i2c_ir),GFP_KERNEL)))
                return -ENOMEM;
	memset(ir,0,sizeof(struct i2c_ir));
	memcpy(&ir->client,&client_template,sizeof(struct i2c_client));
        memcpy(&ir->lirc,&lirc_template,sizeof(struct lirc_plugin));
	
	ir->lirc.data		= ir;
	ir->lirc.minor		= minor;
	ir->nextkey		= -1;
	i2c_set_clientdata(&ir->client,ir);
	switch(addr)
	{
	case 0x64:
		strncpy(ir->client.name, "Pixelview IR", I2C_NAME_SIZE);
		ir->lirc.code_length = 8;
		ir->lirc.sample_rate = 10;
		ir->lirc.add_to_buf = add_to_buf_pixelview;
		break;
	case 0x4b:
		strncpy(ir->client.name,"PV951 IR", I2C_NAME_SIZE);
		ir->lirc.code_length = 32;
		ir->lirc.sample_rate = 10;
		ir->lirc.add_to_buf = add_to_buf_pv951;
		break;
	case 0x18:
	case 0x1a:
		strncpy(ir->client.name,"Hauppauge IR", I2C_NAME_SIZE);
		ir->lirc.code_length = 13;
		ir->lirc.sample_rate = 6;
		ir->lirc.add_to_buf = add_to_buf_haup;
		break;
	case 0x30:
		strncpy(ir->client.name,"KNC ONE IR", I2C_NAME_SIZE);
		ir->lirc.code_length = 8;
		ir->lirc.sample_rate = 10;
		ir->lirc.add_to_buf = add_to_buf_knc1;
		break;
	case 0x21:
	case 0x23:
		strncpy(ir->client.name,"TV-Box IR", I2C_NAME_SIZE);
		ir->lirc.code_length = 8;
		ir->lirc.sample_rate = 10;
		ir->lirc.add_to_buf = add_to_buf_pcf8574;
		ir->bits = ir->client.flags & 0xff;
		ir->flag = (ir->client.flags >> 8) & 0xff;
		break;
		
	default:
		/* shouldn't happen */
		dprintk(DRIVER_NAME ": unknown i2c address (0x%02x)?\n",addr);
		kfree(ir);
		return -1;
	}
	dprintk(DRIVER_NAME ": chip found @ 0x%02x (%s)\n",addr,
			ir->client.name);
	
	/* register device */
	i2c_attach_client(&ir->client);
	
	if((ret = lirc_register_plugin(&ir->lirc))) {
		dprintk(DRIVER_NAME ": device registration failed with %d\n",
				ret);
		kfree(ir);
		return -1;
	}
	ir->lirc.minor = ret;
	dprintk(DRIVER_NAME ": driver registered\n");
	return 0;
}
static int lirc_i2c_detach(struct i2c_client *client)
{
        struct i2c_ir *ir = i2c_get_clientdata(client);
	int err;
	
	/* unregister device */
	if ((err = lirc_unregister_plugin(ir->lirc.minor))) {
		dprintk(DRIVER_NAME ": lirc unregister failed\n");
		return err;
	} else {
		dprintk(DRIVER_NAME ": lirc unregister successful\n");
	}
	if ((err = i2c_detach_client(&ir->client))) {
		dprintk(DRIVER_NAME ": i2c detach failed\n");
		return err;
	} else {
		dprintk(DRIVER_NAME ": i2c detach successful\n");
	}
	/* free memory */
	kfree(ir);
	return 0;
}
static int lirc_i2c_probe(struct i2c_adapter *adap) {
	dprintk(DRIVER_NAME ": starting probe for adapter %s (0x%x)\n",
			adap->name, adap->id);
	return i2c_probe(adap, &addr_data, lirc_i2c_attach);
}
static int __init lirc_i2c_init(void)
{
	dprintk(DRIVER_NAME ": init\n");
	request_module("bttv");
	request_module("lirc_dev");
	return i2c_add_driver(&driver);
}
static void __exit lirc_i2c_exit(void)
{
	dprintk(DRIVER_NAME ": exit\n");
	i2c_del_driver(&driver);
}
module_init(lirc_i2c_init);
module_exit(lirc_i2c_exit);
(-) linux-2.6.5.orig/drivers/char/lirc/lirc_it87.c (+957 lines)
Line 0    Link Here 
/*
 * LIRC driver for ITE IT8712/IT8705 CIR port
 *
 * Copyright (C) 2001 Hans-Günter Lütke Uphues <hg_lu@web.de>
 *
 * 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
 *
 * ITE IT8705 and IT8712(not tested) CIR-port support for lirc based
 * via cut and paste from lirc_sir.c (C) 2000 Milan Pikula
 *
 * Attention: Sendmode only tested with debugging logs
 *
 * 2001/02/27 Christoph Bartelmus <lirc@bartelmus.de> :
 *   reimplemented read function
 */
#include <linux/version.h>
#include <linux/module.h>
#include <linux/config.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/signal.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/kernel.h>
#include <linux/major.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 <asm/system.h>
#include <asm/segment.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/fcntl.h>
#include <linux/timer.h>
#include <linux/lirc.h>
#include "lirc_dev.h"
#include "lirc_it87.h"
static unsigned long it87_bits_in_byte_out = 0;
static unsigned long it87_send_counter = 0;
static unsigned char it87_RXEN_mask = IT87_CIR_RCR_RXEN;
#define RBUF_LEN 1024
#define WBUF_LEN 1024
#define LIRC_DRIVER_NAME "lirc_it87"
/* timeout for sequences in jiffies (=5/100s) */
/* must be longer than TIME_CONST */
#define IT87_TIMEOUT	(HZ*5/100)
static int io = IT87_CIR_DEFAULT_IOBASE;
static int irq = IT87_CIR_DEFAULT_IRQ;
static unsigned char it87_freq = 38; /* kHz */
/* receiver demodulator default: off */
static unsigned char it87_enable_demodulator = 0;
static spinlock_t timer_lock = SPIN_LOCK_UNLOCKED;
static struct timer_list timerlist;
/* time of last signal change detected */
static struct timeval last_tv = {0, 0};
/* time of last UART data ready interrupt */
static struct timeval last_intr_tv = {0, 0};
static int last_value = 0;
static DECLARE_WAIT_QUEUE_HEAD(lirc_read_queue);
static spinlock_t hardware_lock = SPIN_LOCK_UNLOCKED;
static spinlock_t dev_lock = SPIN_LOCK_UNLOCKED;
static lirc_t rx_buf[RBUF_LEN]; unsigned int rx_tail = 0, rx_head = 0;
static lirc_t tx_buf[WBUF_LEN];
/* SECTION: Prototypes */
/* Communication with user-space */
static int lirc_open(struct inode * inode,
		     struct file * file);
static int lirc_close(struct inode * inode,
		      struct file *file);
static unsigned int lirc_poll(struct file * file,
			      poll_table * wait);
static ssize_t lirc_read(struct file * file,
			 char * buf,
			 size_t count,
			 loff_t * ppos);
static ssize_t lirc_write(struct file * file,
			  const char * buf,
			  size_t n,
			  loff_t * pos);
static int lirc_ioctl(struct inode *node,
		      struct file *filep,
		      unsigned int cmd,
		      unsigned long arg);
static void add_read_queue(int flag,
			   unsigned long val);
static int init_chrdev(void);
static void drop_chrdev(void);
	/* Hardware */
static irqreturn_t it87_interrupt(int irq,
			   void * dev_id,
			   struct pt_regs * regs);
static void send_space(unsigned long len);
static void send_pulse(unsigned long len);
static void init_send(void);
static void terminate_send(unsigned long len);
static int init_hardware(void);
static void drop_hardware(void);
	/* Initialisation */
static int init_port(void);
static void drop_port(void);
int init_module(void);
void cleanup_module(void);
/* SECTION: Communication with user-space */
static int lirc_open(struct inode * inode,
		     struct file * file)
{
	spin_lock(&dev_lock);
#ifdef CONFIG_MODULE_UNLOAD
	if (module_refcount(THIS_MODULE))
	{
		spin_unlock(&dev_lock);
		return -EBUSY;
	}
#endif
	try_module_get(THIS_MODULE);
	spin_unlock(&dev_lock);
	return 0;
}
static int lirc_close(struct inode * inode,
		      struct file *file)
{
	module_put(THIS_MODULE);
	return 0;
}
static unsigned int lirc_poll(struct file * file,
			      poll_table * wait)
{
	poll_wait(file, &lirc_read_queue, wait);
	if (rx_head != rx_tail)
		return POLLIN | POLLRDNORM;
	return 0;
}
static ssize_t lirc_read(struct file * file,
			 char * buf,
			 size_t count,
			 loff_t * ppos)
{
	int n=0;
	int retval=0;
	while(n<count)
	{
		if(file->f_flags & O_NONBLOCK &&
		   rx_head==rx_tail)
		{
			retval = -EAGAIN;
			break;
		}
		retval=wait_event_interruptible(lirc_read_queue,
						rx_head!=rx_tail);
		if(retval)
		{
			break;
		}
		retval=verify_area(VERIFY_WRITE,(void *) buf+n,
				   sizeof(lirc_t));
		if (retval)
		{
			return retval;
		}
		copy_to_user((void *) buf+n,(void *) (rx_buf+rx_head),
			     sizeof(lirc_t));
		rx_head=(rx_head+1)&(RBUF_LEN-1);
		n+=sizeof(lirc_t);
	}
	if(n)
	{
		return n;
	}
	return retval;
}
static ssize_t lirc_write(struct file * file,
			  const char * buf,
			  size_t n,
			  loff_t * pos)
{
	int i;
	int retval;
        if(n%sizeof(lirc_t) || (n/sizeof(lirc_t)) > WBUF_LEN)
		return(-EINVAL);
	retval = verify_area(VERIFY_READ, buf, n);
	if (retval)
		return retval;
	copy_from_user(tx_buf, buf, n);
	i = 0;
	n/=sizeof(lirc_t);
	init_send();
	while (1) {
		if (i >= n)
			break;
		if (tx_buf[i])
			send_pulse(tx_buf[i]);
		i++;
		if (i >= n)
			break;
		if (tx_buf[i])
			send_space(tx_buf[i]);
		i++;
	}
	terminate_send(tx_buf[i-1]);
	return n;
}
static int lirc_ioctl(struct inode *node,
		      struct file *filep,
		      unsigned int cmd,
		      unsigned long arg)
{
	int retval = 0;
	unsigned long value = 0;
	unsigned int ivalue;
	if (cmd == LIRC_GET_FEATURES)
		value = LIRC_CAN_SEND_PULSE |
			LIRC_CAN_SET_SEND_CARRIER |
			LIRC_CAN_REC_MODE2;
	else if (cmd == LIRC_GET_SEND_MODE)
		value = LIRC_MODE_PULSE;
	else if (cmd == LIRC_GET_REC_MODE)
		value = LIRC_MODE_MODE2;
	switch (cmd) {
	case LIRC_GET_FEATURES:
	case LIRC_GET_SEND_MODE:
	case LIRC_GET_REC_MODE:
		retval = put_user(value, (unsigned long *) arg);
		break;
	case LIRC_SET_SEND_MODE:
	case LIRC_SET_REC_MODE:
		retval = get_user(value, (unsigned long *) arg);
		break;
	case LIRC_SET_SEND_CARRIER:
		retval=get_user(ivalue,(unsigned int *) arg);
		if(retval) return(retval);
		ivalue /= 1000;
		if (ivalue > IT87_CIR_FREQ_MAX ||
		    ivalue < IT87_CIR_FREQ_MIN) return(-EINVAL);
		it87_freq = ivalue;
		{
			unsigned long hw_flags;
			spin_lock_irqsave(&hardware_lock, hw_flags);
			outb(((inb(io + IT87_CIR_TCR2) & IT87_CIR_TCR2_TXMPW) |
			      (it87_freq - IT87_CIR_FREQ_MIN) << 3),
			     io + IT87_CIR_TCR2);
			spin_unlock_irqrestore(&hardware_lock, hw_flags);
#ifdef DEBUG
			printk(KERN_DEBUG LIRC_DRIVER_NAME
			       " demodulation frequency: %d kHz\n", it87_freq);
#endif
		}
		break;
	default:
		retval = -ENOIOCTLCMD;
	}
	if (retval)
		return retval;
	if (cmd == LIRC_SET_REC_MODE) {
		if (value != LIRC_MODE_MODE2)
			retval = -ENOSYS;
	} else if (cmd == LIRC_SET_SEND_MODE) {
		if (value != LIRC_MODE_PULSE)
			retval = -ENOSYS;
	}
	return retval;
}
static void add_read_queue(int flag,
			   unsigned long val)
{
	unsigned int new_rx_tail;
	lirc_t newval;
#ifdef DEBUG_SIGNAL
	printk(KERN_DEBUG LIRC_DRIVER_NAME
	       ": add flag %d with val %lu\n",
	       flag,val);
#endif
	newval = val & PULSE_MASK;
	/* statistically pulses are ~TIME_CONST/2 too long: we could
	   maybe make this more exactly but this is good enough */
	if(flag) /* pulse */ {
		if(newval>TIME_CONST/2) {
			newval-=TIME_CONST/2;
		}
		else /* should not ever happen */ {
			newval=1;
		}
		newval|=PULSE_BIT;
	}
	else {
		newval+=TIME_CONST/2;
	}
	new_rx_tail = (rx_tail + 1) & (RBUF_LEN - 1);
	if (new_rx_tail == rx_head) {
#ifdef DEBUG
		printk(KERN_WARNING LIRC_DRIVER_NAME ": Buffer overrun.\n");
#endif
		return;
	}
	rx_buf[rx_tail] = newval;
	rx_tail = new_rx_tail;
	wake_up_interruptible(&lirc_read_queue);
}
static struct file_operations lirc_fops = {
	read:    lirc_read,
	write:   lirc_write,
	poll:    lirc_poll,
	ioctl:   lirc_ioctl,
	open:    lirc_open,
	release: lirc_close,
};
static int set_use_inc(void* data)
{
#if WE_DONT_USE_LOCAL_OPEN_CLOSE
       try_module_get(THIS_MODULE);
#endif
       return 0;
}
static void set_use_dec(void* data)
{
#if WE_DONT_USE_LOCAL_OPEN_CLOSE
       module_put(THIS_MODULE);
#endif
}
static struct lirc_plugin plugin = {
       name:           LIRC_DRIVER_NAME,
       minor:          -1,
       code_length:    1,
       sample_rate:    0,
       data:           NULL,
       add_to_buf:     NULL,
       get_queue:      NULL,
       set_use_inc:    set_use_inc,
       set_use_dec:    set_use_dec,
       fops:           &lirc_fops,
};
int init_chrdev(void)
{
	plugin.minor = lirc_register_plugin(&plugin);
	if (plugin.minor < 0) {
		printk(KERN_ERR LIRC_DRIVER_NAME ": init_chrdev() failed.\n");
		return -EIO;
	}
	return 0;
}
static void drop_chrdev(void)
{
	lirc_unregister_plugin(plugin.minor);
}
/* SECTION: Hardware */
static long delta(struct timeval * tv1,
		  struct timeval * tv2)
{
	unsigned long deltv;
	deltv = tv2->tv_sec - tv1->tv_sec;
	if (deltv > 15)
		deltv = 0xFFFFFF;
	else
		deltv = deltv*1000000 +
			tv2->tv_usec -
			tv1->tv_usec;
	return deltv;
}
static void it87_timeout(unsigned long data)
{
	/* if last received signal was a pulse, but receiving stopped
	   within the 9 bit frame, we need to finish this pulse and
	   simulate a signal change to from pulse to space. Otherwise
	   upper layers will receive two sequences next time. */
	unsigned long flags;
	unsigned long pulse_end;
	/* avoid interference with interrupt */
 	spin_lock_irqsave(&timer_lock, flags);
	if (last_value) {
		/* determine 'virtual' pulse end: */
	 	pulse_end = delta(&last_tv, &last_intr_tv);
#ifdef DEBUG_SIGNAL
		printk(KERN_DEBUG LIRC_DRIVER_NAME
		       ": timeout add %d for %lu usec\n",
		       last_value,
		       pulse_end);
#endif
		add_read_queue(last_value,
			       pulse_end);
		last_value = 0;
		last_tv=last_intr_tv;
	}
	spin_unlock_irqrestore(&timer_lock, flags);
}
static irqreturn_t it87_interrupt(int irq,
			   void * dev_id,
			   struct pt_regs * regs)
{
	unsigned char data;
	struct timeval curr_tv;
	static unsigned long deltv;
	unsigned long deltintrtv;
	unsigned long flags, hw_flags;
	int iir, lsr;
	int fifo = 0;
	iir = inb(io + IT87_CIR_IIR);
	switch (iir & IT87_CIR_IIR_IID) {
	case 0x4:
	case 0x6:
		lsr = inb(io + IT87_CIR_RSR) & (IT87_CIR_RSR_RXFTO |
						    IT87_CIR_RSR_RXFBC);
		fifo = lsr & IT87_CIR_RSR_RXFBC;
#ifdef DEBUG_SIGNAL
		printk(KERN_DEBUG LIRC_DRIVER_NAME
		       "iir: 0x%x fifo: 0x%x\n", iir, lsr);
#endif
		/* avoid interference with timer */
		spin_lock_irqsave(&timer_lock, flags);
		spin_lock_irqsave(&hardware_lock, hw_flags);
		do {
			del_timer(&timerlist);
			data = inb(io + IT87_CIR_DR);
#ifdef DEBUG_SIGNAL
			printk(KERN_DEBUG LIRC_DRIVER_NAME
			       ": data=%.2x\n",
			       data);
#endif
			do_gettimeofday(&curr_tv);
			deltv = delta(&last_tv, &curr_tv);
			deltintrtv = delta(&last_intr_tv, &curr_tv);
#ifdef DEBUG_SIGNAL
			printk(KERN_DEBUG LIRC_DRIVER_NAME
			       ": t %lu , d %d\n",
			       deltintrtv,
			       (int)data);
#endif
			/* if nothing came in last 2 cycles,
			   it was gap */
			if (deltintrtv > TIME_CONST * 2) {
				if (last_value) {
#ifdef DEBUG_SIGNAL
					printk(KERN_DEBUG LIRC_DRIVER_NAME ": GAP\n");
#endif
					/* simulate signal change */
					add_read_queue(last_value,
						       deltv-
						       deltintrtv);
					last_value = 0;
					last_tv.tv_sec = last_intr_tv.tv_sec;
					last_tv.tv_usec = last_intr_tv.tv_usec;
					deltv = deltintrtv;
				}
			}
			data = 1;
			if (data ^ last_value) {
				/* deltintrtv > 2*TIME_CONST,
				   remember ? */
				/* the other case is timeout */
				add_read_queue(last_value,
					       deltv-TIME_CONST);
				last_value = data;
				last_tv = curr_tv;
				if(last_tv.tv_usec>=TIME_CONST) {
					last_tv.tv_usec-=TIME_CONST;
				}
				else {
					last_tv.tv_sec--;
					last_tv.tv_usec+=1000000-
						TIME_CONST;
				}
			}
			last_intr_tv = curr_tv;
			if (data) {
				/* start timer for end of sequence detection */
				timerlist.expires = jiffies + IT87_TIMEOUT;
				add_timer(&timerlist);
			}
			outb((inb(io + IT87_CIR_RCR) & ~IT87_CIR_RCR_RXEN) |
			     IT87_CIR_RCR_RXACT,
			     io + IT87_CIR_RCR);
			if (it87_RXEN_mask) {
				outb(inb(io + IT87_CIR_RCR) | IT87_CIR_RCR_RXEN,
				     io + IT87_CIR_RCR);
			}
			fifo--;
		}
		while (fifo != 0);
		spin_unlock_irqrestore(&hardware_lock, hw_flags);
		spin_unlock_irqrestore(&timer_lock, flags);
		break;
	default:
		/* not our irq */
#ifdef DEBUG_SIGNAL
		printk(KERN_DEBUG LIRC_DRIVER_NAME
		       "unknown IRQ (shouldn't happen) !!\n");
#endif
		break;
	}
	return IRQ_HANDLED; //FIXME true status should be returned (include/linux/interrupt.h)
}
static void send_it87(unsigned long len,
		      unsigned long stime,
		      unsigned char send_byte,
		      unsigned int count_bits)
{
        long count = len / stime;
	long time_left = 0;
	static unsigned char byte_out = 0;
#ifdef DEBUG_SIGNAL
	printk(KERN_DEBUG LIRC_DRIVER_NAME
	       "send_it87: len=%ld, sb=%d\n",
	       len,
	       send_byte);
#endif
	time_left = (long)len - (long)count * (long)stime;
	count += ((2 * time_left) / stime);
	while (count) {
		long i=0;
		for (i=0; i<count_bits; i++) {
			byte_out = (byte_out << 1) | (send_byte & 1);
			it87_bits_in_byte_out++;
		}
		if (it87_bits_in_byte_out == 8) {
#ifdef DEBUG_SIGNAL
			printk(KERN_DEBUG LIRC_DRIVER_NAME
			       "out=0x%x, tsr_txfbc: 0x%x\n",
			       byte_out,
			       inb(io + IT87_CIR_TSR) &
			       IT87_CIR_TSR_TXFBC);
#endif
			while ((inb(io + IT87_CIR_TSR) &
				IT87_CIR_TSR_TXFBC) >= IT87_CIR_FIFO_SIZE);
			{
				unsigned long hw_flags;
				spin_lock_irqsave(&hardware_lock, hw_flags);
				outb(byte_out, io + IT87_CIR_DR);
				spin_unlock_irqrestore(&hardware_lock, hw_flags);
			}
			it87_bits_in_byte_out = 0;
			it87_send_counter++;
			byte_out = 0;
		}
		count--;
	}
}
/*
maybe: exchange space and pulse because
it8705 only modulates 0-bits
*/
static void send_space(unsigned long len)
{
	send_it87(len,
		  TIME_CONST,
		  IT87_CIR_SPACE,
		  IT87_CIR_BAUDRATE_DIVISOR);
}
static void send_pulse(unsigned long len)
{
	send_it87(len,
		  TIME_CONST,
		  IT87_CIR_PULSE,
		  IT87_CIR_BAUDRATE_DIVISOR);
}
static void init_send()
{
	unsigned long flags;
	spin_lock_irqsave(&hardware_lock, flags);
	/* RXEN=0: receiver disable */
	it87_RXEN_mask = 0;
	outb(inb(io + IT87_CIR_RCR) & ~IT87_CIR_RCR_RXEN,
	     io + IT87_CIR_RCR);
	spin_unlock_irqrestore(&hardware_lock, flags);
	it87_bits_in_byte_out = 0;
	it87_send_counter = 0;
}
static void terminate_send(unsigned long len)
{
	unsigned long flags;
	unsigned long last = 0;
	last = it87_send_counter;
	/* make sure all necessary data has been sent */
	while (last == it87_send_counter)
		send_space(len);
	/* wait until all data sent */
	while ((inb(io + IT87_CIR_TSR) & IT87_CIR_TSR_TXFBC) != 0);
	/* then reenable receiver */
	spin_lock_irqsave(&hardware_lock, flags);
	it87_RXEN_mask = IT87_CIR_RCR_RXEN;
	outb(inb(io + IT87_CIR_RCR) | IT87_CIR_RCR_RXEN,
	     io + IT87_CIR_RCR);
	spin_unlock_irqrestore(&hardware_lock, flags);
}
static int init_hardware(void)
{
	unsigned long flags;
	unsigned char it87_rcr = 0;
	spin_lock_irqsave(&hardware_lock, flags);
	/* init cir-port */
	/* enable r/w-access to Baudrate-Register */
	outb(IT87_CIR_IER_BR, io + IT87_CIR_IER);
	outb(IT87_CIR_BAUDRATE_DIVISOR % 0x100, io+IT87_CIR_BDLR);
	outb(IT87_CIR_BAUDRATE_DIVISOR / 0x100, io+IT87_CIR_BDHR);
	/* Baudrate Register off, define IRQs: Input only */
	outb(IT87_CIR_IER_IEC | IT87_CIR_IER_RDAIE, io + IT87_CIR_IER);
	/* RX: HCFS=0, RXDCR = 001b (35,6..40,3 kHz), RXEN=1 */
	it87_rcr = (IT87_CIR_RCR_RXEN & it87_RXEN_mask) | 0x1;
	if (it87_enable_demodulator)
		it87_rcr |= IT87_CIR_RCR_RXEND;
	outb(it87_rcr, io + IT87_CIR_RCR);
	/* TX: 38kHz, 13,3us (pulse-width */
	outb(((it87_freq - IT87_CIR_FREQ_MIN) << 3) | 0x06,
	     io + IT87_CIR_TCR2);
	spin_unlock_irqrestore(&hardware_lock, flags);
	return 0;
}
static void drop_hardware(void)
{
	unsigned long flags;
	spin_lock_irqsave(&hardware_lock, flags);
	disable_irq(irq);
	/* receiver disable */
	it87_RXEN_mask = 0;
	outb(0x1, io + IT87_CIR_RCR);
	/* turn off irqs */
	outb(0, io + IT87_CIR_IER);
	/* fifo clear */
        outb(IT87_CIR_TCR1_FIFOCLR, io+IT87_CIR_TCR1);
        /* reset */
        outb(IT87_CIR_IER_RESET, io+IT87_CIR_IER);
	enable_irq(irq);
	spin_unlock_irqrestore(&hardware_lock, flags);
}
static unsigned char it87_read(unsigned char port)
{
	outb(port, IT87_ADRPORT);
	return inb(IT87_DATAPORT);
}
static void it87_write(unsigned char port,
		       unsigned char data)
{
	outb(port, IT87_ADRPORT);
	outb(data, IT87_DATAPORT);
}
/* SECTION: Initialisation */
static int init_port(void)
{
	int retval = 0;
	unsigned char init_bytes[4] = {IT87_INIT};
	unsigned char it87_chipid = 0;
	unsigned char ldn = 0;
	unsigned int  it87_io = 0;
	unsigned int  it87_irq = 0;
	/* Enter MB PnP Mode */
	outb(init_bytes[0], IT87_ADRPORT);
	outb(init_bytes[1], IT87_ADRPORT);
	outb(init_bytes[2], IT87_ADRPORT);
	outb(init_bytes[3], IT87_ADRPORT);
	/* 8712 or 8705 ? */
	it87_chipid = it87_read(IT87_CHIP_ID1);
	if (it87_chipid != 0x87) {
		retval = -ENXIO;
		return retval;
	}
	it87_chipid = it87_read(IT87_CHIP_ID2);
	if ((it87_chipid != 0x12) && (it87_chipid != 0x05)) {
		printk(KERN_INFO LIRC_DRIVER_NAME
		       ": no IT8705/12 found, exiting..\n");
		retval = -ENXIO;
		return retval;
	}
	printk(KERN_INFO LIRC_DRIVER_NAME
	       ": found IT87%.2x.\n",
	       it87_chipid);
	/* get I/O-Port and IRQ */
	if (it87_chipid == 0x12)
		ldn = IT8712_CIR_LDN;
	else
		ldn = IT8705_CIR_LDN;
	it87_write(IT87_LDN, ldn);
	it87_io = it87_read(IT87_CIR_BASE_MSB) * 256 +
		it87_read(IT87_CIR_BASE_LSB);
	if (it87_io == 0) {
		if (io == 0)
			io = IT87_CIR_DEFAULT_IOBASE;
		printk(KERN_INFO LIRC_DRIVER_NAME
		       ": set default io 0x%x\n",
		       io);
		it87_write(IT87_CIR_BASE_MSB, io / 0x100);
		it87_write(IT87_CIR_BASE_LSB, io % 0x100);
	}
	else
		io = it87_io;
	it87_irq = it87_read(IT87_CIR_IRQ);
	if (it87_irq == 0) {
		if (irq == 0)
			irq = IT87_CIR_DEFAULT_IRQ;
		printk(KERN_INFO LIRC_DRIVER_NAME
		       ": set default irq 0x%x\n",
		       irq);
		it87_write(IT87_CIR_IRQ, irq);
	}
	else
		irq = it87_irq;
	{
		unsigned long hw_flags;
		spin_lock_irqsave(&hardware_lock, hw_flags);
		/* reset */
		outb(IT87_CIR_IER_RESET, io+IT87_CIR_IER);
		/* fifo clear */
		outb(IT87_CIR_TCR1_FIFOCLR |
		     /*	     IT87_CIR_TCR1_ILE | */
		     IT87_CIR_TCR1_TXRLE |
		     IT87_CIR_TCR1_TXENDF, io+IT87_CIR_TCR1);
		spin_unlock_irqrestore(&hardware_lock, hw_flags);
	}
	/* get I/O port access and IRQ line */
	retval = request_region(io, 8, LIRC_DRIVER_NAME);
	if (!retval) {
		printk(KERN_ERR LIRC_DRIVER_NAME
		       ": Unable to reserve IO region for LIRC IT87. Port 0x%.4x already in use.\n",
		       io);
		/* Leaving MB PnP Mode */
		it87_write(IT87_CFGCTRL, 0x2);
		return retval;
	}
	/* activate CIR-Device */
	it87_write(IT87_CIR_ACT, 0x1);
	/* Leaving MB PnP Mode */
	it87_write(IT87_CFGCTRL, 0x2);
	retval = request_irq(irq, it87_interrupt, 0 /*SA_INTERRUPT*/,
			     LIRC_DRIVER_NAME, NULL);
	if (retval < 0) {
		printk(KERN_ERR LIRC_DRIVER_NAME
		       ": IRQ %d already in use.\n",
		       irq);
		return retval;
	}
	printk(KERN_INFO LIRC_DRIVER_NAME
	       ": I/O port 0x%.4x, IRQ %d.\n",
	       io,
	       irq);
	request_region(io, 8, LIRC_DRIVER_NAME);
	init_timer(&timerlist);
	timerlist.function = it87_timeout;
	timerlist.data = 0xabadcafe;
	return 0;
}
static void drop_port(void)
{
/*
        unsigned char init_bytes[4] = {IT87_INIT};
        / * Enter MB PnP Mode * /
        outb(init_bytes[0], IT87_ADRPORT);
        outb(init_bytes[1], IT87_ADRPORT);
        outb(init_bytes[2], IT87_ADRPORT);
        outb(init_bytes[3], IT87_ADRPORT);
        / * deactivate CIR-Device * /
        it87_write(IT87_CIR_ACT, 0x0);
        / * Leaving MB PnP Mode * /
        it87_write(IT87_CFGCTRL, 0x2);
*/
	del_timer_sync(&timerlist);
	free_irq(irq, NULL);
	release_region(io, 8);
}
int init_lirc_it87(void)
{
	int retval;
	init_waitqueue_head(&lirc_read_queue);
	retval = init_port();
	if (retval < 0)
		return retval;
	init_hardware();
	printk(KERN_INFO LIRC_DRIVER_NAME
	       ": Installed.\n");
	return 0;
}
MODULE_AUTHOR("Hans-Günter Lütke Uphues");
MODULE_DESCRIPTION("LIRC driver for ITE IT8712/IT8705 CIR port");
MODULE_PARM(io, "i");
MODULE_PARM_DESC(io,
		 "I/O base address (default: 0x310)");
MODULE_PARM(irq, "i");
MODULE_PARM_DESC(irq,
		 "Interrupt (1,3-12) (default: 7)");
MODULE_PARM(it87_enable_demodulator, "i");
MODULE_PARM_DESC(it87_enable_demodulator,
		 "Receiver demodulator enable/disable (1/0), default: 0");
MODULE_LICENSE("GPL");
static int __init lirc_it87_init(void)
{
	int retval;
	retval=init_chrdev();
	if(retval < 0)
		return retval;
	retval = init_lirc_it87();
	if (retval) {
		drop_chrdev();
		return retval;
	}
	return 0;
}
static void __exit lirc_it87_exit(void)
{
	drop_hardware();
	drop_chrdev();
	drop_port();
	printk(KERN_INFO LIRC_DRIVER_NAME ": Uninstalled.\n");
}
module_init(lirc_it87_init);
module_exit(lirc_it87_exit);
/*
 * Overrides for Emacs so that we follow Linus's tabbing style.
 * ---------------------------------------------------------------------------
 * Local variables:
 * c-basic-offset: 8
 * End:
 */
(-) linux-2.6.5.orig/drivers/char/lirc/lirc_it87.h (+116 lines)
Line 0    Link Here 
/* lirc_it87.h */
/* SECTION: Definitions */
/********************************* ITE IT87xx ************************/
/* based on the following documentation from ITE:
   a) IT8712F Preliminary CIR Programming Guide V0.1
   b) IT8705F Simple LPC I/O Preliminary Specifiction V0.3
   c) IT8712F EC-LPC I/O Preliminary Specification V0.5
*/
/* IT8712/05 Ports: */
#define IT87_ADRPORT      0x2e
#define IT87_DATAPORT     0x2f
#define IT87_INIT         0x87, 0x01, 0x55, 0x55
/* alternate Ports: */
/*
#define IT87_ADRPORT      0x4e
#define IT87_DATAPORT     0x4f
#define IT87_INIT         0x87, 0x01, 0x55, 0xaa
 */
/* IT8712/05 Registers */
#define IT87_CFGCTRL      0x2
#define IT87_LDN          0x7
#define IT87_CHIP_ID1     0x20
#define IT87_CHIP_ID2     0x21
#define IT87_CFG_VERSION  0x22
#define IT87_SWSUSPEND    0x23
#define IT8712_CIR_LDN    0xa
#define IT8705_CIR_LDN    0x7
/* CIR Configuration Registers: */
#define IT87_CIR_ACT      0x30
#define IT87_CIR_BASE_MSB 0x60
#define IT87_CIR_BASE_LSB 0x61
#define IT87_CIR_IRQ      0x70
#define IT87_CIR_CONFIG   0xf0
/* List of IT87_CIR registers: offset to BaseAddr */
#define IT87_CIR_DR   0
#define IT87_CIR_IER  1
#define IT87_CIR_RCR  2
#define IT87_CIR_TCR1 3
#define IT87_CIR_TCR2 4
#define IT87_CIR_TSR  5
#define IT87_CIR_RSR  6
#define IT87_CIR_BDLR 5
#define IT87_CIR_BDHR 6
#define IT87_CIR_IIR  7
/* Bit Definitionen */
/* IER: */
#define IT87_CIR_IER_TM_EN   0x80
#define IT87_CIR_IER_RESEVED 0x40
#define IT87_CIR_IER_RESET   0x20
#define IT87_CIR_IER_BR      0x10
#define IT87_CIR_IER_IEC     0x8
#define IT87_CIR_IER_RFOIE   0x4
#define IT87_CIR_IER_RDAIE   0x2
#define IT87_CIR_IER_TLDLIE  0x1
/* RCR: */
#define IT87_CIR_RCR_RDWOS  0x80
#define IT87_CIR_RCR_HCFS   0x40
#define IT87_CIR_RCR_RXEN   0x20
#define IT87_CIR_RCR_RXEND  0x10
#define IT87_CIR_RCR_RXACT  0x8
#define IT87_CIR_RCR_RXDCR  0x7
/* TCR1: */
#define IT87_CIR_TCR1_FIFOCLR 0x80
#define IT87_CIR_TCR1_ILE     0x40
#define IT87_CIR_TCR1_FIFOTL  0x30
#define IT87_CIR_TCR1_TXRLE   0x8
#define IT87_CIR_TCR1_TXENDF  0x4
#define IT87_CIR_TCR1_TXMPM   0x3
/* TCR2: */
#define IT87_CIR_TCR2_CFQ   0xf8
#define IT87_CIR_TCR2_TXMPW 0x7
/* TSR: */
#define IT87_CIR_TSR_RESERVED 0xc0
#define IT87_CIR_TSR_TXFBC    0x3f
/* RSR: */
#define IT87_CIR_RSR_RXFTO    0x80
#define IT87_CIR_RSR_RESERVED 0x40
#define IT87_CIR_RSR_RXFBC    0x3f
/* IIR: */
#define IT87_CIR_IIR_RESERVED 0xf8
#define IT87_CIR_IIR_IID      0x6
#define IT87_CIR_IIR_IIP      0x1
/* TM: */
#define IT87_CIR_TM_IL_SEL    0x80
#define IT87_CIR_TM_RESERVED  0x40
#define IT87_CIR_TM_TM_REG    0x3f
#define IT87_CIR_FIFO_SIZE 32
/* Baudratedivisor for IT87: power of 2: only 1,2,4 or 8) */
#define IT87_CIR_BAUDRATE_DIVISOR 0x1
#define IT87_CIR_DEFAULT_IOBASE 0x310
#define IT87_CIR_DEFAULT_IRQ    0x7
#define IT87_CIR_SPACE 0x00
#define IT87_CIR_PULSE 0xff
#define IT87_CIR_FREQ_MIN 27
#define IT87_CIR_FREQ_MAX 58
#define TIME_CONST (IT87_CIR_BAUDRATE_DIVISOR * 8000000ul / 115200ul)
/********************************* ITE IT87xx ************************/
(-) linux-2.6.5.orig/drivers/char/lirc/lirc_mceusb.c (+1530 lines)
Line 0    Link Here 
/*
 * USB Microsoft IR Transceiver driver - 0.1
 *
 * Copyright (c) 2003-2004 Dan Conti (dconti@acm.wwu.edu)
 *
 * This driver is based on the USB skeleton driver packaged with the
 * kernel, and the notice from that package has been retained below.
 *
 * The Microsoft IR Transceiver is a neat little IR receiver with two
 * emitters on it designed for Windows Media Center. This driver might
 * work for all media center remotes, but I have only tested it with
 * the philips model. The first revision of this driver only supports
 * the receive function - the transmit function will be much more
 * tricky due to the nature of the hardware. Microsoft chose to build
 * this device inexpensively, therefore making it extra dumb.  There
 * is no interrupt endpoint on this device; all usb traffic happens
 * over two bulk endpoints. As a result of this, poll() for this
 * device is an actual hardware poll (instead of a receive queue
 * check) and is rather expensive.
 *
 * This driver is structured in three basic layers
 *  - lower  - interface with the usb device and manage usb data
 *  - middle - api to convert usb data into mode2 and provide this in
 *    _read calls
 *  - mceusb_* - linux driver interface
 *
 * The key routines are as follows:
 *  msir_fetch_more_data - this reads incoming data, strips off the
 *                         start codes the ir receiver places on them,
 *                         and dumps it in an * internal buffer
 *  msir_generate_mode2  - this takes the above data, depacketizes it,
 *                         and generates mode2 data to feed out
 *                         through read calls
 *
 *
 * All trademarks property of their respective owners.
 *
 * 2003_11_11 - Restructured to minimalize code interpretation in the
 *              driver. The normal use case will be with lirc.
 *
 * 2004_01_01 - Removed all code interpretation. Generate mode2 data
 *              for passing off to lirc. Cleanup
 *
 * 2004_01_04 - Removed devfs handle. Put in a temporary workaround
 *              for a known issue where repeats generate two
 *              sequential spaces * (last_was_repeat_gap)
 *
 * TODO
 *   - Fix up minor number, registration of major/minor with usb subsystem
 *   - Fix up random EINTR being sent
 *   - Fix problem where third key in a repeat sequence is randomly truncated
 *
 */
/*
 * USB Skeleton driver - 0.6
 *
 * Copyright (c) 2001 Greg Kroah-Hartman (greg@kroah.com)
 *
 *	This program is free software; you can redistribute it and/or
 *	modify it under the terms of the GNU General Public License as
 *	published by the Free Software Foundation; either version 2 of
 *	the License, or (at your option) any later version.
 *
 *
 * This driver is to be used as a skeleton driver to be able to create a
 * USB driver quickly.  The design of it is based on the usb-serial and
 * dc2xx drivers.
 *
 * Thanks to Oliver Neukum and David Brownell for their help in debugging
 * this driver.
 *
 * TODO:
 *	- fix urb->status race condition in write sequence
 *	- move minor_table to a dynamic list.
 *
 * History:
 *
 * 2001_11_05 - 0.6 - fix minor locking problem in skel_disconnect.
 *			Thanks to Pete Zaitcev for the fix.
 * 2001_09_04 - 0.5 - fix devfs bug in skel_disconnect. Thanks to wim delvaux
 * 2001_08_21 - 0.4 - more small bug fixes.
 * 2001_05_29 - 0.3 - more bug fixes based on review from linux-usb-devel
 * 2001_05_24 - 0.2 - bug fixes based on review from linux-usb-devel people
 * 2001_05_01 - 0.1 - first version
 *
 */
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/signal.h>
#include <linux/errno.h>
#include <linux/poll.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/fcntl.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#include <linux/list.h>
#include <linux/smp_lock.h>
#include <linux/usb.h>
#ifdef CONFIG_USB_DEBUG
	static int debug = 1;
#else
	static int debug = 1;
#endif
#include <linux/lirc.h>
#include "drivers/lirc_dev/lirc_dev.h"
/* Version Information */
#define DRIVER_VERSION "v0.1"
#define DRIVER_AUTHOR "Dan Conti, dconti@acm.wwu.edu"
#define DRIVER_DESC "USB Microsoft IR Transceiver Driver"
/* Module paramaters */
MODULE_PARM(debug, "i");
MODULE_PARM_DESC(debug, "Debug enabled or not");
/* Define these values to match your device */
#define USB_MCEUSB_VENDOR_ID	0x045e
#define USB_MCEUSB_PRODUCT_ID	0x006d
/* table of devices that work with this driver */
static struct usb_device_id mceusb_table [] = {
	{ USB_DEVICE(USB_MCEUSB_VENDOR_ID, USB_MCEUSB_PRODUCT_ID) },
	{ }					/* Terminating entry */
};
MODULE_DEVICE_TABLE (usb, mceusb_table);
/* XXX TODO, 244 is likely unused but not reserved */
/* Get a minor range for your devices from the usb maintainer */
#define USB_MCEUSB_MINOR_BASE	244
/* we can have up to this number of device plugged in at once */
#define MAX_DEVICES		16
/* Structure to hold all of our device specific stuff */
struct usb_skel	     {
	/* save off the usb device pointer */
	struct usb_device *	    udev;
	/* the interface for this device */
	struct usb_interface *	interface;
	/* the starting minor number for this device */
	unsigned char   minor;
	/* the number of ports this device has */
	unsigned char   num_ports;
	/* number of interrupt in endpoints we have */
	char            num_interrupt_in;
	/* number of bulk in endpoints we have */
	char            num_bulk_in;
	/* number of bulk out endpoints we have */
	char            num_bulk_out;
	/* the buffer to receive data */
	unsigned char *    bulk_in_buffer;
	/* the size of the receive buffer */
	int                bulk_in_size;
	/* the address of the bulk in endpoint */
	__u8               bulk_in_endpointAddr;
	/* the buffer to send data */
	unsigned char *    bulk_out_buffer;
	/* the size of the send buffer */
	int	           bulk_out_size;
	/* the urb used to send data */
	struct urb *       write_urb;
	/* the address of the bulk out endpoint */
	__u8               bulk_out_endpointAddr;
	wait_queue_head_t  wait_q;      /* for timeouts */
	int                open_count;	/* number of times this port
					 * has been opened */
	struct semaphore   sem;		/* locks this structure */
	struct lirc_plugin* plugin;
	/* Used in converting to mode2 and storing */
        /* buffer for the mode2 data, since lirc reads 4bytes */
	int    mode2_data[256];
	int    mode2_idx;               /* read index */
	int    mode2_count;             /* words available (i.e. write
					 * index) */
	int    mode2_partial_pkt_size;
	int    mode2_once;
	/* Used for storing preprocessed usb data before converting to mode2*/
	char   usb_dbuffer[1024];
	int    usb_dstart;
	int    usb_dcount;
	int    usb_valid_bytes_in_bulk_buffer;
	/* Set to 1 if the last value we adjusted was a repeat gap; we
	 * need to hold this value around until we process a lead
	 * space on the repeat code, otherwise we pass off two
	 * sequential spaces */
	int    last_was_repeat_gap;
};
/* driver api */
static ssize_t mceusb_read	(struct file *file, char *buffer,
				 size_t count, loff_t *ppos);
static ssize_t mceusb_write	(struct file *file, const char *buffer,
				 size_t count, loff_t *ppos);
static unsigned int mceusb_poll (struct file* file, poll_table* wait);
static int mceusb_open		(struct inode *inode, struct file *file);
static int mceusb_release	(struct inode *inode, struct file *file);
static void * mceusb_probe	(struct usb_device *dev, unsigned int ifnum,
				 const struct usb_device_id *id);
static void *mceusb_disconnect	(struct usb_interface *intf);
static void mceusb_write_bulk_callback	(struct urb *urb);
/* lower level api */
static int msir_fetch_more_data( struct usb_skel* dev, int dont_block );
static int msir_read_from_buffer( struct usb_skel* dev, char* buffer, int len );
static int msir_mark_as_read( struct usb_skel* dev, int count );
static int msir_available_data( struct usb_skel* dev );
/* middle */
static int msir_generate_mode2( struct usb_skel* dev, signed char* usb_data,
				int bytecount );
static int msir_copy_mode2( struct usb_skel* dev, int* mode2_data, int count );
static int msir_available_mode2( struct usb_skel* dev );
/* helper functions */
static void msir_cleanup( struct usb_skel* dev );
static int set_use_inc(void* data);
static void set_use_dec(void* data);
/* array of pointers to our devices that are currently connected */
static struct usb_skel		*minor_table[MAX_DEVICES];
/* lock to protect the minor_table structure */
static DECLARE_MUTEX (minor_table_mutex);
/*
 * File operations needed when we register this driver.
 * This assumes that this driver NEEDS file operations,
 * of course, which means that the driver is expected
 * to have a node in the /dev directory. If the USB
 * device were for a network interface then the driver
 * would use "struct net_driver" instead, and a serial
 * device would use "struct tty_driver".
 */
static struct file_operations mceusb_fops = {
	/*
	 * The owner field is part of the module-locking
	 * mechanism. The idea is that the kernel knows
	 * which module to increment the use-counter of
	 * BEFORE it calls the device's open() function.
	 * This also means that the kernel can decrement
	 * the use-counter again before calling release()
	 * or should the open() function fail.
	 *
	 * Not all device structures have an "owner" field
	 * yet. "struct file_operations" and "struct net_device"
	 * do, while "struct tty_driver" does not. If the struct
	 * has an "owner" field, then initialize it to the value
	 * THIS_MODULE and the kernel will handle all module
	 * locking for you automatically. Otherwise, you must
	 * increment the use-counter in the open() function
	 * and decrement it again in the release() function
	 * yourself.
	 */
	owner:		THIS_MODULE,
	read:		mceusb_read,
	write:		mceusb_write,
	poll:           mceusb_poll,
	ioctl:          NULL,
	open:		mceusb_open,
	release:	mceusb_release,
};
/* usb specific object needed to register this driver with the usb subsystem */
static struct usb_driver mceusb_driver = {
	name:		"ir_transceiver",
	probe:		mceusb_probe,
	disconnect:	mceusb_disconnect,
	fops:		&mceusb_fops,
	minor:		USB_MCEUSB_MINOR_BASE,
	id_table:	mceusb_table,
};
/**
 *	usb_mceusb_debug_data
 */
static inline void usb_mceusb_debug_data (const char *function, int size,
					  const unsigned char *data)
{
	int i;
	if (!debug)
		return;
	printk (KERN_DEBUG __FILE__": %s - length = %d, data = ",
		function, size);
	for (i = 0; i < size; ++i) {
		printk ("%.2x ", data[i]);
	}
	printk ("\n");
}
/**
 *	mceusb_delete
 */
static inline void mceusb_delete (struct usb_skel *dev)
{
	minor_table[dev->minor] = NULL;
	if (dev->bulk_in_buffer != NULL)
		kfree (dev->bulk_in_buffer);
	if (dev->bulk_out_buffer != NULL)
		kfree (dev->bulk_out_buffer);
	if (dev->write_urb != NULL)
		usb_free_urb (dev->write_urb);
	kfree (dev);
}
static void mceusb_setup( struct usb_device *udev )
{
	char data[8];
	int res;
	memset( data, 0, 8 );
	/* Get Status */
	res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
			      USB_REQ_GET_STATUS, USB_DIR_IN,
			      0, 0, data, 2, HZ * 3);
	/*    res = usb_get_status( udev, 0, 0, data ); */
	dbg(__FUNCTION__ " res = %d status = 0x%x 0x%x",
	    res, data[0], data[1] );
	/* This is a strange one. They issue a set address to the
	 * device on the receive control pipe and expect a certain
	 * value pair back
	 */
	memset( data, 0, 8 );
	res = usb_control_msg( udev, usb_rcvctrlpipe(udev, 0),
			       5, USB_TYPE_VENDOR, 0, 0,
			       data, 2, HZ * 3 );
	dbg(__FUNCTION__ " res = %d, devnum = %d", res, udev->devnum);
	dbg(__FUNCTION__ " data[0] = %d, data[1] = %d", data[0], data[1] );
	/* set feature */
	res = usb_control_msg( udev, usb_sndctrlpipe(udev, 0),
			       USB_REQ_SET_FEATURE, USB_TYPE_VENDOR,
			       0xc04e, 0x0000, NULL, 0, HZ * 3 );
	dbg(__FUNCTION__ " res = %d", res);
	/* These two are sent by the windows driver, but stall for
	 * me. I dont have an analyzer on the linux side so i can't
	 * see what is actually different and why * the device takes
	 * issue with them
	 */
#if 0
	/* this is some custom control message they send */
	res = usb_control_msg( udev, usb_sndctrlpipe(udev, 0),
			       0x04, USB_TYPE_VENDOR,
			       0x0808, 0x0000, NULL, 0, HZ * 3 );
	dbg(__FUNCTION__ " res = %d", res);
	/* this is another custom control message they send */
	res = usb_control_msg( udev, usb_sndctrlpipe(udev, 0),
			       0x02, USB_TYPE_VENDOR,
			       0x0000, 0x0100, NULL, 0, HZ * 3 );
	dbg(__FUNCTION__ " res = %d", res);
#endif
}
/**
 *	mceusb_open
 */
static int mceusb_open (struct inode *inode, struct file *file)
{
	struct usb_skel *dev = NULL;
	struct usb_device* udev = NULL;
	int subminor;
	int retval = 0;
	dbg(__FUNCTION__);
	/* This is a very sucky point. On lirc, we get passed the
	 * minor number of the lirc device, which is totally
	 * retarded. We want to support people opening /dev/usb/msir0
	 * directly though, so try and determine who the hell is
	 * calling us here
	 */
	if( MAJOR( inode->i_rdev ) != USB_MAJOR )
	{
		/* This is the lirc device just passing on the
		 * request. We probably mismatch minor numbers here,
		 * but the lucky fact is that nobody will ever use two
		 * of the exact same remotes with two recievers on one
		 * machine
		 */
		subminor = 0;
	} else {
		subminor = MINOR (inode->i_rdev) - USB_MCEUSB_MINOR_BASE;
	}
	if ((subminor < 0) ||
	    (subminor >= MAX_DEVICES)) {
		dbg("subminor %d", subminor);
		return -ENODEV;
	}
	/* Increment our usage count for the module.
	 * This is redundant here, because "struct file_operations"
	 * has an "owner" field. This line is included here soley as
	 * a reference for drivers using lesser structures... ;-)
	 */
	try_module_get(THIS_MODULE);
	/* lock our minor table and get our local data for this minor */
	down (&minor_table_mutex);
	dev = minor_table[subminor];
	if (dev == NULL) {
		dbg("dev == NULL");
		up (&minor_table_mutex);
		module_put(THIS_MODULE);
		return -ENODEV;
	}
	udev = dev->udev;
	/* lock this device */
	down (&dev->sem);
	/* unlock the minor table */
	up (&minor_table_mutex);
	/* increment our usage count for the driver */
	++dev->open_count;
	/* save our object in the file's private structure */
	file->private_data = dev;
	/* init the waitq */
	init_waitqueue_head( &dev->wait_q );
	/* clear off the first few messages. these look like
	 * calibration or test data, i can't really tell
	 * this also flushes in case we have random ir data queued up
	 */
	{
		char junk[64];
		int partial = 0, retval, i;
		for( i = 0; i < 40; i++ )
		{
			retval = usb_bulk_msg (udev,
					       usb_rcvbulkpipe
					       (udev,
						dev->bulk_in_endpointAddr),
					       junk, 64,
					       &partial, HZ*10);
		}
	}
	msir_cleanup( dev );
	/* unlock this device */
	up (&dev->sem);
	return retval;
}
/**
 *	mceusb_release
 */
static int mceusb_release (struct inode *inode, struct file *file)
{
	struct usb_skel *dev;
	int retval = 0;
	dev = (struct usb_skel *)file->private_data;
	if (dev == NULL) {
		dbg (__FUNCTION__ " - object is NULL");
		return -ENODEV;
	}
	dbg(__FUNCTION__ " - minor %d", dev->minor);
	/* lock our minor table */
	down (&minor_table_mutex);
	/* lock our device */
	down (&dev->sem);
	if (dev->open_count <= 0) {
		dbg (__FUNCTION__ " - device not opened");
		retval = -ENODEV;
		goto exit_not_opened;
	}
	if (dev->udev == NULL) {
		/* the device was unplugged before the file was released */
		up (&dev->sem);
		mceusb_delete (dev);
		up (&minor_table_mutex);
		module_put(THIS_MODULE);
		return 0;
	}
	/* decrement our usage count for the device */
	--dev->open_count;
	if (dev->open_count <= 0) {
		/* shutdown any bulk writes that might be going on */
		usb_unlink_urb (dev->write_urb);
		dev->open_count = 0;
	}
	/* decrement our usage count for the module */
	module_put(THIS_MODULE);
  exit_not_opened:
	up (&dev->sem);
	up (&minor_table_mutex);
	return retval;
}
static void msir_cleanup( struct usb_skel* dev )
{
	memset( dev->bulk_in_buffer, 0, dev->bulk_in_size );
	memset( dev->usb_dbuffer, 0, sizeof(dev->usb_dbuffer) );
	dev->usb_dstart = 0;
	dev->usb_dcount = 0;
	dev->usb_valid_bytes_in_bulk_buffer = 0;
	memset( dev->mode2_data, 0, sizeof(dev->mode2_data) );
	dev->mode2_partial_pkt_size = 0;
	dev->mode2_count = 0;
	dev->mode2_idx = 0;
	dev->mode2_once = 0;
	dev->last_was_repeat_gap = 0;
}
static int set_use_inc(void* data)
{
	/*    struct usb_skel* skel = (struct usb_skel*)data; */
	try_module_get(THIS_MODULE);
	return 0;
}
static void set_use_dec(void* data)
{
	/* check for unplug here */
	struct usb_skel* dev = (struct usb_skel*) data;
	if( !dev->udev )
	{
		lirc_unregister_plugin( dev->minor );
		lirc_buffer_free( dev->plugin->rbuf );
		kfree( dev->plugin->rbuf );
		kfree( dev->plugin );
	}
	module_put(THIS_MODULE);
}
static int msir_available_mode2( struct usb_skel* dev )
{
	return dev->mode2_count - dev->last_was_repeat_gap;
}
static int msir_available_data( struct usb_skel* dev )
{
	return dev->usb_dcount;
}
static int msir_copy_mode2( struct usb_skel* dev, int* mode2_data, int count )
{
	int words_to_read = count;
	//    int words_avail   = dev->mode2_count;
	int words_avail = msir_available_mode2( dev );
	if( !dev->mode2_once && words_avail )
	{
		int space = PULSE_MASK;
		count--;
		copy_to_user( mode2_data, &space, 4 );
		dev->mode2_once = 1;
		if( count )
		{
			mode2_data++;
		}
		else
		{
			return 1;
		}
	}
	if( !words_avail )
	{
		return 0;
	}
	if( words_to_read > words_avail )
	{
		words_to_read = words_avail;
	}
	dbg(__FUNCTION__ " dev->mode2_count %d, dev->mode2_idx %d",
	    dev->mode2_count, dev->mode2_idx);
	dbg(__FUNCTION__ " words_avail %d words_to_read %d",
	    words_avail, words_to_read);
	copy_to_user( mode2_data, &( dev->mode2_data[dev->mode2_idx] ),
		      words_to_read<<2 );
	dbg(__FUNCTION__ " would copy_to_user() %d w", words_to_read);
	dev->mode2_idx += words_to_read;
	dev->mode2_count -= words_to_read;
	if( dev->mode2_count == 0 )
	{
		dev->mode2_idx = 0;
	}
	else if( dev->mode2_count == 1 && dev->last_was_repeat_gap )
	{
		// shift down the repeat gap and map it up to a
		// lirc-acceptable value
		dev->mode2_data[0] = dev->mode2_data[dev->mode2_idx];
		if( dev->mode2_data[0] >= 60000 &&
		    dev->mode2_data[0] <= 70000 )
			dev->mode2_data[0] = 95000;
		//printk(__FUNCTION__ " shifting value %d down from %d prev %d\n", dev->mode2_data[0], dev->mode2_idx,
		//    dev->mode2_data[dev->mode2_idx-1]);
		dev->mode2_idx = 0;
	}
	return words_to_read;
}
static int msir_read_from_buffer( struct usb_skel* dev, char* buffer, int len )
{
	if( len > dev->usb_dcount )
	{
		len = dev->usb_dcount;
	}
	memcpy( buffer, dev->usb_dbuffer + dev->usb_dstart, len );
	return len;
}
static int msir_mark_as_read( struct usb_skel* dev, int count )
{
	//    if( count != dev->usb_dcount )
	//        printk(KERN_INFO __FUNCTION__ " count %d dev->usb_dcount %d dev->usb_dstart %d", count, dev->usb_dcount, dev->usb_dstart );
	if( count > dev->usb_dcount )
		count = dev->usb_dcount;
	dev->usb_dcount -= count;
	dev->usb_dstart += count;
	if( !dev->usb_dcount )
		dev->usb_dstart = 0;
	return 0;
}
/*
 * msir_fetch_more_data
 *
 * The goal here is to read in more remote codes from the remote. In
 * the event that the remote isn't sending us anything, the caller
 * will block until a key is pressed (i.e. this performs phys read,
 * filtering, and queueing of data) unless dont_block is set to 1; in
 * this situation, it will perform a few reads and will exit out if it
 * does not see any appropriate data
 *
 * dev->sem should be locked when this function is called - fine grain
 * locking isn't really important here anyways
 *
 * TODO change this to do partials based on term codes, or not always fill
 */
static int msir_fetch_more_data( struct usb_skel* dev, int dont_block )
{
	int retries = 0;
	int count, this_read, partial;
	int retval;
	int writeindex, terminators = 0;
	int bytes_to_read = sizeof(dev->usb_dbuffer) - dev->usb_dcount;
	signed char* ibuf;
	int sequential_empty_reads = 0;
	/* special case where we are already full */
	if( bytes_to_read == 0 )
		return dev->usb_dcount;
	/* shift down */
	if( dev->usb_dcount && dev->usb_dstart != 0 )
	{
		printk( __FUNCTION__ " shifting %d bytes from %d\n",
			dev->usb_dcount, dev->usb_dstart );
		memcpy( dev->usb_dbuffer, dev->usb_dbuffer + dev->usb_dstart,
			dev->usb_dcount );
	}
	dev->usb_dstart = 0;
	writeindex = dev->usb_dcount;
	count = bytes_to_read;
	ibuf = (signed char*)dev->bulk_in_buffer;
	if( !dev->usb_valid_bytes_in_bulk_buffer )
	{
		memset( ibuf, 0, dev->bulk_in_size );
	}
#if 0
	printk( __FUNCTION__ " going to read, dev->usb_dcount %d, bytes_to_read %d vbb %d\n", dev->usb_dcount, bytes_to_read,
		dev->usb_valid_bytes_in_bulk_buffer );
#endif
	/* 8 is the minimum read size */
	while( count > 8 )
	{
		int i, goodbytes = 0;
		/* break out if we were interrupted */
		if( signal_pending(current) )
		{
			printk( __FUNCTION__ " got signal %ld\n",
				current->pending.signal.sig[0]);
			return dev->usb_dcount ? dev->usb_dcount : -EINTR;
		}
		/* or if we were unplugged */
		if( !dev->udev )
		{
			return -ENODEV;
		}
		/* or on data issues */
		if( writeindex == sizeof(dev->usb_dbuffer) )
		{
			printk( __FUNCTION__ " buffer full, returning\n");
			return dev->usb_dcount;
		}
		// always read the maximum
		this_read = dev->bulk_in_size;
		partial = 0;
		if( dev->usb_valid_bytes_in_bulk_buffer ) {
			retval = 0;
			this_read = partial = dev->usb_valid_bytes_in_bulk_buffer;
			dev->usb_valid_bytes_in_bulk_buffer = 0;
		} else {
			// This call always returns almost immediately
			// with data, since this device will always
			// provide a 2 byte response on a bulk
			// read. Not exactly friendly to the usb bus
			// or our load avg. We attempt to compensate
			// for this on 2 byte reads below
			memset( ibuf, 0, dev->bulk_in_size );
			retval = usb_bulk_msg (dev->udev,
					       usb_rcvbulkpipe
					       (dev->udev,
						dev->bulk_in_endpointAddr),
					       (unsigned char*)ibuf, this_read,
					       &partial, HZ*10);
		}
		if( retval )
		{
			/* break out on errors */
			printk(__FUNCTION__ " got retval %d %d %d",
			       retval, this_read, partial );
			if( retval == USB_ST_DATAOVERRUN && retries < 5 )
			{
				retries++;
				interruptible_sleep_on_timeout
					( &dev->wait_q, HZ );
				continue;
			}
			else
			{
				return -EIO;
			}
		} else {
			retries = 0;
		}
		if( partial )
		{
			this_read = partial;
		}
		/* All packets i've seen start with b1 60. If no data
		 * was actually available, the transceiver still gives
		 * this byte pair back. We only care about actual
		 * codes, so we can safely ignore these 2 byte reads
		 */
		if( this_read > 2 )
		{
#if 0
			printk( __FUNCTION__ " read %d bytes partial %d goodbytes %d writeidx %d\n",
				this_read, partial, goodbytes, writeindex );
#endif
			sequential_empty_reads = 0;
			/* copy from the input buffer to the capture buffer */
			for( i = 0; i < this_read; i++ )
			{
				if( (((unsigned char*)ibuf)[i] == 0xb1) ||
				    (ibuf[i] == 0x60) )
					;
				else
				{
					if( writeindex == sizeof(dev->usb_dbuffer) )
					{
						/* this can happen in
						 * repeats, where
						 * basically the bulk
						 * buffer is getting
						 * spammed and we
						 * aren't processing
						 * data fast enough
						 */
#if 1
						dev->usb_valid_bytes_in_bulk_buffer = this_read - i;
						memcpy( ibuf, &( ibuf[i] ),
							dev->usb_valid_bytes_in_bulk_buffer );
#endif
						break;
					}
					dev->usb_dbuffer[writeindex++] = ibuf[i];
					goodbytes++;
					if( ibuf[i] == 0x7f )
					{
						terminators++;
						/* This is a bug - we should either get 10 or 15 */
						if( terminators > 15 )
						{
							dbg("bugbug - terminators %d at %d gb %d", terminators, i, goodbytes );
						} else
							dbg("terminator %d at %d gb %d", terminators, i, goodbytes );
						dbg("writeindex %d", writeindex);
					}
					else if( terminators )
					{
						if( ((unsigned char*)ibuf)[i] == 128 )
						{
							/* copy back any remainder and break out */
							dev->usb_valid_bytes_in_bulk_buffer = this_read - (i + 1);
							if( dev->usb_valid_bytes_in_bulk_buffer )
							{
								memcpy( ibuf, &( ibuf[i+1] ), dev->usb_valid_bytes_in_bulk_buffer );
							}
							count = 0;
							break;
						}
						if( terminators == 10 ||
						    terminators == 15 )
							dbg("post-termination data %d idx %d %d", ibuf[i], dev->usb_dcount, i);
					}
				}
			}
			dev->usb_dcount += goodbytes;
			count -= goodbytes;
		} else {
			sequential_empty_reads++;
			// assume no data
			if( dont_block && sequential_empty_reads == 5 )
				break;
			// Try to be nice to the usb bus by sleeping
			// for a bit here before going in to the next
			// read
			interruptible_sleep_on_timeout( &dev->wait_q, 1 );
		}
	}
	/* return the number of bytes available now */
	return dev->usb_dcount;
}
// layout of data, per Christoph Bartelmus
// The protocol is:
// 1 byte: -length of following packet
// the following bytes of the packet are:
// negative value:
//   -(number of time units) of pulse
// positive value:
//   (number of time units) of space
// one time unit is 50us
#define MCE_TIME_UNIT 50
// returns the number of bytes processed from the 'usb_data' array
static int msir_generate_mode2( struct usb_skel* dev, signed char* usb_data,
				int bytecount )
{
	int bytes_left_in_packet = 0;
	int pos = 0;
	int mode2count = 0;
	int last_was_pulse = 1;
	int last_pkt = 0;
	int split_pkt_size = 0;
	// XXX no bounds checking here
	int* mode2_data;
	int mode2_limit = sizeof( dev->mode2_data ) - dev->mode2_count;
	// If data exists in the buffer, we have to point to the last
	// item there so we can append consecutive pulse/space
	// ops. Otherwise, set last_was_pulse 1 (since the first byte
	// is a pulse, and we want to store in the first array
	// location
	if( dev->mode2_count == 0 )
	{
		mode2_data = &( dev->mode2_data[0] );
		last_was_pulse = (dev->mode2_once ? 1 : 0);
		mode2_data[0] = 0;
	}
	else
	{
		mode2_data = &( dev->mode2_data[dev->mode2_idx +
						dev->mode2_count - 1] );
		last_was_pulse = (mode2_data[0] & PULSE_BIT) ? 1 : 0;
	}
	while( pos < bytecount && !last_pkt &&
	       (mode2_limit > (dev->mode2_count + mode2count)) )
	{
		if( dev->mode2_partial_pkt_size )
		{
			bytes_left_in_packet = dev->mode2_partial_pkt_size;
			dev->mode2_partial_pkt_size = 0;
		}
		else {
			bytes_left_in_packet = 128 + usb_data[pos];
			// XXX out of sync? find the next packet
			// header, establish a distance, and fix the
			// packet size
			if( bytes_left_in_packet > 4 )
			{
				int i;
				for( i = pos + 1; i < pos + 4; i++ )
				{
					if( (int)(128 + usb_data[i]) <= 4 )
					{
						bytes_left_in_packet = i - pos;
						break;
					}
				}
			}
			else
			{
				// otherwise, increment past the header
				pos++;
			}
		}
		// special case where we have a terminator at the
		// start but not at the end of this packet, indicating
		// potential repeat, or the packet is less than 4
		// bytes, indicating end also special case a split
		// starting packet
		if( pos > 1 && bytes_left_in_packet < 4 )
		{
			// end
			last_pkt = 1;
		}
		else if( usb_data[pos] == 127 &&
			 usb_data[pos+bytes_left_in_packet-1] != 127 )
		{
			// the genius ir transciever is blending data
			// from the repeat events into a single
			// packet. how we handle this is by splitting
			// the packet (and truncating the packet size
			// value we read), then rewriting a new packet
			// header onto the outbound data.  it's
			// ultraghetto.
			while( usb_data[pos+bytes_left_in_packet-1] != 127 )
			{
				bytes_left_in_packet--;
				split_pkt_size++;
			}
			// repeat code
			last_pkt = 2;
		}
		while( bytes_left_in_packet && pos < bytecount )
		{
			int keycode = usb_data[pos];
			int pulse = 0;
			pos++;
			if( keycode < 0 )
			{
				pulse = 1;
				keycode += 128;
			}
			keycode *= MCE_TIME_UNIT;
			// on a state change, increment the position
			// for the output buffer and initialize the
			// current spot to 0; otherwise we need to
			// concatenate pulse/gap values for lirc to be
			// happy
			if( pulse != last_was_pulse &&
			    (mode2count || mode2_data[mode2count]))
			{
				if( dev->last_was_repeat_gap )
				{
					//printk( __FUNCTION__ " transition with lwrg set lastval %d idx1 %d idx2 %d\n",
					//  mode2_data[mode2count],mode2count, dev->mode2_count+dev->mode2_idx-1 );
				}
				mode2count++;
				mode2_data[mode2count] = 0;
			}
			mode2_data[mode2count] += keycode;
			// Or in the pulse bit, and map all gap
			// lengths to a fixed value; this makes lirc
			// happy, sort of.
			if( pulse ) {
				mode2_data[mode2count] |= PULSE_BIT;
				dev->last_was_repeat_gap = 0;
			}
			last_was_pulse = pulse;
			bytes_left_in_packet--;
		}
	}
	// If the last value in the data array is a repeat gap, set
	// the last_was_repeat_gap flag
	if( mode2_data[mode2count] > 20000 && mode2_data[mode2count] < 70000 )
	{
		// printk(__FUNCTION__ " setting lwrg for val %d idx1 %d idx2 %d\n",
		//    mode2_data[mode2count], mode2count, dev->mode2_count+dev->mode2_idx-1 );
		dev->last_was_repeat_gap = 1;
	} else {
		dev->last_was_repeat_gap = 0;
	}
	// this is a bit tricky; we need to change to a counter, but
	// if we already had data in dev->mode2_data, then byte 0
	// actually was pre-existing data and shouldn't be counted
	if( mode2count && !dev->mode2_count )
	{
		mode2count++;
		//        printk(__FUNCTION__ " mode2count++ to %d\n", mode2count);
	}
	// never lie about how much output we have
	dev->mode2_count += mode2count;
	if( last_pkt == 1 )
	{
		return bytecount;
	}
	else
	{
		//  note the partial pkt size, and make sure we only claim
		//  the bytes we processed
		if( last_pkt == 2 )
		{
			dev->mode2_partial_pkt_size = split_pkt_size;
		}
#if 1
		// XXX this i am not sure about; it seems like this should be required, but it
		// isn't, and seems to cause problems
		else
		{
			dev->mode2_partial_pkt_size = bytes_left_in_packet;
		}
#endif
		return pos;
	}
}
static ssize_t mceusb_read( struct file* file, char* buffer,
			    size_t count, loff_t* ppos)
{
	char _data_buffer[128];
	struct usb_skel* dev;
	int read_count;
	int bytes_copied = 0;
	dev = (struct usb_skel*) file->private_data;
	if( (count % 4) != 0 )
	{
		return -EINVAL;
	}
	down( &dev->sem );
	/* verify that the device wasn't unplugged */
	if (dev->udev == NULL) {
		up( &dev->sem );
		return -ENODEV;
	}
	dbg(__FUNCTION__ " (1) calling msir_copy_mode2 with %d", count);
	bytes_copied = 4 * msir_copy_mode2( dev, (int*)buffer, count >> 2 );
	if( bytes_copied == count )
	{
		up( &dev->sem );
		return count;
	}
	/* we didn't get enough mode2 data. the process now is a bit complex
	 * 1. see if we have data read from the usb device that hasn't
	 *    been converted to mode2; if so, convert that, and try to
	 *    copy that out
	 * 2. otherwise, go ahead and read more, then convert that, then copy
	 */
	if( dev->usb_dcount )
	{
		read_count = msir_read_from_buffer( dev, _data_buffer, 128 );
		read_count = msir_generate_mode2
			( dev, (signed char*)_data_buffer, read_count );
		msir_mark_as_read( dev, read_count );
		bytes_copied += (4 * msir_copy_mode2
				 ( dev, (int*)(buffer + bytes_copied),
				   (count-bytes_copied) >> 2 ));
	}
	if( bytes_copied == count )
	{
		up( &dev->sem );
		return count;
	}
	/* read more data in a loop until we get enough */
	while( bytes_copied < count )
	{
		read_count = msir_fetch_more_data
			( dev, (file->f_flags & O_NONBLOCK ? 1 : 0) );
		if( read_count <= 0 )
		{
			up( &dev->sem );
			return (read_count ? read_count : -EWOULDBLOCK);
		}
		read_count = msir_read_from_buffer( dev, _data_buffer, 128 );
		read_count = msir_generate_mode2
			( dev, (signed char*)_data_buffer, read_count );
		msir_mark_as_read( dev, read_count );
		bytes_copied += (4 * msir_copy_mode2
				 ( dev, (int*)(buffer + bytes_copied),
				   (count-bytes_copied) >> 2 ));
	}
	up( &dev->sem );
	return bytes_copied;
}
/**
 * mceusb_poll
 */
static unsigned int mceusb_poll(struct file* file, poll_table* wait)
{
	struct usb_skel* dev;
	int data;
	dev = (struct usb_skel*)file->private_data;
	// So this is a crummy poll. Unfortunately all the lirc tools
	// assume your hardware is interrupt driven. Instead, we have
	// to actually read here to see whether or not there is data
	// (unless we have a key saved up - unlikely )
	//    if( dev->usb_dcount || dev->mode2_count )
	if( msir_available_data( dev ) || msir_available_mode2( dev ) )
	{
		return POLLIN | POLLRDNORM;
	}
	else {
		down( &dev->sem );
		data = msir_fetch_more_data( dev, 1 );
		up( &dev->sem );
		if( data )
			return POLLIN | POLLRDNORM;
	}
	return 0;
}
/**
 *	mceusb_write
 */
static ssize_t mceusb_write (struct file *file, const char *buffer, size_t count, loff_t *ppos)
{
	struct usb_skel *dev;
	ssize_t bytes_written = 0;
	int retval = 0;
	dev = (struct usb_skel *)file->private_data;
	dbg(__FUNCTION__ " - minor %d, count = %d", dev->minor, count);
	/* lock this object */
	down (&dev->sem);
	/* verify that the device wasn't unplugged */
	if (dev->udev == NULL) {
		retval = -ENODEV;
		goto exit;
	}
	/* verify that we actually have some data to write */
	if (count == 0) {
		dbg(__FUNCTION__ " - write request of 0 bytes");
		goto exit;
	}
	/* see if we are already in the middle of a write */
	if (dev->write_urb->status == -EINPROGRESS) {
		dbg (__FUNCTION__ " - already writing");
		goto exit;
	}
	/* we can only write as much as 1 urb will hold */
	bytes_written = (count > dev->bulk_out_size) ?
		dev->bulk_out_size : count;
	/* copy the data from userspace into our urb */
	if (copy_from_user(dev->write_urb->transfer_buffer, buffer,
			   bytes_written)) {
		retval = -EFAULT;
		goto exit;
	}
	usb_mceusb_debug_data (__FUNCTION__, bytes_written,
			       dev->write_urb->transfer_buffer);
	/* set up our urb */
	FILL_BULK_URB(dev->write_urb, dev->udev,
		      usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr),
		      dev->write_urb->transfer_buffer, bytes_written,
		      mceusb_write_bulk_callback, dev);
	/* send the data out the bulk port */
	retval = usb_submit_urb(dev->write_urb);
	if (retval) {
		err(__FUNCTION__ " - failed submitting write urb, error %d",
		    retval);
	} else {
		retval = bytes_written;
	}
  exit:
	/* unlock the device */
	up (&dev->sem);
	return retval;
}
/*
 *	mceusb_write_bulk_callback
 */
static void mceusb_write_bulk_callback (struct urb *urb)
{
	struct usb_skel *dev = (struct usb_skel *)urb->context;
	dbg(__FUNCTION__ " - minor %d", dev->minor);
	if ((urb->status != -ENOENT) &&
	    (urb->status != -ECONNRESET)) {
		dbg(__FUNCTION__ " - nonzero write bulk status received: %d",
		    urb->status);
		return;
	}
	return;
}
/**
 *	mceusb_probe
 *
 *	Called by the usb core when a new device is connected that it
 *	thinks this driver might be interested in.
 */
static void * mceusb_probe(struct usb_device *udev, unsigned int ifnum,
			   const struct usb_device_id *id)
{
	struct usb_skel *dev = NULL;
	struct usb_interface *interface;
	struct usb_interface_descriptor *iface_desc;
	struct usb_endpoint_descriptor *endpoint;
	struct lirc_plugin* plugin;
	struct lirc_buffer* rbuf;
	int minor;
	int buffer_size;
	int i;
	char name[10];
	/* See if the device offered us matches what we can accept */
	if ((udev->descriptor.idVendor != USB_MCEUSB_VENDOR_ID) ||
	    (udev->descriptor.idProduct != USB_MCEUSB_PRODUCT_ID)) {
		return NULL;
	}
	/* select a "subminor" number (part of a minor number) */
	down (&minor_table_mutex);
	for (minor = 0; minor < MAX_DEVICES; ++minor) {
		if (minor_table[minor] == NULL)
			break;
	}
	if (minor >= MAX_DEVICES) {
		info ("Too many devices plugged in, can not handle this device.");
		goto exit;
	}
	/* allocate memory for our device state and intialize it */
	dev = kmalloc (sizeof(struct usb_skel), GFP_KERNEL);
	if (dev == NULL) {
		err ("Out of memory");
		goto exit;
	}
	minor_table[minor] = dev;
	interface = &udev->actconfig->interface[ifnum];
	init_MUTEX (&dev->sem);
	dev->udev = udev;
	dev->interface = interface;
	dev->minor = minor;
	/* set up the endpoint information */
	/* check out the endpoints */
	iface_desc = &interface->altsetting[0];
	for (i = 0; i < iface_desc->bNumEndpoints; ++i) {
		endpoint = &iface_desc->endpoint[i];
		if ((endpoint->bEndpointAddress & 0x80) &&
		    ((endpoint->bmAttributes & 3) == 0x02)) {
			/* we found a bulk in endpoint */
			buffer_size = endpoint->wMaxPacketSize;
			dev->bulk_in_size = buffer_size;
			dev->bulk_in_endpointAddr = endpoint->bEndpointAddress;
			dev->bulk_in_buffer = kmalloc (buffer_size, GFP_KERNEL);
			if (!dev->bulk_in_buffer) {
				err("Couldn't allocate bulk_in_buffer");
				goto error;
			}
		}
		if (((endpoint->bEndpointAddress & 0x80) == 0x00) &&
		    ((endpoint->bmAttributes & 3) == 0x02)) {
			/* we found a bulk out endpoint */
			dev->write_urb = usb_alloc_urb(0);
			if (!dev->write_urb) {
				err("No free urbs available");
				goto error;
			}
			buffer_size = endpoint->wMaxPacketSize;
			dev->bulk_out_size = buffer_size;
			dev->bulk_out_endpointAddr = endpoint->bEndpointAddress;
			dev->bulk_out_buffer = kmalloc (buffer_size, GFP_KERNEL);
			if (!dev->bulk_out_buffer) {
				err("Couldn't allocate bulk_out_buffer");
				goto error;
			}
			FILL_BULK_URB(dev->write_urb, udev,
				      usb_sndbulkpipe
				      (udev, endpoint->bEndpointAddress),
				      dev->bulk_out_buffer, buffer_size,
				      mceusb_write_bulk_callback, dev);
		}
	}
	memset( dev->mode2_data, 0, sizeof( dev->mode2_data ) );
	dev->mode2_idx = 0;
	dev->mode2_count = 0;
	dev->mode2_partial_pkt_size = 0;
	dev->mode2_once = 0;
	dev->last_was_repeat_gap = 0;
	/* Set up our lirc plugin */
	if(!(plugin = kmalloc(sizeof(struct lirc_plugin), GFP_KERNEL))) {
		err("out of memory");
		goto error;
	}
	memset( plugin, 0, sizeof(struct lirc_plugin) );
	if(!(rbuf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL))) {
		err("out of memory");
		kfree( plugin );
		goto error;
	}
	/* the lirc_atiusb module doesn't memset rbuf here ... ? */
	if( lirc_buffer_init( rbuf, sizeof(lirc_t),
			      sizeof(struct lirc_buffer))) {
		err("out of memory");
		kfree( plugin );
		kfree( rbuf );
		goto error;
	}
	strcpy(plugin->name, "lirc_mce ");
	plugin->minor       = minor;
	plugin->code_length = sizeof(lirc_t);
	plugin->features    = LIRC_CAN_REC_MODE2; // | LIRC_CAN_SEND_MODE2;
	plugin->data        = dev;
	plugin->rbuf        = rbuf;
	plugin->ioctl       = NULL;
	plugin->set_use_inc = &set_use_inc;
	plugin->set_use_dec = &set_use_dec;
	plugin->fops        = &mceusb_fops;
	if( lirc_register_plugin( plugin ) < 0 )
	{
		kfree( plugin );
		lirc_buffer_free( rbuf );
		kfree( rbuf );
		goto error;
	}
	dev->plugin = plugin;
	mceusb_setup( udev );
	/* let the user know what node this device is now attached to */
	info ("USB Microsoft IR Transceiver device now attached to msir%d",
	      dev->minor);
	goto exit;
  error:
	mceusb_delete (dev);
	dev = NULL;
  exit:
	up (&minor_table_mutex);
	return dev;
}
/**
 *	mceusb_disconnect
 *
 *	Called by the usb core when the device is removed from the system.
 */
static void mceusb_disconnect(struct usb_device *udev, void *ptr)
{
	int minor;
	down (&minor_table_mutex);
	down (&dev->sem);
	minor = dev->minor;
	/* unhook lirc things */
	lirc_unregister_plugin( dev->minor );
	lirc_buffer_free( dev->plugin->rbuf );
	kfree( dev->plugin->rbuf );
	kfree( dev->plugin );
	/* if the device is not opened, then we clean up right now */
	if (!dev->open_count) {
		up (&dev->sem);
		mceusb_delete (dev);
	} else {
		dev->udev = NULL;
		up (&dev->sem);
	}
	info("USB Skeleton #%d now disconnected", minor);
	up (&minor_table_mutex);
	return NULL;
}
/**
 *	usb_mceusb_init
 */
static int __init usb_mceusb_init(void)
{
	int result;
	/* register this driver with the USB subsystem */
	result = usb_register(&mceusb_driver);
	if (result < 0) {
		err("usb_register failed for the "__FILE__" driver. Error number %d",
		    result);
		return -1;
	}
	info(DRIVER_DESC " " DRIVER_VERSION);
	return 0;
}
/**
 *	usb_mceusb_exit
 */
static void __exit usb_mceusb_exit(void)
{
	/* deregister this driver with the USB subsystem */
	usb_deregister(&mceusb_driver);
}
module_init (usb_mceusb_init);
module_exit (usb_mceusb_exit);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
(-) linux-2.6.5.orig/drivers/char/lirc/lirc_parallel.c (+743 lines)
Line 0    Link Here 
/*      $Id: lirc_parallel.c,v 5.18 2002/11/19 20:22:07 ranty Exp $      */
/****************************************************************************
 ** lirc_parallel.c *********************************************************
 ****************************************************************************
 *
 * lirc_parallel - device driver for infra-red signal receiving and
 *                 transmitting unit built by the author
 *
 * Copyright (C) 1998 Christoph Bartelmus <lirc@bartelmus.de>
 *
 *  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
 *
 */
/***********************************************************************
 *************************       Includes        ***********************
 ***********************************************************************/
#include <linux/version.h>
#include <linux/config.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/signal.h>
#include <linux/config.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/ioport.h>
#include <linux/time.h>
#include <linux/mm.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <asm/io.h>
#include <asm/signal.h>
#include <asm/irq.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#include <linux/poll.h>
#include <linux/parport.h>
#include <linux/lirc.h>
#include "lirc_dev.h"
#include "lirc_parallel.h"
#define LIRC_DRIVER_NAME "lirc_parallel"
/***********************************************************************
 *************************   Globale Variablen   ***********************
 ***********************************************************************/
unsigned int irq = CONFIG_LIRC_IRQ_PARALLEL;
unsigned int io = CONFIG_LIRC_PORT_PARALLEL;
#ifdef CONFIG_LIRC_TIMER
unsigned int timer = 0;
unsigned int default_timer = CONFIG_LIRC_TIMER;
#endif
#define WBUF_SIZE (256)
#define RBUF_SIZE (256) /* this must be a power of 2 larger than 1 */
static lirc_t wbuf[WBUF_SIZE];
static lirc_t rbuf[RBUF_SIZE];
DECLARE_WAIT_QUEUE_HEAD(lirc_wait);
unsigned int rptr=0,wptr=0;
unsigned int lost_irqs=0;
int is_open=0;
struct parport *pport;
struct pardevice *ppdevice;
int is_claimed=0;
int pf(void *handle);
void kf(void *handle);
/***********************************************************************
 *************************   Interne Funktionen  ***********************
 ***********************************************************************/
unsigned int __inline__ in(int offset)
{
	switch(offset)
	{
	case LIRC_LP_BASE:
		return(parport_read_data(pport));
	case LIRC_LP_STATUS:
		return(parport_read_status(pport));
	case LIRC_LP_CONTROL:
		return(parport_read_control(pport));
	}
	return(0); /* make compiler happy */
}
void __inline__ out(int offset, int value)
{
	switch(offset)
	{
	case LIRC_LP_BASE:
		parport_write_data(pport,value);
		break;
	case LIRC_LP_CONTROL:
		parport_write_control(pport,value);
		break;
	case LIRC_LP_STATUS:
		printk(KERN_INFO "%s: attempt to write to status register\n",
		       LIRC_DRIVER_NAME);
		break;
	}
}
unsigned int __inline__ lirc_get_timer(void)
{
	return(in(LIRC_PORT_TIMER)&LIRC_PORT_TIMER_BIT);
}
unsigned int __inline__  lirc_get_signal(void)
{
	return(in(LIRC_PORT_SIGNAL)&LIRC_PORT_SIGNAL_BIT);
}
void __inline__ lirc_on(void)
{
	out(LIRC_PORT_DATA,LIRC_PORT_DATA_BIT);
}
void __inline__ lirc_off(void)
{
	out(LIRC_PORT_DATA,0);
}
unsigned int init_CONFIG_LIRC_TIMER(void)
{
	struct timeval tv,now;
	unsigned int level,newlevel,timeelapsed,newtimer;
	int count=0;
	do_gettimeofday(&tv);
	tv.tv_sec++;                     /* wait max. 1 sec. */
	level=lirc_get_timer();
	do
	{
		newlevel=lirc_get_timer();
		if(level==0 && newlevel!=0) count++;
		level=newlevel;
		do_gettimeofday(&now);
	}
	while(count<1000 && (now.tv_sec<tv.tv_sec
			     || (now.tv_sec==tv.tv_sec
				 && now.tv_usec<tv.tv_usec)));
	timeelapsed=((now.tv_sec+1-tv.tv_sec)*1000000
		     +(now.tv_usec-tv.tv_usec));
	if(count>=1000 && timeelapsed>0)
	{
		if(default_timer==0)                    /* autodetect timer */
		{
			newtimer=(1000000*count)/timeelapsed;
			printk(KERN_INFO "%s: %u Hz timer detected\n",
			       LIRC_DRIVER_NAME,newtimer);
			return(newtimer);
		}
		else
		{
			newtimer=(1000000*count)/timeelapsed;
			if(abs(newtimer-default_timer)>
			   default_timer/10) /* bad timer */
			{
				printk(KERN_NOTICE "%s: bad timer: %u Hz\n",
				       LIRC_DRIVER_NAME,newtimer);
				printk(KERN_NOTICE "%s: using default timer: "
				       "%u Hz\n",
				       LIRC_DRIVER_NAME,default_timer);
				return(default_timer);
			}
			else
			{
				printk(KERN_INFO "%s: %u Hz timer detected\n",
				       LIRC_DRIVER_NAME,newtimer);
				return(newtimer); /* use detected value */
			}
		}
	}
	else
	{
		printk(KERN_NOTICE "%s: no timer detected\n",LIRC_DRIVER_NAME);
		return(0);
	}
}
int lirc_claim(void)
{
	if(parport_claim(ppdevice)!=0)
	{
		printk(KERN_WARNING "%s: could not claim port\n",
		       LIRC_DRIVER_NAME);
		printk(KERN_WARNING "%s: waiting for port becoming available"
		       "\n",LIRC_DRIVER_NAME);
		if(parport_claim_or_block(ppdevice)<0)
		{
			printk(KERN_NOTICE "%s: could not claim port, giving"
			       " up\n",LIRC_DRIVER_NAME);
			return(0);
		}
	}
	out(LIRC_LP_CONTROL,LP_PSELECP|LP_PINITP);
	is_claimed=1;
	return(1);
}
/***********************************************************************
 *************************   interrupt handler  ************************
 ***********************************************************************/
static inline void rbuf_write(lirc_t signal)
{
	unsigned int nwptr;
	nwptr=(wptr+1) & (RBUF_SIZE-1);
	if(nwptr==rptr) /* no new signals will be accepted */
	{
		lost_irqs++;
		printk(KERN_NOTICE "%s: buffer overrun\n",LIRC_DRIVER_NAME);
		return;
	}
	rbuf[wptr]=signal;
	wptr=nwptr;
}
void irq_handler(int i,void *blah,struct pt_regs * regs)
{
	struct timeval tv;
	static struct timeval lasttv;
	static int init=0;
	long signal;
	lirc_t data;
	unsigned int level,newlevel;
	unsigned int timeout;
#ifdef CONFIG_MODULE_UNLOAD
	if(!module_refcount(THIS_MODULE))
		return;
#endif
	if(!is_claimed)
	{
		return;
	}
	/* disable interrupt */
	/*
	  disable_irq(irq);
	  out(LIRC_PORT_IRQ,in(LIRC_PORT_IRQ)&(~LP_PINTEN));
	*/
	if(in(1)&LP_PSELECD)
	{
		return;
	}
#ifdef CONFIG_LIRC_TIMER
	if(init)
	{
		do_gettimeofday(&tv);
	        signal=tv.tv_sec-lasttv.tv_sec;
		if(signal>15)
		{
			data=PULSE_MASK;  /* really long time */
		}
		else
		{
			data=(lirc_t) (signal*1000000+
				       tv.tv_usec-lasttv.tv_usec+
				       LIRC_SFH506_DELAY);
		};
		rbuf_write(data); /* space */
	}
	else
	{
		if(timer==0) /* wake up; we'll lose this signal
				but it will be garbage if the device
				is turned on anyway
			      */
		{
			timer=init_CONFIG_LIRC_TIMER();
			/* enable_irq(irq); */
			return;
		}
		init=1;
	}
	timeout=timer/10;           /* timeout after 1/10 sec. */
	signal=1;
	level=lirc_get_timer();
	do{
		newlevel=lirc_get_timer();
		if(level==0 && newlevel!=0) signal++;
		level=newlevel;
		/* giving up */
		if(signal>timeout || (in(1)&LP_PSELECD))
		{
			signal=0;
			printk(KERN_NOTICE "%s: timeout\n",LIRC_DRIVER_NAME);
			break;
		}
	}
	while(lirc_get_signal());
	if(signal!=0)
	{
		/* ajust value to usecs */
		signal=(long) (((unsigned long long) signal)*1000000)/timer;
		if(signal>LIRC_SFH506_DELAY)
		{
			data=signal-LIRC_SFH506_DELAY;
		}
		else
		{
			data=1;
		}
		rbuf_write(PULSE_BIT|data); /* pulse */
	}
	do_gettimeofday(&lasttv);
#else
	/* add your code here */
#endif
	wake_up_interruptible(&lirc_wait);
	/* enable interrupt */
	/*
	  enable_irq(irq);
	  out(LIRC_PORT_IRQ,in(LIRC_PORT_IRQ)|LP_PINTEN);
	*/
}
/***********************************************************************
 **************************   file_operations   ************************
 ***********************************************************************/
static loff_t lirc_lseek(struct file *filep,loff_t offset,int orig)
{
	return(-ESPIPE);
}
static ssize_t lirc_read(struct file *filep,char *buf,size_t n,loff_t *ppos)
{
	int result;
	int count=0;
	DECLARE_WAITQUEUE(wait, current);
	if(n%sizeof(lirc_t)) return(-EINVAL);
	result=verify_area(VERIFY_WRITE,buf,n);
	if(result) return(result);
	add_wait_queue(&lirc_wait,&wait);
	current->state=TASK_INTERRUPTIBLE;
	while(count<n)
	{
		if(rptr!=wptr)
		{
			copy_to_user(buf+count,(char *) &rbuf[rptr],
				     sizeof(lirc_t));
			rptr=(rptr+1)&(RBUF_SIZE-1);
			count+=sizeof(lirc_t);
		}
		else
		{
			if(filep->f_flags & O_NONBLOCK)
			{
				result=-EAGAIN;
				break;
			}
			if (signal_pending(current))
			{
				result=-ERESTARTSYS;
				break;
			}
			schedule();
			current->state=TASK_INTERRUPTIBLE;
		}
	}
	remove_wait_queue(&lirc_wait,&wait);
	current->state=TASK_RUNNING;
	return(count ? count:result);
}
static ssize_t lirc_write(struct file *filep,const char *buf,size_t n,
			  loff_t *ppos)
{
	int result,count;
	unsigned int i;
	unsigned int level,newlevel;
	unsigned long flags;
	lirc_t counttimer;
	if(!is_claimed)
	{
		return(-EBUSY);
	}
	if(n%sizeof(lirc_t)) return(-EINVAL);
	result=verify_area(VERIFY_READ,buf,n);
	if(result) return(result);
	count=n/sizeof(lirc_t);
	if(count>WBUF_SIZE || count%2==0) return(-EINVAL);
	copy_from_user(wbuf,buf,n);
#ifdef CONFIG_LIRC_TIMER
	if(timer==0) /* try again if device is ready */
	{
		timer=init_CONFIG_LIRC_TIMER();
		if(timer==0) return(-EIO);
	}
	/* ajust values from usecs */
	for(i=0;i<count;i++)
	{
		wbuf[i]=(lirc_t) (((double) wbuf[i])*timer/1000000);
	}
	local_save_flags(flags);local_irq_disable();
	i=0;
	while(i<count)
	{
		level=lirc_get_timer();
		counttimer=0;
		lirc_on();
		do
		{
			newlevel=lirc_get_timer();
			if(level==0 && newlevel!=0) counttimer++;
			level=newlevel;
			if(in(1)&LP_PSELECD)
			{
				lirc_off();
				local_irq_restore(flags); /* sti(); */
				return(-EIO);
			}
		}
		while(counttimer<wbuf[i]);i++;
		lirc_off();
		if(i==count) break;
		counttimer=0;
		do
		{
			newlevel=lirc_get_timer();
			if(level==0 && newlevel!=0) counttimer++;
			level=newlevel;
			if(in(1)&LP_PSELECD)
			{
				local_irq_restore(flags); /* sti(); */
				return(-EIO);
			}
		}
		while(counttimer<wbuf[i]);i++;
	}
	local_irq_restore(flags); /* sti(); */
#else
	/*
	   place code that handles write
	   without extarnal timer here
	*/
#endif
	return(n);
}
static unsigned int lirc_poll(struct file *file, poll_table * wait)
{
	poll_wait(file, &lirc_wait,wait);
	if (rptr!=wptr)
		return(POLLIN|POLLRDNORM);
	return(0);
}
static int lirc_ioctl(struct inode *node,struct file *filep,unsigned int cmd,
		      unsigned long arg)
{
        int result;
	unsigned long features=LIRC_CAN_SEND_PULSE|LIRC_CAN_REC_MODE2,mode;
	switch(cmd)
	{
	case LIRC_GET_FEATURES:
		result=put_user(features,(unsigned long *) arg);
		if(result) return(result);
		break;
	case LIRC_GET_SEND_MODE:
		result=put_user(LIRC_MODE_PULSE,(unsigned long *) arg);
		if(result) return(result);
		break;
	case LIRC_GET_REC_MODE:
		result=put_user(LIRC_MODE_MODE2,(unsigned long *) arg);
		if(result) return(result);
		break;
	case LIRC_SET_SEND_MODE:
		result=get_user(mode,(unsigned long *) arg);
		if(result) return(result);
		if(mode!=LIRC_MODE_PULSE) return(-EINVAL);
		break;
	case LIRC_SET_REC_MODE:
		result=get_user(mode,(unsigned long *) arg);
		if(result) return(result);
		if(mode!=LIRC_MODE_MODE2) return(-ENOSYS);
		break;
	default:
		return(-ENOIOCTLCMD);
	}
	return(0);
}
static int lirc_open(struct inode* node,struct file* filep)
{
#ifdef CONFIG_MODULE_UNLOAD
	if(module_refcount(THIS_MODULE))
	{
		return(-EBUSY);
	}
#endif
	if(!lirc_claim())
	{
		return(-EBUSY);
	}
	pport->ops->enable_irq(pport);
	/* init read ptr */
	rptr=wptr=0;
	lost_irqs=0;
	try_module_get(THIS_MODULE);
	is_open=1;
	return(0);
}
static int lirc_close(struct inode* node,struct file* filep)
{
	if(is_claimed)
	{
		is_claimed=0;
		parport_release(ppdevice);
	}
	is_open=0;
	module_put(THIS_MODULE);
	return(0);
}
static struct file_operations lirc_fops =
{
	llseek:  lirc_lseek,
	read:    lirc_read,
	write:   lirc_write,
	poll:    lirc_poll,
	ioctl:   lirc_ioctl,
	open:    lirc_open,
	release: lirc_close
};
static int set_use_inc(void* data)
{
#if WE_DONT_USE_LOCAL_OPEN_CLOSE
       try_module_get(THIS_MODULE);
#endif
       return 0;
}
static void set_use_dec(void* data)
{
#if WE_DONT_USE_LOCAL_OPEN_CLOSE
       module_put(THIS_MODULE);
#endif
}
static struct lirc_plugin plugin = {
       name:           LIRC_DRIVER_NAME,
       minor:          -1,
       code_length:    1,
       sample_rate:    0,
       data:           NULL,
       add_to_buf:     NULL,
       get_queue:      NULL,
       set_use_inc:    set_use_inc,
       set_use_dec:    set_use_dec,
       fops:           &lirc_fops,
};
static int __init lirc_parallel_init (void)
{
#ifndef LIRC26
	pport=parport_enumerate();
	while(pport!=NULL)
	{
		if(pport->base==io)
		{
			break;
		}
		pport=pport->next;
	}
#else
	pport=parport_find_base(io);
#endif
	if(pport==NULL)
	{
		printk(KERN_NOTICE "%s: no port at %x found\n",
		       LIRC_DRIVER_NAME,io);
		return(-ENXIO);
	}
	ppdevice=parport_register_device(pport,LIRC_DRIVER_NAME,
					 pf,kf,irq_handler,0,NULL);
	if(ppdevice==NULL)
	{
		printk(KERN_NOTICE "%s: parport_register_device() failed\n",
		       LIRC_DRIVER_NAME);
		return(-ENXIO);
	}
	if(parport_claim(ppdevice)!=0)
		goto skip_init;
	is_claimed=1;
	out(LIRC_LP_CONTROL,LP_PSELECP|LP_PINITP);
#ifdef CONFIG_LIRC_TIMER
#       ifdef DEBUG
	out(LIRC_PORT_DATA,LIRC_PORT_DATA_BIT);
#       endif
	timer=init_CONFIG_LIRC_TIMER();
#       if 0 	/* continue even if device is offline */
	if(timer==0)
	{
		is_claimed=0;
		parport_release(pport);
		parport_unregister_device(ppdevice);
		return(-EIO);
	}
#       endif
#       ifdef DEBUG
	out(LIRC_PORT_DATA,0);
#       endif
#endif
	is_claimed=0;
	parport_release(ppdevice);
 skip_init:
	if ((plugin.minor = lirc_register_plugin(&plugin)) < 0)
	{
		printk(KERN_NOTICE "%s: register_chrdev() failed\n",LIRC_DRIVER_NAME);
		parport_unregister_device(ppdevice);
		return(-EIO);
	}
	printk(KERN_INFO "%s: installed using port 0x%04x irq %d\n",LIRC_DRIVER_NAME,io,irq);
	return(0);
}
static void __exit lirc_parallel_exit(void)
{
#ifdef CONFIG_MODULE_UNLOAD
	if(module_refcount(THIS_MODULE)) return;
#endif
	parport_unregister_device(ppdevice);
	lirc_unregister_plugin(plugin.minor);
}
static struct timer_list poll_timer;
static void poll_state(unsigned long ignored);
static void poll_state(unsigned long ignored)
{
	printk(KERN_NOTICE "%s: time\n",
	       LIRC_DRIVER_NAME);
	del_timer(&poll_timer);
	if(is_claimed)
		return;
	kf(NULL);
	if(!is_claimed)
	{
		printk(KERN_NOTICE "%s: could not claim port, giving up\n",
		       LIRC_DRIVER_NAME);
		init_timer(&poll_timer);
		poll_timer.expires=jiffies+HZ;
		poll_timer.data=(unsigned long) current;
		poll_timer.function=poll_state;
		add_timer(&poll_timer);
	}
}
int pf(void *handle)
{
	pport->ops->disable_irq(pport);
	is_claimed=0;
	return(0);
}
void kf(void *handle)
{
	if(!is_open)
		return;
	if(!lirc_claim())
		return;
	pport->ops->enable_irq(pport);
	/* this is a bit annoying when you actually print...*/
	/*
	printk(KERN_INFO "%s: reclaimed port\n",LIRC_DRIVER_NAME);
	*/
}
MODULE_AUTHOR("Christoph Bartelmus");
MODULE_DESCRIPTION("Infrared receiver driver for parallel ports.");
MODULE_LICENSE("GPL");
MODULE_PARM(io, "i");
MODULE_PARM_DESC(io, "I/O address base (0x3bc, 0x378 or 0x278)");
MODULE_PARM(irq, "i");
MODULE_PARM_DESC(irq, "Interrupt (7 or 5)");
module_init(lirc_parallel_init);
module_exit(lirc_parallel_exit);
(-) linux-2.6.5.orig/drivers/char/lirc/lirc_parallel.h (+24 lines)
Line 0    Link Here 
/*      $Id: lirc_parallel.h,v 5.1 1999/07/21 18:23:37 columbus Exp $      */
#ifndef _LIRC_PARALLEL_H
#define _LIRC_PARALLEL_H
#include <linux/lp.h>
#define LIRC_PORT_LEN 3
#define LIRC_LP_BASE    0
#define LIRC_LP_STATUS  1
#define LIRC_LP_CONTROL 2
#define LIRC_PORT_DATA           LIRC_LP_BASE    /* base */
#define LIRC_PORT_DATA_BIT               0x01    /* 1st bit */
#define LIRC_PORT_TIMER        LIRC_LP_STATUS    /* status port */
#define LIRC_PORT_TIMER_BIT          LP_PBUSY    /* busy signal */
#define LIRC_PORT_SIGNAL       LIRC_LP_STATUS    /* status port */
#define LIRC_PORT_SIGNAL_BIT          LP_PACK    /* ack signal */
#define LIRC_PORT_IRQ         LIRC_LP_CONTROL    /* control port */
#define LIRC_SFH506_DELAY 0             /* delay t_phl in usecs */
#endif
(-) linux-2.6.5.orig/drivers/char/lirc/lirc_serial.c (+1056 lines)
Line 0    Link Here 
/*      $Id: lirc_serial.c,v 5.49 2004/01/12 10:21:12 lirc Exp $      */
/****************************************************************************
 ** 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>
 *
 *  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 CONFIG_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
   Flameeyes Patches Contribution
    - Ronald Wahl <ronald.wahl@informatik.tu-chemnitz.de> sent a patch to
      eliminate a deadlock on SMP systems.
    - Florian Steinel <Florian.Steinel@t-online.de> sent a patch to fix irq
      disabling by kernel.
    - Jindrich Makovicka <makovick@kmlinux.fjfi.cvut.cz> sent a patch fixing
      one-shot use of lirc_serial.
*/
#include <linux/config.h>
#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/major.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/smp_lock.h>
#include <asm/system.h>
#include <asm/segment.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/fcntl.h>
#include <linux/lirc.h>
#include "lirc_dev.h"
#if defined(rdtsc)
#define USE_RDTSC
#warning "Note: using rdtsc instruction"
#endif
struct lirc_serial
{
	int type;
	int signal_pin;
	int signal_pin_change;
	int on;
	int off;
	long (*send_pulse)(unsigned long length);
	void (*send_space)(long length);
	int features;
};
#define LIRC_HOMEBREW        0
#define LIRC_IRDEO           1
#define LIRC_IRDEO_REMOTE    2
#define LIRC_ANIMAX          3
#define LIRC_IGOR            4
#ifdef CONFIG_LIRC_SERIAL_IRDEO
int type=LIRC_IRDEO;
#elif defined(CONFIG_LIRC_SERIAL_IRDEO_REMOTE)
int type=LIRC_IRDEO_REMOTE;
#elif defined(CONFIG_LIRC_SERIAL_ANIMAX)
int type=LIRC_ANIMAX;
#elif defined(CONFIG_LIRC_SERIAL_IGOR)
int type=LIRC_IGOR;
#else
int type=LIRC_HOMEBREW;
#endif
#ifdef CONFIG_LIRC_SERIAL_SOFTCARRIER
int softcarrier=1;
#else
int softcarrier=0;
#endif
static int sense = -1;   /* -1 = auto, 0 = active high, 1 = active low */
static int io = CONFIG_LIRC_PORT_SERIAL;
static int irq = CONFIG_LIRC_IRQ_SERIAL;
static int debug = 0;
MODULE_PARM(type, "i");
MODULE_PARM_DESC(type, "Hardware type (0 = home-brew, 1 = IRdeo,"
		 " 2 = IRdeo Remote, 3 = AnimaX");
MODULE_PARM(io, "i");
MODULE_PARM_DESC(io, "I/O address base (0x3f8 or 0x2f8)");
MODULE_PARM(irq, "i");
MODULE_PARM_DESC(irq, "Interrupt (4 or 3)");
MODULE_PARM(sense, "i");
MODULE_PARM_DESC(sense, "Override autodetection of IR receiver circuit"
		 " (0 = active high, 1 = active low )");
MODULE_PARM(softcarrier, "i");
MODULE_PARM_DESC(softcarrier, "Software carrier (0 = off, 1 = on)");
MODULE_PARM(debug,"i");
#define dprintk           if (debug) printk
#define LOGHEAD           "lirc_serial: "
/* forward declarations */
long send_pulse_irdeo(unsigned long length);
long send_pulse_homebrew(unsigned long length);
void send_space_irdeo(long length);
void send_space_homebrew(long length);
struct lirc_serial hardware[]=
{
	/* home-brew receiver/transmitter */
	{
		LIRC_HOMEBREW,
		UART_MSR_DCD,
		UART_MSR_DDCD,
		UART_MCR_RTS|UART_MCR_OUT2|UART_MCR_DTR,
		UART_MCR_RTS|UART_MCR_OUT2,
		send_pulse_homebrew,
		send_space_homebrew,
		(
#ifdef CONFIG_LIRC_SERIAL_TRANSMITTER
		 LIRC_CAN_SET_SEND_DUTY_CYCLE|
		 LIRC_CAN_SET_SEND_CARRIER|
		 LIRC_CAN_SEND_PULSE|
#endif
		 LIRC_CAN_REC_MODE2)
	},
	/* IRdeo classic */
	{
		LIRC_IRDEO,
		UART_MSR_DSR,
		UART_MSR_DDSR,
		UART_MCR_OUT2,
		UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2,
		send_pulse_irdeo,
		send_space_irdeo,
		(LIRC_CAN_SET_SEND_DUTY_CYCLE|
		 LIRC_CAN_SEND_PULSE|
		 LIRC_CAN_REC_MODE2)
	},
	/* IRdeo remote */
	{
		LIRC_IRDEO_REMOTE,
		UART_MSR_DSR,
		UART_MSR_DDSR,
		UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2,
		UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2,
		send_pulse_irdeo,
		send_space_irdeo,
		(LIRC_CAN_SET_SEND_DUTY_CYCLE|
		 LIRC_CAN_SEND_PULSE|
		 LIRC_CAN_REC_MODE2)
	},
	/* AnimaX */
	{
		LIRC_ANIMAX,
		UART_MSR_DCD,
		UART_MSR_DDCD,
		0,
		UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2,
		NULL,
		NULL,
		LIRC_CAN_REC_MODE2
	},
	/* home-brew receiver/transmitter (Igor Cesko's variation) */
	{
		LIRC_HOMEBREW,
		UART_MSR_DSR,
		UART_MSR_DDSR,
		UART_MCR_RTS|UART_MCR_OUT2|UART_MCR_DTR,
		UART_MCR_RTS|UART_MCR_OUT2,
		send_pulse_homebrew,
		send_space_homebrew,
		(
#ifdef CONFIG_LIRC_SERIAL_TRANSMITTER
		 LIRC_CAN_SET_SEND_DUTY_CYCLE|
		 LIRC_CAN_SET_SEND_CARRIER|
		 LIRC_CAN_SEND_PULSE|
#endif
		 LIRC_CAN_REC_MODE2)
	}
};
#define LIRC_DRIVER_NAME "lirc_serial"
#define RS_ISR_PASS_LIMIT 256
/* A long pulse code from a remote might take upto 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
#define WBUF_LEN 256
static struct timeval lasttv = {0, 0};
static spinlock_t lirc_lock = SPIN_LOCK_UNLOCKED;
static struct lirc_buffer rbuf;
static lirc_t wbuf[WBUF_LEN];
unsigned int freq = 38000;
unsigned int duty_cycle = 50;
/* Initialized in init_timing_params() */
unsigned long period = 0;
unsigned long pulse_width = 0;
unsigned long space_width = 0;
#if defined(__i386__)
/*
  From:
  Linux I/O port programming mini-HOWTO
  Author: Riku Saikkonen <Riku.Saikkonen@hut.fi>
  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 CONFIG_LIRC_SERIAL_TRANSMITTER_LATENCY 450
#else
/* does anybody have information on other platforms ? */
/* 256 = 1<<8 */
#define CONFIG_LIRC_SERIAL_TRANSMITTER_LATENCY 256
#endif  /* __i386__ */
static inline unsigned int sinp(int offset)
{
	return inb(io + offset);
}
static inline void soutp(int offset, int value)
{
	outb(value, io + offset);
}
static inline void on(void)
{
	soutp(UART_MCR,hardware[type].on);
}
static inline void off(void)
{
	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 inline 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
 */
/* So send_pulse can quickly convert microseconds to clocks */
unsigned long conv_us_to_clocks = 0;
static inline int init_timing_params(unsigned int new_duty_cycle,
		unsigned int new_freq)
{
	unsigned long long loops_per_sec,work;
	duty_cycle=new_duty_cycle;
	freq=new_freq;
	loops_per_sec=current_cpu_data.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 */
	conv_us_to_clocks=(work>>32);
	/* 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;
	dprintk(LOGHEAD
	       ": 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, current_cpu_data.loops_per_jiffy,
	       pulse_width, space_width, conv_us_to_clocks);
	return 0;
}
#else /* ! USE_RDTSC */
static inline 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<=
	   CONFIG_LIRC_SERIAL_TRANSMITTER_LATENCY) return(-EINVAL);
	if(256*1000000L/new_freq*(100-new_duty_cycle)/100<=
	   CONFIG_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;
	dprintk(LOGHEAD
	       ": 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 */
long send_pulse_irdeo(unsigned long length)
{
	long rawbits;
	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)
	{
		return((-rawbits)*10000/1152);
	}
	else
	{
		return((3-i)*3*10000/1152+(-rawbits)*10000/1152);
	}
}
#ifdef USE_RDTSC
/* Version that uses Pentium rdtsc instruction to measure clocks */
/* This version does sub-microsecond timing using rdtsc instruction,
 * and does away with the fudged CONFIG_LIRC_SERIAL_TRANSMITTER_LATENCY
 * Implicitly i586 architecture...  - Steve
 */
static inline long send_pulse_homebrew_softcarrier(unsigned long length)
{
	int flag;
	unsigned long target, start, now;
	/* Get going quick as we can */
	rdtscl(start);on();
	/* Convert length from microseconds to clocks */
	length*=conv_us_to_clocks;
	/* And loop till time is up - flipping at right intervals */
	now=start;
	target=pulse_width;
	flag=1;
	while((now-start)<length)
	{
		/* Delay till flip time */
		do
		{
			rdtscl(now);
		}
		while ((now-start)<target);
		/* flip */
		if(flag)
		{
			rdtscl(now);off();
			target+=space_width;
		}
		else
		{
			rdtscl(now);on();
			target+=pulse_width;
		}
		flag=!flag;
	}
	rdtscl(now);
	return(((now-start)-length)/conv_us_to_clocks);
}
#else /* ! USE_RDTSC */
/* 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 */
/* To match 8 fractional bits used for pulse/space length */
static inline long send_pulse_homebrew_softcarrier(unsigned long length)
{
	int flag;
	unsigned long actual, target, d;
	length<<=8;
	actual=target=0; flag=0;
	while(actual<length)
	{
		if(flag)
		{
			off();
			target+=space_width;
		}
		else
		{
			on();
			target+=pulse_width;
		}
		d=(target-actual-CONFIG_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)+CONFIG_LIRC_SERIAL_TRANSMITTER_LATENCY;
		flag=!flag;
	}
	return((actual-length)>>8);
}
#endif /* USE_RDTSC */
long send_pulse_homebrew(unsigned long length)
{
	if(length<=0) return 0;
	if(softcarrier)
	{
		return send_pulse_homebrew_softcarrier(length);
	}
	else
	{
		on();
		safe_udelay(length);
		return(0);
	}
}
void send_space_irdeo(long length)
{
	if(length<=0) return;
	safe_udelay(length);
}
void send_space_homebrew(long length)
{
        off();
	if(length<=0) return;
	safe_udelay(length);
}
static void inline rbwrite(lirc_t l)
{
	if(lirc_buffer_full(&rbuf))    /* no new signals will be accepted */
	{
		dprintk(LOGHEAD ": Buffer overrun\n");
		return;
	}
	_lirc_buffer_write_1(&rbuf, (void *)&l);
}
static void inline frbwrite(lirc_t l)
{
	/* simple noise filter */
	static lirc_t pulse=0L,space=0L;
	static unsigned int ptr=0;
	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);
}
irqreturn_t irq_handler(int i, void *blah, struct pt_regs *regs)
{
	struct timeval tv;
	int status,counter,dcd;
	long deltv;
	lirc_t data;
	counter=0;
	do{
		counter++;
		status=sinp(UART_MSR);
		if(counter>RS_ISR_PASS_LIMIT)
		{
			printk(KERN_WARNING LIRC_DRIVER_NAME ": AIEEEE: "
			       "We're caught!\n");
			break;
		}
		if((status&hardware[type].signal_pin_change) && sense!=-1)
		{
			/* get current time */
			do_gettimeofday(&tv);
			/* New mode, written by Trent Piepho
			   <xyzzy@u.washington.edu>. */
			/* The old format was not very portable.
			   We now use the type lirc_t 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. */
			/* calculate time since last interrupt in
			   microseconds */
			dcd=(status & hardware[type].signal_pin) ? 1:0;
			deltv=tv.tv_sec-lasttv.tv_sec;
			if(deltv>15)
			{
				dprintk(LOGHEAD
				       ": AIEEEE: %d %d %lx %lx %lx %lx\n",
				       dcd,sense,
				       tv.tv_sec,lasttv.tv_sec,
				       tv.tv_usec,lasttv.tv_usec);
				data=PULSE_MASK; /* really long time */
				if(!(dcd^sense)) /* sanity check */
				{
				        /* detecting pulse while this
					   MUST be a space! */
				        sense=sense ? 0:1;
				}
			}
			else
			{
				data=(lirc_t) (deltv*1000000+
					       tv.tv_usec-
					       lasttv.tv_usec);
			};
			if(tv.tv_sec<lasttv.tv_sec ||
			   (tv.tv_sec==lasttv.tv_sec &&
			    tv.tv_usec<lasttv.tv_usec))
			{
				printk(KERN_WARNING LIRC_DRIVER_NAME
				       ": AIEEEE: your clock just jumped "
				       "backwards\n");
				printk(KERN_WARNING LIRC_DRIVER_NAME
				       ": %d %d %lx %lx %lx %lx\n",
				       dcd,sense,
				       tv.tv_sec,lasttv.tv_sec,
				       tv.tv_usec,lasttv.tv_usec);
				data=PULSE_MASK;
			}
			frbwrite(dcd^sense ? data : (data|PULSE_BIT));
			lasttv=tv;
			wake_up_interruptible(&rbuf.wait_poll);
		}
	} while(!(sinp(UART_IIR) & UART_IIR_NO_INT)); /* still pending ? */
	return IRQ_HANDLED;
}
static DECLARE_WAIT_QUEUE_HEAD(power_supply_queue);
static spinlock_t lirc_lock;
static int init_port(void)
{
	unsigned long flags;
	/* Reserve io region. */
	if(!request_region(io, 8, LIRC_DRIVER_NAME))
	{
		printk(KERN_ERR  LIRC_DRIVER_NAME
		       ": port %04x already in use\n", io);
		printk(KERN_WARNING LIRC_DRIVER_NAME
		       ": use 'setserial /dev/ttySX uart none'\n");
		printk(KERN_WARNING LIRC_DRIVER_NAME
		       ": or compile the serial port driver as module and\n");
		printk(KERN_WARNING LIRC_DRIVER_NAME
		       ": make sure this module is loaded first\n");
		return(-EBUSY);
	}
	lock_kernel();
	spin_lock_irqsave(&lirc_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)));
	/* Clear registers. */
	sinp(UART_LSR);
	sinp(UART_RX);
	sinp(UART_IIR);
	sinp(UART_MSR);
	/* Set line for power source */
	soutp(UART_MCR, hardware[type].off);
	/* Clear registers again to be sure. */
	sinp(UART_LSR);
	sinp(UART_RX);
	sinp(UART_IIR);
	sinp(UART_MSR);
	switch(hardware[type].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;
	}
	spin_unlock_irqrestore(&lirc_lock, flags);
	/* 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 sec for the power supply */
		sleep_on_timeout(&power_supply_queue,HZ);
		sense=(sinp(UART_MSR) & hardware[type].signal_pin) ? 1:0;
		printk(KERN_INFO  LIRC_DRIVER_NAME  ": auto-detected active "
		       "%s receiver\n",sense ? "low":"high");
	}
	else
	{
		printk(KERN_INFO  LIRC_DRIVER_NAME  ": Manually using active "
		       "%s receiver\n",sense ? "low":"high");
	};
	unlock_kernel();
	return 0;
}
static int set_use_inc(void* data)
{
	int result;
	unsigned long flags;
	spin_lock(&lirc_lock);
#ifdef CONFIG_MODULE_UNLOAD
	if(module_refcount(THIS_MODULE))
	{
		spin_unlock(&lirc_lock);
		return -EBUSY;
	}
#endif
	/* initialize timestamp */
	do_gettimeofday(&lasttv);
	result=request_irq(irq,irq_handler,SA_INTERRUPT,LIRC_DRIVER_NAME,NULL);
	switch(result)
	{
	case -EBUSY:
		printk(KERN_ERR LIRC_DRIVER_NAME ": IRQ %d busy\n", irq);
		spin_unlock(&lirc_lock);
		return -EBUSY;
	case -EINVAL:
		printk(KERN_ERR LIRC_DRIVER_NAME
		       ": Bad irq number or handler\n");
		spin_unlock(&lirc_lock);
		return -EINVAL;
	default:
		dprintk(LOGHEAD
		       ": Interrupt %d, port %04x obtained\n", irq, io);
		break;
	};
	local_irq_save(flags);
	/* Set DLAB 0. */
	soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
	soutp(UART_IER, sinp(UART_IER)|UART_IER_MSI);
	local_irq_restore(flags);
	try_module_get(THIS_MODULE);
	spin_unlock(&lirc_lock);
	return 0;
}
static void set_use_dec(void* data)
{	unsigned long flags;
	spin_lock_irqsave(&lirc_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(&lirc_lock, flags);
	free_irq(irq, NULL);
	dprintk(LOGHEAD ": freed IRQ %d\n", irq);
	module_put(THIS_MODULE);
}
static ssize_t lirc_write(struct file *file, const char *buf,
			 size_t n, loff_t * ppos)
{
	int retval,i,count;
	unsigned long flags;
	long delta=0;
	if(!(hardware[type].features&LIRC_CAN_SEND_PULSE))
	{
		return(-EBADF);
	}
	if(n%sizeof(lirc_t)) return(-EINVAL);
	retval=verify_area(VERIFY_READ,buf,n);
	if(retval) return(retval);
	count=n/sizeof(lirc_t);
	if(count>WBUF_LEN || count%2==0) return(-EINVAL);
	copy_from_user(wbuf,buf,n);
	spin_lock_irqsave(&lirc_lock, flags);
	if(hardware[type].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(&lirc_lock, flags);
	return(n);
}
static int lirc_ioctl(struct inode *node,struct file *filep,unsigned int cmd,
		      unsigned long arg)
{
        int result;
	unsigned long value;
	unsigned int ivalue;
	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),
				(unsigned long *) arg);
		if(result) return(result);
		break;
	case LIRC_SET_SEND_MODE:
		if(!(hardware[type].features&LIRC_CAN_SEND_MASK))
		{
			return(-ENOIOCTLCMD);
		}
		result=get_user(value,(unsigned long *) arg);
		if(result) return(result);
		/* only LIRC_MODE_PULSE supported */
		if(value!=LIRC_MODE_PULSE) return(-ENOSYS);
		break;
	case LIRC_GET_LENGTH:
		return(-ENOSYS);
		break;
	case LIRC_SET_SEND_DUTY_CYCLE:
		dprintk(LOGHEAD ": SET_SEND_DUTY_CYCLE\n");
		if(!(hardware[type].features&LIRC_CAN_SET_SEND_DUTY_CYCLE))
		{
			return(-ENOIOCTLCMD);
		}
		result=get_user(ivalue,(unsigned int *) arg);
		if(result) return(result);
		if(ivalue<=0 || ivalue>100) return(-EINVAL);
		return init_timing_params(ivalue, freq);
		break;
	case LIRC_SET_SEND_CARRIER:
		dprintk(LOGHEAD ": SET_SEND_CARRIER\n");
		if(!(hardware[type].features&LIRC_CAN_SET_SEND_CARRIER))
		{
			return(-ENOIOCTLCMD);
		}
		result=get_user(ivalue,(unsigned int *) arg);
		if(result) return(result);
		if(ivalue>500000 || ivalue<20000) return(-EINVAL);
		return init_timing_params(duty_cycle, ivalue);
		break;
	default:
		return(-ENOIOCTLCMD);
	}
	return(0);
}
static struct file_operations lirc_fops =
{
	write:   lirc_write,
};
static struct lirc_plugin plugin = {
	name:		LIRC_DRIVER_NAME,
	minor:		-1,
	code_length:	1,
	sample_rate:	0,
	data:		NULL,
	add_to_buf:	NULL,
	get_queue:	NULL,
	rbuf:		&rbuf,
	set_use_inc:	set_use_inc,
	set_use_dec:	set_use_dec,
	ioctl:		lirc_ioctl,
	fops:		&lirc_fops,
};
MODULE_AUTHOR("Ralph Metzler, Trent Piepho, Ben Pfaff, Christoph Bartelmus");
MODULE_DESCRIPTION("Infra-red receiver driver for serial ports.");
MODULE_LICENSE("GPL");
static int __init lirc_serial_init(void)
{
	int result;
	switch(type)
	{
	case LIRC_HOMEBREW:
	case LIRC_IRDEO:
	case LIRC_IRDEO_REMOTE:
	case LIRC_ANIMAX:
	case LIRC_IGOR:
		break;
	default:
		return(-EINVAL);
	}
	if(!softcarrier && hardware[type].type==LIRC_HOMEBREW)
	{
		hardware[type].features&=~(LIRC_CAN_SET_SEND_DUTY_CYCLE|
					   LIRC_CAN_SET_SEND_CARRIER);
	}
	if ((result = init_port()) < 0)
		return result;
	plugin.features = hardware[type].features;
	lirc_buffer_init(&rbuf, sizeof(lirc_t), RBUF_LEN);
	if ((plugin.minor = lirc_register_plugin(&plugin)) < 0) {
		printk(KERN_ERR  LIRC_DRIVER_NAME
		       ": register_chrdev failed!\n");
		release_region(io, 8);
		return -EIO;
	}
	return 0;
}
static void __exit lirc_serial_exit(void)
{
	release_region(io, 8);
	lirc_buffer_free(&rbuf);
	lirc_unregister_plugin(plugin.minor);
	dprintk(LOGHEAD ": cleaned up module\n");
}
module_init(lirc_serial_init);
module_exit(lirc_serial_exit);
(-) linux-2.6.5.orig/drivers/char/lirc/lirc_sir.c (+1309 lines)
Line 0    Link Here 
/*
 * LIRC SIR driver, (C) 2000 Milan Pikula <www@fornax.sk>
 *
 * lirc_sir - Device driver for use with SIR (serial infra red)
 * mode of IrDA on many notebooks.
 *
 *  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
 *
 *
 * 2000/09/16 Frank Przybylski <mail@frankprzybylski.de> :
 *  added timeout and relaxed pulse detection, removed gap bug
 *
 * 2000/12/15 Christoph Bartelmus <lirc@bartelmus.de> :
 *   added support for Tekram Irmate 210 (sending does not work yet,
 *   kind of disappointing that nobody was able to implement that
 *   before),
 *   major clean-up
 *
 * 2001/02/27 Christoph Bartelmus <lirc@bartelmus.de> :
 *   added support for StrongARM SA1100 embedded microprocessor
 *   parts cut'n'pasted from sa1100_ir.c (C) 2000 Russell King
 */
#include <linux/version.h>
#include <linux/module.h>
#include <linux/config.h>
#if !defined(CONFIG_LIRC_ON_SA1100) && !defined(CONFIG_SERIAL_MODULE)
#warning "******************************************"
#warning " Your serial port driver is compiled into "
#warning " the kernel. You will have to release the "
#warning " port you want to use for LIRC with:      "