Go to:
Gentoo Home
Documentation
Forums
Lists
Bugs
Planet
Store
Wiki
Get Gentoo!
Gentoo's Bugzilla – Attachment 29379 Details for
Bug 47404
gentoo-dev-sources lirc_i2c problems
Home
|
New
–
[Ex]
|
Browse
|
Search
|
Privacy Policy
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
modified older patch
4105_lirc_infrared-2.6.2_rc1-01242004.patch (text/plain), 231.82 KB, created by
Jordan
on 2004-04-15 15:58:21 UTC
(
hide
)
Description:
modified older patch
Filename:
MIME Type:
Creator:
Jordan
Created:
2004-04-15 15:58:21 UTC
Size:
231.82 KB
patch
obsolete
>diff -NPaur linux-2.6.2/Documentation/lirc/lirc_it87 linux-2.6.2-lirc/Documentation/lirc/lirc_it87 >--- linux-2.6.2/Documentation/lirc/lirc_it87 1970-01-01 01:00:00.000000000 +0100 >+++ linux-2.6.2-lirc/Documentation/lirc/lirc_it87 2004-02-09 20:03:11.565862952 +0100 >@@ -0,0 +1,54 @@ >+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 >diff -ruN linux-2.6.5rc2.old/drivers/char/Kconfig linux-2.6.5rc2/drivers/char/Kconfig >--- linux-2.6.5rc2.old/drivers/char/Kconfig 2004-03-19 22:50:18.885872928 -0500 >+++ linux-2.6.5rc2/drivers/char/Kconfig 2004-03-19 22:52:07.101421664 -0500 >@@ -599,6 +599,8 @@ > bool "Support for console on line printer" > depends on PC9800_OLDLP > >+source "drivers/char/lirc/Kconfig" >+ > config QIC02_TAPE > tristate "QIC-02 tape support" > help >diff -ruN linux-2.6.5rc2.old/drivers/char/Makefile linux-2.6.5rc2/drivers/char/Makefile >diff -NPaur linux-2.6.2/drivers/char/Makefile linux-2.6.2-lirc/drivers/char/Makefile >--- linux-2.6.2/drivers/char/Makefile 2004-02-09 20:00:26.563947056 +0100 >+++ linux-2.6.2-lirc/drivers/char/Makefile 2004-02-09 20:03:15.015338552 +0100 >@@ -7,7 +7,7 @@ > # > 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 \ > consolemap_deftbl.o selection.o keyboard.o >diff -NPaur linux-2.6.2/drivers/char/lirc/Kconfig linux-2.6.2-lirc/drivers/char/lirc/Kconfig >--- linux-2.6.2/drivers/char/lirc/Kconfig 1970-01-01 01:00:00.000000000 +0100 >+++ linux-2.6.2-lirc/drivers/char/lirc/Kconfig 2004-02-09 20:03:15.017338248 +0100 >@@ -0,0 +1,208 @@ >+# 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 >+ >diff -NPaur linux-2.6.2/drivers/char/lirc/Makefile linux-2.6.2-lirc/drivers/char/lirc/Makefile >--- linux-2.6.2/drivers/char/lirc/Makefile 1970-01-01 01:00:00.000000000 +0100 >+++ linux-2.6.2-lirc/drivers/char/lirc/Makefile 2004-02-09 20:03:15.019337944 +0100 >@@ -0,0 +1,14 @@ >+# >+# 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 >diff -NPaur linux-2.6.2/drivers/char/lirc/lirc_atiusb.c linux-2.6.2-lirc/drivers/char/lirc/lirc_atiusb.c >--- linux-2.6.2/drivers/char/lirc/lirc_atiusb.c 1970-01-01 01:00:00.000000000 +0100 >+++ linux-2.6.2-lirc/drivers/char/lirc/lirc_atiusb.c 2004-02-09 20:03:15.022337488 +0100 >@@ -0,0 +1,629 @@ >+/* 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: 405_lirc_infrared-2.6.2-02092004.patch,v 1.1 2004/02/24 22:27:37 brad_mssw 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 >+ >diff -NPaur linux-2.6.2/drivers/char/lirc/lirc_bt829.c linux-2.6.2-lirc/drivers/char/lirc/lirc_bt829.c >--- linux-2.6.2/drivers/char/lirc/lirc_bt829.c 1970-01-01 01:00:00.000000000 +0100 >+++ linux-2.6.2-lirc/drivers/char/lirc/lirc_bt829.c 2004-02-09 20:03:15.045333992 +0100 >@@ -0,0 +1,364 @@ >+/* >+ * 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_get_key (void* data, unsigned char* key, int key_no) >+{ >+ int status; >+ status = poll_main(); >+ *key = (status >> 8) & 0xFF; >+ // if ( status & 0xFF ) { >+ // printk(KERN_INFO "ATIR reading key %02X\n",*key); >+ // } >+ return (status & 0xFF) ? 0 : -1; >+} >+ >+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.get_key = atir_get_key; >+ 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); >diff -NPaur linux-2.6.2/drivers/char/lirc/lirc_dev.c linux-2.6.2-lirc/drivers/char/lirc/lirc_dev.c >--- linux-2.6.2/drivers/char/lirc/lirc_dev.c 1970-01-01 01:00:00.000000000 +0100 >+++ linux-2.6.2-lirc/drivers/char/lirc/lirc_dev.c 2004-02-09 20:03:15.048333536 +0100 >@@ -0,0 +1,713 @@ >+/* >+ * 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: 405_lirc_infrared-2.6.2-02092004.patch,v 1.1 2004/02/24 22:27:37 brad_mssw 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) >+{ >+ 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; >+ >+ 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) { >+ 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 || 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->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: 8 >+ * End: >+ */ >diff -NPaur linux-2.6.2/drivers/char/lirc/lirc_dev.h linux-2.6.2-lirc/drivers/char/lirc/lirc_dev.h >--- linux-2.6.2/drivers/char/lirc/lirc_dev.h 1970-01-01 01:00:00.000000000 +0100 >+++ linux-2.6.2-lirc/drivers/char/lirc/lirc_dev.h 2004-02-09 20:03:15.050333232 +0100 >@@ -0,0 +1,206 @@ >+/* >+ * LIRC base driver >+ * >+ * (L) by Artur Lipowski <alipowski@interia.pl> >+ * This code is licensed under GNU GPL >+ * >+ * $Id: 405_lirc_infrared-2.6.2-02092004.patch,v 1.1 2004/02/24 22:27:37 brad_mssw 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 >diff -NPaur linux-2.6.2/drivers/char/lirc/lirc_gpio.c linux-2.6.2-lirc/drivers/char/lirc/lirc_gpio.c >--- linux-2.6.2/drivers/char/lirc/lirc_gpio.c 1970-01-01 01:00:00.000000000 +0100 >+++ linux-2.6.2-lirc/drivers/char/lirc/lirc_gpio.c 2004-02-09 20:03:15.053332776 +0100 >@@ -0,0 +1,550 @@ >+/* >+ * 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: 405_lirc_infrared-2.6.2-02092004.patch,v 1.1 2004/02/24 22:27:37 brad_mssw 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; >+} >+ >+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 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: >+ */ >diff -NPaur linux-2.6.2/drivers/char/lirc/lirc_i2c.c linux-2.6.2-lirc/drivers/char/lirc/lirc_i2c.c >--- linux-2.6.2/drivers/char/lirc/lirc_i2c.c 1970-01-01 01:00:00.000000000 +0100 >+++ linux-2.6.2-lirc/drivers/char/lirc/lirc_i2c.c 2004-02-09 20:03:15.067330648 +0100 >@@ -0,0 +1,444 @@ >+/* $Id: 405_lirc_infrared-2.6.2-02092004.patch,v 1.1 2004/02/24 22:27:37 brad_mssw 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" >+ >+/* ----------------------------------------------------------------------- */ >+ >+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 get_key_pcf8574(void* data, unsigned char* key, int key_no) >+{ >+ struct i2c_ir *ir = data; >+ int rc; >+ unsigned char all, mask; >+ >+ /* 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) { >+ dprintk(DRIVER_NAME ": %s read error\n", ir->client.name); >+ return -1; >+ } >+ >+ /* drop duplicate polls */ >+ if (ir->b[0] == (rc & all)) { >+ return -1; >+ } >+ 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 -1; >+ } >+ >+ /* return valid key code */ >+ *key = rc & ir->bits; >+ return 0; >+} >+ >+static int get_key_haup(void* data, unsigned char* key, int key_no) >+{ >+ struct i2c_ir *ir = data; >+ unsigned char buf[3]; >+ __u16 code; >+ >+ if (ir->nextkey != -1) { >+ /* pass second byte */ >+ *key = ir->nextkey; >+ ir->nextkey = -1; >+ return 0; >+ } >+ >+ /* poll IR chip */ >+ if (3 == i2c_master_recv(&ir->client,buf,3)) { >+ ir->b[0] = buf[0]; >+ ir->b[1] = buf[1]; >+ ir->b[2] = buf[2]; >+ } else { >+ dprintk(DRIVER_NAME ": read error\n"); >+ /* keep last successfull read buffer */ >+ } >+ >+ /* key pressed ? */ >+ if ((ir->b[0] & 0x80) == 0) >+ return -1; >+ >+ 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); >+ >+ /* return it */ >+ *key = (code >> 8) & 0xff; >+ ir->nextkey = code & 0xff; >+ return 0; >+} >+ >+static int get_key_pixelview(void* data, unsigned char* key, int key_no) >+{ >+ struct i2c_ir *ir = data; >+ unsigned char b; >+ >+ /* poll IR chip */ >+ if (1 != i2c_master_recv(&ir->client,&b,1)) { >+ dprintk(DRIVER_NAME ": read error\n"); >+ return -1; >+ } >+ dprintk(DRIVER_NAME ": key %02x\n", b); >+ *key = b; >+ return 0; >+} >+ >+static int get_key_pv951(void* data, unsigned char* key, int key_no) >+{ >+ struct i2c_ir *ir = data; >+ unsigned char b; >+ static unsigned char codes[4]; >+ >+ if(key_no>0) >+ { >+ if(key_no>=4) { >+ dprintk(DRIVER_NAME >+ ": something wrong in get_key_pv951\n"); >+ return -EBADRQC; >+ } >+ *key = codes[key_no]; >+ return 0; >+ } >+ >+ /* poll IR chip */ >+ if (1 != i2c_master_recv(&ir->client,&b,1)) { >+ dprintk(DRIVER_NAME ": read error\n"); >+ return -1; >+ } >+ /* ignore 0xaa */ >+ if (b==0xaa) >+ return -1; >+ dprintk(DRIVER_NAME ": key %02x\n", b); >+ >+ codes[2] = reverse(b,8); >+ codes[3] = (~codes[2])&0xff; >+ codes[0] = 0x61; >+ codes[1] = 0xD6; >+ >+ *key=codes[0]; >+ return 0; >+} >+ >+static int get_key_knc1(void *data, unsigned char *key, int key_no) >+{ >+ struct i2c_ir *ir = data; >+ unsigned char b; >+ static unsigned char last_button = 0xFF; >+ >+ /* poll IR chip */ >+ if (1 != i2c_master_recv(&ir->client,&b,1)) { >+ dprintk(DRIVER_NAME ": read error\n"); >+ return -1; >+ } >+ >+ /* 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 */ >+ >+ if( b == 0xFF ) >+ return -1; >+ >+ dprintk(DRIVER_NAME ": key %02x\n", b); >+ >+ if ( b == 0xFE ) >+ b = last_button; >+ >+ *key = b; >+ last_button = b; >+ 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.get_key = get_key_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.get_key = get_key_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.get_key = get_key_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.get_key = get_key_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.get_key = get_key_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); >diff -NPaur linux-2.6.2/drivers/char/lirc/lirc_it87.c linux-2.6.2-lirc/drivers/char/lirc/lirc_it87.c >--- linux-2.6.2/drivers/char/lirc/lirc_it87.c 1970-01-01 01:00:00.000000000 +0100 >+++ linux-2.6.2-lirc/drivers/char/lirc/lirc_it87.c 2004-02-09 20:03:15.072329888 +0100 >@@ -0,0 +1,953 @@ >+/* >+ * 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 void 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); >+ if (module_refcount(THIS_MODULE)) { >+ spin_unlock(&dev_lock); >+ return -EBUSY; >+ } >+ 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, >+ get_key: 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 void 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; >+ } >+} >+ >+ >+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: >+ */ >diff -NPaur linux-2.6.2/drivers/char/lirc/lirc_it87.h linux-2.6.2-lirc/drivers/char/lirc/lirc_it87.h >--- linux-2.6.2/drivers/char/lirc/lirc_it87.h 1970-01-01 01:00:00.000000000 +0100 >+++ linux-2.6.2-lirc/drivers/char/lirc/lirc_it87.h 2004-02-09 20:03:15.074329584 +0100 >@@ -0,0 +1,116 @@ >+/* 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 ************************/ >diff -NPaur linux-2.6.2/drivers/char/lirc/lirc_mceusb.c linux-2.6.2-lirc/drivers/char/lirc/lirc_mceusb.c >--- linux-2.6.2/drivers/char/lirc/lirc_mceusb.c 1970-01-01 01:00:00.000000000 +0100 >+++ linux-2.6.2-lirc/drivers/char/lirc/lirc_mceusb.c 2004-02-09 20:03:15.100325632 +0100 >@@ -0,0 +1,1530 @@ >+/* >+ * 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"); >+ >diff -NPaur linux-2.6.2/drivers/char/lirc/lirc_parallel.c linux-2.6.2-lirc/drivers/char/lirc/lirc_parallel.c >--- linux-2.6.2/drivers/char/lirc/lirc_parallel.c 1970-01-01 01:00:00.000000000 +0100 >+++ linux-2.6.2-lirc/drivers/char/lirc/lirc_parallel.c 2004-02-09 20:03:15.104325024 +0100 >@@ -0,0 +1,733 @@ >+/* $Id: 405_lirc_infrared-2.6.2-02092004.patch,v 1.1 2004/02/24 22:27:37 brad_mssw 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; >+ >+ if(!module_refcount(THIS_MODULE)) >+ return; >+ >+ 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) >+{ >+ if(module_refcount(THIS_MODULE)) >+ { >+ return(-EBUSY); >+ } >+ 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, >+ get_key: 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) >+{ >+ pport=parport_enumerate(); >+ while(pport!=NULL) >+ { >+ if(pport->base==io) >+ { >+ break; >+ } >+ pport=pport->next; >+ } >+ 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) >+{ >+ if(module_refcount(THIS_MODULE)) return; >+ 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); >+ >diff -NPaur linux-2.6.2/drivers/char/lirc/lirc_parallel.h linux-2.6.2-lirc/drivers/char/lirc/lirc_parallel.h >--- linux-2.6.2/drivers/char/lirc/lirc_parallel.h 1970-01-01 01:00:00.000000000 +0100 >+++ linux-2.6.2-lirc/drivers/char/lirc/lirc_parallel.h 2004-02-09 20:03:15.105324872 +0100 >@@ -0,0 +1,24 @@ >+/* $Id: 405_lirc_infrared-2.6.2-02092004.patch,v 1.1 2004/02/24 22:27:37 brad_mssw 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 >diff -NPaur linux-2.6.2/drivers/char/lirc/lirc_serial.c linux-2.6.2-lirc/drivers/char/lirc/lirc_serial.c >--- linux-2.6.2/drivers/char/lirc/lirc_serial.c 1970-01-01 01:00:00.000000000 +0100 >+++ linux-2.6.2-lirc/drivers/char/lirc/lirc_serial.c 2004-02-09 20:29:55.619009808 +0100 >@@ -0,0 +1,1053 @@ >+/* $Id: 405_lirc_infrared-2.6.2-02092004.patch,v 1.1 2004/02/24 22:27:37 brad_mssw 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 <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); >+ if(module_refcount(THIS_MODULE)) >+ { >+ spin_unlock(&lirc_lock); >+ return -EBUSY; >+ } >+ >+ /* 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, >+ get_key: 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); >diff -NPaur linux-2.6.2/drivers/char/lirc/lirc_sir.c linux-2.6.2-lirc/drivers/char/lirc/lirc_sir.c >--- linux-2.6.2/drivers/char/lirc/lirc_sir.c 1970-01-01 01:00:00.000000000 +0100 >+++ linux-2.6.2-lirc/drivers/char/lirc/lirc_sir.c 2004-02-09 20:03:15.134320464 +0100 >@@ -0,0 +1,1303 @@ >+/* >+ * 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: " >+#warning " setserial /dev/ttySx uart none " >+#warning "******************************************" >+#endif >+ >+#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> >+#ifdef CONFIG_LIRC_ON_SA1100 >+#include <asm/hardware.h> >+#ifdef CONFIG_SA1100_COLLIE >+#include <asm/arch/tc35143.h> >+#include <asm/ucb1200.h> >+#endif >+#endif >+ >+#include <linux/timer.h> >+ >+#include <linux/lirc.h> >+#include "lirc_dev.h" >+ >+/* SECTION: Definitions */ >+ >+/**************************** Tekram dongle ***************************/ >+#ifdef CONFIG_LIRC_SIR_TEKRAM >+/* stolen from kernel source */ >+/* definitions for Tekram dongle */ >+#define TEKRAM_115200 0x00 >+#define TEKRAM_57600 0x01 >+#define TEKRAM_38400 0x02 >+#define TEKRAM_19200 0x03 >+#define TEKRAM_9600 0x04 >+#define TEKRAM_2400 0x08 >+ >+#define TEKRAM_PW 0x10 /* Pulse select bit */ >+ >+/* 10bit * 1s/115200bit in milli seconds = 87ms*/ >+#define TIME_CONST (10000000ul/115200ul) >+ >+#endif >+ >+#ifdef CONFIG_LIRC_SIR_ACTISYS_ACT200L >+static void init_act200(void); >+#endif >+ >+/******************************* SA1100 ********************************/ >+#ifdef CONFIG_LIRC_ON_SA1100 >+struct sa1100_ser2_registers >+{ >+ /* HSSP control register */ >+ unsigned char hscr0; >+ /* UART registers */ >+ unsigned char utcr0; >+ unsigned char utcr1; >+ unsigned char utcr2; >+ unsigned char utcr3; >+ unsigned char utcr4; >+ unsigned char utdr; >+ unsigned char utsr0; >+ unsigned char utsr1; >+} sr; >+ >+static int irq=IRQ_Ser2ICP; >+ >+#define LIRC_ON_SA1100_TRANSMITTER_LATENCY 0 >+ >+/* pulse/space ratio of 50/50 */ >+unsigned long pulse_width = (13-LIRC_ON_SA1100_TRANSMITTER_LATENCY); >+/* 1000000/freq-pulse_width */ >+unsigned long space_width = (13-LIRC_ON_SA1100_TRANSMITTER_LATENCY); >+unsigned int freq = 38000; /* modulation frequency */ >+unsigned int duty_cycle = 50; /* duty cycle of 50% */ >+ >+#endif >+ >+#define RBUF_LEN 1024 >+#define WBUF_LEN 1024 >+ >+#define LIRC_DRIVER_NAME "lirc_sir" >+ >+#ifndef CONFIG_LIRC_SIR_TEKRAM >+#define PULSE '[' >+ >+/* 9bit * 1s/115200bit in milli seconds = 78.125ms*/ >+#define TIME_CONST (9000000ul/115200ul) >+#endif >+ >+ >+/* timeout for sequences in jiffies (=5/100s) */ >+/* must be longer than TIME_CONST */ >+#define SIR_TIMEOUT (HZ*5/100) >+ >+#ifndef CONFIG_LIRC_ON_SA1100 >+static int io = CONFIG_LIRC_PORT_SIR; >+static int irq = CONFIG_LIRC_IRQ_SIR; >+static int threshold = 3; >+#endif >+ >+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 spinlock_t lirc_lock; >+ >+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; >+#ifndef CONFIG_LIRC_SIR_TEKRAM >+static lirc_t tx_buf[WBUF_LEN]; >+#endif >+ >+/* 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); >+#ifdef MODULE >+static int init_chrdev(void); >+static void drop_chrdev(void); >+#endif >+ /* Hardware */ >+static irqreturn_t sir_interrupt(int irq, void * dev_id, struct pt_regs * regs); >+#ifndef CONFIG_LIRC_SIR_TEKRAM >+static void send_space(unsigned long len); >+static void send_pulse(unsigned long len); >+#endif >+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); >+ >+#ifdef CONFIG_LIRC_ON_SA1100 >+void inline on(void) >+{ >+ PPSR|=PPC_TXD2; >+} >+ >+void inline off(void) >+{ >+ PPSR&=~PPC_TXD2; >+} >+#else >+static inline unsigned int sinp(int offset) >+{ >+ return inb(io + offset); >+} >+ >+static inline void soutp(int offset, int value) >+{ >+ outb(value, io + offset); >+} >+#endif >+ >+#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); >+} >+ >+/* SECTION: Communication with user-space */ >+ >+static int lirc_open(struct inode * inode, struct file * file) >+{ >+ spin_lock(&dev_lock); >+ if (module_refcount(THIS_MODULE)) { >+ spin_unlock(&dev_lock); >+ return -EBUSY; >+ } >+ if (!try_module_get(THIS_MODULE)) { >+ spin_unlock(&dev_lock); >+ return -EINVAL; >+ } >+ 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; >+ DECLARE_WAITQUEUE(wait,current); >+ >+ if(n%sizeof(lirc_t)) return(-EINVAL); >+ >+ add_wait_queue(&lirc_read_queue,&wait); >+ current->state=TASK_INTERRUPTIBLE; >+ while(n<count) >+ { >+ if(rx_head!=rx_tail) >+ { >+ 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); >+ } >+ else >+ { >+ if(file->f_flags & O_NONBLOCK) >+ { >+ retval=-EAGAIN; >+ break; >+ } >+ if(signal_pending(current)) >+ { >+ retval=-ERESTARTSYS; >+ break; >+ } >+ schedule(); >+ current->state=TASK_INTERRUPTIBLE; >+ } >+ } >+ remove_wait_queue(&lirc_read_queue,&wait); >+ current->state=TASK_RUNNING; >+ return (n ? n : retval); >+} >+static ssize_t lirc_write(struct file * file, const char * buf, size_t n, loff_t * pos) >+{ >+ unsigned long flags; >+#ifdef CONFIG_LIRC_SIR_TEKRAM >+ return(-EBADF); >+#else >+ 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); >+#ifdef CONFIG_LIRC_ON_SA1100 >+ /* disable receiver */ >+ Ser2UTCR3=0; >+#endif >+ spin_lock_irqsave(&lirc_lock, flags); >+ 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++; >+ } >+ spin_unlock_irqrestore(&lirc_lock, flags); >+#ifdef CONFIG_LIRC_ON_SA1100 >+ off(); >+ udelay(1000); /* wait 1ms for IR diode to recover */ >+ Ser2UTCR3=0; >+ /* clear status register to prevent unwanted interrupts */ >+ Ser2UTSR0 &= (UTSR0_RID | UTSR0_RBB | UTSR0_REB); >+ /* enable receiver */ >+ Ser2UTCR3=UTCR3_RXE|UTCR3_RIE; >+#endif >+ return n; >+#endif >+} >+ >+static int lirc_ioctl(struct inode *node,struct file *filep,unsigned int cmd, >+ unsigned long arg) >+{ >+ int retval = 0; >+ unsigned long value = 0; >+#ifdef CONFIG_LIRC_ON_SA1100 >+ unsigned int ivalue; >+#endif >+ >+#ifdef CONFIG_LIRC_SIR_TEKRAM >+ if (cmd == LIRC_GET_FEATURES) >+ value = LIRC_CAN_REC_MODE2; >+ else if (cmd == LIRC_GET_SEND_MODE) >+ value = 0; >+ else if (cmd == LIRC_GET_REC_MODE) >+ value = LIRC_MODE_MODE2; >+#elif defined(CONFIG_LIRC_ON_SA1100) >+ if (cmd == LIRC_GET_FEATURES) >+ value = LIRC_CAN_SEND_PULSE | >+ LIRC_CAN_SET_SEND_DUTY_CYCLE | >+ 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; >+#else >+ if (cmd == LIRC_GET_FEATURES) >+ value = LIRC_CAN_SEND_PULSE | 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; >+#endif >+ >+ 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; >+#ifdef CONFIG_LIRC_ON_SA1100 >+ case LIRC_SET_SEND_DUTY_CYCLE: >+ retval=get_user(ivalue,(unsigned int *) arg); >+ if(retval) return(retval); >+ if(ivalue<=0 || ivalue>100) return(-EINVAL); >+ /* (ivalue/100)*(1000000/freq) */ >+ duty_cycle=ivalue; >+ pulse_width=(unsigned long) duty_cycle*10000/freq; >+ space_width=(unsigned long) 1000000L/freq-pulse_width; >+ if(pulse_width>=LIRC_ON_SA1100_TRANSMITTER_LATENCY) >+ pulse_width-=LIRC_ON_SA1100_TRANSMITTER_LATENCY; >+ if(space_width>=LIRC_ON_SA1100_TRANSMITTER_LATENCY) >+ space_width-=LIRC_ON_SA1100_TRANSMITTER_LATENCY; >+ break; >+ case LIRC_SET_SEND_CARRIER: >+ retval=get_user(ivalue,(unsigned int *) arg); >+ if(retval) return(retval); >+ if(ivalue>500000 || ivalue<20000) return(-EINVAL); >+ freq=ivalue; >+ pulse_width=(unsigned long) duty_cycle*10000/freq; >+ space_width=(unsigned long) 1000000L/freq-pulse_width; >+ if(pulse_width>=LIRC_ON_SA1100_TRANSMITTER_LATENCY) >+ pulse_width-=LIRC_ON_SA1100_TRANSMITTER_LATENCY; >+ if(space_width>=LIRC_ON_SA1100_TRANSMITTER_LATENCY) >+ space_width-=LIRC_ON_SA1100_TRANSMITTER_LATENCY; >+ break; >+#endif >+ default: >+ retval = -ENOIOCTLCMD; >+ >+ } >+ >+ if (retval) >+ return retval; >+ >+#ifdef CONFIG_LIRC_SIR_TEKRAM >+ if (cmd == LIRC_SET_REC_MODE) { >+ if (value != LIRC_MODE_MODE2) >+ retval = -ENOSYS; >+ } else if (cmd == LIRC_SET_SEND_MODE) { >+ retval = -ENOSYS; >+ } >+#else >+ 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; >+ } >+#endif >+ 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 >+ if (!try_module_get(THIS_MODULE)) { >+ return -EINVAL; >+ } >+#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, >+ .get_key = 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 sir_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) >+ { >+#ifndef CONFIG_LIRC_ON_SA1100 >+ /* clear unread bits in UART and restart */ >+ outb(UART_FCR_CLEAR_RCVR, io + UART_FCR); >+#endif >+ /* 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 sir_interrupt(int irq, void *dev_id, struct pt_regs *regs) >+{ >+ unsigned char data; >+ struct timeval curr_tv; >+ static unsigned long deltv; >+#ifdef CONFIG_LIRC_ON_SA1100 >+ int status; >+ static int n=0; >+ >+ //printk("interrupt\n"); >+ status = Ser2UTSR0; >+ /* >+ * Deal with any receive errors first. The bytes in error may be >+ * the only bytes in the receive FIFO, so we do this first. >+ */ >+ while (status & UTSR0_EIF) >+ { >+ int bstat; >+ >+#ifdef DEBUG >+ printk("EIF\n"); >+ bstat = Ser2UTSR1; >+ >+ if (bstat & UTSR1_FRE) >+ printk("frame error\n"); >+ if (bstat & UTSR1_ROR) >+ printk("receive fifo overrun\n"); >+ if(bstat&UTSR1_PRE) >+ printk("parity error\n"); >+#endif >+ >+ bstat = Ser2UTDR; >+ n++; >+ status = Ser2UTSR0; >+ } >+ >+ if (status & (UTSR0_RFS | UTSR0_RID)) >+ { >+ do_gettimeofday(&curr_tv); >+ deltv = delta(&last_tv, &curr_tv); >+ do >+ { >+#ifdef DEBUG_SIGNAL >+ printk(KERN_DEBUG LIRC_DRIVER_NAME": t %lu , d %d\n", >+ deltintrtv,(int)data); >+#endif >+ data=Ser2UTDR; >+ //printk("data: %d\n",data); >+ n++; >+ } >+ while(status&UTSR0_RID && /* do not empty fifo in >+ order to get UTSR0_RID in >+ any case */ >+ Ser2UTSR1 & UTSR1_RNE); /* data ready */ >+ >+ if(status&UTSR0_RID) >+ { >+ //printk("add\n"); >+ add_read_queue(0,deltv-n*TIME_CONST); /*space*/ >+ add_read_queue(1,n*TIME_CONST); /*pulse*/ >+ n=0; >+ last_tv=curr_tv; >+ } >+ } >+ >+ if (status & UTSR0_TFS) { >+ >+ printk("transmit fifo not full, shouldn't ever happen\n"); >+ } >+ >+ /* >+ * We must clear certain bits. >+ */ >+ status &= (UTSR0_RID | UTSR0_RBB | UTSR0_REB); >+ if (status) >+ Ser2UTSR0 = status; >+#else >+ unsigned long deltintrtv; >+ unsigned long flags; >+ int iir, lsr; >+ >+ while ((iir = inb(io + UART_IIR) & UART_IIR_ID)) { >+ switch (iir&UART_IIR_ID) { /* FIXME toto treba preriedit */ >+ case UART_IIR_MSI: >+ (void) inb(io + UART_MSR); >+ break; >+ case UART_IIR_RLSI: >+ (void) inb(io + UART_LSR); >+ break; >+ case UART_IIR_THRI: >+#if 0 >+ if (lsr & UART_LSR_THRE) /* FIFO is empty */ >+ outb(data, io + UART_TX) >+#endif >+ break; >+ case UART_IIR_RDI: >+ /* avoid interference with timer */ >+ spin_lock_irqsave(&timer_lock, flags); >+ do >+ { >+ del_timer(&timerlist); >+ data = inb(io + UART_RX); >+ 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 X cycles, >+ it was gap */ >+ if (deltintrtv > TIME_CONST * threshold) { >+ 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 + SIR_TIMEOUT; >+ add_timer(&timerlist); >+ } >+ } >+ while ((lsr = inb(io + UART_LSR)) >+ & UART_LSR_DR); /* data ready */ >+ spin_unlock_irqrestore(&timer_lock, flags); >+ break; >+ default: >+ break; >+ } >+ } >+#endif >+ return IRQ_HANDLED; >+} >+ >+#ifdef CONFIG_LIRC_ON_SA1100 >+void send_pulse(unsigned long length) >+{ >+ unsigned long k,delay; >+ int flag; >+ >+ if(length==0) return; >+ /* this won't give us the carrier frequency we really want >+ due to integer arithmetic, but we can accept this inaccuracy */ >+ >+ for(k=flag=0;k<length;k+=delay,flag=!flag) >+ { >+ if(flag) >+ { >+ off(); >+ delay=space_width; >+ } >+ else >+ { >+ on(); >+ delay=pulse_width; >+ } >+ safe_udelay(delay); >+ } >+ off(); >+} >+ >+void send_space(unsigned long length) >+{ >+ if(length==0) return; >+ off(); >+ safe_udelay(length); >+} >+#elif defined(CONFIG_LIRC_SIR_TEKRAM) >+#else >+static void send_space(unsigned long len) >+{ >+ safe_udelay(len); >+} >+ >+static void send_pulse(unsigned long len) >+{ >+ long bytes_out = len / TIME_CONST; >+ long time_left; >+ >+ if (!bytes_out) >+ bytes_out++; >+ time_left = (long)len - (long)bytes_out * (long)TIME_CONST; >+ while (--bytes_out) { >+ outb(PULSE, io + UART_TX); >+ /* FIXME treba seriozne cakanie z drivers/char/serial.c */ >+ while (!(inb(io + UART_LSR) & UART_LSR_THRE)); >+ } >+#if 0 >+ if (time_left > 0) >+ safe_udelay(time_left); >+#endif >+} >+#endif >+ >+#ifdef CONFIG_SA1100_COLLIE >+static inline int sa1100_irda_set_power_collie(int state) >+{ >+ if (state) { >+ /* >+ * 0 - off >+ * 1 - short range, lowest power >+ * 2 - medium range, medium power >+ * 3 - maximum range, high power >+ */ >+ ucb1200_set_io_direction(TC35143_GPIO_IR_ON, >+ TC35143_IODIR_OUTPUT); >+ ucb1200_set_io(TC35143_GPIO_IR_ON, TC35143_IODAT_LOW); >+ udelay(100); >+ } >+ else { >+ /* OFF */ >+ ucb1200_set_io_direction(TC35143_GPIO_IR_ON, >+ TC35143_IODIR_OUTPUT); >+ ucb1200_set_io(TC35143_GPIO_IR_ON, TC35143_IODAT_HIGH); >+ } >+ return 0; >+} >+#endif >+ >+static int init_hardware(void) >+{ >+ int flags; >+ >+ spin_lock_irqsave(&hardware_lock, flags); >+ /* reset UART */ >+#ifdef CONFIG_LIRC_ON_SA1100 >+#ifdef CONFIG_SA1100_BITSY >+ if (machine_is_bitsy()) { >+ printk("Power on IR module\n"); >+ set_bitsy_egpio(EGPIO_BITSY_IR_ON); >+ } >+#endif >+#ifdef CONFIG_SA1100_COLLIE >+ sa1100_irda_set_power_collie(3); /* power on */ >+#endif >+ sr.hscr0=Ser2HSCR0; >+ >+ sr.utcr0=Ser2UTCR0; >+ sr.utcr1=Ser2UTCR1; >+ sr.utcr2=Ser2UTCR2; >+ sr.utcr3=Ser2UTCR3; >+ sr.utcr4=Ser2UTCR4; >+ >+ sr.utdr=Ser2UTDR; >+ sr.utsr0=Ser2UTSR0; >+ sr.utsr1=Ser2UTSR1; >+ >+ /* configure GPIO */ >+ /* output */ >+ PPDR|=PPC_TXD2; >+ PSDR|=PPC_TXD2; >+ /* set output to 0 */ >+ off(); >+ >+ /* >+ * Enable HP-SIR modulation, and ensure that the port is disabled. >+ */ >+ Ser2UTCR3=0; >+ Ser2HSCR0=sr.hscr0 & (~HSCR0_HSSP); >+ >+ /* clear status register to prevent unwanted interrupts */ >+ Ser2UTSR0 &= (UTSR0_RID | UTSR0_RBB | UTSR0_REB); >+ >+ /* 7N1 */ >+ Ser2UTCR0=UTCR0_1StpBit|UTCR0_7BitData; >+ /* 115200 */ >+ Ser2UTCR1=0; >+ Ser2UTCR2=1; >+ /* use HPSIR, 1.6 usec pulses */ >+ Ser2UTCR4=UTCR4_HPSIR|UTCR4_Z1_6us; >+ >+ /* enable receiver, receive fifo interrupt */ >+ Ser2UTCR3=UTCR3_RXE|UTCR3_RIE; >+ >+ /* clear status register to prevent unwanted interrupts */ >+ Ser2UTSR0 &= (UTSR0_RID | UTSR0_RBB | UTSR0_REB); >+ >+#elif defined(CONFIG_LIRC_SIR_TEKRAM) >+ /* disable FIFO */ >+ soutp(UART_FCR, >+ UART_FCR_CLEAR_RCVR| >+ UART_FCR_CLEAR_XMIT| >+ UART_FCR_TRIGGER_1); >+ >+ /* 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))); >+ >+ /* Set DLAB 1. */ >+ soutp(UART_LCR, sinp(UART_LCR) | UART_LCR_DLAB); >+ >+ /* Set divisor to 12 => 9600 Baud */ >+ soutp(UART_DLM,0); >+ soutp(UART_DLL,12); >+ >+ /* Set DLAB 0. */ >+ soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB)); >+ >+ /* power supply */ >+ soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2); >+ safe_udelay(50*1000); >+ >+ /* -DTR low -> reset PIC */ >+ soutp(UART_MCR, UART_MCR_RTS|UART_MCR_OUT2); >+ udelay(1*1000); >+ >+ soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2); >+ udelay(100); >+ >+ >+ /* -RTS low -> send control byte */ >+ soutp(UART_MCR, UART_MCR_DTR|UART_MCR_OUT2); >+ udelay(7); >+ soutp(UART_TX, TEKRAM_115200|TEKRAM_PW); >+ >+ /* one byte takes ~1042 usec to transmit at 9600,8N1 */ >+ udelay(1500); >+ >+ /* back to normal operation */ >+ soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2); >+ udelay(50); >+ >+ udelay(1500); >+ >+ /* read previous control byte */ >+ printk(KERN_INFO LIRC_DRIVER_NAME >+ ": 0x%02x\n",sinp(UART_RX)); >+ >+ /* 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, 8 Bit */ >+ soutp(UART_LCR, UART_LCR_WLEN8); >+ /* enable interrupts */ >+ soutp(UART_IER, sinp(UART_IER)|UART_IER_RDI); >+#else >+ outb(0, io + UART_MCR); >+ outb(0, io + UART_IER); >+ /* init UART */ >+ /* set DLAB, speed = 115200 */ >+ outb(UART_LCR_DLAB | UART_LCR_WLEN7, io + UART_LCR); >+ outb(1, io + UART_DLL); outb(0, io + UART_DLM); >+ /* 7N1+start = 9 bits at 115200 ~ 3 bits at 44000 */ >+ outb(UART_LCR_WLEN7, io + UART_LCR); >+ /* FIFO operation */ >+ outb(UART_FCR_ENABLE_FIFO, io + UART_FCR); >+ /* interrupts */ >+ // outb(UART_IER_RLSI|UART_IER_RDI|UART_IER_THRI, io + UART_IER); >+ outb(UART_IER_RDI, io + UART_IER); >+ /* turn on UART */ >+ outb(UART_MCR_DTR|UART_MCR_RTS|UART_MCR_OUT2, io + UART_MCR); >+#ifdef CONFIG_LIRC_SIR_ACTISYS_ACT200L >+ init_act200(); >+#endif >+#endif >+ spin_unlock_irqrestore(&hardware_lock, flags); >+ return 0; >+} >+ >+static void drop_hardware(void) >+{ >+ int flags; >+ >+ spin_lock_irqsave(&hardware_lock, flags); >+ >+#ifdef CONFIG_LIRC_ON_SA1100 >+ Ser2UTCR3=0; >+ >+ Ser2UTCR0=sr.utcr0; >+ Ser2UTCR1=sr.utcr1; >+ Ser2UTCR2=sr.utcr2; >+ Ser2UTCR4=sr.utcr4; >+ Ser2UTCR3=sr.utcr3; >+ >+ Ser2HSCR0=sr.hscr0; >+#ifdef CONFIG_SA1100_BITSY >+ if (machine_is_bitsy()) { >+ clr_bitsy_egpio(EGPIO_BITSY_IR_ON); >+ } >+#endif >+#ifdef CONFIG_SA1100_COLLIE >+ sa1100_irda_set_power_collie(0); /* power off */ >+#endif >+#else >+ /* turn off interrupts */ >+ outb(0, io + UART_IER); >+#endif >+ spin_unlock_irqrestore(&hardware_lock, flags); >+} >+ >+/* SECTION: Initialisation */ >+ >+static int init_port(void) >+{ >+ int retval; >+ >+#ifndef CONFIG_LIRC_ON_SA1100 >+ /* get I/O port access and IRQ line */ >+ retval = request_region(io, 8, LIRC_DRIVER_NAME); >+ if (!retval) { >+ printk(KERN_ERR LIRC_DRIVER_NAME >+ ": i/o port 0x%.4x already in use.\n", >+ io); >+ return retval; >+ } >+ printk(KERN_INFO LIRC_DRIVER_NAME >+ ": I/O port 0x%.4x, IRQ %d.\n", >+ io, irq); >+#endif >+ retval = request_irq(irq, sir_interrupt, SA_INTERRUPT, >+ LIRC_DRIVER_NAME, NULL); >+ if (retval < 0) { >+ printk(KERN_ERR LIRC_DRIVER_NAME >+ ": IRQ %d already in use.\n", >+ irq); >+ return retval; >+ } >+ >+ init_timer(&timerlist); >+ timerlist.function = sir_timeout; >+ timerlist.data = 0xabadcafe; >+ >+ return 0; >+} >+ >+static void drop_port(void) >+{ >+ disable_irq(irq); >+ free_irq(irq, NULL); >+ del_timer_sync(&timerlist); >+#ifndef CONFIG_LIRC_ON_SA1100 >+ release_region(io, 8); >+#endif >+} >+ >+#ifdef CONFIG_LIRC_SIR_ACTISYS_ACT200L >+/******************************************************/ >+/* Crystal/Cirrus CS8130 IR transceiver, used in Actisys Act200L dongle */ >+/* some code borrowed from Linux IRDA driver */ >+ >+/* Regsiter 0: Control register #1 */ >+#define ACT200L_REG0 0x00 >+#define ACT200L_TXEN 0x01 /* Enable transmitter */ >+#define ACT200L_RXEN 0x02 /* Enable receiver */ >+#define ACT200L_ECHO 0x08 /* Echo control chars */ >+ >+/* Register 1: Control register #2 */ >+#define ACT200L_REG1 0x10 >+#define ACT200L_LODB 0x01 /* Load new baud rate count value */ >+#define ACT200L_WIDE 0x04 /* Expand the maximum allowable pulse */ >+ >+/* Register 3: Transmit mode register #2 */ >+#define ACT200L_REG3 0x30 >+#define ACT200L_B0 0x01 /* DataBits, 0=6, 1=7, 2=8, 3=9(8P) */ >+#define ACT200L_B1 0x02 /* DataBits, 0=6, 1=7, 2=8, 3=9(8P) */ >+#define ACT200L_CHSY 0x04 /* StartBit Synced 0=bittime, 1=startbit */ >+ >+/* Register 4: Output Power register */ >+#define ACT200L_REG4 0x40 >+#define ACT200L_OP0 0x01 /* Enable LED1C output */ >+#define ACT200L_OP1 0x02 /* Enable LED2C output */ >+#define ACT200L_BLKR 0x04 >+ >+/* Register 5: Receive Mode register */ >+#define ACT200L_REG5 0x50 >+#define ACT200L_RWIDL 0x01 /* fixed 1.6us pulse mode */ >+ /*.. other various IRDA bit modes, and TV remote modes..*/ >+ >+/* Register 6: Receive Sensitivity register #1 */ >+#define ACT200L_REG6 0x60 >+#define ACT200L_RS0 0x01 /* receive threshold bit 0 */ >+#define ACT200L_RS1 0x02 /* receive threshold bit 1 */ >+ >+/* Register 7: Receive Sensitivity register #2 */ >+#define ACT200L_REG7 0x70 >+#define ACT200L_ENPOS 0x04 /* Ignore the falling edge */ >+ >+/* Register 8,9: Baud Rate Dvider register #1,#2 */ >+#define ACT200L_REG8 0x80 >+#define ACT200L_REG9 0x90 >+ >+#define ACT200L_2400 0x5f >+#define ACT200L_9600 0x17 >+#define ACT200L_19200 0x0b >+#define ACT200L_38400 0x05 >+#define ACT200L_57600 0x03 >+#define ACT200L_115200 0x01 >+ >+/* Register 13: Control register #3 */ >+#define ACT200L_REG13 0xd0 >+#define ACT200L_SHDW 0x01 /* Enable access to shadow registers */ >+ >+/* Register 15: Status register */ >+#define ACT200L_REG15 0xf0 >+ >+/* Register 21: Control register #4 */ >+#define ACT200L_REG21 0x50 >+#define ACT200L_EXCK 0x02 /* Disable clock output driver */ >+#define ACT200L_OSCL 0x04 /* oscillator in low power, medium accuracy mode */ >+ >+static void init_act200(void) >+{ >+ int i; >+ __u8 control[] = { >+ ACT200L_REG15, >+ ACT200L_REG13 | ACT200L_SHDW, >+ ACT200L_REG21 | ACT200L_EXCK | ACT200L_OSCL, >+ ACT200L_REG13, >+ ACT200L_REG7 | ACT200L_ENPOS, >+ ACT200L_REG6 | ACT200L_RS0 | ACT200L_RS1, >+ ACT200L_REG5 | ACT200L_RWIDL, >+ ACT200L_REG4 | ACT200L_OP0 | ACT200L_OP1 | ACT200L_BLKR, >+ ACT200L_REG3 | ACT200L_B0, >+ ACT200L_REG0 | ACT200L_TXEN | ACT200L_RXEN, >+ ACT200L_REG8 | (ACT200L_115200 & 0x0f), >+ ACT200L_REG9 | ((ACT200L_115200 >> 4) & 0x0f), >+ ACT200L_REG1 | ACT200L_LODB | ACT200L_WIDE >+ }; >+ >+ /* Set DLAB 1. */ >+ soutp(UART_LCR, UART_LCR_DLAB | UART_LCR_WLEN8); >+ >+ /* Set divisor to 12 => 9600 Baud */ >+ soutp(UART_DLM,0); >+ soutp(UART_DLL,12); >+ >+ /* Set DLAB 0. */ >+ soutp(UART_LCR, UART_LCR_WLEN8); >+ /* Set divisor to 12 => 9600 Baud */ >+ >+ /* power supply */ >+ soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2); >+ for (i=0; i<50; i++) { >+ safe_udelay(1000); >+ } >+ >+ /* Reset the dongle : set RTS low for 25 ms */ >+ soutp(UART_MCR, UART_MCR_DTR|UART_MCR_OUT2); >+ for (i=0; i<25; i++) { >+ udelay(1000); >+ } >+ >+ soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2); >+ udelay(100); >+ >+ /* Clear DTR and set RTS to enter command mode */ >+ soutp(UART_MCR, UART_MCR_RTS|UART_MCR_OUT2); >+ udelay(7); >+ >+/* send out the control register settings for 115K 7N1 SIR operation */ >+ for (i=0; i<sizeof(control); i++) { >+ soutp(UART_TX, control[i]); >+ /* one byte takes ~1042 usec to transmit at 9600,8N1 */ >+ udelay(1500); >+ } >+ >+ /* back to normal operation */ >+ soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2); >+ udelay(50); >+ >+ udelay(1500); >+ soutp(UART_LCR, sinp(UART_LCR) | UART_LCR_DLAB); >+ >+ /* Set DLAB 1. */ >+ soutp(UART_LCR, UART_LCR_DLAB | UART_LCR_WLEN7); >+ >+ /* Set divisor to 1 => 115200 Baud */ >+ soutp(UART_DLM,0); >+ soutp(UART_DLL,1); >+ >+ /* Set DLAB 0. */ >+ soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB)); >+ >+ /* Set DLAB 0, 7 Bit */ >+ soutp(UART_LCR, UART_LCR_WLEN7); >+ >+ /* enable interrupts */ >+ soutp(UART_IER, sinp(UART_IER)|UART_IER_RDI); >+} >+#endif >+ >+int init_lirc_sir(void) >+{ >+ int retval; >+ >+ init_waitqueue_head(&lirc_read_queue); >+ retval = init_port(); >+ if (retval < 0) >+ return retval; >+ init_hardware(); >+ enable_irq(irq); >+ printk(KERN_INFO LIRC_DRIVER_NAME >+ ": Installed.\n"); >+ return 0; >+} >+ >+#ifdef CONFIG_LIRC_SIR_TEKRAM >+MODULE_AUTHOR("Christoph Bartelmus"); >+MODULE_DESCRIPTION("Infrared receiver driver for Tekram Irmate 210"); >+#elif defined(CONFIG_LIRC_ON_SA1100) >+MODULE_AUTHOR("Christoph Bartelmus"); >+MODULE_DESCRIPTION("LIRC driver for StrongARM SA1100 embedded microprocessor"); >+#elif defined(CONFIG_LIRC_SIR_ACTISYS_ACT200L) >+MODULE_AUTHOR("Karl Bongers"); >+MODULE_DESCRIPTION("LIRC driver for Actisys Act200L"); >+#else >+MODULE_AUTHOR("Milan Pikula"); >+MODULE_DESCRIPTION("Infrared receiver driver for SIR type serial ports"); >+#endif >+ >+#ifdef CONFIG_LIRC_ON_SA1100 >+MODULE_PARM(irq, "i"); >+MODULE_PARM_DESC(irq, "Interrupt (16)"); >+#else >+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(threshold, "i"); >+MODULE_PARM_DESC(threshold, "space detection threshold (3)"); >+#endif >+ >+MODULE_LICENSE("GPL"); >+ >+static int __init lirc_sir_init(void) >+{ >+ int retval; >+ >+ retval=init_chrdev(); >+ if(retval < 0) >+ return retval; >+ retval = init_lirc_sir(); >+ if (retval) { >+ drop_chrdev(); >+ return retval; >+ } >+ return 0; >+} >+ >+static void __exit lirc_sir_exit(void) >+{ >+ if(module_refcount(THIS_MODULE)) return; >+ drop_hardware(); >+ drop_chrdev(); >+ drop_port(); >+ printk(KERN_INFO LIRC_DRIVER_NAME ": Uninstalled.\n"); >+} >+ >+module_init(lirc_sir_init); >+module_exit(lirc_sir_exit); >diff -NPaur linux-2.6.2/include/linux/lirc.h linux-2.6.2-lirc/include/linux/lirc.h >--- linux-2.6.2/include/linux/lirc.h 1970-01-01 01:00:00.000000000 +0100 >+++ linux-2.6.2-lirc/include/linux/lirc.h 2004-02-09 20:03:15.136320160 +0100 >@@ -0,0 +1,103 @@ >+/* $Id: 405_lirc_infrared-2.6.2-02092004.patch,v 1.1 2004/02/24 22:27:37 brad_mssw 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
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 47404
: 29379