public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
From: Pavel Machek <pavel@suse.cz>
To: Flameeyes <dgp85@users.sourceforge.net>
Cc: LKML <linux-kernel@vger.kernel.org>,
	LIRC list <lirc-list@lists.sourceforge.net>
Subject: Re: [PATCH] lirc for 2.5/2.6 kernels - 20030802
Date: Thu, 7 Aug 2003 23:43:12 +0200	[thread overview]
Message-ID: <20030807214311.GC211@elf.ucw.cz> (raw)
In-Reply-To: <1059820741.3116.24.camel@laurelin>

Hi!

> This is the fourth version of my patch for use LIRC drivers under
> 2.5/2.6 kernels.
> 
> As usual, you can find it at
> http://flameeyes.web.ctonet.it/lirc/patch-lirc-20030802.diff.bz2
> 
> I changed the naming scheme, because I tried and the patch applies also
> in earliers and (probably) futures kernels, and call it only
> "patch-lirc.diff" will confuse about the versions. I think a datestamp
> is the best choice for now.

If you want to get this applied to the official tree (I hope you want
;-), you probably should start with smaller patch. I killed all
drivers but lirc_gpio, to make patch smaller/easier to check.

Its now small enough to inline, and that's good. 

Few questions:

* What does "For now don't try to use as static version" comment in
  lirc_gpio mean. Does it only work as a module?

* This looks like it should be integrated with drivers/input. After
  all remote control is just a strange keyboard. What are reasons that
  can't be done?
								Pavel


--- linux/drivers/char/Kconfig	2003-06-15 22:42:37.000000000 +0200
+++ linux-lirc/drivers/char/Kconfig	2003-08-07 22:53:49.000000000 +0200
@@ -591,6 +591,7 @@
 
 source "drivers/i2c/Kconfig"
 
+source "drivers/char/lirc/Kconfig"
 
 menu "Mice"
 
--- linux/drivers/char/Makefile	2003-06-15 22:42:37.000000000 +0200
+++ linux-lirc/drivers/char/Makefile	2003-08-07 22:53:49.000000000 +0200
@@ -7,7 +7,7 @@
 #
 FONTMAPFILE = cp437.uni
 
-obj-y	 += mem.o tty_io.o n_tty.o tty_ioctl.o pty.o misc.o random.o
+obj-y	 += mem.o tty_io.o n_tty.o tty_ioctl.o pty.o misc.o random.o lirc/
 
 obj-$(CONFIG_VT) += vt_ioctl.o vc_screen.o consolemap.o consolemap_deftbl.o selection.o keyboard.o
 obj-$(CONFIG_HW_CONSOLE) += vt.o defkeymap.o
--- linux/drivers/char/lirc/Kconfig	1970-01-01 01:00:00.000000000 +0100
+++ linux-lirc/drivers/char/lirc/Kconfig	2003-08-07 22:57:49.000000000 +0200
@@ -0,0 +1,22 @@
+# 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
+
+menu "Linux InfraRed Controller"
+
+config LIRC_SUPPORT
+	bool "Linux InfraRed Controller"
+
+	config LIRC_MAX_DEV
+		int "Maximum LIRC devices"
+		default "2"
+		depends on LIRC_SUPPORT
+
+	config LIRC_GPIO
+		tristate "GPIO Driver"
+		depends on LIRC_SUPPORT && VIDEO_BT848
+
+endmenu
+
--- linux/drivers/char/lirc/Makefile	1970-01-01 01:00:00.000000000 +0100
+++ linux-lirc/drivers/char/lirc/Makefile	2003-08-07 22:57:56.000000000 +0200
@@ -0,0 +1,6 @@
+#
+# Makefile for the lirc drivers
+#
+
+obj-$(CONFIG_LIRC_SUPPORT) += lirc_dev.o
+obj-$(CONFIG_LIRC_GPIO) += lirc_gpio.o
--- linux/drivers/char/lirc/lirc_dev.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-lirc/drivers/char/lirc/lirc_dev.c	2003-08-07 22:53:49.000000000 +0200
@@ -0,0 +1,718 @@
+/*
+ * 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.25 2003/05/04 09:31:28 ranty Exp $
+ *
+ */
+
+#include <linux/version.h>
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.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/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 tpid;
+	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->tpid = -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)
+{
+	unsigned char buf[BUFLEN];
+	unsigned int i;
+
+	if (lirc_buffer_full(ir->buf)) {
+		dprintk(LOGHEAD "buffer overflow\n",
+			ir->p.name, ir->p.minor);
+		return -EOVERFLOW;
+	}
+
+	for (i=0; i < ir->buf->chunk_size; i++) {
+		if (ir->p.get_key(ir->p.data, &buf[i], i)) {
+			return -ENODATA;
+		}
+		dprintk(LOGHEAD "remote code (0x%x) now in buffer\n",
+			ir->p.name, ir->p.minor, buf[i]);
+	}
+
+	/* here is the only point at which we add key codes to the buffer */
+	lirc_buffer_write_1(ir->buf, buf);
+
+	return SUCCESS;
+}
+
+/* main function of the polling thread
+ */
+static int lirc_thread(void *irctl)
+{
+	struct irctl *ir = irctl;
+
+	lock_kernel();
+
+	/* This thread doesn't need any user-level access,
+	 * so get rid of all our resources
+	 */
+	exit_mm(current);
+	exit_files(current);
+	exit_fs(current);
+	current->session = 1;
+	current->pgrp = 1;
+	current->euid = 0;
+	current->tty = NULL;
+	sigfillset(&current->blocked);
+
+	strcpy(current->comm, "lirc_dev");
+
+	unlock_kernel();
+
+	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) {
+				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);
+
+	if (ir->t_notify2 != NULL) {
+		down(ir->t_notify2);
+	}
+
+	ir->tpid = -1;
+	if (ir->t_notify != NULL) {
+		up(ir->t_notify);
+	}
+
+	dprintk(LOGHEAD "poll thread ended\n", ir->p.name, ir->p.minor);
+
+	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 || 50 < p->sample_rate) {
+			printk("lirc_dev: lirc_register_plugin:"
+			       "sample_rate must be beetween 2 and 50!\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->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;
+
+	if(p->sample_rate || p->get_queue) {
+		/* try to fire up polling thread */
+		ir->t_notify = &tn;
+		ir->tpid = kernel_thread(lirc_thread, (void*)ir, 0);
+		if (ir->tpid < 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->tpid >= 0) {
+		ir->t_notify = &tn;
+		ir->t_notify2 = &tn2;
+		ir->shutdown = 1;
+		{
+			struct task_struct *p;
+
+			p = find_task_by_pid(ir->tpid);
+			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);
+
+	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.value) >= CONFIG_LIRC_MAX_DEV) {
+		dprintk("lirc_dev [%d]: open result = -ENODEV\n",
+			MINOR(inode->i_rdev.value));
+		return -ENODEV;
+	}
+
+	ir = &irctls[MINOR(inode->i_rdev.value)];
+
+	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.value)];
+
+	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.value)];
+
+	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.value)];
+
+	dprintk(LOGHEAD "poll 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.value)];
+	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.value)];
+
+	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->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: 8
+ * End:
+ */
--- linux/drivers/char/lirc/lirc_dev.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-lirc/drivers/char/lirc/lirc_dev.h	2003-08-07 22:53:49.000000000 +0200
@@ -0,0 +1,206 @@
+/*
+ * 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);
+}
+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);
+}
+
+struct lirc_plugin
+{
+     char name[40];
+     int minor;
+     int code_length;
+     int sample_rate;
+     unsigned long features;
+     void* data;
+     int (*get_key) (void* data, unsigned char* key, int key_no);
+     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 ofthe  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 pooling will be performed and get_key
+ * 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
+ *
+ * get_key:
+ * get_key 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
+ *
+ * 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/drivers/char/lirc/lirc_gpio.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-lirc/drivers/char/lirc/lirc_gpio.c	2003-08-07 22:53:49.000000000 +0200
@@ -0,0 +1,548 @@
+/*
+ * 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.30 2003/06/07 22:00:10 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},
+#ifdef BTTV_PXELVWPLTVPAK
+	{BTTV_PXELVWPLTVPAK,          0, 0x00003e00,          0, 0x0010000,          0,   0, 15,  0},
+#endif
+	{BTTV_PXELVWPLTVPRO,          0, 0x00001f00,          0, 0x0008000,          0, 500, 12, 32},
+#ifdef BTTV_PV_BT878P_9B
+	{BTTV_PV_BT878P_9B,           0, 0x00001f00,          0, 0x0008000,          0, 500, 12, 32},
+#endif
+	{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_PXELVWPLTVPRO:
+#ifdef BTTV_PV_BT878P_9B
+	case BTTV_PV_BT878P_9B:
+#endif
+#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:
+	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;
+}
+
+static int get_key(void* data, unsigned char *key, int key_no)
+{
+	static unsigned long next_time = 0;
+	static unsigned char codes[MAX_BYTES];
+	unsigned long code = 0;
+	unsigned char cur_codes[MAX_BYTES];
+
+	if (key_no > 0)	{
+		if (code_bytes < 2 || key_no >= code_bytes) {
+			dprintk(LOGHEAD "something wrong in get_key\n", card);
+			return -EBADRQC;
+		}
+		*key = codes[key_no];
+		return SUCCESS;
+	}
+
+	if (bttv_read_gpio(card, &code)) {
+		dprintk(LOGHEAD "cannot read GPIO\n", card);
+		return -EIO;
+	}
+
+	if (build_key(code, cur_codes)) {
+		return -EFAULT;
+	}
+
+	if (soft_gap) {
+		if (!memcmp(codes, cur_codes, code_bytes) &&
+		    jiffies < next_time) {
+			return -EAGAIN;
+		}
+		next_time = jiffies + soft_gap;
+	}
+
+	memcpy(codes, cur_codes, code_bytes);
+
+	*key = codes[0];
+
+	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  ",
+	.get_key	= get_key,
+	.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 %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);
+}
+/* Dont try to use it as a static version !  */
+
+MODULE_DESCRIPTION("Driver module for remote control (data from bt848 GPIO port)");
+MODULE_AUTHOR("Artur Lipowski");
+MODULE_LICENSE("GPL");
+
+module_init(lirc_gpio_init);
+module_exit(lirc_gpio_exit);
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
--- linux/include/linux/lirc.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-lirc/include/linux/lirc.h	2003-08-07 22:53:49.000000000 +0200
@@ -0,0 +1,103 @@
+/*      $Id: lirc.h,v 5.8 2003/01/26 12:57:59 lirc Exp $      */
+
+#ifndef _LINUX_LIRC_H
+#define _LINUX_LIRC_H
+
+#if defined (__linux__)
+#include <asm/types.h>
+#include <linux/ioctl.h>
+#else
+#include <sys/types.h>
+typedef u_int32_t __u32;
+#endif
+
+#define PULSE_BIT  0x01000000
+#define PULSE_MASK 0x00FFFFFF
+
+typedef int lirc_t;
+
+/*
+ * lirc compatible hardware features
+ */
+
+
+#define LIRC_MODE2SEND(x) (x)
+#define LIRC_SEND2MODE(x) (x)
+#define LIRC_MODE2REC(x) ((x) << 16)
+#define LIRC_REC2MODE(x) ((x) >> 16)
+
+#define LIRC_MODE_RAW                  0x00000001
+#define LIRC_MODE_PULSE                0x00000002
+#define LIRC_MODE_MODE2                0x00000004
+#define LIRC_MODE_CODE                 0x00000008
+#define LIRC_MODE_LIRCCODE             0x00000010
+#define LIRC_MODE_STRING               0x00000020
+
+
+#define LIRC_CAN_SEND_RAW              LIRC_MODE2SEND(LIRC_MODE_RAW)
+#define LIRC_CAN_SEND_PULSE            LIRC_MODE2SEND(LIRC_MODE_PULSE)
+#define LIRC_CAN_SEND_MODE2            LIRC_MODE2SEND(LIRC_MODE_MODE2)
+#define LIRC_CAN_SEND_CODE             LIRC_MODE2SEND(LIRC_MODE_CODE)
+#define LIRC_CAN_SEND_LIRCCODE         LIRC_MODE2SEND(LIRC_MODE_LIRCCODE)
+#define LIRC_CAN_SEND_STRING           LIRC_MODE2SEND(LIRC_MODE_STRING)
+
+#define LIRC_CAN_SEND_MASK             0x0000003f
+
+#define LIRC_CAN_SET_SEND_CARRIER      0x00000100
+#define LIRC_CAN_SET_SEND_DUTY_CYCLE   0x00000200
+
+#define LIRC_CAN_REC_RAW               LIRC_MODE2REC(LIRC_MODE_RAW)
+#define LIRC_CAN_REC_PULSE             LIRC_MODE2REC(LIRC_MODE_PULSE)
+#define LIRC_CAN_REC_MODE2             LIRC_MODE2REC(LIRC_MODE_MODE2)
+#define LIRC_CAN_REC_CODE              LIRC_MODE2REC(LIRC_MODE_CODE)
+#define LIRC_CAN_REC_LIRCCODE          LIRC_MODE2REC(LIRC_MODE_LIRCCODE)
+#define LIRC_CAN_REC_STRING            LIRC_MODE2REC(LIRC_MODE_STRING)
+
+#define LIRC_CAN_REC_MASK              LIRC_MODE2REC(LIRC_CAN_SEND_MASK)
+
+#define LIRC_CAN_SET_REC_CARRIER       (LIRC_CAN_SET_SEND_CARRIER << 16)
+#define LIRC_CAN_SET_REC_DUTY_CYCLE    (LIRC_CAN_SET_SEND_DUTY_CYCLE << 16)
+
+#define LIRC_CAN_SET_REC_DUTY_CYCLE_RANGE 0x40000000
+#define LIRC_CAN_SET_REC_CARRIER_RANGE    0x80000000
+
+
+#define LIRC_CAN_SEND(x) ((x)&LIRC_CAN_SEND_MASK)
+#define LIRC_CAN_REC(x) ((x)&LIRC_CAN_REC_MASK)
+
+/*
+ * IOCTL commands for lirc driver
+ */
+
+#define LIRC_GET_FEATURES              _IOR('i', 0x00000000, __u32)
+
+#define LIRC_GET_SEND_MODE             _IOR('i', 0x00000001, __u32)
+#define LIRC_GET_REC_MODE              _IOR('i', 0x00000002, __u32)
+#define LIRC_GET_SEND_CARRIER          _IOR('i', 0x00000003, __u32)
+#define LIRC_GET_REC_CARRIER           _IOR('i', 0x00000004, __u32)
+#define LIRC_GET_SEND_DUTY_CYCLE       _IOR('i', 0x00000005, __u32)
+#define LIRC_GET_REC_DUTY_CYCLE        _IOR('i', 0x00000006, __u32)
+
+/* code length in bits, currently only for LIRC_MODE_LIRCCODE */
+#define LIRC_GET_LENGTH                _IOR('i', 0x0000000f, __u32)
+
+#define LIRC_SET_SEND_MODE             _IOW('i', 0x00000011, __u32)
+#define LIRC_SET_REC_MODE              _IOW('i', 0x00000012, __u32)
+/* Note: these can reset the according pulse_width */
+#define LIRC_SET_SEND_CARRIER          _IOW('i', 0x00000013, __u32)
+#define LIRC_SET_REC_CARRIER           _IOW('i', 0x00000014, __u32)
+#define LIRC_SET_SEND_DUTY_CYCLE       _IOW('i', 0x00000015, __u32)
+#define LIRC_SET_REC_DUTY_CYCLE        _IOW('i', 0x00000016, __u32)
+
+/* to set a range use
+   LIRC_SET_REC_DUTY_CYCLE_RANGE/LIRC_SET_REC_CARRIER_RANGE with the
+   lower bound first and later
+   LIRC_SET_REC_DUTY_CYCLE/LIRC_SET_REC_CARRIER with the upper bound */
+
+#define LIRC_SET_REC_DUTY_CYCLE_RANGE  _IOW('i', 0x0000001e, __u32)
+#define LIRC_SET_REC_CARRIER_RANGE     _IOW('i', 0x0000001f, __u32)
+
+#define DEV_LIRC	"lirc"
+#define IRCTL_DEV_MAJOR 61
+
+#endif


-- 
When do you have a heart between your knees?
[Johanka's followup: and *two* hearts?]

  reply	other threads:[~2003-08-07 21:45 UTC|newest]

Thread overview: 35+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2003-08-02 10:39 [PATCH] lirc for 2.5/2.6 kernels - 20030802 Flameeyes
2003-08-07 21:43 ` Pavel Machek [this message]
2003-08-08  9:21   ` Flameeyes
2003-08-08 23:17     ` Pavel Machek
2003-08-09 14:46       ` Christoph Bartelmus
2003-08-11 12:47         ` Pavel Machek
2003-08-11 13:11           ` Flameeyes
2003-08-11 14:42             ` Herbert Pötzl
2003-08-11 15:41               ` Pavel Machek
2003-08-11 18:40               ` Gerd Knorr
2003-08-11 15:38             ` Pavel Machek
2003-08-11 15:48               ` Flameeyes
2003-08-11 16:39                 ` Gerd Knorr
2003-08-11 17:56                   ` Johannes Stezenbach
2003-08-11 18:55                     ` Pavel Machek
2003-08-11 18:59                     ` Vojtech Pavlik
2003-08-11 19:17                       ` Pavel Machek
2003-08-11 19:34                         ` Vojtech Pavlik
2003-08-11 19:55                           ` Pavel Machek
2003-08-11 19:59                           ` Valdis.Kletnieks
2003-08-11 20:13                             ` Vojtech Pavlik
2003-08-11 19:22                       ` Pavel Machek
2003-08-14 15:22                   ` Dennis Björklund
2003-08-14 16:06                     ` Gerd Knorr
2003-08-11 18:52                 ` Pavel Machek
2003-08-11 20:01                   ` Flameeyes
2003-08-11 17:35               ` Johannes Stezenbach
2003-08-11 18:57                 ` Pavel Machek
2003-08-11 18:31           ` Gerd Knorr
2003-08-11 18:59             ` Pavel Machek
2003-08-11 19:54               ` Gerd Knorr
2003-08-11 20:04                 ` Pavel Machek
2003-08-11 13:14         ` Pavel Machek
2003-08-09 19:15   ` Flameeyes
2003-08-11 11:10   ` Pavel Machek

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20030807214311.GC211@elf.ucw.cz \
    --to=pavel@suse.cz \
    --cc=dgp85@users.sourceforge.net \
    --cc=linux-kernel@vger.kernel.org \
    --cc=lirc-list@lists.sourceforge.net \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox