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 +# Check for new patch at http://flameeyes.web.ctonet.it +# +# Thanks to Koos Vriezen for the Homebrew support. +# Thanks to Jeff Clark 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 for SMP patch. +# Thanks to Vince for the temporary lirc_atiusb driver. +# Thanks to Paul Miller 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 + * + * This driver was derived from: + * Vladimir Dergachev 's 2002 + * "USB ATI Remote support" (input device) + * Adrian Dewhurst 's 2002 + * "USB StreamZap remote driver" (LIRC) + * Artur Lipowski '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 +#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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if KERNEL26 +#include +#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 " +#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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + * + * 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define __KERNEL_SYSCALLS__ +#include +#include +#include + +#include + +#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; minorsample_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 + * 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 +#include +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 + * patch for the AverMedia by Santiago Garcia Mantinan + * and Christoph Bartelmus + * patch for the BestBuy by Miguel Angel Alvarez + * patch for the Winfast TV2000 by Juan Toledo + * + * patch for the I-O Data GV-BCTV5/PCI by Jens C. Rasmussen + * + * + * 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 + +#include +#include +#include +#include +#include +#include + +#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>= 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 + * modified for PixelView (BT878P+W/FM) by + * Michal Kochanowicz + * Christoph Bartelmus + * modified for KNC ONE TV Station/Anubis Typhoon TView Tuner by + * Ulrich Mueller + * modified for Asus TV-Box and Creative/VisionTek BreakOut-Box by + * Stefan Jahn + * modified for Linux 2.6 by + * Jeffrey Clark + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#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; ibits | 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 + * + * 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 : + * reimplemented read function + */ + + +#include +#include + +#include + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#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(nf_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= 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_USB_DEBUG + static int debug = 1; +#else + static int debug = 1; +#endif + +#include +#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 + * + * 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 + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#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=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(countf_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;iops->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 + +#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 + * Copyright (C) 1998 Trent Piepho + * Copyright (C) 1998 Ben Pfaff + * Copyright (C) 1999 Christoph Bartelmus + * + * 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 July 2001 + + Flameeyes Patches Contribution + - Ronald Wahl sent a patch to + eliminate a deadlock on SMP systems. + - Florian Steinel sent a patch to fix irq + disabling by kernel. + - Jindrich Makovicka sent a patch fixing + one-shot use of lirc_serial. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#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 + 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)>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 + . */ + + /* 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 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;i100) 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 + * + * 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 : + * added timeout and relaxed pulse detection, removed gap bug + * + * 2000/12/15 Christoph Bartelmus : + * 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 : + * added support for StrongARM SA1100 embedded microprocessor + * parts cut'n'pasted from sa1100_ir.c (C) 2000 Russell King + */ + + +#include +#include +#include + +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_LIRC_ON_SA1100 +#include +#ifdef CONFIG_SA1100_COLLIE +#include +#include +#endif +#endif + +#include + +#include +#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(nf_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 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 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 +#include +#else +#include +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