diff -uNr linux-2.6.8-rc4.orig/drivers/char/Kconfig linux-2.6.8-rc4/drivers/char/Kconfig --- linux-2.6.8-rc4.orig/drivers/char/Kconfig 2004-08-10 09:33:29.000000000 +0200 +++ linux-2.6.8-rc4/drivers/char/Kconfig 2004-08-10 10:22:53.524975088 +0200 @@ -427,6 +427,7 @@ source "drivers/serial/Kconfig" +source "drivers/char/lirc/Kconfig" config UNIX98_PTYS bool "Unix98 PTY support" if EMBEDDED diff -uNr linux-2.6.8-rc4.orig/drivers/char/lirc/Kconfig linux-2.6.8-rc4/drivers/char/lirc/Kconfig --- linux-2.6.8-rc4.orig/drivers/char/lirc/Kconfig 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.8-rc4/drivers/char/lirc/Kconfig 2004-08-10 10:21:59.728153448 +0200 @@ -0,0 +1,204 @@ +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_IRCTL_DEV_MAJOR + int "Major device number" + default "61" + 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_SASEM + tristate "USB Driver for Sasem Remote Controller" + 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 -uNr linux-2.6.8-rc4.orig/drivers/char/lirc/lirc_atiusb.c linux-2.6.8-rc4/drivers/char/lirc/lirc_atiusb.c --- linux-2.6.8-rc4.orig/drivers/char/lirc/lirc_atiusb.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.8-rc4/drivers/char/lirc/lirc_atiusb.c 2004-06-13 16:33:11.000000000 +0200 @@ -0,0 +1,681 @@ +/* lirc_atiusb - USB remote support for LIRC + * (currently only supports X10 USB remotes) + * + * 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$ + */ + +/* + * 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 + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "kcompat.h" +#include "lirc_dev.h" + +#define DRIVER_VERSION "0.4" +#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) + +/* module parameters */ +#ifdef CONFIG_USB_DEBUG + static int debug = 1; +#else + static int debug = 0; +#endif +#define dprintk if (debug) printk +static int mask = 0xFFFF; // channel acceptance bit mask +static int unique = 0; // enable channel-specific codes +static int repeat = 10; // repeat time in 1/100 sec +static unsigned long repeat_jiffies; // repeat timeout + +/* 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; +#ifdef KERNEL_2_5 + dma_addr_t dma_in; + dma_addr_t dma_out; +#endif + + /* handle repeats */ + unsigned char old[CODE_LENGTH]; + unsigned long old_jiffies; + + /* 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); + +#ifdef KERNEL_2_5 + 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); + + MOD_INC_USE_COUNT; + + if (!ir->connected) { + if (!ir->usbdev) + return -ENOENT; + ir->urb_in->dev = ir->usbdev; +#ifdef KERNEL_2_5 + 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); + MOD_DEC_USE_COUNT; + 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; + } + MOD_DEC_USE_COUNT; +} + +static void usb_remote_printdata(struct irctl *ir, char *buf, int len) +{ + char codes[USB_BUFLEN*3 + 1]; + int i; + + if (len <= 0) + return; + + for (i = 0; i < len && i < USB_BUFLEN; i++) { + snprintf(codes+i*3, 4, "%02x ", buf[i] & 0xFF); + } + printk(DRIVER_NAME "[%d]: data received %s (length=%d)\n", + ir->devnum, codes, len); +} + +#ifdef KERNEL_2_5 +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; + int i, len; + int chan; + + if (!urb) + return; + + if (!(ir = urb->context)) { + usb_unlink_urb(urb); + return; + } + + len = urb->actual_length; + if (debug) + usb_remote_printdata(ir,urb->transfer_buffer,len); + + switch (urb->status) { + + /* success */ + case SUCCESS: + /* some remotes emit both 4 and 5 byte length codes. */ + if (len < CODE_MIN_LENGTH || len > CODE_LENGTH) + break; + + // *** channel not tested with 4/5-byte Dutch remotes *** + chan = ((ir->buf_in[len-1]>>4) & 0x0F); + if ( !((1<devnum, chan+1); + break; + } + + dprintk(DRIVER_NAME "[%d]: accept channel %d\n", + ir->devnum, chan+1); + + /* strip channel code */ + if (!unique) + ir->buf_in[len-1] &= 0x0F; + + /* check for repeats */ + if (memcmp(ir->old, ir->buf_in, len) == 0) { + if (ir->old_jiffies + repeat_jiffies > jiffies) { + break; + } + } else { + memcpy(ir->old, ir->buf_in, len); + for (i = len; i < CODE_LENGTH; i++) ir->old[i] = 0; + } + ir->old_jiffies = jiffies; + + lirc_buffer_write_1(ir->p->rbuf, ir->old); + wake_up(&ir->p->rbuf->wait_poll); + break; + + /* unlink */ + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + usb_unlink_urb(urb); + return; + } + + /* resubmit urb */ +#ifdef KERNEL_2_5 + usb_submit_urb(urb, SLAB_ATOMIC); +#else + usb_submit_urb(urb); +#endif +} + +#ifdef KERNEL_2_5 +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); +} + +#ifdef KERNEL_2_5 +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"); + +#ifdef KERNEL_2_5 + dev = interface_to_usbdev(intf); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,4) + idesc = intf->cur_altsetting; +#else + idesc = &intf->altsetting[intf->act_altsetting]; +#endif + 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; +#ifdef KERNEL_2_5 + } 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); +#ifdef KERNEL_2_5 + 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); +#ifdef KERNEL_2_5 + 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); + +#ifdef KERNEL_2_5 + usb_set_intfdata(intf, ir); + return SUCCESS; +#else + return ir; +#endif +} + + +#ifdef KERNEL_2_5 +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); +#ifdef KERNEL_2_5 + 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"); + + repeat_jiffies = repeat*HZ/100; + + 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"); +MODULE_PARM(mask, "i"); +MODULE_PARM_DESC(mask, "set channel acceptance bit mask"); +MODULE_PARM(unique, "i"); +MODULE_PARM_DESC(unique, "enable channel-specific codes"); +MODULE_PARM(repeat, "i"); +MODULE_PARM_DESC(repeat, "repeat timeout (1/100 sec)"); + +#ifndef KERNEL_2_5 +EXPORT_NO_SYMBOLS; +#endif + diff -uNr linux-2.6.8-rc4.orig/drivers/char/lirc/lirc_bt829.c linux-2.6.8-rc4/drivers/char/lirc/lirc_bt829.c --- linux-2.6.8-rc4.orig/drivers/char/lirc/lirc_bt829.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.8-rc4/drivers/char/lirc/lirc_bt829.c 2004-04-27 20:45:34.000000000 +0200 @@ -0,0 +1,389 @@ +/* + * 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 +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 0) +#error "This driver needs kernel version 2.4.0 or higher" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kcompat.h" +#include "lirc_dev.h" + + + +MODULE_AUTHOR("Froenchenko Leonid"); +MODULE_DESCRIPTION("IR remote driver for bt829 based TV cards"); + + +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; +#ifndef KERNEL_2_5 + /* unnecessary with recent kernels */ + if ( !pci_present() ) { + printk(KERN_ERR "ATIR: no pci in this kernel\n"); + } +#endif + 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", + pci_pretty_name(my_dev)); + pci_addr_phys = 0; + if ( my_dev->resource[0].flags & IORESOURCE_MEM ) { + pci_addr_phys = my_dev->resource[0].start; + printk(KERN_INFO "ATIR memory at 0x%08X \n",(unsigned int)pci_addr_phys); + } + if ( pci_addr_phys == 0 ) { + printk(KERN_ERR "ATIR no memory resource ?\n"); + return 0; + } + } else { + printk(KERN_ERR "ATIR: pci_prob failed\n"); + return 0; + } + return 1; +} + + +int atir_add_to_buf (void* data, struct lirc_buffer* buf) +{ + unsigned char key; + int status; + status = poll_main(); + key = (status >> 8) & 0xFF; + if( status & 0xFF ) + { + // printk(KERN_INFO "ATIR reading key %02X\n",*key); + lirc_buffer_write_1( buf, &key ); + return 0; + } + return -ENODATA; +} + +int atir_set_use_inc(void* data) +{ + MOD_INC_USE_COUNT; + printk(KERN_DEBUG "ATIR driver is opened\n"); + return 0; +} + +void atir_set_use_dec(void* data) +{ + MOD_DEC_USE_COUNT; + printk(KERN_DEBUG "ATIR driver is closed\n"); +} + +int init_module(void) +{ + if ( !do_pci_probe() ) { + return 1; + } + + if ( !atir_init_start() ) { + return 1; + } + + strcpy(atir_plugin.name,"ATIR"); + atir_plugin.minor = -1; + atir_plugin.code_length = 8; + atir_plugin.sample_rate = 10; + atir_plugin.data = 0; + atir_plugin.add_to_buf = atir_add_to_buf; + atir_plugin.set_use_inc = atir_set_use_inc; + atir_plugin.set_use_dec = atir_set_use_dec; + + atir_minor = lirc_register_plugin(&atir_plugin); + printk(KERN_DEBUG "ATIR driver is registered on minor %d\n",atir_minor); + + return 0; +} + + +void cleanup_module(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); +} + + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * --------------------------------------------------------------------------- + * Local variables: + * c-basic-offset: 8 + * End: + */ diff -uNr linux-2.6.8-rc4.orig/drivers/char/lirc/lirc_dev.c linux-2.6.8-rc4/drivers/char/lirc/lirc_dev.c --- linux-2.6.8-rc4.orig/drivers/char/lirc/lirc_dev.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.8-rc4/drivers/char/lirc/lirc_dev.c 2004-04-08 23:17:46.000000000 +0200 @@ -0,0 +1,795 @@ +/* + * 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$ + * + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) +#define LIRC_HAVE_DEVFS +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) +#warning "**********************************************************" +#warning "************ disabling devfs for 2.6 kernels *************" +#warning "**********************************************************" +#undef LIRC_HAVE_DEVFS +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 2, 18) +#error "**********************************************************" +#error " Sorry, this driver needs kernel version 2.2.18 or higher " +#error "**********************************************************" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef LIRC_HAVE_DEVFS +#include +#endif +#include +#include +#include +#include +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) +#include +#endif +#define __KERNEL_SYSCALLS__ +#include + +#include "kcompat.h" +#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 tpid; + struct semaphore *t_notify; + struct semaphore *t_notify2; + int shutdown; + long jiffies_to_wait; + +#ifdef LIRC_HAVE_DEVFS + devfs_handle_t devfs_handle; +#endif +}; + +DECLARE_MUTEX(plugin_lock); + +static struct irctl irctls[MAX_IRCTL_DEVICES]; +static struct file_operations fops; + + +/* helper function + * initializes the irctl structure + */ +static inline void init_irctl(struct irctl *ir) +{ + memset(&ir->p, 0, sizeof(struct lirc_plugin)); + ir->p.minor = NOPLUG; + + ir->tpid = -1; + ir->t_notify = NULL; + ir->t_notify2 = NULL; + ir->shutdown = 0; + ir->jiffies_to_wait = 0; + + ir->open = 0; +} + + +/* helper function + * reads key codes from plugin and puts them into buffer + * buffer free space is checked and locking performed + * returns 0 on success + */ + +inline static int add_to_buf(struct irctl *ir) +{ + if (lirc_buffer_full(ir->buf)) { + dprintk(LOGHEAD "buffer overflow\n", + ir->p.name, ir->p.minor); + return -EOVERFLOW; + } + + if(ir->p.add_to_buf) { + int res = -ENODATA; + int got_data = 0; + + /* service the device as long as it is returning + * data and we have space + */ + while( !lirc_buffer_full(ir->buf) ) + { + res = ir->p.add_to_buf( ir->p.data, ir->buf ); + if( res == SUCCESS ) + got_data++; + else + break; + } + + if( res == -ENODEV ) + { + ir->shutdown = 1; + } + return (got_data ? SUCCESS : res); + } + + return SUCCESS; +} + +/* main function of the polling thread + */ +static int lirc_thread(void *irctl) +{ + struct irctl *ir = irctl; + + /* This thread doesn't need any user-level access, + * so get rid of all our resources + */ + 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); + + if (ir->t_notify2 != NULL) { + down(ir->t_notify2); + } + + ir->tpid = -1; + if (ir->t_notify != NULL) { + up(ir->t_notify); + } + + dprintk(LOGHEAD "poll thread ended\n", ir->p.name, ir->p.minor); + + return 0; +} + +/* + * + */ +int lirc_register_plugin(struct lirc_plugin *p) +{ + struct irctl *ir; + int minor; + int bytes_in_key; +#ifdef LIRC_HAVE_DEVFS + char name[16]; +#endif + DECLARE_MUTEX_LOCKED(tn); + + if (!p) { + printk("lirc_dev: lirc_register_plugin:" + "plugin pointer must be not NULL!\n"); + return -EBADRQC; + } + + if (MAX_IRCTL_DEVICES <= p->minor) { + printk("lirc_dev: lirc_register_plugin:" + "\" minor\" must be between 0 and %d (%d)!\n", + MAX_IRCTL_DEVICES-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 || HZ < p->sample_rate) { + printk("lirc_dev: lirc_register_plugin:" + "sample_rate must be between 2 and %d!\n", HZ); + return -EBADRQC; + } + if (!p->add_to_buf) { + printk("lirc_dev: lirc_register_plugin:" + "add_to_buf cannot be NULL when " + "sample_rate is set\n"); + return -EBADRQC; + } + } else if (!(p->fops && p->fops->read) + && !p->get_queue && !p->rbuf) { + printk("lirc_dev: lirc_register_plugin:" + "fops->read, get_queue and rbuf " + "cannot all be NULL!\n"); + return -EBADRQC; + } else if (!p->get_queue && !p->rbuf) { + if (!(p->fops && p->fops->read && p->fops->poll) + || (!p->fops->ioctl && !p->ioctl)) { + printk("lirc_dev: lirc_register_plugin:" + "neither read, poll nor ioctl can be NULL!\n"); + return -EBADRQC; + } + } + + down_interruptible(&plugin_lock); + + minor = p->minor; + + if (0 > minor) { + /* find first free slot for plugin */ + for (minor=0; 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 LIRC_HAVE_DEVFS + sprintf (name, DEV_LIRC "/%d", ir->p.minor); + ir->devfs_handle = devfs_register(NULL, name, DEVFS_FL_DEFAULT, + CONFIG_LIRC_IRCTL_DEV_MAJOR, ir->p.minor, + S_IFCHR | S_IRUSR | S_IWUSR, + &fops, NULL); +#endif + + if(p->sample_rate || p->get_queue) { + /* try to fire up polling thread */ + ir->t_notify = &tn; + ir->tpid = kernel_thread(lirc_thread, (void*)ir, 0); + if (ir->tpid < 0) { + up(&plugin_lock); + printk("lirc_dev: lirc_register_plugin:" + "cannot run poll thread for minor = %d\n", + p->minor); + return -ECHILD; + } + down(&tn); + ir->t_notify = NULL; + } + up(&plugin_lock); + + MOD_INC_USE_COUNT; + + 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 >= MAX_IRCTL_DEVICES) { + printk("lirc_dev: lirc_unregister_plugin:" + "\" minor\" must be between 0 and %d!\n", + MAX_IRCTL_DEVICES-1); + return -EBADRQC; + } + + ir = &irctls[minor]; + + down_interruptible(&plugin_lock); + + if (ir->p.minor != minor) { + printk("lirc_dev: lirc_unregister_plugin:" + "minor (%d) device not registered!", minor); + up(&plugin_lock); + return -ENOENT; + } + + if (ir->open) { + printk("lirc_dev: lirc_unregister_plugin:" + "plugin %s[%d] in use!", ir->p.name, ir->p.minor); + up(&plugin_lock); + return -EBUSY; + } + + /* end up polling thread */ + if (ir->tpid >= 0) { + ir->t_notify = &tn; + ir->t_notify2 = &tn2; + ir->shutdown = 1; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) + { + struct task_struct *p; + + p = find_task_by_pid(ir->tpid); + wake_up_process(p); + } +#else + /* 2.2.x does not export wake_up_process() */ + wake_up_interruptible(ir->p.get_queue(ir->p.data)); +#endif + 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 LIRC_HAVE_DEVFS + devfs_unregister(ir->devfs_handle); +#endif + if (ir->buf != ir->p.rbuf){ + lirc_buffer_free(ir->buf); + kfree(ir->buf); + } + ir->buf = NULL; + init_irctl(ir); + up(&plugin_lock); + + MOD_DEC_USE_COUNT; + + return SUCCESS; +} + +/* + * + */ +static int irctl_open(struct inode *inode, struct file *file) +{ + struct irctl *ir; + int retval; + + if (MINOR(inode->i_rdev) >= MAX_IRCTL_DEVICES) { + 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 "read called\n", ir->p.name, ir->p.minor); + + /* if the plugin has a specific read function use it instead */ + if(ir->p.fops && ir->p.fops->write) + return ir->p.fops->write(file, buffer, length, ppos); + + return -EINVAL; +} + + +static struct file_operations fops = { + read: irctl_read, + write: irctl_write, + poll: irctl_poll, + ioctl: irctl_ioctl, + open: irctl_open, + release: irctl_close +}; + + + +EXPORT_SYMBOL(lirc_register_plugin); +EXPORT_SYMBOL(lirc_unregister_plugin); + +/* + * + */ +int lirc_dev_init(void) +{ + int i; + + for (i=0; i < MAX_IRCTL_DEVICES; ++i) { + init_irctl(&irctls[i]); + } + +#ifndef LIRC_HAVE_DEVFS + i = register_chrdev(CONFIG_LIRC_IRCTL_DEV_MAJOR, +#else + i = devfs_register_chrdev(CONFIG_LIRC_IRCTL_DEV_MAJOR, +#endif + 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", + CONFIG_LIRC_IRCTL_DEV_MAJOR); + + return SUCCESS; +} + +/* ---------------------------------------------------------------------- */ + +/* For now dont try to use it as a static version ! */ + +#ifdef MODULE + +MODULE_DESCRIPTION("LIRC base driver module"); +MODULE_AUTHOR("Artur Lipowski"); +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +/* + * + */ +int init_module(void) +{ + return lirc_dev_init(); +} + +/* + * + */ +void cleanup_module(void) +{ + int ret; + +#ifndef LIRC_HAVE_DEVFS + ret = unregister_chrdev(CONFIG_LIRC_IRCTL_DEV_MAJOR, IRCTL_DEV_NAME); +#else + ret = devfs_unregister_chrdev(CONFIG_LIRC_IRCTL_DEV_MAJOR, IRCTL_DEV_NAME); +#endif + + if (0 > ret){ + printk("lirc_dev: error in module_unregister_chrdev: %d\n", + ret); + } else { + dprintk("lirc_dev: module successfully unloaded\n"); + } +} +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * --------------------------------------------------------------------------- + * Local variables: + * c-basic-offset: 8 + * End: + */ diff -uNr linux-2.6.8-rc4.orig/drivers/char/lirc/lirc_dev.h linux-2.6.8-rc4/drivers/char/lirc/lirc_dev.h --- linux-2.6.8-rc4.orig/drivers/char/lirc/lirc_dev.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.8-rc4/drivers/char/lirc/lirc_dev.h 2004-04-09 17:33:40.000000000 +0200 @@ -0,0 +1,238 @@ +/* + * LIRC base driver + * + * (L) by Artur Lipowski + * This code is licensed under GNU GPL + * + * $Id$ + * + */ + +#ifndef _LINUX_LIRC_DEV_H +#define _LINUX_LIRC_DEV_H + +#define MAX_IRCTL_DEVICES 2 +#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); +} +static inline int lirc_buffer_available(struct lirc_buffer *buf) +{ + return (buf->size - buf->fill); +} +extern inline void lirc_buffer_lock(struct lirc_buffer *buf, unsigned long *flags) +{ + spin_lock_irqsave(&buf->lock, *flags); +} +extern inline void lirc_buffer_unlock(struct lirc_buffer *buf, unsigned long *flags) +{ + spin_unlock_irqrestore(&buf->lock, *flags); +} +static inline void _lirc_buffer_remove_1(struct lirc_buffer *buf) +{ + buf->head = mod(buf->head+1, buf->size); + buf->fill -= 1; +} +static inline void lirc_buffer_remove_1(struct lirc_buffer *buf) +{ + unsigned long flags; + lirc_buffer_lock(buf, &flags); + _lirc_buffer_remove_1(buf); + lirc_buffer_unlock(buf, &flags); +} +static inline void _lirc_buffer_read_1(struct lirc_buffer *buf, + unsigned char *dest) +{ + memcpy(dest, &buf->data[buf->head*buf->chunk_size], buf->chunk_size); + buf->head = mod(buf->head+1, buf->size); + buf->fill -= 1; +} +static inline void lirc_buffer_read_1(struct lirc_buffer *buf, + unsigned char *dest) +{ + unsigned long flags; + lirc_buffer_lock(buf, &flags); + _lirc_buffer_read_1(buf, dest); + lirc_buffer_unlock(buf, &flags); +} +static inline void _lirc_buffer_write_1(struct lirc_buffer *buf, + unsigned char *orig) +{ + memcpy(&buf->data[buf->tail*buf->chunk_size], orig, buf->chunk_size); + buf->tail = mod(buf->tail+1, buf->size); + buf->fill++; +} +static inline void lirc_buffer_write_1(struct lirc_buffer *buf, + unsigned char *orig) +{ + unsigned long flags; + lirc_buffer_lock(buf, &flags); + _lirc_buffer_write_1(buf, orig); + lirc_buffer_unlock(buf, &flags); +} +static inline void _lirc_buffer_write_n(struct lirc_buffer *buf, + unsigned char* orig, int count) +{ + memcpy(&buf->data[buf->tail*buf->chunk_size], orig, + count*buf->chunk_size); + buf->tail = mod(buf->tail+count, buf->size); + buf->fill += count; +} +static inline void lirc_buffer_write_n(struct lirc_buffer *buf, + unsigned char* orig, int count) +{ + unsigned long flags; + int space1; + lirc_buffer_lock(buf,&flags); + if( buf->head > buf->tail ) space1 = buf->head - buf->tail; + else space1 = buf->size - buf->tail; + + if( count > space1 ) + { + _lirc_buffer_write_n(buf, orig, space1); + _lirc_buffer_write_n(buf, orig+(space1*buf->chunk_size), + count-space1); + } + else + { + _lirc_buffer_write_n(buf, orig, count); + } + lirc_buffer_unlock(buf, &flags); +} + +struct lirc_plugin +{ + char name[40]; + int minor; + int code_length; + int sample_rate; + unsigned long features; + void* data; + int (*add_to_buf) (void* data, struct lirc_buffer* buf); + wait_queue_head_t* (*get_queue) (void* data); + struct lirc_buffer *rbuf; + int (*set_use_inc) (void* data); + void (*set_use_dec) (void* data); + int (*ioctl) (struct inode *,struct file *,unsigned int, + unsigned long); + struct file_operations *fops; +}; +/* name: + * this string will be used for logs + * + * minor: + * indicates minor device (/dev/lirc) number for registered plugin + * if caller fills it with negative value, then the first free minor + * number will be used (if available) + * + * code_length: + * length of the remote control key code expressed in bits + * + * sample_rate: + * sample_rate equal to 0 means that no polling will be performed and + * add_to_buf will be triggered by external events (through task queue + * returned by get_queue) + * + * data: + * it may point to any plugin data and this pointer will be passed to + * all callback functions + * + * add_to_buf: + * add_to_buf will be called after specified period of the time or + * triggered by the external event, this behavior depends on value of + * the sample_rate this function will be called in user context. This + * routine should return 0 if data was added to the buffer and + * -ENODATA if none was available. This should add some number of bits + * evenly divisible by code_length to the buffer + * + * get_queue: + * this callback should return a pointer to the task queue which will + * be used for external event waiting + * + * rbuf: + * if not NULL, it will be used as a read buffer, you will have to + * write to the buffer by other means, like irq's (see also + * lirc_serial.c). + * + * set_use_inc: + * set_use_inc will be called after device is opened + * + * set_use_dec: + * set_use_dec will be called after device is closed + * + * ioctl: + * Some ioctl's can be directly handled by lirc_dev but will be + * forwared here if not NULL and only handled if it returns + * -ENOIOCTLCMD (see also lirc_serial.c). + * + * fops: + * file_operations for drivers which don't fit the current plugin model. + */ + + +/* following functions can be called ONLY from user context + * + * returns negative value on error or minor number + * of the registered device if success + * contens of the structure pointed by p is copied + */ +extern int lirc_register_plugin(struct lirc_plugin *p); + +/* returns negative value on error or 0 if success +*/ +extern int lirc_unregister_plugin(int minor); + + +#endif diff -uNr linux-2.6.8-rc4.orig/drivers/char/lirc/lirc_gpio.c linux-2.6.8-rc4/drivers/char/lirc/lirc_gpio.c --- linux-2.6.8-rc4.orig/drivers/char/lirc/lirc_gpio.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.8-rc4/drivers/char/lirc/lirc_gpio.c 2004-04-27 08:08:24.000000000 +0200 @@ -0,0 +1,572 @@ +/* + * 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$ + * + */ + +#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 + +#include +#include +#include +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) +#include +#endif +#include + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0) +#include "../drivers/char/bttv.h" +#include "../drivers/char/bttvp.h" +#else +#include "../drivers/media/video/bttv.h" +#include "../drivers/media/video/bttvp.h" +#endif + +#if BTTV_VERSION_CODE < KERNEL_VERSION(0,7,45) +#error "*******************************************************" +#error " Sorry, this driver needs bttv version 0.7.45 or " +#error " higher. If you are using the bttv package, copy it to " +#error " the kernel " +#error "*******************************************************" +#endif + +#include "kcompat.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; +} + +/* add_to_buf - copy a code to the buffer */ +static int add_to_buf(void* data, struct lirc_buffer* buf) +{ + static unsigned long next_time = 0; + static unsigned char prev_codes[MAX_BYTES]; + unsigned long code = 0; + unsigned char cur_codes[MAX_BYTES]; + + if (bttv_read_gpio(card, &code)) { + dprintk(LOGHEAD "cannot read GPIO\n", card); + return -EIO; + } + + if (build_key(code, cur_codes)) { + return -EFAULT; + } + + if (soft_gap) { + if (!memcmp(prev_codes, cur_codes, code_bytes) && + jiffies < next_time) { + return -EAGAIN; + } + next_time = jiffies + soft_gap; + } + memcpy( prev_codes, cur_codes, code_bytes ); + + lirc_buffer_write_1( buf, cur_codes ); + + return SUCCESS; +} + +static int set_use_inc(void* data) +{ + MOD_INC_USE_COUNT; + return 0; +} + +static void set_use_dec(void* data) +{ + MOD_DEC_USE_COUNT; +} + +static wait_queue_head_t* get_queue(void* data) +{ + return bttv_get_gpio_queue(card); +} + +static struct lirc_plugin plugin = { + .name = "lirc_gpio ", + .add_to_buf = add_to_buf, + .get_queue = get_queue, + .set_use_inc = set_use_inc, + .set_use_dec = set_use_dec, +}; + +/* + * + */ +int gpio_remote_init(void) +{ + int ret; + unsigned int mask; + + /* "normalize" gpio_mask + * this means shift it right until first bit is set + */ + while (!(gpio_mask & 1u)) { + gpio_pre_shift++; + gpio_mask >>= 1; + } + + if (code_length) { + plugin.code_length = code_length; + } else { + /* calculate scan code length in bits if needed */ + plugin.code_length = 1; + mask = gpio_mask >> 1; + while (mask) { + if (mask & 1u) { + plugin.code_length++; + } + mask >>= 1; + } + } + + code_bytes = (plugin.code_length/8) + (plugin.code_length%8 ? 1 : 0); + if (MAX_BYTES < code_bytes) { + printk (LOGHEAD "scan code too long (%d bytes)\n", + minor, code_bytes); + return -EBADRQC; + } + + if (gpio_enable) { + if(bttv_gpio_enable(card, gpio_enable, gpio_enable)) { + printk(LOGHEAD "gpio_enable failure\n", minor); + return -EIO; + } + } + + + /* translate ms to jiffies */ + soft_gap = (soft_gap*HZ) / 1000; + + plugin.minor = minor; + plugin.sample_rate = sample_rate; + + ret = lirc_register_plugin(&plugin); + + if (0 > ret) { + printk (LOGHEAD "device registration failed with %d\n", + minor, ret); + return ret; + } + + minor = ret; + printk(LOGHEAD "driver registered\n", minor); + + return SUCCESS; +} + +#ifndef KERNEL_2_5 +EXPORT_NO_SYMBOLS; +#endif + +/* Dont try to use it as a static version ! */ + +#ifdef MODULE +MODULE_DESCRIPTION("Driver module for remote control (data from bt848 GPIO port)"); +MODULE_AUTHOR("Artur Lipowski"); +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +/* + * + */ +int init_module(void) +{ + int type,cardid,card_type; + + if (MAX_IRCTL_DEVICES < minor) { + printk("lirc_gpio: parameter minor (%d) must be less than %d!\n", + minor, MAX_IRCTL_DEVICES-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 || HZ < sample_rate)) { + printk(LOGHEAD "parameter sample_rate " + "must be between 2 and %d!\n", minor, HZ); + return -EBADRQC; + } + + if (sample_rate!=0 && soft_gap && + ((2000/sample_rate) > soft_gap || 1000 < soft_gap)) { + printk(LOGHEAD "parameter soft_gap " + "must be between %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); + return -EBADRQC; + } + 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 cleanup_module(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); +} +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * --------------------------------------------------------------------------- + * Local variables: + * c-basic-offset: 8 + * End: + */ diff -uNr linux-2.6.8-rc4.orig/drivers/char/lirc/lirc_i2c.c linux-2.6.8-rc4/drivers/char/lirc/lirc_i2c.c --- linux-2.6.8-rc4.orig/drivers/char/lirc/lirc_i2c.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.8-rc4/drivers/char/lirc/lirc_i2c.c 2004-04-09 17:33:41.000000000 +0200 @@ -0,0 +1,545 @@ +/* $Id$ */ + +/* + * i2c IR lirc plugin for Hauppauge and Pixelview cards - new 2.3.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 + * + * 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 +#if LINUX_VERSION_CODE < 0x020200 +#error "--- Sorry, this driver needs kernel version 2.2.0 or higher. ---" +#endif + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef I2C_CLIENT_END +#error "********************************************************" +#error " Sorry, this driver needs the new I2C stack. " +#error " You can get it at http://www2.lm-sensors.nu/~lm78/. " +#error "********************************************************" +#endif + +#include + +#include + +#include "kcompat.h" +#include "lirc_dev.h" +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0) +#include "../drivers/char/bttv.h" +#else +#include "../drivers/media/video/bttv.h" +#endif + +struct IR { + struct lirc_plugin l; + struct i2c_client c; + 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"); + +#define dprintk if (debug) printk + +/* ----------------------------------------------------------------------- */ + +#define DEVICE_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->c) & ~all; + + /* send bit mask */ + rc = i2c_smbus_write_byte(&ir->c, (0xff & all) | mask); + + /* receive scan code */ + rc = i2c_smbus_read_byte(&ir->c); + + if (rc == -1) { + dprintk(DEVICE_NAME ": %s read error\n", ir->c.name); + return -EIO; + } + + /* drop duplicate polls */ + if (ir->b[0] == (rc & all)) { + return -ENODATA; + } + ir->b[0] = rc & all; + + dprintk(DEVICE_NAME ": %s key 0x%02X %s\n", + ir->c.name, rc & ir->bits, + (rc & ir->flag) ? "released" : "pressed"); + + if (rc & ir->flag) { + /* ignore released buttons */ + return -ENODATA; + } + + /* set valid key code */ + key = rc & ir->bits; + lirc_buffer_write_1( buf, &key ); + return 0; +} + +static int add_to_buf_haup(void* data, struct lirc_buffer* buf) +{ + struct IR *ir = data; + unsigned char keybuf[3]; + __u16 code; + unsigned char codes[2]; + + /* poll IR chip */ + if (3 == i2c_master_recv(&ir->c,keybuf,3)) { + ir->b[0] = keybuf[0]; + ir->b[1] = keybuf[1]; + ir->b[2] = keybuf[2]; + dprintk(KERN_DEBUG DEVICE_NAME ": key (0x%02x/0x%02x)\n", + ir->b[0], ir->b[1]); + } else { + dprintk(KERN_DEBUG DEVICE_NAME ": read error\n"); + /* keep last successfull read buffer */ + } + + /* key pressed ? */ + if ((ir->b[0] & 0x80) == 0) + return -ENODATA; + + /* look what we have */ + code = (((__u16)ir->b[0]&0x7f)<<6) | (ir->b[1]>>2); + + codes[0] = (code >> 8) & 0xff; + codes[1] = code & 0xff; + + /* return it */ + lirc_buffer_write_1( buf, codes ); + return 0; +} + +static int add_to_buf_pixelview(void* data, struct lirc_buffer* buf) +{ + struct IR *ir = data; + unsigned char key; + + /* poll IR chip */ + if (1 != i2c_master_recv(&ir->c,&key,1)) { + dprintk(KERN_DEBUG DEVICE_NAME ": read error\n"); + return -1; + } + dprintk(KERN_DEBUG DEVICE_NAME ": key %02x\n", key); + + /* return it */ + lirc_buffer_write_1( buf, &key ); + return 0; +} + +static int add_to_buf_pv951(void* data, struct lirc_buffer* buf) +{ + struct IR *ir = data; + unsigned char key; + unsigned char codes[4]; + + /* poll IR chip */ + if (1 != i2c_master_recv(&ir->c,&key,1)) { + dprintk(KERN_DEBUG DEVICE_NAME ": read error\n"); + return -ENODATA; + } + /* ignore 0xaa */ + if (key==0xaa) + return -ENODATA; + dprintk(KERN_DEBUG DEVICE_NAME ": key %02x\n", key); + + codes[0] = 0x61; + codes[1] = 0xD6; + codes[2] = reverse(key,8); + codes[3] = (~codes[2])&0xff; + + lirc_buffer_write_1( buf, codes ); + return 0; +} + +static int add_to_buf_knc1(void *data, struct lirc_buffer* buf) +{ + static unsigned char last_key = 0xFF; + struct IR *ir = data; + unsigned char key; + + /* poll IR chip */ + if (1 != i2c_master_recv(&ir->c,&key,1)) { + dprintk(KERN_DEBUG DEVICE_NAME ": read error\n"); + return -ENODATA; + } + + /* it seems that 0xFE indicates that a button is still hold + down, while 0xFF indicates that no button is hold + down. 0xFE sequences are sometimes interrupted by 0xFF */ + + dprintk(KERN_DEBUG DEVICE_NAME ": key %02x\n", key); + + if( key == 0xFF ) + return -ENODATA; + + if ( key == 0xFE ) + key = last_key; + + last_key = key; + lirc_buffer_write_1( buf, &key ); + + return 0; +} + +static int set_use_inc(void* data) +{ + struct IR *ir = data; + + /* lock bttv in memory while /dev/lirc is in use */ + /* this is completely broken code. lirc_unregister_plugin() + must be possible even when the device is open */ +#ifdef KERNEL_2_5 + i2c_use_client(&ir->c); +#else + if (ir->c.adapter->inc_use) + ir->c.adapter->inc_use(ir->c.adapter); +#endif + + MOD_INC_USE_COUNT; + return 0; +} + +static void set_use_dec(void* data) +{ + struct IR *ir = data; + +#ifdef KERNEL_2_5 + i2c_release_client(&ir->c); +#else + if (ir->c.adapter->dec_use) + ir->c.adapter->dec_use(ir->c.adapter); +#endif + MOD_DEC_USE_COUNT; +} + +static struct lirc_plugin lirc_template = { + name: "lirc_i2c", + set_use_inc: set_use_inc, + set_use_dec: set_use_dec +}; + +/* ----------------------------------------------------------------------- */ + +static int ir_attach(struct i2c_adapter *adap, int addr, + unsigned short flags, int kind); +static int ir_detach(struct i2c_client *client); +static int ir_probe(struct i2c_adapter *adap); +static int ir_command(struct i2c_client *client, unsigned int cmd, void *arg); + +static struct i2c_driver driver = { + name: "i2c ir driver", + id: I2C_DRIVERID_EXP3, /* FIXME */ + flags: I2C_DF_NOTIFY, + attach_adapter: ir_probe, + detach_client: ir_detach, + command: ir_command, +}; + +static struct i2c_client client_template = +{ + name: "unset", + driver: &driver +}; + +static int ir_attach(struct i2c_adapter *adap, int addr, + unsigned short flags, int kind) +{ + struct IR *ir; + + client_template.adapter = adap; + client_template.addr = addr; + + if (NULL == (ir = kmalloc(sizeof(struct IR),GFP_KERNEL))) + return -ENOMEM; + memcpy(&ir->l,&lirc_template,sizeof(struct lirc_plugin)); + memcpy(&ir->c,&client_template,sizeof(struct i2c_client)); + + ir->c.adapter = adap; + ir->c.addr = addr; +#ifdef KERNEL_2_5 + i2c_set_clientdata(&ir->c, ir); +#else + ir->c.data = ir; +#endif + ir->l.data = ir; + ir->l.minor = minor; + ir->l.sample_rate = 10; + ir->nextkey = -1; + + switch(addr) + { + case 0x64: + strcpy(ir->c.name,"Pixelview IR"); + ir->l.code_length = 8; + ir->l.add_to_buf=add_to_buf_pixelview; + break; + case 0x4b: + strcpy(ir->c.name,"PV951 IR"); + ir->l.code_length = 32; + ir->l.add_to_buf=add_to_buf_pv951; + break; + case 0x18: + case 0x1a: + strcpy(ir->c.name,"Hauppauge IR"); + ir->l.code_length = 13; + ir->l.add_to_buf=add_to_buf_haup; + break; + case 0x30: + strcpy(ir->c.name,"KNC ONE IR"); + ir->l.code_length = 8; + ir->l.add_to_buf=add_to_buf_knc1; + break; + case 0x21: + case 0x23: + strcpy(ir->c.name,"TV-Box IR"); + ir->l.code_length = 8; + ir->l.add_to_buf=add_to_buf_pcf8574; + ir->bits = flags & 0xff; + ir->flag = (flags >> 8) & 0xff; + break; + + default: + /* shouldn't happen */ + printk("lirc_i2c: Huh? unknown i2c address (0x%02x)?\n",addr); + kfree(ir); + return -1; + } + printk("lirc_i2c: chip found @ 0x%02x (%s)\n",addr,ir->c.name); + + /* register device */ + i2c_attach_client(&ir->c); + ir->l.minor = lirc_register_plugin(&ir->l); +#ifdef KERNEL_2_5 + i2c_use_client(&ir->c); +#else + if (ir->c.adapter->inc_use) + ir->c.adapter->inc_use(ir->c.adapter); +#endif + + return 0; +} + +static int ir_detach(struct i2c_client *client) +{ +#ifdef KERNEL_2_5 + struct IR *ir = i2c_get_clientdata(client); +#else + struct IR *ir = client->data; +#endif + + /* unregister device */ +#ifdef KERNEL_2_5 + i2c_release_client(&ir->c); +#else + if (ir->c.adapter->dec_use) + ir->c.adapter->dec_use(ir->c.adapter); +#endif + lirc_unregister_plugin(ir->l.minor); + i2c_detach_client(&ir->c); + + /* free memory */ + kfree(ir); + return 0; +} + +static int ir_probe(struct i2c_adapter *adap) { + + /* The external IR receiver is at i2c address 0x34 (0x35 for + reads). Future Hauppauge cards will have an internal + receiver at 0x30 (0x31 for reads). In theory, both can be + fitted, and Hauppauge suggest an external overrides an + internal. + + That's why we probe 0x1a (~0x34) first. CB + */ + + static const int probe[] = { 0x1a, 0x18, 0x4b, 0x64, 0x30, -1}; + struct i2c_client c; char buf; int i,rc; + + if (adap->id == (I2C_ALGO_BIT | I2C_HW_B_BT848)) { + memset(&c,0,sizeof(c)); + c.adapter = adap; + for (i = 0; -1 != probe[i]; i++) { + c.addr = probe[i]; + rc = i2c_master_recv(&c,&buf,1); + dprintk("lirc_i2c: probe 0x%02x @ %s: %s\n", + probe[i], adap->name, + (1 == rc) ? "yes" : "no"); + if (1 == rc) + { + ir_attach(adap,probe[i],0,0); + } + } + } + + /* Asus TV-Box and Creative/VisionTek BreakOut-Box (PCF8574) */ + else if (adap->id == (I2C_ALGO_BIT | I2C_HW_B_RIVA)) { + /* addresses to probe; + leave 0x24 and 0x25 because SAA7113H possibly uses it + 0x21 and 0x22 possibly used by SAA7108E + Asus: 0x21 is a correct address (channel 1 of PCF8574) + Creative: 0x23 is a correct address (channel 3 of PCF8574) + VisionTek: 0x23 is a correct address (channel 3 of PCF8574) + */ + static const int pcf_probe[] = { 0x20, 0x21, 0x22, 0x23, + 0x24, 0x25, 0x26, 0x27, -1 }; + int ret1, ret2, ret3, ret4; + unsigned char bits = 0, flag = 0; + + memset(&c,0,sizeof(c)); + c.adapter = adap; + for (i = 0; -1 != pcf_probe[i]; i++) { + c.addr = pcf_probe[i]; + ret1 = i2c_smbus_write_byte(&c, 0xff); + ret2 = i2c_smbus_read_byte(&c); + ret3 = i2c_smbus_write_byte(&c, 0x00); + ret4 = i2c_smbus_read_byte(&c); + + /* ensure that the writable bitmask works correctly */ + rc = 0; + if (ret1 != -1 && ret2 != -1 && + ret3 != -1 && ret4 != -1) { + /* in the Asus TV-Box: bit 1-0 */ + if (((ret2 & 0x03) == 0x03) && + ((ret4 & 0x03) == 0x00)) { + bits = (unsigned char) ~0x07; + flag = 0x04; + rc = 1; + } + /* in the Creative/VisionTek BreakOut-Box: bit 7-6 */ + if (((ret2 & 0xc0) == 0xc0) && + ((ret4 & 0xc0) == 0x00)) { + bits = (unsigned char) ~0xe0; + flag = 0x20; + rc = 1; + } + } + dprintk(DEVICE_NAME ": probe 0x%02x @ %s: %s\n", + c.addr, adap->name, rc ? "yes" : "no"); + if (rc) + ir_attach(adap,pcf_probe[i],bits|(flag<<8),0); + } + } + + return 0; +} + +static int ir_command(struct i2c_client *client,unsigned int cmd, void *arg) +{ + /* nothing */ + return 0; +} + +/* ----------------------------------------------------------------------- */ +#ifdef MODULE +MODULE_AUTHOR("Gerd Knorr, Michal Kochanowicz, Christoph Bartelmus, Ulrich Mueller, Stefan Jahn"); +MODULE_DESCRIPTION("Infrared receiver driver for Hauppauge and Pixelview cards (i2c stack)"); +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif +#endif + + +#ifdef MODULE +int init_module(void) +#else +int lirc_i2c_init(void) +#endif +{ + request_module("bttv"); + request_module("rivatv"); + i2c_add_driver(&driver); + return 0; +} + +#ifdef MODULE +void cleanup_module(void) +{ + i2c_del_driver(&driver); +} +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * --------------------------------------------------------------------------- + * Local variables: + * c-basic-offset: 8 + * End: + */ diff -uNr linux-2.6.8-rc4.orig/drivers/char/lirc/lirc_it87.c linux-2.6.8-rc4/drivers/char/lirc/lirc_it87.c --- linux-2.6.8-rc4.orig/drivers/char/lirc/lirc_it87.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.8-rc4/drivers/char/lirc/lirc_it87.c 2004-04-27 20:45:31.000000000 +0200 @@ -0,0 +1,970 @@ +/* + * 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 + +#ifdef HAVE_CONFIG_H +# include +#endif + +#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 "kcompat.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); +#ifdef MODULE +static int init_chrdev(void); +static void drop_chrdev(void); +#endif + /* Hardware */ +static irqreturn_t it87_interrupt(int irq, void * dev_id, + struct pt_regs * regs); +static void send_space(unsigned long len); +static void send_pulse(unsigned long len); +static void init_send(void); +static void terminate_send(unsigned long len); +static int init_hardware(void); +static void drop_hardware(void); + /* Initialisation */ +static int init_port(void); +static void drop_port(void); +int init_module(void); +void cleanup_module(void); + + +/* SECTION: Communication with user-space */ + +static int lirc_open(struct inode * inode, + struct file * file) +{ + spin_lock(&dev_lock); + if (MOD_IN_USE) { + spin_unlock(&dev_lock); + return -EBUSY; + } + MOD_INC_USE_COUNT; + spin_unlock(&dev_lock); + return 0; +} + + +static int lirc_close(struct inode * inode, + struct file *file) +{ + MOD_DEC_USE_COUNT; + 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 + MOD_INC_USE_COUNT; +#endif + return 0; +} + +static void set_use_dec(void* data) +{ +#if WE_DONT_USE_LOCAL_OPEN_CLOSE + MOD_DEC_USE_COUNT; +#endif +} +static struct lirc_plugin plugin = { + name: LIRC_DRIVER_NAME, + minor: -1, + code_length: 1, + sample_rate: 0, + data: NULL, + add_to_buf: NULL, + get_queue: NULL, + set_use_inc: set_use_inc, + set_use_dec: set_use_dec, + fops: &lirc_fops, +}; + + +#ifdef MODULE +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); +} +#endif + + +/* SECTION: Hardware */ +static long delta(struct timeval * tv1, + struct timeval * tv2) +{ + unsigned long deltv; + + deltv = tv2->tv_sec - tv1->tv_sec; + if (deltv > 15) + deltv = 0xFFFFFF; + else + deltv = deltv*1000000 + + tv2->tv_usec - + tv1->tv_usec; + return deltv; +} + + +static void it87_timeout(unsigned long data) +{ + /* if last received signal was a pulse, but receiving stopped + within the 9 bit frame, we need to finish this pulse and + simulate a signal change to from pulse to space. Otherwise + upper layers will receive two sequences next time. */ + + unsigned long flags; + unsigned long pulse_end; + + /* avoid interference with interrupt */ + spin_lock_irqsave(&timer_lock, flags); + if (last_value) { + /* determine 'virtual' pulse end: */ + pulse_end = delta(&last_tv, &last_intr_tv); +#ifdef DEBUG_SIGNAL + printk(KERN_DEBUG LIRC_DRIVER_NAME + ": timeout add %d for %lu usec\n", + last_value, + pulse_end); +#endif + add_read_queue(last_value, + pulse_end); + last_value = 0; + last_tv=last_intr_tv; + } + spin_unlock_irqrestore(&timer_lock, flags); +} + + +static irqreturn_t it87_interrupt(int irq, + void * dev_id, + struct pt_regs * regs) +{ + unsigned char data; + struct timeval curr_tv; + static unsigned long deltv; + unsigned long deltintrtv; + unsigned long flags, hw_flags; + int iir, lsr; + int fifo = 0; + + iir = inb(io + IT87_CIR_IIR); + + switch (iir & IT87_CIR_IIR_IID) { + case 0x4: + case 0x6: + lsr = inb(io + IT87_CIR_RSR) & (IT87_CIR_RSR_RXFTO | + IT87_CIR_RSR_RXFBC); + fifo = lsr & IT87_CIR_RSR_RXFBC; +#ifdef DEBUG_SIGNAL + printk(KERN_DEBUG LIRC_DRIVER_NAME + "iir: 0x%x fifo: 0x%x\n", iir, lsr); +#endif + + /* avoid interference with timer */ + spin_lock_irqsave(&timer_lock, flags); + spin_lock_irqsave(&hardware_lock, hw_flags); + do { + del_timer(&timerlist); + data = inb(io + IT87_CIR_DR); +#ifdef DEBUG_SIGNAL + printk(KERN_DEBUG LIRC_DRIVER_NAME + ": data=%.2x\n", + data); +#endif + do_gettimeofday(&curr_tv); + deltv = delta(&last_tv, &curr_tv); + deltintrtv = delta(&last_intr_tv, &curr_tv); +#ifdef DEBUG_SIGNAL + printk(KERN_DEBUG LIRC_DRIVER_NAME + ": t %lu , d %d\n", + deltintrtv, + (int)data); +#endif + /* if nothing came in last 2 cycles, + it was gap */ + if (deltintrtv > TIME_CONST * 2) { + if (last_value) { +#ifdef DEBUG_SIGNAL + printk(KERN_DEBUG LIRC_DRIVER_NAME ": GAP\n"); +#endif + /* simulate signal change */ + add_read_queue(last_value, + deltv- + deltintrtv); + last_value = 0; + last_tv.tv_sec = last_intr_tv.tv_sec; + last_tv.tv_usec = last_intr_tv.tv_usec; + deltv = deltintrtv; + } + } + data = 1; + if (data ^ last_value) { + /* deltintrtv > 2*TIME_CONST, + remember ? */ + /* the other case is timeout */ + add_read_queue(last_value, + deltv-TIME_CONST); + last_value = data; + last_tv = curr_tv; + if(last_tv.tv_usec>=TIME_CONST) { + last_tv.tv_usec-=TIME_CONST; + } + else { + last_tv.tv_sec--; + last_tv.tv_usec+=1000000- + TIME_CONST; + } + } + last_intr_tv = curr_tv; + if (data) { + /* start timer for end of sequence detection */ + timerlist.expires = jiffies + IT87_TIMEOUT; + add_timer(&timerlist); + } + outb((inb(io + IT87_CIR_RCR) & ~IT87_CIR_RCR_RXEN) | + IT87_CIR_RCR_RXACT, + io + IT87_CIR_RCR); + if (it87_RXEN_mask) { + outb(inb(io + IT87_CIR_RCR) | IT87_CIR_RCR_RXEN, + io + IT87_CIR_RCR); + } + fifo--; + } + while (fifo != 0); + spin_unlock_irqrestore(&hardware_lock, hw_flags); + spin_unlock_irqrestore(&timer_lock, flags); + + return IRQ_RETVAL(IRQ_HANDLED); + + default: + /* not our irq */ +#ifdef DEBUG_SIGNAL + printk(KERN_DEBUG LIRC_DRIVER_NAME + "unknown IRQ (shouldn't happen) !!\n"); +#endif + return IRQ_RETVAL(IRQ_NONE); + } +} + + +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 = check_region(io, 8); + if (retval < 0) { + printk(KERN_ERR LIRC_DRIVER_NAME + ": i/o 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; + } + + request_region(io, 8, LIRC_DRIVER_NAME); + printk(KERN_INFO LIRC_DRIVER_NAME + ": I/O port 0x%.4x, IRQ %d.\n", + io, + irq); + + 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; +} + + +#ifdef MODULE + +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"); +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +#ifndef KERNEL_2_5 +EXPORT_NO_SYMBOLS; +#endif + + +int init_module(void) +{ + int retval; + + retval=init_chrdev(); + if(retval < 0) + return retval; + retval = init_lirc_it87(); + if (retval) { + drop_chrdev(); + return retval; + } + return 0; +} + + +void cleanup_module(void) +{ + drop_hardware(); + drop_chrdev(); + drop_port(); + printk(KERN_INFO LIRC_DRIVER_NAME ": Uninstalled.\n"); +} +#endif + + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * --------------------------------------------------------------------------- + * Local variables: + * c-basic-offset: 8 + * End: + */ diff -uNr linux-2.6.8-rc4.orig/drivers/char/lirc/lirc_it87.h linux-2.6.8-rc4/drivers/char/lirc/lirc_it87.h --- linux-2.6.8-rc4.orig/drivers/char/lirc/lirc_it87.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.8-rc4/drivers/char/lirc/lirc_it87.h 2001-11-15 11:50:19.000000000 +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 -uNr linux-2.6.8-rc4.orig/drivers/char/lirc/lirc_mceusb.c linux-2.6.8-rc4/drivers/char/lirc/lirc_mceusb.c --- linux-2.6.8-rc4.orig/drivers/char/lirc/lirc_mceusb.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.8-rc4/drivers/char/lirc/lirc_mceusb.c 2004-07-25 18:27:33.000000000 +0200 @@ -0,0 +1,1026 @@ +/* + * USB Microsoft IR Transceiver driver - 0.2 + * + * 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. + * + * All trademarks property of their respective owners. This driver was + * originally based on the USB skeleton driver, although significant + * portions of that code have been removed as the driver has evolved. + * + * 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) + * + * 2004_02_17 - Changed top level api to no longer use fops, and + * instead use new interface for polling via + * lirc_thread. Restructure data read/mode2 generation to + * a single pass, reducing number of buffers. Rev to .2 + * + * 2004_02_27 - Last of fixups to plugin->add_to_buf API. Properly + * handle broken fragments from the receiver. Up the + * sample rate and remove any pacing from + * fetch_more_data. Fixes all known issues. + * + * TODO + * - Fix up minor number, registration of major/minor with usb subsystem + * + */ +/* + * USB Skeleton driver - 1.1 + * + * Copyright (C) 2001-2003 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, version 2. + * + * + * 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, David Brownell, and Alan Stern for their help + * in debugging this driver. + * + * + * History: + * + * 2003-05-06 - 1.1 - changes due to usb core changes with usb_register_dev() + * 2003-02-25 - 1.0 - fix races involving urb->status, unlink_urb(), and + * disconnect. Fix transfer amount in read(). Use + * macros instead of magic numbers in probe(). Change + * size variables to size_t. Show how to eliminate + * DMA bounce buffer. + * 2002_12_12 - 0.9 - compile fixes and got rid of fixed minor array. + * 2002_09_26 - 0.8 - changes due to USB core conversion to struct device + * driver. + * 2002_02_12 - 0.7 - zero out dev in probe function for devices that do + * not have both a bulk in and bulk out endpoint. + * Thanks to Holger Waechtler for the fix. + * 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 +#ifdef KERNEL_2_5 +#include +#include +#else +#include +#include +#include +#include +#include +#include +#endif + +#ifdef CONFIG_USB_DEBUG + static int debug = 1; +#else + static int debug; +#endif + +#include +#include "kcompat.h" +#include "lirc_dev.h" + + +/* Use our own dbg macro */ +#undef dbg +#define dbg(format, arg...) do { if (debug) printk(KERN_DEBUG __FILE__ ": " format "\n" , ## arg); } while (0) + +/* Version Information */ +#define DRIVER_VERSION "v0.2" +#define DRIVER_AUTHOR "Dan Conti, dconti@acm.wwu.edu" +#define DRIVER_DESC "USB Microsoft IR Transceiver Driver" +#define DRIVER_NAME "lirc_mceusb" + +/* 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); + +/* 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 { + struct usb_device * udev; /* save off the usb device pointer */ + struct usb_interface * interface; /* the interface for this device */ + unsigned char minor; /* the starting minor number for this device */ + unsigned char num_ports; /* the number of ports this device has */ + char num_interrupt_in; /* number of interrupt in endpoints we have */ + char num_bulk_in; /* number of bulk in endpoints we have */ + char num_bulk_out; /* number of bulk out endpoints we have */ + + unsigned char * bulk_in_buffer; /* the buffer to receive data */ + int bulk_in_size; /* the size of the receive buffer */ + __u8 bulk_in_endpointAddr; /* the address of the bulk in endpoint */ + + unsigned char * bulk_out_buffer; /* the buffer to send data */ + int bulk_out_size; /* the size of the send buffer */ + struct urb * write_urb; /* the urb used to send data */ + __u8 bulk_out_endpointAddr; /* the address of the bulk out endpoint */ + + atomic_t write_busy; /* true iff write urb is busy */ + struct completion write_finished; /* wait for the write to finish */ + + 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 */ + + int present; /* if the device is not disconnected */ + + struct lirc_plugin* plugin; + + lirc_t lircdata[256]; /* place to store values until lirc processes them */ + int lircidx; /* current index */ + int lirccnt; /* remaining values */ + + int usb_valid_bytes_in_bulk_buffer; /* leftover data from a previous read */ + int mce_bytes_left_in_packet; /* for packets split across multiple reads */ + + /* Value to hold the last received space; 0 if last value + * received was a pulse + */ + int last_space; + +#ifdef KERNEL_2_5 + dma_addr_t dma_in; + dma_addr_t dma_out; +#endif +}; + +#define MCE_TIME_UNIT 50 + +/* driver api */ +#ifdef KERNEL_2_5 +static int mceusb_probe (struct usb_interface *interface, const struct usb_device_id *id); +static void mceusb_disconnect (struct usb_interface *interface); +static void mceusb_write_bulk_callback (struct urb *urb, struct pt_regs *regs); +#else +static void * mceusb_probe (struct usb_device *dev, unsigned int ifnum, const struct usb_device_id *id); +static void mceusb_disconnect (struct usb_device *dev, void *ptr); +static void mceusb_write_bulk_callback (struct urb *urb); +#endif + +/* read data from the usb bus; convert to mode2 */ +static int msir_fetch_more_data( struct usb_skel* dev, int dont_block ); + +/* helper functions */ +static void msir_cleanup( struct usb_skel* dev ); +static void set_use_dec( void* data ); +static int set_use_inc( 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); +static void mceusb_setup( struct usb_device *udev ); + +/* usb specific object needed to register this driver with the usb subsystem */ +static struct usb_driver mceusb_driver = { + .owner = THIS_MODULE, + .name = DRIVER_NAME, + .probe = mceusb_probe, + .disconnect = mceusb_disconnect, + .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) +{ + dbg("%s",__func__); + minor_table[dev->minor] = NULL; +#ifdef KERNEL_2_5 + usb_buffer_free(dev->udev, dev->bulk_in_size, dev->bulk_in_buffer, dev->dma_in); + usb_buffer_free(dev->udev, dev->bulk_out_size, dev->bulk_out_buffer, dev->dma_out); +#else + if (dev->bulk_in_buffer != NULL) + kfree (dev->bulk_in_buffer); + if (dev->bulk_out_buffer != NULL) + kfree (dev->bulk_out_buffer); +#endif + 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("%s - res = %d status = 0x%x 0x%x",__func__,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("%s - res = %d, devnum = %d", __func__, res, udev->devnum); + dbg("%s - data[0] = %d, data[1] = %d", __func__, 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("%s - res = %d", __func__, 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("%s - res = %d", __func__, 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("%s - res = %d", __func__, res); +#endif +} + +static void msir_cleanup( struct usb_skel* dev ) +{ + memset( dev->bulk_in_buffer, 0, dev->bulk_in_size ); + + dev->usb_valid_bytes_in_bulk_buffer = 0; + + dev->last_space = PULSE_MASK; + + dev->mce_bytes_left_in_packet = 0; + dev->lircidx = 0; + dev->lirccnt = 0; + memset( dev->lircdata, 0, sizeof(dev->lircdata) ); +} + +static int set_use_inc(void* data) +{ + MOD_INC_USE_COUNT; + 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 ); + } + + MOD_DEC_USE_COUNT; +} + +/* + * 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 + * + * This routine always returns the number of words available + * + */ +static int msir_fetch_more_data( struct usb_skel* dev, int dont_block ) +{ + int retries = 0; + int words_to_read = + (sizeof(dev->lircdata)/sizeof(lirc_t)) - dev->lirccnt; + int partial, this_read = 0; + int bulkidx = 0; + int bytes_left_in_packet = 0; + signed char* signedp = (signed char*)dev->bulk_in_buffer; + + if( words_to_read == 0 ) + return dev->lirccnt; + + /* this forces all existing data to be read by lirc before we + * issue another usb command. this is the only form of + * throttling we have + */ + if( dev->lirccnt ) + { + return dev->lirccnt; + } + + /* reserve room for our leading space */ + if( dev->last_space ) + words_to_read--; + + while( words_to_read ) + { + /* handle signals and USB disconnects */ + if( signal_pending(current) ) + { + return dev->lirccnt ? dev->lirccnt : -EINTR; + } + if( !dev->udev ) + { + return -ENODEV; + } + + bulkidx = 0; + + /* + * perform data read (phys or from previous buffer) + */ + + /* use leftovers if present, otherwise perform a read */ + if( dev->usb_valid_bytes_in_bulk_buffer ) + { + this_read = partial = + dev->usb_valid_bytes_in_bulk_buffer; + dev->usb_valid_bytes_in_bulk_buffer = 0; + } + else + { + int retval; + + this_read = dev->bulk_in_size; + partial = 0; + retval = usb_bulk_msg + (dev->udev, + usb_rcvbulkpipe + (dev->udev, dev->bulk_in_endpointAddr), + (unsigned char*)dev->bulk_in_buffer, + this_read, &partial, HZ*10); + + /* retry a few times on overruns; map all + other errors to -EIO */ + if( retval ) + { + if( retval == -EOVERFLOW && + retries < 5 ) + { + retries++; + interruptible_sleep_on_timeout + ( &dev->wait_q, HZ ); + continue; + } + else + { + return -EIO; + } + } + + retries = 0; + if( partial ) + this_read = partial; + + /* skip the header */ + bulkidx += 2; + + /* check for empty reads (header only) */ + if( this_read == 2 ) + { + /* assume no data */ + if( dont_block ) + { + break; + } + + /* sleep for a bit before performing + another read */ + interruptible_sleep_on_timeout + ( &dev->wait_q, 1 ); + continue; + } + } + + /* + * process data + */ + + /* at this point this_read is > 0 */ + while( bulkidx < this_read && + (words_to_read > (dev->last_space ? 1 : 0)) ) + //while( bulkidx < this_read && words_to_read ) + { + int keycode; + int pulse = 0; + + /* read packet length if needed */ + if( !bytes_left_in_packet ) + { + + /* we assume we are on a packet length + * value. it is possible, in some + * cases, to get a packet that does + * not start with a length, apparently + * due to some sort of fragmenting, + * but occaisonally we do not receive + * the second half of a fragment + */ + bytes_left_in_packet = + 128 + signedp[bulkidx++]; + + /* unfortunately rather than keep all + * the data in the packetized format, + * the transceiver sends a trailing 8 + * bytes that aren't part of the + * transmittion from the remote, + * aren't packetized, and dont really + * have any value. we can basically + * tell we have hit them if 1) we have + * a loooong space currently stored + * up, and 2) the bytes_left value for + * this packet is obviously wrong + */ + if( bytes_left_in_packet > 4 ) + { + if( dev->mce_bytes_left_in_packet ) + { + bytes_left_in_packet = dev->mce_bytes_left_in_packet; + bulkidx--; + } + bytes_left_in_packet = 0; + bulkidx = this_read; + } + + /* always clear this if we have a + valid packet */ + dev->mce_bytes_left_in_packet = 0; + + /* continue here to verify we haven't + hit the end of the bulk_in */ + continue; + + } + + /* + * generate mode2 + */ + + keycode = signedp[bulkidx++]; + if( keycode < 0 ) + { + pulse = 1; + keycode += 128; + } + keycode *= MCE_TIME_UNIT; + + bytes_left_in_packet--; + + if( pulse ) + { + if( dev->last_space ) + { + dev->lircdata[dev->lirccnt++] = + dev->last_space; + dev->last_space = 0; + words_to_read--; + + /* clear the lirc_t for the pulse */ + dev->lircdata[dev->lirccnt] = 0; + } + dev->lircdata[dev->lirccnt] += keycode; + dev->lircdata[dev->lirccnt] |= PULSE_BIT; + } + else + { + /* on pulse->space transition, add one + for the existing pulse */ + if( dev->lircdata[dev->lirccnt] && + !dev->last_space ) + { + dev->lirccnt++; + words_to_read--; + } + + dev->last_space += keycode; + } + } + } + + /* save off some info if we are exiting mid-packet, or with + leftovers */ + if( bytes_left_in_packet ) + { + dev->mce_bytes_left_in_packet = bytes_left_in_packet; + } + if( bulkidx < this_read ) + { + dev->usb_valid_bytes_in_bulk_buffer = (this_read - bulkidx); + memcpy( dev->bulk_in_buffer, &(dev->bulk_in_buffer[bulkidx]), + dev->usb_valid_bytes_in_bulk_buffer ); + } + return dev->lirccnt; +} + +/* mceusb_add_to_buf: called by lirc_dev to fetch all available keys + * this is used as a polling interface for us: since we set + * plugin->sample_rate we will periodically get the below call to + * check for new data returns 0 on success, or -ENODATA if nothing is + * available + */ +static int mceusb_add_to_buf(void* data, struct lirc_buffer* buf ) +{ + struct usb_skel* dev = (struct usb_skel*) data; + + down( &dev->sem ); + + /* verify device still present */ + if( dev->udev == NULL ) + { + up( &dev->sem ); + return -ENODEV; + } + + if( !dev->lirccnt ) + { + int res; + dev->lircidx = 0; + + res = msir_fetch_more_data( dev, 1 ); + + if( res == 0 ) + res = -ENODATA; + if( res < 0 ) { + up( &dev->sem ); + return res; + } + } + + if( dev->lirccnt ) + { + int keys_to_copy; + + /* determine available buffer space and available data */ + keys_to_copy = lirc_buffer_available( buf ); + if( keys_to_copy > dev->lirccnt ) + { + keys_to_copy = dev->lirccnt; + } + + lirc_buffer_write_n( buf, (unsigned char*) &(dev->lircdata[dev->lircidx]), keys_to_copy ); + dev->lircidx += keys_to_copy; + dev->lirccnt -= keys_to_copy; + + up( &dev->sem ); + return 0; + } + + up( &dev->sem ); + return -ENODATA; +} + +/** + * mceusb_write_bulk_callback + */ +#ifdef KERNEL_2_5 +static void mceusb_write_bulk_callback (struct urb *urb, struct pt_regs *regs) +#else +static void mceusb_write_bulk_callback (struct urb *urb) +#endif +{ + struct usb_skel *dev = (struct usb_skel *)urb->context; + + dbg("%s - minor %d", __func__, dev->minor); + + if ((urb->status != -ENOENT) && + (urb->status != -ECONNRESET)) { + dbg("%s - nonzero write buld status received: %d\n", __func__, 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. + */ +#ifdef KERNEL_2_5 +static int mceusb_probe(struct usb_interface *interface, const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(interface); + struct usb_host_interface *iface_desc; +#else +static void * mceusb_probe(struct usb_device *udev, unsigned int ifnum, + const struct usb_device_id *id) +{ + struct usb_interface *interface = &udev->actconfig->interface[ifnum]; + struct usb_interface_descriptor *iface_desc; +#endif + struct usb_skel *dev = NULL; + struct usb_endpoint_descriptor *endpoint; + + struct lirc_plugin* plugin; + struct lirc_buffer* rbuf; + + int minor; + size_t buffer_size; + int i; + int retval = -ENOMEM; + + /* 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)) { + dbg("Wrong Vendor/Product IDs"); +#ifdef KERNEL_2_5 + return -ENODEV; +#else + return NULL; +#endif + } + + /* 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 error; + } + + /* allocate memory for our device state and initialize it */ + dev = kmalloc (sizeof(struct usb_skel), GFP_KERNEL); + if (dev == NULL) { + err ("Out of memory"); +#ifdef KERNEL_2_5 + retval = -ENOMEM; +#endif + goto error; + } + minor_table[minor] = dev; + + memset (dev, 0x00, sizeof (*dev)); + init_MUTEX (&dev->sem); + dev->udev = udev; + dev->interface = interface; + dev->minor = minor; + + /* set up the endpoint information */ + /* check out the endpoints */ + /* use only the first bulk-in and bulk-out endpoints */ +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,4) + iface_desc = interface->cur_altsetting; +#else + iface_desc = &interface->altsetting[0]; +#endif + +#ifdef KERNEL_2_5 + for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { + endpoint = &iface_desc->endpoint[i].desc; +#else + for (i = 0; i < iface_desc->bNumEndpoints; ++i) { + endpoint = &iface_desc->endpoint[i]; +#endif + if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) && + ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == + USB_ENDPOINT_XFER_BULK)) { + dbg("we found a bulk in endpoint"); + buffer_size = endpoint->wMaxPacketSize; + dev->bulk_in_size = buffer_size; + dev->bulk_in_endpointAddr = endpoint->bEndpointAddress; +#ifdef KERNEL_2_5 + dev->bulk_in_buffer = usb_buffer_alloc + (udev, buffer_size, SLAB_ATOMIC, &dev->dma_in); +#else + dev->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL); +#endif + if (!dev->bulk_in_buffer) { + err("Couldn't allocate bulk_in_buffer"); + goto error; + } + } + + if (((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == 0x00) && + ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == + USB_ENDPOINT_XFER_BULK)) { + dbg("we found a bulk out endpoint"); +#ifdef KERNEL_2_5 + dev->write_urb = usb_alloc_urb(0, GFP_KERNEL); +#else + dev->write_urb = usb_alloc_urb(0); +#endif + 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; +#ifdef KERNEL_2_5 + dev->bulk_out_buffer = usb_buffer_alloc(udev, buffer_size, SLAB_ATOMIC, &dev->dma_out); +#else + dev->bulk_out_buffer = kmalloc (buffer_size, GFP_KERNEL); +#endif + if (!dev->bulk_out_buffer) { + err("Couldn't allocate bulk_out_buffer"); + goto error; + } +#ifdef KERNEL_2_5 + usb_fill_bulk_urb(dev->write_urb, udev, + usb_sndbulkpipe + (udev, endpoint->bEndpointAddress), + dev->bulk_out_buffer, buffer_size, + mceusb_write_bulk_callback, dev); +#else + FILL_BULK_URB(dev->write_urb, udev, + usb_sndbulkpipe + (udev, endpoint->bEndpointAddress), + dev->bulk_out_buffer, buffer_size, + mceusb_write_bulk_callback, dev); +#endif + } + } + + if (!(dev->bulk_in_endpointAddr && dev->bulk_out_endpointAddr)) { + err("Couldn't find both bulk-in and bulk-out endpoints"); + goto error; + } + + /* init the waitq */ + init_waitqueue_head( &dev->wait_q ); + + + /* 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), 128)) { + err("out of memory"); + kfree( plugin ); + kfree( rbuf ); + goto error; + } + + strcpy(plugin->name, DRIVER_NAME " "); + plugin->minor = minor; + plugin->code_length = sizeof(lirc_t) * 8; + 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->sample_rate = 80; // sample at 100hz (10ms) + plugin->add_to_buf = &mceusb_add_to_buf; + // plugin->fops = &mceusb_fops; + if( lirc_register_plugin(plugin) < 0 ) + { + kfree( plugin ); + lirc_buffer_free( rbuf ); + kfree( rbuf ); + goto error; + } + dev->plugin = plugin; + + /* 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 ); + mceusb_setup( udev ); + +#ifdef KERNEL_2_5 + /* we can register the device now, as it is ready */ + usb_set_intfdata (interface, dev); +#endif + /* 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); + up (&minor_table_mutex); +#ifdef KERNEL_2_5 + return 0; +#else + return dev; +#endif + error: + mceusb_delete (dev); + dev = NULL; + dbg("%s: retval = %x",__func__,retval); + up (&minor_table_mutex); +#ifdef KERNEL_2_5 + return retval; +#else + return NULL; +#endif +} + +/** + * mceusb_disconnect + * + * Called by the usb core when the device is removed from the system. + * + * This routine guarantees that the driver will not submit any more urbs + * by clearing dev->udev. It is also supposed to terminate any currently + * active urbs. Unfortunately, usb_bulk_msg(), used in skel_read(), does + * not provide any way to do this. But at least we can cancel an active + * write. + */ +#ifdef KERNEL_2_5 +static void mceusb_disconnect(struct usb_interface *interface) +#else +static void mceusb_disconnect(struct usb_device *udev, void *ptr) +#endif +{ + struct usb_skel *dev; + int minor; +#ifdef KERNEL_2_5 + dev = usb_get_intfdata (interface); + usb_set_intfdata (interface, NULL); +#else + dev = (struct usb_skel *)ptr; +#endif + + down (&minor_table_mutex); + down (&dev->sem); + minor = dev->minor; + + /* unhook lirc things */ + lirc_unregister_plugin( minor ); + lirc_buffer_free( dev->plugin->rbuf ); + kfree( dev->plugin->rbuf ); + kfree( dev->plugin ); +#ifdef KERNEL_2_5 + /* terminate an ongoing write */ + if (atomic_read (&dev->write_busy)) { + usb_unlink_urb (dev->write_urb); + wait_for_completion (&dev->write_finished); + } + + /* prevent device read, write and ioctl */ + dev->present = 0; +#endif + + mceusb_delete (dev); + + info("Microsoft IR Transceiver #%d now disconnected", minor); + up (&dev->sem); + up (&minor_table_mutex); +} + + + +/** + * usb_mceusb_init + */ +static int __init usb_mceusb_init(void) +{ + int result; + + /* register this driver with the USB subsystem */ + result = usb_register(&mceusb_driver); +#ifdef KERNEL_2_5 + if ( result ) { +#else + if ( result < 0 ) { +#endif + err("usb_register failed for the " DRIVER_NAME " driver. error number %d",result); +#ifdef KERNEL_2_5 + return result; +#else + return -1; +#endif + } + + 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 -uNr linux-2.6.8-rc4.orig/drivers/char/lirc/lirc_parallel.c linux-2.6.8-rc4/drivers/char/lirc/lirc_parallel.c --- linux-2.6.8-rc4.orig/drivers/char/lirc/lirc_parallel.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.8-rc4/drivers/char/lirc/lirc_parallel.c 2004-05-16 11:14:39.000000000 +0200 @@ -0,0 +1,762 @@ +/* $Id$ */ + +/**************************************************************************** + ** 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 *********************** + ***********************************************************************/ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 2, 18) +#error "**********************************************************" +#error " Sorry, this driver needs kernel version 2.2.18 or higher " +#error "**********************************************************" +#endif + +#include + +#ifdef CONFIG_SMP +#error "--- Sorry, this driver is not SMP safe. ---" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include "kcompat.h" +#include "lirc_dev.h" + +#include "lirc_parallel.h" + +#define LIRC_DRIVER_NAME "lirc_parallel" + +/*********************************************************************** + ************************* Globale Variablen *********************** + ***********************************************************************/ + +unsigned int irq = CONFIG_LIRC_IRQ_PARALLEL; +unsigned int io = CONFIG_LIRC_PORT_PARALLEL; +#ifdef CONFIG_LIRC_TIMER +unsigned int timer = 0; +unsigned int default_timer = CONFIG_LIRC_TIMER; +#endif + +#define WBUF_SIZE (256) +#define RBUF_SIZE (256) /* this must be a power of 2 larger than 1 */ + +static lirc_t wbuf[WBUF_SIZE]; +static lirc_t rbuf[RBUF_SIZE]; + +DECLARE_WAIT_QUEUE_HEAD(lirc_wait); + +unsigned int rptr=0,wptr=0; +unsigned int lost_irqs=0; +int is_open=0; + +struct parport *pport; +struct pardevice *ppdevice; +int is_claimed=0; + +/*********************************************************************** + ************************* 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_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(!MOD_IN_USE) + 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 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_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 LIRC_TIMER + if(timer==0) /* try again if device is ready */ + { + timer=init_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; + + MOD_INC_USE_COUNT; + 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; + MOD_DEC_USE_COUNT; + 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 + MOD_INC_USE_COUNT; +#endif + return 0; +} + +static void set_use_dec(void* data) +{ +#if WE_DONT_USE_LOCAL_OPEN_CLOSE + MOD_DEC_USE_COUNT; +#endif +} + +static struct lirc_plugin plugin = { + name: LIRC_DRIVER_NAME, + minor: -1, + code_length: 1, + sample_rate: 0, + data: NULL, + add_to_buf: NULL, + get_queue: NULL, + set_use_inc: set_use_inc, + set_use_dec: set_use_dec, + fops: &lirc_fops, +}; + +#ifdef MODULE + +MODULE_AUTHOR("Christoph Bartelmus"); +MODULE_DESCRIPTION("Infrared receiver driver for parallel ports."); +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +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)"); + +#ifndef KERNEL_2_5 +EXPORT_NO_SYMBOLS; +#endif + +int pf(void *handle); +void kf(void *handle); + +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); + */ +} + +/*********************************************************************** + ****************** init_module()/cleanup_module() ****************** + ***********************************************************************/ + +int init_module(void) +{ +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 3) + pport=parport_find_base(io); +#else + pport=parport_enumerate(); + while(pport!=NULL) + { + if(pport->base==io) + { + break; + } + pport=pport->next; + } +#endif + if(pport==NULL) + { + printk(KERN_NOTICE "%s: no port at %x found\n", + LIRC_DRIVER_NAME,io); + return(-ENXIO); + } + ppdevice=parport_register_device(pport,LIRC_DRIVER_NAME, + pf,kf,irq_handler,0,NULL); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 3) + parport_put_port(pport); +#endif + 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 LIRC_TIMER +# ifdef DEBUG + out(LIRC_PORT_DATA,LIRC_PORT_DATA_BIT); +# endif + + timer=init_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); +} + +void cleanup_module(void) +{ + if(MOD_IN_USE) return; + parport_unregister_device(ppdevice); + lirc_unregister_plugin(plugin.minor); +} +#endif diff -uNr linux-2.6.8-rc4.orig/drivers/char/lirc/lirc_parallel.h linux-2.6.8-rc4/drivers/char/lirc/lirc_parallel.h --- linux-2.6.8-rc4.orig/drivers/char/lirc/lirc_parallel.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.8-rc4/drivers/char/lirc/lirc_parallel.h 2001-06-23 18:59:16.000000000 +0200 @@ -0,0 +1,24 @@ +/* $Id$ */ + +#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 -uNr linux-2.6.8-rc4.orig/drivers/char/lirc/lirc_sasem.c linux-2.6.8-rc4/drivers/char/lirc/lirc_sasem.c --- linux-2.6.8-rc4.orig/drivers/char/lirc/lirc_sasem.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.8-rc4/drivers/char/lirc/lirc_sasem.c 2004-06-19 08:29:09.000000000 +0200 @@ -0,0 +1,453 @@ +/* lirc_sasem.c - USB remote support for LIRC + * Version 0.1 [beta status] + * + * Copyright (C) 2004 Oliver Stabel + * + * This driver was derived from: + * Paul Miller 's 2003-2004 + * "lirc_atiusb - USB remote support for LIRC" + * Culver Consulting Services 's 2003 + * "Sasem OnAir VFD/IR USB driver" + * + * + * 2004/06/13 - 1st version + * + * TODO + * - keypresses seem to be rather sluggish sometimes; check + * intervall and timing + * - simulate USBLCD device to work with LCDProc + * - include fs operations + * - analyse LCD command set + * - check USB Minor allocation + * - param to enable/disable LIRC communication (??) + * + */ + +/* + * 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_sasem.h" + +#include +#include "lirc_dev.h" + +MODULE_AUTHOR( DRIVER_AUTHOR ); +MODULE_DESCRIPTION( DRIVER_DESC ); +MODULE_LICENSE("GPL"); + +static int debug = 0; + +MODULE_PARM(debug, "i"); +MODULE_PARM_DESC(debug, "enable debug = 1, disable = 0 (default)"); + +static t_usb_device_id s_sasemID [] = { + { USB_DEVICE(0x11ba, 0x0101) }, + { } +}; +MODULE_DEVICE_TABLE (usb, s_sasemID); + +static t_usb_driver s_SasemDriver = +{ + owner: THIS_MODULE, + name: "Sasem", + probe: s_sasemProbe, + disconnect: s_sasemDisconnect, + minor: SASEM_MINOR, + id_table: s_sasemID, +}; + +static int __init s_sasemInit (void) +{ + printk(BANNER); + + if (usb_register(&s_SasemDriver)) { + printk("USB registration failed"); + return -ENOSYS; + } + + return 0; +} + +static void __exit s_sasemExit (void) +{ + usb_deregister (&s_SasemDriver); +} + +module_init (s_sasemInit); +module_exit (s_sasemExit); + +static void * s_sasemProbe(t_usb_device *p_dev, unsigned p_iInterfaceNum, + const t_usb_device_id *p_id) +{ + t_sasemDevice *l_sasemDevice = NULL; + t_usb_endpoint_descriptor *l_endpoint; + t_usb_interface_descriptor *l_currentInterfaceDescriptor; + int l_iPipe; + int l_iDevnum; + t_lirc_plugin *l_lircPlugin = NULL; + t_lirc_buffer *l_lircBuffer = NULL; + int l_iLircMinor = -1; + int l_iMemFailure; + char l_cBuf[63], l_cName[128]=""; + + if (debug) printk("onair probe\n"); + + l_currentInterfaceDescriptor = p_dev->actconfig->interface-> + altsetting; + l_endpoint = l_currentInterfaceDescriptor->endpoint; + + if (!(l_endpoint->bEndpointAddress & 0x80) || + ((l_endpoint->bmAttributes & 3) != 0x03)) { + printk("OnAir config endpoint error"); + return NULL; + } + + l_iDevnum = p_dev->devnum; + + l_iMemFailure = 0; + if (!(l_sasemDevice = kmalloc(sizeof(t_sasemDevice), GFP_KERNEL))) { + printk("kmalloc(sizeof(t_sasemDevice), GFP_KERNEL)) failed"); + l_iMemFailure = 1; + } + else { + memset(l_sasemDevice, 0, sizeof(t_sasemDevice)); + if (!(l_lircPlugin = kmalloc(sizeof(t_lirc_plugin), GFP_KERNEL))) { + printk("kmalloc(sizeof(t_lirc_plugin), GFP_KERNEL)) failed"); + l_iMemFailure = 2; + } + else if (!(l_lircBuffer = kmalloc(sizeof(t_lirc_buffer), GFP_KERNEL))) { + printk("kmalloc(sizeof(t_lirc_buffer), GFP_KERNEL)) failed"); + l_iMemFailure = 3; + } + else if (lirc_buffer_init(l_lircBuffer, MAX_INTERRUPT_DATA, 4)) { + printk("lirc_buffer_init failed"); + l_iMemFailure = 4; + } + else if (!(l_sasemDevice->m_urbIn = usb_alloc_urb(0))) { + printk("usb_alloc_urb(0) failed"); + l_iMemFailure = 5; + } else { + + memset(l_lircPlugin, 0, sizeof(t_lirc_plugin)); + strcpy(l_lircPlugin->name, DRIVER_NAME " "); + l_lircPlugin->minor = -1; + l_lircPlugin->code_length = MAX_INTERRUPT_DATA*8; + l_lircPlugin->features = LIRC_CAN_REC_LIRCCODE; + l_lircPlugin->data = l_sasemDevice; + + l_lircPlugin->rbuf = l_lircBuffer; + l_lircPlugin->set_use_inc = &s_lirc_set_use_inc; + l_lircPlugin->set_use_dec = &s_lirc_set_use_dec; + + if ((l_iLircMinor = lirc_register_plugin(l_lircPlugin)) < 0) { + printk("lirc_register_plugin(l_lircPlugin)) failed"); + l_iMemFailure = 9; + } + } + } + switch (l_iMemFailure) { + case 9: + usb_free_urb(l_sasemDevice->m_urbIn); + case 5: + case 4: + kfree(l_lircBuffer); + case 3: + kfree(l_lircPlugin); + case 2: + kfree(l_sasemDevice); + case 1: + return NULL; + } + + l_lircPlugin->minor = l_iLircMinor; + + init_MUTEX(&l_sasemDevice->m_semLock); + down_interruptible(&l_sasemDevice->m_semLock); + l_sasemDevice->m_descriptorIn = l_endpoint; + l_sasemDevice->m_device = p_dev; + l_sasemDevice->m_lircPlugin = l_lircPlugin; + up(&l_sasemDevice->m_semLock); + + l_iPipe = usb_rcvintpipe(l_sasemDevice->m_device, + l_sasemDevice->m_descriptorIn-> + bEndpointAddress); + + usb_fill_int_urb(l_sasemDevice->m_urbIn, l_sasemDevice->m_device, + l_iPipe, l_sasemDevice->m_cBufferIn, + sizeof(l_sasemDevice->m_cBufferIn), + s_sasemCallbackIn, l_sasemDevice, + l_sasemDevice->m_descriptorIn->bInterval); + + if (p_dev->descriptor.iManufacturer && + usb_string(p_dev, p_dev->descriptor.iManufacturer, l_cBuf, 63) > 0) + { + strncpy(l_cName, l_cBuf, 128); + } + if (p_dev->descriptor.iProduct && + usb_string(p_dev, p_dev->descriptor.iProduct, l_cBuf, 63) > 0) + { + snprintf(l_cName, 128, "%s %s", l_cName, l_cBuf); + } + printk(DRIVER_NAME "[%d]: %s on usb%d\n", l_iDevnum, l_cName, + p_dev->bus->busnum); + + return l_sasemDevice; +} + + +static void s_sasemDisconnect(t_usb_device *p_dev, void *p_ptr) { + t_sasemDevice *l_sasemDevice = p_ptr; + if (debug) printk("s_sasemDisconnect\n"); + + down_interruptible(&l_sasemDevice->m_semLock); + usb_unlink_urb(l_sasemDevice->m_urbIn); + usb_free_urb(l_sasemDevice->m_urbIn); + s_unregister_from_lirc(l_sasemDevice); + up(&l_sasemDevice->m_semLock); + kfree (l_sasemDevice); +} + +static void s_sasemCallbackIn(t_urb *p_urb) +{ + t_sasemDevice *l_sasemDevice; + int l_iDevnum; + int l_iLen; + char l_cBuf[MAX_INTERRUPT_DATA]; + int i; + + if (debug) printk("s_sasemCallbackIn\n"); + + if (!p_urb) + { + return; + } + + if (!(l_sasemDevice = p_urb->context)) { + usb_unlink_urb(p_urb); + return; + } + + l_iDevnum = l_sasemDevice->m_iDevnum; + if (debug) { + printk(DRIVER_NAME "[%d]: data received (length %d)\n", + l_iDevnum, p_urb->actual_length); + printk(DRIVER_NAME + " intr_callback called %x %x %x %x %x %x %x %x\n", + l_sasemDevice->m_cBufferIn[0], + l_sasemDevice->m_cBufferIn[1], + l_sasemDevice->m_cBufferIn[2], + l_sasemDevice->m_cBufferIn[3], + l_sasemDevice->m_cBufferIn[4], + l_sasemDevice->m_cBufferIn[5], + l_sasemDevice->m_cBufferIn[6], + l_sasemDevice->m_cBufferIn[7]); + } + + switch (p_urb->status) { + + /* success */ + case 0: + l_iLen = p_urb->actual_length; + if (l_iLen > MAX_INTERRUPT_DATA) return; + + memcpy(l_cBuf,p_urb->transfer_buffer,l_iLen); + + // is this needed? The OnAir device should always + // return 8 bytes + for (i = l_iLen; i < MAX_INTERRUPT_DATA; i++) l_cBuf[i] = 0; + + // the OnAir device seems not to be able to signal a + // pressed button by repeating its code. Keeping a + // button pressed first sends the real code (e.g. 0C + // 80 7F 41 BE 00 00 00) and then keeps sending 08 00 + // 00 00 00 00 00 00 as long as the button is pressed + // (notice that in the real key code 80 = !7F and 41 = + // !BE is this important? maybe for validation?) maybe + // 08 00 00 00 00 00 00 00 is the number of presses? + // who knows ... + // so lets do the following: if a code != the 08 code + // arrives, store it to repeat it if necessary for + // LIRC. If an 08 code follows afterwards, send the + // old code again to the buffer do this as long as the + // 08 code is being sent + // example: + // Code from Remote Lirc Buffer + // 0C 80 7F 41 BE 00 00 00 0C 80 7F 41 BE 00 00 00 + // 08 00 00 00 00 00 00 00 0C 80 7F 41 BE 00 00 00 + // 08 00 00 00 00 00 00 00 0C 80 7F 41 BE 00 00 00 + // 08 00 00 00 00 00 00 00 0C 80 7F 41 BE 00 00 00 + // 08 00 00 00 00 00 00 00 0C 80 7F 41 BE 00 00 00 + // 0C 80 7F 40 BF 00 00 00 0C 80 7F 40 BF 00 00 00 + // 08 00 00 00 00 00 00 00 0C 80 7F 40 BF 00 00 00 + // 08 00 00 00 00 00 00 00 0C 80 7F 40 BF 00 00 00 + // 0C 80 7F 41 BE 00 00 00 0C 80 7F 41 BE 00 00 00 + + if (memcmp(l_cBuf, sc_cSasemCode, MAX_INTERRUPT_DATA) == 0) { + // the repeat code is being sent, so we copy + // the old code to LIRC + if (l_sasemDevice->m_iCodeSaved != 0) { + memcpy(l_cBuf, &l_sasemDevice->m_cLastCode, + MAX_INTERRUPT_DATA); + } + // there was no old code so what to do? + else { + // TODO + } + } + else + { + // save the current valid code for repeats + memcpy(&l_sasemDevice->m_cLastCode, l_cBuf, + MAX_INTERRUPT_DATA); + // set flag to signal a valid code was save; + // just for safety reasons + l_sasemDevice->m_iCodeSaved = 1; + } + + /* copy 1 code to lirc_buffer */ + lirc_buffer_write_1(l_sasemDevice->m_lircPlugin->rbuf, + l_cBuf); + wake_up(&l_sasemDevice->m_lircPlugin->rbuf->wait_poll); + break; + + /* unlink */ + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + usb_unlink_urb(p_urb); + return; + } + + /* resubmit urb */ + usb_submit_urb(p_urb); +} + +static int s_unregister_from_lirc(t_sasemDevice *p_sasemDevice) { + t_lirc_plugin *l_lircPlugin = p_sasemDevice->m_lircPlugin; + int l_iDevnum; + int l_iReturn; + + l_iDevnum = p_sasemDevice->m_iDevnum; + if (debug) printk(DRIVER_NAME "[%d]: unregister from lirc called\n", + l_iDevnum); + + if ((l_iReturn = lirc_unregister_plugin(l_lircPlugin->minor)) > 0) { + printk(DRIVER_NAME "[%d]: error in lirc_unregister minor: %d\n" + "Trying again...\n", l_iDevnum, l_lircPlugin->minor); + if (l_iReturn == -EBUSY) { + printk(DRIVER_NAME "[%d]: device is opened, " + "will unregister on close\n", l_iDevnum); + return -EAGAIN; + } + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(HZ); + + if ((l_iReturn = lirc_unregister_plugin(l_lircPlugin->minor)) > 0) { + printk(DRIVER_NAME "[%d]: lirc_unregister failed\n", + l_iDevnum); + } + } + + if (l_iReturn != 0) { + printk(DRIVER_NAME "[%d]: didn't free resources\n", + l_iDevnum); + return -EAGAIN; + } + + printk(DRIVER_NAME "[%d]: usb remote disconnected\n", l_iDevnum); + + lirc_buffer_free(l_lircPlugin->rbuf); + kfree(l_lircPlugin->rbuf); + kfree(l_lircPlugin); + return 0; +} + +static int s_lirc_set_use_inc(void *p_data) +{ + t_sasemDevice *l_sasemDevice = p_data; + int l_iDevnum; + + if (!l_sasemDevice) { + printk(DRIVER_NAME "[?]: s_lirc_set_use_inc called with no context\n"); + return -EIO; + } + + l_iDevnum = l_sasemDevice->m_iDevnum; + if (debug) printk(DRIVER_NAME "[%d]: s_lirc_set_use_inc\n", + l_iDevnum); + + if (!l_sasemDevice->m_iConnected) { + + /* + this is the trigger from LIRC to start + transfering data so the URB is being submitted + */ + + if (!l_sasemDevice->m_device) + return -ENOENT; + + /* set USB device in URB */ + l_sasemDevice->m_urbIn->dev = l_sasemDevice->m_device; + + /* start communication by submitting URB */ + if (usb_submit_urb(l_sasemDevice->m_urbIn)) { + printk(DRIVER_NAME "[%d]: open result = -EIO error " + "submitting urb\n", l_iDevnum); + return -EIO; + } + + /* indicate that URB has been submitted */ + l_sasemDevice->m_iConnected = 1; + } + + return 0; +} + +static void s_lirc_set_use_dec(void *p_data) { + t_sasemDevice *l_sasemDevice = p_data; + int l_iDevnum; + + if (!l_sasemDevice) { + printk(DRIVER_NAME "[?]: s_lirc_set_use_dec called with no context\n"); + return; + } + + l_iDevnum = l_sasemDevice->m_iDevnum; + if (debug) printk(DRIVER_NAME "[%d]: s_lirc_set_use_dec\n", + l_iDevnum); + + if (l_sasemDevice->m_iConnected) { + + /* + URB has been submitted before so it can be unlinked + */ + + down_interruptible(&l_sasemDevice->m_semLock); + usb_unlink_urb(l_sasemDevice->m_urbIn); + l_sasemDevice->m_iConnected = 0; + up(&l_sasemDevice->m_semLock); + } +} diff -uNr linux-2.6.8-rc4.orig/drivers/char/lirc/lirc_sasem.h linux-2.6.8-rc4/drivers/char/lirc/lirc_sasem.h --- linux-2.6.8-rc4.orig/drivers/char/lirc/lirc_sasem.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.8-rc4/drivers/char/lirc/lirc_sasem.h 2004-06-19 08:34:35.000000000 +0200 @@ -0,0 +1,70 @@ +/* + * Version Information + */ +#define DRIVER_VERSION "v0.1" +#define DATE "June 2004" +#define DRIVER_AUTHOR "Oliver Stabel " +#define DRIVER_DESC "USB Driver for Sasem Remote Controller V1.1" +#define DRIVER_SHORTDESC "Sasem" +#define DRIVER_NAME "lirc_sasem" + +#define BANNER \ + KERN_INFO DRIVER_SHORTDESC " " DRIVER_VERSION " (" DATE ")\n" \ + KERN_INFO " by " DRIVER_AUTHOR "\n" + +static const char longbanner[] = { + DRIVER_DESC ", " DRIVER_VERSION " (" DATE "), by " DRIVER_AUTHOR +}; + +#define MAX_INTERRUPT_DATA 8 +#define SASEM_MINOR 144 + +static const char sc_cSasemCode[MAX_INTERRUPT_DATA] = + { 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +typedef struct usb_driver t_usb_driver, *tp_usb_driver; +typedef struct usb_device t_usb_device, *tp_usb_device; +typedef struct usb_interface t_usb_interface, *tp_usb_interface; +typedef struct usb_device_id t_usb_device_id, *tp_usb_device_id; +typedef struct usb_host_interface t_usb_host_interface, + *tp_usb_host_interface; +typedef struct usb_interface_descriptor t_usb_interface_descriptor, + *tp_usb_interface_descriptor; +typedef struct usb_endpoint_descriptor t_usb_endpoint_descriptor, + *tp_usb_endpoint_descriptor; +typedef struct urb t_urb, *tp_urb; + +typedef struct semaphore t_semaphore, *tp_semaphore; + +typedef struct lirc_plugin t_lirc_plugin, *tp_lirc_plugin; +typedef struct lirc_buffer t_lirc_buffer; + +struct sasemDevice { + t_usb_device *m_device; + t_usb_endpoint_descriptor *m_descriptorIn; + t_usb_endpoint_descriptor *m_descriptorOut; + t_urb *m_urbIn; + t_urb *m_urbOut; + unsigned int m_iInterfaceNum; + int m_iDevnum; + unsigned char m_cBufferIn[MAX_INTERRUPT_DATA]; + t_semaphore m_semLock; + + char m_cLastCode[MAX_INTERRUPT_DATA]; + int m_iCodeSaved; + + /* lirc */ + t_lirc_plugin *m_lircPlugin; + int m_iConnected; +}; + +typedef struct sasemDevice t_sasemDevice, *tp_sasemDevice; + +static void* s_sasemProbe(t_usb_device *p_dev, unsigned p_iInterfaceNum, + const t_usb_device_id *p_id); +static void s_sasemDisconnect(t_usb_device *p_dev, void *p_ptr); +static void s_sasemCallbackIn(t_urb *p_urb); + +static int s_unregister_from_lirc(t_sasemDevice *p_sasemDevice); +static int s_lirc_set_use_inc(void *p_data); +static void s_lirc_set_use_dec(void *p_data); diff -uNr linux-2.6.8-rc4.orig/drivers/char/lirc/lirc_serial.c linux-2.6.8-rc4/drivers/char/lirc/lirc_serial.c --- linux-2.6.8-rc4.orig/drivers/char/lirc/lirc_serial.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.8-rc4/drivers/char/lirc/lirc_serial.c 2004-04-24 21:59:07.000000000 +0200 @@ -0,0 +1,1100 @@ +/* $Id$ */ + +/**************************************************************************** + ** 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 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 +*/ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 2, 18) +#error "**********************************************************" +#error " Sorry, this driver needs kernel version 2.2.18 or higher " +#error "**********************************************************" +#endif + +#include + +#ifndef 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 +#include + +#include +#include "kcompat.h" +#include "lirc_dev.h" + +#if defined(LIRC_SERIAL_SOFTCARRIER) && !defined(LIRC_SERIAL_TRANSMITTER) +#warning "Software carrier only affects transmitting" +#endif + +#if defined(rdtsc) + +#define USE_RDTSC +#warning "Note: using rdtsc instruction" +#endif + +#ifdef LIRC_SERIAL_ANIMAX +#ifdef LIRC_SERIAL_TRANSMITTER +#warning "******************************************" +#warning " This receiver does not have a " +#warning " transmitter diode " +#warning "******************************************" +#endif +#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 LIRC_SERIAL_IRDEO +int type=LIRC_IRDEO; +#elif defined(LIRC_SERIAL_IRDEO_REMOTE) +int type=LIRC_IRDEO_REMOTE; +#elif defined(LIRC_SERIAL_ANIMAX) +int type=LIRC_ANIMAX; +#elif defined(LIRC_SERIAL_IGOR) +int type=LIRC_IGOR; +#else +int type=LIRC_HOMEBREW; +#endif + +#ifdef LIRC_SERIAL_SOFTCARRIER +int softcarrier=1; +#else +int softcarrier=0; +#endif + +/* 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 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 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 int sense = -1; /* -1 = auto, 0 = active high, 1 = active low */ + +static spinlock_t lirc_lock = SPIN_LOCK_UNLOCKED; + +static int io = CONFIG_LIRC_PORT_SERIAL; +static int irq = CONFIG_LIRC_IRQ_SERIAL; + +static struct timeval lasttv = {0, 0}; + +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 LIRC_SERIAL_TRANSMITTER_LATENCY 450 + +#else + +/* does anybody have information on other platforms ? */ +/* 256 = 1<<8 */ +#define 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; +#ifdef DEBUG + printk(KERN_INFO LIRC_DRIVER_NAME + ": 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); +#endif + 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<= + LIRC_SERIAL_TRANSMITTER_LATENCY) return(-EINVAL); + if(256*1000000L/new_freq*(100-new_duty_cycle)/100<= + 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; +#ifdef DEBUG + printk(KERN_WARNING LIRC_DRIVER_NAME + ": in init_timing_params, freq=%d pulse=%ld, " + "space=%ld\n", freq, pulse_width, space_width); +#endif + 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 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)+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 */ + { +# ifdef DEBUG + printk(KERN_WARNING LIRC_DRIVER_NAME ": Buffer overrun\n"); +# endif + 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) + { +#ifdef DEBUG + printk(KERN_WARNING LIRC_DRIVER_NAME + ": AIEEEE: %d %d %lx %lx %lx %lx\n", + dcd,sense, + tv.tv_sec,lasttv.tv_sec, + tv.tv_usec,lasttv.tv_usec); +#endif + 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; + } + + local_irq_restore(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"); + }; + + return 0; +} + +static int set_use_inc(void* data) +{ + int result; + unsigned long flags; + + spin_lock(&lirc_lock); + if(MOD_IN_USE) + { + spin_unlock(&lirc_lock); + return -EBUSY; + } + + /* Init read buffer. */ + if (lirc_buffer_init(&rbuf, sizeof(lirc_t), RBUF_LEN) < 0) + return -ENOMEM; + + /* 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); + lirc_buffer_free(&rbuf); + return -EBUSY; + case -EINVAL: + printk(KERN_ERR LIRC_DRIVER_NAME + ": Bad irq number or handler\n"); + spin_unlock(&lirc_lock); + lirc_buffer_free(&rbuf); + return -EINVAL; + default: +# ifdef DEBUG + printk(KERN_INFO LIRC_DRIVER_NAME + ": Interrupt %d, port %04x obtained\n", irq, io); +# endif + 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); + + MOD_INC_USE_COUNT; + spin_unlock(&lirc_lock); + return 0; +} + +static void set_use_dec(void* data) +{ unsigned long flags; + + local_irq_save(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))); + local_irq_restore(flags); + + free_irq(irq, NULL); +# ifdef DEBUG + printk(KERN_INFO LIRC_DRIVER_NAME ": freed IRQ %d\n", irq); +# endif + lirc_buffer_free(&rbuf); + + MOD_DEC_USE_COUNT; +} + +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); + local_irq_save(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: +# ifdef DEBUG + printk(KERN_WARNING LIRC_DRIVER_NAME ": SET_SEND_CARRIER\n"); +# endif + if(!(hardware[type].features&LIRC_CAN_SET_SEND_CARRIER)) + { + return(-ENOIOCTLCMD); + } + + result=get_user(ivalue,(unsigned int *) arg); + if(result) return(result); + if(ivalue>500000 || ivalue<20000) return(-EINVAL); + return init_timing_params(duty_cycle, ivalue); + break; + + default: + return(-ENOIOCTLCMD); + } + return(0); +} + +static struct file_operations lirc_fops = +{ + write: lirc_write, +}; + +static struct lirc_plugin plugin = { + name: LIRC_DRIVER_NAME, + minor: -1, + code_length: 1, + sample_rate: 0, + data: NULL, + add_to_buf: NULL, + get_queue: NULL, + rbuf: &rbuf, + set_use_inc: set_use_inc, + set_use_dec: set_use_dec, + ioctl: lirc_ioctl, + fops: &lirc_fops, +}; + +#ifdef MODULE + +MODULE_AUTHOR("Ralph Metzler, Trent Piepho, Ben Pfaff, Christoph Bartelmus"); +MODULE_DESCRIPTION("Infra-red receiver driver for serial ports."); +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +MODULE_PARM(type, "i"); +MODULE_PARM_DESC(type, "Hardware type (0 = home-brew, 1 = IRdeo," + " 2 = IRdeo Remote, 3 = AnimaX, 4 = IgorPlug"); + +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)"); + + +#ifndef KERNEL_2_5 +EXPORT_NO_SYMBOLS; +#endif + +int init_module(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; + 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; +} + +void cleanup_module(void) +{ + release_region(io, 8); + lirc_unregister_plugin(plugin.minor); +# ifdef DEBUG + printk(KERN_INFO LIRC_DRIVER_NAME ": cleaned up module\n"); +# endif +} + +#endif diff -uNr linux-2.6.8-rc4.orig/drivers/char/lirc/lirc_sir.c linux-2.6.8-rc4/drivers/char/lirc/lirc_sir.c --- linux-2.6.8-rc4.orig/drivers/char/lirc/lirc_sir.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.8-rc4/drivers/char/lirc/lirc_sir.c 2004-04-27 20:28:16.000000000 +0200 @@ -0,0 +1,1321 @@ +/* + * 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 +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 2, 18) +#error "**********************************************************" +#error " Sorry, this driver needs kernel version 2.2.18 or higher " +#error "**********************************************************" +#endif +#include + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#if !defined(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 LIRC_ON_SA1100 +#include +#ifdef CONFIG_SA1100_COLLIE +#include +#include +#endif +#endif + +#include + +#include +#include "lirc_dev.h" +#include "kcompat.h" + +/* SECTION: Definitions */ + +/**************************** Tekram dongle ***************************/ +#ifdef 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 LIRC_SIR_ACTISYS_ACT200L +static void init_act200(void); +#endif + +/******************************* SA1100 ********************************/ +#ifdef 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 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 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 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 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 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 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 (MOD_IN_USE) { + spin_unlock(&dev_lock); + return -EBUSY; + } + MOD_INC_USE_COUNT; + spin_unlock(&dev_lock); + return 0; +} + +static int lirc_close(struct inode * inode, struct file *file) +{ + MOD_DEC_USE_COUNT; + 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 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 LIRC_ON_SA1100 + /* disable receiver */ + Ser2UTCR3=0; +#endif + local_irq_save(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++; + } + local_irq_restore(flags); +#ifdef 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 LIRC_ON_SA1100 + unsigned int ivalue; +#endif + +#ifdef 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(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 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 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 + MOD_INC_USE_COUNT; +#endif + return 0; +} + +static void set_use_dec(void* data) +{ +#if WE_DONT_USE_LOCAL_OPEN_CLOSE + MOD_DEC_USE_COUNT; +#endif +} +static struct lirc_plugin plugin = { + name: LIRC_DRIVER_NAME, + minor: -1, + code_length: 1, + sample_rate: 0, + data: NULL, + add_to_buf: NULL, + get_queue: NULL, + set_use_inc: set_use_inc, + set_use_dec: set_use_dec, + fops: &lirc_fops, +}; + + +#ifdef MODULE +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); +} +#endif + +/* 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 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 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_RETVAL(IRQ_HANDLED); +} + +#ifdef 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) +{ + unsigned long flags; + + spin_lock_irqsave(&hardware_lock, flags); + /* reset UART */ +#ifdef 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(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 LIRC_SIR_ACTISYS_ACT200L + init_act200(); +#endif +#endif + spin_unlock_irqrestore(&hardware_lock, flags); + return 0; +} + +static void drop_hardware(void) +{ + unsigned long flags; + + spin_lock_irqsave(&hardware_lock, flags); + +#ifdef 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 LIRC_ON_SA1100 + /* get I/O port access and IRQ line */ + retval = check_region(io, 8); + if (retval < 0) { + printk(KERN_ERR LIRC_DRIVER_NAME + ": i/o port 0x%.4x already in use.\n", + io); + return retval; + } +#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; + } +#ifndef LIRC_ON_SA1100 + request_region(io, 8, LIRC_DRIVER_NAME); + printk(KERN_INFO LIRC_DRIVER_NAME + ": I/O port 0x%.4x, IRQ %d.\n", + io, irq); +#endif + + 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 LIRC_ON_SA1100 + release_region(io, 8); +#endif +} + +#ifdef 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 MODULE + +#ifdef LIRC_SIR_TEKRAM +MODULE_AUTHOR("Christoph Bartelmus"); +MODULE_DESCRIPTION("Infrared receiver driver for Tekram Irmate 210"); +#elif defined(LIRC_ON_SA1100) +MODULE_AUTHOR("Christoph Bartelmus"); +MODULE_DESCRIPTION("LIRC driver for StrongARM SA1100 embedded microprocessor"); +#elif defined(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 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 + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +#ifndef KERNEL_2_5 +EXPORT_NO_SYMBOLS; +#endif + +int init_module(void) +{ + int retval; + + retval=init_chrdev(); + if(retval < 0) + return retval; + retval = init_lirc_sir(); + if (retval) { + drop_chrdev(); + return retval; + } + return 0; +} + +void cleanup_module(void) +{ + drop_hardware(); + drop_chrdev(); + drop_port(); + printk(KERN_INFO LIRC_DRIVER_NAME ": Uninstalled.\n"); +} +#endif diff -uNr linux-2.6.8-rc4.orig/drivers/char/lirc/Makefile linux-2.6.8-rc4/drivers/char/lirc/Makefile --- linux-2.6.8-rc4.orig/drivers/char/lirc/Makefile 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.8-rc4/drivers/char/lirc/Makefile 2004-08-10 10:22:06.189171224 +0200 @@ -0,0 +1,11 @@ +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_SASEM) += lirc_sasem.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 -uNr linux-2.6.8-rc4.orig/drivers/char/Makefile linux-2.6.8-rc4/drivers/char/Makefile --- linux-2.6.8-rc4.orig/drivers/char/Makefile 2004-08-10 09:33:45.000000000 +0200 +++ linux-2.6.8-rc4/drivers/char/Makefile 2004-08-10 10:23:05.725120384 +0200 @@ -7,7 +7,7 @@ # FONTMAPFILE = cp437.uni -obj-y += mem.o random.o tty_io.o n_tty.o tty_ioctl.o +obj-y += mem.o random.o tty_io.o n_tty.o tty_ioctl.o lirc/ obj-$(CONFIG_VT) += vt_ioctl.o vc_screen.o consolemap.o \ consolemap_deftbl.o selection.o keyboard.o diff -uNr linux-2.6.8-rc4.orig/include/linux/lirc.h linux-2.6.8-rc4/include/linux/lirc.h --- linux-2.6.8-rc4.orig/include/linux/lirc.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.8-rc4/include/linux/lirc.h 2003-01-26 13:57:59.000000000 +0100 @@ -0,0 +1,100 @@ +/* $Id$ */ + +#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) + +#endif diff -uNr linux-2.6.8-rc4.orig/drivers/char/lirc/kcompat.h linux-2.6.8-rc4/drivers/char/lirc/kcompat.h --- linux-2.6.8-rc4.orig/drivers/char/lirc/kcompat.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.8-rc4/drivers/char/lirc/kcompat.h 2004-04-27 20:52:33.000000000 +0200 @@ -0,0 +1,74 @@ +/* $Id$ */ + +#include +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 3, 0) +#include +#include +static inline void del_timer_sync(struct timer_list * timerlist) +{ + start_bh_atomic(); + del_timer(timerlist); + end_bh_atomic(); +} +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0) +#undef daemonize +#define daemonize(name) do { \ + \ + lock_kernel(); \ + \ + exit_mm(current); \ + exit_files(current); \ + exit_fs(current); \ + current->session = 1; \ + current->pgrp = 1; \ + current->euid = 0; \ + current->tty = NULL; \ + sigfillset(¤t->blocked); \ + \ + strcpy(current->comm, name); \ + \ + unlock_kernel(); \ + \ +} while (0) + +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0) +#define KERNEL_2_5 + +#undef MOD_INC_USE_COUNT +#define MOD_INC_USE_COUNT try_module_get(THIS_MODULE) +#undef MOD_DEC_USE_COUNT +#define MOD_DEC_USE_COUNT module_put(THIS_MODULE) + +#endif + +#ifndef IRQ_RETVAL +typedef void irqreturn_t; +#define IRQ_NONE +#define IRQ_HANDLED +#define IRQ_RETVAL(x) +#endif + +#ifndef MOD_IN_USE +#ifdef CONFIG_MODULE_UNLOAD +#define MOD_IN_USE module_refcount(THIS_MODULE) +#else +#error "LIRC modules currently require" +#error " 'Loadable module support ---> Module unloading'" +#error "to be enabled in the kernel" +#endif +#endif + +#if !defined(local_irq_save) +#define local_irq_save(flags) do{ save_flags(flags);cli(); } while(0) +#endif +#if !defined(local_irq_restore) +#define local_irq_restore(flags) do{ restore_flags(flags); } while(0) +#endif + +#if !defined(pci_pretty_name) +#define pci_pretty_name(dev) ((dev)->name) +#endif