lkml.org 
[lkml]   [2008]   [Sep]   [9]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 18/18] lirc driver for the zilog/haupauge IR transceiver
    Date
    ---
    diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig
    index 5f9d860..2ba0904 100644
    --- a/drivers/input/Kconfig
    +++ b/drivers/input/Kconfig
    @@ -170,6 +170,8 @@ source "drivers/input/tablet/Kconfig"

    source "drivers/input/touchscreen/Kconfig"

    +source "drivers/input/lirc/Kconfig"
    +
    source "drivers/input/misc/Kconfig"

    endif
    diff --git a/drivers/input/Makefile b/drivers/input/Makefile
    index 98c4f9a..4dcb852 100644
    --- a/drivers/input/Makefile
    +++ b/drivers/input/Makefile
    @@ -25,3 +25,5 @@ obj-$(CONFIG_INPUT_MISC) += misc/
    obj-$(CONFIG_INPUT_APMPOWER) += apm-power.o

    obj-$(CONFIG_XEN_KBDDEV_FRONTEND) += xen-kbdfront.o
    +
    +obj-$(CONFIG_INPUT_LIRC) += lirc/
    diff --git a/drivers/input/lirc/Kconfig b/drivers/input/lirc/Kconfig
    new file mode 100644
    index 0000000..47183ee
    --- /dev/null
    +++ b/drivers/input/lirc/Kconfig
    @@ -0,0 +1,142 @@
    +#
    +# LIRC driver(s) configuration
    +#
    +menuconfig INPUT_LIRC
    + bool "Linux Infrared Remote Control IR receiver/transmitter drivers"
    + default n
    + help
    + Say Y here, and all supported Linux Infrared Remote Control IR and
    + RF receiver and transmitter drivers will be displayed. When paired
    + with a remote control and the lirc daemon, the receiver drivers
    + allow control of your Linux system via remote control.
    +
    +if INPUT_LIRC
    +
    +config LIRC_DEV
    + tristate "LIRC device loadable module support"
    + default m
    + help
    + LIRC device loadable module support, required for most LIRC drivers
    +
    +config LIRC_ATIUSB
    + tristate "ATI RF USB Receiver support"
    + default m
    + depends on LIRC_DEV
    + help
    + Driver for the ATI USB RF remote receiver
    +
    +config LIRC_BT829
    + tristate "BT829 based hardware"
    + default m
    + depends on LIRC_DEV
    + help
    + Driver for the IR interface on BT829-based hardware
    +
    +config LIRC_CMDIR
    + tristate "CommandIR USB Transceiver"
    + default m
    + depends on LIRC_DEV
    + help
    + Driver for the CommandIR USB Transceiver
    +
    +config LIRC_I2C
    + tristate "I2C Based IR Receivers"
    + default m
    + depends on LIRC_DEV
    + help
    + Driver for I2C-based IR receivers, such as those commonly
    + found onboard Hauppauge PVR-150/250/350 video capture cards
    +
    +config LIRC_IGORPLUGUSB
    + tristate "Igor Cesko's USB IR Receiver"
    + default m
    + depends on LIRC_DEV
    + help
    + Driver for Igor Cesko's USB IR Receiver
    +
    +config LIRC_IMON
    + tristate "Soundgraph IMON Receiver"
    + default m
    + depends on LIRC_DEV
    + help
    + Driver for the Soundgraph IMON IR Receiver
    +
    +config LIRC_IT87
    + tristate "ITE IT87XX CIR Port Receiver"
    + default m
    + depends on LIRC_DEV
    + help
    + Driver for the ITE IT87xx IR Receiver
    +
    +config LIRC_ITE8709
    + tristate "ITE8709 CIR Port Receiver"
    + default m
    + depends on LIRC_DEV && PNP
    + help
    + Driver for the ITE8709 IR Receiver
    +
    +config LIRC_MCEUSB
    + tristate "Microsoft Media Center Ed. Receiver, v1"
    + default m
    + depends on LIRC_DEV
    + help
    + Driver for the Microsoft Media Center Ed. Receiver, v1
    +
    +config LIRC_MCEUSB2
    + tristate "Microsoft Media Center Ed. Receiver, v2"
    + default m
    + depends on LIRC_DEV
    + help
    + Driver for the Microsoft Media Center Ed. Receiver, v2
    +
    +config LIRC_PARALLEL
    + tristate "Homebrew Parallel Port Receiver"
    + default m
    + depends on LIRC_DEV && !SMP
    + help
    + Driver for Homebrew Parallel Port Receivers
    +
    +config LIRC_SASEM
    + tristate "Sasem USB IR Remote"
    + default m
    + depends on LIRC_DEV
    + help
    + Driver for the Sasem OnAir Remocon-V or Dign HV5 HTPC IR/VFD Module
    +
    +config LIRC_SERIAL
    + tristate "Homebrew Serial Port Receiver"
    + default m
    + depends on LIRC_DEV
    + help
    + Driver for Homebrew Serial Port Receivers
    +
    +config LIRC_SIR
    + tristate "Built-in SIR IrDA port"
    + default m
    + depends on LIRC_DEV
    + help
    + Driver for the SIR IrDA port
    +
    +config LIRC_STREAMZAP
    + tristate "Streamzap PC Receiver"
    + default m
    + depends on LIRC_DEV
    + help
    + Driver for the Streamzap PC Receiver
    +
    +config LIRC_TTUSBIR
    + tristate "Technotrend USB IR Receiver"
    + default m
    + depends on LIRC_DEV
    + help
    + Driver for the Technotrend USB IR Receiver
    +
    +config LIRC_ZILOG
    + tristate "Zilog/Hauppauge IR Transmitter"
    + default m
    + depends on LIRC_DEV
    + help
    + Driver for the Zilog/Hauppauge IR Transmitter, found on
    + PVR-150/500, HVR-1200/1250/1700/1800, HD-PVR and other cards
    +
    +endif
    diff --git a/drivers/input/lirc/Makefile b/drivers/input/lirc/Makefile
    new file mode 100644
    index 0000000..411db5e
    --- /dev/null
    +++ b/drivers/input/lirc/Makefile
    @@ -0,0 +1,25 @@
    +# Makefile for the lirc drivers.
    +#
    +
    +# Each configuration option enables a list of files.
    +
    +EXTRA_CFLAGS =-DIRCTL_DEV_MAJOR=61 -DLIRC_SERIAL_TRANSMITTER -I$(src)
    +
    +obj-$(CONFIG_LIRC_DEV) += lirc_dev.o
    +obj-$(CONFIG_LIRC_ATIUSB) += lirc_atiusb.o
    +obj-$(CONFIG_LIRC_BT829) += lirc_bt829.o
    +obj-$(CONFIG_LIRC_CMDIR) += lirc_cmdir.o
    +obj-$(CONFIG_LIRC_I2C) += lirc_i2c.o
    +obj-$(CONFIG_LIRC_IGORPLUGUSB) += lirc_igorplugusb.o
    +obj-$(CONFIG_LIRC_IMON) += lirc_imon.o
    +obj-$(CONFIG_LIRC_IT87) += lirc_it87.o
    +obj-$(CONFIG_LIRC_ITE8709) += lirc_ite8709.o
    +obj-$(CONFIG_LIRC_MCEUSB) += lirc_mceusb.o
    +obj-$(CONFIG_LIRC_MCEUSB2) += lirc_mceusb2.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_STREAMZAP) += lirc_streamzap.o
    +obj-$(CONFIG_LIRC_TTUSBIR) += lirc_ttusbir.o
    +obj-$(CONFIG_LIRC_ZILOG) += lirc_zilog.o
    diff --git a/drivers/input/lirc/commandir.c b/drivers/input/lirc/commandir.c
    new file mode 100644
    index 0000000..a05b0d6
    --- /dev/null
    +++ b/drivers/input/lirc/commandir.c
    @@ -0,0 +1,982 @@
    +
    +/*
    + *
    + * Hardware Driver for COMMANDIR USB Transceiver
    + * 2005-2007 InnovationOne - Matt Bodkin, Evelyn Yeung
    + *
    + * Version 1.4.2
    + * For 2.4.* or 2.6.* kernel versions
    + * Based on the USB Skeleton driver, versions 0.7 and 2.0
    + *
    + */
    +
    +#include <linux/version.h>
    +#include <linux/autoconf.h>
    +
    +#include <linux/kernel.h>
    +#include <linux/errno.h>
    +#include <linux/init.h>
    +#include <linux/slab.h>
    +#include <linux/module.h>
    +#include <linux/usb.h>
    +#include <linux/smp_lock.h>
    +#include "commandir.h"
    +
    +#include <linux/kref.h>
    +#include <linux/uaccess.h>
    +
    +#define DRIVER_VERSION "v1.1.2"
    +#define DRIVER_AUTHOR "Evelyn Yeung, InnovationOne"
    +#define DRIVER_DESC "CommandIR USB Transceiver Driver"
    +
    +#define USB_CMDIR_VENDOR_ID 0x10c4
    +#define USB_CMDIR_PRODUCT_ID 0x0003
    +#define USB_CMDIR_MINOR_BASE 192
    +
    +/* table of devices that work with this driver */
    +static struct usb_device_id cmdir_table[] =
    +{
    + { USB_DEVICE(USB_CMDIR_VENDOR_ID, USB_CMDIR_PRODUCT_ID) },
    + { } /* Terminating entry */
    +};
    +MODULE_DEVICE_TABLE(usb, cmdir_table);
    +
    +/* circular packet queue */
    +unsigned char ourbuffers[QUEUELENGTH][64];
    +int waitusecs[QUEUELENGTH];
    +int ourbufferlengths[QUEUELENGTH];
    +int nexttosend;
    +int nexttofill;
    +int send_status = SEND_IDLE;
    +int last_tx_sec;
    +int last_tx_usec;
    +
    +static int curTXFill;
    +struct timeval tp;
    +
    +int debug_commandir;
    +
    +/* Structure to hold all of our device specific stuff */
    +struct usb_skel {
    + struct usb_device *udev; /* the usb device for this device */
    + struct usb_interface *interface; /* the interface for this device */
    + unsigned char *bulk_in_buffer; /* the buffer to receive data */
    + size_t bulk_in_size; /* the size of the receive buffer */
    + __u8 bulk_in_endpointAddr; /* the address of the bulk in endpoint */
    + __u8 bulk_out_endpointAddr; /* the address of the bulk out endpoint */
    + struct kref kref;
    +};
    +#define to_skel_dev(d) container_of(d, struct usb_skel, kref)
    +
    +static struct file_operations cmdir_fops = {
    + .read = cmdir_file_read,
    + .write = cmdir_file_write,
    + .open = cmdir_open,
    + .release = cmdir_release,
    +};
    +
    +/* usb class driver info in order to get a minor number from the usb core,
    + * and to have the device registered with devfs and the driver core */
    +static struct usb_class_driver cmdir_class = {
    + .name = "usb/commandir%d",
    + .fops = &cmdir_fops,
    + /* .mode = S_IFCHR | S_IRUSR | S_IWUSR |
    + * S_IRGRP | S_IWGRP | S_IROTH, */
    + .minor_base = USB_CMDIR_MINOR_BASE,
    +};
    +
    +static struct usb_driver cmdir_driver = {
    + .name = "commandir",
    + .probe = cmdir_probe,
    + .disconnect = cmdir_disconnect,
    + .id_table = cmdir_table,
    +};
    +
    +
    +static int lcd_device;
    +static int rx_device;
    +static int def_device;
    +
    +#define DEFAULT_TRANSMITTERS 0x0F
    +static unsigned int transmitters = DEFAULT_TRANSMITTERS;
    +static unsigned int next_transmitters = DEFAULT_TRANSMITTERS;
    +
    +#define CMDIR_VAR_LEN 68
    +static char cmdir_var[] =
    +"COMMANDIRx:\n TX Enabled: 1, 2, 3, 4\n RX: commandirx\n LCD: commandirx";
    +
    +
    +static void cmdir_delete(struct kref *kref)
    +{
    + struct usb_skel *dev = to_skel_dev(kref);
    +
    + usb_put_dev(dev->udev);
    + kfree(dev->bulk_in_buffer);
    + kfree(dev);
    +}
    +
    +static int __init usb_cmdir_init(void)
    +{
    + int result;
    +
    + /* register this driver with the USB subsystem */
    + result = usb_register(&cmdir_driver);
    +
    + if (result)
    + err("usb_register failed. Error number %d", result);
    +
    + return result;
    +}
    +
    +static int cmdir_open(struct inode *inode, struct file *file)
    +{
    + struct usb_skel *dev;
    + struct usb_interface *interface;
    + int subminor;
    + int retval = 0;
    +
    + subminor = iminor(inode);
    + interface = usb_find_interface(&cmdir_driver, subminor);
    + if (!interface) {
    + err("%s - error, can't find device for minor %d",
    + __func__, subminor);
    + retval = -ENODEV;
    + goto exit;
    + }
    +
    + dev = usb_get_intfdata(interface);
    + if (!dev) {
    + retval = -ENODEV;
    + goto exit;
    + }
    +
    + kref_get(&dev->kref); /* increment our usage count for the device */
    + file->private_data = dev; /* save object in file's private structure */
    +
    +exit:
    + return retval;
    +}
    +
    +static int cmdir_probe(struct usb_interface *interface,
    + const struct usb_device_id *id)
    +{
    + struct usb_skel *dev = NULL;
    + struct usb_host_interface *iface_desc;
    + struct usb_endpoint_descriptor *endpoint;
    + size_t buffer_size;
    +
    + int i;
    + int retval = -ENOMEM;
    + int minor;
    +
    + /* allocate memory for our device state and initialize it */
    + dev = kmalloc(sizeof(*dev), GFP_KERNEL);
    + if (dev == NULL) {
    + err("Out of memory");
    + goto error;
    + }
    + memset(dev, 0x00, sizeof(*dev));
    + kref_init(&dev->kref);
    + dev->udev = usb_get_dev(interface_to_usbdev(interface));
    + dev->interface = interface;
    +
    + /* set up the endpoint information */
    + /* use only the first bulk-in and bulk-out endpoints */
    + iface_desc = interface->cur_altsetting;
    + for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
    + endpoint = &iface_desc->endpoint[i].desc;
    +
    + if (!dev->bulk_in_endpointAddr &&
    + (endpoint->bEndpointAddress & USB_DIR_IN) &&
    + ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
    + == USB_ENDPOINT_XFER_BULK)) {
    + /* we found a bulk in endpoint */
    + buffer_size = endpoint->wMaxPacketSize;
    + dev->bulk_in_size = buffer_size;
    + dev->bulk_in_endpointAddr = endpoint->bEndpointAddress;
    + dev->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL);
    + if (!dev->bulk_in_buffer) {
    + err("Could not allocate bulk_in_buffer");
    + goto error;
    + }
    + }
    +
    + if (!dev->bulk_out_endpointAddr &&
    + !(endpoint->bEndpointAddress & USB_DIR_IN) &&
    + ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
    + == USB_ENDPOINT_XFER_BULK)) {
    + /* we found a bulk out endpoint */
    + dev->bulk_out_endpointAddr = endpoint->bEndpointAddress;
    + }
    + }
    + if (!(dev->bulk_in_endpointAddr && dev->bulk_out_endpointAddr)) {
    + err("Could not find both bulk-in and bulk-out endpoints");
    + goto error;
    + }
    +
    + /* save our data pointer in this interface device */
    + usb_set_intfdata(interface, dev);
    +
    + /* we can register the device now, as it is ready */
    + retval = usb_register_dev(interface, &cmdir_class);
    + if (retval) {
    + /* something prevented us from registering this driver */
    + err("Not able to get a minor for this device.");
    + usb_set_intfdata(interface, NULL);
    + goto error;
    + }
    +
    + /* check whether minor already includes base */
    + minor = interface->minor;
    + if (minor >= USB_CMDIR_MINOR_BASE)
    + minor = minor-USB_CMDIR_MINOR_BASE;
    +
    + /* let the user know what node this device is now attached to */
    + info("CommandIR USB device now attached to commandir%d", minor);
    +
    + reset_cmdir(minor);
    +
    + return 0;
    +
    +error:
    + if (dev)
    + kref_put(&dev->kref, cmdir_delete);
    + return retval;
    +}
    +
    +
    +static void cmdir_disconnect(struct usb_interface *interface)
    +{
    + struct usb_skel *dev;
    + int minor = interface->minor;
    +
    + /* prevent cmdir_open() from racing cmdir_disconnect() */
    + lock_kernel();
    +
    + dev = usb_get_intfdata(interface);
    + usb_set_intfdata(interface, NULL);
    +
    + /* give back our minor */
    + usb_deregister_dev(interface, &cmdir_class);
    +
    + unlock_kernel();
    +
    + /* decrement our usage count */
    + kref_put(&dev->kref, cmdir_delete);
    +
    + /* check whether minor already includes base */
    + if (minor >= USB_CMDIR_MINOR_BASE)
    + minor = minor-USB_CMDIR_MINOR_BASE;
    +
    + info("CommandIR #%d now disconnected", minor);
    +
    + /* check if default RX device still exists */
    + if (minor == rx_device) {
    + /* decrement until find next valid device */
    + while (rx_device > 0) {
    + rx_device--;
    + if (cmdir_check(rx_device) == 0)
    + break;
    + }
    + if (minor > 0)
    + info("Active Receiver is on CommandIR #%d", rx_device);
    + }
    +}
    +
    +static int cmdir_release(struct inode *inode, struct file *file)
    +{
    + struct usb_skel *dev;
    + int retval = 0;
    +
    + dev = (struct usb_skel *)file->private_data;
    + if (dev == NULL)
    + /*dbg(" - object is NULL");*/
    + return -ENODEV;
    +
    + /* decrement the count on our device */
    + kref_put(&dev->kref, cmdir_delete);
    + return retval;
    +}
    +
    +static void __exit usb_cmdir_exit(void)
    +{
    + /* deregister this driver with the USB subsystem */
    + usb_deregister(&cmdir_driver);
    +
    +}
    +
    +static int cmdir_check(int device_num)
    +{
    + struct usb_interface *interface;
    +
    + interface = usb_find_interface(&cmdir_driver,
    + USB_CMDIR_MINOR_BASE+device_num);
    + if (!interface) {
    + /* also check without adding base, for devfs */
    + interface = usb_find_interface(&cmdir_driver, rx_device);
    + if (!interface)
    + return -ENODEV;
    + }
    + return 0;
    +}
    +
    +static void init_cmdir_var(int device_num)
    +{
    + int i;
    + unsigned int multiplier = 1;
    +
    + for (i = 0; i < device_num; i++)
    + multiplier = multiplier*0x10;
    + transmitters |= multiplier * 0x0F;
    + next_transmitters = transmitters;
    + info("commandir%d reset", device_num);
    + return;
    +}
    +
    +static void reset_cmdir(int device_num)
    +{
    + unsigned char ctrl_buffer[MCU_CTRL_SIZE];
    + int retval;
    + int i;
    +
    + ctrl_buffer[0] = RESET_HEADER;
    + for (i = 1; i < MCU_CTRL_SIZE; i++)
    + ctrl_buffer[i] = 'j';
    + retval = write_core(ctrl_buffer, MCU_CTRL_SIZE, NULL, device_num);
    +
    + init_cmdir_var(device_num);
    + print_cmdir(device_num);
    +
    + return;
    +}
    +
    +static void update_cmdir_string(int device_num)
    +{
    + int next_comma = 0;
    + int next_pos = 25;
    + unsigned int multiplier;
    + int i;
    +
    + /* cmdir_var[] = "COMMANDIRx:\n"
    + * " TX Enabled: 1, 2, 3, 4\n"
    + * " RX: commandirx\n"
    + * " LCD: commandirx\n" */
    +
    + cmdir_var[9] = ASCII0+device_num;
    + cmdir_var[50] = ASCII0+rx_device;
    + cmdir_var[67] = ASCII0+lcd_device;
    +
    + for (i = 25; i < 35; i++)
    + cmdir_var[i] = ' ';
    +
    + multiplier = 1;
    + for (i = 0; i < device_num; i++)
    + multiplier = multiplier*0x10;
    +
    + if (transmitters & (multiplier*0x01)) {
    + cmdir_var[next_pos] = '1';
    + next_pos += 3;
    + next_comma++;
    + }
    + if (transmitters & (multiplier*0x02)) {
    + cmdir_var[next_pos] = '2';
    + if (next_comma > 0)
    + cmdir_var[next_pos-2] = ',';
    + next_pos += 3;
    + next_comma++;
    + }
    + if (transmitters & (multiplier*0x04)) {
    + cmdir_var[next_pos] = '3';
    + if (next_comma > 0)
    + cmdir_var[next_pos-2] = ',';
    + next_pos += 3;
    + next_comma++;
    + }
    + if (transmitters & (multiplier*0x08)) {
    + cmdir_var[next_pos] = '4';
    + if (next_comma > 0)
    + cmdir_var[next_pos-2] = ',';
    + next_pos += 3;
    + next_comma++;
    + }
    + return;
    +}
    +
    +static void print_cmdir(int device_num)
    +{
    + update_cmdir_string(device_num);
    + info("%s", cmdir_var);
    + return;
    +}
    +
    +static ssize_t cmdir_file_read(struct file *file, char *buffer,
    + size_t count, loff_t *ppos)
    +{
    + int retval = 0;
    + int minor = 0;
    + struct usb_skel *dev;
    +
    + dev = (struct usb_skel *)file->private_data;
    + minor = dev->interface->minor;
    + if (minor >= USB_CMDIR_MINOR_BASE)
    + minor = minor - USB_CMDIR_MINOR_BASE;
    +
    + if (((int)*ppos) == 0) {
    + update_cmdir_string(minor);
    + if (copy_to_user(buffer, cmdir_var, CMDIR_VAR_LEN))
    + retval = -EFAULT;
    + else
    + retval = CMDIR_VAR_LEN;
    + return retval;
    + } else
    + return 0;
    +}
    +
    +/* Read data from CommandIR */
    +ssize_t cmdir_read(unsigned char *buffer, size_t count)
    +{
    + struct usb_skel *dev;
    + int length, retval = 0;
    +
    + struct usb_interface *interface;
    + interface = usb_find_interface(&cmdir_driver,
    + USB_CMDIR_MINOR_BASE+rx_device);
    + if (!interface) {
    + /* also check without adding base, for devfs */
    + interface = usb_find_interface(&cmdir_driver, rx_device);
    + if (!interface)
    + return -ENODEV;
    + }
    + dev = usb_get_intfdata(interface);
    + if (!dev)
    + return -ENODEV;
    + retval = usb_bulk_msg(dev->udev, usb_rcvbulkpipe(dev->udev,
    + dev->bulk_in_endpointAddr),
    + dev->bulk_in_buffer, min(dev->bulk_in_size, count),
    + &length, HZ*10);
    + if (!retval) {
    + if (!memcpy(buffer, dev->bulk_in_buffer, length))
    + retval = -EFAULT;
    + else {
    + /* current status of the TX buffer */
    + curTXFill = buffer[2];
    + retval = length;
    + }
    + }
    + /* suppress errors */
    + /*
    + else {
    + err("Read from device failed, error %d",retval);
    + }
    + */
    + /* printk(KERN_INFO "CommandIR Reporting TX buffer at %d bytes. \n",
    + * curTXFill); */
    + return retval;
    +}
    +EXPORT_SYMBOL(cmdir_read);
    +
    +static ssize_t cmdir_file_write(struct file *file, const char *buffer,
    + size_t count, loff_t *ppos)
    +{
    + int retval;
    + int i;
    + int equalsign = 0;
    + int changeType = 0;
    + unsigned char ctrl_buffer[MCU_CTRL_SIZE];
    + char *local_buffer;
    + int minor;
    +
    + /* set as default - if non-specific error,
    + * won't keep calling this function */
    + retval = count;
    + local_buffer = kmalloc(count, GFP_KERNEL);
    +
    + /* verify that we actually have some data to write */
    + if (count == 0) {
    + err("Write request of 0 bytes");
    + goto exit;
    + }
    + if (count > 64) {
    + err("Input too long");
    + goto exit;
    + }
    +
    + /* copy the data from userspace into our local buffer */
    + if (copy_from_user(local_buffer, buffer, count)) {
    + retval = -EFAULT;
    + goto exit;
    + }
    +
    + /* parse code */
    + changeType = cNothing;
    + equalsign = 0;
    + for (i = 0; i < MCU_CTRL_SIZE; i++)
    + ctrl_buffer[i] = 'j';
    +
    + for (i = 0; i < count; i++) {
    + switch (local_buffer[i]) {
    + case 'X':
    + case 'x':
    + if ((i > 0) && ((local_buffer[i - 1] == 'R')
    + || (local_buffer[i - 1] == 'r')))
    + changeType = cRX;
    + break;
    + case 'S':
    + case 's':
    + if ((i > 1) && ((local_buffer[i - 1] == 'E')
    + || (local_buffer[i - 1] == 'e'))) {
    + if ((local_buffer[i-2] == 'R')
    + || (local_buffer[i-2] == 'r'))
    + changeType = cRESET;
    + }
    + break;
    + case 'L':
    + case 'l':
    + if ((i > 0) && ((local_buffer[i - 1] == 'F')
    + || (local_buffer[i - 1] == 'f')))
    + changeType = cFLASH;
    + break;
    + case 'C':
    + case 'c':
    + if ((i > 0) && ((local_buffer[i - 1] == 'L')
    + || (local_buffer[i - 1] == 'l')))
    + changeType = cLCD;
    + break;
    + case '=':
    + if (changeType != cNothing)
    + equalsign = i;
    + break;
    + case '0':
    + case '1':
    + case '2':
    + case '3':
    + case '4':
    + case '5':
    + case '6':
    + case '7':
    + if (equalsign > 0) {
    + minor = local_buffer[i] - ASCII0;
    + switch (changeType) {
    + case cRESET:
    + ctrl_buffer[0] = RESET_HEADER;
    + retval = write_core(ctrl_buffer,
    + MCU_CTRL_SIZE,
    + cmdir_write_bulk_callback,
    + minor);
    + if (retval != MCU_CTRL_SIZE) {
    + if (retval == -ENODEV)
    + err("Device %d "
    + "unplugged", minor);
    + else
    + err("Error on write to "
    + "%d", minor);
    + goto exit;
    + } else
    + retval = count;
    + init_cmdir_var(minor);
    + break;
    + case cFLASH:
    + ctrl_buffer[0] = FLASH_HEADER;
    + info("Flashing indicators on device %d",
    + minor);
    + retval = write_core(ctrl_buffer,
    + MCU_CTRL_SIZE,
    + cmdir_write_bulk_callback,
    + minor);
    + if (retval != MCU_CTRL_SIZE) {
    + if (retval == -ENODEV)
    + err("Device %d "
    + "unplugged", minor);
    + else
    + err("Error on write to "
    + "%d", minor);
    + goto exit;
    + } else
    + retval = count;
    + break;
    + case cRX:
    + rx_device = minor;
    + info("Default receiver set to %d",
    + minor);
    + break;
    + case cLCD:
    + lcd_device = minor;
    + info("commandir: Default LCD set to %d",
    + minor);
    + break;
    + default:
    + break;
    + }
    + }
    + break;
    + case ',':
    + equalsign = 0;
    + changeType = cNothing;
    + break;
    + default:
    + if ((equalsign > 0) && (local_buffer[i] > 32)) {
    + err("Non-numerical argument");
    + goto exit;
    + }
    + break;
    + }
    + }
    +
    + if ((changeType != cNothing) && (equalsign == 0))
    + err("No device specified");
    + if (changeType == cNothing)
    + err("Unknown command");
    +
    +exit:
    + kfree(local_buffer);
    + return retval;
    +}
    +
    +int cmdir_write(unsigned char *buffer, int count,
    + void *callback_fct, int usecdelay)
    +{
    + /* Always add to queue, then send queue number
    + * no locks
    + * mbodkin, Sept 8, 2005 */
    + int ret = 0;
    + if (debug_commandir == 1) {
    + do_gettimeofday(&tp);
    + printk(KERN_INFO "cmdir_write at %d\n", (int)tp.tv_usec);
    + }
    + ret = add_cmdir_queue(buffer, count, callback_fct, usecdelay);
    +
    + if (ret == -1) {
    + printk(KERN_INFO "cmdir_write returning 0\n");
    + return 0;
    + }
    + return count;
    +
    +}
    +EXPORT_SYMBOL(cmdir_write);
    +
    +int add_cmdir_queue(unsigned char *buffer, int count,
    + void *callback_vct, int usecdelay)
    +{
    + int ret = 0;
    + if ((nexttofill + 1) % (QUEUELENGTH - 1) == nexttosend) {
    +
    + /* our buffer is full */
    + printk(KERN_INFO "Too many packets backlogged "
    + "in CommandIR Queue.\n");
    + return -1;
    + }
    + /* go ahead and use this one: */
    + memcpy(ourbuffers[nexttofill], buffer, count);
    + ourbufferlengths[nexttofill] = count;
    + waitusecs[nexttofill] = (usecdelay == 0) ? 10000 : usecdelay;
    + /* printk(KERN_INFO "Adding %d to queue at position %d.\n",
    + * count, nexttofill); */
    + nexttofill = (nexttofill + 1) % (QUEUELENGTH - 1);
    + ret = nexttofill;
    + /* if (timer_running == 0) */
    + send_queue(); /* fake it if the timer's not running */
    + return ret; /* we accepted the full packet */
    +
    +}
    +
    +int send_queue()
    +{
    + int last_sent = 0;
    + int ret = 0;
    + if (debug_commandir == 1) {
    + do_gettimeofday(&tp);
    + printk(KERN_INFO "Send_queue() at %d\n", (int)tp.tv_usec);
    + }
    + /* initiate the send/callback routine if not already running. */
    + if (send_status == SEND_IDLE) {
    + if (!(nexttofill == nexttosend)) {
    + /* start it up: */
    +
    + last_sent = nexttosend - 1;
    + if (last_sent < 0)
    + last_sent = QUEUELENGTH - 1;
    + /* Final check - is it TIME to send this packet yet? */
    + /* if (wait_to_tx(waitusecs[last_sent]) == 0) { */
    + /* always send if there's room,
    + * otherwise wait until room */
    + if (curTXFill < 190) {
    + if (debug_commandir == 1) {
    + do_gettimeofday(&tp);
    + printk(KERN_INFO "Sending packet data "
    + "at %d\n", (int)tp.tv_usec);
    + }
    + ret = cmdir_write_queue(ourbuffers[nexttosend],
    + ourbufferlengths[nexttosend], NULL);
    + if (ret <= 0) {
    + /* send failed - the device is either
    + * unplugged or full
    + * nexttosend =
    + * (nexttosend + 1)
    + * % (QUEUELENGTH - 1); */
    + send_status = SEND_IDLE;
    + return 0; /*send_queue(); */
    + } else
    + nexttosend = (nexttosend + 1)
    + % (QUEUELENGTH - 1);
    + return 1;
    + } else {
    + if (debug_commandir == 1) {
    + do_gettimeofday(&tp);
    + printk(KERN_INFO "Not time to send yet "
    + "- starting timer at %d.\n",
    + (int)tp.tv_usec);
    + printk(KERN_INFO "Enabling timer.\n");
    + }
    + return 0; /* doesn't matter anymore */
    + }
    + } else {
    + if (debug_commandir == 1) {
    + do_gettimeofday(&tp);
    + printk(KERN_INFO "No more data to send %d!\n",
    + (int)tp.tv_usec);
    + }
    + last_tx_sec = 0; /* reset our TX counters */
    + last_tx_usec = 0;
    + return 1; /* nothing more to send! */
    + }
    + } else {
    + if (debug_commandir == 1)
    + /* will try again on the callback */
    + printk(KERN_INFO "Already sending\n");
    + return 1; /* then the timer shouldn't be running... */
    + }
    + return 0; /* should never get here... */
    +}
    +
    +
    +int wait_to_tx(int usecs)
    +{
    + /* don't return until last_time + usecs has been reached
    + * for non-zero last_tx's. */
    + int wait_until_sec = 0, wait_until_usec = 0;
    + int now_sec = 0, now_usec = 0;
    + if (debug_commandir == 1)
    + printk(KERN_INFO "waittotx(%d)\n", usecs);
    + if (usecs == 0)
    + return 0;
    +
    + if (!(last_tx_sec == 0 && last_tx_usec == 0)) {
    + /* calculate wait time: */
    + wait_until_sec = last_tx_sec + (usecs / 1000000);
    + wait_until_usec = last_tx_usec + usecs;
    +
    + do_gettimeofday(&tp);
    + now_sec = tp.tv_sec;
    + now_usec = tp.tv_usec;
    +
    + if (wait_until_usec > 1000000) {
    + /* we've spilled over to the next second. */
    + wait_until_sec++;
    + wait_until_usec -= 1000000;
    + /* printk(KERN_INFO "usec rollover\n"); */
    + }
    + if (debug_commandir == 1)
    + printk(KERN_INFO "Testing for the right second, now = "
    + "%d %d, wait = %d %d\n",
    + now_sec, now_usec,
    + wait_until_sec, wait_until_usec);
    + /* now we are always on the same second. */
    + if (now_sec > wait_until_sec) {
    + if (debug_commandir == 1)
    + printk(KERN_INFO "Setting last_tx_sec to %d.\n",
    + wait_until_sec);
    + last_tx_sec = wait_until_sec;
    + last_tx_usec = wait_until_usec;
    + return 0;
    + }
    +
    + if ((now_sec == wait_until_sec)
    + && (now_usec > wait_until_usec)) {
    + if (debug_commandir == 1)
    + printk(KERN_INFO "Setting last_tx_sec to %d.\n",
    + wait_until_sec);
    + last_tx_sec = wait_until_sec;
    + last_tx_usec = wait_until_usec;
    + return 0;
    + }
    + return -1; /* didn't send */
    + }
    +
    + do_gettimeofday(&tp);
    + last_tx_usec = tp.tv_usec;
    + last_tx_sec = tp.tv_sec;
    + return 0; /* if there's no last even, go ahead and send */
    +}
    +
    +
    +int cmdir_write_queue(unsigned char *buffer, int count, void *callback_fct)
    +{
    + int retval = count;
    + static char prev_signal_num;
    + unsigned char next_mask;
    + unsigned int multiplier;
    + int i;
    +
    + send_status = SEND_ACTIVE;
    +
    + if (count < 2) {
    + err("Not enough bytes (write request of %d bytes)", count);
    + return count;
    + }
    +
    + /* check data; decide which device to send to */
    + switch (buffer[0]) {
    + case TX_HEADER:
    + case TX_HEADER_NEW:
    + /* this is LIRC transmit data */
    + if (curTXFill >= 190) {
    + printk(KERN_INFO
    + "TX buffer too full to send more TX data\n");
    + return 0;
    + }
    + if (next_transmitters != transmitters) {
    + if (buffer[1] != prev_signal_num)
    + /* this is new signal; change transmitter mask*/
    + transmitters = next_transmitters;
    + }
    + prev_signal_num = buffer[1];
    +
    + multiplier = 1;
    + for (i = 0; i < MAX_DEVICES; i++) {
    + next_mask = 0;
    + if (transmitters & (0x01*multiplier))
    + next_mask |= TX1_ENABLE;
    + if (transmitters & (0x02*multiplier))
    + next_mask |= TX2_ENABLE;
    + if (transmitters & (0x04*multiplier))
    + next_mask |= TX3_ENABLE;
    + if (transmitters & (0x08*multiplier))
    + next_mask |= TX4_ENABLE;
    +
    + if (next_mask > 0) {
    + buffer[1] = next_mask;
    + retval = write_core(buffer, count,
    + callback_fct, i);
    + if (retval != count) {
    + if (retval == -ENODEV)
    + err("Device %d not plugged in",
    + i);
    + else
    + err("Write error to device %d",
    + i);
    + return retval;
    + }
    + }
    + multiplier = multiplier*0x10;
    + }
    + return retval;
    + break;
    + case LCD_HEADER:
    + return write_core(buffer, count, callback_fct, lcd_device);
    + break;
    + default:
    + return write_core(buffer, count, callback_fct, def_device);
    + break;
    + }
    + /* should never get here */
    + return retval;
    +
    +}
    +
    +int write_core(unsigned char *buffer, int count,
    + void *callback_fct, int device_num)
    +{
    + struct usb_skel *dev;
    + int retval = count;
    +
    + struct usb_interface *interface;
    + struct urb *urb = NULL;
    + char *buf = NULL;
    + interface = usb_find_interface(&cmdir_driver,
    + USB_CMDIR_MINOR_BASE + device_num);
    + if (!interface) {
    + /* also check without adding base, for devfs */
    + interface = usb_find_interface(&cmdir_driver, device_num);
    + if (!interface)
    + return -ENODEV;
    + }
    + dev = usb_get_intfdata(interface);
    + if (!dev)
    + return -ENODEV;
    + /* create a urb, and a buffer for it, and copy the data to the urb */
    + urb = usb_alloc_urb(0, GFP_ATOMIC); /* Now -=Atomic=- */
    + if (!urb) {
    + retval = -ENOMEM;
    + goto error;
    + }
    + buf = usb_buffer_alloc(dev->udev, count,
    + GFP_KERNEL, &urb->transfer_dma);
    + if (!buf) {
    + retval = -ENOMEM;
    + goto error;
    + }
    + if (!memcpy(buf, buffer, count)) {
    + retval = -EFAULT;
    + goto error;
    + }
    + /* initialize the urb properly */
    + if (callback_fct == NULL) {
    + usb_fill_bulk_urb(urb, dev->udev,
    + usb_sndbulkpipe(dev->udev,
    + dev->bulk_out_endpointAddr),
    + buf, count, (void *) cmdir_write_bulk_callback, dev);
    + } else {
    + usb_fill_bulk_urb(urb, dev->udev,
    + usb_sndbulkpipe(dev->udev,
    + dev->bulk_out_endpointAddr),
    + buf, count, callback_fct, dev);
    + }
    + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; /* double check this */
    +
    + /* send the data out the bulk port */
    + retval = usb_submit_urb(urb, GFP_KERNEL);
    + if (retval) {
    + err("%s - failed submitting write urb, error %d",
    + __func__, retval);
    + goto error;
    + }
    +
    + /* release our reference to this urb, the USB
    + * core will eventually free it entirely */
    + usb_free_urb(urb);
    + return count;
    +
    +error:
    + usb_buffer_free(dev->udev, count, buf, urb->transfer_dma);
    + usb_free_urb(urb);
    + return retval;
    +}
    +
    +static void cmdir_write_bulk_callback(struct urb *urb, struct pt_regs *regs)
    +{
    + struct usb_skel *dev;
    + dev = (struct usb_skel *)urb->context;
    + send_status = SEND_IDLE;
    + if (debug_commandir == 1)
    + printk(KERN_INFO "callback()\n");
    + /* free up our allocated buffer */
    +
    + usb_buffer_free(urb->dev, urb->transfer_buffer_length,
    + urb->transfer_buffer, urb->transfer_dma);
    + send_queue(); /* send the next packet */
    +
    +}
    +
    +int set_tx_channels(unsigned int next_tx)
    +{
    + next_transmitters = next_tx;
    + return 0;
    +}
    +EXPORT_SYMBOL(set_tx_channels);
    +
    +module_init(usb_cmdir_init);
    +module_exit(usb_cmdir_exit);
    +
    +MODULE_AUTHOR(DRIVER_AUTHOR);
    +MODULE_DESCRIPTION(DRIVER_DESC);
    +MODULE_LICENSE("GPL");
    diff --git a/drivers/input/lirc/commandir.h b/drivers/input/lirc/commandir.h
    new file mode 100644
    index 0000000..bed703d
    --- /dev/null
    +++ b/drivers/input/lirc/commandir.h
    @@ -0,0 +1,68 @@
    +/*
    + * commandir.h
    + */
    +
    +#define ASCII0 48
    +
    +/* transmitter channel control */
    +#define MAX_DEVICES 8
    +#define MAX_CHANNELS 32
    +#define TX1_ENABLE 0x80
    +#define TX2_ENABLE 0x40
    +#define TX3_ENABLE 0x20
    +#define TX4_ENABLE 0x10
    +
    +/* command types */
    +#define cNothing 0
    +#define cRESET 1
    +#define cFLASH 2
    +#define cLCD 3
    +#define cRX 4
    +
    +/* CommandIR control codes */
    +#define MCU_CTRL_SIZE 3
    +#define FREQ_HEADER 2
    +#define RESET_HEADER 3
    +#define FLASH_HEADER 4
    +#define LCD_HEADER 5
    +#define TX_HEADER 7
    +#define TX_HEADER_NEW 8
    +
    +/* Queue buffering constants */
    +#define SEND_IDLE 0
    +#define SEND_ACTIVE 1
    +
    +#define QUEUELENGTH 256
    +
    +extern int cmdir_write(unsigned char *buffer, int count,
    + void *callback_fct, int u);
    +extern ssize_t cmdir_read(unsigned char *buffer, size_t count);
    +extern int set_tx_channels(unsigned int next_tx);
    +
    +
    +static int cmdir_open(struct inode *inode, struct file *file);
    +static int cmdir_probe(struct usb_interface *interface,
    + const struct usb_device_id *id);
    +static void cmdir_disconnect(struct usb_interface *interface);
    +static int cmdir_release(struct inode *inode, struct file *file);
    +static int cmdir_check(int device_num);
    +static void init_cmdir_var(int device_num);
    +static void reset_cmdir(int device_num);
    +static void update_cmdir_string(int device_num);
    +static void print_cmdir(int device_num);
    +static ssize_t cmdir_file_read(struct file *file, char *buffer,
    + size_t count, loff_t *ppos);
    +ssize_t cmdir_read(unsigned char *buffer, size_t count);
    +static ssize_t cmdir_file_write(struct file *file, const char *buffer,
    + size_t count, loff_t *ppos);
    +int cmdir_write(unsigned char *buffer, int count, void *callback_fct, int u);
    +int write_core(unsigned char *buffer, int count,
    + void *callback_fct, int device_num);
    +static void cmdir_write_bulk_callback(struct urb *urb, struct pt_regs *regs);
    +int set_tx_channels(unsigned int next_tx);
    +
    +int add_cmdir_queue(unsigned char *buffer, int count,
    + void *callback_vct, int usecdelay);
    +int cmdir_write_queue(unsigned char *buffer, int count, void *callback_vct);
    +int send_queue(void);
    +int wait_to_tx(int usecs);
    diff --git a/drivers/input/lirc/lirc.h b/drivers/input/lirc/lirc.h
    new file mode 100644
    index 0000000..dcdb6e8
    --- /dev/null
    +++ b/drivers/input/lirc/lirc.h
    @@ -0,0 +1,103 @@
    +/*
    + * lirc.h - linux infrared remote control header file
    + * last modified 2007/09/27
    + */
    +
    +#ifndef _LINUX_LIRC_H
    +#define _LINUX_LIRC_H
    +
    +#include <linux/types.h>
    +#include <linux/ioctl.h>
    +
    +#define PULSE_BIT 0x01000000
    +#define PULSE_MASK 0x00FFFFFF
    +
    +/*
    + * 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_SET_TRANSMITTER_MASK 0x00000400
    +
    +#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_GET_REC_RESOLUTION 0x20000000
    +
    +#define LIRC_CAN_SEND(x) ((x)&LIRC_CAN_SEND_MASK)
    +#define LIRC_CAN_REC(x) ((x)&LIRC_CAN_REC_MASK)
    +
    +#define LIRC_CAN_NOTIFY_DECODE 0x01000000
    +
    +/*
    + * 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)
    +#define LIRC_GET_REC_RESOLUTION _IOR('i', 0x00000007, __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)
    +#define LIRC_SET_TRANSMITTER_MASK _IOW('i', 0x00000017, __u32)
    +
    +/* to set a range use
    + LIRC_SET_REC_DUTY_CYCLE_RANGE/LIRC_SET_REC_CARRIER_RANGE with the
    + lower bound first and later
    + LIRC_SET_REC_DUTY_CYCLE/LIRC_SET_REC_CARRIER with the upper bound */
    +
    +#define LIRC_SET_REC_DUTY_CYCLE_RANGE _IOW('i', 0x0000001e, __u32)
    +#define LIRC_SET_REC_CARRIER_RANGE _IOW('i', 0x0000001f, __u32)
    +
    +#define LIRC_NOTIFY_DECODE _IO('i', 0x00000020)
    +
    +#endif
    diff --git a/drivers/input/lirc/lirc_atiusb.c b/drivers/input/lirc/lirc_atiusb.c
    new file mode 100644
    index 0000000..0e07204
    --- /dev/null
    +++ b/drivers/input/lirc/lirc_atiusb.c
    @@ -0,0 +1,1321 @@
    +/* lirc_atiusb - USB remote support for LIRC
    + * (currently only supports X10 USB remotes)
    + * (supports ATI Remote Wonder and ATI Remote Wonder II, too)
    + *
    + * Copyright (C) 2003-2004 Paul Miller <pmiller9@users.sourceforge.net>
    + *
    + * This driver was derived from:
    + * Vladimir Dergachev <volodya@minspring.com>'s 2002
    + * "USB ATI Remote support" (input device)
    + * Adrian Dewhurst <sailor-lk@sailorfrag.net>'s 2002
    + * "USB StreamZap remote driver" (LIRC)
    + * Artur Lipowski <alipowski@kki.net.pl>'s 2002
    + * "lirc_dev" and "lirc_gpio" LIRC modules
    + * Michael Wojciechowski
    + * initial xbox support
    + * Vassilis Virvilis <vasvir@iit.demokritos.gr> 2006
    + * reworked the patch for lirc submission
    + *
    + */
    +
    +/*
    + * This program is free software; you can redistribute it and/or modify
    + * it under the terms of the GNU General Public License as published by
    + * the Free Software Foundation; either version 2 of the License, or
    + * (at your option) any later version.
    + *
    + * This program is distributed in the hope that it will be useful,
    + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    + * GNU General Public License for more details.
    + *
    + * You should have received a copy of the GNU General Public License
    + * along with this program; if not, write to the Free Software
    + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
    + *
    + */
    +
    +#include <linux/version.h>
    +
    +#include <linux/autoconf.h>
    +
    +#include <linux/kernel.h>
    +#include <linux/errno.h>
    +#include <linux/init.h>
    +#include <linux/slab.h>
    +#include <linux/module.h>
    +#include <linux/kmod.h>
    +#include <linux/smp_lock.h>
    +#include <linux/completion.h>
    +#include <linux/uaccess.h>
    +#include <linux/usb.h>
    +#include <linux/poll.h>
    +#include <linux/wait.h>
    +#include <linux/list.h>
    +
    +#include "lirc.h"
    +#include "lirc_dev.h"
    +
    +#define DRIVER_VERSION "1.71"
    +#define DRIVER_AUTHOR "Paul Miller <pmiller9@users.sourceforge.net>"
    +#define DRIVER_DESC "USB remote driver for LIRC"
    +#define DRIVER_NAME "lirc_atiusb"
    +
    +#define CODE_LENGTH (code_length[ir->remote_type])
    +#define CODE_MIN_LENGTH (code_min_length[ir->remote_type])
    +#define DECODE_LENGTH (decode_length[ir->remote_type])
    +
    +#define RW2_MODENAV_KEYCODE 0x3F
    +#define RW2_NULL_MODE 0xFF
    +/* Fake (virtual) keycode indicating compass mouse usage */
    +#define RW2_MOUSE_KEYCODE 0xFF
    +#define RW2_PRESSRELEASE_KEYCODE 0xFE
    +
    +#define RW2_PRESS_CODE 1
    +#define RW2_HOLD_CODE 2
    +#define RW2_RELEASE_CODE 0
    +
    +/* module parameters */
    +#ifdef CONFIG_USB_DEBUG
    +static int debug = 1;
    +#else
    +static int debug;
    +#endif
    +#define dprintk(fmt, args...) \
    + do { \
    + if (debug) \
    + printk(KERN_DEBUG fmt, ## args); \
    + } while (0)
    +
    +/* ATI, ATI2, XBOX */
    +static const int code_length[] = {5, 3, 6};
    +static const int code_min_length[] = {3, 3, 6};
    +static const int decode_length[] = {5, 3, 1};
    +/* USB_BUFF_LEN must be the maximum value of the code_length array.
    + * It is used for static arrays. */
    +#define USB_BUFF_LEN 6
    +
    +static int mask = 0xFFFF; /* channel acceptance bit mask */
    +static int unique; /* enable channel-specific codes */
    +static int repeat = 10; /* repeat time in 1/100 sec */
    +static int emit_updown; /* send seperate press/release codes (rw2) */
    +static int emit_modekeys; /* send keycodes for aux1-4, pc, and mouse (rw2) */
    +static unsigned long repeat_jiffies; /* repeat timeout */
    +static int mdeadzone; /* mouse sensitivity >= 0 */
    +static int mgradient = 375; /* 1000*gradient from cardinal direction */
    +
    +/* 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 mutex_lock(&ir->lock)
    +#define IRUNLOCK mutex_unlock(&ir->lock)
    +
    +/* general constants */
    +#define SUCCESS 0
    +#define SEND_FLAG_IN_PROGRESS 1
    +#define SEND_FLAG_COMPLETE 2
    +#define FREE_ALL 0xFF
    +
    +/* endpoints */
    +#define EP_KEYS 0
    +#define EP_MOUSE 1
    +#define EP_MOUSE_ADDR 0x81
    +#define EP_KEYS_ADDR 0x82
    +
    +#define VENDOR_ATI1 0x0bc7
    +#define VENDOR_ATI2 0x0471
    +#define VENDOR_MS1 0x040b
    +#define VENDOR_MS2 0x045e
    +#define VENDOR_MS3 0xFFFF
    +
    +static struct usb_device_id usb_remote_table[] = {
    + /* X10 USB Firecracker Interface */
    + { USB_DEVICE(VENDOR_ATI1, 0x0002) },
    +
    + /* X10 VGA Video Sender */
    + { USB_DEVICE(VENDOR_ATI1, 0x0003) },
    +
    + /* ATI Wireless Remote Receiver */
    + { USB_DEVICE(VENDOR_ATI1, 0x0004) },
    +
    + /* NVIDIA Wireless Remote Receiver */
    + { USB_DEVICE(VENDOR_ATI1, 0x0005) },
    +
    + /* ATI Wireless Remote Receiver */
    + { USB_DEVICE(VENDOR_ATI1, 0x0006) },
    +
    + /* X10 USB Wireless Transceivers */
    + { USB_DEVICE(VENDOR_ATI1, 0x0007) },
    + { USB_DEVICE(VENDOR_ATI1, 0x0008) },
    + { USB_DEVICE(VENDOR_ATI1, 0x0009) },
    + { USB_DEVICE(VENDOR_ATI1, 0x000A) },
    + { USB_DEVICE(VENDOR_ATI1, 0x000B) },
    + { USB_DEVICE(VENDOR_ATI1, 0x000C) },
    + { USB_DEVICE(VENDOR_ATI1, 0x000D) },
    + { USB_DEVICE(VENDOR_ATI1, 0x000E) },
    + { USB_DEVICE(VENDOR_ATI1, 0x000F) },
    +
    + /* ATI Remote Wonder 2: Input Device */
    + { USB_DEVICE(VENDOR_ATI2, 0x0602) },
    +
    + /* ATI Remote Wonder 2: Controller (???) */
    + { USB_DEVICE(VENDOR_ATI2, 0x0603) },
    +
    + /* Gamester Xbox DVD Movie Playback Kit IR */
    + { USB_DEVICE(VENDOR_MS1, 0x6521) },
    +
    + /* Microsoft Xbox DVD Movie Playback Kit IR */
    + { USB_DEVICE(VENDOR_MS2, 0x0284) },
    +
    + /* Some chinese manufacterer -- conflicts with the joystick from the
    + * same manufacterer */
    + { USB_DEVICE(VENDOR_MS3, 0xFFFF) },
    +
    + /* Terminating entry */
    + { }
    +};
    +
    +
    +/* init strings */
    +#define USB_OUTLEN 7
    +
    +static char init1[] = {0x01, 0x00, 0x20, 0x14};
    +static char init2[] = {0x01, 0x00, 0x20, 0x14, 0x20, 0x20, 0x20};
    +
    +struct in_endpt {
    + /* inner link in list of endpoints for the remote specified by ir */
    + struct list_head iep_list_link;
    + struct irctl *ir;
    + struct urb *urb;
    + struct usb_endpoint_descriptor *ep;
    + int type;
    +
    + /* buffers and dma */
    + unsigned char *buf;
    + unsigned int len;
    + dma_addr_t dma;
    +
    + /* handle repeats */
    + unsigned char old[USB_BUFF_LEN];
    + unsigned long old_jiffies;
    +};
    +
    +struct out_endpt {
    + struct irctl *ir;
    + struct urb *urb;
    + struct usb_endpoint_descriptor *ep;
    +
    + /* buffers and dma */
    + unsigned char *buf;
    + dma_addr_t dma;
    +
    + /* handle sending (init strings) */
    + int send_flags;
    + wait_queue_head_t wait;
    +};
    +
    +
    +/* data structure for each usb remote */
    +struct irctl {
    + /* inner link in list of all remotes managed by this module */
    + struct list_head remote_list_link;
    + /* Number of usb interfaces associated with this device */
    + int dev_refcount;
    +
    + /* usb */
    + struct usb_device *usbdev;
    + /* Head link to list of all inbound endpoints in this remote */
    + struct list_head iep_listhead;
    + struct out_endpt *out_init;
    + int devnum;
    +
    + /* remote type based on usb_device_id tables */
    + enum {
    + ATI1_COMPATIBLE,
    + ATI2_COMPATIBLE,
    + XBOX_COMPATIBLE
    + } remote_type;
    +
    + /* rw2 current mode (mirror's remote's state) */
    + int mode;
    +
    + /* lirc */
    + struct lirc_plugin *p;
    + int connected;
    +
    + /* locking */
    + struct mutex lock;
    +};
    +
    +/* list of all registered devices via the remote_list_link in irctl */
    +static struct list_head remote_list;
    +
    +/* Convenience macros to retrieve a pointer to the surrounding struct from
    + * the given list_head reference within, pointed at by link. */
    +#define get_iep_from_link(link) \
    + list_entry((link), struct in_endpt, iep_list_link);
    +#define get_irctl_from_link(link) \
    + list_entry((link), struct irctl, remote_list_link);
    +
    +/* send packet - used to initialize remote */
    +static void send_packet(struct out_endpt *oep, u16 cmd, unsigned char *data)
    +{
    + struct irctl *ir = oep->ir;
    + DECLARE_WAITQUEUE(wait, current);
    + int timeout = HZ; /* 1 second */
    + unsigned char buf[USB_OUTLEN];
    +
    + dprintk(DRIVER_NAME "[%d]: send called (%#x)\n", ir->devnum, cmd);
    +
    + IRLOCK;
    + oep->urb->transfer_buffer_length = LO(cmd) + 1;
    + oep->urb->dev = oep->ir->usbdev;
    + oep->send_flags = SEND_FLAG_IN_PROGRESS;
    +
    + memcpy(buf+1, data, LO(cmd));
    + buf[0] = HI(cmd);
    + memcpy(oep->buf, buf, LO(cmd)+1);
    +
    + set_current_state(TASK_INTERRUPTIBLE);
    + add_wait_queue(&oep->wait, &wait);
    +
    + if (usb_submit_urb(oep->urb, GFP_ATOMIC)) {
    + set_current_state(TASK_RUNNING);
    + remove_wait_queue(&oep->wait, &wait);
    + IRUNLOCK;
    + return;
    + }
    + IRUNLOCK;
    +
    + while (timeout && (oep->urb->status == -EINPROGRESS)
    + && !(oep->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(&oep->wait, &wait);
    + usb_unlink_urb(oep->urb);
    +}
    +
    +static int unregister_from_lirc(struct irctl *ir)
    +{
    + struct lirc_plugin *p = ir->p;
    + int devnum;
    +
    + devnum = ir->devnum;
    + dprintk(DRIVER_NAME "[%d]: unregister from lirc called\n", devnum);
    +
    + lirc_unregister_plugin(p->minor);
    +
    + printk(DRIVER_NAME "[%d]: usb remote disconnected\n", devnum);
    + return SUCCESS;
    +}
    +
    +
    +static int set_use_inc(void *data)
    +{
    + struct irctl *ir = data;
    + struct list_head *pos, *n;
    + struct in_endpt *iep;
    + int rtn;
    +
    + 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);
    +
    + IRLOCK;
    + if (!ir->connected) {
    + if (!ir->usbdev) {
    + IRUNLOCK;
    + dprintk(DRIVER_NAME "[%d]: !ir->usbdev\n", ir->devnum);
    + return -ENOENT;
    + }
    +
    + /* Iterate through the inbound endpoints */
    + list_for_each_safe(pos, n, &ir->iep_listhead) {
    + /* extract the current in_endpt */
    + iep = get_iep_from_link(pos);
    + iep->urb->dev = ir->usbdev;
    + dprintk(DRIVER_NAME "[%d]: linking iep 0x%02x (%p)\n",
    + ir->devnum, iep->ep->bEndpointAddress, iep);
    + rtn = usb_submit_urb(iep->urb, GFP_ATOMIC);
    + if (rtn) {
    + printk(DRIVER_NAME "[%d]: open result = %d "
    + "error submitting urb\n",
    + ir->devnum, rtn);
    + IRUNLOCK;
    + return -EIO;
    + }
    + }
    + ir->connected = 1;
    + }
    + IRUNLOCK;
    +
    + return SUCCESS;
    +}
    +
    +static void set_use_dec(void *data)
    +{
    + struct irctl *ir = data;
    + struct list_head *pos, *n;
    + struct in_endpt *iep;
    +
    + if (!ir) {
    + printk(DRIVER_NAME "[?]: set_use_dec called with no context\n");
    + return;
    + }
    + dprintk(DRIVER_NAME "[%d]: set use dec\n", ir->devnum);
    +
    + IRLOCK;
    + if (ir->connected) {
    + /* Free inbound usb urbs */
    + list_for_each_safe(pos, n, &ir->iep_listhead) {
    + iep = get_iep_from_link(pos);
    + dprintk(DRIVER_NAME "[%d]: unlinking iep 0x%02x (%p)\n",
    + ir->devnum, iep->ep->bEndpointAddress, iep);
    + usb_kill_urb(iep->urb);
    + }
    + ir->connected = 0;
    + }
    + IRUNLOCK;
    +}
    +
    +static void print_data(struct in_endpt *iep, char *buf, int len)
    +{
    + const int clen = code_length[iep->ir->remote_type];
    + char codes[clen * 3 + 1];
    + int i;
    +
    + if (len <= 0)
    + return;
    +
    + for (i = 0; i < len && i < clen; i++)
    + snprintf(codes+i*3, 4, "%02x ", buf[i] & 0xFF);
    + printk(DRIVER_NAME "[%d]: data received %s (ep=0x%x length=%d)\n",
    + iep->ir->devnum, codes, iep->ep->bEndpointAddress, len);
    +}
    +
    +static int code_check_ati1(struct in_endpt *iep, int len)
    +{
    + struct irctl *ir = iep->ir;
    + int i, chan;
    +
    + /* ATI RW1: some remotes emit both 4 and 5 byte length codes. */
    + /* ATI RW2: emit 3 byte codes */
    + if (len < CODE_MIN_LENGTH || len > CODE_LENGTH)
    + return -1;
    +
    + /* *** channel not tested with 4/5-byte Dutch remotes *** */
    + chan = ((iep->buf[len-1]>>4) & 0x0F);
    +
    + /* strip channel code */
    + if (!unique) {
    + iep->buf[len-1] &= 0x0F;
    + iep->buf[len-3] -= (chan<<4);
    + }
    +
    + if (!((1U<<chan) & mask)) {
    + dprintk(DRIVER_NAME "[%d]: ignore channel %d\n",
    + ir->devnum, chan+1);
    + return -1;
    + }
    + dprintk(DRIVER_NAME "[%d]: accept channel %d\n", ir->devnum, chan+1);
    +
    + if (ir->remote_type == ATI1_COMPATIBLE) {
    + for (i = len; i < CODE_LENGTH; i++)
    + iep->buf[i] = 0;
    + /* check for repeats */
    + if (memcmp(iep->old, iep->buf, len) == 0) {
    + if (iep->old_jiffies + repeat_jiffies > jiffies)
    + return -1;
    + } else
    + memcpy(iep->old, iep->buf, CODE_LENGTH);
    + iep->old_jiffies = jiffies;
    + }
    +
    + return SUCCESS;
    +}
    +
    +/*
    + * Since the ATI Remote Wonder II has quite a different structure from the
    + * prior version, this function was seperated out to clarify the sanitization
    + * process.
    + *
    + * Here is a summary of the main differences:
    + *
    + * a. The rw2 has no sense of a transmission channel. But, it does have an
    + * auxilliary mode state, which is set by the mode buttons Aux1 through
    + * Aux4 and "PC". These map respectively to 0-4 in the first byte of the
    + * recv buffer. Any subsequent button press sends this mode number as its
    + * "channel code." Annoyingly enough, the mode setting buttons all send
    + * the same key code (0x3f), and can only be distinguished via their mode
    + * byte.
    + *
    + * Because of this, old-style "unique"-parameter-enabled channel squashing
    + * kills the functionality of the aux1-aux4 and PC buttons. However, to
    + * not do so would cause each remote key to send a different code depending
    + * on the active aux. Further complicating matters, using the mouse norb
    + * also sends an identical code as would pushing the active aux button. To
    + * handle this we need a seperate parameter, like rw2modes, with the
    + * following values and meanings:
    + *
    + * 0: Don't squash any channel info
    + * 1: Only squash channel data for non-mode setting keys
    + * 2: Ignore aux keypresses, but don't squash channel
    + * 3: Ignore aux keypresses and squash channel data
    + *
    + * Option 1 may seem useless since the mouse sends the same code, but one
    + * need only ignore in userspace any press of a mode-setting code that only
    + * reaffirms the current mode. The 3rd party lirccd should be able to
    + * handle this easily enough, but lircd doesn't keep the state necessary
    + * for this. TODO We could work around this in the driver by emitting a
    + * single 02 (press) code for a mode key only if that mode is not currently
    + * active.
    + *
    + * Option 2 would be useful for those wanting super configurability,
    + * offering the ability to program 5 times the number actions based on the
    + * current mode.
    + *
    + * b. The rw2 has its own built in repeat handling; the keys endpoint
    + * encodes this in the second byte as 1 for press, 2 for hold, and 0 for
    + * release. This is generally much more responsive than lirc's built-in
    + * timeout handling.
    + *
    + * The problem is that the remote can send the release-recieve pair
    + * (0,1) while one is still holding down the same button if the
    + * transmission is momentarilly interrupted. (It seems that the receiver
    + * manages this count instead of the remote.) By default, this information
    + * is squashed to 2.
    + *
    + * In order to expose the built-in repeat code, set the emit_updown
    + * parameter as described below.
    + *
    + * c. The mouse norb is much more sensitive than on the rw1. It emulates
    + * a joystick-like controller with the second byte representing the x-axis
    + * and the third, the y-axis. Treated as signed integers, these axes range
    + * approximately as follows:
    + *
    + * x: (left) -46 ... 46 (right) (0xd2..0x2e)
    + * y: (up) -46 ... 46 (down) (0xd2..0x2e)
    + *
    + * NB these values do not correspond to the pressure with which the mouse
    + * norb is pushed in a given direction, but rather seems to indicate the
    + * duration for which a given direction is held.
    + *
    + * These are normalized to 8 cardinal directions for easy configuration via
    + * lircd.conf. The normalization can be fined tuned with the mdeadzone and
    + * mgradient parameters as described below.
    + *
    + * d. The interrupt rate of the mouse vs. the normal keys is different.
    + *
    + * mouse: ~27Hz (37ms between interrupts)
    + * keys: ~10Hz (100ms between interrupts)
    + *
    + * This means that the normal gap mechanism for lircd won't work as
    + * expected; is emit_updown>0 if you can get away with it.
    + */
    +static int code_check_ati2(struct in_endpt *iep, int len)
    +{
    + struct irctl *ir = iep->ir;
    + int mode, i;
    + char *buf = iep->buf;
    +
    + if (len != CODE_LENGTH) {
    + dprintk(DRIVER_NAME
    + "[%d]: Huh? Abnormal length (%d) buffer recieved.\n",
    + ir->devnum, len);
    + return -1;
    + }
    + for (i = len; i < CODE_LENGTH; i++)
    + iep->buf[i] = 0;
    +
    + mode = buf[0];
    +
    + /* Squash the mode indicator if unique wasn't set non-zero */
    + if (!unique)
    + buf[0] = 0;
    +
    + if (iep->ep->bEndpointAddress == EP_KEYS_ADDR) {
    + /* ignore mouse navigation indicator key and
    + * mode-set (aux) keys */
    + if (buf[2] == RW2_MODENAV_KEYCODE) {
    + if (emit_modekeys >= 2) /* emit raw */
    + buf[0] = mode;
    + else if (emit_modekeys == 1) {
    + /* translate */
    + buf[0] = mode;
    + if (ir->mode != mode) {
    + buf[1] = 0x03;
    + ir->mode = mode;
    + return SUCCESS;
    + }
    + } else {
    + dprintk(DRIVER_NAME
    + "[%d]: ignore dummy code 0x%x "
    + "(ep=0x%x)\n", ir->devnum,
    + buf[2], iep->ep->bEndpointAddress);
    + return -1;
    + }
    + }
    +
    + if (buf[1] != 2) {
    + /* handle press/release codes */
    + if (emit_updown == 0) /* ignore */
    + return -1;
    + else if (emit_updown == 1) /* normalize keycode */
    + buf[2] = RW2_PRESSRELEASE_KEYCODE;
    + /* else emit raw */
    + }
    +
    + } else {
    + int x = (signed char)buf[1];
    + int y = (signed char)buf[2];
    + int code = 0x00;
    + int dir_ew, dir_ns;
    +
    + buf[2] = RW2_MOUSE_KEYCODE;
    +
    + /* sensitivity threshold (use L2norm^2) */
    + if (mdeadzone > (x*x+y*y)) {
    + buf[1] = 0x00;
    + return SUCCESS;
    + }
    +
    +/* Nybble encoding: xy, 2 is -1 (S or W); 1 (N or E) */
    +#define MOUSE_N 0x01
    +#define MOUSE_NE 0x11
    +#define MOUSE_E 0x10
    +#define MOUSE_SE 0x12
    +#define MOUSE_S 0x02
    +#define MOUSE_SW 0x22
    +#define MOUSE_W 0x20
    +#define MOUSE_NW 0x21
    +
    + /* cardinal leanings: positive x -> E, positive y -> S */
    + dir_ew = (x > 0) ? MOUSE_E : MOUSE_W;
    + dir_ns = (y > 0) ? MOUSE_S : MOUSE_N;
    +
    + /* convert coordintes(angle) into compass direction */
    + if (x == 0)
    + code = dir_ns;
    + else if (y == 0)
    + code = dir_ew;
    + else {
    + if (abs(1000*y/x) > mgradient)
    + code = dir_ns;
    + if (abs(1000*x/y) > mgradient)
    + code |= dir_ew;
    + }
    +
    + buf[1] = code;
    + dprintk(DRIVER_NAME "[%d]: mouse compass=0x%x %s%s (%d,%d)\n",
    + ir->devnum, code,
    + (code & MOUSE_S ? "S" : (code & MOUSE_N ? "N" : "")),
    + (code & MOUSE_E ? "E" : (code & MOUSE_W ? "W" : "")),
    + x, y);
    + }
    +
    + return SUCCESS;
    +}
    +
    +static int code_check_xbox(struct in_endpt *iep, int len)
    +{
    + struct irctl *ir = iep->ir;
    + const int clen = CODE_LENGTH;
    +
    + if (len != clen) {
    + dprintk(DRIVER_NAME ": We got %d instead of %d bytes from xbox "
    + "ir.. ?\n", len, clen);
    + return -1;
    + }
    +
    + /* check for repeats */
    + if (memcmp(iep->old, iep->buf, len) == 0) {
    + if (iep->old_jiffies + repeat_jiffies > jiffies)
    + return -1;
    + } else {
    + /* the third byte of xbox ir packet seems to contain key info
    + * the last two bytes are.. some kind of clock? */
    + iep->buf[0] = iep->buf[2];
    + memset(iep->buf + 1, 0, len - 1);
    + memcpy(iep->old, iep->buf, len);
    + }
    + iep->old_jiffies = jiffies;
    +
    + return SUCCESS;
    +}
    +
    +static void usb_remote_recv(struct urb *urb)
    +{
    + struct in_endpt *iep;
    + int len, result = -1;
    +
    + if (!urb)
    + return;
    + iep = urb->context;
    + if (!iep) {
    + usb_unlink_urb(urb);
    + return;
    + }
    + if (!iep->ir->usbdev)
    + return;
    +
    + len = urb->actual_length;
    + if (debug)
    + print_data(iep, urb->transfer_buffer, len);
    +
    + switch (urb->status) {
    +
    + /* success */
    + case SUCCESS:
    + switch (iep->ir->remote_type) {
    + case XBOX_COMPATIBLE:
    + result = code_check_xbox(iep, len);
    + break;
    + case ATI2_COMPATIBLE:
    + result = code_check_ati2(iep, len);
    + break;
    + case ATI1_COMPATIBLE:
    + default:
    + result = code_check_ati1(iep, len);
    + }
    + if (result < 0)
    + break;
    + lirc_buffer_write_1(iep->ir->p->rbuf, iep->buf);
    + wake_up(&iep->ir->p->rbuf->wait_poll);
    + break;
    +
    + /* unlink */
    + case -ECONNRESET:
    + case -ENOENT:
    + case -ESHUTDOWN:
    + usb_unlink_urb(urb);
    + return;
    +
    + case -EPIPE:
    + default:
    + break;
    + }
    +
    + /* resubmit urb */
    + usb_submit_urb(urb, GFP_ATOMIC);
    +}
    +
    +static void usb_remote_send(struct urb *urb)
    +{
    + struct out_endpt *oep;
    +
    + if (!urb)
    + return;
    + oep = urb->context;
    + if (!oep) {
    + usb_unlink_urb(urb);
    + return;
    + }
    + if (!oep->ir->usbdev)
    + return;
    +
    + dprintk(DRIVER_NAME "[%d]: usb out called\n", oep->ir->devnum);
    +
    + if (urb->status)
    + return;
    +
    + oep->send_flags |= SEND_FLAG_COMPLETE;
    + wmb();
    + if (waitqueue_active(&oep->wait))
    + wake_up(&oep->wait);
    +}
    +
    +
    +/***************************************************************************
    + * Initialization and removal
    + ***************************************************************************/
    +
    +/*
    + * Free iep according to mem_failure which specifies a checkpoint into the
    + * initialization sequence for rollback recovery.
    + */
    +static void free_in_endpt(struct in_endpt *iep, int mem_failure)
    +{
    + struct irctl *ir;
    + dprintk(DRIVER_NAME ": free_in_endpt(%p, %d)\n", iep, mem_failure);
    + if (!iep)
    + return;
    +
    + ir = iep->ir;
    + if (!ir) {
    + dprintk(DRIVER_NAME ": free_in_endpt: WARNING! null ir\n");
    + return;
    + }
    + IRLOCK;
    + switch (mem_failure) {
    + case FREE_ALL:
    + case 5:
    + list_del(&iep->iep_list_link);
    + dprintk(DRIVER_NAME "[%d]: free_in_endpt removing ep=0x%0x "
    + "from list\n", ir->devnum, iep->ep->bEndpointAddress);
    + case 4:
    + if (iep->urb) {
    + usb_unlink_urb(iep->urb);
    + usb_free_urb(iep->urb);
    + iep->urb = 0;
    + } else
    + dprintk(DRIVER_NAME "[%d]: free_in_endpt null urb!\n",
    + ir->devnum);
    + case 3:
    + usb_buffer_free(iep->ir->usbdev, iep->len, iep->buf, iep->dma);
    + iep->buf = 0;
    + case 2:
    + kfree(iep);
    + }
    + IRUNLOCK;
    +}
    +
    +/*
    + * Construct a new inbound endpoint for this remote, and add it to the list of
    + * in_epts in ir.
    + */
    +static struct in_endpt *new_in_endpt(struct irctl *ir,
    + struct usb_endpoint_descriptor *ep)
    +{
    + struct usb_device *dev = ir->usbdev;
    + struct in_endpt *iep;
    + int pipe, maxp, len, addr;
    + int mem_failure;
    +
    + addr = ep->bEndpointAddress;
    + pipe = usb_rcvintpipe(dev, addr);
    + maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
    +
    +/* len = (maxp > USB_BUFLEN) ? USB_BUFLEN : maxp;
    + * len -= (len % CODE_LENGTH); */
    + len = CODE_LENGTH;
    +
    + dprintk(DRIVER_NAME "[%d]: acceptable inbound endpoint (0x%x) found "
    + "(maxp=%d len=%d)\n", ir->devnum, addr, maxp, len);
    +
    + mem_failure = 0;
    + iep = kmalloc(sizeof(*iep), GFP_KERNEL);
    + if (!iep) {
    + mem_failure = 1;
    + goto new_in_endpt_failure_check;
    + }
    + memset(iep, 0, sizeof(*iep));
    + iep->ir = ir;
    + iep->ep = ep;
    + iep->len = len;
    +
    + iep->buf = usb_buffer_alloc(dev, len, GFP_ATOMIC, &iep->dma);
    + if (!iep->buf) {
    + mem_failure = 2;
    + goto new_in_endpt_failure_check;
    + }
    +
    + iep->urb = usb_alloc_urb(0, GFP_KERNEL);
    + if (!iep->urb)
    + mem_failure = 3;
    +
    +new_in_endpt_failure_check:
    +
    + if (mem_failure) {
    + free_in_endpt(iep, mem_failure);
    + printk(DRIVER_NAME "[%d]: ep=0x%x out of memory (code=%d)\n",
    + ir->devnum, addr, mem_failure);
    + return NULL;
    + }
    + list_add_tail(&iep->iep_list_link, &ir->iep_listhead);
    + dprintk(DRIVER_NAME "[%d]: adding ep=0x%0x to list\n",
    + ir->devnum, iep->ep->bEndpointAddress);
    + return iep;
    +}
    +
    +static void free_out_endpt(struct out_endpt *oep, int mem_failure)
    +{
    + struct irctl *ir;
    + dprintk(DRIVER_NAME ": free_out_endpt(%p, %d)\n", oep, mem_failure);
    + if (!oep)
    + return;
    +
    + wake_up_all(&oep->wait);
    +
    + ir = oep->ir;
    + if (!ir) {
    + dprintk(DRIVER_NAME ": free_out_endpt: WARNING! null ir\n");
    + return;
    + }
    + IRLOCK;
    + switch (mem_failure) {
    + case FREE_ALL:
    + case 4:
    + if (oep->urb) {
    + usb_unlink_urb(oep->urb);
    + usb_free_urb(oep->urb);
    + oep->urb = 0;
    + } else {
    + dprintk(DRIVER_NAME "[%d]: free_out_endpt: null urb!\n",
    + ir->devnum);
    + }
    + case 3:
    + usb_buffer_free(oep->ir->usbdev, USB_OUTLEN,
    + oep->buf, oep->dma);
    + oep->buf = 0;
    + case 2:
    + kfree(oep);
    + }
    + IRUNLOCK;
    +}
    +
    +static struct out_endpt *new_out_endpt(struct irctl *ir,
    + struct usb_endpoint_descriptor *ep)
    +{
    + struct usb_device *dev = ir->usbdev;
    + struct out_endpt *oep;
    + int mem_failure;
    +
    + dprintk(DRIVER_NAME "[%d]: acceptable outbound endpoint (0x%x) found\n",
    + ir->devnum, ep->bEndpointAddress);
    +
    + mem_failure = 0;
    + oep = kmalloc(sizeof(*oep), GFP_KERNEL);
    + if (!oep)
    + mem_failure = 1;
    + else {
    + memset(oep, 0, sizeof(*oep));
    + oep->ir = ir;
    + oep->ep = ep;
    + init_waitqueue_head(&oep->wait);
    +
    + oep->buf = usb_buffer_alloc(dev, USB_OUTLEN,
    + GFP_ATOMIC, &oep->dma);
    + if (!oep->buf)
    + mem_failure = 2;
    + else {
    + oep->urb = usb_alloc_urb(0, GFP_KERNEL);
    + if (!oep->urb)
    + mem_failure = 3;
    + }
    + }
    + if (mem_failure) {
    + free_out_endpt(oep, mem_failure);
    + printk(DRIVER_NAME "[%d]: ep=0x%x out of memory (code=%d)\n",
    + ir->devnum, ep->bEndpointAddress, mem_failure);
    + return NULL;
    + }
    + return oep;
    +}
    +
    +static void free_irctl(struct irctl *ir, int mem_failure)
    +{
    + struct list_head *pos, *n;
    + struct in_endpt *in;
    + dprintk(DRIVER_NAME ": free_irctl(%p, %d)\n", ir, mem_failure);
    +
    + if (!ir)
    + return;
    +
    + list_for_each_safe(pos, n, &ir->iep_listhead) {
    + in = get_iep_from_link(pos);
    + free_in_endpt(in, FREE_ALL);
    + }
    + if (ir->out_init) {
    + free_out_endpt(ir->out_init, FREE_ALL);
    + ir->out_init = NULL;
    + }
    +
    + IRLOCK;
    + switch (mem_failure) {
    + case FREE_ALL:
    + case 6:
    + if (!--ir->dev_refcount) {
    + list_del(&ir->remote_list_link);
    + dprintk(DRIVER_NAME "[%d]: free_irctl: removing "
    + "remote from list\n", ir->devnum);
    + } else {
    + dprintk(DRIVER_NAME "[%d]: free_irctl: refcount at %d,"
    + "aborting free_irctl\n",
    + ir->devnum, ir->dev_refcount);
    + IRUNLOCK;
    + return;
    + }
    + case 5:
    + case 4:
    + case 3:
    + if (ir->p) {
    + switch (mem_failure) {
    + case 5:
    + lirc_buffer_free(ir->p->rbuf);
    + case 4:
    + kfree(ir->p->rbuf);
    + case 3:
    + kfree(ir->p);
    + }
    + } else
    + printk(DRIVER_NAME "[%d]: ir->p is a null pointer!\n",
    + ir->devnum);
    + case 2:
    + IRUNLOCK;
    + kfree(ir);
    + return;
    + }
    + IRUNLOCK;
    +}
    +
    +static struct irctl *new_irctl(struct usb_device *dev)
    +{
    + struct irctl *ir;
    + struct lirc_plugin *plugin;
    + int type, devnum, dclen;
    + int mem_failure;
    +
    + devnum = dev->devnum;
    +
    + /* determine remote type */
    + switch (cpu_to_le16(dev->descriptor.idVendor)) {
    + case VENDOR_ATI1:
    + type = ATI1_COMPATIBLE;
    + break;
    + case VENDOR_ATI2:
    + type = ATI2_COMPATIBLE;
    + break;
    + case VENDOR_MS1:
    + case VENDOR_MS2:
    + case VENDOR_MS3:
    + type = XBOX_COMPATIBLE;
    + break;
    + default:
    + dprintk(DRIVER_NAME "[%d]: unknown type\n", devnum);
    + return NULL;
    + }
    + dprintk(DRIVER_NAME "[%d]: remote type = %d\n", devnum, type);
    +
    + /* allocate kernel memory */
    + mem_failure = 0;
    + ir = kmalloc(sizeof(*ir), GFP_KERNEL);
    + if (!ir) {
    + mem_failure = 1;
    + goto new_irctl_failure_check;
    + }
    +
    + /* at this stage we cannot use the macro [DE]CODE_LENGTH: ir
    + * is not yet setup */
    + dclen = decode_length[type];
    + memset(ir, 0, sizeof(*ir));
    + /* add this infrared remote struct to remote_list, keeping track
    + * of the number of drivers registered. */
    + dprintk(DRIVER_NAME "[%d]: adding remote to list\n", devnum);
    + list_add_tail(&ir->remote_list_link, &remote_list);
    + ir->dev_refcount = 1;
    +
    + plugin = kmalloc(sizeof(*plugin), GFP_KERNEL);
    + if (!plugin) {
    + mem_failure = 2;
    + goto new_irctl_failure_check;
    + }
    +
    + memset(plugin, 0, sizeof(*plugin));
    + ir->p = plugin;
    + plugin->rbuf = kmalloc(sizeof(*(plugin->rbuf)), GFP_KERNEL);
    + if (!plugin->rbuf) {
    + mem_failure = 3;
    + goto new_irctl_failure_check;
    + }
    +
    + if (lirc_buffer_init(plugin->rbuf, dclen, 1)) {
    + mem_failure = 4;
    + goto new_irctl_failure_check;
    + }
    +
    + strcpy(plugin->name, DRIVER_NAME " ");
    + plugin->minor = -1;
    + plugin->code_length = dclen * 8;
    + plugin->features = LIRC_CAN_REC_LIRCCODE;
    + plugin->data = ir;
    + plugin->set_use_inc = &set_use_inc;
    + plugin->set_use_dec = &set_use_dec;
    + plugin->dev = &dev->dev;
    + plugin->owner = THIS_MODULE;
    + ir->usbdev = dev;
    + ir->remote_type = type;
    + ir->devnum = devnum;
    + ir->mode = RW2_NULL_MODE;
    +
    + mutex_init(&ir->lock);
    + INIT_LIST_HEAD(&ir->iep_listhead);
    +
    +new_irctl_failure_check:
    +
    + if (mem_failure) {
    + free_irctl(ir, mem_failure);
    + printk(DRIVER_NAME "[%d]: out of memory (code=%d)\n",
    + devnum, mem_failure);
    + return NULL;
    + }
    + return ir;
    +}
    +
    +
    +/*
    + * Scan the global list of remotes to see if the device listed is one of them.
    + * If it is, the corresponding irctl is returned, with its dev_refcount
    + * incremented. Otherwise, returns null.
    + */
    +static struct irctl *get_prior_reg_ir(struct usb_device *dev)
    +{
    + struct list_head *pos;
    + struct irctl *ir = NULL;
    +
    + dprintk(DRIVER_NAME "[%d]: scanning remote_list...\n", dev->devnum);
    + list_for_each(pos, &remote_list) {
    + ir = get_irctl_from_link(pos);
    + if (ir->usbdev != dev) {
    + dprintk(DRIVER_NAME "[%d]: device %d isn't it...",
    + dev->devnum, ir->devnum);
    + ir = NULL;
    + } else {
    + dprintk(DRIVER_NAME "[%d]: prior instance found.\n",
    + dev->devnum);
    + ir->dev_refcount++;
    + break;
    + }
    + }
    + return ir;
    +}
    +
    +/* If the USB interface has an out endpoint for control (eg, the first Remote
    + * Wonder) send the appropriate initialization packets. */
    +static void send_outbound_init(struct irctl *ir)
    +{
    + if (ir->out_init) {
    + struct out_endpt *oep = ir->out_init;
    + dprintk(DRIVER_NAME "[%d]: usb_remote_probe: initializing "
    + "outbound ep\n", ir->devnum);
    + usb_fill_int_urb(oep->urb, ir->usbdev,
    + usb_sndintpipe(ir->usbdev, oep->ep->bEndpointAddress),
    + oep->buf, USB_OUTLEN, usb_remote_send,
    + oep, oep->ep->bInterval);
    +
    + send_packet(oep, 0x8004, init1);
    + send_packet(oep, 0x8007, init2);
    + }
    +}
    +
    +/* Log driver and usb info */
    +static void log_usb_dev_info(struct usb_device *dev)
    +{
    + char buf[63], name[128] = "";
    + 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", dev->devnum, name,
    + dev->bus->busnum, dev->devnum);
    +}
    +
    +
    +static int usb_remote_probe(struct usb_interface *intf,
    + const struct usb_device_id *id)
    +{
    + struct usb_device *dev = interface_to_usbdev(intf);
    + struct usb_host_interface *idesc;
    + struct usb_endpoint_descriptor *ep;
    + struct in_endpt *iep;
    + struct irctl *ir;
    + int i, type;
    +
    + dprintk(DRIVER_NAME "[%d]: usb_remote_probe: dev:%p, intf:%p, id:%p)\n",
    + dev->devnum, dev, intf, id);
    +
    + idesc = intf->cur_altsetting;
    +
    + /* Check if a usb remote has already been registered for this device */
    + ir = get_prior_reg_ir(dev);
    +
    + if (!ir) {
    + ir = new_irctl(dev);
    + if (!ir)
    + return -ENOMEM;
    + }
    + type = ir->remote_type;
    +
    + /* step through the endpoints to find first in and first out endpoint
    + * of type interrupt transfer */
    + for (i = 0; i < idesc->desc.bNumEndpoints; ++i) {
    + ep = &idesc->endpoint[i].desc;
    + dprintk(DRIVER_NAME "[%d]: processing endpoint %d\n",
    + dev->devnum, i);
    + if (((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) ==
    + USB_DIR_IN) &&
    + ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
    + USB_ENDPOINT_XFER_INT)) {
    +
    + iep = new_in_endpt(ir, ep);
    + if (iep)
    + usb_fill_int_urb(iep->urb, dev,
    + usb_rcvintpipe(dev,
    + iep->ep->bEndpointAddress),
    + iep->buf, iep->len, usb_remote_recv,
    + iep, iep->ep->bInterval);
    + }
    +
    + if (((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) ==
    + USB_DIR_OUT) &&
    + ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
    + USB_ENDPOINT_XFER_INT) &&
    + (ir->out_init == NULL))
    + ir->out_init = new_out_endpt(ir, ep);
    + }
    + if (list_empty(&ir->iep_listhead)) {
    + printk(DRIVER_NAME "[%d]: inbound endpoint not found\n",
    + ir->devnum);
    + free_irctl(ir, FREE_ALL);
    + return -ENODEV;
    + }
    + if (ir->dev_refcount == 1) {
    + ir->p->minor = lirc_register_plugin(ir->p);
    + if (ir->p->minor < 0) {
    + free_irctl(ir, FREE_ALL);
    + return -ENODEV;
    + }
    +
    + /* Note new driver registration in kernel logs */
    + log_usb_dev_info(dev);
    +
    + /* outbound data (initialization) */
    + send_outbound_init(ir);
    + }
    +
    + usb_set_intfdata(intf, ir);
    + return SUCCESS;
    +}
    +
    +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);
    +
    + dprintk(DRIVER_NAME ": disconnecting remote %d:\n",
    + ir ? ir->devnum : -1);
    + if (!ir || !ir->p)
    + return;
    +
    + if (ir->usbdev) {
    + /* Only unregister once */
    + ir->usbdev = NULL;
    + unregister_from_lirc(ir);
    + }
    +
    + /* This also removes the current remote from remote_list */
    + free_irctl(ir, FREE_ALL);
    +}
    +
    +static struct usb_driver usb_remote_driver = {
    + .name = DRIVER_NAME,
    + .probe = usb_remote_probe,
    + .disconnect = usb_remote_disconnect,
    + .id_table = usb_remote_table
    +};
    +
    +static int __init usb_remote_init(void)
    +{
    + int i;
    +
    + INIT_LIST_HEAD(&remote_list);
    +
    + printk(KERN_INFO "\n" DRIVER_NAME ": " DRIVER_DESC " "
    + DRIVER_VERSION "\n");
    + printk(DRIVER_NAME ": " DRIVER_AUTHOR "\n");
    + dprintk(DRIVER_NAME ": debug mode enabled\n");
    +
    +#ifdef MODULE
    + request_module("lirc_dev");
    +#endif
    +
    + repeat_jiffies = repeat*HZ/100;
    +
    + i = usb_register(&usb_remote_driver);
    + if (i) {
    + 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);
    +}
    +
    +#ifdef MODULE
    +module_init(usb_remote_init);
    +module_exit(usb_remote_exit);
    +
    +MODULE_DESCRIPTION(DRIVER_DESC);
    +MODULE_AUTHOR(DRIVER_AUTHOR);
    +MODULE_LICENSE("GPL");
    +MODULE_DEVICE_TABLE(usb, usb_remote_table);
    +
    +module_param(debug, bool, 0644);
    +MODULE_PARM_DESC(debug, "Debug enabled or not (default: 0)");
    +
    +module_param(mask, int, 0644);
    +MODULE_PARM_DESC(mask, "Set channel acceptance bit mask (default: 0xFFFF)");
    +
    +module_param(unique, bool, 0644);
    +MODULE_PARM_DESC(unique, "Enable channel-specific codes (default: 0)");
    +
    +module_param(repeat, int, 0644);
    +MODULE_PARM_DESC(repeat, "Repeat timeout (1/100 sec) (default: 10)");
    +
    +module_param(mdeadzone, int, 0644);
    +MODULE_PARM_DESC(mdeadzone, "rw2 mouse sensitivity threshold (default: 0)");
    +
    +/*
    + * Enabling this will cause the built-in Remote Wonder II repeate coding to
    + * not be squashed. The second byte of the keys output will then be:
    + *
    + * 1 initial press (button down)
    + * 2 holding (button remains pressed)
    + * 0 release (button up)
    + *
    + * By default, the driver emits 2 for both 1 and 2, and emits nothing for 0.
    + * This is good for people having trouble getting their rw2 to send a good
    + * consistent signal to the receiver.
    + *
    + * However, if you have no troubles with the driver outputting up-down pairs
    + * at random points while you're still holding a button, then you can enable
    + * this parameter to get finer grain repeat control out of your remote:
    + *
    + * 1 Emit a single (per-channel) virtual code for all up/down events
    + * 2 Emit the actual rw2 output
    + *
    + * 1 is easier to write lircd configs for; 2 allows full control.
    + */
    +module_param(emit_updown, int, 0644);
    +MODULE_PARM_DESC(emit_updown, "emit press/release codes (rw2): 0:don't "
    + "(default), 1:emit 2 codes only, 2:code for each button");
    +
    +module_param(emit_modekeys, int, 0644);
    +MODULE_PARM_DESC(emit_modekeys, "emit keycodes for aux1-aux4, pc, and mouse "
    + "(rw2): 0:don't (default), 1:emit translated codes: one for "
    + "mode switch, one for same mode, 2:raw codes");
    +
    +module_param(mgradient, int, 0644);
    +MODULE_PARM_DESC(mgradient, "rw2 mouse: 1000*gradient from E to NE (default: "
    + "500 => .5 => ~27 degrees)");
    +
    +#else /* not MODULE */
    +subsys_initcall(usb_remote_init);
    +
    +#endif /* MODULE */
    +
    diff --git a/drivers/input/lirc/lirc_bt829.c b/drivers/input/lirc/lirc_bt829.c
    new file mode 100644
    index 0000000..01cbdfe
    --- /dev/null
    +++ b/drivers/input/lirc/lirc_bt829.c
    @@ -0,0 +1,388 @@
    +/*
    + * Remote control driver for the TV-card based on bt829
    + *
    + * by Leonid Froenchenko <lfroen@galileo.co.il>
    + *
    + * This program is free software; you can redistribute it and/or modify
    + * it under the terms of the GNU General Public License as published by
    + * the Free Software Foundation; either version 2 of the License, or
    + * (at your option) any later version.
    + *
    + * This program is distributed in the hope that it will be useful,
    + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    + * GNU General Public License for more details.
    + *
    + * You should have received a copy of the GNU General Public License
    + * along with this program; if not, write to the Free Software
    + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
    +*/
    +
    +#include <linux/version.h>
    +
    +#include <linux/autoconf.h>
    +#include <linux/kernel.h>
    +#include <linux/module.h>
    +#include <linux/threads.h>
    +#include <linux/sched.h>
    +#include <linux/ioport.h>
    +#include <linux/pci.h>
    +#include <linux/delay.h>
    +
    +#include "lirc_dev.h"
    +
    +static int poll_main(void);
    +static int atir_init_start(void);
    +
    +static void write_index(unsigned char index, unsigned int value);
    +static unsigned int read_index(unsigned char index);
    +
    +static void do_i2c_start(void);
    +static void do_i2c_stop(void);
    +
    +static void seems_wr_byte(unsigned char al);
    +static unsigned char seems_rd_byte(void);
    +
    +static unsigned int read_index(unsigned char al);
    +static void write_index(unsigned char ah, unsigned int edx);
    +
    +static void cycle_delay(int cycle);
    +
    +static void do_set_bits(unsigned char bl);
    +static unsigned char do_get_bits(void);
    +
    +#define DATA_PCI_OFF 0x7FFC00
    +#define WAIT_CYCLE 20
    +
    +static int debug;
    +#define dprintk(fmt, args...) \
    + do { \
    + if (debug) \
    + printk(KERN_DEBUG fmt, ## args); \
    + } while (0)
    +
    +static int atir_minor;
    +static unsigned long pci_addr_phys;
    +static unsigned char *pci_addr_lin;
    +
    +static struct lirc_plugin atir_plugin;
    +
    +static struct pci_dev *do_pci_probe(void)
    +{
    + struct pci_dev *my_dev;
    + my_dev = pci_get_device(PCI_VENDOR_ID_ATI,
    + PCI_DEVICE_ID_ATI_264VT, NULL);
    + if (my_dev) {
    + printk(KERN_ERR "ATIR: Using device: %s\n",
    + pci_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 NULL;
    + }
    + } else {
    + printk(KERN_ERR "ATIR: pci_prob failed\n");
    + return NULL;
    + }
    + return my_dev;
    +}
    +
    +static 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) {
    + dprintk("ATIR reading key %02X\n", key);
    + lirc_buffer_write_1(buf, &key);
    + return 0;
    + }
    + return -ENODATA;
    +}
    +
    +static int atir_set_use_inc(void *data)
    +{
    + dprintk("ATIR driver is opened\n");
    + return 0;
    +}
    +
    +static void atir_set_use_dec(void *data)
    +{
    + dprintk("ATIR driver is closed\n");
    +}
    +
    +int init_module(void)
    +{
    + struct pci_dev *pdev;
    +
    + pdev = do_pci_probe();
    + if (pdev == NULL)
    + 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_plugin.dev = &pdev->dev;
    + atir_plugin.owner = THIS_MODULE;
    +
    + atir_minor = lirc_register_plugin(&atir_plugin);
    + dprintk("ATIR driver is registered on minor %d\n", atir_minor);
    +
    + return 0;
    +}
    +
    +
    +void cleanup_module(void)
    +{
    + lirc_unregister_plugin(atir_minor);
    +}
    +
    +
    +static int atir_init_start(void)
    +{
    + pci_addr_lin = 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;
    +}
    +
    +static void cycle_delay(int cycle)
    +{
    + udelay(WAIT_CYCLE*cycle);
    +}
    +
    +
    +static 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;
    +}
    +
    +static 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);
    +}
    +
    +static 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);
    +}
    +
    +static 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);
    +}
    +
    +static 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);
    +
    + bits_2 = do_get_bits();
    + if (bits_2 & 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;
    +}
    +
    +static 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);
    +}
    +
    +static 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;
    +}
    +
    +static unsigned int read_index(unsigned char index)
    +{
    + unsigned char *addr;
    + unsigned int value;
    + /* addr = pci_addr_lin + DATA_PCI_OFF + ((index & 0xFF) << 2); */
    + addr = pci_addr_lin + ((index & 0xFF) << 2);
    + value = readl(addr);
    + return value;
    +}
    +
    +static void write_index(unsigned char index, unsigned int reg_val)
    +{
    + unsigned char *addr;
    + addr = pci_addr_lin + ((index & 0xFF) << 2);
    + writel(reg_val, addr);
    +}
    +
    +MODULE_AUTHOR("Froenchenko Leonid");
    +MODULE_DESCRIPTION("IR remote driver for bt829 based TV cards");
    +MODULE_LICENSE("GPL");
    +
    +module_param(debug, bool, 0644);
    +MODULE_PARM_DESC(debug, "Debug enabled or not");
    +
    +/*
    + * Overrides for Emacs so that we follow Linus's tabbing style.
    + * ---------------------------------------------------------------------------
    + * Local variables:
    + * c-basic-offset: 8
    + * End:
    + */
    diff --git a/drivers/input/lirc/lirc_cmdir.c b/drivers/input/lirc/lirc_cmdir.c
    new file mode 100644
    index 0000000..1faa8e3
    --- /dev/null
    +++ b/drivers/input/lirc/lirc_cmdir.c
    @@ -0,0 +1,596 @@
    +/*
    + * lirc_cmdir.c - Driver for InnovationOne's COMMANDIR USB Transceiver
    + *
    + * This driver requires the COMMANDIR hardware driver, available at
    + * http://www.commandir.com/.
    + *
    + * Copyright (C) 2005 InnovationOne - Evelyn Yeung
    + *
    + * This program is free software; you can redistribute it and/or modify
    + * it under the terms of the GNU General Public License as published by
    + * the Free Software Foundation; either version 2 of the License, or
    + * (at your option) any later version.
    + *
    + * This program is distributed in the hope that it will be useful,
    + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    + * GNU General Public License for more details.
    + *
    + * You should have received a copy of the GNU General Public License
    + * along with this program; if not, write to the Free Software
    + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
    + */
    +
    +
    +#include <linux/version.h>
    +
    +#include <linux/autoconf.h>
    +#include <linux/module.h>
    +#include <linux/errno.h>
    +#include <linux/signal.h>
    +#include <linux/sched.h>
    +#include <linux/kernel.h>
    +#include <linux/time.h>
    +#include <linux/string.h>
    +#include <linux/delay.h>
    +#include <linux/poll.h>
    +#include "lirc.h"
    +#include "lirc_dev.h"
    +#include "lirc_cmdir.h"
    +
    +static int debug;
    +#define dprintk(fmt, args...) \
    + do { \
    + if (debug) \
    + printk(KERN_DEBUG fmt, ## args); \
    + } while (0)
    +
    +struct lirc_cmdir {
    + int features;
    +};
    +
    +struct lirc_cmdir hardware = {
    + (
    + /* LIRC_CAN_SET_SEND_DUTY_CYCLE| */
    + LIRC_CAN_SET_SEND_CARRIER|
    + LIRC_CAN_SEND_PULSE|
    + LIRC_CAN_SET_TRANSMITTER_MASK|
    + LIRC_CAN_REC_MODE2)
    + ,
    +};
    +
    +#define LIRC_DRIVER_NAME "lirc_cmdir"
    +#define RBUF_LEN 256
    +#define WBUF_LEN 256
    +#define MAX_PACKET 64
    +
    +static struct lirc_buffer rbuf;
    +static int wbuf[WBUF_LEN];
    +static unsigned char cmdir_char[4*WBUF_LEN];
    +static unsigned char write_control[MCU_CTRL_SIZE];
    +static unsigned int last_mc_time;
    +static int usb_status = ON;
    +static unsigned char signal_num;
    +char timerval;
    +
    +unsigned int freq = 38000;
    +/* unsigned int duty_cycle = 50; */
    +
    +
    +#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);
    +}
    +
    +static unsigned int get_time_value(unsigned int firstint,
    + unsigned int secondint, unsigned char overflow)
    +{ /* get difference between two timestamps from MCU */
    + unsigned int t_answer = 0;
    +
    + if (secondint > firstint) {
    + t_answer = secondint - firstint + overflow*65536;
    + } else {
    + if (overflow > 0)
    + t_answer = (65536 - firstint) + secondint +
    + (overflow - 1) * 65536;
    + else
    + t_answer = (65536 - firstint) + secondint;
    + }
    +
    + /* clamp to long signal */
    + if (t_answer > 16000000)
    + t_answer = PULSE_MASK;
    +
    + return t_answer;
    +}
    +
    +
    +static int set_use_inc(void *data)
    +{
    + /* Init read buffer. */
    + if (lirc_buffer_init(&rbuf, sizeof(int), RBUF_LEN) < 0)
    + return -ENOMEM;
    +
    + return 0;
    +}
    +
    +static void set_use_dec(void *data)
    +{
    + lirc_buffer_free(&rbuf);
    +}
    +
    +
    +static void usb_error_handle(int retval)
    +{
    + switch (retval) {
    + case -ENODEV:
    + /* device has been unplugged */
    + if (usb_status == ON) {
    + usb_status = OFF;
    + printk(LIRC_DRIVER_NAME ": device is unplugged\n");
    + }
    + break;
    + default:
    + printk(LIRC_DRIVER_NAME ": usb error = %d\n", retval);
    + break;
    + }
    +}
    +
    +static int write_to_usb(unsigned char *buffer, int count, int time_elapsed)
    +{
    + int write_return;
    +
    + write_return = cmdir_write(buffer, count, NULL, time_elapsed);
    + if (write_return != count) {
    + usb_error_handle(write_return);
    + } else {
    + if (usb_status == OFF) {
    + printk(LIRC_DRIVER_NAME ": device is now plugged in\n");
    + usb_status = ON;
    + }
    + }
    + return write_return;
    +}
    +
    +static void set_freq(void)
    +{
    + /* float tempfreq = 0.0; */
    + int write_return;
    +
    + /*
    + * Can't use floating point in 2.6 kernel!
    + * May be some loss of precision
    + */
    + timerval = (1000000 / freq) / 2;
    + write_control[0] = FREQ_HEADER;
    + write_control[1] = timerval;
    + write_control[2] = 0;
    + write_return = write_to_usb(write_control, MCU_CTRL_SIZE, 0);
    + if (write_return == MCU_CTRL_SIZE)
    + printk(LIRC_DRIVER_NAME ": freq set to %dHz\n", freq);
    + else
    + printk(LIRC_DRIVER_NAME ": freq unchanged\n");
    +
    +}
    +
    +static int cmdir_convert_RX(unsigned char *orig_rxbuffer)
    +{
    + unsigned char tmp_char_buffer[80];
    + unsigned int tmp_int_buffer[20];
    + unsigned int final_data_buffer[20];
    + unsigned int num_data_values = 0;
    + unsigned char num_data_bytes = 0;
    + unsigned int orig_index = 0;
    + int i;
    +
    + for (i = 0; i < 80; i++)
    + tmp_char_buffer[i] = 0;
    + for (i = 0; i < 20; i++)
    + tmp_int_buffer[i] = 0;
    +
    + /*
    + * get number of data bytes that follow the control bytes
    + * (NOT including them)
    + */
    + num_data_bytes = orig_rxbuffer[1];
    +
    + /* check if num_bytes is multiple of 3; if not, error */
    + if (num_data_bytes % 3 > 0)
    + return -1;
    + if (num_data_bytes > 60)
    + return -3;
    + if (num_data_bytes < 3)
    + return -2;
    +
    + /*
    + * get number of ints to be returned; num_data_bytes does
    + * NOT include control bytes
    + */
    + num_data_values = num_data_bytes/3;
    +
    + for (i = 0; i < num_data_values; i++) {
    + tmp_char_buffer[i*4] = orig_rxbuffer[(i+1)*3];
    + tmp_char_buffer[i*4+1] = orig_rxbuffer[(i+1)*3+1];
    + tmp_char_buffer[i*4+2] = 0;
    + tmp_char_buffer[i*4+3] = 0;
    + }
    +
    + /* convert to int array */
    + memcpy((unsigned char *)tmp_int_buffer, tmp_char_buffer,
    + (num_data_values*4));
    +
    + if (orig_rxbuffer[5] < 255) {
    + /* space */
    + final_data_buffer[0] = get_time_value(last_mc_time,
    + tmp_int_buffer[0],
    + orig_rxbuffer[5]);
    + } else { /* is pulse */
    + final_data_buffer[0] = get_time_value(last_mc_time,
    + tmp_int_buffer[0],
    + 0);
    + final_data_buffer[0] |= PULSE_BIT;
    + }
    + for (i = 1; i < num_data_values; i++) {
    + /*
    + * index of orig_rxbuffer that corresponds to
    + * overflow/pulse/space
    + */
    + orig_index = (i + 1)*3 + 2;
    + if (orig_rxbuffer[orig_index] < 255) {
    + final_data_buffer[i] =
    + get_time_value(tmp_int_buffer[i - 1],
    + tmp_int_buffer[i],
    + orig_rxbuffer[orig_index]);
    + } else {
    + final_data_buffer[i] =
    + get_time_value(tmp_int_buffer[i - 1],
    + tmp_int_buffer[i],
    + 0);
    + final_data_buffer[i] |= PULSE_BIT;
    + }
    + }
    + last_mc_time = tmp_int_buffer[num_data_values - 1];
    +
    + if (lirc_buffer_full(&rbuf)) {
    + printk(KERN_ERR LIRC_DRIVER_NAME ": lirc_buffer is full\n");
    + return -EOVERFLOW;
    + }
    + lirc_buffer_write_n(&rbuf, (char *)final_data_buffer, num_data_values);
    +
    + return 0;
    +}
    +
    +
    +static int usb_read_once(void)
    +{
    + int read_retval = 0;
    + int conv_retval = 0;
    + unsigned char read_buffer[MAX_PACKET];
    + int i = 0;
    + int tooFull = 5; /* read up to 5 packets */
    +
    + for (i = 0; i < MAX_PACKET; i++)
    + read_buffer[i] = 0;
    +
    + while (tooFull--) {
    + read_retval = cmdir_read(read_buffer, MAX_PACKET);
    + /* Loop until we unload the data build-up */
    + if (read_buffer[1] < 60)
    + tooFull = 0;
    + if (!(read_retval == MAX_PACKET)) {
    + if (read_retval == -ENODEV) {
    + if (usb_status == ON) {
    + printk(KERN_ALERT LIRC_DRIVER_NAME
    + ": device is unplugged\n");
    + usb_status = OFF;
    + }
    + } else {
    + /* supress errors */
    + printk(KERN_ALERT LIRC_DRIVER_NAME
    + ": usb error on read = %d\n",
    + read_retval);
    + return -ENODATA;
    + }
    + dprintk("Error 3\n");
    + return -ENODATA;
    + } else {
    + if (usb_status == OFF) {
    + usb_status = ON;
    + printk(LIRC_DRIVER_NAME
    + ": device is now plugged in\n");
    + }
    + }
    +
    + if (read_buffer[0] & 0x08) {
    + conv_retval = cmdir_convert_RX(read_buffer);
    + if (conv_retval == 0) {
    + if (!tooFull)
    + return 0;
    + else
    + dprintk("Looping for more data...\n");
    + } else {
    + dprintk("Error 2: %d\n", (int)conv_retval);
    + return -ENODATA;
    + }
    + } else {
    + /* There really is no data in their buffer */
    + dprintk("Empty RX Buffer!\n");
    + return -ENODATA;
    + }
    + }
    + return -1;
    +}
    +
    +int add_to_buf(void *data, struct lirc_buffer *buf)
    +{
    + return usb_read_once();
    +}
    +
    +
    +static ssize_t lirc_write(struct file *file, const char *buf,
    + size_t n, loff_t *ppos)
    +{
    + int i, count;
    + unsigned int mod_signal_length = 0;
    + unsigned int time_elapse = 0;
    + unsigned int total_time_elapsed = 0;
    + unsigned int num_bytes_already_sent = 0;
    + unsigned int hibyte = 0;
    + unsigned int lobyte = 0;
    + int cmdir_cnt = 0;
    + unsigned int wait_this = 0;
    + struct timeval start_time;
    + struct timeval end_time;
    + unsigned int real_time_elapsed = 0;
    +
    + /* save the time we started the write: */
    + do_gettimeofday(&start_time);
    +
    + if (n % sizeof(int))
    + return -EINVAL;
    +
    + count = n / sizeof(int);
    + if (count > WBUF_LEN || count % 2 == 0)
    + return -EINVAL;
    + if (copy_from_user(wbuf, buf, n))
    + return -EFAULT;
    +
    + /*
    + * the first time we have to flag that this is the start of a new
    + * signal otherwise COMMANDIR may receive 2 back-to-back pulses &
    + * invert the signal
    + */
    + cmdir_char[0] = TX_HEADER_NEW;
    + signal_num++;
    + cmdir_char[1] = signal_num;
    + cmdir_cnt = 2;
    + for (i = 0; i < count; i++) {
    + /* conversion to number of modulation frequency pulse edges */
    + mod_signal_length = wbuf[i] >> 3;
    + /*
    + * account for minor rounding errors -
    + * calculate length from this:
    + */
    + time_elapse += mod_signal_length * timerval;
    +
    + hibyte = mod_signal_length / 256;
    + lobyte = mod_signal_length % 256;
    + cmdir_char[cmdir_cnt+1] = lobyte;
    + cmdir_char[cmdir_cnt] = hibyte;
    + cmdir_cnt += 2;
    +
    + /* write data to usb if full packet is collected */
    + if (cmdir_cnt % MAX_PACKET == 0) {
    + write_to_usb(cmdir_char, MAX_PACKET, time_elapse);
    +
    + total_time_elapsed += time_elapse;
    +
    + num_bytes_already_sent += MAX_PACKET;
    + time_elapse = 0;
    +
    + if ((i + 1) < count) {
    + /* still more to send: */
    + cmdir_char[0] = TX_HEADER; /* Next Packet */
    + cmdir_char[1] = signal_num;
    + cmdir_cnt = 2; /* reset the count */
    + }
    + }
    + }
    +
    + /* send last chunk of data */
    + if (cmdir_cnt > 0) {
    + total_time_elapsed += time_elapse;
    + write_to_usb(cmdir_char, cmdir_cnt, time_elapse);
    + }
    + /* XXX ERS remove all this? */
    + /*
    + * we need to _manually delay ourselves_ to remain backwards
    + * compatible with LIRC and prevent our queue buffer from overflowing.
    + * Queuing in this driver is about instant, and send_start for example
    + * will fill it up quickly and prevent send_stop from taking immediate
    + * effect.
    + */
    + dprintk("Total elapsed time is: %d. \n", total_time_elapsed);
    + do_gettimeofday(&end_time);
    + /*
    + * udelay for the difference between endtime and
    + * start + total_time_elapsed
    + */
    + if (start_time.tv_usec < end_time.tv_usec)
    + real_time_elapsed = (end_time.tv_usec - start_time.tv_usec);
    + else
    + real_time_elapsed = ((end_time.tv_usec + 1000000) -
    + start_time.tv_usec);
    + dprintk("Real time elapsed was %u.\n", real_time_elapsed);
    + if (real_time_elapsed < (total_time_elapsed - 1000))
    + wait_this = total_time_elapsed - real_time_elapsed - 1000;
    +
    +#if 0 /* enable this for backwards compatibility */
    + safe_udelay(wait_this);
    +#endif
    +
    + return n;
    +}
    +
    +
    +static int lirc_ioctl(struct inode *node, struct file *filep, unsigned int cmd,
    + unsigned long arg)
    +{
    + int result;
    + unsigned long value;
    + unsigned int ivalue;
    + unsigned int multiplier = 1;
    + unsigned int mask = 0;
    + int i;
    +
    + switch (cmd) {
    + case LIRC_SET_TRANSMITTER_MASK:
    + if (!(hardware.features&LIRC_CAN_SET_TRANSMITTER_MASK))
    + return -ENOIOCTLCMD;
    + result = get_user(ivalue, (unsigned int *) arg);
    + if (result)
    + return result;
    + for (i = 0; i < MAX_CHANNELS; i++) {
    + multiplier = multiplier * 0x10;
    + mask |= multiplier;
    + }
    + if (ivalue >= mask)
    + return MAX_CHANNELS;
    + set_tx_channels(ivalue);
    + return 0;
    + break;
    +
    + case LIRC_GET_SEND_MODE:
    + if (!(hardware.features & LIRC_CAN_SEND_MASK))
    + return -ENOIOCTLCMD;
    +
    + result = put_user(LIRC_SEND2MODE
    + (hardware.features & LIRC_CAN_SEND_MASK),
    + (unsigned long *) arg);
    + if (result)
    + return result;
    + break;
    +
    + case LIRC_SET_SEND_MODE:
    + if (!(hardware.features&LIRC_CAN_SEND_MASK))
    + return -ENOIOCTLCMD;
    +
    + result = get_user(value, (unsigned long *)arg);
    + if (result)
    + return result;
    + break;
    +
    + case LIRC_GET_LENGTH:
    + return -ENOSYS;
    + break;
    +
    + case LIRC_SET_SEND_DUTY_CYCLE:
    + dprintk(KERN_WARNING LIRC_DRIVER_NAME
    + ": SET_SEND_DUTY_CYCLE\n");
    +
    + if (!(hardware.features&LIRC_CAN_SET_SEND_DUTY_CYCLE))
    + return -ENOIOCTLCMD;
    +
    + result = get_user(ivalue, (unsigned int *)arg);
    + if (result)
    + return result;
    + if (ivalue <= 0 || ivalue > 100)
    + return -EINVAL;
    +
    + /* TODO: */
    + dprintk(LIRC_DRIVER_NAME
    + ": set_send_duty_cycle not yet supported\n");
    +
    + return 0;
    + break;
    +
    + case LIRC_SET_SEND_CARRIER:
    + dprintk(KERN_WARNING LIRC_DRIVER_NAME ": SET_SEND_CARRIER\n");
    +
    + if (!(hardware.features & LIRC_CAN_SET_SEND_CARRIER))
    + return -ENOIOCTLCMD;
    +
    + result = get_user(ivalue, (unsigned int *)arg);
    + if (result)
    + return result;
    + if (ivalue > 500000 || ivalue < 24000)
    + return -EINVAL;
    + if (ivalue != freq) {
    + freq = ivalue;
    + set_freq();
    + }
    + return 0;
    + 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 = 20,
    + .data = NULL,
    + .add_to_buf = add_to_buf,
    + .get_queue = NULL,
    + .rbuf = &rbuf,
    + .set_use_inc = set_use_inc,
    + .set_use_dec = set_use_dec,
    + .ioctl = lirc_ioctl,
    + .fops = &lirc_fops,
    + .dev = NULL,
    + .owner = THIS_MODULE,
    +};
    +
    +#ifdef MODULE
    +
    +MODULE_AUTHOR("Evelyn Yeung, Matt Bodkin");
    +MODULE_DESCRIPTION("InnovationOne driver for "
    + "CommandIR USB infrared transceiver");
    +#ifdef MODULE_LICENSE
    +MODULE_LICENSE("GPL");
    +#endif
    +
    +module_param(debug, bool, 0644);
    +MODULE_PARM_DESC(debug, "Enable debugging messages");
    +
    +
    +int init_module(void)
    +{
    + plugin.features = hardware.features;
    + plugin.minor = lirc_register_plugin(&plugin);
    + if (plugin.minor < 0) {
    + printk(KERN_ERR LIRC_DRIVER_NAME
    + ": register_chrdev failed!\n");
    + return -EIO;
    + }
    + set_freq();
    + return 0;
    +}
    +
    +void cleanup_module(void)
    +{
    + lirc_unregister_plugin(plugin.minor);
    + printk(KERN_INFO LIRC_DRIVER_NAME ": module removed\n");
    +}
    +
    +#endif
    +
    +
    diff --git a/drivers/input/lirc/lirc_cmdir.h b/drivers/input/lirc/lirc_cmdir.h
    new file mode 100644
    index 0000000..f2400c3
    --- /dev/null
    +++ b/drivers/input/lirc/lirc_cmdir.h
    @@ -0,0 +1,25 @@
    +/*
    + * lirc_cmdir.h
    + */
    +
    +#ifndef LIRC_CMDIR_H
    +#define LIRC_CMDIR_H
    +
    +#define ON 1
    +#define OFF 0
    +
    +/* transmitter channel control */
    +#define MAX_CHANNELS 32
    +
    +/* CommandIR control codes */
    +#define MCU_CTRL_SIZE 3
    +#define FREQ_HEADER 2
    +#define TX_HEADER 7
    +#define TX_HEADER_NEW 8
    +
    +extern int cmdir_write(unsigned char *buffer, int count,
    + void *callback_fct, int u);
    +extern ssize_t cmdir_read(unsigned char *buffer, size_t count);
    +extern int set_tx_channels(unsigned int next_tx);
    +
    +#endif
    diff --git a/drivers/input/lirc/lirc_dev.c b/drivers/input/lirc/lirc_dev.c
    new file mode 100644
    index 0000000..c8f325c
    --- /dev/null
    +++ b/drivers/input/lirc/lirc_dev.c
    @@ -0,0 +1,809 @@
    +/*
    + * LIRC base driver
    + *
    + * (L) by Artur Lipowski <alipowski@interia.pl>
    + *
    + * This program is free software; you can redistribute it and/or modify
    + * it under the terms of the GNU General Public License as published by
    + * the Free Software Foundation; either version 2 of the License, or
    + * (at your option) any later version.
    + *
    + * This program is distributed in the hope that it will be useful,
    + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    + * GNU General Public License for more details.
    + *
    + * You should have received a copy of the GNU General Public License
    + * along with this program; if not, write to the Free Software
    + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
    + *
    + */
    +
    +#include <linux/version.h>
    +
    +#include <linux/autoconf.h>
    +#include <linux/module.h>
    +#include <linux/kernel.h>
    +#include <linux/sched.h>
    +#include <linux/errno.h>
    +#include <linux/ioctl.h>
    +#include <linux/fs.h>
    +#include <linux/poll.h>
    +#include <linux/smp_lock.h>
    +#include <linux/completion.h>
    +#include <linux/uaccess.h>
    +#include <linux/errno.h>
    +#include <linux/semaphore.h>
    +#define __KERNEL_SYSCALLS__
    +#include <linux/unistd.h>
    +#include <linux/kthread.h>
    +
    +/* SysFS header */
    +#include <linux/device.h>
    +
    +#include "lirc.h"
    +#include "lirc_dev.h"
    +
    +static int debug;
    +#define dprintk(fmt, args...) \
    + do { \
    + if (debug) \
    + printk(KERN_DEBUG fmt, ## args); \
    + } while (0)
    +
    +#define IRCTL_DEV_NAME "BaseRemoteCtl"
    +#define SUCCESS 0
    +#define NOPLUG -1
    +#define LOGHEAD "lirc_dev (%s[%d]): "
    +
    +struct irctl {
    + struct lirc_plugin p;
    + int attached;
    + int open;
    +
    + struct mutex buffer_lock;
    + struct lirc_buffer *buf;
    +
    + struct task_struct *task;
    + long jiffies_to_wait;
    +
    +};
    +
    +static DEFINE_MUTEX(plugin_lock);
    +
    +static struct irctl irctls[MAX_IRCTL_DEVICES];
    +static struct file_operations fops;
    +
    +/* Only used for sysfs but defined to void otherwise */
    +static struct class *lirc_class;
    +
    +/* helper function
    + * initializes the irctl structure
    + */
    +static inline void init_irctl(struct irctl *ir)
    +{
    + memset(&ir->p, 0, sizeof(struct lirc_plugin));
    + mutex_init(&ir->buffer_lock);
    + ir->p.minor = NOPLUG;
    +
    + ir->task = NULL;
    + ir->jiffies_to_wait = 0;
    +
    + ir->open = 0;
    + ir->attached = 0;
    +}
    +
    +static void cleanup(struct irctl *ir)
    +{
    + dprintk(LOGHEAD "cleaning up\n", ir->p.name, ir->p.minor);
    +
    + device_destroy(lirc_class, MKDEV(IRCTL_DEV_MAJOR, ir->p.minor));
    +
    + if (ir->buf != ir->p.rbuf) {
    + lirc_buffer_free(ir->buf);
    + kfree(ir->buf);
    + }
    + ir->buf = NULL;
    +
    + init_irctl(ir);
    +}
    +
    +/* helper function
    + * reads key codes from plugin and puts them into buffer
    + * buffer free space is checked and locking performed
    + * returns 0 on success
    + */
    +static inline 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)
    + kthread_stop(ir->task);
    +
    + 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
    + */
    +
    + dprintk(LOGHEAD "poll thread started\n", ir->p.name, ir->p.minor);
    +
    + do {
    + if (ir->open) {
    + if (ir->jiffies_to_wait) {
    + set_current_state(TASK_INTERRUPTIBLE);
    + schedule_timeout(ir->jiffies_to_wait);
    + } else {
    + interruptible_sleep_on(
    + ir->p.get_queue(ir->p.data));
    + }
    + if (kthread_should_stop())
    + 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 */
    + set_current_state(TASK_INTERRUPTIBLE);
    + schedule_timeout(HZ/2);
    + }
    + } while (!kthread_should_stop());
    +
    + 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;
    + int err;
    + DECLARE_COMPLETION(tn);
    +
    + if (!p) {
    + printk(KERN_ERR "lirc_dev: lirc_register_plugin: "
    + "plugin pointer must be not NULL!\n");
    + err = -EBADRQC;
    + goto out;
    + }
    +
    + if (MAX_IRCTL_DEVICES <= p->minor) {
    + printk(KERN_ERR "lirc_dev: lirc_register_plugin: "
    + "\"minor\" must be between 0 and %d (%d)!\n",
    + MAX_IRCTL_DEVICES-1, p->minor);
    + err = -EBADRQC;
    + goto out;
    + }
    +
    + if (1 > p->code_length || (BUFLEN * 8) < p->code_length) {
    + printk(KERN_ERR "lirc_dev: lirc_register_plugin: "
    + "code length in bits for minor (%d) "
    + "must be less than %d!\n",
    + p->minor, BUFLEN * 8);
    + err = -EBADRQC;
    + goto out;
    + }
    +
    + printk(KERN_INFO "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(KERN_ERR "lirc_dev: lirc_register_plugin: "
    + "sample_rate must be between 2 and %d!\n", HZ);
    + err = -EBADRQC;
    + goto out;
    + }
    + if (!p->add_to_buf) {
    + printk(KERN_ERR "lirc_dev: lirc_register_plugin: "
    + "add_to_buf cannot be NULL when "
    + "sample_rate is set\n");
    + err = -EBADRQC;
    + goto out;
    + }
    + } else if (!(p->fops && p->fops->read)
    + && !p->get_queue && !p->rbuf) {
    + printk(KERN_ERR "lirc_dev: lirc_register_plugin: "
    + "fops->read, get_queue and rbuf "
    + "cannot all be NULL!\n");
    + err = -EBADRQC;
    + goto out;
    + } else if (!p->get_queue && !p->rbuf) {
    + if (!(p->fops && p->fops->read && p->fops->poll)
    + || (!p->fops->ioctl && !p->ioctl)) {
    + printk(KERN_ERR "lirc_dev: lirc_register_plugin: "
    + "neither read, poll nor ioctl can be NULL!\n");
    + err = -EBADRQC;
    + goto out;
    + }
    + }
    +
    + if (p->owner == NULL) {
    + printk(KERN_ERR "lirc_dev: lirc_register_plugin: "
    + "no module owner registered\n");
    + err = -EBADRQC;
    + goto out;
    + }
    +
    + mutex_lock(&plugin_lock);
    +
    + minor = p->minor;
    +
    + if (0 > minor) {
    + /* find first free slot for plugin */
    + for (minor = 0; minor < MAX_IRCTL_DEVICES; minor++)
    + if (irctls[minor].p.minor == NOPLUG)
    + break;
    + if (MAX_IRCTL_DEVICES == minor) {
    + printk(KERN_ERR "lirc_dev: lirc_register_plugin: "
    + "no free slots for plugins!\n");
    + err = -ENOMEM;
    + goto out_lock;
    + }
    + } else if (irctls[minor].p.minor != NOPLUG) {
    + printk(KERN_ERR "lirc_dev: lirc_register_plugin: "
    + "minor (%d) just registered!\n", minor);
    + err = -EBUSY;
    + goto out_lock;
    + }
    +
    + ir = &irctls[minor];
    +
    + if (p->sample_rate) {
    + ir->jiffies_to_wait = HZ / p->sample_rate;
    + } else {
    + /* it means - wait for external 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);
    + if (!ir->buf) {
    + err = -ENOMEM;
    + goto out_lock;
    + }
    + if (lirc_buffer_init(ir->buf, bytes_in_key,
    + BUFLEN/bytes_in_key) != 0) {
    + kfree(ir->buf);
    + err = -ENOMEM;
    + goto out_lock;
    + }
    + }
    +
    + 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;
    +
    + device_create(lirc_class, ir->p.dev,
    + MKDEV(IRCTL_DEV_MAJOR, ir->p.minor), NULL,
    + "lirc%u", ir->p.minor);
    +
    + if (p->sample_rate || p->get_queue) {
    + /* try to fire up polling thread */
    + ir->task = kthread_run(lirc_thread, (void *)ir, "lirc_dev");
    + if (IS_ERR(ir->task)) {
    + printk(KERN_ERR "lirc_dev: lirc_register_plugin: "
    + "cannot run poll thread for minor = %d\n",
    + p->minor);
    + err = -ECHILD;
    + goto out_sysfs;
    + }
    + }
    + ir->attached = 1;
    + mutex_unlock(&plugin_lock);
    +
    +/*
    + * Recent kernels should handle this autmatically by increasing/decreasing
    + * use count when a dependant module is loaded/unloaded.
    + */
    + dprintk("lirc_dev: plugin %s registered at minor number = %d\n",
    + ir->p.name, ir->p.minor);
    + p->minor = minor;
    + return minor;
    +
    +out_sysfs:
    + device_destroy(lirc_class, MKDEV(IRCTL_DEV_MAJOR, ir->p.minor));
    +out_lock:
    + mutex_unlock(&plugin_lock);
    +out:
    + return err;
    +}
    +EXPORT_SYMBOL(lirc_register_plugin);
    +
    +int lirc_unregister_plugin(int minor)
    +{
    + struct irctl *ir;
    + DECLARE_COMPLETION(tn);
    + DECLARE_COMPLETION(tn2);
    +
    + if (minor < 0 || minor >= MAX_IRCTL_DEVICES) {
    + printk(KERN_ERR "lirc_dev: lirc_unregister_plugin: "
    + "\"minor\" must be between 0 and %d!\n",
    + MAX_IRCTL_DEVICES-1);
    + return -EBADRQC;
    + }
    +
    + ir = &irctls[minor];
    +
    + mutex_lock(&plugin_lock);
    +
    + if (ir->p.minor != minor) {
    + printk(KERN_ERR "lirc_dev: lirc_unregister_plugin: "
    + "minor (%d) device not registered!", minor);
    + mutex_unlock(&plugin_lock);
    + return -ENOENT;
    + }
    +
    + /* end up polling thread */
    + if (ir->task) {
    + wake_up_process(ir->task);
    + kthread_stop(ir->task);
    + }
    +
    + dprintk("lirc_dev: plugin %s unregistered from minor number = %d\n",
    + ir->p.name, ir->p.minor);
    +
    + ir->attached = 0;
    + if (ir->open) {
    + dprintk(LOGHEAD "releasing opened plugin\n",
    + ir->p.name, ir->p.minor);
    + wake_up_interruptible(&ir->buf->wait_poll);
    + mutex_lock(&ir->buffer_lock);
    + ir->p.set_use_dec(ir->p.data);
    + module_put(ir->p.owner);
    + mutex_unlock(&ir->buffer_lock);
    + } else
    + cleanup(ir);
    + mutex_unlock(&plugin_lock);
    +
    +/*
    + * Recent kernels should handle this autmatically by increasing/decreasing
    + * use count when a dependant module is loaded/unloaded.
    + */
    +
    + return SUCCESS;
    +}
    +EXPORT_SYMBOL(lirc_unregister_plugin);
    +
    +/*
    + *
    + */
    +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);
    +
    + if (mutex_lock_interruptible(&plugin_lock))
    + return -ERESTARTSYS;
    +
    + if (ir->p.minor == NOPLUG) {
    + mutex_unlock(&plugin_lock);
    + dprintk(LOGHEAD "open result = -ENODEV\n",
    + ir->p.name, ir->p.minor);
    + return -ENODEV;
    + }
    +
    + if (ir->open) {
    + mutex_unlock(&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;
    +
    + if (ir->p.owner != NULL && try_module_get(ir->p.owner)) {
    + ++ir->open;
    + retval = ir->p.set_use_inc(ir->p.data);
    +
    + if (retval != SUCCESS) {
    + module_put(ir->p.owner);
    + --ir->open;
    + }
    + } else {
    + if (ir->p.owner == NULL)
    + dprintk(LOGHEAD "no module owner!!!\n",
    + ir->p.name, ir->p.minor);
    +
    + retval = -ENODEV;
    + }
    +
    + dprintk(LOGHEAD "open result = %d\n", ir->p.name, ir->p.minor, retval);
    + mutex_unlock(&plugin_lock);
    +
    + return retval;
    +}
    +
    +/*
    + *
    + */
    +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);
    +
    + if (mutex_lock_interruptible(&plugin_lock))
    + return -ERESTARTSYS;
    +
    + --ir->open;
    + if (ir->attached) {
    + ir->p.set_use_dec(ir->p.data);
    + module_put(ir->p.owner);
    + } else {
    + cleanup(ir);
    + }
    +
    + mutex_unlock(&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)];
    + unsigned int ret;
    +
    + 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);
    +
    + mutex_lock(&ir->buffer_lock);
    + if (!ir->attached) {
    + mutex_unlock(&ir->buffer_lock);
    + return POLLERR;
    + }
    +
    + 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");
    +
    + ret = lirc_buffer_empty(ir->buf) ? 0 : (POLLIN|POLLRDNORM);
    +
    + mutex_unlock(&ir->buffer_lock);
    + return ret;
    +}
    +
    +/*
    + *
    + */
    +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 (0x%x)\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 || !ir->attached) {
    + 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 changing either
    + */
    + 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 (mutex_lock_interruptible(&ir->buffer_lock))
    + return -ERESTARTSYS;
    + if (!ir->attached) {
    + mutex_unlock(&ir->buffer_lock);
    + return -ENODEV;
    + }
    +
    + if (length % ir->buf->chunk_size) {
    + dprintk(LOGHEAD "read result = -EINVAL\n",
    + ir->p.name, ir->p.minor);
    + mutex_unlock(&ir->buffer_lock);
    + 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);
    + set_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) {
    + ret = -EWOULDBLOCK;
    + break;
    + }
    + if (signal_pending(current)) {
    + ret = -ERESTARTSYS;
    + break;
    + }
    + schedule();
    + set_current_state(TASK_INTERRUPTIBLE);
    + if (!ir->attached) {
    + ret = -ENODEV;
    + break;
    + }
    + } 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);
    + set_current_state(TASK_RUNNING);
    + mutex_unlock(&ir->buffer_lock);
    +
    + dprintk(LOGHEAD "read result = %s (%d)\n",
    + ir->p.name, ir->p.minor, ret ? "-EFAULT" : "OK", ret);
    +
    + return ret ? ret : written;
    +}
    +
    +
    +void *lirc_get_pdata(struct file *file)
    +{
    + void *data = NULL;
    +
    + if (file && file->f_dentry && file->f_dentry->d_inode &&
    + file->f_dentry->d_inode->i_rdev) {
    + struct irctl *ir;
    + ir = &irctls[MINOR(file->f_dentry->d_inode->i_rdev)];
    + data = ir->p.data;
    + }
    +
    + return data;
    +}
    +EXPORT_SYMBOL(lirc_get_pdata);
    +
    +
    +static ssize_t irctl_write(struct file *file, const char *buffer,
    + size_t length, loff_t *ppos)
    +{
    + struct irctl *ir = &irctls[MINOR(file->f_dentry->d_inode->i_rdev)];
    +
    + dprintk(LOGHEAD "write called\n", ir->p.name, ir->p.minor);
    +
    + /* if the plugin has a specific read function use it instead */
    + if (ir->p.fops && ir->p.fops->write)
    + return ir->p.fops->write(file, buffer, length, ppos);
    +
    + if (!ir->attached)
    + return -ENODEV;
    +
    + return -EINVAL;
    +}
    +
    +
    +static struct file_operations fops = {
    + .read = irctl_read,
    + .write = irctl_write,
    + .poll = irctl_poll,
    + .ioctl = irctl_ioctl,
    + .open = irctl_open,
    + .release = irctl_close
    +};
    +
    +
    +static int lirc_dev_init(void)
    +{
    + int i;
    +
    + for (i = 0; i < MAX_IRCTL_DEVICES; ++i)
    + init_irctl(&irctls[i]);
    +
    + if (register_chrdev(IRCTL_DEV_MAJOR, IRCTL_DEV_NAME, &fops)) {
    + printk(KERN_ERR "lirc_dev: register_chrdev failed\n");
    + goto out;
    + }
    +
    + lirc_class = class_create(THIS_MODULE, "lirc");
    + if (IS_ERR(lirc_class)) {
    + printk(KERN_ERR "lirc_dev: class_create failed\n");
    + goto out_unregister;
    + }
    +
    + printk(KERN_INFO "lirc_dev: IR Remote Control driver registered, "
    + "major %d \n", IRCTL_DEV_MAJOR);
    +
    + return SUCCESS;
    +
    +out_unregister:
    + /* unregister_chrdev returns void now */
    + unregister_chrdev(IRCTL_DEV_MAJOR, IRCTL_DEV_NAME);
    +out:
    + return -1;
    +}
    +
    +/* ---------------------------------------------------------------------- */
    +
    +#ifdef MODULE
    +
    +/*
    + *
    + */
    +int init_module(void)
    +{
    + return lirc_dev_init();
    +}
    +
    +/*
    + *
    + */
    +void cleanup_module(void)
    +{
    + /* unregister_chrdev returns void now */
    + unregister_chrdev(IRCTL_DEV_MAJOR, IRCTL_DEV_NAME);
    + class_destroy(lirc_class);
    + dprintk("lirc_dev: module unloaded\n");
    +}
    +
    +MODULE_DESCRIPTION("LIRC base driver module");
    +MODULE_AUTHOR("Artur Lipowski");
    +MODULE_LICENSE("GPL");
    +MODULE_ALIAS_CHARDEV_MAJOR(IRCTL_DEV_MAJOR);
    +
    +module_param(debug, bool, 0644);
    +MODULE_PARM_DESC(debug, "Enable debugging messages");
    +
    +#else /* not a MODULE */
    +subsys_initcall(lirc_dev_init);
    +
    +#endif /* MODULE */
    +
    +/*
    + * Overrides for Emacs so that we follow Linus's tabbing style.
    + * ---------------------------------------------------------------------------
    + * Local variables:
    + * c-basic-offset: 8
    + * End:
    + */
    diff --git a/drivers/input/lirc/lirc_dev.h b/drivers/input/lirc/lirc_dev.h
    new file mode 100644
    index 0000000..920dd43
    --- /dev/null
    +++ b/drivers/input/lirc/lirc_dev.h
    @@ -0,0 +1,262 @@
    +/*
    + * LIRC base driver
    + *
    + * (L) by Artur Lipowski <alipowski@interia.pl>
    + * This code is licensed under GNU GPL
    + *
    + */
    +
    +#ifndef _LINUX_LIRC_DEV_H
    +#define _LINUX_LIRC_DEV_H
    +
    +#define MAX_IRCTL_DEVICES 4
    +#define BUFLEN 16
    +
    +/* #define LIRC_BUFF_POWER_OF_2 */
    +#ifdef LIRC_BUFF_POWER_OF_2
    +#define mod(n, div) ((n) & ((div) - 1))
    +#else
    +#define mod(n, div) ((n) % (div))
    +#endif
    +#include <linux/slab.h>
    +#include <linux/fs.h>
    +
    +struct lirc_buffer {
    + wait_queue_head_t wait_poll;
    + spinlock_t lock;
    +
    + unsigned char *data;
    + unsigned int chunk_size;
    + unsigned int size; /* in chunks */
    + unsigned int fill; /* in chunks */
    + int head, tail; /* in chunks */
    + /* Using chunks instead of bytes pretends to simplify boundary checking
    + * And should allow for some performance fine tunning later */
    +};
    +static inline void _lirc_buffer_clear(struct lirc_buffer *buf)
    +{
    + buf->head = 0;
    + buf->tail = 0;
    + buf->fill = 0;
    +}
    +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);
    + _lirc_buffer_clear(buf);
    + 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 = 0;
    + buf->tail = 0;
    + 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;
    +}
    +static inline void lirc_buffer_lock(struct lirc_buffer *buf,
    + unsigned long *flags)
    +{
    + spin_lock_irqsave(&buf->lock, *flags);
    +}
    +static inline void lirc_buffer_unlock(struct lirc_buffer *buf,
    + unsigned long *flags)
    +{
    + spin_unlock_irqrestore(&buf->lock, *flags);
    +}
    +static inline void lirc_buffer_clear(struct lirc_buffer *buf)
    +{
    + unsigned long flags;
    + lirc_buffer_lock(buf, &flags);
    + _lirc_buffer_clear(buf);
    + lirc_buffer_unlock(buf, &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;
    + struct device *dev;
    + struct module *owner;
    +};
    +/* 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.
    + *
    + * owner:
    + * the module owning this struct
    + *
    + */
    +
    +
    +/* 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);
    +
    +/* Returns the private data stored in the lirc_plugin
    + * associated with the given device file pointer.
    + */
    +void *lirc_get_pdata(struct file *file);
    +
    +#endif
    diff --git a/drivers/input/lirc/lirc_i2c.c b/drivers/input/lirc/lirc_i2c.c
    new file mode 100644
    index 0000000..4714641
    --- /dev/null
    +++ b/drivers/input/lirc/lirc_i2c.c
    @@ -0,0 +1,639 @@
    +/*
    + * i2c IR lirc plugin for Hauppauge and Pixelview cards - new 2.3.x i2c stack
    + *
    + * Copyright (c) 2000 Gerd Knorr <kraxel@goldbach.in-berlin.de>
    + * modified for PixelView (BT878P+W/FM) by
    + * Michal Kochanowicz <mkochano@pld.org.pl>
    + * Christoph Bartelmus <lirc@bartelmus.de>
    + * modified for KNC ONE TV Station/Anubis Typhoon TView Tuner by
    + * Ulrich Mueller <ulrich.mueller42@web.de>
    + * modified for Asus TV-Box and Creative/VisionTek BreakOut-Box by
    + * Stefan Jahn <stefan@lkcc.org>
    + * modified for inclusion into kernel sources by
    + * Jerome Brock <jbrock@users.sourceforge.net>
    + * modified for Leadtek Winfast PVR2000 by
    + * Thomas Reitmayr (treitmayr@yahoo.com)
    + * modified for Hauppauge HVR-1300 by
    + * Jan Frey (jfrey@gmx.de)
    + *
    + * parts are cut&pasted from the old lirc_haup.c driver
    + *
    + * This program is free software; you can redistribute it and/or modify
    + * it under the terms of the GNU General Public License as published by
    + * the Free Software Foundation; either version 2 of the License, or
    + * (at your option) any later version.
    + *
    + * This program is distributed in the hope that it will be useful,
    + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    + * GNU General Public License for more details.
    + *
    + * You should have received a copy of the GNU General Public License
    + * along with this program; if not, write to the Free Software
    + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
    + *
    + */
    +
    +
    +#include <linux/version.h>
    +#include <linux/module.h>
    +#include <linux/kmod.h>
    +#include <linux/kernel.h>
    +#include <linux/sched.h>
    +#include <linux/string.h>
    +#include <linux/timer.h>
    +#include <linux/delay.h>
    +#include <linux/errno.h>
    +#include <linux/slab.h>
    +#include <linux/i2c.h>
    +#include <linux/i2c-algo-bit.h>
    +
    +#include <linux/semaphore.h>
    +
    +#include "lirc_dev.h"
    +
    +struct IR {
    + struct lirc_plugin l;
    + struct i2c_client c;
    + int nextkey;
    + unsigned char b[3];
    + unsigned char bits;
    + unsigned char flag;
    +};
    +
    +/* ----------------------------------------------------------------------- */
    +
    +#define DEVICE_NAME "lirc_i2c"
    +
    +/* ----------------------------------------------------------------------- */
    +/* insmod parameters */
    +
    +static int debug; /* debug output */
    +static int minor = -1; /* minor number */
    +
    +#define dprintk(fmt, args...) \
    + do { \
    + if (debug) \
    + printk(KERN_DEBUG DEVICE_NAME ": " fmt, \
    + ## args); \
    + } while (0)
    +
    +/* ----------------------------------------------------------------------- */
    +
    +static inline int reverse(int data, int bits)
    +{
    + int i;
    + int c;
    +
    + for (c = 0, i = 0; i < bits; i++)
    + c |= ((data & (1<<i)) ? 1 : 0) << (bits-1-i);
    +
    + return c;
    +}
    +
    +static int add_to_buf_adap(void *data, struct lirc_buffer *buf)
    +{
    + struct IR *ir = data;
    + unsigned char keybuf[4];
    +
    + keybuf[0] = 0x00;
    + i2c_master_send(&ir->c, keybuf, 1);
    + /* poll IR chip */
    + if (i2c_master_recv(&ir->c, keybuf, sizeof(keybuf)) != sizeof(keybuf)) {
    + dprintk("read error\n");
    + return -EIO;
    + }
    +
    + dprintk("key (0x%02x%02x%02x%02x)\n",
    + keybuf[0], keybuf[1], keybuf[2], keybuf[3]);
    +
    + /* key pressed ? */
    + if (keybuf[2] == 0xff)
    + return -ENODATA;
    +
    + /* remove repeat bit */
    + keybuf[2] &= 0x7f;
    + keybuf[3] |= 0x80;
    +
    + lirc_buffer_write_1(buf, keybuf);
    + return 0;
    +}
    +
    +static int add_to_buf_pcf8574(void *data, struct lirc_buffer *buf)
    +{
    + struct IR *ir = data;
    + int rc;
    + unsigned char all, mask;
    + unsigned char key;
    +
    + /* compute all valid bits (key code + pressed/release flag) */
    + all = ir->bits | ir->flag;
    +
    + /* save IR writable mask bits */
    + mask = i2c_smbus_read_byte(&ir->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("%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("%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;
    +}
    +
    +/* common for Hauppauge IR receivers */
    +static int add_to_buf_haup_common(void *data, struct lirc_buffer *buf,
    + unsigned char *keybuf, int size, int offset)
    +{
    + struct IR *ir = data;
    + __u16 code;
    + unsigned char codes[2];
    +
    + /* poll IR chip */
    + if (size == i2c_master_recv(&ir->c, keybuf, size)) {
    + ir->b[0] = keybuf[offset];
    + ir->b[1] = keybuf[offset+1];
    + ir->b[2] = keybuf[offset+2];
    + dprintk("key (0x%02x/0x%02x)\n", ir->b[0], ir->b[1]);
    + } else {
    + dprintk("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;
    +}
    +
    +/* specific for the Hauppauge PVR150 IR receiver */
    +static int add_to_buf_haup_pvr150(void *data, struct lirc_buffer *buf)
    +{
    + unsigned char keybuf[6];
    + /* fetch 6 bytes, first relevant is at offset 3 */
    + return add_to_buf_haup_common(data, buf, keybuf, 6, 3);
    +}
    +
    +/* used for all Hauppauge IR receivers but the PVR150 */
    +static int add_to_buf_haup(void *data, struct lirc_buffer *buf)
    +{
    + unsigned char keybuf[3];
    + /* fetch 3 bytes, first relevant is at offset 0 */
    + return add_to_buf_haup_common(data, buf, keybuf, 3, 0);
    +}
    +
    +
    +static int add_to_buf_pvr2000(void *data, struct lirc_buffer *buf)
    +{
    + struct IR *ir = data;
    + unsigned char key;
    + s32 flags;
    + s32 code;
    +
    + /* poll IR chip */
    + flags = i2c_smbus_read_byte_data(&ir->c, 0x10);
    + if (-1 == flags) {
    + dprintk("read error\n");
    + return -ENODATA;
    + }
    + /* key pressed ? */
    + if (0 == (flags & 0x80))
    + return -ENODATA;
    +
    + /* read actual key code */
    + code = i2c_smbus_read_byte_data(&ir->c, 0x00);
    + if (-1 == code) {
    + dprintk("read error\n");
    + return -ENODATA;
    + }
    +
    + key = code & 0xFF;
    +
    + dprintk("IR Key/Flags: (0x%02x/0x%02x)\n", key, flags & 0xFF);
    +
    + /* return it */
    + lirc_buffer_write_1(buf, &key);
    + 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("read error\n");
    + return -1;
    + }
    + dprintk("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("read error\n");
    + return -ENODATA;
    + }
    + /* ignore 0xaa */
    + if (key == 0xaa)
    + return -ENODATA;
    + dprintk("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("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("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 */
    + i2c_use_client(&ir->c);
    +
    + return 0;
    +}
    +
    +static void set_use_dec(void *data)
    +{
    + struct IR *ir = data;
    +
    + i2c_release_client(&ir->c);
    +}
    +
    +static struct lirc_plugin lirc_template = {
    + .name = "lirc_i2c",
    + .set_use_inc = set_use_inc,
    + .set_use_dec = set_use_dec,
    + .dev = NULL,
    + .owner = THIS_MODULE,
    +};
    +
    +/* ----------------------------------------------------------------------- */
    +
    +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 = {
    + .driver = {
    + .owner = THIS_MODULE,
    + .name = "i2c ir driver",
    + },
    + .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;
    + int err;
    +
    + client_template.adapter = adap;
    + client_template.addr = addr;
    +
    + ir = kmalloc(sizeof(struct IR), GFP_KERNEL);
    + if (!ir)
    + 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;
    + i2c_set_clientdata(&ir->c, ir);
    + ir->l.data = ir;
    + ir->l.minor = minor;
    + ir->l.sample_rate = 10;
    + ir->nextkey = -1;
    +
    + switch (addr) {
    + case 0x64:
    + strlcpy(ir->c.name, "Pixelview IR", I2C_NAME_SIZE);
    + ir->l.code_length = 8;
    + ir->l.add_to_buf = add_to_buf_pixelview;
    + break;
    + case 0x4b:
    + strlcpy(ir->c.name, "PV951 IR", I2C_NAME_SIZE);
    + ir->l.code_length = 32;
    + ir->l.add_to_buf = add_to_buf_pv951;
    + break;
    + case 0x71:
    + if (adap->id == I2C_HW_B_BT848 ||
    + adap->id == I2C_HW_B_CX2341X) {
    + /* The PVR150 IR receiver uses the same protocol as
    + * other Hauppauge cards, but the data flow is
    + * different, so we need to deal with it by its own. */
    + strlcpy(ir->c.name, "Hauppauge PVR150", I2C_NAME_SIZE);
    + } else /* I2C_HW_B_CX2388x */
    + strlcpy(ir->c.name, "Hauppauge HVR1300", I2C_NAME_SIZE);
    + ir->l.code_length = 13;
    + ir->l.add_to_buf = add_to_buf_haup_pvr150;
    + break;
    + case 0x6b:
    + strlcpy(ir->c.name, "Adaptec IR", I2C_NAME_SIZE);
    + ir->l.code_length = 32;
    + ir->l.add_to_buf = add_to_buf_adap;
    + break;
    + case 0x18:
    + case 0x1a:
    + if (adap->id == I2C_HW_B_BT848 ||
    + adap->id == I2C_HW_B_CX2341X) {
    + strlcpy(ir->c.name, "Hauppauge IR", I2C_NAME_SIZE);
    + ir->l.code_length = 13;
    + ir->l.add_to_buf = add_to_buf_haup;
    + } else { /* I2C_HW_B_CX2388x */
    + strlcpy(ir->c.name, "Leadtek IR", I2C_NAME_SIZE);
    + ir->l.code_length = 8;
    + ir->l.add_to_buf = add_to_buf_pvr2000;
    + }
    + break;
    + case 0x30:
    + strlcpy(ir->c.name, "KNC ONE IR", I2C_NAME_SIZE);
    + ir->l.code_length = 8;
    + ir->l.add_to_buf = add_to_buf_knc1;
    + break;
    + case 0x21:
    + case 0x23:
    + strlcpy(ir->c.name, "TV-Box IR", I2C_NAME_SIZE);
    + 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(KERN_INFO "lirc_i2c: chip 0x%x found @ 0x%02x (%s)\n",
    + adap->id, addr, ir->c.name);
    +
    + /* register device */
    + err = i2c_attach_client(&ir->c);
    + if (err) {
    + kfree(ir);
    + return err;
    + }
    + ir->l.minor = lirc_register_plugin(&ir->l);
    +
    + return 0;
    +}
    +
    +static int ir_detach(struct i2c_client *client)
    +{
    + struct IR *ir = i2c_get_clientdata(client);
    +
    + /* unregister device */
    + 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
    + *
    + * The i2c address for the Hauppauge PVR-150 card is 0xe2,
    + * so we need to probe 0x71 as well. */
    +
    + static const int probe[] = {
    + 0x1a, /* Hauppauge IR external */
    + 0x18, /* Hauppauge IR internal */
    + 0x71, /* Hauppauge IR (PVR150) */
    + 0x4b, /* PV951 IR */
    + 0x64, /* Pixelview IR */
    + 0x30, /* KNC ONE IR */
    + 0x6b, /* Adaptec IR */
    + -1};
    +
    + static const int probe_cx88[] = {
    + 0x18, /* Leadtek Winfast PVR2000 */
    + 0x71, /* Hauppauge HVR-IR */
    + -1};
    +
    + struct i2c_client c;
    + char buf;
    + int i, rc;
    +
    + if (adap->id == I2C_HW_B_BT848 ||
    + adap->id == I2C_HW_B_CX2341X) {
    + 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("probe 0x%02x @ %s: %s\n",
    + probe[i], adap->name,
    + (1 == rc) ? "yes" : "no");
    + if (1 == rc)
    + ir_attach(adap, probe[i], 0, 0);
    + }
    + }
    +
    + /* Leadtek Winfast PVR2000 or Hauppauge HVR-1300 */
    + else if (adap->id == I2C_HW_B_CX2388x) {
    + memset(&c, 0, sizeof(c));
    + c.adapter = adap;
    + for (i = 0; -1 != probe_cx88[i]; i++) {
    + c.addr = probe_cx88[i];
    + rc = i2c_master_recv(&c, &buf, 1);
    + dprintk("probe 0x%02x @ %s: %s\n",
    + c.addr, adap->name,
    + (1 == rc) ? "yes" : "no");
    + if (1 == rc)
    + ir_attach(adap, c.addr, 0, 0);
    + }
    + }
    +
    + /* Asus TV-Box and Creative/VisionTek BreakOut-Box (PCF8574) */
    + else if (adap->id == 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("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
    +
    +int init_module(void)
    +{
    + request_module("bttv");
    + request_module("rivatv");
    + request_module("ivtv");
    + request_module("cx8800");
    + i2c_add_driver(&driver);
    + return 0;
    +}
    +
    +void cleanup_module(void)
    +{
    + i2c_del_driver(&driver);
    +}
    +
    +MODULE_DESCRIPTION("Infrared receiver driver for Hauppauge and "
    + "Pixelview cards (i2c stack)");
    +MODULE_AUTHOR("Gerd Knorr, Michal Kochanowicz, Christoph Bartelmus, "
    + "Ulrich Mueller, Stefan Jahn, Jerome Brock");
    +MODULE_LICENSE("GPL");
    +
    +module_param(minor, int, 0444);
    +MODULE_PARM_DESC(minor, "Preferred minor device number");
    +
    +module_param(debug, bool, 0644);
    +MODULE_PARM_DESC(debug, "Enable debugging messages");
    +
    +#endif /* MODULE */
    +
    +/*
    + * Overrides for Emacs so that we follow Linus's tabbing style.
    + * ---------------------------------------------------------------------------
    + * Local variables:
    + * c-basic-offset: 8
    + * End:
    + */
    diff --git a/drivers/input/lirc/lirc_igorplugusb.c b/drivers/input/lirc/lirc_igorplugusb.c
    new file mode 100644
    index 0000000..ab9bdd6
    --- /dev/null
    +++ b/drivers/input/lirc/lirc_igorplugusb.c
    @@ -0,0 +1,619 @@
    +/* lirc_igorplugusb - USB remote support for LIRC
    + *
    + * Supports the standard homebrew IgorPlugUSB receiver with Igor's firmware.
    + * See http://www.cesko.host.sk/IgorPlugUSB/IgorPlug-USB%20(AVR)_eng.htm
    + *
    + * The device can only record bursts of up to 36 pulses/spaces.
    + * Works fine with RC5. Longer commands lead to device buffer overrun.
    + * (Maybe a better firmware or a microcontroller with more ram can help?)
    + *
    + * Version 0.1 [beta status]
    + *
    + * Copyright (C) 2004 Jan M. Hochstein
    + * <hochstein@algo.informatik.tu-darmstadt.de>
    + *
    + * This driver was derived from:
    + * Paul Miller <pmiller9@users.sourceforge.net>
    + * "lirc_atiusb" module
    + * Vladimir Dergachev <volodya@minspring.com>'s 2002
    + * "USB ATI Remote support" (input device)
    + * Adrian Dewhurst <sailor-lk@sailorfrag.net>'s 2002
    + * "USB StreamZap remote driver" (LIRC)
    + * Artur Lipowski <alipowski@kki.net.pl>'s 2002
    + * "lirc_dev" and "lirc_gpio" LIRC modules
    + *
    + */
    +
    +/*
    + * This program is free software; you can redistribute it and/or modify
    + * it under the terms of the GNU General Public License as published by
    + * the Free Software Foundation; either version 2 of the License, or
    + * (at your option) any later version.
    + *
    + * This program is distributed in the hope that it will be useful,
    + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    + * GNU General Public License for more details.
    + *
    + * You should have received a copy of the GNU General Public License
    + * along with this program; if not, write to the Free Software
    + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
    + *
    + */
    +
    +#include <linux/version.h>
    +
    +#include <linux/autoconf.h>
    +#include <linux/module.h>
    +#include <linux/kernel.h>
    +#include <linux/kmod.h>
    +#include <linux/sched.h>
    +#include <linux/errno.h>
    +#include <linux/ioctl.h>
    +#include <linux/fs.h>
    +#include <linux/usb.h>
    +#include <linux/poll.h>
    +#include <linux/smp_lock.h>
    +#include <linux/time.h>
    +
    +#include "lirc.h"
    +#include "lirc_dev.h"
    +
    +
    +/* module identification */
    +#define DRIVER_VERSION "0.1"
    +#define DRIVER_AUTHOR \
    + "Jan M. Hochstein <hochstein@algo.informatik.tu-darmstadt.de>"
    +#define DRIVER_DESC "USB remote driver for LIRC"
    +#define DRIVER_NAME "lirc_igorplugusb"
    +
    +/* debugging support */
    +#ifdef CONFIG_USB_DEBUG
    +static int debug = 1;
    +#else
    +static int debug;
    +#endif
    +
    +#define dprintk(fmt, args...) \
    + do { \
    + if (debug) \
    + printk(KERN_DEBUG fmt, ## args); \
    + } while (0)
    +
    +/* general constants */
    +#define SUCCESS 0
    +
    +/* One mode2 pulse/space has 4 bytes. */
    +#define CODE_LENGTH sizeof(int)
    +
    +/* Igor's firmware cannot record bursts longer than 36. */
    +#define DEVICE_BUFLEN 36
    +
    +/** Header at the beginning of the device's buffer:
    + unsigned char data_length
    + unsigned char data_start (!=0 means ring-buffer overrun)
    + unsigned char counter (incremented by each burst)
    +**/
    +#define DEVICE_HEADERLEN 3
    +
    +/* This is for the gap */
    +#define ADDITIONAL_LIRC_BYTES 2
    +
    +/* times to poll per second */
    +#define SAMPLE_RATE 100
    +static int sample_rate = SAMPLE_RATE;
    +
    +
    +/**** Igor's USB Request Codes */
    +
    +#define SET_INFRABUFFER_EMPTY 1
    +/**
    + * Params: none
    + * Answer: empty
    + *
    +**/
    +
    +#define GET_INFRACODE 2
    +/**
    + * Params:
    + * wValue: offset to begin reading infra buffer
    + *
    + * Answer: infra data
    + *
    +**/
    +
    +#define SET_DATAPORT_DIRECTION 3
    +/**
    + * Params:
    + * wValue: (byte) 1 bit for each data port pin (0=in, 1=out)
    + *
    + * Answer: empty
    + *
    +**/
    +
    +#define GET_DATAPORT_DIRECTION 4
    +/**
    + * Params: none
    + *
    + * Answer: (byte) 1 bit for each data port pin (0=in, 1=out)
    + *
    +**/
    +
    +#define SET_OUT_DATAPORT 5
    +/**
    + * Params:
    + * wValue: byte to write to output data port
    + *
    + * Answer: empty
    + *
    +**/
    +
    +#define GET_OUT_DATAPORT 6
    +/**
    + * Params: none
    + *
    + * Answer: least significant 3 bits read from output data port
    + *
    +**/
    +
    +#define GET_IN_DATAPORT 7
    +/**
    + * Params: none
    + *
    + * Answer: least significant 3 bits read from input data port
    + *
    +**/
    +
    +#define READ_EEPROM 8
    +/**
    + * Params:
    + * wValue: offset to begin reading EEPROM
    + *
    + * Answer: EEPROM bytes
    + *
    +**/
    +
    +#define WRITE_EEPROM 9
    +/**
    + * Params:
    + * wValue: offset to EEPROM byte
    + * wIndex: byte to write
    + *
    + * Answer: empty
    + *
    +**/
    +
    +#define SEND_RS232 10
    +/**
    + * Params:
    + * wValue: byte to send
    + *
    + * Answer: empty
    + *
    +**/
    +
    +#define RECV_RS232 11
    +/**
    + * Params: none
    + *
    + * Answer: byte received
    + *
    +**/
    +
    +#define SET_RS232_BAUD 12
    +/**
    + * Params:
    + * wValue: byte to write to UART bit rate register (UBRR)
    + *
    + * Answer: empty
    + *
    +**/
    +
    +#define GET_RS232_BAUD 13
    +/**
    + * Params: none
    + *
    + * Answer: byte read from UART bit rate register (UBRR)
    + *
    +**/
    +
    +
    +/* data structure for each usb remote */
    +struct irctl {
    +
    + /* usb */
    + struct usb_device *usbdev;
    + struct urb *urb_in;
    + int devnum;
    +
    + unsigned char *buf_in;
    + unsigned int len_in;
    + int in_space;
    + struct timeval last_time;
    +
    + dma_addr_t dma_in;
    +
    + /* lirc */
    + struct lirc_plugin *p;
    +
    + /* handle sending (init strings) */
    + int send_flags;
    + wait_queue_head_t wait_out;
    +};
    +
    +static int unregister_from_lirc(struct irctl *ir)
    +{
    + struct lirc_plugin *p = ir->p;
    + int devnum;
    +
    + if (!ir->p)
    + return -EINVAL;
    +
    + devnum = ir->devnum;
    + dprintk(DRIVER_NAME "[%d]: unregister from lirc called\n", devnum);
    +
    + lirc_unregister_plugin(p->minor);
    +
    + printk(DRIVER_NAME "[%d]: usb remote disconnected\n", devnum);
    +
    + lirc_buffer_free(p->rbuf);
    + kfree(p->rbuf);
    + kfree(p);
    + kfree(ir);
    + ir->p = NULL;
    + return SUCCESS;
    +}
    +
    +static int set_use_inc(void *data)
    +{
    + struct irctl *ir = data;
    +
    + if (!ir) {
    + printk(DRIVER_NAME "[?]: set_use_inc called with no context\n");
    + return -EIO;
    + }
    + dprintk(DRIVER_NAME "[%d]: set use inc\n", ir->devnum);
    +
    + if (!ir->usbdev)
    + return -ENODEV;
    +
    + 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);
    +}
    +
    +
    +/**
    + * Called in user context.
    + * 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
    +**/
    +static int usb_remote_poll(void *data, struct lirc_buffer *buf)
    +{
    + int ret;
    + struct irctl *ir = (struct irctl *)data;
    +
    + if (!ir->usbdev) /* Has the device been removed? */
    + return -ENODEV;
    +
    + memset(ir->buf_in, 0, ir->len_in);
    +
    + ret = usb_control_msg(
    + ir->usbdev, usb_rcvctrlpipe(ir->usbdev, 0),
    + GET_INFRACODE, USB_TYPE_VENDOR|USB_DIR_IN,
    + 0/* offset */, /*unused*/0,
    + ir->buf_in, ir->len_in,
    + /*timeout*/HZ * USB_CTRL_GET_TIMEOUT);
    + if (ret > 0) {
    + int i = DEVICE_HEADERLEN;
    + int code, timediff;
    + struct timeval now;
    +
    + if (ret <= 1) /* ACK packet has 1 byte --> ignore */
    + return -ENODATA;
    +
    + dprintk(DRIVER_NAME ": Got %d bytes. Header: %02x %02x %02x\n",
    + ret, ir->buf_in[0], ir->buf_in[1], ir->buf_in[2]);
    +
    + if (ir->buf_in[2] != 0) {
    + printk(DRIVER_NAME "[%d]: Device buffer overrun.\n",
    + ir->devnum);
    + /* start at earliest byte */
    + i = DEVICE_HEADERLEN + ir->buf_in[2];
    + /* where are we now? space, gap or pulse? */
    + }
    +
    + do_gettimeofday(&now);
    + timediff = now.tv_sec - ir->last_time.tv_sec;
    + if (timediff + 1 > PULSE_MASK / 1000000)
    + timediff = PULSE_MASK;
    + else {
    + timediff *= 1000000;
    + timediff += now.tv_usec - ir->last_time.tv_usec;
    + }
    + ir->last_time.tv_sec = now.tv_sec;
    + ir->last_time.tv_usec = now.tv_usec;
    +
    + /* create leading gap */
    + code = timediff;
    + lirc_buffer_write_n(buf, (unsigned char *)&code, 1);
    + ir->in_space = 1; /* next comes a pulse */
    +
    + /* MODE2: pulse/space (PULSE_BIT) in 1us units */
    +
    + while (i < ret) {
    + /* 1 Igor-tick = 85.333333 us */
    + code = (unsigned int)ir->buf_in[i] * 85
    + + (unsigned int)ir->buf_in[i] / 3;
    + if (ir->in_space)
    + code |= PULSE_BIT;
    + lirc_buffer_write_n(buf, (unsigned char *)&code, 1);
    + /* 1 chunk = CODE_LENGTH bytes */
    + ir->in_space ^= 1;
    + ++i;
    + }
    +
    + ret = usb_control_msg(
    + ir->usbdev, usb_rcvctrlpipe(ir->usbdev, 0),
    + SET_INFRABUFFER_EMPTY, USB_TYPE_VENDOR|USB_DIR_IN,
    + /*unused*/0, /*unused*/0,
    + /*dummy*/ir->buf_in, /*dummy*/ir->len_in,
    + /*timeout*/HZ * USB_CTRL_GET_TIMEOUT);
    + if (ret < 0)
    + printk(DRIVER_NAME "[%d]: SET_INFRABUFFER_EMPTY: "
    + "error %d\n", ir->devnum, ret);
    + return SUCCESS;
    + } else
    + printk(DRIVER_NAME "[%d]: GET_INFRACODE: error %d\n",
    + ir->devnum, ret);
    +
    + return -ENODATA;
    +}
    +
    +
    +
    +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;
    + struct usb_host_endpoint *ep_ctl2;
    + struct irctl *ir = NULL;
    + struct lirc_plugin *plugin = NULL;
    + struct lirc_buffer *rbuf = NULL;
    + int devnum, pipe, maxp, bytes_in_key;
    + int minor = 0;
    + char buf[63], name[128] = "";
    + int mem_failure = 0;
    + int ret;
    +
    + dprintk(DRIVER_NAME ": usb probe called.\n");
    +
    + dev = interface_to_usbdev(intf);
    +
    + idesc = intf->cur_altsetting; /* in 2.6.6 */
    +
    + if (idesc->desc.bNumEndpoints != 1)
    + return -ENODEV;
    + ep_ctl2 = idesc->endpoint;
    + if (((ep_ctl2->desc.bEndpointAddress & USB_ENDPOINT_DIR_MASK)
    + != USB_DIR_IN)
    + || (ep_ctl2->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
    + != USB_ENDPOINT_XFER_CONTROL)
    + return -ENODEV;
    + pipe = usb_rcvctrlpipe(dev, ep_ctl2->desc.bEndpointAddress);
    + devnum = dev->devnum;
    + maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
    +
    + bytes_in_key = CODE_LENGTH;
    +
    + dprintk(DRIVER_NAME "[%d]: bytes_in_key=%d maxp=%d\n",
    + devnum, bytes_in_key, maxp);
    +
    +
    + /* allocate kernel memory */
    + mem_failure = 0;
    + ir = kmalloc(sizeof(struct irctl), GFP_KERNEL);
    + if (!ir) {
    + mem_failure = 1;
    + goto mem_failure_switch;
    + }
    +
    + memset(ir, 0, sizeof(struct irctl));
    +
    + plugin = kmalloc(sizeof(struct lirc_plugin), GFP_KERNEL);
    + if (!plugin) {
    + mem_failure = 2;
    + goto mem_failure_switch;
    + }
    +
    + rbuf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
    + if (!rbuf) {
    + mem_failure = 3;
    + goto mem_failure_switch;
    + }
    +
    + if (lirc_buffer_init(rbuf, bytes_in_key,
    + DEVICE_BUFLEN+ADDITIONAL_LIRC_BYTES)) {
    + mem_failure = 4;
    + goto mem_failure_switch;
    + }
    +
    + ir->buf_in = usb_buffer_alloc(dev,
    + DEVICE_BUFLEN+DEVICE_HEADERLEN,
    + GFP_ATOMIC, &ir->dma_in);
    + if (!ir->buf_in) {
    + mem_failure = 5;
    + goto mem_failure_switch;
    + }
    +
    + memset(plugin, 0, sizeof(struct lirc_plugin));
    +
    + strcpy(plugin->name, DRIVER_NAME " ");
    + plugin->minor = -1;
    + plugin->code_length = bytes_in_key*8; /* in bits */
    + plugin->features = LIRC_CAN_REC_MODE2;
    + plugin->data = ir;
    + plugin->rbuf = rbuf;
    + plugin->set_use_inc = &set_use_inc;
    + plugin->set_use_dec = &set_use_dec;
    + plugin->sample_rate = sample_rate; /* per second */
    + plugin->add_to_buf = &usb_remote_poll;
    + plugin->dev = &dev->dev;
    + plugin->owner = THIS_MODULE;
    +
    + init_waitqueue_head(&ir->wait_out);
    +
    + minor = lirc_register_plugin(plugin);
    + if (minor < 0)
    + mem_failure = 9;
    +
    +mem_failure_switch:
    +
    + /* free allocated memory in case of failure */
    + switch (mem_failure) {
    + case 9:
    + usb_buffer_free(dev, DEVICE_BUFLEN+DEVICE_HEADERLEN,
    + ir->buf_in, ir->dma_in);
    + case 5:
    + lirc_buffer_free(rbuf);
    + 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);
    + return -ENOMEM;
    + }
    +
    + plugin->minor = minor;
    + ir->p = plugin;
    + ir->devnum = devnum;
    + ir->usbdev = dev;
    + ir->len_in = DEVICE_BUFLEN+DEVICE_HEADERLEN;
    + ir->in_space = 1; /* First mode2 event is a space. */
    + do_gettimeofday(&ir->last_time);
    +
    + 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);
    +
    + /* clear device buffer */
    + ret = usb_control_msg(ir->usbdev, usb_rcvctrlpipe(ir->usbdev, 0),
    + SET_INFRABUFFER_EMPTY, USB_TYPE_VENDOR|USB_DIR_IN,
    + /*unused*/0, /*unused*/0,
    + /*dummy*/ir->buf_in, /*dummy*/ir->len_in,
    + /*timeout*/HZ * USB_CTRL_GET_TIMEOUT);
    + if (ret < 0)
    + printk(DRIVER_NAME "[%d]: SET_INFRABUFFER_EMPTY: error %d\n",
    + devnum, ret);
    +
    + usb_set_intfdata(intf, ir);
    + return SUCCESS;
    +}
    +
    +
    +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);
    +
    + if (!ir || !ir->p)
    + return;
    +
    + ir->usbdev = NULL;
    + wake_up_all(&ir->wait_out);
    +
    + usb_buffer_free(dev, ir->len_in, ir->buf_in, ir->dma_in);
    +
    + unregister_from_lirc(ir);
    +}
    +
    +static struct usb_device_id usb_remote_id_table[] = {
    + /* Igor Plug USB (Atmel's Manufact. ID) */
    + { USB_DEVICE(0x03eb, 0x0002) },
    +
    + /* Terminating entry */
    + { }
    +};
    +
    +static struct usb_driver usb_remote_driver = {
    + .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(KERN_INFO "\n"
    + DRIVER_NAME ": " DRIVER_DESC " v" DRIVER_VERSION "\n");
    + printk(DRIVER_NAME ": " DRIVER_AUTHOR "\n");
    + dprintk(DRIVER_NAME ": debug mode enabled\n");
    +
    +#ifdef MODULE
    + request_module("lirc_dev");
    +#endif
    +
    + i = usb_register(&usb_remote_driver);
    + if (i < 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);
    +}
    +
    +#ifdef MODULE
    +module_init(usb_remote_init);
    +module_exit(usb_remote_exit);
    +
    +#include <linux/vermagic.h>
    +MODULE_INFO(vermagic, VERMAGIC_STRING);
    +
    +MODULE_DESCRIPTION(DRIVER_DESC);
    +MODULE_AUTHOR(DRIVER_AUTHOR);
    +MODULE_LICENSE("GPL");
    +MODULE_DEVICE_TABLE(usb, usb_remote_id_table);
    +
    +module_param(sample_rate, int, 0644);
    +MODULE_PARM_DESC(sample_rate, "Sampling rate in Hz (default: 100)");
    +
    +#else /* not MODULE */
    +subsys_initcall(usb_remote_driver);
    +
    +#endif /* MODULE */
    +
    +/*
    + * Overrides for Emacs so that we follow Linus's tabbing style.
    + * ---------------------------------------------------------------------------
    + * Local variables:
    + * c-basic-offset: 8
    + * End:
    + */
    diff --git a/drivers/input/lirc/lirc_imon.c b/drivers/input/lirc/lirc_imon.c
    new file mode 100644
    index 0000000..bdfd953
    --- /dev/null
    +++ b/drivers/input/lirc/lirc_imon.c
    @@ -0,0 +1,1280 @@
    +/*
    + * lirc_imon.c: LIRC plugin/VFD driver for Ahanix/Soundgraph IMON IR/VFD
    + *
    + * Version 0.3
    + * Supports newer iMON models that send decoded IR signals.
    + * This includes the iMON PAD model.
    + * Removed module option for vfd_proto_6p. This driver supports
    + * multiple iMON devices so it is meaningless to have
    + * a global option to set protocol variants.
    + *
    + * Version 0.2 beta 2 [January 31, 2005]
    + * USB disconnect/reconnect no longer causes problems for lircd
    + *
    + * Version 0.2 beta 1 [January 29, 2005]
    + * Added support for original iMON receiver(ext USB)
    + *
    + * Version 0.2 alpha 2 [January 24, 2005]
    + * Added support for VFDs with 6-packet protocol
    + *
    + * Version 0.2 alpha 1 [January 23, 2005]
    + * Added support for 2.6 kernels
    + * Reworked disconnect handling
    + * Incorporated Changwoo Ryu's algorithm
    + *
    + * Version 0.1 alpha 1[July 5, 2004]
    + *
    + * Copyright(C) 2004 Venky Raju(dev@venky.ws)
    + *
    + * lirc_imon 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., 675 Mass Ave, Cambridge, MA 02139, USA.
    + *
    + */
    +#include <linux/version.h>
    +#include <linux/autoconf.h>
    +
    +#include <linux/errno.h>
    +#include <linux/init.h>
    +#include <linux/kernel.h>
    +#include <linux/module.h>
    +#include <linux/slab.h>
    +#include <linux/uaccess.h>
    +#include <linux/usb.h>
    +
    +#include "lirc.h"
    +#include "lirc_dev.h"
    +
    +
    +#define MOD_AUTHOR "Venky Raju <dev@venky.ws>"
    +#define MOD_DESC "Driver for Soundgraph iMON MultiMedia IR/VFD"
    +#define MOD_NAME "lirc_imon"
    +#define MOD_VERSION "0.3"
    +
    +#define VFD_MINOR_BASE 144 /* Same as LCD */
    +#define DEVICE_NAME "lcd%d"
    +
    +#define BUF_CHUNK_SIZE 4
    +#define BUF_SIZE 128
    +
    +#define BIT_DURATION 250 /* each bit received is 250us */
    +
    +#define SUCCESS 0
    +#define TRUE 1
    +#define FALSE 0
    +
    +
    +/* ------------------------------------------------------------
    + * P R O T O T Y P E S
    + * ------------------------------------------------------------
    + */
    +
    +/* USB Callback prototypes */
    +static int imon_probe(struct usb_interface *interface,
    + const struct usb_device_id *id);
    +static void imon_disconnect(struct usb_interface *interface);
    +static void usb_rx_callback(struct urb *urb);
    +static void usb_tx_callback(struct urb *urb);
    +
    +/* VFD file_operations function prototypes */
    +static int vfd_open(struct inode *inode, struct file *file);
    +static int vfd_close(struct inode *inode, struct file *file);
    +static ssize_t vfd_write(struct file *file, const char *buf,
    + size_t n_bytes, loff_t *pos);
    +
    +/* LCD file_operations override function prototypes */
    +static ssize_t lcd_write(struct file *file, const char *buf,
    + size_t n_bytes, loff_t *pos);
    +
    +/* LIRC plugin function prototypes */
    +static int ir_open(void *data);
    +static void ir_close(void *data);
    +
    +/* Driver init/exit prototypes */
    +static int __init imon_init(void);
    +static void __exit imon_exit(void);
    +
    +/* ------------------------------------------------------------
    + * G L O B A L S
    + * ------------------------------------------------------------
    + */
    +
    +struct imon_context {
    + struct usb_device *dev;
    + int vfd_supported; /* not all controllers do */
    + int vfd_isopen; /* VFD port has been opened */
    + int ir_isopen; /* IR port open */
    + int ir_isassociating; /* IR port open for association */
    + int dev_present; /* USB device presence */
    + struct mutex lock; /* to lock this object */
    + wait_queue_head_t remove_ok; /* For unexpected USB disconnects */
    +
    + int vfd_proto_6p; /* VFD requires 6th packet */
    + int ir_onboard_decode; /* IR signals decoded onboard */
    +
    + struct lirc_plugin *plugin;
    + struct usb_endpoint_descriptor *rx_endpoint;
    + struct usb_endpoint_descriptor *tx_endpoint;
    + struct urb *rx_urb;
    + struct urb *tx_urb;
    + int tx_control;
    + unsigned char usb_rx_buf[8];
    + unsigned char usb_tx_buf[8];
    +
    + struct rx_data {
    + int count; /* length of 0 or 1 sequence */
    + int prev_bit; /* logic level of sequence */
    + int initial_space; /* initial space flag */
    + } rx;
    +
    + struct tx_t {
    + unsigned char data_buf[35]; /* user data buffer */
    + struct completion finished; /* wait for write to finish */
    + atomic_t busy; /* write in progress */
    + int status; /* status of tx completion */
    + } tx;
    +};
    +
    +#define LOCK_CONTEXT mutex_lock(&context->lock)
    +#define UNLOCK_CONTEXT mutex_unlock(&context->lock)
    +
    +/* VFD file operations */
    +static struct file_operations vfd_fops = {
    + .owner = THIS_MODULE,
    + .open = &vfd_open,
    + .write = &vfd_write,
    + .release = &vfd_close
    +};
    +
    +/* USB Device ID for IMON USB Control Board */
    +static struct usb_device_id imon_usb_id_table[] = {
    + /* IMON USB Control Board (IR & VFD) */
    + { USB_DEVICE(0x0aa8, 0xffda) },
    + /* IMON USB Control Board (IR only) */
    + { USB_DEVICE(0x0aa8, 0x8001) },
    + /* IMON USB Control Board (IR & VFD) */
    + { USB_DEVICE(0x15c2, 0xffda) },
    + /* IMON USB Control Board (IR & VFD) */
    + { USB_DEVICE(0x15c2, 0xffdc) },
    + /* IMON USB Control Board (IR & LCD) */
    + { USB_DEVICE(0x15c2, 0x0038) },
    + /* IMON USB Control Board (ext IR only) */
    + { USB_DEVICE(0x04e8, 0xff30) },
    + {}
    +};
    +
    +/* Some iMON VFD models requires a 6th packet */
    +static struct usb_device_id vfd_proto_6p_list[] = {
    + { USB_DEVICE(0x15c2, 0xffda) },
    + { USB_DEVICE(0x15c2, 0xffdc) },
    + { USB_DEVICE(0x15c2, 0x0038) },
    + {}
    +};
    +static unsigned char vfd_packet6[] = {
    + 0x01, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF };
    +
    +/* Some iMON LCD models use control endpoint */
    +static struct usb_device_id lcd_control_endpoint_list[] = {
    + { USB_DEVICE(0x15c2, 0x0038) },
    + {}
    +};
    +
    +/* Newer iMON models decode the signal onboard */
    +static struct usb_device_id ir_onboard_decode_list[] = {
    + { USB_DEVICE(0x15c2, 0xffdc) },
    + { USB_DEVICE(0x15c2, 0x0038) },
    + {}
    +};
    +
    +/* USB Device data */
    +static struct usb_driver imon_driver = {
    + .name = MOD_NAME,
    + .probe = imon_probe,
    + .disconnect = imon_disconnect,
    + .id_table = imon_usb_id_table,
    +};
    +
    +static struct usb_class_driver imon_class = {
    + .name = DEVICE_NAME,
    + .fops = &vfd_fops,
    + .minor_base = VFD_MINOR_BASE,
    +};
    +
    +/* to prevent races between open() and disconnect() */
    +static DECLARE_MUTEX(disconnect_sem);
    +
    +static int debug;
    +
    +/* lcd or vfd? */
    +static int is_lcd;
    +
    +
    +/* ------------------------------------------------------------
    + * M O D U L E C O D E
    + * ------------------------------------------------------------
    + */
    +
    +MODULE_AUTHOR(MOD_AUTHOR);
    +MODULE_DESCRIPTION(MOD_DESC);
    +MODULE_LICENSE("GPL");
    +MODULE_DEVICE_TABLE(usb, imon_usb_id_table);
    +module_param(debug, int, 0);
    +MODULE_PARM_DESC(debug, "Debug messages: 0=no, 1=yes(default: no)");
    +module_param(is_lcd, int, 0);
    +MODULE_PARM_DESC(is_lcd, "The device is an LCD: 0=no (it's a VFD), "
    + "1=yes (default: no)");
    +
    +static inline void delete_context(struct imon_context *context)
    +{
    + if (context->vfd_supported)
    + usb_free_urb(context->tx_urb);
    + usb_free_urb(context->rx_urb);
    + lirc_buffer_free(context->plugin->rbuf);
    + kfree(context->plugin->rbuf);
    + kfree(context->plugin);
    + kfree(context);
    +
    + if (debug)
    + info("%s: context deleted", __func__);
    +}
    +
    +static inline void deregister_from_lirc(struct imon_context *context)
    +{
    + int retval;
    + int minor = context->plugin->minor;
    +
    + retval = lirc_unregister_plugin(minor);
    + if (retval)
    + err("%s: unable to deregister from lirc(%d)",
    + __func__, retval);
    + else
    + info("Deregistered iMON plugin(minor:%d)", minor);
    +
    +}
    +
    +/**
    + * Called when the VFD device(e.g. /dev/usb/lcd)
    + * is opened by the application.
    + */
    +static int vfd_open(struct inode *inode, struct file *file)
    +{
    + struct usb_interface *interface;
    + struct imon_context *context = NULL;
    + int subminor;
    + int retval = SUCCESS;
    +
    + /* prevent races with disconnect */
    + down(&disconnect_sem);
    +
    + subminor = iminor(inode);
    + interface = usb_find_interface(&imon_driver, subminor);
    + if (!interface) {
    + err("%s: could not find interface for minor %d",
    + __func__, subminor);
    + retval = -ENODEV;
    + goto exit;
    + }
    + context = usb_get_intfdata(interface);
    +
    + if (!context) {
    + err("%s: no context found for minor %d",
    + __func__, subminor);
    + retval = -ENODEV;
    + goto exit;
    + }
    +
    + LOCK_CONTEXT;
    +
    + if (!context->vfd_supported) {
    + err("%s: VFD not supported by device", __func__);
    + retval = -ENODEV;
    + } else if (context->vfd_isopen) {
    + err("%s: VFD port is already open", __func__);
    + retval = -EBUSY;
    + } else {
    + context->vfd_isopen = TRUE;
    + file->private_data = context;
    + info("VFD port opened");
    + }
    +
    + UNLOCK_CONTEXT;
    +
    +exit:
    + up(&disconnect_sem);
    + return retval;
    +}
    +
    +/**
    + * Called when the VFD device(e.g. /dev/usb/lcd)
    + * is closed by the application.
    + */
    +static int vfd_close(struct inode *inode, struct file *file)
    +{
    + struct imon_context *context = NULL;
    + int retval = SUCCESS;
    +
    + context = (struct imon_context *) file->private_data;
    +
    + if (!context) {
    + err("%s: no context for device", __func__);
    + return -ENODEV;
    + }
    +
    + LOCK_CONTEXT;
    +
    + if (!context->vfd_supported) {
    + err("%s: VFD not supported by device", __func__);
    + retval = -ENODEV;
    + } else if (!context->vfd_isopen) {
    + err("%s: VFD is not open", __func__);
    + retval = -EIO;
    + } else {
    + context->vfd_isopen = FALSE;
    + info("VFD port closed");
    + if (!context->dev_present && !context->ir_isopen) {
    + /* Device disconnected before close and IR port is not
    + * open. If IR port is open, context will be deleted by
    + * ir_close. */
    + UNLOCK_CONTEXT;
    + delete_context(context);
    + return retval;
    + }
    + }
    +
    + UNLOCK_CONTEXT;
    + return retval;
    +}
    +
    +/**
    + * Sends a packet to the VFD.
    + */
    +static inline int send_packet(struct imon_context *context)
    +{
    + unsigned int pipe;
    + int interval = 0;
    + int retval = SUCCESS;
    + struct usb_ctrlrequest *control_req = NULL;
    +
    + /* Check if we need to use control or interrupt urb */
    + if (!context->tx_control) {
    + pipe = usb_sndintpipe(context->dev,
    + context->tx_endpoint->bEndpointAddress);
    + interval = context->tx_endpoint->bInterval;
    +
    + usb_fill_int_urb(context->tx_urb, context->dev, pipe,
    + context->usb_tx_buf,
    + sizeof(context->usb_tx_buf),
    + usb_tx_callback, context, interval);
    +
    + context->tx_urb->actual_length = 0;
    + } else {
    + /* fill request into kmalloc'ed space: */
    + control_req = kmalloc(sizeof(struct usb_ctrlrequest), GFP_NOIO);
    + if (control_req == NULL)
    + return -ENOMEM;
    +
    + /* setup packet is '21 09 0200 0001 0008' */
    + control_req->bRequestType = 0x21;
    + control_req->bRequest = 0x09;
    + control_req->wValue = cpu_to_le16(0x0002);
    + control_req->wIndex = cpu_to_le16(0x0100);
    + control_req->wLength = cpu_to_le16(0x0800);
    +
    + /* control pipe is endpoint 0x00 */
    + pipe = usb_sndctrlpipe(context->dev, 0);
    +
    + /* build the control urb */
    + usb_fill_control_urb(context->tx_urb, context->dev, pipe,
    + (unsigned char *)control_req,
    + context->usb_tx_buf,
    + sizeof(context->usb_tx_buf),
    + usb_tx_callback, context);
    + context->tx_urb->actual_length = 0;
    + }
    +
    + init_completion(&context->tx.finished);
    + atomic_set(&(context->tx.busy), 1);
    +
    + retval = usb_submit_urb(context->tx_urb, GFP_KERNEL);
    + if (retval != SUCCESS) {
    + atomic_set(&(context->tx.busy), 0);
    + err("%s: error submitting urb(%d)", __func__, retval);
    + } else {
    + /* Wait for tranmission to complete(or abort) */
    + UNLOCK_CONTEXT;
    + wait_for_completion(&context->tx.finished);
    + LOCK_CONTEXT;
    +
    + retval = context->tx.status;
    + if (retval != SUCCESS)
    + err("%s: packet tx failed(%d)", __func__, retval);
    + }
    +
    + kfree(control_req);
    +
    + return retval;
    +}
    +
    +/**
    + * Sends an associate packet to the iMON 2.4G.
    + *
    + * This might not be such a good idea, since it has an id
    + * collition with some versions of the "IR & VFD" combo.
    + * The only way to determine if it is a RF version is to look
    + * at the product description string.(Which we currently do
    + * not fetch).
    + */
    +static inline int send_associate_24g(struct imon_context *context)
    +{
    + int retval;
    + const unsigned char packet[8] = { 0x01, 0x00, 0x00, 0x00,
    + 0x00, 0x00, 0x00, 0x20 };
    +
    + if (!context) {
    + err("%s: no context for device", __func__);
    + return -ENODEV;
    + }
    +
    + LOCK_CONTEXT;
    +
    + if (!context->dev_present) {
    + err("%s: no iMON device present", __func__);
    + retval = -ENODEV;
    + goto exit;
    + }
    +
    + memcpy(context->usb_tx_buf, packet, sizeof(packet));
    + retval = send_packet(context);
    +
    +exit:
    + UNLOCK_CONTEXT;
    +
    + return retval;
    +}
    +
    +/**
    + * This is the sysfs functions to handle the association og the iMON 2.4G LT.
    + *
    + *
    + */
    +
    +static ssize_t show_associate_remote(struct device *d,
    + struct device_attribute *attr,
    + char *buf)
    +{
    + struct imon_context *context = dev_get_drvdata(d);
    +
    + if (!context)
    + return -ENODEV;
    +
    + if (context->ir_isassociating) {
    + strcpy(buf, "The device it associating press some button "
    + "on the remote.\n");
    + } else if (context->ir_isopen) {
    + strcpy(buf, "Device is open and ready to associate.\n"
    + "Echo something into this file to start "
    + "the process.\n");
    + } else {
    + strcpy(buf, "Device is closed, you need to open it to "
    + "associate the remote(you can use irw).\n");
    + }
    + return strlen(buf);
    +}
    +
    +static ssize_t store_associate_remote(struct device *d,
    + struct device_attribute *attr,
    + const char *buf, size_t count)
    +{
    + struct imon_context *context;
    +
    + context = dev_get_drvdata(d);
    +
    + if (!context)
    + return -ENODEV;
    +
    + if (!context->ir_isopen)
    + return -EINVAL;
    +
    + if (context->ir_isopen) {
    + context->ir_isassociating = TRUE;
    + send_associate_24g(context);
    + }
    +
    + return count;
    +}
    +
    +static DEVICE_ATTR(associate_remote, S_IWUSR | S_IRUGO, show_associate_remote,
    + store_associate_remote);
    +
    +static struct attribute *imon_sysfs_entries[] = {
    + &dev_attr_associate_remote.attr,
    + NULL
    +};
    +
    +static struct attribute_group imon_attribute_group = {
    + .attrs = imon_sysfs_entries
    +};
    +
    +
    +
    +
    +/**
    + * Writes data to the VFD. The IMON VFD is 2x16 characters
    + * and requires data in 5 consecutive USB interrupt packets,
    + * each packet but the last carrying 7 bytes.
    + *
    + * I don't know if the VFD board supports features such as
    + * scrolling, clearing rows, blanking, etc. so at
    + * the caller must provide a full screen of data. If fewer
    + * than 32 bytes are provided spaces will be appended to
    + * generate a full screen.
    + */
    +static ssize_t vfd_write(struct file *file, const char *buf,
    + size_t n_bytes, loff_t *pos)
    +{
    + int i;
    + int offset;
    + int seq;
    + int retval = SUCCESS;
    + struct imon_context *context;
    +
    + context = (struct imon_context *) file->private_data;
    + if (!context) {
    + err("%s: no context for device", __func__);
    + return -ENODEV;
    + }
    +
    + LOCK_CONTEXT;
    +
    + if (!context->dev_present) {
    + err("%s: no iMON device present", __func__);
    + retval = -ENODEV;
    + goto exit;
    + }
    +
    + if (n_bytes <= 0 || n_bytes > 32) {
    + err("%s: invalid payload size", __func__);
    + retval = -EINVAL;
    + goto exit;
    + }
    +
    + if (copy_from_user(context->tx.data_buf, buf, n_bytes)) {
    + retval = -EFAULT;
    + goto exit;
    + }
    +
    + /* Pad with spaces */
    + for (i = n_bytes; i < 32; ++i)
    + context->tx.data_buf[i] = ' ';
    +
    + for (i = 32; i < 35; ++i)
    + context->tx.data_buf[i] = 0xFF;
    +
    + offset = 0;
    + seq = 0;
    +
    + do {
    + memcpy(context->usb_tx_buf, context->tx.data_buf + offset, 7);
    + context->usb_tx_buf[7] = (unsigned char) seq;
    +
    + retval = send_packet(context);
    + if (retval != SUCCESS) {
    + err("%s: send packet failed for packet #%d",
    + __func__, seq/2);
    + goto exit;
    + } else {
    + seq += 2;
    + offset += 7;
    + }
    +
    + } while (offset < 35);
    +
    + if (context->vfd_proto_6p) {
    + /* Send packet #6 */
    + memcpy(context->usb_tx_buf, vfd_packet6, 7);
    + context->usb_tx_buf[7] = (unsigned char) seq;
    + retval = send_packet(context);
    + if (retval != SUCCESS)
    + err("%s: send packet failed for packet #%d",
    + __func__, seq/2);
    + }
    +
    +exit:
    + UNLOCK_CONTEXT;
    +
    + return (retval == SUCCESS) ? n_bytes : retval;
    +}
    +
    +/**
    + * Writes data to the LCD. The iMON OEM LCD screen excepts 8-byte
    + * packets. We accept data as 16 hexadecimal digits, followed by a
    + * newline (to make it easy to drive the device from a command-line
    + * -- even though the actual binary data is a bit complicated).
    + *
    + * The device itself is not a "traditional" text-mode display. It's
    + * actually a 16x96 pixel bitmap display. That means if you want to
    + * display text, you've got to have your own "font" and translate the
    + * text into bitmaps for display. This is really flexible (you can
    + * display whatever diacritics you need, and so on), but it's also
    + * a lot more complicated than most LCDs...
    + */
    +static ssize_t lcd_write(struct file *file, const char *buf,
    + size_t n_bytes, loff_t *pos)
    +{
    + int retval = SUCCESS;
    + struct imon_context *context;
    +
    + context = (struct imon_context *) file->private_data;
    + if (!context) {
    + err("%s: no context for device", __func__);
    + return -ENODEV;
    + }
    +
    + LOCK_CONTEXT;
    +
    + if (!context->dev_present) {
    + err("%s: no iMON device present", __func__);
    + retval = -ENODEV;
    + goto exit;
    + }
    +
    + if (n_bytes != 8) {
    + err("%s: invalid payload size: %d (expecting 8)",
    + __func__, (int) n_bytes);
    + retval = -EINVAL;
    + goto exit;
    + }
    +
    + if (copy_from_user(context->usb_tx_buf, buf, 8)) {
    + retval = -EFAULT;
    + goto exit;
    + }
    +
    + retval = send_packet(context);
    + if (retval != SUCCESS) {
    + err("%s: send packet failed!", __func__);
    + goto exit;
    + } else if (debug) {
    + info("%s: write %d bytes to LCD", __func__, (int) n_bytes);
    + }
    +exit:
    + UNLOCK_CONTEXT;
    + return (retval == SUCCESS) ? n_bytes : retval;
    +}
    +
    +/**
    + * Callback function for USB core API: transmit data
    + */
    +static void usb_tx_callback(struct urb *urb)
    +{
    + struct imon_context *context;
    +
    + if (!urb)
    + return;
    + context = (struct imon_context *) urb->context;
    + if (!context)
    + return;
    +
    + context->tx.status = urb->status;
    +
    + /* notify waiters that write has finished */
    + atomic_set(&context->tx.busy, 0);
    + complete(&context->tx.finished);
    +
    + return;
    +}
    +
    +/**
    + * Called by lirc_dev when the application opens /dev/lirc
    + */
    +static int ir_open(void *data)
    +{
    + int retval = SUCCESS;
    + struct imon_context *context;
    +
    + /* prevent races with disconnect */
    + down(&disconnect_sem);
    +
    + context = (struct imon_context *) data;
    +
    + LOCK_CONTEXT;
    +
    + if (context->ir_isopen) {
    + err("%s: IR port is already open", __func__);
    + retval = -EBUSY;
    + goto exit;
    + }
    +
    + /* initial IR protocol decode variables */
    + context->rx.count = 0;
    + context->rx.initial_space = 1;
    + context->rx.prev_bit = 0;
    +
    + usb_fill_int_urb(context->rx_urb, context->dev,
    + usb_rcvintpipe(context->dev,
    + context->rx_endpoint->bEndpointAddress),
    + context->usb_rx_buf, sizeof(context->usb_rx_buf),
    + usb_rx_callback, context, context->rx_endpoint->bInterval);
    +
    + retval = usb_submit_urb(context->rx_urb, GFP_KERNEL);
    +
    + if (retval)
    + err("%s: usb_submit_urb failed for ir_open(%d)",
    + __func__, retval);
    + else {
    + context->ir_isopen = TRUE;
    + info("IR port opened");
    + }
    +
    +exit:
    + UNLOCK_CONTEXT;
    +
    + up(&disconnect_sem);
    + return SUCCESS;
    +}
    +
    +/**
    + * Called by lirc_dev when the application closes /dev/lirc
    + */
    +static void ir_close(void *data)
    +{
    + struct imon_context *context;
    +
    + context = (struct imon_context *)data;
    + if (!context) {
    + err("%s: no context for device", __func__);
    + return;
    + }
    +
    + LOCK_CONTEXT;
    +
    + usb_kill_urb(context->rx_urb);
    + context->ir_isopen = FALSE;
    + context->ir_isassociating = FALSE;
    + info("IR port closed");
    +
    + if (!context->dev_present) {
    + /* Device disconnected while IR port was
    + * still open. Plugin was not deregistered
    + * at disconnect time, so do it now. */
    + deregister_from_lirc(context);
    +
    + if (!context->vfd_isopen) {
    + UNLOCK_CONTEXT;
    + delete_context(context);
    + return;
    + }
    + /* If VFD port is open, context will be deleted by vfd_close */
    + }
    +
    + UNLOCK_CONTEXT;
    + return;
    +}
    +
    +/**
    + * Convert bit count to time duration(in us) and submit
    + * the value to lirc_dev.
    + */
    +static inline void submit_data(struct imon_context *context)
    +{
    + unsigned char buf[4];
    + int value = context->rx.count;
    + int i;
    +
    + if (debug)
    + info("submitting data to LIRC\n");
    +
    + value *= BIT_DURATION;
    + value &= PULSE_MASK;
    + if (context->rx.prev_bit)
    + value |= PULSE_BIT;
    +
    + for (i = 0; i < 4; ++i)
    + buf[i] = value>>(i*8);
    +
    + lirc_buffer_write_1(context->plugin->rbuf, buf);
    + wake_up(&context->plugin->rbuf->wait_poll);
    + return;
    +}
    +
    +/**
    + * Process the incoming packet
    + */
    +static inline void incoming_packet(struct imon_context *context,
    + struct urb *urb)
    +{
    + int len = urb->actual_length;
    + unsigned char *buf = urb->transfer_buffer;
    + int octet, bit;
    + unsigned char mask;
    + int chunk_num;
    +#ifdef DEBUG
    + int i;
    +#endif
    +
    + if (len != 8) {
    + warn("%s: invalid incoming packet size(%d)",
    + __func__, len);
    + return;
    + }
    +
    + /* iMON 2.4G associate frame */
    + if (buf[0] == 0x00 &&
    + buf[2] == 0xFF && /* REFID */
    + buf[3] == 0xFF &&
    + buf[4] == 0xFF &&
    + buf[5] == 0xFF && /* iMON 2.4G */
    + ((buf[6] == 0x4E && buf[7] == 0xDF) || /* LT */
    + (buf[6] == 0x5E && buf[7] == 0xDF))) { /* DT */
    + warn("%s: remote associated refid=%02X", __func__, buf[1]);
    + context->ir_isassociating = FALSE;
    + }
    +
    + chunk_num = buf[7];
    +
    + if (chunk_num == 0xFF)
    + return; /* filler frame, no data here */
    +
    + if (buf[0] == 0xFF &&
    + buf[1] == 0xFF &&
    + buf[2] == 0xFF &&
    + buf[3] == 0xFF &&
    + buf[4] == 0xFF &&
    + buf[5] == 0xFF && /* iMON 2.4G */
    + ((buf[6] == 0x4E && buf[7] == 0xAF) || /* LT */
    + (buf[6] == 0x5E && buf[7] == 0xAF))) /* DT */
    + return; /* filler frame, no data here */
    +
    +#ifdef DEBUG
    + for (i = 0; i < 8; ++i)
    + printk(KERN_INFO "%02x ", buf[i]);
    + printk(KERN_INFO "\n");
    +#endif
    +
    + if (context->ir_onboard_decode) {
    + /* The signals have been decoded onboard the iMON controller */
    + lirc_buffer_write_1(context->plugin->rbuf, buf);
    + wake_up(&context->plugin->rbuf->wait_poll);
    + return;
    + }
    +
    + /* Translate received data to pulse and space lengths.
    + * Received data is active low, i.e. pulses are 0 and
    + * spaces are 1.
    + *
    + * My original algorithm was essentially similar to
    + * Changwoo Ryu's with the exception that he switched
    + * the incoming bits to active high and also fed an
    + * initial space to LIRC at the start of a new sequence
    + * if the previous bit was a pulse.
    + *
    + * I've decided to adopt his algorithm. */
    +
    + if (chunk_num == 1 && context->rx.initial_space) {
    + /* LIRC requires a leading space */
    + context->rx.prev_bit = 0;
    + context->rx.count = 4;
    + submit_data(context);
    + context->rx.count = 0;
    + }
    +
    + for (octet = 0; octet < 5; ++octet) {
    + mask = 0x80;
    + for (bit = 0; bit < 8; ++bit) {
    + int curr_bit = !(buf[octet] & mask);
    + if (curr_bit != context->rx.prev_bit) {
    + if (context->rx.count) {
    + submit_data(context);
    + context->rx.count = 0;
    + }
    + context->rx.prev_bit = curr_bit;
    + }
    + ++context->rx.count;
    + mask >>= 1;
    + }
    + }
    +
    + if (chunk_num == 10) {
    + if (context->rx.count) {
    + submit_data(context);
    + context->rx.count = 0;
    + }
    + context->rx.initial_space = context->rx.prev_bit;
    + }
    +}
    +
    +/**
    + * Callback function for USB core API: receive data
    + */
    +static void usb_rx_callback(struct urb *urb)
    +{
    + struct imon_context *context;
    +
    + if (!urb)
    + return;
    + context = (struct imon_context *) urb->context;
    + if (!context)
    + return;
    +
    + switch (urb->status) {
    + case -ENOENT: /* usbcore unlink successful! */
    + return;
    + case SUCCESS:
    + if (context->ir_isopen)
    + incoming_packet(context, urb);
    + break;
    + default:
    + warn("%s: status(%d): ignored", __func__, urb->status);
    + break;
    + }
    +
    + usb_submit_urb(context->rx_urb, GFP_ATOMIC);
    + return;
    +}
    +
    +
    +
    +/**
    + * Callback function for USB core API: Probe
    + */
    +static int imon_probe(struct usb_interface *interface,
    + const struct usb_device_id *id)
    +{
    + struct usb_device *dev = NULL;
    + struct usb_host_interface *iface_desc = NULL;
    + struct usb_endpoint_descriptor *rx_endpoint = NULL;
    + struct usb_endpoint_descriptor *tx_endpoint = NULL;
    + struct urb *rx_urb = NULL;
    + struct urb *tx_urb = NULL;
    + struct lirc_plugin *plugin = NULL;
    + struct lirc_buffer *rbuf = NULL;
    + int lirc_minor = 0;
    + int num_endpoints;
    + int retval = SUCCESS;
    + int vfd_ep_found;
    + int ir_ep_found;
    + int alloc_status;
    + int vfd_proto_6p = FALSE;
    + int ir_onboard_decode = FALSE;
    + int tx_control = FALSE;
    + struct imon_context *context = NULL;
    + int i;
    +
    + info("%s: found IMON device", __func__);
    +
    + /*
    + * If it's the LCD, as opposed to the VFD, we just need to replace
    + * the "write" file op.
    + */
    + if (is_lcd)
    + vfd_fops.write = &lcd_write;
    +
    + dev = usb_get_dev(interface_to_usbdev(interface));
    + iface_desc = interface->cur_altsetting;
    + num_endpoints = iface_desc->desc.bNumEndpoints;
    +
    + /*
    + * Scan the endpoint list and set:
    + * first input endpoint = IR endpoint
    + * first output endpoint = VFD endpoint
    + */
    +
    + ir_ep_found = FALSE;
    + vfd_ep_found = FALSE;
    +
    + for (i = 0; i < num_endpoints && !(ir_ep_found && vfd_ep_found); ++i) {
    + struct usb_endpoint_descriptor *ep;
    + int ep_dir;
    + int ep_type;
    + ep = &iface_desc->endpoint[i].desc;
    + ep_dir = ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK;
    + ep_type = ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
    +
    + if (!ir_ep_found &&
    + ep_dir == USB_DIR_IN &&
    + ep_type == USB_ENDPOINT_XFER_INT) {
    +
    + rx_endpoint = ep;
    + ir_ep_found = TRUE;
    + if (debug)
    + info("%s: found IR endpoint", __func__);
    +
    + } else if (!vfd_ep_found &&
    + ep_dir == USB_DIR_OUT &&
    + ep_type == USB_ENDPOINT_XFER_INT) {
    + tx_endpoint = ep;
    + vfd_ep_found = TRUE;
    + if (debug)
    + info("%s: found VFD endpoint", __func__);
    + }
    + }
    +
    + /*
    + * If we didn't find a vfd endpoint, and we have a next-gen LCD,
    + * use control urb instead of interrupt
    + */
    + if (!vfd_ep_found) {
    + if (usb_match_id(interface, lcd_control_endpoint_list)) {
    + tx_control = 1;
    + vfd_ep_found = TRUE;
    + if (debug)
    + info("%s: LCD device uses control endpoint, "
    + "not interface OUT endpoint", __func__);
    + }
    + }
    +
    + /* Input endpoint is mandatory */
    + if (!ir_ep_found) {
    + err("%s: no valid input(IR) endpoint found.", __func__);
    + retval = -ENODEV;
    + goto exit;
    + } else {
    + /* Determine if the IR signals are decoded onboard */
    + if (usb_match_id(interface, ir_onboard_decode_list))
    + ir_onboard_decode = TRUE;
    +
    + if (debug)
    + info("ir_onboard_decode: %d", ir_onboard_decode);
    + }
    +
    + /* Determine if VFD requires 6 packets */
    + if (vfd_ep_found) {
    + if (usb_match_id(interface, vfd_proto_6p_list))
    + vfd_proto_6p = TRUE;
    +
    + if (debug)
    + info("vfd_proto_6p: %d", vfd_proto_6p);
    + }
    +
    +
    + /* Allocate memory */
    +
    + alloc_status = SUCCESS;
    +
    + context = kmalloc(sizeof(struct imon_context), GFP_KERNEL);
    + if (!context) {
    + err("%s: kmalloc failed for context", __func__);
    + alloc_status = 1;
    + goto alloc_status_switch;
    + }
    + plugin = kmalloc(sizeof(struct lirc_plugin), GFP_KERNEL);
    + if (!plugin) {
    + err("%s: kmalloc failed for lirc_plugin", __func__);
    + alloc_status = 2;
    + goto alloc_status_switch;
    + }
    + rbuf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
    + if (!rbuf) {
    + err("%s: kmalloc failed for lirc_buffer", __func__);
    + alloc_status = 3;
    + goto alloc_status_switch;
    + }
    + if (lirc_buffer_init(rbuf, BUF_CHUNK_SIZE, BUF_SIZE)) {
    + err("%s: lirc_buffer_init failed", __func__);
    + alloc_status = 4;
    + goto alloc_status_switch;
    + }
    + rx_urb = usb_alloc_urb(0, GFP_KERNEL);
    + if (!rx_urb) {
    + err("%s: usb_alloc_urb failed for IR urb", __func__);
    + alloc_status = 5;
    + goto alloc_status_switch;
    + }
    + if (vfd_ep_found) {
    + tx_urb = usb_alloc_urb(0, GFP_KERNEL);
    + if (!tx_urb) {
    + err("%s: usb_alloc_urb failed for VFD urb",
    + __func__);
    + alloc_status = 6;
    + goto alloc_status_switch;
    + }
    + }
    +
    + /* clear all members of imon_context and lirc_plugin */
    + memset(context, 0, sizeof(struct imon_context));
    + mutex_init(&context->lock);
    + context->vfd_proto_6p = vfd_proto_6p;
    + context->ir_onboard_decode = ir_onboard_decode;
    +
    + memset(plugin, 0, sizeof(struct lirc_plugin));
    +
    + strcpy(plugin->name, MOD_NAME);
    + plugin->minor = -1;
    + plugin->code_length = (ir_onboard_decode) ?
    + 32 : sizeof(int) * 8;
    + plugin->sample_rate = 0;
    + plugin->features = (ir_onboard_decode) ?
    + LIRC_CAN_REC_LIRCCODE : LIRC_CAN_REC_MODE2;
    + plugin->data = context;
    + plugin->rbuf = rbuf;
    + plugin->set_use_inc = ir_open;
    + plugin->set_use_dec = ir_close;
    + plugin->dev = &dev->dev;
    + plugin->owner = THIS_MODULE;
    +
    + LOCK_CONTEXT;
    +
    + lirc_minor = lirc_register_plugin(plugin);
    + if (lirc_minor < 0) {
    + err("%s: lirc_register_plugin failed", __func__);
    + alloc_status = 7;
    + UNLOCK_CONTEXT;
    + goto alloc_status_switch;
    + } else
    + info("%s: Registered iMON plugin(minor:%d)",
    + __func__, lirc_minor);
    +
    + /* Needed while unregistering! */
    + plugin->minor = lirc_minor;
    +
    + context->dev = dev;
    + context->dev_present = TRUE;
    + context->rx_endpoint = rx_endpoint;
    + context->rx_urb = rx_urb;
    + if (vfd_ep_found) {
    + context->vfd_supported = TRUE;
    + context->tx_endpoint = tx_endpoint;
    + context->tx_urb = tx_urb;
    + context->tx_control = tx_control;
    + }
    + context->plugin = plugin;
    +
    + usb_set_intfdata(interface, context);
    +
    + if (cpu_to_le16(dev->descriptor.idProduct) == 0xffdc) {
    + int err;
    +
    + err = sysfs_create_group(&interface->dev.kobj,
    + &imon_attribute_group);
    + if (err)
    + err("%s: Could not create sysfs entries(%d)",
    + __func__, err);
    + }
    +
    + if (vfd_ep_found) {
    + if (debug)
    + info("Registering VFD with sysfs");
    + if (usb_register_dev(interface, &imon_class)) {
    + /* Not a fatal error, so ignore */
    + info("%s: could not get a minor number for VFD",
    + __func__);
    + }
    + }
    +
    + info("%s: iMON device on usb<%d:%d> initialized",
    + __func__, dev->bus->busnum, dev->devnum);
    +
    + UNLOCK_CONTEXT;
    +
    +alloc_status_switch:
    +
    + switch (alloc_status) {
    + case 7:
    + if (vfd_ep_found)
    + usb_free_urb(tx_urb);
    + case 6:
    + usb_free_urb(rx_urb);
    + case 5:
    + lirc_buffer_free(rbuf);
    + case 4:
    + kfree(rbuf);
    + case 3:
    + kfree(plugin);
    + case 2:
    + kfree(context);
    + context = NULL;
    + case 1:
    + retval = -ENOMEM;
    + case SUCCESS:
    + ;
    + }
    +
    +exit:
    + return retval;
    +}
    +
    +/**
    + * Callback function for USB core API: disonnect
    + */
    +static void imon_disconnect(struct usb_interface *interface)
    +{
    + struct imon_context *context;
    +
    + /* prevent races with ir_open()/vfd_open() */
    + down(&disconnect_sem);
    +
    + context = usb_get_intfdata(interface);
    + LOCK_CONTEXT;
    +
    + info("%s: iMON device disconnected", __func__);
    +
    + /* sysfs_remove_group is safe to call even if sysfs_create_group
    + * hasn't been called */
    + sysfs_remove_group(&interface->dev.kobj,
    + &imon_attribute_group);
    + usb_set_intfdata(interface, NULL);
    + context->dev_present = FALSE;
    +
    + /* Stop reception */
    + usb_kill_urb(context->rx_urb);
    +
    + /* Abort ongoing write */
    + if (atomic_read(&context->tx.busy)) {
    + usb_kill_urb(context->tx_urb);
    + wait_for_completion(&context->tx.finished);
    + }
    +
    + /* De-register from lirc_dev if IR port is not open */
    + if (!context->ir_isopen)
    + deregister_from_lirc(context);
    +
    + if (context->vfd_supported)
    + usb_deregister_dev(interface, &imon_class);
    +
    + UNLOCK_CONTEXT;
    +
    + if (!context->ir_isopen && !context->vfd_isopen)
    + delete_context(context);
    +
    + up(&disconnect_sem);
    +}
    +
    +static int __init imon_init(void)
    +{
    + int rc;
    +
    + info(MOD_DESC ", v" MOD_VERSION);
    + info(MOD_AUTHOR);
    +
    + rc = usb_register(&imon_driver);
    + if (rc) {
    + err("%s: usb register failed(%d)", __func__, rc);
    + return -ENODEV;
    + }
    + return SUCCESS;
    +}
    +
    +static void __exit imon_exit(void)
    +{
    + usb_deregister(&imon_driver);
    + info("module removed. Goodbye!");
    +}
    +
    +#ifdef MODULE
    +module_init(imon_init);
    +module_exit(imon_exit);
    +
    +#else
    +subsys_initcall(imon_init);
    +
    +#endif /* MODULE */
    diff --git a/drivers/input/lirc/lirc_it87.c b/drivers/input/lirc/lirc_it87.c
    new file mode 100644
    index 0000000..0a64847
    --- /dev/null
    +++ b/drivers/input/lirc/lirc_it87.c
    @@ -0,0 +1,999 @@
    +/*
    + * LIRC driver for ITE IT8712/IT8705 CIR port
    + *
    + * Copyright (C) 2001 Hans-Gunter Lutke Uphues <hg_lu@web.de>
    + *
    + * This program is free software; you can redistribute it and/or
    + * modify it under the terms of the GNU General Public License as
    + * published by the Free Software Foundation; either version 2 of the
    + * License, or (at your option) any later version.
    + *
    + * This program is distributed in the hope that it will be useful, but
    + * WITHOUT ANY WARRANTY; without even the implied warranty of
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    + * General Public License for more details.
    +
    + * You should have received a copy of the GNU General Public License
    + * along with this program; if not, write to the Free Software
    + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
    + * USA
    + *
    + * ITE IT8705 and IT8712(not tested) CIR-port support for lirc based
    + * via cut and paste from lirc_sir.c (C) 2000 Milan Pikula
    + *
    + * Attention: Sendmode only tested with debugging logs
    + *
    + * 2001/02/27 Christoph Bartelmus <lirc@bartelmus.de> :
    + * reimplemented read function
    + * 2005/06/05 Andrew Calkin implemented support for Asus Digimatrix,
    + * based on work of the following member of the Outertrack Digimatrix
    + * Forum: Art103 <r_tay@hotmail.com>
    + */
    +
    +
    +#include <linux/version.h>
    +#include <linux/module.h>
    +#include <linux/autoconf.h>
    +
    +
    +#include <linux/sched.h>
    +#include <linux/errno.h>
    +#include <linux/signal.h>
    +#include <linux/fs.h>
    +#include <linux/interrupt.h>
    +#include <linux/ioport.h>
    +#include <linux/kernel.h>
    +#include <linux/major.h>
    +#include <linux/serial_reg.h>
    +#include <linux/time.h>
    +#include <linux/string.h>
    +#include <linux/types.h>
    +#include <linux/wait.h>
    +#include <linux/mm.h>
    +#include <linux/delay.h>
    +#include <linux/poll.h>
    +#include <asm/system.h>
    +#include <linux/uaccess.h>
    +#include <linux/io.h>
    +#include <linux/irq.h>
    +#include <linux/fcntl.h>
    +
    +#include <linux/timer.h>
    +
    +#include "lirc.h"
    +#include "lirc_dev.h"
    +
    +#include "lirc_it87.h"
    +
    +#ifdef LIRC_IT87_DIGIMATRIX
    +static int digimatrix = 1;
    +static int it87_freq = 36; /* kHz */
    +static int irq = 9;
    +#else
    +static int digimatrix;
    +static int it87_freq = 38; /* kHz */
    +static int irq = IT87_CIR_DEFAULT_IRQ;
    +#endif
    +
    +static unsigned long it87_bits_in_byte_out;
    +static unsigned long it87_send_counter;
    +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)
    +
    +/* insmod parameters */
    +static int debug;
    +#define dprintk(fmt, args...) \
    + do { \
    + if (debug) \
    + printk(KERN_DEBUG LIRC_DRIVER_NAME ": " \
    + fmt, ## args); \
    + } while (0)
    +
    +static int io = IT87_CIR_DEFAULT_IOBASE;
    +/* receiver demodulator default: off */
    +static int it87_enable_demodulator;
    +
    +static int timer_enabled;
    +static DEFINE_SPINLOCK(timer_lock);
    +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;
    +
    +static DECLARE_WAIT_QUEUE_HEAD(lirc_read_queue);
    +
    +static DEFINE_SPINLOCK(hardware_lock);
    +static DEFINE_SPINLOCK(dev_lock);
    +
    +static int rx_buf[RBUF_LEN];
    +unsigned int rx_tail, rx_head;
    +static int 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);
    +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);
    +
    +
    +/* SECTION: Communication with user-space */
    +
    +static int lirc_open(struct inode *inode, struct file *file)
    +{
    + spin_lock(&dev_lock);
    + if (module_refcount(THIS_MODULE)) {
    + spin_unlock(&dev_lock);
    + return -EBUSY;
    + }
    + spin_unlock(&dev_lock);
    + return 0;
    +}
    +
    +
    +static int lirc_close(struct inode *inode, struct file *file)
    +{
    + return 0;
    +}
    +
    +
    +static unsigned int lirc_poll(struct file *file, poll_table *wait)
    +{
    + poll_wait(file, &lirc_read_queue, wait);
    + if (rx_head != rx_tail)
    + return POLLIN | POLLRDNORM;
    + return 0;
    +}
    +
    +
    +static ssize_t lirc_read(struct file *file, char *buf,
    + size_t count, loff_t *ppos)
    +{
    + int n = 0;
    + int retval = 0;
    +
    + while (n < count) {
    + if (file->f_flags & O_NONBLOCK && rx_head == rx_tail) {
    + retval = -EAGAIN;
    + break;
    + }
    + retval = wait_event_interruptible(lirc_read_queue,
    + rx_head != rx_tail);
    + if (retval)
    + break;
    +
    + if (copy_to_user((void *) buf + n, (void *) (rx_buf + rx_head),
    + sizeof(int))) {
    + retval = -EFAULT;
    + break;
    + }
    + rx_head = (rx_head + 1) & (RBUF_LEN - 1);
    + n += sizeof(int);
    + }
    + 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 = 0;
    +
    + if (n % sizeof(int) || (n / sizeof(int)) > WBUF_LEN)
    + return -EINVAL;
    + if (copy_from_user(tx_buf, buf, n))
    + return -EFAULT;
    + n /= sizeof(int);
    + 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;
    + unsigned long hw_flags;
    +
    + 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;
    +
    + 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);
    + dprintk("demodulation frequency: %d kHz\n", it87_freq);
    +
    + 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;
    + int newval;
    +
    + dprintk("add flag %d with val %lu\n", flag, val);
    +
    + 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) {
    + dprintk("Buffer overrun.\n");
    + 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)
    +{
    + return 0;
    +}
    +
    +static void set_use_dec(void *data)
    +{
    +}
    +
    +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,
    + .dev = NULL,
    + .owner = THIS_MODULE,
    +};
    +
    +
    +#ifdef MODULE
    +static 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)
    +{
    + unsigned long flags;
    +
    + /* avoid interference with interrupt */
    + spin_lock_irqsave(&timer_lock, flags);
    +
    + if (digimatrix) {
    + /* We have timed out.
    + Disable the RX mechanism.
    + */
    +
    + 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);
    + dprintk(" TIMEOUT\n");
    + timer_enabled = 0;
    +
    + /* fifo clear */
    + outb(inb(io + IT87_CIR_TCR1) | IT87_CIR_TCR1_FIFOCLR,
    + io+IT87_CIR_TCR1);
    +
    + } else {
    + /* 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. */
    +
    + if (last_value) {
    + unsigned long pulse_end;
    +
    + /* determine 'virtual' pulse end: */
    + pulse_end = delta(&last_tv, &last_intr_tv);
    + dprintk("timeout add %d for %lu usec\n",
    + last_value, pulse_end);
    + 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)
    +{
    + 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;
    + static char lastbit;
    + char bit;
    +
    + /* Bit duration in microseconds */
    + const unsigned long bit_duration = 1000000ul /
    + (115200 / IT87_CIR_BAUDRATE_DIVISOR);
    +
    +
    + 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;
    + dprintk("iir: 0x%x fifo: 0x%x\n", iir, lsr);
    +
    + /* avoid interference with timer */
    + spin_lock_irqsave(&timer_lock, flags);
    + spin_lock_irqsave(&hardware_lock, hw_flags);
    + if (digimatrix) {
    + static unsigned long acc_pulse;
    + static unsigned long acc_space;
    +
    + do {
    + data = inb(io + IT87_CIR_DR);
    + data = ~data;
    + fifo--;
    + if (data != 0x00) {
    + if (timer_enabled)
    + del_timer(&timerlist);
    + /* start timer for end of
    + * sequence detection */
    + timerlist.expires = jiffies +
    + IT87_TIMEOUT;
    + add_timer(&timerlist);
    + timer_enabled = 1;
    + }
    + /* Loop through */
    + for (bit = 0; bit < 8; ++bit) {
    + if ((data >> bit) & 1) {
    + ++acc_pulse;
    + if (lastbit == 0) {
    + add_read_queue(0,
    + acc_space *
    + bit_duration);
    + acc_space = 0;
    + }
    + } else {
    + ++acc_space;
    + if (lastbit == 1) {
    + add_read_queue(1,
    + acc_pulse *
    + bit_duration);
    + acc_pulse = 0;
    + }
    + }
    + lastbit = (data >> bit) & 1;
    + }
    +
    + } while (fifo != 0);
    + } else { /* Normal Operation */
    + do {
    + del_timer(&timerlist);
    + data = inb(io + IT87_CIR_DR);
    +
    + dprintk("data=%.2x\n", data);
    + do_gettimeofday(&curr_tv);
    + deltv = delta(&last_tv, &curr_tv);
    + deltintrtv = delta(&last_intr_tv, &curr_tv);
    +
    + dprintk("t %lu , d %d\n",
    + deltintrtv, (int)data);
    +
    + /* if nothing came in last 2 cycles,
    + it was gap */
    + if (deltintrtv > TIME_CONST * 2) {
    + if (last_value) {
    + dprintk("GAP\n");
    +
    + /* 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 */
    + dprintk("unknown IRQ (shouldn't happen) !!\n");
    + 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;
    + unsigned long hw_flags;
    +
    + dprintk("%s: len=%ld, sb=%d\n", __func__, len, send_byte);
    +
    + time_left = (long)len - (long)count * (long)stime;
    + count += ((2 * time_left) / stime);
    + while (count) {
    + long i = 0;
    + for (i = 0; i < count_bits; i++) {
    + byte_out = (byte_out << 1) | (send_byte & 1);
    + it87_bits_in_byte_out++;
    + }
    + if (it87_bits_in_byte_out == 8) {
    + dprintk("out=0x%x, tsr_txfbc: 0x%x\n",
    + byte_out,
    + inb(io + IT87_CIR_TSR) &
    + IT87_CIR_TSR_TXFBC);
    +
    + while ((inb(io + IT87_CIR_TSR) &
    + IT87_CIR_TSR_TXFBC) >= IT87_CIR_FIFO_SIZE)
    + ;
    +
    + 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 */
    + if (digimatrix) {
    + outb(IT87_CIR_IER_IEC | IT87_CIR_IER_RFOIE, io + IT87_CIR_IER);
    + /* RX: HCFS=0, RXDCR = 001b (33,75..38,25 kHz), RXEN=1 */
    + } else {
    + 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);
    + if (digimatrix) {
    + /* Set FIFO depth to 1 byte, and disable TX */
    + outb(inb(io + IT87_CIR_TCR1) | 0x00,
    + io + IT87_CIR_TCR1);
    +
    + /* TX: it87_freq (36kHz),
    + 'reserved' sensitivity setting (0x00) */
    + outb(((it87_freq - IT87_CIR_FREQ_MIN) << 3) | 0x00,
    + io + IT87_CIR_TCR2);
    + } else {
    + /* 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)
    +{
    + unsigned long hw_flags;
    + 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 (digimatrix || 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;
    +
    + 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 */
    + if (request_region(io, 8, LIRC_DRIVER_NAME) == NULL) {
    + 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 -EBUSY;
    + }
    +
    + /* 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 /*IRQF_DISABLED*/,
    + LIRC_DRIVER_NAME, NULL);
    + if (retval < 0) {
    + printk(KERN_ERR LIRC_DRIVER_NAME
    + ": IRQ %d already in use.\n",
    + irq);
    + release_region(io, 8);
    + return retval;
    + }
    +
    + 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);
    +}
    +
    +
    +static 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
    +
    +static int __init lirc_it87_init(void)
    +{
    + int retval;
    +
    + retval = init_chrdev();
    + if (retval < 0)
    + return retval;
    + retval = init_lirc_it87();
    + if (retval) {
    + drop_chrdev();
    + return retval;
    + }
    + return 0;
    +}
    +
    +
    +static void __exit lirc_it87_exit(void)
    +{
    + drop_hardware();
    + drop_chrdev();
    + drop_port();
    + printk(KERN_INFO LIRC_DRIVER_NAME ": Uninstalled.\n");
    +}
    +
    +module_init(lirc_it87_init);
    +module_exit(lirc_it87_exit);
    +
    +MODULE_DESCRIPTION("LIRC driver for ITE IT8712/IT8705 CIR port");
    +MODULE_AUTHOR("Hans-Gunter Lutke Uphues");
    +MODULE_LICENSE("GPL");
    +
    +module_param(io, int, 0444);
    +MODULE_PARM_DESC(io, "I/O base address (default: 0x310)");
    +
    +module_param(irq, int, 0444);
    +#ifdef LIRC_IT87_DIGIMATRIX
    +MODULE_PARM_DESC(irq, "Interrupt (1,3-12) (default: 9)");
    +#else
    +MODULE_PARM_DESC(irq, "Interrupt (1,3-12) (default: 7)");
    +#endif
    +
    +module_param(it87_enable_demodulator, bool, 0444);
    +MODULE_PARM_DESC(it87_enable_demodulator,
    + "Receiver demodulator enable/disable (1/0), default: 0");
    +
    +module_param(debug, bool, 0644);
    +MODULE_PARM_DESC(debug, "Enable debugging messages");
    +
    +module_param(digimatrix, bool, 0644);
    +#ifdef LIRC_IT87_DIGIMATRIX
    +MODULE_PARM_DESC(digimatrix,
    + "Asus Digimatrix it87 compat. enable/disable (1/0), default: 1");
    +#else
    +MODULE_PARM_DESC(digimatrix,
    + "Asus Digimatrix it87 compat. enable/disable (1/0), default: 0");
    +#endif
    +
    +
    +module_param(it87_freq, int, 0444);
    +#ifdef LIRC_IT87_DIGIMATRIX
    +MODULE_PARM_DESC(it87_freq,
    + "Carrier demodulator frequency (kHz), (default: 36)");
    +#else
    +MODULE_PARM_DESC(it87_freq,
    + "Carrier demodulator frequency (kHz), (default: 38)");
    +#endif
    +
    +#endif /* MODULE */
    +
    +
    +/*
    + * Overrides for Emacs so that we follow Linus's tabbing style.
    + * ---------------------------------------------------------------------------
    + * Local variables:
    + * c-basic-offset: 8
    + * End:
    + */
    diff --git a/drivers/input/lirc/lirc_it87.h b/drivers/input/lirc/lirc_it87.h
    new file mode 100644
    index 0000000..a997204
    --- /dev/null
    +++ b/drivers/input/lirc/lirc_it87.h
    @@ -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 --git a/drivers/input/lirc/lirc_ite8709.c b/drivers/input/lirc/lirc_ite8709.c
    new file mode 100644
    index 0000000..d03ecf7
    --- /dev/null
    +++ b/drivers/input/lirc/lirc_ite8709.c
    @@ -0,0 +1,545 @@
    +/*
    + * LIRC driver for ITE8709 CIR port
    + *
    + * Copyright (C) 2008 Grégory Lardière <spmf2004-lirc@yahoo.fr>
    + *
    + * This program is free software; you can redistribute it and/or
    + * modify it under the terms of the GNU General Public License as
    + * published by the Free Software Foundation; either version 2 of the
    + * License, or (at your option) any later version.
    + *
    + * This program is distributed in the hope that it will be useful, but
    + * WITHOUT ANY WARRANTY; without even the implied warranty of
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    + * General Public License for more details.
    + *
    + * You should have received a copy of the GNU General Public License
    + * along with this program; if not, write to the Free Software
    + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
    + * USA
    + */
    +
    +#include <linux/version.h>
    +#include <linux/module.h>
    +
    +#include <linux/autoconf.h>
    +#include <linux/interrupt.h>
    +#include <linux/delay.h>
    +
    +#include <linux/pnp.h>
    +#include <linux/io.h>
    +
    +#include "lirc.h"
    +#include "lirc_dev.h"
    +
    +#define LIRC_DRIVER_NAME "lirc_ite8709"
    +
    +#define BUF_CHUNK_SIZE sizeof(int)
    +#define BUF_SIZE (128*BUF_CHUNK_SIZE)
    +
    +/*******************************************************************************
    +* The ITE8709 device seems to be the combination of IT8512 superIO chip and *
    +* a specific firmware running on the IT8512's embedded micro-controller. *
    +* In addition of the embedded micro-controller, the IT8512 chip contains a *
    +* CIR module and several other modules. A few modules are directly accessible *
    +* by the host CPU, but most of them are only accessible by the *
    +* micro-controller. The CIR module is only accessible by the micro-controller. *
    +* The battery-backed SRAM module is accessible by the host CPU and the *
    +* micro-controller. So one of the MC's firmware role is to act as a bridge *
    +* between the host CPU and the CIR module. The firmware implements a kind of *
    +* communication protocol using the SRAM module as a shared memory. The IT8512 *
    +* specification is publicly available on ITE's web site, but the communication *
    +* protocol is not, so it was reverse-engineered. *
    +*******************************************************************************/
    +
    +/* ITE8709 Registers addresses and values (reverse-engineered) */
    +#define ITE8709_MODE 0x1a
    +#define ITE8709_REG_ADR 0x1b
    +#define ITE8709_REG_VAL 0x1c
    +#define ITE8709_IIR 0x1e /* Interrupt identification register */
    +#define ITE8709_RFSR 0x1f /* Receiver FIFO status register */
    +#define ITE8709_FIFO_START 0x20
    +
    +#define ITE8709_MODE_READY 0X00
    +#define ITE8709_MODE_WRITE 0X01
    +#define ITE8709_MODE_READ 0X02
    +#define ITE8709_IIR_RDAI 0x02 /* Receiver data available interrupt */
    +#define ITE8709_IIR_RFOI 0x04 /* Receiver FIFO overrun interrupt */
    +#define ITE8709_RFSR_MASK 0x3f /* FIFO byte count mask */
    +
    +/* IT8512 CIR-module registers addresses and values (from IT8512 E/F */
    +/* specification v0.4.1) */
    +#define IT8512_REG_MSTCR 0x01 /* Master control register */
    +#define IT8512_REG_IER 0x02 /* Interrupt enable register */
    +#define IT8512_REG_CFR 0x04 /* Carrier frequency register */
    +#define IT8512_REG_RCR 0x05 /* Receive control register */
    +#define IT8512_REG_BDLR 0x08 /* Baud rate divisor low byte register */
    +#define IT8512_REG_BDHR 0x09 /* Baud rate divisor high byte register */
    +
    +#define IT8512_MSTCR_RESET 0x01 /* Reset registers to default value */
    +#define IT8512_MSTCR_FIFOCLR 0x02 /* Clear FIFO */
    +#define IT8512_MSTCR_FIFOTL_7 0x04 /* FIFO threshold level : 7 */
    +#define IT8512_MSTCR_FIFOTL_25 0x0c /* FIFO threshold level : 25 */
    +#define IT8512_IER_RDAIE 0x02 /* Enable data interrupt request */
    +#define IT8512_IER_RFOIE 0x04 /* Enable FIFO overrun interrupt req */
    +#define IT8512_IER_IEC 0x80 /* Enable interrupt request */
    +#define IT8512_CFR_CF_36KHZ 0x09 /* Carrier freq : low speed, 36kHz */
    +#define IT8512_RCR_RXDCR_1 0x01 /* Demodulation carrier range : 1 */
    +#define IT8512_RCR_RXACT 0x08 /* Receiver active */
    +#define IT8512_RCR_RXEN 0x80 /* Receiver enable */
    +#define IT8512_BDR_6 6 /* Baud rate divisor : 6 */
    +
    +/* Actual values used by this driver */
    +#define CFG_FIFOTL IT8512_MSTCR_FIFOTL_25
    +#define CFG_CR_FREQ IT8512_CFR_CF_36KHZ
    +#define CFG_DCR IT8512_RCR_RXDCR_1
    +#define CFG_BDR IT8512_BDR_6
    +#define CFG_TIMEOUT 100000 /* Rearm interrupt when a space is > 100 ms */
    +
    +static int debug;
    +
    +struct ite8709_device {
    + int use_count;
    + int io;
    + int irq;
    + spinlock_t hardware_lock;
    + unsigned long long acc_pulse;
    + unsigned long long acc_space;
    + char lastbit;
    + struct timeval last_tv;
    + struct lirc_plugin plugin;
    + struct lirc_buffer buffer;
    + struct tasklet_struct tasklet;
    + char force_rearm;
    + char rearmed;
    + char device_busy;
    +};
    +
    +#define dprintk(fmt, args...) \
    + do { \
    + if (debug) \
    + printk(KERN_DEBUG LIRC_DRIVER_NAME ": " \
    + fmt, ## args); \
    + } while (0)
    +
    +
    +static unsigned char ite8709_read(struct ite8709_device *dev,
    + unsigned char port)
    +{
    + outb(port, dev->io);
    + return inb(dev->io+1);
    +}
    +
    +static void ite8709_write(struct ite8709_device *dev, unsigned char port,
    + unsigned char data)
    +{
    + outb(port, dev->io);
    + outb(data, dev->io+1);
    +}
    +
    +static void ite8709_wait_device(struct ite8709_device *dev)
    +{
    + int i = 0;
    + /* loop until device tells it's ready to continue */
    + /* iterations count is usually ~750 but can sometimes achieve 13000 */
    + for (i = 0; i < 15000; i++) {
    + udelay(2);
    + if (ite8709_read(dev, ITE8709_MODE) == ITE8709_MODE_READY)
    + break;
    + }
    +}
    +
    +static void ite8709_write_register(struct ite8709_device *dev,
    + unsigned char reg_adr, unsigned char reg_value)
    +{
    + ite8709_wait_device(dev);
    +
    + ite8709_write(dev, ITE8709_REG_VAL, reg_value);
    + ite8709_write(dev, ITE8709_REG_ADR, reg_adr);
    + ite8709_write(dev, ITE8709_MODE, ITE8709_MODE_WRITE);
    +}
    +
    +static void ite8709_init_hardware(struct ite8709_device *dev)
    +{
    + spin_lock_irq(&dev->hardware_lock);
    + dev->device_busy = 1;
    + spin_unlock_irq(&dev->hardware_lock);
    +
    + ite8709_write_register(dev, IT8512_REG_BDHR, (CFG_BDR >> 8) & 0xff);
    + ite8709_write_register(dev, IT8512_REG_BDLR, CFG_BDR & 0xff);
    + ite8709_write_register(dev, IT8512_REG_CFR, CFG_CR_FREQ);
    + ite8709_write_register(dev, IT8512_REG_IER,
    + IT8512_IER_IEC | IT8512_IER_RFOIE | IT8512_IER_RDAIE);
    + ite8709_write_register(dev, IT8512_REG_RCR, CFG_DCR);
    + ite8709_write_register(dev, IT8512_REG_MSTCR,
    + CFG_FIFOTL | IT8512_MSTCR_FIFOCLR);
    + ite8709_write_register(dev, IT8512_REG_RCR,
    + IT8512_RCR_RXEN | IT8512_RCR_RXACT | CFG_DCR);
    +
    + spin_lock_irq(&dev->hardware_lock);
    + dev->device_busy = 0;
    + spin_unlock_irq(&dev->hardware_lock);
    +
    + tasklet_enable(&dev->tasklet);
    +}
    +
    +static void ite8709_drop_hardware(struct ite8709_device *dev)
    +{
    + tasklet_disable(&dev->tasklet);
    +
    + spin_lock_irq(&dev->hardware_lock);
    + dev->device_busy = 1;
    + spin_unlock_irq(&dev->hardware_lock);
    +
    + ite8709_write_register(dev, IT8512_REG_RCR, 0);
    + ite8709_write_register(dev, IT8512_REG_MSTCR,
    + IT8512_MSTCR_RESET | IT8512_MSTCR_FIFOCLR);
    +
    + spin_lock_irq(&dev->hardware_lock);
    + dev->device_busy = 0;
    + spin_unlock_irq(&dev->hardware_lock);
    +}
    +
    +static int ite8709_set_use_inc(void *data)
    +{
    + struct ite8709_device *dev;
    + dev = data;
    + if (dev->use_count == 0)
    + ite8709_init_hardware(dev);
    + dev->use_count++;
    + return 0;
    +}
    +
    +static void ite8709_set_use_dec(void *data)
    +{
    + struct ite8709_device *dev;
    + dev = data;
    + dev->use_count--;
    + if (dev->use_count == 0)
    + ite8709_drop_hardware(dev);
    +}
    +
    +static void ite8709_add_read_queue(struct ite8709_device *dev, int flag,
    + unsigned long long val)
    +{
    + int value;
    +
    + dprintk("add a %llu usec %s\n", val, flag ? "pulse" : "space");
    +
    + value = (val > PULSE_MASK) ? PULSE_MASK : val;
    + if (flag)
    + value |= PULSE_BIT;
    +
    + if (!lirc_buffer_full(&dev->buffer)) {
    + lirc_buffer_write_1(&dev->buffer, (void *) &value);
    + wake_up(&dev->buffer.wait_poll);
    + }
    +}
    +
    +static irqreturn_t ite8709_interrupt(int irq, void *dev_id)
    +{
    + unsigned char data;
    + int iir, rfsr, i;
    + int fifo = 0;
    + char bit;
    + struct timeval curr_tv;
    +
    + /* Bit duration in microseconds */
    + const unsigned long bit_duration = 1000000ul / (115200 / CFG_BDR);
    +
    + struct ite8709_device *dev;
    + dev = dev_id;
    +
    + /* If device is busy, we simply discard data because we are in one of */
    + /* these two cases : shutting down or rearming the device, so this */
    + /* doesn't really matter and this avoids waiting too long in IRQ ctx */
    + spin_lock(&dev->hardware_lock);
    + if (dev->device_busy) {
    + spin_unlock(&dev->hardware_lock);
    + return IRQ_RETVAL(IRQ_HANDLED);
    + }
    +
    + iir = ite8709_read(dev, ITE8709_IIR);
    +
    + switch (iir) {
    + case ITE8709_IIR_RFOI:
    + dprintk("fifo overrun, scheduling forced rearm just in case\n");
    + dev->force_rearm = 1;
    + tasklet_schedule(&dev->tasklet);
    + spin_unlock(&dev->hardware_lock);
    + return IRQ_RETVAL(IRQ_HANDLED);
    +
    + case ITE8709_IIR_RDAI:
    + rfsr = ite8709_read(dev, ITE8709_RFSR);
    + fifo = rfsr & ITE8709_RFSR_MASK;
    + if (fifo > 32)
    + fifo = 32;
    + dprintk("iir: 0x%x rfsr: 0x%x fifo: %d\n", iir, rfsr, fifo);
    +
    + if (dev->rearmed) {
    + do_gettimeofday(&curr_tv);
    + dev->acc_space += 1000000ull
    + * (curr_tv.tv_sec - dev->last_tv.tv_sec)
    + + (curr_tv.tv_usec - dev->last_tv.tv_usec);
    + dev->rearmed = 0;
    + }
    + for (i = 0; i < fifo; i++) {
    + data = ite8709_read(dev, i+ITE8709_FIFO_START);
    + data = ~data;
    + /* Loop through */
    + for (bit = 0; bit < 8; ++bit) {
    + if ((data >> bit) & 1) {
    + dev->acc_pulse += bit_duration;
    + if (dev->lastbit == 0) {
    + ite8709_add_read_queue(dev, 0,
    + dev->acc_space);
    + dev->acc_space = 0;
    + }
    + } else {
    + dev->acc_space += bit_duration;
    + if (dev->lastbit == 1) {
    + ite8709_add_read_queue(dev, 1,
    + dev->acc_pulse);
    + dev->acc_pulse = 0;
    + }
    + }
    + dev->lastbit = (data >> bit) & 1;
    + }
    + }
    + ite8709_write(dev, ITE8709_RFSR, 0);
    +
    + if (dev->acc_space > CFG_TIMEOUT) {
    + dprintk("scheduling rearm IRQ\n");
    + do_gettimeofday(&dev->last_tv);
    + dev->force_rearm = 0;
    + tasklet_schedule(&dev->tasklet);
    + }
    +
    + spin_unlock(&dev->hardware_lock);
    + return IRQ_RETVAL(IRQ_HANDLED);
    +
    + default:
    + /* not our irq */
    + dprintk("unknown IRQ (shouldn't happen) !!\n");
    + spin_unlock(&dev->hardware_lock);
    + return IRQ_RETVAL(IRQ_NONE);
    + }
    +}
    +
    +static void ite8709_rearm_irq(unsigned long data)
    +{
    + struct ite8709_device *dev;
    + unsigned long flags;
    + dev = (struct ite8709_device *) data;
    +
    + spin_lock_irqsave(&dev->hardware_lock, flags);
    + dev->device_busy = 1;
    + spin_unlock_irqrestore(&dev->hardware_lock, flags);
    +
    + if (dev->force_rearm || dev->acc_space > CFG_TIMEOUT) {
    + dprintk("rearming IRQ\n");
    + ite8709_write_register(dev, IT8512_REG_RCR,
    + IT8512_RCR_RXACT | CFG_DCR);
    + ite8709_write_register(dev, IT8512_REG_MSTCR,
    + CFG_FIFOTL | IT8512_MSTCR_FIFOCLR);
    + ite8709_write_register(dev, IT8512_REG_RCR,
    + IT8512_RCR_RXEN | IT8512_RCR_RXACT | CFG_DCR);
    + if (!dev->force_rearm)
    + dev->rearmed = 1;
    + dev->force_rearm = 0;
    + }
    +
    + spin_lock_irqsave(&dev->hardware_lock, flags);
    + dev->device_busy = 0;
    + spin_unlock_irqrestore(&dev->hardware_lock, flags);
    +}
    +
    +static int ite8709_cleanup(struct ite8709_device *dev, int stage, int errno,
    + char *msg)
    +{
    + if (msg != NULL)
    + printk(KERN_ERR LIRC_DRIVER_NAME ": %s\n", msg);
    +
    + switch (stage) {
    + case 6:
    + if (dev->use_count > 0)
    + ite8709_drop_hardware(dev);
    + case 5:
    + free_irq(dev->irq, dev);
    + case 4:
    + release_region(dev->io, 2);
    + case 3:
    + lirc_unregister_plugin(dev->plugin.minor);
    + case 2:
    + lirc_buffer_free(dev->plugin.rbuf);
    + case 1:
    + kfree(dev);
    + case 0:
    + ;
    + }
    +
    + return errno;
    +}
    +
    +static int __devinit ite8709_pnp_probe(struct pnp_dev *dev,
    + const struct pnp_device_id *dev_id)
    +{
    + struct lirc_plugin *plugin;
    + struct ite8709_device *ite8709_dev;
    + int ret;
    +
    + /* Check resources validity */
    + if (!pnp_irq_valid(dev, 0))
    + return ite8709_cleanup(NULL, 0, -ENODEV, "invalid IRQ");
    + if (!pnp_port_valid(dev, 2))
    + return ite8709_cleanup(NULL, 0, -ENODEV, "invalid IO port");
    +
    + /* Allocate memory for device struct */
    + ite8709_dev = kzalloc(sizeof(struct ite8709_device), GFP_KERNEL);
    + if (ite8709_dev == NULL)
    + return ite8709_cleanup(NULL, 0, -ENOMEM, "kzalloc failed");
    + pnp_set_drvdata(dev, ite8709_dev);
    +
    + /* Initialize device struct */
    + ite8709_dev->use_count = 0;
    + ite8709_dev->irq = pnp_irq(dev, 0);
    + ite8709_dev->io = pnp_port_start(dev, 2);
    + ite8709_dev->hardware_lock = __SPIN_LOCK_UNLOCKED(
    + ite8709_dev->hardware_lock);
    + ite8709_dev->acc_pulse = 0;
    + ite8709_dev->acc_space = 0;
    + ite8709_dev->lastbit = 0;
    + do_gettimeofday(&ite8709_dev->last_tv);
    + tasklet_init(&ite8709_dev->tasklet, ite8709_rearm_irq,
    + (long) ite8709_dev);
    + ite8709_dev->force_rearm = 0;
    + ite8709_dev->rearmed = 0;
    + ite8709_dev->device_busy = 0;
    +
    + /* Initialize plugin struct */
    + plugin = &ite8709_dev->plugin;
    + strcpy(plugin->name, LIRC_DRIVER_NAME);
    + plugin->minor = -1;
    + plugin->code_length = sizeof(int) * 8;
    + plugin->sample_rate = 0;
    + plugin->features = LIRC_CAN_REC_MODE2;
    + plugin->data = ite8709_dev;
    + plugin->add_to_buf = NULL;
    + plugin->get_queue = NULL;
    + plugin->rbuf = &ite8709_dev->buffer;
    + plugin->set_use_inc = ite8709_set_use_inc;
    + plugin->set_use_dec = ite8709_set_use_dec;
    + plugin->ioctl = NULL;
    + plugin->fops = NULL;
    + plugin->dev = &dev->dev;
    + plugin->owner = THIS_MODULE;
    +
    + /* Initialize LIRC buffer */
    + if (lirc_buffer_init(plugin->rbuf, BUF_CHUNK_SIZE, BUF_SIZE))
    + return ite8709_cleanup(ite8709_dev, 1, -ENOMEM,
    + "lirc_buffer_init() failed");
    +
    + /* Register LIRC plugin */
    + ret = lirc_register_plugin(plugin);
    + if (ret < 0)
    + return ite8709_cleanup(ite8709_dev, 2, ret,
    + "lirc_register_plugin() failed");
    +
    + /* Reserve I/O port access */
    + if (!request_region(ite8709_dev->io, 2, LIRC_DRIVER_NAME))
    + return ite8709_cleanup(ite8709_dev, 3, -EBUSY,
    + "i/o port already in use");
    +
    + /* Reserve IRQ line */
    + ret = request_irq(ite8709_dev->irq, ite8709_interrupt, 0,
    + LIRC_DRIVER_NAME, ite8709_dev);
    + if (ret < 0)
    + return ite8709_cleanup(ite8709_dev, 4, ret,
    + "IRQ already in use");
    +
    + /* Initialize hardware */
    + ite8709_drop_hardware(ite8709_dev); /* Shutdown hw until first use */
    +
    + printk(KERN_INFO LIRC_DRIVER_NAME ": device found : irq=%d io=0x%x\n",
    + ite8709_dev->irq, ite8709_dev->io);
    +
    + return 0;
    +}
    +
    +static void __devexit ite8709_pnp_remove(struct pnp_dev *dev)
    +{
    + struct ite8709_device *ite8709_dev;
    + ite8709_dev = pnp_get_drvdata(dev);
    +
    + ite8709_cleanup(ite8709_dev, 6, 0, NULL);
    +
    + printk(KERN_INFO LIRC_DRIVER_NAME ": device removed\n");
    +}
    +
    +#ifdef CONFIG_PM
    +static int ite8709_pnp_suspend(struct pnp_dev *dev, pm_message_t state)
    +{
    + struct ite8709_device *ite8709_dev;
    + ite8709_dev = pnp_get_drvdata(dev);
    +
    + if (ite8709_dev->use_count > 0)
    + ite8709_drop_hardware(ite8709_dev);
    +
    + return 0;
    +}
    +
    +static int ite8709_pnp_resume(struct pnp_dev *dev)
    +{
    + struct ite8709_device *ite8709_dev;
    + ite8709_dev = pnp_get_drvdata(dev);
    +
    + if (ite8709_dev->use_count > 0)
    + ite8709_init_hardware(ite8709_dev);
    +
    + return 0;
    +}
    +#else
    +#define ite8709_pnp_suspend NULL
    +#define ite8709_pnp_resume NULL
    +#endif
    +
    +static const struct pnp_device_id pnp_dev_table[] = {
    + {"ITE8709", 0},
    + {}
    +};
    +
    +MODULE_DEVICE_TABLE(pnp, pnp_dev_table);
    +
    +static struct pnp_driver ite8709_pnp_driver = {
    + .name = LIRC_DRIVER_NAME,
    + .probe = ite8709_pnp_probe,
    + .remove = __devexit_p(ite8709_pnp_remove),
    + .suspend = ite8709_pnp_suspend,
    + .resume = ite8709_pnp_resume,
    + .id_table = pnp_dev_table,
    +};
    +
    +int init_module(void)
    +{
    + return pnp_register_driver(&ite8709_pnp_driver);
    +}
    +
    +void cleanup_module(void)
    +{
    + pnp_unregister_driver(&ite8709_pnp_driver);
    +}
    +
    +MODULE_DESCRIPTION("LIRC driver for ITE8709 CIR port");
    +MODULE_AUTHOR("Grégory Lardière");
    +MODULE_LICENSE("GPL");
    +
    +module_param(debug, bool, 0644);
    +MODULE_PARM_DESC(debug, "Enable debugging messages");
    +
    +/*
    + * Overrides for Emacs so that we follow Linus's tabbing style.
    + * ---------------------------------------------------------------------------
    + * Local variables:
    + * c-basic-offset: 8
    + * End:
    + */
    diff --git a/drivers/input/lirc/lirc_mceusb.c b/drivers/input/lirc/lirc_mceusb.c
    new file mode 100644
    index 0000000..f1874f3
    --- /dev/null
    +++ b/drivers/input/lirc/lirc_mceusb.c
    @@ -0,0 +1,890 @@
    +/*
    + * 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 <linux/autoconf.h>
    +#include <linux/kernel.h>
    +#include <linux/errno.h>
    +#include <linux/init.h>
    +#include <linux/slab.h>
    +#include <linux/module.h>
    +#include <linux/smp_lock.h>
    +#include <linux/usb.h>
    +#include <linux/completion.h>
    +#include <linux/uaccess.h>
    +
    +#ifdef CONFIG_USB_DEBUG
    +static int debug = 1;
    +#else
    +static int debug;
    +#endif
    +
    +#include "lirc.h"
    +#include "lirc_dev.h"
    +
    +/* Use our own dbg macro */
    +#define dprintk(fmt, args...) \
    + do { \
    + if (debug) \
    + printk(KERN_DEBUG __FILE__ ": " \
    + fmt "\n", ## args); \
    + } 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"
    +
    +/* 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 */
    +};
    +
    +/* 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 */
    + char num_bulk_in; /* number of bulk in endpoints */
    + char num_bulk_out; /* number of bulk out endpoints */
    +
    + 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 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 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 mutex sem; /* locks this structure */
    +
    + int present; /* if the device is not disconnected */
    +
    + struct lirc_plugin *plugin;
    +
    + int lircdata[256]; /* place to store data until lirc processes it */
    + int lircidx; /* current index */
    + int lirccnt; /* remaining values */
    +
    + int usb_valid_bytes_in_bulk_buffer; /* leftover data from prior read */
    + int mce_bytes_left_in_packet; /* for packets split across reads */
    +
    + /* Value to hold the last received space; 0 if last value
    + * received was a pulse */
    + int last_space;
    +
    + dma_addr_t dma_in;
    + dma_addr_t dma_out;
    +};
    +
    +#define MCE_TIME_UNIT 50
    +
    +/* driver api */
    +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);
    +
    +/* 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 = {
    + .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(KERN_DEBUG "%.2x ", data[i]);
    + printk(KERN_DEBUG "\n");
    +}
    +
    +/**
    + *mceusb_delete
    + */
    +static inline void mceusb_delete(struct usb_skel *dev)
    +{
    + dprintk("%s", __func__);
    + minor_table[dev->minor] = NULL;
    + 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);
    + 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 ); */
    + dprintk("%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);
    + dprintk("%s - res = %d, devnum = %d", __func__, res, udev->devnum);
    + dprintk("%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);
    +
    + dprintk("%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);
    +
    + dprintk("%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);
    +
    + dprintk("%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)
    +{
    + return 0;
    +}
    +
    +static void set_use_dec(void *data)
    +{
    +}
    +
    +/*
    + * 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(int)) - 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 = dev->usb_valid_bytes_in_bulk_buffer;
    + partial = this_read;
    + 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 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;
    +
    + mutex_lock(&dev->sem);
    +
    + /* verify device still present */
    + if (dev->udev == NULL) {
    + mutex_unlock(&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) {
    + mutex_unlock(&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;
    +
    + mutex_unlock(&dev->sem);
    + return 0;
    + }
    +
    + mutex_unlock(&dev->sem);
    + return -ENODATA;
    +}
    +
    +/**
    + * mceusb_write_bulk_callback
    + */
    +static void mceusb_write_bulk_callback(struct urb *urb)
    +{
    + struct usb_skel *dev = (struct usb_skel *)urb->context;
    +
    + dprintk("%s - minor %d", __func__, dev->minor);
    +
    + if ((urb->status != -ENOENT) &&
    + (urb->status != -ECONNRESET)) {
    + dprintk("%s - nonzero write buld status received: %d",
    + __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.
    + */
    +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;
    + 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;
    + char junk[64];
    + int partial = 0;
    +
    + /* See if the device offered us matches what we can accept */
    + if (cpu_to_le16(udev->descriptor.idVendor) != USB_MCEUSB_VENDOR_ID ||
    + cpu_to_le16(udev->descriptor.idProduct) != USB_MCEUSB_PRODUCT_ID) {
    + dprintk("Wrong Vendor/Product IDs");
    + return -ENODEV;
    + }
    +
    + /* 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");
    + retval = -ENOMEM;
    + goto error;
    + }
    + minor_table[minor] = dev;
    +
    + memset(dev, 0x00, sizeof(*dev));
    + mutex_init(&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 */
    + iface_desc = interface->cur_altsetting;
    +
    + for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
    + endpoint = &iface_desc->endpoint[i].desc;
    + if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) &&
    + ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
    + USB_ENDPOINT_XFER_BULK)) {
    + dprintk("we found a bulk in endpoint");
    + buffer_size = endpoint->wMaxPacketSize;
    + dev->bulk_in_size = buffer_size;
    + dev->bulk_in_endpointAddr = endpoint->bEndpointAddress;
    + dev->bulk_in_buffer =
    + usb_buffer_alloc(udev, buffer_size,
    + GFP_ATOMIC, &dev->dma_in);
    + 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)) {
    + dprintk("we found a bulk out endpoint");
    + dev->write_urb = usb_alloc_urb(0, GFP_KERNEL);
    + if (!dev->write_urb) {
    + err("No free urbs available");
    + goto error;
    + }
    + buffer_size = endpoint->wMaxPacketSize;
    + dev->bulk_out_size = buffer_size;
    + dev->bulk_out_endpointAddr = endpoint->bEndpointAddress;
    + dev->bulk_out_buffer =
    + usb_buffer_alloc(udev, buffer_size,
    + GFP_ATOMIC, &dev->dma_out);
    + if (!dev->bulk_out_buffer) {
    + err("Couldn't allocate bulk_out_buffer");
    + goto error;
    + }
    + usb_fill_bulk_urb(dev->write_urb, udev,
    + usb_sndbulkpipe
    + (udev, endpoint->bEndpointAddress),
    + dev->bulk_out_buffer, buffer_size,
    + mceusb_write_bulk_callback, dev);
    + }
    + }
    +
    + 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 */
    + plugin = kmalloc(sizeof(struct lirc_plugin), GFP_KERNEL);
    + if (!plugin) {
    + err("out of memory");
    + goto error;
    + }
    + memset(plugin, 0, sizeof(struct lirc_plugin));
    +
    + rbuf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
    + if (!rbuf) {
    + err("out of memory");
    + kfree(plugin);
    + goto error;
    + }
    +
    + /* the lirc_atiusb module doesn't memset rbuf here ... ? */
    + if (lirc_buffer_init(rbuf, sizeof(int), 128)) {
    + err("out of memory");
    + kfree(plugin);
    + kfree(rbuf);
    + goto error;
    + }
    +
    + strcpy(plugin->name, DRIVER_NAME " ");
    + plugin->minor = minor;
    + plugin->code_length = sizeof(int) * 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; */
    + plugin->dev = &udev->dev;
    + plugin->owner = THIS_MODULE;
    + 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
    + */
    + for (i = 0; i < 40; i++)
    + (void) usb_bulk_msg(udev,
    + usb_rcvbulkpipe(udev,
    + dev->bulk_in_endpointAddr),
    + junk, 64, &partial, HZ*10);
    +
    + msir_cleanup(dev);
    + mceusb_setup(udev);
    +
    + /* we can register the device now, as it is ready */
    + usb_set_intfdata(interface, dev);
    + /* 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);
    + return 0;
    +error:
    + mceusb_delete(dev);
    + dev = NULL;
    + dprintk("%s: retval = %x", __func__, retval);
    + up(&minor_table_mutex);
    + return retval;
    +}
    +
    +/**
    + * 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.
    + */
    +static void mceusb_disconnect(struct usb_interface *interface)
    +{
    + struct usb_skel *dev;
    + int minor;
    + dev = usb_get_intfdata(interface);
    + usb_set_intfdata(interface, NULL);
    +
    + down(&minor_table_mutex);
    + mutex_lock(&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);
    + /* terminate an ongoing write */
    + if (atomic_read(&dev->write_busy)) {
    + usb_kill_urb(dev->write_urb);
    + wait_for_completion(&dev->write_finished);
    + }
    +
    + /* prevent device read, write and ioctl */
    + dev->present = 0;
    +
    + mceusb_delete(dev);
    +
    + info("Microsoft IR Transceiver #%d now disconnected", minor);
    + mutex_unlock(&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);
    + if (result) {
    + err("usb_register failed for the " DRIVER_NAME
    + " driver. error number %d", result);
    + return result;
    + }
    +
    + 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);
    +}
    +
    +#ifdef MODULE
    +module_init(usb_mceusb_init);
    +module_exit(usb_mceusb_exit);
    +
    +MODULE_DESCRIPTION(DRIVER_DESC);
    +MODULE_AUTHOR(DRIVER_AUTHOR);
    +MODULE_LICENSE("GPL");
    +MODULE_DEVICE_TABLE(usb, mceusb_table);
    +
    +module_param(debug, int, 0644);
    +MODULE_PARM_DESC(debug, "Debug enabled or not");
    +
    +#else /* not MODULE */
    +subsys_initcall(usb_mceusb_init);
    +
    +#endif /* MODULE */
    diff --git a/drivers/input/lirc/lirc_mceusb2.c b/drivers/input/lirc/lirc_mceusb2.c
    new file mode 100644
    index 0000000..1e683d3
    --- /dev/null
    +++ b/drivers/input/lirc/lirc_mceusb2.c
    @@ -0,0 +1,1119 @@
    +/*
    + * LIRC driver for Philips eHome USB Infrared Transceiver
    + * and the Microsoft MCE 2005 Remote Control
    + *
    + * (C) by Martin A. Blatter <martin_a_blatter@yahoo.com>
    + *
    + * Transmitter support and reception code cleanup.
    + * (C) by Daniel Melander <lirc@rajidae.se>
    + *
    + * Derived from ATI USB driver by Paul Miller and the original
    + * MCE USB driver by Dan Corti
    + *
    + * This driver will only work reliably with kernel version 2.6.10
    + * or higher, probably because of differences in USB device enumeration
    + * in the kernel code. Device initialization fails most of the time
    + * with earlier kernel versions.
    + *
    + **********************************************************************
    + *
    + * This program is free software; you can redistribute it and/or modify
    + * it under the terms of the GNU General Public License as published by
    + * the Free Software Foundation; either version 2 of the License, or
    + * (at your option) any later version.
    + *
    + * This program is distributed in the hope that it will be useful,
    + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    + * GNU General Public License for more details.
    + *
    + * You should have received a copy of the GNU General Public License
    + * along with this program; if not, write to the Free Software
    + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
    + *
    + */
    +
    +#include <linux/version.h>
    +
    +#include <linux/autoconf.h>
    +
    +#include <linux/kernel.h>
    +#include <linux/errno.h>
    +#include <linux/init.h>
    +#include <linux/slab.h>
    +#include <linux/module.h>
    +#include <linux/kmod.h>
    +#include <linux/smp_lock.h>
    +#include <linux/completion.h>
    +#include <linux/uaccess.h>
    +#include <linux/usb.h>
    +#include <linux/poll.h>
    +#include <linux/wait.h>
    +#include <linux/time.h>
    +
    +#include "lirc.h"
    +#include "lirc_dev.h"
    +
    +#define DRIVER_VERSION "1.48"
    +#define DRIVER_AUTHOR "Daniel Melander <lirc@rajidae.se>, " \
    + "Martin Blatter <martin_a_blatter@yahoo.com>"
    +#define DRIVER_DESC "Philips eHome USB IR Transceiver and Microsoft " \
    + "MCE 2005 Remote Control driver for LIRC"
    +#define DRIVER_NAME "lirc_mceusb2"
    +
    +#define USB_BUFLEN 16 /* USB reception buffer length */
    +#define LIRCBUF_SIZE 256 /* LIRC work buffer length */
    +
    +/* MCE constants */
    +#define MCE_CMDBUF_SIZE 384 /* MCE Command buffer length */
    +#define MCE_TIME_UNIT 50 /* Approx 50us resolution */
    +#define MCE_CODE_LENGTH 5 /* Normal length of packet (with header) */
    +#define MCE_PACKET_SIZE 4 /* Normal length of packet (without header) */
    +#define MCE_PACKET_HEADER 0x84 /* Actual header format is 0x80 + num_bytes */
    +#define MCE_CONTROL_HEADER 0x9F /* MCE status header */
    +#define MCE_TX_HEADER_LENGTH 3 /* # of bytes in the initializing tx header */
    +#define MCE_MAX_CHANNELS 2 /* Two transmitters, hardware dependent? */
    +#define MCE_DEFAULT_TX_MASK 0x03 /* Val opts: TX1=0x01, TX2=0x02, ALL=0x03 */
    +#define MCE_PULSE_BIT 0x80 /* Pulse bit, MSB set == PULSE else SPACE */
    +#define MCE_PULSE_MASK 0x7F /* Pulse mask */
    +#define MCE_MAX_PULSE_LENGTH 0x7F /* Longest transmittable pulse symbol */
    +#define MCE_PACKET_LENGTH_MASK 0x7F /* Pulse mask */
    +
    +
    +/* module parameters */
    +#ifdef CONFIG_USB_DEBUG
    +static int debug = 1;
    +#else
    +static int debug;
    +#endif
    +#define dprintk(fmt, args...) \
    + do { \
    + if (debug) \
    + printk(KERN_DEBUG fmt, ## args); \
    + } while (0)
    +
    +/* lock irctl structure */
    +#define IRLOCK mutex_lock(&ir->lock)
    +#define IRUNLOCK mutex_unlock(&ir->lock)
    +
    +/* general constants */
    +#define SUCCESS 0
    +#define SEND_FLAG_IN_PROGRESS 1
    +#define SEND_FLAG_COMPLETE 2
    +#define RECV_FLAG_IN_PROGRESS 3
    +#define RECV_FLAG_COMPLETE 4
    +
    +#define PHILUSB_INBOUND 1
    +#define PHILUSB_OUTBOUND 2
    +
    +#define VENDOR_PHILIPS 0x0471
    +#define VENDOR_SMK 0x0609
    +#define VENDOR_TATUNG 0x1460
    +#define VENDOR_GATEWAY 0x107b
    +#define VENDOR_SHUTTLE 0x1308
    +#define VENDOR_SHUTTLE2 0x051c
    +#define VENDOR_MITSUMI 0x03ee
    +#define VENDOR_TOPSEED 0x1784
    +#define VENDOR_RICAVISION 0x179d
    +#define VENDOR_ITRON 0x195d
    +#define VENDOR_FIC 0x1509
    +#define VENDOR_LG 0x043e
    +#define VENDOR_MICROSOFT 0x045e
    +#define VENDOR_FORMOSA 0x147a
    +#define VENDOR_FINTEK 0x1934
    +#define VENDOR_PINNACLE 0x2304
    +
    +static struct usb_device_id usb_remote_table[] = {
    + /* Philips eHome Infrared Transceiver */
    + { USB_DEVICE(VENDOR_PHILIPS, 0x0815) },
    + /* Philips Infrared Transceiver - HP branded */
    + { USB_DEVICE(VENDOR_PHILIPS, 0x060c) },
    + /* Philips SRM5100 */
    + { USB_DEVICE(VENDOR_PHILIPS, 0x060d) },
    + /* Philips Infrared Transceiver - Omaura */
    + { USB_DEVICE(VENDOR_PHILIPS, 0x060f) },
    + /* SMK/Toshiba G83C0004D410 */
    + { USB_DEVICE(VENDOR_SMK, 0x031d) },
    + /* SMK eHome Infrared Transceiver (Sony VAIO) */
    + { USB_DEVICE(VENDOR_SMK, 0x0322) },
    + /* bundled with Hauppauge PVR-150 */
    + { USB_DEVICE(VENDOR_SMK, 0x0334) },
    + /* Tatung eHome Infrared Transceiver */
    + { USB_DEVICE(VENDOR_TATUNG, 0x9150) },
    + /* Shuttle eHome Infrared Transceiver */
    + { USB_DEVICE(VENDOR_SHUTTLE, 0xc001) },
    + /* Shuttle eHome Infrared Transceiver */
    + { USB_DEVICE(VENDOR_SHUTTLE2, 0xc001) },
    + /* Gateway eHome Infrared Transceiver */
    + { USB_DEVICE(VENDOR_GATEWAY, 0x3009) },
    + /* Mitsumi */
    + { USB_DEVICE(VENDOR_MITSUMI, 0x2501) },
    + /* Topseed eHome Infrared Transceiver */
    + { USB_DEVICE(VENDOR_TOPSEED, 0x0001) },
    + /* Topseed HP eHome Infrared Transceiver */
    + { USB_DEVICE(VENDOR_TOPSEED, 0x0006) },
    + /* Topseed eHome Infrared Transceiver */
    + { USB_DEVICE(VENDOR_TOPSEED, 0x0007) },
    + /* Topseed eHome Infrared Transceiver */
    + { USB_DEVICE(VENDOR_TOPSEED, 0x0008) },
    + /* Ricavision internal Infrared Transceiver */
    + { USB_DEVICE(VENDOR_RICAVISION, 0x0010) },
    + /* Itron ione Libra Q-11 */
    + { USB_DEVICE(VENDOR_ITRON, 0x7002) },
    + /* FIC eHome Infrared Transceiver */
    + { USB_DEVICE(VENDOR_FIC, 0x9242) },
    + /* LG eHome Infrared Transceiver */
    + { USB_DEVICE(VENDOR_LG, 0x9803) },
    + /* Microsoft MCE Infrared Transceiver */
    + { USB_DEVICE(VENDOR_MICROSOFT, 0x00a0) },
    + /* Formosa eHome Infrared Transceiver */
    + { USB_DEVICE(VENDOR_FORMOSA, 0xe015) },
    + /* Formosa21 / eHome Infrared Receiver */
    + { USB_DEVICE(VENDOR_FORMOSA, 0xe016) },
    + /* Formosa aim / Trust MCE Infrared Receiver */
    + { USB_DEVICE(VENDOR_FORMOSA, 0xe017) },
    + /* Formosa Industrial Computing / Beanbag Emulation Device */
    + { USB_DEVICE(VENDOR_FORMOSA, 0xe018) },
    + /* Fintek eHome Infrared Transceiver */
    + { USB_DEVICE(VENDOR_FINTEK, 0x0602) },
    + /* Pinnacle Remote Kit */
    + { USB_DEVICE(VENDOR_PINNACLE, 0x0225) },
    + /* Terminating entry */
    + { }
    +};
    +
    +static struct usb_device_id pinnacle_list[] = {
    + { USB_DEVICE(VENDOR_PINNACLE, 0x0225) },
    + {}
    +};
    +
    +static struct usb_device_id transmitter_mask_list[] = {
    + { USB_DEVICE(VENDOR_SMK, 0x031d) },
    + { USB_DEVICE(VENDOR_SMK, 0x0322) },
    + { USB_DEVICE(VENDOR_SMK, 0x0334) },
    + { USB_DEVICE(VENDOR_TOPSEED, 0x0001) },
    + { USB_DEVICE(VENDOR_TOPSEED, 0x0007) },
    + { USB_DEVICE(VENDOR_TOPSEED, 0x0008) },
    + { USB_DEVICE(VENDOR_PINNACLE, 0x0225) },
    + {}
    +};
    +
    +/* data structure for each usb remote */
    +struct irctl {
    +
    + /* usb */
    + struct usb_device *usbdev;
    + struct urb *urb_in;
    + int devnum;
    + struct usb_endpoint_descriptor *usb_ep_in;
    + struct usb_endpoint_descriptor *usb_ep_out;
    +
    + /* buffers and dma */
    + unsigned char *buf_in;
    + unsigned int len_in;
    + dma_addr_t dma_in;
    + dma_addr_t dma_out;
    +
    + /* lirc */
    + struct lirc_plugin *p;
    + int lircdata;
    + unsigned char is_pulse;
    + struct {
    + u32 connected:1;
    + u32 pinnacle:1;
    + u32 transmitter_mask_inverted:1;
    + u32 reserved:29;
    + } flags;
    +
    + unsigned char transmitter_mask;
    + unsigned int carrier_freq;
    +
    + /* handle sending (init strings) */
    + int send_flags;
    + wait_queue_head_t wait_out;
    +
    + struct mutex lock;
    +};
    +
    +/* init strings */
    +static char init1[] = {0x00, 0xff, 0xaa, 0xff, 0x0b};
    +static char init2[] = {0xff, 0x18};
    +
    +static char pin_init1[] = { 0x9f, 0x07};
    +static char pin_init2[] = { 0x9f, 0x13};
    +static char pin_init3[] = { 0x9f, 0x0d};
    +
    +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(KERN_INFO "" DRIVER_NAME "[%d]: data received %s (length=%d)\n",
    + ir->devnum, codes, len);
    +}
    +
    +static void usb_async_callback(struct urb *urb, struct pt_regs *regs)
    +{
    + struct irctl *ir;
    + int len;
    +
    + if (!urb)
    + return;
    +
    + ir = urb->context;
    + if (ir) {
    + len = urb->actual_length;
    +
    + dprintk(DRIVER_NAME
    + "[%d]: callback called (status=%d len=%d)\n",
    + ir->devnum, urb->status, len);
    +
    + if (debug)
    + usb_remote_printdata(ir, urb->transfer_buffer, len);
    + }
    +
    +}
    +
    +
    +/* request incoming or send outgoing usb packet - used to initialize remote */
    +static void request_packet_async(struct irctl *ir,
    + struct usb_endpoint_descriptor *ep,
    + unsigned char *data, int size, int urb_type)
    +{
    + int res;
    + struct urb *async_urb;
    + unsigned char *async_buf;
    +
    + if (urb_type) {
    + async_urb = usb_alloc_urb(0, GFP_KERNEL);
    + if (async_urb) {
    + /* alloc buffer */
    + async_buf = kmalloc(size, GFP_KERNEL);
    + if (async_buf) {
    + if (urb_type == PHILUSB_OUTBOUND) {
    + /* outbound data */
    + usb_fill_int_urb(async_urb, ir->usbdev,
    + usb_sndintpipe(ir->usbdev,
    + ep->bEndpointAddress),
    + async_buf,
    + size,
    + (usb_complete_t) usb_async_callback,
    + ir, ep->bInterval);
    +
    + memcpy(async_buf, data, size);
    + } else {
    + /* inbound data */
    + usb_fill_int_urb(async_urb, ir->usbdev,
    + usb_rcvintpipe(ir->usbdev,
    + ep->bEndpointAddress),
    + async_buf, size,
    + (usb_complete_t) usb_async_callback,
    + ir, ep->bInterval);
    + }
    + } else {
    + usb_free_urb(async_urb);
    + return;
    + }
    + }
    + } else {
    + /* standard request */
    + async_urb = ir->urb_in;
    + ir->send_flags = RECV_FLAG_IN_PROGRESS;
    + }
    + dprintk(DRIVER_NAME "[%d]: receive request called (size=%#x)\n",
    + ir->devnum, size);
    +
    + async_urb->transfer_buffer_length = size;
    + async_urb->dev = ir->usbdev;
    +
    + res = usb_submit_urb(async_urb, GFP_ATOMIC);
    + if (res) {
    + dprintk(DRIVER_NAME "[%d]: receive request FAILED! (res=%d)\n",
    + ir->devnum, res);
    + return;
    + }
    + dprintk(DRIVER_NAME "[%d]: receive request complete (res=%d)\n",
    + ir->devnum, res);
    +}
    +
    +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);
    +
    + rtn = lirc_unregister_plugin(p->minor);
    + if (rtn > 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);
    +
    + rtn = lirc_unregister_plugin(p->minor);
    + if (rtn > 0)
    + printk(DRIVER_NAME "[%d]: lirc_unregister failed\n",
    + devnum);
    + }
    +
    + if (rtn != SUCCESS) {
    + printk(DRIVER_NAME "[%d]: didn't free resources\n", devnum);
    + return -EAGAIN;
    + }
    +
    + printk(DRIVER_NAME "[%d]: usb remote disconnected\n", devnum);
    +
    + lirc_buffer_free(p->rbuf);
    + kfree(p->rbuf);
    + kfree(p);
    + kfree(ir);
    + return SUCCESS;
    +}
    +
    +static int set_use_inc(void *data)
    +{
    + struct irctl *ir = data;
    +
    + if (!ir) {
    + printk(DRIVER_NAME "[?]: set_use_inc called with no context\n");
    + return -EIO;
    + }
    + dprintk(DRIVER_NAME "[%d]: set use inc\n", ir->devnum);
    +
    + if (!ir->flags.connected) {
    + if (!ir->usbdev)
    + return -ENOENT;
    + ir->flags.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->flags.connected) {
    + IRLOCK;
    + ir->flags.connected = 0;
    + IRUNLOCK;
    + }
    +}
    +
    +static void send_packet_to_lirc(struct irctl *ir)
    +{
    + if (ir->lircdata != 0) {
    + lirc_buffer_write_1(ir->p->rbuf,
    + (unsigned char *) &ir->lircdata);
    + wake_up(&ir->p->rbuf->wait_poll);
    + ir->lircdata = 0;
    + }
    +}
    +
    +static void usb_remote_recv(struct urb *urb, struct pt_regs *regs)
    +{
    + struct irctl *ir;
    + int buf_len, packet_len;
    + int i, j;
    +
    + if (!urb)
    + return;
    +
    + ir = urb->context;
    + if (!ir) {
    + usb_unlink_urb(urb);
    + return;
    + }
    +
    + buf_len = urb->actual_length;
    + packet_len = 0;
    +
    + if (debug)
    + usb_remote_printdata(ir, urb->transfer_buffer, buf_len);
    +
    + if (ir->send_flags == RECV_FLAG_IN_PROGRESS) {
    + ir->send_flags = SEND_FLAG_COMPLETE;
    + dprintk(DRIVER_NAME "[%d]: setup answer received %d bytes\n",
    + ir->devnum, buf_len);
    + }
    +
    + switch (urb->status) {
    + /* success */
    + case SUCCESS:
    + for (i = 0; i < buf_len; i++) {
    + /* decode mce packets of the form (84),AA,BB,CC,DD */
    + switch (ir->buf_in[i]) {
    +
    + /* data headers */
    + case 0x90: /* used Pinnacle Remote Kit */
    + case 0x8F:
    + case 0x8E:
    + case 0x8D:
    + case 0x8C:
    + case 0x8B:
    + case 0x8A:
    + case 0x89:
    + case 0x88:
    + case 0x87:
    + case 0x86:
    + case 0x85:
    + case 0x84:
    + case 0x83:
    + case 0x82:
    + case 0x81:
    + case 0x80:
    + /* decode packet data */
    + packet_len = ir->buf_in[i] &
    + MCE_PACKET_LENGTH_MASK;
    + for (j = 1;
    + j <= packet_len && (i+j < buf_len);
    + j++) {
    + /* rising/falling flank */
    + if (ir->is_pulse !=
    + (ir->buf_in[i + j] &
    + MCE_PULSE_BIT)) {
    + send_packet_to_lirc(ir);
    + ir->is_pulse =
    + ir->buf_in[i + j] &
    + MCE_PULSE_BIT;
    + }
    +
    + /* accumulate mce pulse/space values */
    + ir->lircdata +=
    + (ir->buf_in[i + j] &
    + MCE_PULSE_MASK)*MCE_TIME_UNIT;
    + ir->lircdata |=
    + (ir->is_pulse ? PULSE_BIT : 0);
    + }
    +
    + i += packet_len;
    + break;
    +
    + /* status header (0x9F) */
    + case MCE_CONTROL_HEADER:
    + /* A transmission containing one or
    + more consecutive ir commands always
    + ends with a GAP of 100ms followed by the
    + sequence 0x9F 0x01 0x01 0x9F 0x15
    + 0x00 0x00 0x80 */
    +
    + /*
    + Uncomment this if the last 100ms
    + "infinity"-space should be transmitted
    + to lirc directly instead of at the beginning
    + of the next transmission. Changes pulse/space order.
    +
    + if (++i < buf_len && ir->buf_in[i]==0x01)
    + send_packet_to_lirc(ir);
    +
    + */
    +
    + /* end decode loop */
    + i = buf_len;
    + break;
    + default:
    + break;
    + }
    + }
    +
    + break;
    +
    + /* unlink */
    + case -ECONNRESET:
    + case -ENOENT:
    + case -ESHUTDOWN:
    + usb_unlink_urb(urb);
    + return;
    +
    + case -EPIPE:
    + default:
    + break;
    + }
    +
    + /* resubmit urb */
    + usb_submit_urb(urb, GFP_ATOMIC);
    +}
    +
    +
    +static ssize_t lirc_write(struct file *file, const char *buf,
    + size_t n, loff_t *ppos)
    +{
    + int i, count = 0, cmdcount = 0;
    + struct irctl *ir = NULL;
    + int wbuf[LIRCBUF_SIZE]; /* Workbuffer with values from lirc */
    + unsigned char cmdbuf[MCE_CMDBUF_SIZE]; /* MCE command buffer */
    + unsigned long signal_duration = 0; /* Singnal length in us */
    + struct timeval start_time, end_time;
    +
    + do_gettimeofday(&start_time);
    +
    + /* Retrieve lirc_plugin data for the device */
    + ir = lirc_get_pdata(file);
    + if (!ir && !ir->usb_ep_out)
    + return -EFAULT;
    +
    + if (n % sizeof(int))
    + return -EINVAL;
    + count = n / sizeof(int);
    +
    + /* Check if command is within limits */
    + if (count > LIRCBUF_SIZE || count%2 == 0)
    + return -EINVAL;
    + if (copy_from_user(wbuf, buf, n))
    + return -EFAULT;
    +
    + /* MCE tx init header */
    + cmdbuf[cmdcount++] = MCE_CONTROL_HEADER;
    + cmdbuf[cmdcount++] = 0x08;
    + cmdbuf[cmdcount++] = ir->transmitter_mask;
    +
    + /* Generate mce packet data */
    + for (i = 0; (i < count) && (cmdcount < MCE_CMDBUF_SIZE); i++) {
    + signal_duration += wbuf[i];
    + wbuf[i] = wbuf[i] / MCE_TIME_UNIT;
    +
    + do { /* loop to support long pulses/spaces > 127*50us=6.35ms */
    +
    + /* Insert mce packet header every 4th entry */
    + if ((cmdcount < MCE_CMDBUF_SIZE) &&
    + (cmdcount - MCE_TX_HEADER_LENGTH) %
    + MCE_CODE_LENGTH == 0)
    + cmdbuf[cmdcount++] = MCE_PACKET_HEADER;
    +
    + /* Insert mce packet data */
    + if (cmdcount < MCE_CMDBUF_SIZE)
    + cmdbuf[cmdcount++] =
    + (wbuf[i] < MCE_PULSE_BIT ?
    + wbuf[i] : MCE_MAX_PULSE_LENGTH) |
    + (i & 1 ? 0x00 : MCE_PULSE_BIT);
    + else
    + return -EINVAL;
    + } while ((wbuf[i] > MCE_MAX_PULSE_LENGTH) &&
    + (wbuf[i] -= MCE_MAX_PULSE_LENGTH));
    + }
    +
    + /* Fix packet length in last header */
    + cmdbuf[cmdcount - (cmdcount - MCE_TX_HEADER_LENGTH) % MCE_CODE_LENGTH] =
    + 0x80 + (cmdcount - MCE_TX_HEADER_LENGTH) % MCE_CODE_LENGTH - 1;
    +
    + /* Check if we have room for the empty packet at the end */
    + if (cmdcount >= MCE_CMDBUF_SIZE)
    + return -EINVAL;
    +
    + /* All mce commands end with an empty packet (0x80) */
    + cmdbuf[cmdcount++] = 0x80;
    +
    + /* Transmit the command to the mce device */
    + request_packet_async(ir, ir->usb_ep_out, cmdbuf,
    + cmdcount, PHILUSB_OUTBOUND);
    +
    + /* The lircd gap calculation expects the write function to
    + wait the time it takes for the ircommand to be sent before
    + it returns. */
    + do_gettimeofday(&end_time);
    + signal_duration -= (end_time.tv_usec - start_time.tv_usec) +
    + (end_time.tv_sec - start_time.tv_sec) * 1000000;
    +
    + /* delay with the closest number of ticks */
    + set_current_state(TASK_INTERRUPTIBLE);
    + schedule_timeout(usecs_to_jiffies(signal_duration));
    +
    + return n;
    +}
    +
    +static void set_transmitter_mask(struct irctl *ir, unsigned int mask)
    +{
    + if (ir->flags.transmitter_mask_inverted)
    + ir->transmitter_mask = (mask != 0x03 ? mask ^ 0x03 : mask) << 1;
    + else
    + ir->transmitter_mask = mask;
    +}
    +
    +
    +/* Sets the send carrier frequency */
    +static int set_send_carrier(struct irctl *ir, int carrier)
    +{
    + int clk = 10000000;
    + int prescaler = 0, divisor = 0;
    + unsigned char cmdbuf[] = { 0x9F, 0x06, 0x01, 0x80 };
    +
    + /* Carrier is changed */
    + if (ir->carrier_freq != carrier) {
    +
    + if (carrier <= 0) {
    + ir->carrier_freq = carrier;
    + dprintk(DRIVER_NAME "[%d]: SET_CARRIER disabling "
    + "carrier modulation\n", ir->devnum);
    + request_packet_async(ir, ir->usb_ep_out,
    + cmdbuf, sizeof(cmdbuf),
    + PHILUSB_OUTBOUND);
    + return carrier;
    + }
    +
    + for (prescaler = 0; prescaler < 4; ++prescaler) {
    + divisor = (clk >> (2 * prescaler)) / carrier;
    + if (divisor <= 0xFF) {
    + ir->carrier_freq = carrier;
    + cmdbuf[2] = prescaler;
    + cmdbuf[3] = divisor;
    + dprintk(DRIVER_NAME "[%d]: SET_CARRIER "
    + "requesting %d Hz\n",
    + ir->devnum, carrier);
    +
    + /* Transmit the new carrier to the mce
    + device */
    + request_packet_async(ir, ir->usb_ep_out,
    + cmdbuf, sizeof(cmdbuf),
    + PHILUSB_OUTBOUND);
    + return carrier;
    + }
    + }
    +
    + return -EINVAL;
    +
    + }
    +
    + return carrier;
    +}
    +
    +
    +static int lirc_ioctl(struct inode *node, struct file *filep,
    + unsigned int cmd, unsigned long arg)
    +{
    + int result;
    + unsigned int ivalue;
    + unsigned long lvalue;
    + struct irctl *ir = NULL;
    +
    + /* Retrieve lirc_plugin data for the device */
    + ir = lirc_get_pdata(filep);
    + if (!ir && !ir->usb_ep_out)
    + return -EFAULT;
    +
    +
    + switch (cmd) {
    + case LIRC_SET_TRANSMITTER_MASK:
    +
    + result = get_user(ivalue, (unsigned int *) arg);
    + if (result)
    + return result;
    + switch (ivalue) {
    + case 0x01: /* Transmitter 1 => 0x04 */
    + case 0x02: /* Transmitter 2 => 0x02 */
    + case 0x03: /* Transmitter 1 & 2 => 0x06 */
    + set_transmitter_mask(ir, ivalue);
    + break;
    +
    + default: /* Unsupported transmitter mask */
    + return MCE_MAX_CHANNELS;
    + }
    +
    + dprintk(DRIVER_NAME ": SET_TRANSMITTERS mask=%d\n", ivalue);
    + break;
    +
    + case LIRC_GET_SEND_MODE:
    +
    + result = put_user(LIRC_SEND2MODE(LIRC_CAN_SEND_PULSE &
    + LIRC_CAN_SEND_MASK),
    + (unsigned long *) arg);
    +
    + if (result)
    + return result;
    + break;
    +
    + case LIRC_SET_SEND_MODE:
    +
    + result = get_user(lvalue, (unsigned long *) arg);
    +
    + if (result)
    + return result;
    + if (lvalue != (LIRC_MODE_PULSE&LIRC_CAN_SEND_MASK))
    + return -EINVAL;
    + break;
    +
    + case LIRC_SET_SEND_CARRIER:
    +
    + result = get_user(ivalue, (unsigned int *) arg);
    + if (result)
    + return result;
    +
    + set_send_carrier(ir, ivalue);
    + break;
    +
    + default:
    + return -ENOIOCTLCMD;
    + }
    +
    + return 0;
    +}
    +
    +static struct file_operations lirc_fops = {
    + .write = lirc_write,
    +};
    +
    +
    +static int usb_remote_probe(struct usb_interface *intf,
    + const struct usb_device_id *id)
    +{
    + struct usb_device *dev = interface_to_usbdev(intf);
    + struct usb_host_interface *idesc;
    + struct usb_endpoint_descriptor *ep = NULL;
    + struct usb_endpoint_descriptor *ep_in = NULL;
    + struct usb_endpoint_descriptor *ep_out = NULL;
    + struct usb_host_config *config;
    + struct irctl *ir = NULL;
    + struct lirc_plugin *plugin = NULL;
    + struct lirc_buffer *rbuf = NULL;
    + int devnum, pipe, maxp;
    + int minor = 0;
    + int i;
    + char buf[63], name[128] = "";
    + int mem_failure = 0;
    + int is_pinnacle;
    +
    + dprintk(DRIVER_NAME ": usb probe called\n");
    +
    + usb_reset_device(dev);
    +
    + config = dev->actconfig;
    +
    + idesc = intf->cur_altsetting;
    +
    + is_pinnacle = usb_match_id(intf, pinnacle_list) ? 1 : 0;
    +
    + /* step through the endpoints to find first bulk in and out endpoint */
    + for (i = 0; i < idesc->desc.bNumEndpoints; ++i) {
    + ep = &idesc->endpoint[i].desc;
    +
    + if ((ep_in == NULL)
    + && ((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
    + == USB_DIR_IN)
    + && (((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
    + == USB_ENDPOINT_XFER_BULK)
    + || ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
    + == USB_ENDPOINT_XFER_INT))) {
    +
    + dprintk(DRIVER_NAME ": acceptable inbound endpoint "
    + "found\n");
    + ep_in = ep;
    + ep_in->bmAttributes = USB_ENDPOINT_XFER_INT;
    + if (is_pinnacle)
    + /*
    + * setting seems to 1 seem to cause issues with
    + * Pinnacle timing out on transfer.
    + */
    + ep_in->bInterval = ep->bInterval;
    + else
    + ep_in->bInterval = 1;
    + }
    +
    + if ((ep_out == NULL)
    + && ((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
    + == USB_DIR_OUT)
    + && (((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
    + == USB_ENDPOINT_XFER_BULK)
    + || ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
    + == USB_ENDPOINT_XFER_INT))) {
    +
    + dprintk(DRIVER_NAME ": acceptable outbound endpoint "
    + "found\n");
    + ep_out = ep;
    + ep_out->bmAttributes = USB_ENDPOINT_XFER_INT;
    + if (is_pinnacle)
    + /*
    + * setting seems to 1 seem to cause issues with
    + * Pinnacle timing out on transfer.
    + */
    + ep_out->bInterval = ep->bInterval;
    + else
    + ep_out->bInterval = 1;
    + }
    + }
    + if (ep_in == NULL) {
    + dprintk(DRIVER_NAME ": inbound and/or endpoint not found\n");
    + return -ENODEV;
    + }
    +
    + devnum = dev->devnum;
    + pipe = usb_rcvintpipe(dev, ep_in->bEndpointAddress);
    + maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
    +
    + /* allocate kernel memory */
    + mem_failure = 0;
    + ir = kmalloc(sizeof(struct irctl), GFP_KERNEL);
    + if (!ir) {
    + mem_failure = 1;
    + goto mem_failure_switch;
    + }
    +
    + memset(ir, 0, sizeof(struct irctl));
    +
    + plugin = kmalloc(sizeof(struct lirc_plugin), GFP_KERNEL);
    + if (!plugin) {
    + mem_failure = 2;
    + goto mem_failure_switch;
    + }
    +
    + rbuf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
    + if (!rbuf) {
    + mem_failure = 3;
    + goto mem_failure_switch;
    + }
    +
    + if (lirc_buffer_init(rbuf, sizeof(int), LIRCBUF_SIZE)) {
    + mem_failure = 4;
    + goto mem_failure_switch;
    + }
    +
    + ir->buf_in = usb_buffer_alloc(dev, maxp, GFP_ATOMIC, &ir->dma_in);
    + if (!ir->buf_in) {
    + mem_failure = 5;
    + goto mem_failure_switch;
    + }
    +
    + ir->urb_in = usb_alloc_urb(0, GFP_KERNEL);
    + if (!ir->urb_in) {
    + mem_failure = 7;
    + goto mem_failure_switch;
    + }
    +
    + memset(plugin, 0, sizeof(struct lirc_plugin));
    +
    + strcpy(plugin->name, DRIVER_NAME " ");
    + plugin->minor = -1;
    + plugin->features = LIRC_CAN_SEND_PULSE |
    + LIRC_CAN_SET_TRANSMITTER_MASK |
    + LIRC_CAN_REC_MODE2 |
    + LIRC_CAN_SET_SEND_CARRIER;
    + plugin->data = ir;
    + plugin->rbuf = rbuf;
    + plugin->set_use_inc = &set_use_inc;
    + plugin->set_use_dec = &set_use_dec;
    + plugin->code_length = sizeof(int) * 8;
    + plugin->ioctl = lirc_ioctl;
    + plugin->fops = &lirc_fops;
    + plugin->dev = &dev->dev;
    + plugin->owner = THIS_MODULE;
    +
    + mutex_init(&ir->lock);
    + init_waitqueue_head(&ir->wait_out);
    +
    + minor = lirc_register_plugin(plugin);
    + if (minor < 0)
    + mem_failure = 9;
    +
    +mem_failure_switch:
    +
    + /* free allocated memory incase of failure */
    + switch (mem_failure) {
    + case 9:
    + usb_free_urb(ir->urb_in);
    + case 7:
    + usb_buffer_free(dev, maxp, ir->buf_in, ir->dma_in);
    + case 5:
    + lirc_buffer_free(rbuf);
    + 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);
    + return -ENOMEM;
    + }
    +
    + plugin->minor = minor;
    + ir->p = plugin;
    + ir->devnum = devnum;
    + ir->usbdev = dev;
    + ir->len_in = maxp;
    + ir->flags.connected = 0;
    + ir->flags.pinnacle = is_pinnacle;
    + ir->flags.transmitter_mask_inverted =
    + usb_match_id(intf, transmitter_mask_list) ? 0 : 1;
    +
    + ir->lircdata = PULSE_MASK;
    + ir->is_pulse = 0;
    +
    + /* ir->flags.transmitter_mask_inverted must be set */
    + set_transmitter_mask(ir, MCE_DEFAULT_TX_MASK);
    + /* Saving usb interface data for use by the transmitter routine */
    + ir->usb_ep_in = ep_in;
    + ir->usb_ep_out = ep_out;
    +
    + 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);
    +
    + /* inbound data */
    + usb_fill_int_urb(ir->urb_in, dev, pipe, ir->buf_in,
    + maxp, (usb_complete_t) usb_remote_recv, ir, ep_in->bInterval);
    +
    + /* initialize device */
    + if (ir->flags.pinnacle) {
    + int usbret;
    +
    + /*
    + * I have no idea why but this reset seems to be crucial to
    + * getting the device to do outbound IO correctly - without
    + * this the device seems to hang, ignoring all input - although
    + * IR signals are correctly sent from the device, no input is
    + * interpreted by the device and the host never does the
    + * completion routine
    + */
    +
    + usbret = usb_reset_configuration(dev);
    + printk(DRIVER_NAME "[%d]: usb reset config ret %x\n",
    + devnum, usbret);
    +
    + /*
    + * its possible we really should wait for a return
    + * for each of these...
    + */
    + request_packet_async(ir, ep_in, NULL, maxp, PHILUSB_INBOUND);
    + request_packet_async(ir, ep_out, pin_init1, sizeof(pin_init1),
    + PHILUSB_OUTBOUND);
    + request_packet_async(ir, ep_in, NULL, maxp, PHILUSB_INBOUND);
    + request_packet_async(ir, ep_out, pin_init2, sizeof(pin_init2),
    + PHILUSB_OUTBOUND);
    + request_packet_async(ir, ep_in, NULL, maxp, PHILUSB_INBOUND);
    + request_packet_async(ir, ep_out, pin_init3, sizeof(pin_init3),
    + PHILUSB_OUTBOUND);
    + /* if we dont issue the correct number of receives
    + * (PHILUSB_INBOUND) for each outbound, then the first few ir
    + * pulses will be interpreted by the usb_async_callback routine
    + * - we should ensure we have the right amount OR less - as the
    + * usb_remote_recv routine will handle the control packets OK -
    + * they start with 0x9f - but the async callback doesnt handle
    + * ir pulse packets
    + */
    + request_packet_async(ir, ep_in, NULL, maxp, 0);
    + } else {
    + request_packet_async(ir, ep_in, NULL, maxp, PHILUSB_INBOUND);
    + request_packet_async(ir, ep_in, NULL, maxp, PHILUSB_INBOUND);
    + request_packet_async(ir, ep_out, init1,
    + sizeof(init1), PHILUSB_OUTBOUND);
    + request_packet_async(ir, ep_in, NULL, maxp, PHILUSB_INBOUND);
    + request_packet_async(ir, ep_out, init2,
    + sizeof(init2), PHILUSB_OUTBOUND);
    + request_packet_async(ir, ep_in, NULL, maxp, 0);
    + }
    +
    + usb_set_intfdata(intf, ir);
    +
    + return SUCCESS;
    +}
    +
    +
    +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);
    +
    + if (!ir || !ir->p)
    + return;
    +
    + ir->usbdev = NULL;
    + wake_up_all(&ir->wait_out);
    +
    + IRLOCK;
    + usb_kill_urb(ir->urb_in);
    + usb_free_urb(ir->urb_in);
    + usb_buffer_free(dev, ir->len_in, ir->buf_in, ir->dma_in);
    + IRUNLOCK;
    +
    + unregister_from_lirc(ir);
    +}
    +
    +static int usb_remote_suspend(struct usb_interface *intf, pm_message_t message)
    +{
    + struct irctl *ir = usb_get_intfdata(intf);
    + printk(DRIVER_NAME "[%d]: suspend\n", ir->devnum);
    + usb_kill_urb(ir->urb_in);
    + return 0;
    +}
    +
    +static int usb_remote_resume(struct usb_interface *intf)
    +{
    + struct irctl *ir = usb_get_intfdata(intf);
    + printk(DRIVER_NAME "[%d]: resume\n", ir->devnum);
    + if (usb_submit_urb(ir->urb_in, GFP_ATOMIC))
    + return -EIO;
    + return 0;
    +}
    +
    +static struct usb_driver usb_remote_driver = {
    + .name = DRIVER_NAME,
    + .probe = usb_remote_probe,
    + .disconnect = usb_remote_disconnect,
    + .suspend = usb_remote_suspend,
    + .resume = usb_remote_resume,
    + .id_table = usb_remote_table
    +};
    +
    +#ifdef MODULE
    +static int __init usb_remote_init(void)
    +{
    + int i;
    +
    + printk(KERN_INFO "\n");
    + printk(KERN_INFO DRIVER_NAME ": " DRIVER_DESC " " DRIVER_VERSION "\n");
    + printk(KERN_INFO DRIVER_NAME ": " DRIVER_AUTHOR "\n");
    + dprintk(DRIVER_NAME ": debug mode enabled\n");
    +
    + request_module("lirc_dev");
    +
    + i = usb_register(&usb_remote_driver);
    + if (i < 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_DESCRIPTION(DRIVER_DESC);
    +MODULE_AUTHOR(DRIVER_AUTHOR);
    +MODULE_LICENSE("GPL");
    +MODULE_DEVICE_TABLE(usb, usb_remote_table);
    +
    +module_param(debug, bool, 0644);
    +MODULE_PARM_DESC(debug, "Debug enabled or not");
    +#endif /* MODULE */
    diff --git a/drivers/input/lirc/lirc_parallel.c b/drivers/input/lirc/lirc_parallel.c
    new file mode 100644
    index 0000000..912cad2
    --- /dev/null
    +++ b/drivers/input/lirc/lirc_parallel.c
    @@ -0,0 +1,728 @@
    +/****************************************************************************
    + ** lirc_parallel.c *********************************************************
    + ****************************************************************************
    + *
    + * lirc_parallel - device driver for infra-red signal receiving and
    + * transmitting unit built by the author
    + *
    + * Copyright (C) 1998 Christoph Bartelmus <lirc@bartelmus.de>
    + *
    + * This program is free software; you can redistribute it and/or modify
    + * it under the terms of the GNU General Public License as published by
    + * the Free Software Foundation; either version 2 of the License, or
    + * (at your option) any later version.
    + *
    + * This program is distributed in the hope that it will be useful,
    + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    + * GNU General Public License for more details.
    + *
    + * You should have received a copy of the GNU General Public License
    + * along with this program; if not, write to the Free Software
    + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
    + *
    + */
    +
    +/***********************************************************************
    + ************************* Includes ***********************
    + ***********************************************************************/
    +
    +#include <linux/version.h>
    +
    +#include <linux/autoconf.h>
    +
    +#ifdef CONFIG_SMP
    +#error "--- Sorry, this driver is not SMP safe. ---"
    +#endif
    +
    +#include <linux/module.h>
    +#include <linux/sched.h>
    +#include <linux/errno.h>
    +#include <linux/signal.h>
    +#include <linux/autoconf.h>
    +#include <linux/fs.h>
    +#include <linux/kernel.h>
    +#include <linux/ioport.h>
    +#include <linux/time.h>
    +#include <linux/mm.h>
    +#include <linux/delay.h>
    +
    +#include <linux/io.h>
    +#include <linux/signal.h>
    +#include <linux/irq.h>
    +#include <linux/uaccess.h>
    +#include <asm/div64.h>
    +
    +#include <linux/poll.h>
    +#include <linux/parport.h>
    +
    +#include "lirc.h"
    +#include "lirc_dev.h"
    +
    +#include "lirc_parallel.h"
    +
    +#define LIRC_DRIVER_NAME "lirc_parallel"
    +
    +#ifndef LIRC_IRQ
    +#define LIRC_IRQ 7
    +#endif
    +#ifndef LIRC_PORT
    +#define LIRC_PORT 0x378
    +#endif
    +#ifndef LIRC_TIMER
    +#define LIRC_TIMER 65536
    +#endif
    +
    +/***********************************************************************
    + ************************* Globale Variablen ***********************
    + ***********************************************************************/
    +
    +static int debug;
    +static int check_pselecd;
    +
    +unsigned int irq = LIRC_IRQ;
    +unsigned int io = LIRC_PORT;
    +#ifdef LIRC_TIMER
    +unsigned int timer;
    +unsigned int default_timer = LIRC_TIMER;
    +#endif
    +
    +#define WBUF_SIZE (256)
    +#define RBUF_SIZE (256) /* this must be a power of 2 larger than 1 */
    +
    +static int wbuf[WBUF_SIZE];
    +static int rbuf[RBUF_SIZE];
    +
    +DECLARE_WAIT_QUEUE_HEAD(lirc_wait);
    +
    +unsigned int rptr;
    +unsigned int wptr;
    +unsigned int lost_irqs;
    +int is_open;
    +
    +struct parport *pport;
    +struct pardevice *ppdevice;
    +int is_claimed;
    +
    +unsigned int tx_mask = 1;
    +
    +/***********************************************************************
    + ************************* Interne Funktionen ***********************
    + ***********************************************************************/
    +
    +static inline unsigned int 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 */
    +}
    +
    +static inline void 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;
    + }
    +}
    +
    +static inline unsigned int lirc_get_timer(void)
    +{
    + return in(LIRC_PORT_TIMER) & LIRC_PORT_TIMER_BIT;
    +}
    +
    +static inline unsigned int lirc_get_signal(void)
    +{
    + return in(LIRC_PORT_SIGNAL) & LIRC_PORT_SIGNAL_BIT;
    +}
    +
    +static inline void lirc_on(void)
    +{
    + out(LIRC_PORT_DATA, tx_mask);
    +}
    +
    +static inline void lirc_off(void)
    +{
    + out(LIRC_PORT_DATA, 0);
    +}
    +
    +static 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 < tv.tv_sec
    + || (now.tv_sec == tv.tv_sec
    + && now.tv_usec < tv.tv_usec)));
    +
    + timeelapsed = ((now.tv_sec + 1 - tv.tv_sec)*1000000
    + + (now.tv_usec - tv.tv_usec));
    + if (count >= 1000 && timeelapsed > 0) {
    + if (default_timer == 0) {
    + /* autodetect timer */
    + newtimer = (1000000*count)/timeelapsed;
    + printk(KERN_INFO "%s: %u Hz timer detected\n",
    + LIRC_DRIVER_NAME, newtimer);
    + return newtimer;
    + } else {
    + newtimer = (1000000*count)/timeelapsed;
    + if (abs(newtimer - default_timer) > default_timer/10) {
    + /* bad timer */
    + printk(KERN_NOTICE "%s: bad timer: %u Hz\n",
    + LIRC_DRIVER_NAME, newtimer);
    + printk(KERN_NOTICE "%s: using default timer: "
    + "%u Hz\n",
    + LIRC_DRIVER_NAME, default_timer);
    + return default_timer;
    + } else {
    + printk(KERN_INFO "%s: %u Hz timer detected\n",
    + LIRC_DRIVER_NAME, newtimer);
    + return newtimer; /* use detected value */
    + }
    + }
    + } else {
    + printk(KERN_NOTICE "%s: no timer detected\n", LIRC_DRIVER_NAME);
    + return 0;
    + }
    +}
    +
    +static 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(int 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;
    +}
    +
    +static void irq_handler(void *blah)
    +{
    + struct timeval tv;
    + static struct timeval lasttv;
    + static int init;
    + long signal;
    + int data;
    + unsigned int level, newlevel;
    + unsigned int timeout;
    +
    + if (!module_refcount(THIS_MODULE))
    + return;
    +
    + if (!is_claimed)
    + return;
    +
    + /* disable interrupt */
    + /*
    + disable_irq(irq);
    + out(LIRC_PORT_IRQ, in(LIRC_PORT_IRQ) & (~LP_PINTEN));
    + */
    + if (check_pselecd && (in(1) & LP_PSELECD))
    + return;
    +
    +#ifdef LIRC_TIMER
    + if (init) {
    + do_gettimeofday(&tv);
    +
    + signal = tv.tv_sec - lasttv.tv_sec;
    + if (signal > 15)
    + /* really long time */
    + data = PULSE_MASK;
    + else
    + data = (int) (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
    + || (check_pselecd && (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 */
    + unsigned long long helper;
    +
    + helper = ((unsigned long long) signal)*1000000;
    + do_div(helper, timer);
    + signal = (long) helper;
    +
    + 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 = 0;
    + int count = 0;
    + DECLARE_WAITQUEUE(wait, current);
    +
    + if (n % sizeof(int))
    + return -EINVAL;
    +
    + add_wait_queue(&lirc_wait, &wait);
    + set_current_state(TASK_INTERRUPTIBLE);
    + while (count < n) {
    + if (rptr != wptr) {
    + if (copy_to_user(buf+count, (char *) &rbuf[rptr],
    + sizeof(int))) {
    + result = -EFAULT;
    + break;
    + }
    + rptr = (rptr + 1) & (RBUF_SIZE - 1);
    + count += sizeof(int);
    + } else {
    + if (filep->f_flags & O_NONBLOCK) {
    + result = -EAGAIN;
    + break;
    + }
    + if (signal_pending(current)) {
    + result = -ERESTARTSYS;
    + break;
    + }
    + schedule();
    + set_current_state(TASK_INTERRUPTIBLE);
    + }
    + }
    + remove_wait_queue(&lirc_wait, &wait);
    + set_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 count;
    + unsigned int i;
    + unsigned int level, newlevel;
    + unsigned long flags;
    + int counttimer;
    +
    + if (!is_claimed)
    + return -EBUSY;
    +
    + if (n % sizeof(int))
    + return -EINVAL;
    +
    + count = n / sizeof(int);
    +
    + if (count > WBUF_SIZE || count % 2 == 0)
    + return -EINVAL;
    +
    + if (copy_from_user(wbuf, buf, n))
    + return -EFAULT;
    +
    +#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; i < count; i++) {
    + unsigned long long helper;
    +
    + helper = ((unsigned long long) wbuf[i])*timer;
    + do_div(helper, 1000000);
    + wbuf[i] = (int) helper;
    + }
    +
    + local_irq_save(flags);
    + i = 0;
    + while (i < count) {
    + level = lirc_get_timer();
    + counttimer = 0;
    + lirc_on();
    + do {
    + newlevel = lirc_get_timer();
    + if (level == 0 && newlevel != 0)
    + counttimer++;
    + level = newlevel;
    + if (check_pselecd && (in(1) & LP_PSELECD)) {
    + lirc_off();
    + local_irq_restore(flags);
    + return -EIO;
    + }
    + } while (counttimer < wbuf[i]);
    + i++;
    +
    + lirc_off();
    + if (i == count)
    + break;
    + counttimer = 0;
    + do {
    + newlevel = lirc_get_timer();
    + if (level == 0 && newlevel != 0)
    + counttimer++;
    + level = newlevel;
    + if (check_pselecd && (in(1) & LP_PSELECD)) {
    + local_irq_restore(flags);
    + return -EIO;
    + }
    + } while (counttimer < wbuf[i]);
    + i++;
    + }
    + local_irq_restore(flags);
    +#else
    + /* place code that handles write
    + * without external timer here */
    +#endif
    + return n;
    +}
    +
    +static unsigned int lirc_poll(struct file *file, poll_table *wait)
    +{
    + poll_wait(file, &lirc_wait, wait);
    + if (rptr != wptr)
    + return POLLIN | POLLRDNORM;
    + return 0;
    +}
    +
    +static int lirc_ioctl(struct inode *node, struct file *filep, unsigned int cmd,
    + unsigned long arg)
    +{
    + int result;
    + unsigned long features = LIRC_CAN_SET_TRANSMITTER_MASK |
    + LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2;
    + unsigned long mode;
    + unsigned int ivalue;
    +
    + switch (cmd) {
    + case LIRC_GET_FEATURES:
    + result = put_user(features, (unsigned long *) arg);
    + if (result)
    + return result;
    + break;
    + case LIRC_GET_SEND_MODE:
    + result = put_user(LIRC_MODE_PULSE, (unsigned long *) arg);
    + if (result)
    + return result;
    + break;
    + case LIRC_GET_REC_MODE:
    + result = put_user(LIRC_MODE_MODE2, (unsigned long *) arg);
    + if (result)
    + return result;
    + break;
    + case LIRC_SET_SEND_MODE:
    + result = get_user(mode, (unsigned long *) arg);
    + if (result)
    + return result;
    + if (mode != LIRC_MODE_PULSE)
    + return -EINVAL;
    + break;
    + case LIRC_SET_REC_MODE:
    + result = get_user(mode, (unsigned long *) arg);
    + if (result)
    + return result;
    + if (mode != LIRC_MODE_MODE2)
    + return -ENOSYS;
    + break;
    + case LIRC_SET_TRANSMITTER_MASK:
    + result = get_user(ivalue, (unsigned int *) arg);
    + if (result)
    + return result;
    + if ((ivalue & LIRC_PARALLEL_TRANSMITTER_MASK) != ivalue)
    + return LIRC_PARALLEL_MAX_TRANSMITTERS;
    + tx_mask = ivalue;
    + break;
    + default:
    + return -ENOIOCTLCMD;
    + }
    + return 0;
    +}
    +
    +static int lirc_open(struct inode *node, struct file *filep)
    +{
    + if (module_refcount(THIS_MODULE) || !lirc_claim())
    + return -EBUSY;
    +
    + parport_enable_irq(pport);
    +
    + /* init read ptr */
    + rptr = 0;
    + wptr = 0;
    + lost_irqs = 0;
    +
    + 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;
    + 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)
    +{
    + return 0;
    +}
    +
    +static void set_use_dec(void *data)
    +{
    +}
    +
    +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,
    + .dev = NULL,
    + .owner = THIS_MODULE,
    +};
    +
    +#ifdef MODULE
    +
    +static int pf(void *handle);
    +static 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);
    + }
    +}
    +
    +static int pf(void *handle)
    +{
    + parport_disable_irq(pport);
    + is_claimed = 0;
    + return 0;
    +}
    +
    +static void kf(void *handle)
    +{
    + if (!is_open)
    + return;
    + if (!lirc_claim())
    + return;
    + parport_enable_irq(pport);
    + lirc_off();
    + /* 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)
    +{
    + pport = parport_find_base(io);
    + 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);
    + parport_put_port(pport);
    + 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
    + if (debug)
    + out(LIRC_PORT_DATA, tx_mask);
    +
    + 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
    + if (debug)
    + out(LIRC_PORT_DATA, 0);
    +#endif
    +
    + is_claimed = 0;
    + parport_release(ppdevice);
    + skip_init:
    + plugin.minor = lirc_register_plugin(&plugin);
    + if (plugin.minor < 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)
    +{
    + parport_unregister_device(ppdevice);
    + lirc_unregister_plugin(plugin.minor);
    +}
    +
    +MODULE_DESCRIPTION("Infrared receiver driver for parallel ports.");
    +MODULE_AUTHOR("Christoph Bartelmus");
    +MODULE_LICENSE("GPL");
    +
    +module_param(io, int, 0444);
    +MODULE_PARM_DESC(io, "I/O address base (0x3bc, 0x378 or 0x278)");
    +
    +module_param(irq, int, 0444);
    +MODULE_PARM_DESC(irq, "Interrupt (7 or 5)");
    +
    +module_param(tx_mask, int, 0444);
    +MODULE_PARM_DESC(tx_maxk, "Transmitter mask (default: 0x01)");
    +
    +module_param(debug, bool, 0644);
    +MODULE_PARM_DESC(debug, "Enable debugging messages");
    +
    +module_param(check_pselecd, bool, 0644);
    +MODULE_PARM_DESC(debug, "Check for printer (default: 0)");
    +
    +#endif /* MODULE */
    diff --git a/drivers/input/lirc/lirc_parallel.h b/drivers/input/lirc/lirc_parallel.h
    new file mode 100644
    index 0000000..4bed6af
    --- /dev/null
    +++ b/drivers/input/lirc/lirc_parallel.h
    @@ -0,0 +1,26 @@
    +/* lirc_parallel.h */
    +
    +#ifndef _LIRC_PARALLEL_H
    +#define _LIRC_PARALLEL_H
    +
    +#include <linux/lp.h>
    +
    +#define LIRC_PORT_LEN 3
    +
    +#define LIRC_LP_BASE 0
    +#define LIRC_LP_STATUS 1
    +#define LIRC_LP_CONTROL 2
    +
    +#define LIRC_PORT_DATA LIRC_LP_BASE /* base */
    +#define LIRC_PORT_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 */
    +
    +#define LIRC_PARALLEL_MAX_TRANSMITTERS 8
    +#define LIRC_PARALLEL_TRANSMITTER_MASK ((1<<LIRC_PARALLEL_MAX_TRANSMITTERS) - 1)
    +
    +#endif
    diff --git a/drivers/input/lirc/lirc_sasem.c b/drivers/input/lirc/lirc_sasem.c
    new file mode 100644
    index 0000000..003f492
    --- /dev/null
    +++ b/drivers/input/lirc/lirc_sasem.c
    @@ -0,0 +1,969 @@
    +/* lirc_sasem.c - USB remote support for LIRC
    + * Version 0.5
    + *
    + * Copyright (C) 2004-2005 Oliver Stabel <oliver.stabel@gmx.de>
    + * Tim Davies <tim@opensystems.net.au>
    + *
    + * This driver was derived from:
    + * Venky Raju <dev@venky.ws>
    + * "lirc_imon - "LIRC plugin/VFD driver for Ahanix/Soundgraph IMON IR/VFD"
    + * Paul Miller <pmiller9@users.sourceforge.net>'s 2003-2004
    + * "lirc_atiusb - USB remote support for LIRC"
    + * Culver Consulting Services <henry@culcon.com>'s 2003
    + * "Sasem OnAir VFD/IR USB driver"
    + *
    + *
    + * 2004/06/13 - 0.1
    + * initial version
    + *
    + * 2004/06/28 - 0.2
    + * added file system support to write data to VFD device (used
    + * in conjunction with LCDProc)
    + *
    + * 2004/11/22 - 0.3
    + * Ported to 2.6 kernel
    + * - Tim Davies <tim@opensystems.net.au>
    + *
    + * 2005/03/29 - 0.4
    + * A few tidyups and keypress timings
    + * - Tim Davies <tim@opensystems.net.au>
    + *
    + * 2005/06/23 - 0.5
    + * A complete rewrite (shamelessly) based on lirc_imon.c
    + * Tim Davies <tim@opensystems.net.au>
    + *
    + * NOTE - The LCDproc iMon driver should work with this module. More info at
    + * http://www.frogstorm.info/sasem
    + */
    +
    +/*
    + * This program is free software; you can redistribute it and/or modify
    + * it under the terms of the GNU General Public License as published by
    + * the Free Software Foundation; either version 2 of the License, or
    + * (at your option) any later version.
    + *
    + * This program is distributed in the hope that it will be useful,
    + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    + * GNU General Public License for more details.
    + *
    + * You should have received a copy of the GNU General Public License
    + * along with this program; if not, write to the Free Software
    + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
    + */
    +
    +#include <linux/version.h>
    +
    +
    +#include <linux/autoconf.h>
    +
    +#include <linux/errno.h>
    +#include <linux/init.h>
    +#include <linux/kernel.h>
    +#include <linux/module.h>
    +#include <linux/slab.h>
    +#include <linux/uaccess.h>
    +#include <linux/usb.h>
    +
    +#include "lirc.h"
    +#include "lirc_dev.h"
    +
    +
    +#define MOD_AUTHOR "Oliver Stabel <oliver.stabel@gmx.de>, " \
    + "Tim Davies <tim@opensystems.net.au>"
    +#define MOD_DESC "USB Driver for Sasem Remote Controller V1.1"
    +#define MOD_NAME "lirc_sasem"
    +#define MOD_VERSION "0.5"
    +
    +#define VFD_MINOR_BASE 144 /* Same as LCD */
    +#define DEVICE_NAME "lcd%d"
    +
    +#define BUF_CHUNK_SIZE 8
    +#define BUF_SIZE 128
    +
    +#define SUCCESS 0
    +#define TRUE 1
    +#define FALSE 0
    +
    +#define IOCTL_LCD_CONTRAST 1
    +
    +/* ------------------------------------------------------------
    + * P R O T O T Y P E S
    + * ------------------------------------------------------------
    + */
    +
    +/* USB Callback prototypes */
    +static int sasem_probe(struct usb_interface *interface,
    + const struct usb_device_id *id);
    +static void sasem_disconnect(struct usb_interface *interface);
    +static void usb_rx_callback(struct urb *urb);
    +static void usb_tx_callback(struct urb *urb);
    +
    +/* VFD file_operations function prototypes */
    +static int vfd_open(struct inode *inode, struct file *file);
    +static int vfd_ioctl(struct inode *inode, struct file *file,
    + unsigned cmd, unsigned long arg);
    +static int vfd_close(struct inode *inode, struct file *file);
    +static ssize_t vfd_write(struct file *file, const char *buf,
    + size_t n_bytes, loff_t *pos);
    +
    +/* LIRC plugin function prototypes */
    +static int ir_open(void *data);
    +static void ir_close(void *data);
    +
    +/* Driver init/exit prototypes */
    +static int __init sasem_init(void);
    +static void __exit sasem_exit(void);
    +
    +/* ------------------------------------------------------------
    + * G L O B A L S
    + * ------------------------------------------------------------
    + */
    +
    +struct sasem_context {
    +
    + struct usb_device *dev;
    + int vfd_isopen; /* VFD port has been opened */
    + unsigned int vfd_contrast; /* VFD contrast */
    + int ir_isopen; /* IR port has been opened */
    + int dev_present; /* USB device presence */
    + struct mutex lock; /* to lock this object */
    + wait_queue_head_t remove_ok; /* For unexpected USB disconnects */
    +
    + struct lirc_plugin *plugin;
    + struct usb_endpoint_descriptor *rx_endpoint;
    + struct usb_endpoint_descriptor *tx_endpoint;
    + struct urb *rx_urb;
    + struct urb *tx_urb;
    + unsigned char usb_rx_buf[8];
    + unsigned char usb_tx_buf[8];
    +
    + struct tx_t {
    + unsigned char data_buf[32]; /* user data buffer */
    + struct completion finished; /* wait for write to finish */
    + atomic_t busy; /* write in progress */
    + int status; /* status of tx completion */
    + } tx;
    +
    + /* for dealing with repeat codes (wish there was a toggle bit!) */
    + struct timeval presstime;
    + char lastcode[8];
    + int codesaved;
    +};
    +
    +#define LOCK_CONTEXT mutex_lock(&context->lock)
    +#define UNLOCK_CONTEXT mutex_unlock(&context->lock)
    +
    +/* VFD file operations */
    +static struct file_operations vfd_fops = {
    +
    + .owner = THIS_MODULE,
    + .open = &vfd_open,
    + .write = &vfd_write,
    + .ioctl = &vfd_ioctl,
    + .release = &vfd_close
    +};
    +
    +/* USB Device ID for Sasem USB Control Board */
    +static struct usb_device_id sasem_usb_id_table[] = {
    + /* Sasem USB Control Board */
    + { USB_DEVICE(0x11ba, 0x0101) },
    + /* Terminiating entry */
    + {}
    +};
    +
    +/* USB Device data */
    +static struct usb_driver sasem_driver = {
    + .name = MOD_NAME,
    + .probe = sasem_probe,
    + .disconnect = sasem_disconnect,
    + .id_table = sasem_usb_id_table,
    +};
    +
    +static struct usb_class_driver sasem_class = {
    + .name = DEVICE_NAME,
    + .fops = &vfd_fops,
    + .minor_base = VFD_MINOR_BASE,
    +};
    +
    +/* to prevent races between open() and disconnect() */
    +static DECLARE_MUTEX(disconnect_sem);
    +
    +static int debug;
    +
    +
    +/* ------------------------------------------------------------
    + * M O D U L E C O D E
    + * ------------------------------------------------------------
    + */
    +
    +MODULE_AUTHOR(MOD_AUTHOR);
    +MODULE_DESCRIPTION(MOD_DESC);
    +MODULE_LICENSE("GPL");
    +module_param(debug, int, 0);
    +MODULE_PARM_DESC(debug, "Debug messages: 0=no, 1=yes (default: no)");
    +
    +static inline void delete_context(struct sasem_context *context)
    +{
    + usb_free_urb(context->tx_urb); /* VFD */
    + usb_free_urb(context->rx_urb); /* IR */
    + lirc_buffer_free(context->plugin->rbuf);
    + kfree(context->plugin->rbuf);
    + kfree(context->plugin);
    + kfree(context);
    +
    + if (debug)
    + info("%s: context deleted", __func__);
    +}
    +
    +static inline void deregister_from_lirc(struct sasem_context *context)
    +{
    + int retval;
    + int minor = context->plugin->minor;
    +
    + retval = lirc_unregister_plugin(minor);
    + if (retval)
    + err("%s: unable to deregister from lirc (%d)",
    + __func__, retval);
    + else
    + info("Deregistered Sasem plugin (minor:%d)", minor);
    +
    +}
    +
    +/**
    + * Called when the VFD device (e.g. /dev/usb/lcd)
    + * is opened by the application.
    + */
    +static int vfd_open(struct inode *inode, struct file *file)
    +{
    + struct usb_interface *interface;
    + struct sasem_context *context = NULL;
    + int subminor;
    + int retval = SUCCESS;
    +
    + /* prevent races with disconnect */
    + down(&disconnect_sem);
    +
    + subminor = iminor(inode);
    + interface = usb_find_interface(&sasem_driver, subminor);
    + if (!interface) {
    + err("%s: could not find interface for minor %d",
    + __func__, subminor);
    + retval = -ENODEV;
    + goto exit;
    + }
    + context = usb_get_intfdata(interface);
    +
    + if (!context) {
    + err("%s: no context found for minor %d",
    + __func__, subminor);
    + retval = -ENODEV;
    + goto exit;
    + }
    +
    + LOCK_CONTEXT;
    +
    + if (context->vfd_isopen) {
    + err("%s: VFD port is already open", __func__);
    + retval = -EBUSY;
    + } else {
    + context->vfd_isopen = TRUE;
    + file->private_data = context;
    + info("VFD port opened");
    + }
    +
    + UNLOCK_CONTEXT;
    +
    +exit:
    + up(&disconnect_sem);
    + return retval;
    +}
    +
    +/**
    + * Called when the VFD device (e.g. /dev/usb/lcd)
    + * is closed by the application.
    + */
    +static int vfd_ioctl(struct inode *inode, struct file *file,
    + unsigned cmd, unsigned long arg)
    +{
    + struct sasem_context *context = NULL;
    +
    + context = (struct sasem_context *) file->private_data;
    +
    + if (!context) {
    + err("%s: no context for device", __func__);
    + return -ENODEV;
    + }
    +
    + LOCK_CONTEXT;
    +
    + switch (cmd) {
    + case IOCTL_LCD_CONTRAST:
    + if (arg > 1000)
    + arg = 1000;
    + if (arg < 0)
    + arg = 0;
    + context->vfd_contrast = (unsigned int)arg;
    + break;
    + default:
    + info("Unknown IOCTL command");
    + UNLOCK_CONTEXT;
    + return -ENOIOCTLCMD; /* not supported */
    + }
    +
    + UNLOCK_CONTEXT;
    + return 0;
    +}
    +
    +/**
    + * Called when the VFD device (e.g. /dev/usb/lcd)
    + * is closed by the application.
    + */
    +static int vfd_close(struct inode *inode, struct file *file)
    +{
    + struct sasem_context *context = NULL;
    + int retval = SUCCESS;
    +
    + context = (struct sasem_context *) file->private_data;
    +
    + if (!context) {
    + err("%s: no context for device", __func__);
    + return -ENODEV;
    + }
    +
    + LOCK_CONTEXT;
    +
    + if (!context->vfd_isopen) {
    + err("%s: VFD is not open", __func__);
    + retval = -EIO;
    + } else {
    + context->vfd_isopen = FALSE;
    + info("VFD port closed");
    + if (!context->dev_present && !context->ir_isopen) {
    +
    + /* Device disconnected before close and IR port is
    + * not open. If IR port is open, context will be
    + * deleted by ir_close. */
    + UNLOCK_CONTEXT;
    + delete_context(context);
    + return retval;
    + }
    + }
    +
    + UNLOCK_CONTEXT;
    + return retval;
    +}
    +
    +/**
    + * Sends a packet to the VFD.
    + */
    +static inline int send_packet(struct sasem_context *context)
    +{
    + unsigned int pipe;
    + int interval = 0;
    + int retval = SUCCESS;
    +
    + pipe = usb_sndintpipe(context->dev,
    + context->tx_endpoint->bEndpointAddress);
    + interval = context->tx_endpoint->bInterval;
    +
    + usb_fill_int_urb(context->tx_urb, context->dev, pipe,
    + context->usb_tx_buf, sizeof(context->usb_tx_buf),
    + usb_tx_callback, context, interval);
    +
    + context->tx_urb->actual_length = 0;
    +
    + init_completion(&context->tx.finished);
    + atomic_set(&(context->tx.busy), 1);
    +
    + retval = usb_submit_urb(context->tx_urb, GFP_KERNEL);
    + if (retval != SUCCESS) {
    + atomic_set(&(context->tx.busy), 0);
    + err("%s: error submitting urb (%d)", __func__, retval);
    + } else {
    + /* Wait for tranmission to complete (or abort) */
    + UNLOCK_CONTEXT;
    + wait_for_completion(&context->tx.finished);
    + LOCK_CONTEXT;
    +
    + retval = context->tx.status;
    + if (retval != SUCCESS)
    + err("%s: packet tx failed (%d)", __func__, retval);
    + }
    +
    + return retval;
    +}
    +
    +/**
    + * Writes data to the VFD. The Sasem VFD is 2x16 characters
    + * and requires data in 9 consecutive USB interrupt packets,
    + * each packet carrying 8 bytes.
    + */
    +static ssize_t vfd_write(struct file *file, const char *buf,
    + size_t n_bytes, loff_t *pos)
    +{
    + int i;
    + int retval = SUCCESS;
    + struct sasem_context *context;
    +
    + context = (struct sasem_context *) file->private_data;
    + if (!context) {
    + err("%s: no context for device", __func__);
    + return -ENODEV;
    + }
    +
    + LOCK_CONTEXT;
    +
    + if (!context->dev_present) {
    + err("%s: no Sasem device present", __func__);
    + retval = -ENODEV;
    + goto exit;
    + }
    +
    + if (n_bytes <= 0 || n_bytes > 32) {
    + err("%s: invalid payload size", __func__);
    + retval = -EINVAL;
    + goto exit;
    + }
    +
    + retval = copy_from_user(context->tx.data_buf, buf, n_bytes);
    + if (retval < 0)
    + goto exit;
    +
    + /* Pad with spaces */
    + for (i = n_bytes; i < 32; ++i)
    + context->tx.data_buf[i] = ' ';
    +
    + /* Nine 8 byte packets to be sent */
    + /* NOTE: "\x07\x01\0\0\0\0\0\0" or "\x0c\0\0\0\0\0\0\0"
    + * will clear the VFD */
    + for (i = 0; i < 9; i++) {
    + switch (i) {
    + case 0:
    + memcpy(context->usb_tx_buf, "\x07\0\0\0\0\0\0\0", 8);
    + context->usb_tx_buf[1] = (context->vfd_contrast) ?
    + (0x2B - (context->vfd_contrast - 1) / 250)
    + : 0x2B;
    + break;
    + case 1:
    + memcpy(context->usb_tx_buf, "\x09\x01\0\0\0\0\0\0", 8);
    + break;
    + case 2:
    + memcpy(context->usb_tx_buf, "\x0b\x01\0\0\0\0\0\0", 8);
    + break;
    + case 3:
    + memcpy(context->usb_tx_buf, context->tx.data_buf, 8);
    + break;
    + case 4:
    + memcpy(context->usb_tx_buf,
    + context->tx.data_buf + 8, 8);
    + break;
    + case 5:
    + memcpy(context->usb_tx_buf, "\x09\x01\0\0\0\0\0\0", 8);
    + break;
    + case 6:
    + memcpy(context->usb_tx_buf, "\x0b\x02\0\0\0\0\0\0", 8);
    + break;
    + case 7:
    + memcpy(context->usb_tx_buf,
    + context->tx.data_buf + 16, 8);
    + break;
    + case 8:
    + memcpy(context->usb_tx_buf,
    + context->tx.data_buf + 24, 8);
    + break;
    + }
    + retval = send_packet(context);
    + if (retval != SUCCESS) {
    +
    + err("%s: send packet failed for packet #%d",
    + __func__, i);
    + goto exit;
    + }
    + }
    +exit:
    +
    + UNLOCK_CONTEXT;
    +
    + return (retval == SUCCESS) ? n_bytes : retval;
    +}
    +
    +/**
    + * Callback function for USB core API: transmit data
    + */
    +static void usb_tx_callback(struct urb *urb)
    +{
    + struct sasem_context *context;
    +
    + if (!urb)
    + return;
    + context = (struct sasem_context *) urb->context;
    + if (!context)
    + return;
    +
    + context->tx.status = urb->status;
    +
    + /* notify waiters that write has finished */
    + atomic_set(&context->tx.busy, 0);
    + complete(&context->tx.finished);
    +
    + return;
    +}
    +
    +/**
    + * Called by lirc_dev when the application opens /dev/lirc
    + */
    +static int ir_open(void *data)
    +{
    + int retval = SUCCESS;
    + struct sasem_context *context;
    +
    + /* prevent races with disconnect */
    + down(&disconnect_sem);
    +
    + context = (struct sasem_context *) data;
    +
    + LOCK_CONTEXT;
    +
    + if (context->ir_isopen) {
    + err("%s: IR port is already open", __func__);
    + retval = -EBUSY;
    + goto exit;
    + }
    +
    + usb_fill_int_urb(context->rx_urb, context->dev,
    + usb_rcvintpipe(context->dev,
    + context->rx_endpoint->bEndpointAddress),
    + context->usb_rx_buf, sizeof(context->usb_rx_buf),
    + usb_rx_callback, context, context->rx_endpoint->bInterval);
    +
    + retval = usb_submit_urb(context->rx_urb, GFP_KERNEL);
    +
    + if (retval)
    + err("%s: usb_submit_urb failed for ir_open (%d)",
    + __func__, retval);
    + else {
    + context->ir_isopen = TRUE;
    + info("IR port opened");
    + }
    +
    +exit:
    + UNLOCK_CONTEXT;
    +
    + up(&disconnect_sem);
    + return SUCCESS;
    +}
    +
    +/**
    + * Called by lirc_dev when the application closes /dev/lirc
    + */
    +static void ir_close(void *data)
    +{
    + struct sasem_context *context;
    +
    + context = (struct sasem_context *)data;
    + if (!context) {
    + err("%s: no context for device", __func__);
    + return;
    + }
    +
    + LOCK_CONTEXT;
    +
    + usb_kill_urb(context->rx_urb);
    + context->ir_isopen = FALSE;
    + info("IR port closed");
    +
    + if (!context->dev_present) {
    +
    + /*
    + * Device disconnected while IR port was
    + * still open. Plugin was not deregistered
    + * at disconnect time, so do it now.
    + */
    + deregister_from_lirc(context);
    +
    + if (!context->vfd_isopen) {
    +
    + UNLOCK_CONTEXT;
    + delete_context(context);
    + return;
    + }
    + /* If VFD port is open, context will be deleted by vfd_close */
    + }
    +
    + UNLOCK_CONTEXT;
    + return;
    +}
    +
    +/**
    + * Process the incoming packet
    + */
    +static inline void incoming_packet(struct sasem_context *context,
    + struct urb *urb)
    +{
    + int len = urb->actual_length;
    + unsigned char *buf = urb->transfer_buffer;
    + long ms;
    + struct timeval tv;
    +
    + if (len != 8) {
    + warn("%s: invalid incoming packet size (%d)",
    + __func__, len);
    + return;
    + }
    +
    +#ifdef DEBUG
    + int i;
    + for (i = 0; i < 8; ++i)
    + printk(KERN_INFO "%02x ", buf[i]);
    + printk(KERN_INFO "\n");
    +#endif
    +
    + /* Lirc could deal with the repeat code, but we really need to block it
    + * if it arrives too late. Otherwise we could repeat the wrong code. */
    +
    + /* get the time since the last button press */
    + do_gettimeofday(&tv);
    + ms = (tv.tv_sec - context->presstime.tv_sec) * 1000 +
    + (tv.tv_usec - context->presstime.tv_usec) / 1000;
    +
    + if (memcmp(buf, "\x08\0\0\0\0\0\0\0", 8) == 0) {
    + /* the repeat code is being sent, so we copy
    + * the old code to LIRC */
    +
    + /* NOTE: Only if the last code was less than 250ms ago
    + * - no one should be able to push another (undetected) button
    + * in that time and then get a false repeat of the previous
    + * press but it is long enough for a genuine repeat */
    + if ((ms < 250) && (context->codesaved != 0)) {
    + memcpy(buf, &context->lastcode, 8);
    + context->presstime.tv_sec = tv.tv_sec;
    + context->presstime.tv_usec = tv.tv_usec;
    + }
    + } else {
    + /* save the current valid code for repeats */
    + memcpy(&context->lastcode, buf, 8);
    + /* set flag to signal a valid code was save;
    + * just for safety reasons */
    + context->codesaved = 1;
    + context->presstime.tv_sec = tv.tv_sec;
    + context->presstime.tv_usec = tv.tv_usec;
    + }
    +
    + lirc_buffer_write_1(context->plugin->rbuf, buf);
    + wake_up(&context->plugin->rbuf->wait_poll);
    +}
    +
    +/**
    + * Callback function for USB core API: receive data
    + */
    +static void usb_rx_callback(struct urb *urb)
    +{
    + struct sasem_context *context;
    +
    + if (!urb)
    + return;
    + context = (struct sasem_context *) urb->context;
    + if (!context)
    + return;
    +
    + switch (urb->status) {
    +
    + case -ENOENT: /* usbcore unlink successful! */
    + return;
    +
    + case SUCCESS:
    + if (context->ir_isopen)
    + incoming_packet(context, urb);
    + break;
    +
    + default:
    + warn("%s: status (%d): ignored",
    + __func__, urb->status);
    + break;
    + }
    +
    + usb_submit_urb(context->rx_urb, GFP_ATOMIC);
    + return;
    +}
    +
    +
    +
    +/**
    + * Callback function for USB core API: Probe
    + */
    +static int sasem_probe(struct usb_interface *interface,
    + const struct usb_device_id *id)
    +{
    + struct usb_device *dev = NULL;
    + struct usb_host_interface *iface_desc = NULL;
    + struct usb_endpoint_descriptor *rx_endpoint = NULL;
    + struct usb_endpoint_descriptor *tx_endpoint = NULL;
    + struct urb *rx_urb = NULL;
    + struct urb *tx_urb = NULL;
    + struct lirc_plugin *plugin = NULL;
    + struct lirc_buffer *rbuf = NULL;
    + int lirc_minor = 0;
    + int num_endpoints;
    + int retval = SUCCESS;
    + int vfd_ep_found;
    + int ir_ep_found;
    + int alloc_status;
    + struct sasem_context *context = NULL;
    + int i;
    +
    + info("%s: found Sasem device", __func__);
    +
    +
    + dev = usb_get_dev(interface_to_usbdev(interface));
    + iface_desc = interface->cur_altsetting;
    + num_endpoints = iface_desc->desc.bNumEndpoints;
    +
    + /*
    + * Scan the endpoint list and set:
    + * first input endpoint = IR endpoint
    + * first output endpoint = VFD endpoint
    + */
    +
    + ir_ep_found = FALSE;
    + vfd_ep_found = FALSE;
    +
    + for (i = 0; i < num_endpoints && !(ir_ep_found && vfd_ep_found); ++i) {
    +
    + struct usb_endpoint_descriptor *ep;
    + int ep_dir;
    + int ep_type;
    + ep = &iface_desc->endpoint [i].desc;
    + ep_dir = ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK;
    + ep_type = ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
    +
    + if (!ir_ep_found &&
    + ep_dir == USB_DIR_IN &&
    + ep_type == USB_ENDPOINT_XFER_INT) {
    +
    + rx_endpoint = ep;
    + ir_ep_found = TRUE;
    + if (debug)
    + info("%s: found IR endpoint", __func__);
    +
    + } else if (!vfd_ep_found &&
    + ep_dir == USB_DIR_OUT &&
    + ep_type == USB_ENDPOINT_XFER_INT) {
    +
    + tx_endpoint = ep;
    + vfd_ep_found = TRUE;
    + if (debug)
    + info("%s: found VFD endpoint", __func__);
    + }
    + }
    +
    + /* Input endpoint is mandatory */
    + if (!ir_ep_found) {
    +
    + err("%s: no valid input (IR) endpoint found.", __func__);
    + retval = -ENODEV;
    + goto exit;
    + }
    +
    + /* Warning if no VFD endpoint */
    + if (!vfd_ep_found)
    + info("%s: no valid output (VFD) endpoint found.", __func__);
    +
    +
    + /* Allocate memory */
    + alloc_status = SUCCESS;
    +
    + context = kmalloc(sizeof(struct sasem_context), GFP_KERNEL);
    + if (!context) {
    + err("%s: kmalloc failed for context", __func__);
    + alloc_status = 1;
    + goto alloc_status_switch;
    + }
    + plugin = kmalloc(sizeof(struct lirc_plugin), GFP_KERNEL);
    + if (!plugin) {
    + err("%s: kmalloc failed for lirc_plugin", __func__);
    + alloc_status = 2;
    + goto alloc_status_switch;
    + }
    + rbuf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
    + if (!rbuf) {
    + err("%s: kmalloc failed for lirc_buffer", __func__);
    + alloc_status = 3;
    + goto alloc_status_switch;
    + }
    + if (lirc_buffer_init(rbuf, BUF_CHUNK_SIZE, BUF_SIZE)) {
    + err("%s: lirc_buffer_init failed", __func__);
    + alloc_status = 4;
    + goto alloc_status_switch;
    + }
    + rx_urb = usb_alloc_urb(0, GFP_KERNEL);
    + if (!rx_urb) {
    + err("%s: usb_alloc_urb failed for IR urb", __func__);
    + alloc_status = 5;
    + goto alloc_status_switch;
    + }
    + if (vfd_ep_found) {
    + tx_urb = usb_alloc_urb(0, GFP_KERNEL);
    + if (!tx_urb) {
    + err("%s: usb_alloc_urb failed for VFD urb",
    + __func__);
    + alloc_status = 6;
    + goto alloc_status_switch;
    + }
    + }
    +
    + /* clear all members of sasem_context and lirc_plugin */
    + memset(context, 0, sizeof(struct sasem_context));
    + mutex_init(&context->lock);
    +
    + memset(plugin, 0, sizeof(struct lirc_plugin));
    +
    + strcpy(plugin->name, MOD_NAME);
    + plugin->minor = -1;
    + plugin->code_length = 64;
    + plugin->sample_rate = 0;
    + plugin->features = LIRC_CAN_REC_LIRCCODE;
    + plugin->data = context;
    + plugin->rbuf = rbuf;
    + plugin->set_use_inc = ir_open;
    + plugin->set_use_dec = ir_close;
    + plugin->dev = &dev->dev;
    + plugin->owner = THIS_MODULE;
    +
    + LOCK_CONTEXT;
    +
    + lirc_minor = lirc_register_plugin(plugin);
    + if (lirc_minor < 0) {
    + err("%s: lirc_register_plugin failed", __func__);
    + alloc_status = 7;
    + UNLOCK_CONTEXT;
    + } else
    + info("%s: Registered Sasem plugin (minor:%d)",
    + __func__, lirc_minor);
    +
    +alloc_status_switch:
    +
    + switch (alloc_status) {
    +
    + case 7:
    + if (vfd_ep_found)
    + usb_free_urb(tx_urb);
    + case 6:
    + usb_free_urb(rx_urb);
    + case 5:
    + lirc_buffer_free(rbuf);
    + case 4:
    + kfree(rbuf);
    + case 3:
    + kfree(plugin);
    + case 2:
    + kfree(context);
    + context = NULL;
    + case 1:
    + retval = -ENOMEM;
    + goto exit;
    + }
    +
    + /* Needed while unregistering! */
    + plugin->minor = lirc_minor;
    +
    + context->dev = dev;
    + context->dev_present = TRUE;
    + context->rx_endpoint = rx_endpoint;
    + context->rx_urb = rx_urb;
    + if (vfd_ep_found) {
    + context->tx_endpoint = tx_endpoint;
    + context->tx_urb = tx_urb;
    + context->vfd_contrast = 1000; /* range 0 - 1000 */
    + }
    + context->plugin = plugin;
    +
    + usb_set_intfdata(interface, context);
    +
    + if (vfd_ep_found) {
    +
    + if (debug)
    + info("Registering VFD with sysfs");
    + if (usb_register_dev(interface, &sasem_class))
    + /* Not a fatal error, so ignore */
    + info("%s: could not get a minor number for VFD",
    + __func__);
    + }
    +
    + info("%s: Sasem device on usb<%d:%d> initialized",
    + __func__, dev->bus->busnum, dev->devnum);
    +
    + UNLOCK_CONTEXT;
    +exit:
    + return retval;
    +}
    +
    +/**
    + * Callback function for USB core API: disonnect
    + */
    +static void sasem_disconnect(struct usb_interface *interface)
    +{
    + struct sasem_context *context;
    +
    + /* prevent races with ir_open()/vfd_open() */
    + down(&disconnect_sem);
    +
    + context = usb_get_intfdata(interface);
    + LOCK_CONTEXT;
    +
    + info("%s: Sasem device disconnected", __func__);
    +
    + usb_set_intfdata(interface, NULL);
    + context->dev_present = FALSE;
    +
    + /* Stop reception */
    + usb_kill_urb(context->rx_urb);
    +
    + /* Abort ongoing write */
    + if (atomic_read(&context->tx.busy)) {
    +
    + usb_kill_urb(context->tx_urb);
    + wait_for_completion(&context->tx.finished);
    + }
    +
    + /* De-register from lirc_dev if IR port is not open */
    + if (!context->ir_isopen)
    + deregister_from_lirc(context);
    +
    + usb_deregister_dev(interface, &sasem_class);
    +
    + UNLOCK_CONTEXT;
    +
    + if (!context->ir_isopen && !context->vfd_isopen)
    + delete_context(context);
    +
    + up(&disconnect_sem);
    +}
    +
    +#ifdef MODULE
    +static int __init sasem_init(void)
    +{
    + int rc;
    +
    + info(MOD_DESC ", v" MOD_VERSION);
    + info(MOD_AUTHOR);
    +
    + rc = usb_register(&sasem_driver);
    + if (rc < 0) {
    + err("%s: usb register failed (%d)", __func__, rc);
    + return -ENODEV;
    + }
    + return SUCCESS;
    +}
    +
    +static void __exit sasem_exit(void)
    +{
    + usb_deregister(&sasem_driver);
    + info("module removed. Goodbye!");
    +}
    +
    +
    +module_init(sasem_init);
    +module_exit(sasem_exit);
    +
    +#endif /* MODULE */
    diff --git a/drivers/input/lirc/lirc_serial.c b/drivers/input/lirc/lirc_serial.c
    new file mode 100644
    index 0000000..465edd9
    --- /dev/null
    +++ b/drivers/input/lirc/lirc_serial.c
    @@ -0,0 +1,1312 @@
    +/****************************************************************************
    + ** lirc_serial.c ***********************************************************
    + ****************************************************************************
    + *
    + * lirc_serial - Device driver that records pulse- and pause-lengths
    + * (space-lengths) between DDCD event on a serial port.
    + *
    + * Copyright (C) 1996,97 Ralph Metzler <rjkm@thp.uni-koeln.de>
    + * Copyright (C) 1998 Trent Piepho <xyzzy@u.washington.edu>
    + * Copyright (C) 1998 Ben Pfaff <blp@gnu.org>
    + * Copyright (C) 1999 Christoph Bartelmus <lirc@bartelmus.de>
    + * Copyright (C) 2007 Andrei Tanas <andrei@tanas.ca> (suspend/resume support)
    + * 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 <steve@daviesfam.org> July 2001
    +*/
    +
    +#include <linux/version.h>
    +
    +#include <linux/autoconf.h>
    +
    +#include <linux/module.h>
    +#include <linux/errno.h>
    +#include <linux/signal.h>
    +#include <linux/sched.h>
    +#include <linux/fs.h>
    +#include <linux/interrupt.h>
    +#include <linux/ioport.h>
    +#include <linux/kernel.h>
    +#include <linux/major.h>
    +#include <linux/serial_reg.h>
    +#include <linux/time.h>
    +#include <linux/string.h>
    +#include <linux/types.h>
    +#include <linux/wait.h>
    +#include <linux/mm.h>
    +#include <linux/delay.h>
    +#include <linux/poll.h>
    +#include <linux/platform_device.h>
    +
    +#include <asm/system.h>
    +#include <linux/uaccess.h>
    +#include <linux/io.h>
    +#include <linux/irq.h>
    +#include <linux/fcntl.h>
    +
    +#if defined(LIRC_SERIAL_NSLU2)
    +#include <asm/hardware.h>
    +/* From Intel IXP42X Developer's Manual (#252480-005): */
    +/* ftp://download.intel.com/design/network/manuals/25248005.pdf */
    +#define UART_IE_IXP42X_UUE 0x40 /* IXP42X UART Unit enable */
    +#define UART_IE_IXP42X_RTOIE 0x10 /* IXP42X Receiver Data Timeout int.enable */
    +#ifndef NSLU2_LED_GRN_GPIO
    +/* added in 2.6.22 */
    +#define NSLU2_LED_GRN_GPIO NSLU2_LED_GRN
    +#endif
    +#endif
    +
    +#include "lirc.h"
    +#include "lirc_dev.h"
    +
    +#define LIRC_DRIVER_NAME "lirc_serial"
    +
    +struct lirc_serial {
    + 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
    +#define LIRC_NSLU2 5
    +
    +#ifdef LIRC_SERIAL_IRDEO
    +static int type = LIRC_IRDEO;
    +#elif defined(LIRC_SERIAL_IRDEO_REMOTE)
    +static int type = LIRC_IRDEO_REMOTE;
    +#elif defined(LIRC_SERIAL_ANIMAX)
    +static int type = LIRC_ANIMAX;
    +#elif defined(LIRC_SERIAL_IGOR)
    +static int type = LIRC_IGOR;
    +#elif defined(LIRC_SERIAL_NSLU2)
    +static int type = LIRC_NSLU2;
    +#else
    +static int type = LIRC_HOMEBREW;
    +#endif
    +
    +/* Set defaults for NSLU2 */
    +#if defined(LIRC_SERIAL_NSLU2)
    +#ifndef LIRC_IRQ
    +#define LIRC_IRQ IRQ_IXP4XX_UART2
    +#endif
    +#ifndef LIRC_PORT
    +#define LIRC_PORT (IXP4XX_UART2_BASE_VIRT + REG_OFFSET)
    +#endif
    +#ifndef LIRC_IOMMAP
    +#define LIRC_IOMMAP IXP4XX_UART2_BASE_PHYS
    +#endif
    +#ifndef LIRC_IOSHIFT
    +#define LIRC_IOSHIFT 2
    +#endif
    +#ifndef LIRC_ALLOW_MMAPPED_IO
    +#define LIRC_ALLOW_MMAPPED_IO
    +#endif
    +#endif
    +
    +#if defined(LIRC_ALLOW_MMAPPED_IO)
    +#ifndef LIRC_IOMMAP
    +#define LIRC_IOMMAP 0
    +#endif
    +#ifndef LIRC_IOSHIFT
    +#define LIRC_IOSHIFT 0
    +#endif
    +static int iommap = LIRC_IOMMAP;
    +static int ioshift = LIRC_IOSHIFT;
    +#endif
    +
    +#ifdef LIRC_SERIAL_SOFTCARRIER
    +static int softcarrier = 1;
    +#else
    +static int softcarrier;
    +#endif
    +
    +static int share_irq;
    +static int debug;
    +
    +#define dprintk(fmt, args...) \
    + do { \
    + if (debug) \
    + printk(KERN_DEBUG LIRC_DRIVER_NAME ": " \
    + fmt, ## args); \
    + } while (0)
    +
    +/* forward declarations */
    +static long send_pulse_irdeo(unsigned long length);
    +static long send_pulse_homebrew(unsigned long length);
    +static void send_space_irdeo(long length);
    +static void send_space_homebrew(long length);
    +
    +static struct lirc_serial hardware[] = {
    + /* home-brew receiver/transmitter */
    + {
    + 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 */
    + {
    + 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 */
    + {
    + 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 */
    + {
    + 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) */
    + {
    + 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)
    + },
    +
    +#if defined(LIRC_SERIAL_NSLU2)
    + /* Modified Linksys Network Storage Link USB 2.0 (NSLU2):
    + We receive on CTS of the 2nd serial port (R142,LHS), we
    + transmit with a IR diode between GPIO[1] (green status LED),
    + and ground (Matthias Goebl <matthias.goebl@goebl.net>).
    + See also http://www.nslu2-linux.org for this device */
    + {
    + UART_MSR_CTS,
    + UART_MSR_DCTS,
    + 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)
    + },
    +#endif
    +
    +};
    +
    +#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 int txsense; /* 0 = active high, 1 = active low */
    +
    +#ifndef LIRC_IRQ
    +#define LIRC_IRQ 4
    +#endif
    +#ifndef LIRC_PORT
    +#define LIRC_PORT 0x3f8
    +#endif
    +
    +static int io = LIRC_PORT;
    +static int irq = LIRC_IRQ;
    +
    +static struct timeval lasttv = {0, 0};
    +
    +static struct lirc_buffer rbuf;
    +
    +static int wbuf[WBUF_LEN];
    +
    +static unsigned int freq = 38000;
    +static unsigned int duty_cycle = 50;
    +
    +/* Initialized in init_timing_params() */
    +static unsigned long period;
    +static unsigned long pulse_width;
    +static unsigned long space_width;
    +
    +#if defined(__i386__)
    +/*
    + From:
    + Linux I/O port programming mini-HOWTO
    + Author: Riku Saikkonen <Riku.Saikkonen@hut.fi>
    + v, 28 December 1997
    +
    + [...]
    + Actually, a port I/O instruction on most ports in the 0-0x3ff range
    + takes almost exactly 1 microsecond, so if you're, for example, using
    + the parallel port directly, just do additional inb()s from that port
    + to delay.
    + [...]
    +*/
    +/* transmitter latency 1.5625us 0x1.90 - this figure arrived at from
    + * comment above plus trimming to match actual measured frequency.
    + * This will be sensitive to cpu speed, though hopefully most of the 1.5us
    + * is spent in the uart access. Still - for reference test machine was a
    + * 1.13GHz Athlon system - Steve
    + */
    +
    +/* changed from 400 to 450 as this works better on slower machines;
    + faster machines will use the rdtsc code anyway */
    +
    +#define 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)
    +{
    +#if defined(LIRC_ALLOW_MMAPPED_IO)
    + if (iommap != 0) {
    + /* the register is memory-mapped */
    + offset <<= ioshift;
    + return readb(io + offset);
    + }
    +#endif
    + return inb(io + offset);
    +}
    +
    +static inline void soutp(int offset, int value)
    +{
    +#if defined(LIRC_ALLOW_MMAPPED_IO)
    + if (iommap != 0) {
    + /* the register is memory-mapped */
    + offset <<= ioshift;
    + writeb(value, io + offset);
    + }
    +#endif
    + outb(value, io + offset);
    +}
    +
    +static inline void on(void)
    +{
    +#if defined(LIRC_SERIAL_NSLU2)
    + /* On NSLU2, we put the transmit diode between the output of the green
    + status LED and ground */
    + if (type == LIRC_NSLU2) {
    + gpio_line_set(NSLU2_LED_GRN_GPIO, IXP4XX_GPIO_LOW);
    + return;
    + }
    +#endif
    + if (txsense)
    + soutp(UART_MCR, hardware[type].off);
    + else
    + soutp(UART_MCR, hardware[type].on);
    +}
    +
    +static inline void off(void)
    +{
    +#if defined(LIRC_SERIAL_NSLU2)
    + if (type == LIRC_NSLU2) {
    + gpio_line_set(NSLU2_LED_GRN_GPIO, IXP4XX_GPIO_HIGH);
    + return;
    + }
    +#endif
    + if (txsense)
    + soutp(UART_MCR, hardware[type].on);
    + else
    + 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 */
    +static unsigned long conv_us_to_clocks;
    +
    +static inline int init_timing_params(unsigned int new_duty_cycle,
    + unsigned int new_freq)
    +{
    + unsigned long long loops_per_sec, work;
    +
    + duty_cycle = new_duty_cycle;
    + freq = new_freq;
    +
    + loops_per_sec = current_cpu_data.loops_per_jiffy;
    + loops_per_sec *= HZ;
    +
    + /* How many clocks in a microsecond?, avoiding long long divide */
    + work = loops_per_sec;
    + work *= 4295; /* 4295 = 2^32 / 1e6 */
    + conv_us_to_clocks = (work>>32);
    +
    + /* Carrier period in clocks, approach good up to 32GHz clock,
    + gets carrier frequency within 8Hz */
    + period = loops_per_sec>>3;
    + period /= (freq>>3);
    +
    + /* Derive pulse and space from the period */
    +
    + pulse_width = period*duty_cycle/100;
    + space_width = period - pulse_width;
    + dprintk("in init_timing_params, freq=%d, duty_cycle=%d, "
    + "clk/jiffy=%ld, pulse=%ld, space=%ld, "
    + "conv_us_to_clocks=%ld\n",
    + freq, duty_cycle, current_cpu_data.loops_per_jiffy,
    + pulse_width, space_width, conv_us_to_clocks);
    + return 0;
    +}
    +#else /* ! USE_RDTSC */
    +static inline int init_timing_params(unsigned int new_duty_cycle,
    + unsigned int new_freq)
    +{
    +/* period, pulse/space width are kept with 8 binary places -
    + * IE multiplied by 256. */
    + if (256*1000000L/new_freq*new_duty_cycle/100 <=
    + 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;
    + dprintk("in init_timing_params, freq=%d pulse=%ld, "
    + "space=%ld\n", freq, pulse_width, space_width);
    + return 0;
    +}
    +#endif /* USE_RDTSC */
    +
    +
    +/* return value: space length delta */
    +
    +static 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) < length) {
    + /* Delay till flip time */
    + do {
    + rdtscl(now);
    + } while ((now-start) < target);
    +
    + /* flip */
    + if (flag) {
    + rdtscl(now); off();
    + target += space_width;
    + } else {
    + rdtscl(now); on();
    + target += pulse_width;
    + }
    + flag = !flag;
    + }
    + rdtscl(now);
    + return ((now-start)-length) / conv_us_to_clocks;
    +}
    +#else /* ! USE_RDTSC */
    +/* Version using udelay() */
    +
    +/* here we use fixed point arithmetic, with 8
    + fractional bits. that gets us within 0.1% or so of the right average
    + frequency, albeit with some jitter in pulse length - Steve */
    +
    +/* To match 8 fractional bits used for pulse/space length */
    +
    +static inline long send_pulse_homebrew_softcarrier(unsigned long length)
    +{
    + int flag;
    + unsigned long actual, target, d;
    + length <<= 8;
    +
    + actual = 0; target = 0; flag = 0;
    + while (actual < length) {
    + if (flag) {
    + off();
    + target += space_width;
    + } else {
    + on();
    + target += pulse_width;
    + }
    + d = (target-actual-LIRC_SERIAL_TRANSMITTER_LATENCY+128)>>8;
    + /* Note - we've checked in ioctl that the pulse/space
    + widths are big enough so that d is > 0 */
    + udelay(d);
    + actual += (d<<8)+LIRC_SERIAL_TRANSMITTER_LATENCY;
    + flag = !flag;
    + }
    + return (actual-length)>>8;
    +}
    +#endif /* USE_RDTSC */
    +
    +static 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;
    + }
    +}
    +
    +static void send_space_irdeo(long length)
    +{
    + if (length <= 0)
    + return;
    +
    + safe_udelay(length);
    +}
    +
    +static void send_space_homebrew(long length)
    +{
    + off();
    + if (length <= 0)
    + return;
    + safe_udelay(length);
    +}
    +
    +static inline void rbwrite(int l)
    +{
    + if (lirc_buffer_full(&rbuf)) {
    + /* no new signals will be accepted */
    + dprintk("Buffer overrun\n");
    + return;
    + }
    + _lirc_buffer_write_1(&rbuf, (void *)&l);
    +}
    +
    +static inline void frbwrite(int l)
    +{
    + /* simple noise filter */
    + static int pulse, space;
    + static unsigned int ptr;
    +
    + 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);
    +}
    +
    +static irqreturn_t irq_handler(int i, void *blah)
    +{
    + struct timeval tv;
    + int status, counter, dcd;
    + long deltv;
    + int data;
    + static int last_dcd = -1;
    +
    + if ((sinp(UART_IIR) & UART_IIR_NO_INT)) {
    + /* not our interrupt */
    + return IRQ_RETVAL(IRQ_NONE);
    + }
    +
    + counter = 0;
    + do {
    + counter++;
    + status = sinp(UART_MSR);
    + if (counter > RS_ISR_PASS_LIMIT) {
    + printk(KERN_WARNING LIRC_DRIVER_NAME ": AIEEEE: "
    + "We're caught!\n");
    + break;
    + }
    + if ((status&hardware[type].signal_pin_change) && sense != -1) {
    + /* get current time */
    + do_gettimeofday(&tv);
    +
    + /* New mode, written by Trent Piepho
    + <xyzzy@u.washington.edu>. */
    +
    + /* The old format was not very portable.
    + We now use an int 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;
    +
    + if (dcd == last_dcd) {
    + printk(KERN_WARNING LIRC_DRIVER_NAME
    + ": ignoring spike: %d %d %lx %lx %lx %lx\n",
    + dcd, sense,
    + tv.tv_sec, lasttv.tv_sec,
    + tv.tv_usec, lasttv.tv_usec);
    + continue;
    + }
    +
    + deltv = tv.tv_sec-lasttv.tv_sec;
    + if (tv.tv_sec < lasttv.tv_sec ||
    + (tv.tv_sec == lasttv.tv_sec &&
    + tv.tv_usec < lasttv.tv_usec)) {
    + printk(KERN_WARNING LIRC_DRIVER_NAME
    + ": AIEEEE: your clock just jumped "
    + "backwards\n");
    + printk(KERN_WARNING LIRC_DRIVER_NAME
    + ": %d %d %lx %lx %lx %lx\n",
    + dcd, sense,
    + tv.tv_sec, lasttv.tv_sec,
    + tv.tv_usec, lasttv.tv_usec);
    + data = PULSE_MASK;
    + } else if (deltv > 15) {
    + data = PULSE_MASK; /* really long time */
    + if (!(dcd^sense)) {
    + /* sanity check */
    + 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);
    + /* detecting pulse while this
    + MUST be a space! */
    + sense = sense ? 0 : 1;
    + }
    + } else
    + data = (int) (deltv*1000000 +
    + tv.tv_usec -
    + lasttv.tv_usec);
    + frbwrite(dcd^sense ? data : (data|PULSE_BIT));
    + lasttv = tv;
    + last_dcd = dcd;
    + wake_up_interruptible(&rbuf.wait_poll);
    + }
    + } while (!(sinp(UART_IIR) & UART_IIR_NO_INT)); /* still pending ? */
    + return IRQ_RETVAL(IRQ_HANDLED);
    +}
    +
    +static void hardware_init_port(void)
    +{
    + 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)));
    +
    + /* Clear registers. */
    + sinp(UART_LSR);
    + sinp(UART_RX);
    + sinp(UART_IIR);
    + sinp(UART_MSR);
    +
    +#if defined(LIRC_SERIAL_NSLU2)
    + if (type == LIRC_NSLU2) {
    + /* Setup NSLU2 UART */
    +
    + /* Enable UART */
    + soutp(UART_IER, sinp(UART_IER) | UART_IE_IXP42X_UUE);
    + /* Disable Receiver data Time out interrupt */
    + soutp(UART_IER, sinp(UART_IER) & ~UART_IE_IXP42X_RTOIE);
    + /* set out2 = interupt unmask; off() doesn't set MCR
    + on NSLU2 */
    + soutp(UART_MCR, UART_MCR_RTS|UART_MCR_OUT2);
    + }
    +#endif
    +
    + /* Set line for power source */
    + off();
    +
    + /* Clear registers again to be sure. */
    + sinp(UART_LSR);
    + sinp(UART_RX);
    + sinp(UART_IIR);
    + sinp(UART_MSR);
    +
    + switch (type) {
    + case LIRC_IRDEO:
    + case LIRC_IRDEO_REMOTE:
    + /* setup port to 7N1 @ 115200 Baud */
    + /* 7N1+start = 9 bits at 115200 ~ 3 bits at 38kHz */
    +
    + /* Set DLAB 1. */
    + soutp(UART_LCR, sinp(UART_LCR) | UART_LCR_DLAB);
    + /* Set divisor to 1 => 115200 Baud */
    + soutp(UART_DLM, 0);
    + soutp(UART_DLL, 1);
    + /* Set DLAB 0 + 7N1 */
    + soutp(UART_LCR, UART_LCR_WLEN7);
    + /* THR interrupt already disabled at this point */
    + break;
    + default:
    + break;
    + }
    +
    + local_irq_restore(flags);
    +}
    +
    +static int init_port(void)
    +{
    + int i, nlow, nhigh;
    +
    + /* Reserve io region. */
    +#if defined(LIRC_ALLOW_MMAPPED_IO)
    + /* Future MMAP-Developers: Attention!
    + For memory mapped I/O you *might* need to use ioremap() first,
    + for the NSLU2 it's done in boot code. */
    + if (((iommap != 0)
    + && (request_mem_region(iommap, 8<<ioshift,
    + LIRC_DRIVER_NAME) == NULL))
    + || ((iommap == 0)
    + && (request_region(io, 8, LIRC_DRIVER_NAME) == NULL))) {
    +#else
    + if (request_region(io, 8, LIRC_DRIVER_NAME) == NULL) {
    +#endif
    + printk(KERN_ERR LIRC_DRIVER_NAME
    + ": port %04x already in use\n", io);
    + printk(KERN_WARNING LIRC_DRIVER_NAME
    + ": use 'setserial /dev/ttySX uart none'\n");
    + printk(KERN_WARNING LIRC_DRIVER_NAME
    + ": or compile the serial port driver as module and\n");
    + printk(KERN_WARNING LIRC_DRIVER_NAME
    + ": make sure this module is loaded first\n");
    + return -EBUSY;
    + }
    +
    + hardware_init_port();
    +
    + /* 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/2 sec for the power supply */
    +
    + set_current_state(TASK_INTERRUPTIBLE);
    + schedule_timeout(HZ/2);
    +
    + /* probe 9 times every 0.04s, collect "votes" for
    + active high/low */
    + nlow = 0;
    + nhigh = 0;
    + for (i = 0; i < 9; i++) {
    + if (sinp(UART_MSR) & hardware[type].signal_pin)
    + nlow++;
    + else
    + nhigh++;
    + schedule_timeout(HZ/25);
    + }
    + sense = (nlow >= nhigh ? 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;
    +
    + /* Init read buffer. */
    + if (lirc_buffer_init(&rbuf, sizeof(int), RBUF_LEN) < 0)
    + return -ENOMEM;
    +
    + /* initialize timestamp */
    + do_gettimeofday(&lasttv);
    +
    + result = request_irq(irq, irq_handler,
    + IRQF_DISABLED | (share_irq ? IRQF_SHARED : 0),
    + LIRC_DRIVER_NAME, (void *)&hardware);
    +
    + switch (result) {
    + case -EBUSY:
    + printk(KERN_ERR LIRC_DRIVER_NAME ": IRQ %d busy\n", irq);
    + lirc_buffer_free(&rbuf);
    + return -EBUSY;
    + case -EINVAL:
    + printk(KERN_ERR LIRC_DRIVER_NAME
    + ": Bad irq number or handler\n");
    + lirc_buffer_free(&rbuf);
    + return -EINVAL;
    + default:
    + dprintk("Interrupt %d, port %04x obtained\n", irq, io);
    + break;
    + };
    +
    + local_irq_save(flags);
    +
    + /* Set DLAB 0. */
    + soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
    +
    + soutp(UART_IER, sinp(UART_IER)|UART_IER_MSI);
    +
    + local_irq_restore(flags);
    +
    + 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, (void *)&hardware);
    +
    + dprintk("freed IRQ %d\n", irq);
    + lirc_buffer_free(&rbuf);
    +}
    +
    +static ssize_t lirc_write(struct file *file, const char *buf,
    + size_t n, loff_t *ppos)
    +{
    + int i, count;
    + unsigned long flags;
    + long delta = 0;
    +
    + if (!(hardware[type].features&LIRC_CAN_SEND_PULSE))
    + return -EBADF;
    +
    + if (n % sizeof(int))
    + return -EINVAL;
    + count = n / sizeof(int);
    + if (count > WBUF_LEN || count % 2 == 0)
    + return -EINVAL;
    + if (copy_from_user(wbuf, buf, n))
    + return -EFAULT;
    + local_irq_save(flags);
    + if (type == LIRC_IRDEO) {
    + /* DTR, RTS down */
    + on();
    + }
    + for (i = 0; i < count; i++) {
    + if (i%2)
    + hardware[type].send_space(wbuf[i]-delta);
    + else
    + delta = hardware[type].send_pulse(wbuf[i]);
    + }
    + off();
    + local_irq_restore(flags);
    + return n;
    +}
    +
    +static int lirc_ioctl(struct inode *node, struct file *filep, unsigned int cmd,
    + unsigned long arg)
    +{
    + int result;
    + unsigned long value;
    + unsigned int ivalue;
    +
    + switch (cmd) {
    + case LIRC_GET_SEND_MODE:
    + if (!(hardware[type].features&LIRC_CAN_SEND_MASK))
    + return -ENOIOCTLCMD;
    +
    + result = put_user(LIRC_SEND2MODE
    + (hardware[type].features&LIRC_CAN_SEND_MASK),
    + (unsigned long *) arg);
    + if (result)
    + return result;
    + break;
    +
    + case LIRC_SET_SEND_MODE:
    + if (!(hardware[type].features&LIRC_CAN_SEND_MASK))
    + return -ENOIOCTLCMD;
    +
    + result = get_user(value, (unsigned long *) arg);
    + if (result)
    + return result;
    + /* only LIRC_MODE_PULSE supported */
    + if (value != LIRC_MODE_PULSE)
    + return -ENOSYS;
    + break;
    +
    + case LIRC_GET_LENGTH:
    + return -ENOSYS;
    + break;
    +
    + case LIRC_SET_SEND_DUTY_CYCLE:
    + dprintk("SET_SEND_DUTY_CYCLE\n");
    + if (!(hardware[type].features&LIRC_CAN_SET_SEND_DUTY_CYCLE))
    + return -ENOIOCTLCMD;
    +
    + result = get_user(ivalue, (unsigned int *) arg);
    + if (result)
    + return result;
    + if (ivalue <= 0 || ivalue > 100)
    + return -EINVAL;
    + return init_timing_params(ivalue, freq);
    + break;
    +
    + case LIRC_SET_SEND_CARRIER:
    + dprintk("SET_SEND_CARRIER\n");
    + if (!(hardware[type].features&LIRC_CAN_SET_SEND_CARRIER))
    + return -ENOIOCTLCMD;
    +
    + result = get_user(ivalue, (unsigned int *) arg);
    + if (result)
    + return result;
    + if (ivalue > 500000 || ivalue < 20000)
    + return -EINVAL;
    + return init_timing_params(duty_cycle, ivalue);
    + break;
    +
    + default:
    + return -ENOIOCTLCMD;
    + }
    + return 0;
    +}
    +
    +static struct file_operations lirc_fops = {
    + .write = lirc_write,
    +};
    +
    +static struct lirc_plugin plugin = {
    + .name = LIRC_DRIVER_NAME,
    + .minor = -1,
    + .code_length = 1,
    + .sample_rate = 0,
    + .data = NULL,
    + .add_to_buf = NULL,
    + .get_queue = NULL,
    + .rbuf = &rbuf,
    + .set_use_inc = set_use_inc,
    + .set_use_dec = set_use_dec,
    + .ioctl = lirc_ioctl,
    + .fops = &lirc_fops,
    + .dev = NULL,
    + .owner = THIS_MODULE,
    +};
    +
    +#ifdef MODULE
    +
    +static struct platform_device *lirc_serial_dev;
    +
    +static int __devinit lirc_serial_probe(struct platform_device *dev)
    +{
    + return 0;
    +}
    +
    +static int __devexit lirc_serial_remove(struct platform_device *dev)
    +{
    + return 0;
    +}
    +
    +static int lirc_serial_suspend(struct platform_device *dev,
    + pm_message_t state)
    +{
    + /* Set DLAB 0. */
    + soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
    +
    + /* Disable all interrupts */
    + soutp(UART_IER, sinp(UART_IER) &
    + (~(UART_IER_MSI|UART_IER_RLSI|UART_IER_THRI|UART_IER_RDI)));
    +
    + /* Clear registers. */
    + sinp(UART_LSR);
    + sinp(UART_RX);
    + sinp(UART_IIR);
    + sinp(UART_MSR);
    +
    + return 0;
    +}
    +
    +static int lirc_serial_resume(struct platform_device *dev)
    +{
    + unsigned long flags;
    +
    + hardware_init_port();
    +
    + local_irq_save(flags);
    + /* Enable Interrupt */
    + do_gettimeofday(&lasttv);
    + soutp(UART_IER, sinp(UART_IER)|UART_IER_MSI);
    + off();
    +
    + lirc_buffer_clear(&rbuf);
    +
    + local_irq_restore(flags);
    +
    + return 0;
    +}
    +
    +static struct platform_driver lirc_serial_driver = {
    + .probe = lirc_serial_probe,
    + .remove = __devexit_p(lirc_serial_remove),
    + .suspend = lirc_serial_suspend,
    + .resume = lirc_serial_resume,
    + .driver = {
    + .name = "lirc_serial",
    + .owner = THIS_MODULE,
    + },
    +};
    +
    +static int __init lirc_serial_init(void)
    +{
    + int result;
    +
    + result = platform_driver_register(&lirc_serial_driver);
    + if (result) {
    + printk("lirc register returned %d\n", result);
    + return result;
    + }
    +
    + lirc_serial_dev = platform_device_alloc("lirc_serial", 0);
    + if (!lirc_serial_dev) {
    + result = -ENOMEM;
    + goto exit_driver_unregister;
    + }
    +
    + result = platform_device_add(lirc_serial_dev);
    + if (result)
    + goto exit_device_put;
    +
    + return 0;
    +
    +exit_device_put:
    + platform_device_put(lirc_serial_dev);
    +exit_driver_unregister:
    + platform_driver_unregister(&lirc_serial_driver);
    + return result;
    +}
    +
    +static void __exit lirc_serial_exit(void)
    +{
    + platform_device_unregister(lirc_serial_dev);
    + platform_driver_unregister(&lirc_serial_driver);
    +}
    +
    +int __init init_module(void)
    +{
    + int result;
    +
    + result = lirc_serial_init();
    + if (result)
    + return result;
    + switch (type) {
    + case LIRC_HOMEBREW:
    + case LIRC_IRDEO:
    + case LIRC_IRDEO_REMOTE:
    + case LIRC_ANIMAX:
    + case LIRC_IGOR:
    +#if defined(LIRC_SERIAL_NSLU2)
    + case LIRC_NSLU2:
    +#endif
    + break;
    + default:
    + result = -EINVAL;
    + goto exit_serial_exit;
    + }
    + if (!softcarrier) {
    + switch (type) {
    + case LIRC_HOMEBREW:
    + case LIRC_IGOR:
    + case LIRC_NSLU2:
    + hardware[type].features &=
    + ~(LIRC_CAN_SET_SEND_DUTY_CYCLE|
    + LIRC_CAN_SET_SEND_CARRIER);
    + break;
    + }
    + }
    + result = init_port();
    + if (result < 0)
    + goto exit_serial_exit;
    + plugin.features = hardware[type].features;
    + plugin.minor = lirc_register_plugin(&plugin);
    + if (plugin.minor < 0) {
    + printk(KERN_ERR LIRC_DRIVER_NAME
    + ": register_chrdev failed!\n");
    + result = -EIO;
    + goto exit_release;
    + }
    + return 0;
    +exit_release:
    + release_region(io, 8);
    +exit_serial_exit:
    + lirc_serial_exit();
    + return result;
    +}
    +
    +void __exit cleanup_module(void)
    +{
    + lirc_serial_exit();
    +#if defined(LIRC_ALLOW_MMAPPED_IO)
    + if (iommap != 0)
    + release_mem_region(iommap, 8<<ioshift);
    + else
    + release_region(io, 8);
    +#else
    + release_region(io, 8);
    +#endif
    + lirc_unregister_plugin(plugin.minor);
    + dprintk("cleaned up module\n");
    +}
    +
    +MODULE_DESCRIPTION("Infra-red receiver driver for serial ports.");
    +MODULE_AUTHOR("Ralph Metzler, Trent Piepho, Ben Pfaff, "
    + "Christoph Bartelmus, Andrei Tanas");
    +MODULE_LICENSE("GPL");
    +
    +module_param(type, int, 0444);
    +#if defined(LIRC_SERIAL_NSLU2)
    +MODULE_PARM_DESC(type, "Hardware type (0 = home-brew, 1 = IRdeo,"
    + " 2 = IRdeo Remote, 3 = AnimaX, 4 = IgorPlug,"
    + " 5 = NSLU2 RX:CTS2/TX:GreenLED)");
    +#else
    +MODULE_PARM_DESC(type, "Hardware type (0 = home-brew, 1 = IRdeo,"
    + " 2 = IRdeo Remote, 3 = AnimaX, 4 = IgorPlug)");
    +#endif
    +
    +module_param(io, int, 0444);
    +MODULE_PARM_DESC(io, "I/O address base (0x3f8 or 0x2f8)");
    +
    +#if defined(LIRC_ALLOW_MMAPPED_IO)
    +/* some architectures (e.g. intel xscale) have memory mapped registers */
    +module_param(iommap, bool, 0444);
    +MODULE_PARM_DESC(iommap, "physical base for memory mapped I/O"
    + " (0 = no memory mapped io)");
    +
    +/* some architectures (e.g. intel xscale) align the 8bit serial registers
    + on 32bit word boundaries.
    + See linux-kernel/serial/8250.c serial_in()/out() */
    +module_param(ioshift, int, 0444);
    +MODULE_PARM_DESC(ioshift, "shift I/O register offset (0 = no shift)");
    +#endif
    +
    +module_param(irq, int, 0444);
    +MODULE_PARM_DESC(irq, "Interrupt (4 or 3)");
    +
    +module_param(share_irq, bool, 0444);
    +MODULE_PARM_DESC(share_irq, "Share interrupts (0 = off, 1 = on)");
    +
    +module_param(sense, bool, 0444);
    +MODULE_PARM_DESC(sense, "Override autodetection of IR receiver circuit"
    + " (0 = active high, 1 = active low )");
    +
    +#ifdef LIRC_SERIAL_TRANSMITTER
    +module_param(txsense, bool, 0444);
    +MODULE_PARM_DESC(txsense, "Sense of transmitter circuit"
    + " (0 = active high, 1 = active low )");
    +#endif
    +
    +module_param(softcarrier, bool, 0444);
    +MODULE_PARM_DESC(softcarrier, "Software carrier (0 = off, 1 = on)");
    +
    +module_param(debug, bool, 0644);
    +MODULE_PARM_DESC(debug, "Enable debugging messages");
    +
    +#endif /* MODULE */
    diff --git a/drivers/input/lirc/lirc_sir.c b/drivers/input/lirc/lirc_sir.c
    new file mode 100644
    index 0000000..ea192b2
    --- /dev/null
    +++ b/drivers/input/lirc/lirc_sir.c
    @@ -0,0 +1,1302 @@
    +/*
    + * LIRC SIR driver, (C) 2000 Milan Pikula <www@fornax.sk>
    + *
    + * lirc_sir - Device driver for use with SIR (serial infra red)
    + * mode of IrDA on many notebooks.
    + *
    + * This program is free software; you can redistribute it and/or modify
    + * it under the terms of the GNU General Public License as published by
    + * the Free Software Foundation; either version 2 of the License, or
    + * (at your option) any later version.
    + *
    + * This program is distributed in the hope that it will be useful,
    + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    + * GNU General Public License for more details.
    + *
    + * You should have received a copy of the GNU General Public License
    + * along with this program; if not, write to the Free Software
    + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
    + *
    + *
    + * 2000/09/16 Frank Przybylski <mail@frankprzybylski.de> :
    + * added timeout and relaxed pulse detection, removed gap bug
    + *
    + * 2000/12/15 Christoph Bartelmus <lirc@bartelmus.de> :
    + * added support for Tekram Irmate 210 (sending does not work yet,
    + * kind of disappointing that nobody was able to implement that
    + * before),
    + * major clean-up
    + *
    + * 2001/02/27 Christoph Bartelmus <lirc@bartelmus.de> :
    + * added support for StrongARM SA1100 embedded microprocessor
    + * parts cut'n'pasted from sa1100_ir.c (C) 2000 Russell King
    + */
    +
    +
    +#include <linux/version.h>
    +#include <linux/module.h>
    +
    +#include <linux/autoconf.h>
    +
    +#include <linux/module.h>
    +#include <linux/sched.h>
    +#include <linux/errno.h>
    +#include <linux/signal.h>
    +#include <linux/fs.h>
    +#include <linux/interrupt.h>
    +#include <linux/ioport.h>
    +#include <linux/kernel.h>
    +#include <linux/major.h>
    +#include <linux/serial_reg.h>
    +#include <linux/time.h>
    +#include <linux/string.h>
    +#include <linux/types.h>
    +#include <linux/wait.h>
    +#include <linux/mm.h>
    +#include <linux/delay.h>
    +#include <linux/poll.h>
    +#include <asm/system.h>
    +#include <linux/uaccess.h>
    +#include <linux/io.h>
    +#include <asm/irq.h>
    +#include <linux/fcntl.h>
    +#ifdef LIRC_ON_SA1100
    +#include <asm/hardware.h>
    +#ifdef CONFIG_SA1100_COLLIE
    +#include <asm/arch/tc35143.h>
    +#include <asm/ucb1200.h>
    +#endif
    +#endif
    +
    +#include <linux/timer.h>
    +
    +#include "lirc.h"
    +#include "lirc_dev.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 miliseconds = 87ms*/
    +#define TIME_CONST (10000000ul/115200ul)
    +
    +#endif
    +
    +#ifdef LIRC_SIR_ACTISYS_ACT200L
    +static void init_act200(void);
    +#elif defined(LIRC_SIR_ACTISYS_ACT220L)
    +static void init_act220(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 */
    +static unsigned long pulse_width = (13-LIRC_ON_SA1100_TRANSMITTER_LATENCY);
    +/* 1000000/freq-pulse_width */
    +static unsigned long space_width = (13-LIRC_ON_SA1100_TRANSMITTER_LATENCY);
    +static unsigned int freq = 38000; /* modulation frequency */
    +static unsigned int duty_cycle = 50; /* duty cycle of 50% */
    +
    +#endif
    +
    +#define RBUF_LEN 1024
    +#define WBUF_LEN 1024
    +
    +#define LIRC_DRIVER_NAME "lirc_sir"
    +
    +#define PULSE '['
    +
    +#ifndef LIRC_SIR_TEKRAM
    +/* 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
    +#ifndef LIRC_IRQ
    +#define LIRC_IRQ 4
    +#endif
    +#ifndef LIRC_PORT
    +#define LIRC_PORT 0x3e8
    +#endif
    +
    +static int io = LIRC_PORT;
    +static int irq = LIRC_IRQ;
    +static int threshold = 3;
    +#endif
    +
    +static DEFINE_SPINLOCK(timer_lock);
    +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;
    +
    +static DECLARE_WAIT_QUEUE_HEAD(lirc_read_queue);
    +
    +static DEFINE_SPINLOCK(hardware_lock);
    +static DEFINE_SPINLOCK(dev_lock);
    +
    +static int rx_buf[RBUF_LEN];
    +static unsigned int rx_tail, rx_head;
    +static int tx_buf[WBUF_LEN];
    +
    +static int debug;
    +#define dprintk(fmt, args...) \
    + do { \
    + if (debug) \
    + printk(KERN_DEBUG LIRC_DRIVER_NAME ": " \
    + fmt, ## args); \
    + } while (0)
    +
    +/* 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);
    +static void send_space(unsigned long len);
    +static void send_pulse(unsigned long len);
    +static int init_hardware(void);
    +static void drop_hardware(void);
    + /* Initialisation */
    +static int init_port(void);
    +static void drop_port(void);
    +
    +#ifdef LIRC_ON_SA1100
    +static inline void on(void)
    +{
    + PPSR |= PPC_TXD2;
    +}
    +
    +static inline void off(void)
    +{
    + PPSR &= ~PPC_TXD2;
    +}
    +#else
    +static inline unsigned int sinp(int offset)
    +{
    + return inb(io + offset);
    +}
    +
    +static inline void soutp(int offset, int value)
    +{
    + outb(value, io + offset);
    +}
    +#endif
    +
    +#ifndef MAX_UDELAY_MS
    +#define MAX_UDELAY_US 5000
    +#else
    +#define MAX_UDELAY_US (MAX_UDELAY_MS*1000)
    +#endif
    +
    +static inline void safe_udelay(unsigned long usecs)
    +{
    + while (usecs > MAX_UDELAY_US) {
    + udelay(MAX_UDELAY_US);
    + usecs -= MAX_UDELAY_US;
    + }
    + udelay(usecs);
    +}
    +
    +/* SECTION: Communication with user-space */
    +
    +static int lirc_open(struct inode *inode, struct file *file)
    +{
    + spin_lock(&dev_lock);
    + if (module_refcount(THIS_MODULE)) {
    + spin_unlock(&dev_lock);
    + return -EBUSY;
    + }
    + spin_unlock(&dev_lock);
    + return 0;
    +}
    +
    +static int lirc_close(struct inode *inode, struct file *file)
    +{
    + 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(int))
    + return -EINVAL;
    +
    + add_wait_queue(&lirc_read_queue, &wait);
    + set_current_state(TASK_INTERRUPTIBLE);
    + while (n < count) {
    + if (rx_head != rx_tail) {
    + if (copy_to_user((void *) buf + n,
    + (void *) (rx_buf + rx_head),
    + sizeof(int))) {
    + retval = -EFAULT;
    + break;
    + }
    + rx_head = (rx_head + 1) & (RBUF_LEN - 1);
    + n += sizeof(int);
    + } else {
    + if (file->f_flags & O_NONBLOCK) {
    + retval = -EAGAIN;
    + break;
    + }
    + if (signal_pending(current)) {
    + retval = -ERESTARTSYS;
    + break;
    + }
    + schedule();
    + set_current_state(TASK_INTERRUPTIBLE);
    + }
    + }
    + remove_wait_queue(&lirc_read_queue, &wait);
    + set_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;
    + int i;
    +
    + if (n % sizeof(int) || (n / sizeof(int)) > WBUF_LEN)
    + return -EINVAL;
    + if (copy_from_user(tx_buf, buf, n))
    + return -EFAULT;
    + i = 0;
    + n /= sizeof(int);
    +#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;
    +}
    +
    +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;
    +
    + 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 reetval;
    + 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;
    + 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;
    + int newval;
    +
    + dprintk("add flag %d with val %lu\n", flag, val);
    +
    + 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) {
    + dprintk("Buffer overrun.\n");
    + 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)
    +{
    + return 0;
    +}
    +
    +static void set_use_dec(void *data)
    +{
    +}
    +
    +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,
    + .dev = NULL,
    + .owner = THIS_MODULE,
    +};
    +
    +
    +#ifdef MODULE
    +static 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);
    + dprintk("timeout add %d for %lu usec\n", last_value, pulse_end);
    + 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)
    +{
    + unsigned char data;
    + struct timeval curr_tv;
    + static unsigned long deltv;
    +#ifdef LIRC_ON_SA1100
    + int status;
    + static int 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;
    +
    + if (debug) {
    + dprintk("EIF\n");
    + bstat = Ser2UTSR1;
    +
    + if (bstat & UTSR1_FRE)
    + dprintk("frame error\n");
    + if (bstat & UTSR1_ROR)
    + dprintk("receive fifo overrun\n");
    + if (bstat & UTSR1_PRE)
    + dprintk("parity error\n");
    + }
    +
    + bstat = Ser2UTDR;
    + n++;
    + status = Ser2UTSR0;
    + }
    +
    + if (status & (UTSR0_RFS | UTSR0_RID)) {
    + do_gettimeofday(&curr_tv);
    + deltv = delta(&last_tv, &curr_tv);
    + do {
    + data = Ser2UTDR;
    + dprintk("%d data: %u\n", n, (unsigned int) 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) {
    + 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(KERN_ERR "transmit fifo not full, shouldn't 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);
    + dprintk("t %lu, d %d\n", deltintrtv, (int)data);
    + /* if nothing came in last X cycles,
    + it was gap */
    + if (deltintrtv > TIME_CONST * threshold) {
    + if (last_value) {
    + dprintk("GAP\n");
    + /* 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);
    + }
    +
    + lsr = inb(io + UART_LSR);
    + } while (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
    +static void send_pulse(unsigned long length)
    +{
    + unsigned long k, delay;
    + int flag;
    +
    + if (length == 0)
    + return;
    + /* this won't give us the carrier frequency we really want
    + due to integer arithmetic, but we can accept this inaccuracy */
    +
    + for (k = flag = 0; k < length; k += delay, flag = !flag) {
    + if (flag) {
    + off();
    + delay = space_width;
    + } else {
    + on();
    + delay = pulse_width;
    + }
    + safe_udelay(delay);
    + }
    + off();
    +}
    +
    +static void send_space(unsigned long length)
    +{
    + if (length == 0)
    + return;
    + off();
    + safe_udelay(length);
    +}
    +#else
    +static void send_space(unsigned long len)
    +{
    + safe_udelay(len);
    +}
    +
    +static void send_pulse(unsigned long len)
    +{
    + long bytes_out = len / TIME_CONST;
    + long time_left;
    +
    + time_left = (long)len - (long)bytes_out * (long)TIME_CONST;
    + if (bytes_out == 0) {
    + bytes_out++;
    + time_left = 0;
    + }
    + while (bytes_out--) {
    + outb(PULSE, io + UART_TX);
    + /* FIXME treba seriozne cakanie z char/serial.c */
    + while (!(inb(io + UART_LSR) & UART_LSR_THRE))
    + ;
    + }
    +#if 0
    + if (time_left > 0)
    + safe_udelay(time_left);
    +#endif
    +}
    +#endif
    +
    +#ifdef CONFIG_SA1100_COLLIE
    +static inline int sa1100_irda_set_power_collie(int state)
    +{
    + if (state) {
    + /*
    + * 0 - off
    + * 1 - short range, lowest power
    + * 2 - medium range, medium power
    + * 3 - maximum range, high power
    + */
    + ucb1200_set_io_direction(TC35143_GPIO_IR_ON,
    + TC35143_IODIR_OUTPUT);
    + ucb1200_set_io(TC35143_GPIO_IR_ON, TC35143_IODAT_LOW);
    + udelay(100);
    + } else {
    + /* OFF */
    + ucb1200_set_io_direction(TC35143_GPIO_IR_ON,
    + TC35143_IODIR_OUTPUT);
    + ucb1200_set_io(TC35143_GPIO_IR_ON, TC35143_IODAT_HIGH);
    + }
    + return 0;
    +}
    +#endif
    +
    +static int init_hardware(void)
    +{
    + unsigned long flags;
    +
    + spin_lock_irqsave(&hardware_lock, flags);
    + /* reset UART */
    +#ifdef LIRC_ON_SA1100
    +#ifdef CONFIG_SA1100_BITSY
    + if (machine_is_bitsy()) {
    + printk(KERN_INFO "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();
    +#elif defined(LIRC_SIR_ACTISYS_ACT220L)
    + init_act220();
    +#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;
    +
    + /* get I/O port access and IRQ line */
    +#ifndef LIRC_ON_SA1100
    + if (request_region(io, 8, LIRC_DRIVER_NAME) == NULL) {
    + printk(KERN_ERR LIRC_DRIVER_NAME
    + ": i/o port 0x%.4x already in use.\n", io);
    + return -EBUSY;
    + }
    +#endif
    + retval = request_irq(irq, sir_interrupt, IRQF_DISABLED,
    + LIRC_DRIVER_NAME, NULL);
    + if (retval < 0) {
    +# ifndef LIRC_ON_SA1100
    + release_region(io, 8);
    +# endif
    + printk(KERN_ERR LIRC_DRIVER_NAME
    + ": IRQ %d already in use.\n",
    + irq);
    + return retval;
    + }
    +#ifndef LIRC_ON_SA1100
    + 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)
    +{
    + 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 < sizeof(control); i++) {
    + soutp(UART_TX, control[i]);
    + /* one byte takes ~1042 usec to transmit at 9600,8N1 */
    + udelay(1500);
    + }
    +
    + /* back to normal operation */
    + soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2);
    + udelay(50);
    +
    + udelay(1500);
    + soutp(UART_LCR, sinp(UART_LCR) | UART_LCR_DLAB);
    +
    + /* Set DLAB 1. */
    + soutp(UART_LCR, UART_LCR_DLAB | UART_LCR_WLEN7);
    +
    + /* Set divisor to 1 => 115200 Baud */
    + soutp(UART_DLM, 0);
    + soutp(UART_DLL, 1);
    +
    + /* Set DLAB 0. */
    + soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
    +
    + /* Set DLAB 0, 7 Bit */
    + soutp(UART_LCR, UART_LCR_WLEN7);
    +
    + /* enable interrupts */
    + soutp(UART_IER, sinp(UART_IER)|UART_IER_RDI);
    +}
    +#endif
    +
    +#ifdef LIRC_SIR_ACTISYS_ACT220L
    +/* Derived from linux IrDA driver (net/irda/actisys.c)
    + * Drop me a mail for any kind of comment: maxx@spaceboyz.net */
    +
    +void init_act220(void)
    +{
    + int i;
    +
    + /* DLAB 1 */
    + soutp(UART_LCR, UART_LCR_DLAB|UART_LCR_WLEN7);
    +
    + /* 9600 baud */
    + soutp(UART_DLM, 0);
    + soutp(UART_DLL, 12);
    +
    + /* DLAB 0 */
    + soutp(UART_LCR, UART_LCR_WLEN7);
    +
    + /* reset the dongle, set DTR low for 10us */
    + soutp(UART_MCR, UART_MCR_RTS|UART_MCR_OUT2);
    + udelay(10);
    +
    + /* back to normal (still 9600) */
    + soutp(UART_MCR, UART_MCR_DTR|UART_MCR_RTS|UART_MCR_OUT2);
    +
    + /* send RTS pulses until we reach 115200
    + * i hope this is really the same for act220l/act220l+ */
    + for (i = 0; i < 3; i++) {
    + udelay(10);
    + /* set RTS low for 10 us */
    + soutp(UART_MCR, UART_MCR_DTR|UART_MCR_OUT2);
    + udelay(10);
    + /* set RTS high for 10 us */
    + soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2);
    + }
    +
    + /* back to normal operation */
    + udelay(1500); /* better safe than sorry ;) */
    +
    + /* Set DLAB 1. */
    + soutp(UART_LCR, UART_LCR_DLAB | UART_LCR_WLEN7);
    +
    + /* Set divisor to 1 => 115200 Baud */
    + soutp(UART_DLM, 0);
    + soutp(UART_DLL, 1);
    +
    + /* Set DLAB 0, 7 Bit */
    + /* The dongle doesn't seem to have any problems with operation
    + at 7N1 */
    + soutp(UART_LCR, UART_LCR_WLEN7);
    +
    + /* enable interrupts */
    + soutp(UART_IER, UART_IER_RDI);
    +}
    +#endif
    +
    +static int init_lirc_sir(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
    +
    +static int __init lirc_sir_init(void)
    +{
    + int retval;
    +
    + retval = init_chrdev();
    + if (retval < 0)
    + return retval;
    + retval = init_lirc_sir();
    + if (retval) {
    + drop_chrdev();
    + return retval;
    + }
    + return 0;
    +}
    +
    +static void __exit lirc_sir_exit(void)
    +{
    + drop_hardware();
    + drop_chrdev();
    + drop_port();
    + printk(KERN_INFO LIRC_DRIVER_NAME ": Uninstalled.\n");
    +}
    +
    +module_init(lirc_sir_init);
    +module_exit(lirc_sir_exit);
    +
    +#ifdef LIRC_SIR_TEKRAM
    +MODULE_DESCRIPTION("Infrared receiver driver for Tekram Irmate 210");
    +MODULE_AUTHOR("Christoph Bartelmus");
    +#elif defined(LIRC_ON_SA1100)
    +MODULE_DESCRIPTION("LIRC driver for StrongARM SA1100 embedded microprocessor");
    +MODULE_AUTHOR("Christoph Bartelmus");
    +#elif defined(LIRC_SIR_ACTISYS_ACT200L)
    +MODULE_DESCRIPTION("LIRC driver for Actisys Act200L");
    +MODULE_AUTHOR("Karl Bongers");
    +#elif defined(LIRC_SIR_ACTISYS_ACT220L)
    +MODULE_DESCRIPTION("LIRC driver for Actisys Act220L(+)");
    +MODULE_AUTHOR("Jan Roemisch");
    +#else
    +MODULE_DESCRIPTION("Infrared receiver driver for SIR type serial ports");
    +MODULE_AUTHOR("Milan Pikula");
    +#endif
    +MODULE_LICENSE("GPL");
    +
    +#ifdef LIRC_ON_SA1100
    +module_param(irq, int, 0444);
    +MODULE_PARM_DESC(irq, "Interrupt (16)");
    +#else
    +module_param(io, int, 0444);
    +MODULE_PARM_DESC(io, "I/O address base (0x3f8 or 0x2f8)");
    +
    +module_param(irq, int, 0444);
    +MODULE_PARM_DESC(irq, "Interrupt (4 or 3)");
    +
    +module_param(threshold, int, 0444);
    +MODULE_PARM_DESC(threshold, "space detection threshold (3)");
    +#endif
    +
    +module_param(debug, bool, 0644);
    +MODULE_PARM_DESC(debug, "Enable debugging messages");
    +
    +#endif /* MODULE */
    diff --git a/drivers/input/lirc/lirc_streamzap.c b/drivers/input/lirc/lirc_streamzap.c
    new file mode 100644
    index 0000000..69865cb
    --- /dev/null
    +++ b/drivers/input/lirc/lirc_streamzap.c
    @@ -0,0 +1,795 @@
    +/*
    + * Streamzap Remote Control driver
    + *
    + * Copyright (c) 2005 Christoph Bartelmus <lirc@bartelmus.de>
    + *
    + * This driver was based on the work of Greg Wickham and Adrian
    + * Dewhurst. It was substantially rewritten to support correct signal
    + * gaps and now maintains a delay buffer, which is used to present
    + * consistent timing behaviour to user space applications. Without the
    + * delay buffer an ugly hack would be required in lircd, which can
    + * cause sluggish signal decoding in certain situations.
    + *
    + * This driver is based on the USB skeleton driver packaged with the
    + * kernel; 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; either version 2 of the License, or
    + * (at your option) any later version.
    + *
    + * This program is distributed in the hope that it will be useful,
    + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    + * GNU General Public License for more details.
    + *
    + * You should have received a copy of the GNU General Public License
    + * along with this program; if not, write to the Free Software
    + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
    + *
    + */
    +
    +#include <linux/version.h>
    +
    +#include <linux/autoconf.h>
    +#include <linux/kernel.h>
    +#include <linux/errno.h>
    +#include <linux/init.h>
    +#include <linux/slab.h>
    +#include <linux/module.h>
    +#include <linux/smp_lock.h>
    +#include <linux/completion.h>
    +#include <linux/uaccess.h>
    +#include <linux/usb.h>
    +
    +#include "lirc.h"
    +#include "lirc_dev.h"
    +
    +#define DRIVER_VERSION "1.28"
    +#define DRIVER_NAME "lirc_streamzap"
    +#define DRIVER_DESC "Streamzap Remote Control driver"
    +
    +/* ------------------------------------------------------------------ */
    +
    +static int debug;
    +
    +#define USB_STREAMZAP_VENDOR_ID 0x0e9c
    +#define USB_STREAMZAP_PRODUCT_ID 0x0000
    +
    +/* Use our own dbg macro */
    +#define dprintk(fmt, args...) \
    + do { \
    + if (debug) \
    + printk(KERN_DEBUG DRIVER_NAME "[%d]: " \
    + fmt "\n", ## args); \
    + } while (0)
    +
    +/*
    + * table of devices that work with this driver
    + */
    +static struct usb_device_id streamzap_table[] = {
    + /* Streamzap Remote Control */
    + { USB_DEVICE(USB_STREAMZAP_VENDOR_ID, USB_STREAMZAP_PRODUCT_ID) },
    + /* Terminating entry */
    + { }
    +};
    +
    +MODULE_DEVICE_TABLE(usb, streamzap_table);
    +
    +#define STREAMZAP_PULSE_MASK 0xf0
    +#define STREAMZAP_SPACE_MASK 0x0f
    +#define STREAMZAP_RESOLUTION 256
    +
    +/* number of samples buffered */
    +#define STREAMZAP_BUFFER_SIZE 128
    +
    +enum StreamzapDecoderState {
    + PulseSpace,
    + FullPulse,
    + FullSpace,
    + IgnorePulse
    +};
    +
    +/* Structure to hold all of our device specific stuff */
    +/* some remarks regarding locking:
    + theoretically this struct can be accessed from three threads:
    +
    + - from lirc_dev through set_use_inc/set_use_dec
    +
    + - from the USB layer throuh probe/disconnect/irq
    +
    + Careful placement of lirc_register_plugin/lirc_unregister_plugin
    + calls will prevent conflicts. lirc_dev makes sure that
    + set_use_inc/set_use_dec are not being executed and will not be
    + called after lirc_unregister_plugin returns.
    +
    + - by the timer callback
    +
    + The timer is only running when the device is connected and the
    + LIRC device is open. Making sure the timer is deleted by
    + set_use_dec will make conflicts impossible.
    +*/
    +struct usb_streamzap {
    +
    + /* usb */
    + /* save off the usb device pointer */
    + struct usb_device *udev;
    + /* the interface for this device */
    + struct usb_interface *interface;
    +
    + /* buffer & dma */
    + unsigned char *buf_in;
    + dma_addr_t dma_in;
    + unsigned int buf_in_len;
    +
    + struct usb_endpoint_descriptor *endpoint;
    +
    + /* IRQ */
    + struct urb *urb_in;
    +
    + /* lirc */
    + struct lirc_plugin plugin;
    + struct lirc_buffer delay_buf;
    + struct lirc_buffer lirc_buf;
    +
    + /* timer used to support delay buffering */
    + struct timer_list delay_timer;
    + int timer_running;
    + spinlock_t timer_lock;
    +
    + /* tracks whether we are currently receiving some signal */
    + int idle;
    + /* sum of signal lengths received since signal start */
    + unsigned long sum;
    + /* start time of signal; necessary for gap tracking */
    + struct timeval signal_last;
    + struct timeval signal_start;
    + enum StreamzapDecoderState decoder_state;
    + struct timer_list flush_timer;
    + int flush;
    + int in_use;
    +};
    +
    +
    +/* local function prototypes */
    +static int streamzap_probe(struct usb_interface *interface,
    + const struct usb_device_id *id);
    +static void streamzap_disconnect(struct usb_interface *interface);
    +static void usb_streamzap_irq(struct urb *urb);
    +static int streamzap_use_inc(void *data);
    +static void streamzap_use_dec(void *data);
    +static int streamzap_ioctl(struct inode *node, struct file *filep,
    + unsigned int cmd, unsigned long arg);
    +static int streamzap_suspend(struct usb_interface *intf, pm_message_t message);
    +static int streamzap_resume(struct usb_interface *intf);
    +
    +/* usb specific object needed to register this driver with the usb subsystem */
    +
    +static struct usb_driver streamzap_driver = {
    + .name = DRIVER_NAME,
    + .probe = streamzap_probe,
    + .disconnect = streamzap_disconnect,
    + .suspend = streamzap_suspend,
    + .resume = streamzap_resume,
    + .id_table = streamzap_table,
    +};
    +
    +static void stop_timer(struct usb_streamzap *sz)
    +{
    + unsigned long flags;
    +
    + spin_lock_irqsave(&sz->timer_lock, flags);
    + if (sz->timer_running) {
    + sz->timer_running = 0;
    + del_timer_sync(&sz->delay_timer);
    + }
    + spin_unlock_irqrestore(&sz->timer_lock, flags);
    +}
    +
    +static void flush_timeout(unsigned long arg)
    +{
    + struct usb_streamzap *sz = (struct usb_streamzap *) arg;
    +
    + /* finally start accepting data */
    + sz->flush = 0;
    +}
    +static void delay_timeout(unsigned long arg)
    +{
    + unsigned long flags;
    + /* deliver data every 10 ms */
    + static unsigned long timer_inc =
    + (10000/(1000000/HZ)) == 0 ? 1 : (10000/(1000000/HZ));
    + struct usb_streamzap *sz = (struct usb_streamzap *) arg;
    + int data;
    +
    + spin_lock_irqsave(&sz->timer_lock, flags);
    +
    + if (!lirc_buffer_empty(&sz->delay_buf) &&
    + !lirc_buffer_full(&sz->lirc_buf)) {
    + lirc_buffer_read_1(&sz->delay_buf, (unsigned char *) &data);
    + lirc_buffer_write_1(&sz->lirc_buf, (unsigned char *) &data);
    + }
    + if (!lirc_buffer_empty(&sz->delay_buf)) {
    + while (lirc_buffer_available(&sz->delay_buf) <
    + STREAMZAP_BUFFER_SIZE/2 &&
    + !lirc_buffer_full(&sz->lirc_buf)) {
    + lirc_buffer_read_1(&sz->delay_buf,
    + (unsigned char *) &data);
    + lirc_buffer_write_1(&sz->lirc_buf,
    + (unsigned char *) &data);
    + }
    + if (sz->timer_running) {
    + sz->delay_timer.expires += timer_inc;
    + add_timer(&sz->delay_timer);
    + }
    + } else {
    + sz->timer_running = 0;
    + }
    +
    + if (!lirc_buffer_empty(&sz->lirc_buf))
    + wake_up(&sz->lirc_buf.wait_poll);
    +
    + spin_unlock_irqrestore(&sz->timer_lock, flags);
    +}
    +
    +static inline void flush_delay_buffer(struct usb_streamzap *sz)
    +{
    + int data;
    + int empty = 1;
    +
    + while (!lirc_buffer_empty(&sz->delay_buf)) {
    + empty = 0;
    + lirc_buffer_read_1(&sz->delay_buf, (unsigned char *) &data);
    + if (!lirc_buffer_full(&sz->lirc_buf)) {
    + lirc_buffer_write_1(&sz->lirc_buf,
    + (unsigned char *) &data);
    + } else {
    + dprintk("buffer overflow\n", sz->plugin.minor);
    + }
    + }
    + if (!empty)
    + wake_up(&sz->lirc_buf.wait_poll);
    +}
    +
    +static inline void push(struct usb_streamzap *sz, unsigned char *data)
    +{
    + unsigned long flags;
    +
    + spin_lock_irqsave(&sz->timer_lock, flags);
    + if (lirc_buffer_full(&sz->delay_buf)) {
    + int data;
    +
    + lirc_buffer_read_1(&sz->delay_buf, (unsigned char *) &data);
    + if (!lirc_buffer_full(&sz->lirc_buf)) {
    + lirc_buffer_write_1(&sz->lirc_buf,
    + (unsigned char *) &data);
    + } else {
    + dprintk("buffer overflow", sz->plugin.minor);
    + }
    + }
    +
    + lirc_buffer_write_1(&sz->delay_buf, data);
    +
    + if (!sz->timer_running) {
    + sz->delay_timer.expires = jiffies + HZ/10;
    + add_timer(&sz->delay_timer);
    + sz->timer_running = 1;
    + }
    +
    + spin_unlock_irqrestore(&sz->timer_lock, flags);
    +}
    +
    +static inline void push_full_pulse(struct usb_streamzap *sz,
    + unsigned char value)
    +{
    + int pulse;
    +
    + if (sz->idle) {
    + long deltv;
    + int tmp;
    +
    + sz->signal_last = sz->signal_start;
    + do_gettimeofday(&sz->signal_start);
    +
    + deltv = sz->signal_start.tv_sec-sz->signal_last.tv_sec;
    + if (deltv > 15) {
    + tmp = PULSE_MASK; /* really long time */
    + } else {
    + tmp = (int) (deltv*1000000+
    + sz->signal_start.tv_usec -
    + sz->signal_last.tv_usec);
    + tmp -= sz->sum;
    + }
    + dprintk("ls %u", sz->plugin.minor, tmp);
    + push(sz, (char *)&tmp);
    +
    + sz->idle = 0;
    + sz->sum = 0;
    + }
    +
    + pulse = ((int) value)*STREAMZAP_RESOLUTION;
    + pulse += STREAMZAP_RESOLUTION/2;
    + sz->sum += pulse;
    + pulse |= PULSE_BIT;
    +
    + dprintk("p %u", sz->plugin.minor, pulse&PULSE_MASK);
    + push(sz, (char *)&pulse);
    +}
    +
    +static inline void push_half_pulse(struct usb_streamzap *sz,
    + unsigned char value)
    +{
    + push_full_pulse(sz, (value & STREAMZAP_PULSE_MASK)>>4);
    +}
    +
    +static inline void push_full_space(struct usb_streamzap *sz,
    + unsigned char value)
    +{
    + int space;
    +
    + space = ((int) value)*STREAMZAP_RESOLUTION;
    + space += STREAMZAP_RESOLUTION/2;
    + sz->sum += space;
    + dprintk("s %u", sz->plugin.minor, space);
    + push(sz, (char *)&space);
    +}
    +
    +static inline void push_half_space(struct usb_streamzap *sz,
    + unsigned char value)
    +{
    + push_full_space(sz, value & STREAMZAP_SPACE_MASK);
    +}
    +
    +/*
    + * usb_streamzap_irq - IRQ handler
    + *
    + * This procedure is invoked on reception of data from
    + * the usb remote.
    + */
    +static void usb_streamzap_irq(struct urb *urb)
    +{
    + struct usb_streamzap *sz;
    + int len;
    + unsigned int i = 0;
    +
    + if (!urb)
    + return;
    +
    + sz = urb->context;
    + len = urb->actual_length;
    +
    + switch (urb->status) {
    + case -ECONNRESET:
    + case -ENOENT:
    + case -ESHUTDOWN:
    + /* this urb is terminated, clean up */
    + /* sz might already be invalid at this point */
    + dprintk("urb status: %d", -1, urb->status);
    + return;
    + default:
    + break;
    + }
    +
    + dprintk("received %d", sz->plugin.minor, urb->actual_length);
    + if (!sz->flush) {
    + for (i = 0; i < urb->actual_length; i++) {
    + dprintk("%d: %x", sz->plugin.minor,
    + i, (unsigned char) sz->buf_in[i]);
    + switch (sz->decoder_state) {
    + case PulseSpace:
    + if ((sz->buf_in[i]&STREAMZAP_PULSE_MASK) ==
    + STREAMZAP_PULSE_MASK) {
    + sz->decoder_state = FullPulse;
    + continue;
    + } else if ((sz->buf_in[i]&STREAMZAP_SPACE_MASK)
    + == STREAMZAP_SPACE_MASK) {
    + push_half_pulse(sz, sz->buf_in[i]);
    + sz->decoder_state = FullSpace;
    + continue;
    + } else {
    + push_half_pulse(sz, sz->buf_in[i]);
    + push_half_space(sz, sz->buf_in[i]);
    + }
    + break;
    + case FullPulse:
    + push_full_pulse(sz, sz->buf_in[i]);
    + sz->decoder_state = IgnorePulse;
    + break;
    + case FullSpace:
    + if (sz->buf_in[i] == 0xff) {
    + sz->idle = 1;
    + stop_timer(sz);
    + flush_delay_buffer(sz);
    + } else
    + push_full_space(sz, sz->buf_in[i]);
    + sz->decoder_state = PulseSpace;
    + break;
    + case IgnorePulse:
    + if ((sz->buf_in[i]&STREAMZAP_SPACE_MASK) ==
    + STREAMZAP_SPACE_MASK) {
    + sz->decoder_state = FullSpace;
    + continue;
    + }
    + push_half_space(sz, sz->buf_in[i]);
    + sz->decoder_state = PulseSpace;
    + break;
    + }
    + }
    + }
    +
    + /* resubmit only for 2.6 */
    + usb_submit_urb(urb, GFP_ATOMIC);
    +
    + return;
    +}
    +
    +/**
    + * streamzap_probe
    + *
    + * Called by usb-core to associated with a candidate device
    + * On any failure the return value is the ERROR
    + * On success return 0
    + */
    +static int streamzap_probe(struct usb_interface *interface,
    + const struct usb_device_id *id)
    +{
    + struct usb_device *udev = interface_to_usbdev(interface);
    + struct usb_host_interface *iface_host;
    + int retval = -ENOMEM;
    + struct usb_streamzap *sz = NULL;
    + char buf[63], name[128] = "";
    +
    + /***************************************************
    + * Allocate space for device driver specific data
    + */
    + sz = kmalloc(sizeof(struct usb_streamzap), GFP_KERNEL);
    + if (sz == NULL)
    + goto error;
    +
    + memset(sz, 0, sizeof(*sz));
    + sz->udev = udev;
    + sz->interface = interface;
    +
    + /***************************************************
    + * Check to ensure endpoint information matches requirements
    + */
    + iface_host = interface->cur_altsetting;
    +
    + if (iface_host->desc.bNumEndpoints != 1) {
    + err("%s: Unexpected desc.bNumEndpoints (%d)", __func__,
    + iface_host->desc.bNumEndpoints);
    + retval = -ENODEV;
    + goto error;
    + }
    +
    + sz->endpoint = &(iface_host->endpoint[0].desc);
    + if ((sz->endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
    + != USB_DIR_IN) {
    + err("%s: endpoint doesn't match input device 02%02x",
    + __func__, sz->endpoint->bEndpointAddress);
    + retval = -ENODEV;
    + goto error;
    + }
    +
    + if ((sz->endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
    + != USB_ENDPOINT_XFER_INT) {
    + err("%s: endpoint attributes don't match xfer 02%02x",
    + __func__, sz->endpoint->bmAttributes);
    + retval = -ENODEV;
    + goto error;
    + }
    +
    + if (sz->endpoint->wMaxPacketSize == 0) {
    + err("%s: endpoint message size==0? ", __func__);
    + retval = -ENODEV;
    + goto error;
    + }
    +
    + /***************************************************
    + * Allocate the USB buffer and IRQ URB
    + */
    +
    + sz->buf_in_len = sz->endpoint->wMaxPacketSize;
    + sz->buf_in = usb_buffer_alloc(sz->udev, sz->buf_in_len,
    + GFP_ATOMIC, &sz->dma_in);
    + if (sz->buf_in == NULL)
    + goto error;
    +
    + sz->urb_in = usb_alloc_urb(0, GFP_KERNEL);
    + if (sz->urb_in == NULL)
    + goto error;
    +
    + /***************************************************
    + * Connect this device to the LIRC sub-system
    + */
    +
    + if (lirc_buffer_init(&sz->lirc_buf, sizeof(int),
    + STREAMZAP_BUFFER_SIZE))
    + goto error;
    +
    + if (lirc_buffer_init(&sz->delay_buf, sizeof(int),
    + STREAMZAP_BUFFER_SIZE)) {
    + lirc_buffer_free(&sz->lirc_buf);
    + goto error;
    + }
    +
    + /***************************************************
    + * As required memory is allocated now populate the plugin structure
    + */
    +
    + memset(&sz->plugin, 0, sizeof(sz->plugin));
    +
    + strcpy(sz->plugin.name, DRIVER_NAME);
    + sz->plugin.minor = -1;
    + sz->plugin.sample_rate = 0;
    + sz->plugin.code_length = sizeof(int) * 8;
    + sz->plugin.features = LIRC_CAN_REC_MODE2 | LIRC_CAN_GET_REC_RESOLUTION;
    + sz->plugin.data = sz;
    + sz->plugin.rbuf = &sz->lirc_buf;
    + sz->plugin.set_use_inc = &streamzap_use_inc;
    + sz->plugin.set_use_dec = &streamzap_use_dec;
    + sz->plugin.ioctl = streamzap_ioctl;
    + sz->plugin.dev = &udev->dev;
    + sz->plugin.owner = THIS_MODULE;
    +
    + sz->idle = 1;
    + sz->decoder_state = PulseSpace;
    + init_timer(&sz->delay_timer);
    + sz->delay_timer.function = delay_timeout;
    + sz->delay_timer.data = (unsigned long) sz;
    + sz->timer_running = 0;
    + spin_lock_init(&sz->timer_lock);
    +
    + init_timer(&sz->flush_timer);
    + sz->flush_timer.function = flush_timeout;
    + sz->flush_timer.data = (unsigned long) sz;
    + /***************************************************
    + * Complete final initialisations
    + */
    +
    + usb_fill_int_urb(sz->urb_in, udev,
    + usb_rcvintpipe(udev, sz->endpoint->bEndpointAddress),
    + sz->buf_in, sz->buf_in_len, usb_streamzap_irq, sz,
    + sz->endpoint->bInterval);
    +
    + if (udev->descriptor.iManufacturer
    + && usb_string(udev, udev->descriptor.iManufacturer, buf, 63) > 0)
    + strncpy(name, buf, 128);
    +
    + if (udev->descriptor.iProduct
    + && usb_string(udev, udev->descriptor.iProduct, buf, 63) > 0)
    + snprintf(name, 128, "%s %s", name, buf);
    +
    + printk(KERN_INFO DRIVER_NAME "[%d]: %s on usb%d:%d attached\n",
    + sz->plugin.minor, name,
    + udev->bus->busnum, sz->udev->devnum);
    +
    + usb_set_intfdata(interface, sz);
    +
    + if (lirc_register_plugin(&sz->plugin) < 0) {
    + lirc_buffer_free(&sz->delay_buf);
    + lirc_buffer_free(&sz->lirc_buf);
    + goto error;
    + }
    +
    + return 0;
    +
    +error:
    +
    + /***************************************************
    + * Premise is that a 'goto error' can be invoked from inside the
    + * probe function and all necessary cleanup actions will be taken
    + * including freeing any necessary memory blocks
    + */
    +
    + if (retval == -ENOMEM)
    + err("Out of memory");
    +
    + if (sz) {
    + usb_free_urb(sz->urb_in);
    + usb_buffer_free(udev, sz->buf_in_len, sz->buf_in, sz->dma_in);
    + kfree(sz);
    + }
    +
    + return retval;
    +}
    +
    +static int streamzap_use_inc(void *data)
    +{
    + struct usb_streamzap *sz = data;
    +
    + if (!sz) {
    + dprintk("%s called with no context", -1, __func__);
    + return -EINVAL;
    + }
    + dprintk("set use inc", sz->plugin.minor);
    +
    + while (!lirc_buffer_empty(&sz->lirc_buf))
    + lirc_buffer_remove_1(&sz->lirc_buf);
    + while (!lirc_buffer_empty(&sz->delay_buf))
    + lirc_buffer_remove_1(&sz->delay_buf);
    +
    + sz->flush_timer.expires = jiffies + HZ;
    + sz->flush = 1;
    + add_timer(&sz->flush_timer);
    +
    + sz->urb_in->dev = sz->udev;
    + if (usb_submit_urb(sz->urb_in, GFP_ATOMIC)) {
    + dprintk("open result = -EIO error submitting urb",
    + sz->plugin.minor);
    + return -EIO;
    + }
    + sz->in_use++;
    +
    + return 0;
    +}
    +
    +static void streamzap_use_dec(void *data)
    +{
    + struct usb_streamzap *sz = data;
    +
    + if (!sz) {
    + dprintk("%s called with no context", -1, __func__);
    + return;
    + }
    + dprintk("set use dec", sz->plugin.minor);
    +
    + if (sz->flush) {
    + sz->flush = 0;
    + del_timer_sync(&sz->flush_timer);
    + }
    +
    + stop_timer(sz);
    +
    + usb_kill_urb(sz->urb_in);
    +
    + sz->in_use--;
    +}
    +
    +static int streamzap_ioctl(struct inode *node, struct file *filep,
    + unsigned int cmd, unsigned long arg)
    +{
    + int result;
    +
    + switch (cmd) {
    + case LIRC_GET_REC_RESOLUTION:
    + result = put_user(STREAMZAP_RESOLUTION, (unsigned long *) arg);
    + if (result)
    + return result;
    + break;
    + default:
    + return -ENOIOCTLCMD;
    + }
    + return 0;
    +}
    +
    +/**
    + * streamzap_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 streamzap_read(),
    + * does not provide any way to do this.
    + */
    +static void streamzap_disconnect(struct usb_interface *interface)
    +{
    + struct usb_streamzap *sz;
    + int errnum;
    + int minor;
    +
    + sz = usb_get_intfdata(interface);
    +
    + /*
    + * unregister from the LIRC sub-system
    + */
    +
    + errnum = lirc_unregister_plugin(sz->plugin.minor);
    + if (errnum != 0)
    + dprintk("error in lirc_unregister: (returned %d)",
    + sz->plugin.minor, errnum);
    +
    + lirc_buffer_free(&sz->delay_buf);
    + lirc_buffer_free(&sz->lirc_buf);
    +
    + /*
    + * unregister from the USB sub-system
    + */
    +
    + usb_free_urb(sz->urb_in);
    +
    + usb_buffer_free(sz->udev, sz->buf_in_len, sz->buf_in, sz->dma_in);
    +
    + minor = sz->plugin.minor;
    + kfree(sz);
    +
    + printk(KERN_INFO DRIVER_NAME "[%d]: disconnected\n", minor);
    +}
    +
    +static int streamzap_suspend(struct usb_interface *intf, pm_message_t message)
    +{
    + struct usb_streamzap *sz = usb_get_intfdata(intf);
    +
    + printk(DRIVER_NAME "[%d]: suspend\n", sz->plugin.minor);
    + if (sz->in_use) {
    + if (sz->flush) {
    + sz->flush = 0;
    + del_timer_sync(&sz->flush_timer);
    + }
    +
    + stop_timer(sz);
    +
    + usb_kill_urb(sz->urb_in);
    + }
    + return 0;
    +}
    +
    +static int streamzap_resume(struct usb_interface *intf)
    +{
    + struct usb_streamzap *sz = usb_get_intfdata(intf);
    +
    + while (!lirc_buffer_empty(&sz->lirc_buf))
    + lirc_buffer_remove_1(&sz->lirc_buf);
    + while (!lirc_buffer_empty(&sz->delay_buf))
    + lirc_buffer_remove_1(&sz->delay_buf);
    +
    + if (sz->in_use) {
    + sz->flush_timer.expires = jiffies + HZ;
    + sz->flush = 1;
    + add_timer(&sz->flush_timer);
    +
    + sz->urb_in->dev = sz->udev;
    + if (usb_submit_urb(sz->urb_in, GFP_ATOMIC)) {
    + dprintk("open result = -EIO error submitting urb",
    + sz->plugin.minor);
    + return -EIO;
    + }
    + }
    + return 0;
    +}
    +
    +#ifdef MODULE
    +
    +/**
    + * usb_streamzap_init
    + */
    +static int __init usb_streamzap_init(void)
    +{
    + int result;
    +
    + /* register this driver with the USB subsystem */
    +
    + result = usb_register(&streamzap_driver);
    +
    + if (result) {
    + err("usb_register failed. Error number %d",
    + result);
    + return result;
    + }
    +
    + printk(KERN_INFO DRIVER_NAME " " DRIVER_VERSION " registered\n");
    + return 0;
    +}
    +
    +/**
    + * usb_streamzap_exit
    + */
    +static void __exit usb_streamzap_exit(void)
    +{
    + /* deregister this driver with the USB subsystem */
    + usb_deregister(&streamzap_driver);
    +}
    +
    +
    +module_init(usb_streamzap_init);
    +module_exit(usb_streamzap_exit);
    +
    +MODULE_AUTHOR("Christoph Bartelmus, Greg Wickham, Adrian Dewhurst");
    +MODULE_DESCRIPTION(DRIVER_DESC);
    +MODULE_LICENSE("GPL");
    +
    +module_param(debug, bool, 0644);
    +MODULE_PARM_DESC(debug, "Enable debugging messages");
    +
    +#endif /* MODULE */
    diff --git a/drivers/input/lirc/lirc_ttusbir.c b/drivers/input/lirc/lirc_ttusbir.c
    new file mode 100644
    index 0000000..9ed9c7b
    --- /dev/null
    +++ b/drivers/input/lirc/lirc_ttusbir.c
    @@ -0,0 +1,400 @@
    +/****************************************************************************
    + ** lirc_ttusbir.c ***********************************************************
    + ****************************************************************************
    + *
    + * lirc_ttusbir - LIRC device driver for the TechnoTrend USB IR Receiver
    + *
    + * Copyright (C) 2007 Stefan Macher <st_maker-lirc@yahoo.de>
    + *
    + * This program is free software; you can redistribute it and/or modify
    + * it under the terms of the GNU General Public License as published by
    + * the Free Software Foundation; either version 2 of the License, or
    + * (at your option) any later version.
    + *
    + * This program is distributed in the hope that it will be useful,
    + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    + * GNU General Public License for more details.
    + *
    + * You should have received a copy of the GNU General Public License
    + * along with this program; if not, write to the Free Software
    + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
    + *
    + */
    +
    +/* This LIRC driver provides access to the TechnoTrend USB IR Receiver.
    + * The receiver delivers the IR signal as raw sampled true/false data in
    + * isochronous USB packets each of size 128 byte.
    + * Currently the driver reduces the sampling rate by factor of 8 as this
    + * is still more than enough to decode RC-5 - others should be analyzed.
    + * But the driver does not rely on RC-5 it should be able to decode every
    + * IR signal that is not too fast.
    + */
    +
    +#include <linux/version.h>
    +#include <linux/kernel.h>
    +#include <linux/init.h>
    +#include <linux/module.h>
    +#include <linux/errno.h>
    +#include <linux/slab.h>
    +#include <linux/usb.h>
    +
    +#include "lirc.h"
    +#include "lirc_dev.h"
    +
    +MODULE_DESCRIPTION("TechnoTrend USB IR device driver for LIRC");
    +MODULE_AUTHOR("Stefan Macher (st_maker-lirc@yahoo.de)");
    +MODULE_LICENSE("GPL");
    +
    +/* #define DEBUG */
    +#ifdef DEBUG
    +#define DPRINTK printk
    +#else
    +#define DPRINTK(_x_, a...)
    +#endif
    +
    +/* function declarations */
    +static int probe(struct usb_interface *intf, const struct usb_device_id *id);
    +static void disconnect(struct usb_interface *intf);
    +static void urb_complete(struct urb *urb);
    +static int set_use_inc(void *data);
    +static void set_use_dec(void *data);
    +
    +static int num_urbs = 2;
    +module_param(num_urbs, int, 0444);
    +MODULE_PARM_DESC(num_urbs,
    + "Number of URBs in queue. Try to increase to 4 in case "
    + "of problems (default: 2; minimum: 2)");
    +
    +/* table of devices that work with this driver */
    +static struct usb_device_id device_id_table[] = {
    + /* TechnoTrend USB IR Receiver */
    + { USB_DEVICE(0x0B48, 0x2003) },
    + /* Terminating entry */
    + { }
    +};
    +MODULE_DEVICE_TABLE(usb, device_id_table);
    +
    +/* USB driver definition */
    +static struct usb_driver driver = {
    + .name = "TTUSBIR",
    + .id_table = &(device_id_table[0]),
    + .probe = probe,
    + .disconnect = disconnect,
    +};
    +
    +/* USB device definition */
    +struct ttusbir_device {
    + struct usb_driver *driver;
    + struct usb_device *udev;
    + struct usb_interface *interf;
    + struct usb_class_driver class_driver;
    + unsigned int ifnum; /* Interface number to use */
    + unsigned int alt_setting; /* alternate setting to use */
    + unsigned int endpoint; /* Endpoint to use */
    + struct urb **urb; /* num_urb URB pointers*/
    + char **buffer; /* 128 byte buffer for each URB */
    + struct lirc_buffer rbuf; /* Buffer towards LIRC */
    + struct lirc_plugin plugin;
    + int minor;
    + int last_pulse; /* remembers if last received byte was pulse or space */
    + int last_num; /* remembers how many last bytes appeared */
    + int opened;
    +};
    +
    +/*************************************
    + * LIRC specific functions
    + */
    +static int set_use_inc(void *data)
    +{
    + int i;
    + struct ttusbir_device *ttusbir = data;
    +
    + DPRINTK("Sending first URBs\n");
    + /* @TODO Do I need to check if I am already opened */
    + ttusbir->opened = 1;
    +
    + for (i = 0; i < num_urbs; i++)
    + usb_submit_urb(ttusbir->urb[i], GFP_KERNEL);
    +
    + return 0;
    +}
    +
    +static void set_use_dec(void *data)
    +{
    + struct ttusbir_device *ttusbir = data;
    +
    + DPRINTK("Device closed\n");
    +
    + ttusbir->opened = 0;
    +}
    +
    +/*************************************
    + * USB specific functions
    + */
    +
    +/* This mapping table is used to do a very simple filtering of the
    + * input signal.
    + * For a value with at least 4 bits set it returns 0xFF otherwise
    + * 0x00. For faster IR signals this can not be used. But for RC-5 we
    + * still have about 14 samples per pulse/space, i.e. we sample with 14
    + * times higher frequency than the signal frequency */
    +const unsigned char map_table[] =
    +{
    + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
    + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
    + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
    + 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
    + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
    + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
    + 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
    + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
    + 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
    + 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
    + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
    + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
    + 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
    + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
    + 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
    + 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
    + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
    + 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
    + 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
    + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    + 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
    + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
    +};
    +
    +static void urb_complete(struct urb *urb)
    +{
    + struct ttusbir_device *ttusbir;
    + unsigned char *buf;
    + int i;
    + int l;
    +
    + ttusbir = urb->context;
    +
    + if (!ttusbir->opened)
    + return;
    +
    + buf = (unsigned char *)urb->transfer_buffer;
    +
    + for (i = 0; i < 128; i++) {
    + /* Here we do the filtering and some kind of down sampling */
    + buf[i] = ~map_table[buf[i]];
    + if (ttusbir->last_pulse == buf[i]) {
    + if (ttusbir->last_num < PULSE_MASK/63)
    + ttusbir->last_num++;
    + /* else we are in a idle period and do not need to
    + * increment any longer */
    + } else {
    + l = ttusbir->last_num * 62; /* about 62 = us/byte */
    + if (ttusbir->last_pulse) /* pulse or space? */
    + l |= PULSE_BIT;
    + if (!lirc_buffer_full(&ttusbir->rbuf)) {
    + lirc_buffer_write_1(&ttusbir->rbuf, (void *)&l);
    + wake_up_interruptible(&ttusbir->rbuf.wait_poll);
    + }
    + ttusbir->last_num = 0;
    + ttusbir->last_pulse = buf[i];
    + }
    + }
    + usb_submit_urb(urb, GFP_ATOMIC); /* keep data rolling :-) */
    +}
    +
    +/* Called whenever the USB subsystem thinks we could be the right driver
    + to handle this device
    +*/
    +static int probe(struct usb_interface *intf, const struct usb_device_id *id)
    +{
    + int alt_set, endp;
    + int found = 0;
    + int i, j;
    + int struct_size;
    + struct usb_host_interface *host_interf;
    + struct usb_interface_descriptor *interf_desc;
    + struct usb_host_endpoint *host_endpoint;
    + struct ttusbir_device *ttusbir;
    +
    + DPRINTK("Module ttusbir probe\n");
    +
    + /* To reduce memory fragmentation we use only one allocation */
    + struct_size = sizeof(struct ttusbir_device) +
    + (sizeof(struct urb *) * num_urbs) +
    + (sizeof(char *) * num_urbs) +
    + (num_urbs * 128);
    + ttusbir = kmalloc(struct_size, GFP_KERNEL);
    + if (!ttusbir)
    + return -ENOMEM;
    + memset(ttusbir, 0, struct_size);
    +
    + ttusbir->urb = (struct urb **)((char *)ttusbir +
    + sizeof(struct ttusbir_device));
    + ttusbir->buffer = (char **)((char *)ttusbir->urb +
    + (sizeof(struct urb *) * num_urbs));
    + for (i = 0; i < num_urbs; i++)
    + ttusbir->buffer[i] = (char *)ttusbir->buffer +
    + (sizeof(char *)*num_urbs) + (i * 128);
    +
    + ttusbir->driver = &driver;
    + ttusbir->alt_setting = -1;
    + /* @TODO check if error can be returned */
    + ttusbir->udev = usb_get_dev(interface_to_usbdev(intf));
    + ttusbir->interf = intf;
    + ttusbir->last_pulse = 0x00;
    + ttusbir->last_num = 0;
    +
    + /* Now look for interface setting we can handle
    + We are searching for the alt setting where end point
    + 0x82 has max packet size 16
    + */
    + for (alt_set = 0; alt_set < intf->num_altsetting && !found; alt_set++) {
    + host_interf = &intf->altsetting[alt_set];
    + interf_desc = &host_interf->desc;
    + for (endp = 0; endp < interf_desc->bNumEndpoints; endp++) {
    + host_endpoint = &host_interf->endpoint[endp];
    + if ((host_endpoint->desc.bEndpointAddress == 0x82) &&
    + (host_endpoint->desc.wMaxPacketSize == 0x10)) {
    + ttusbir->alt_setting = alt_set;
    + ttusbir->endpoint = endp;
    + found = 1;
    + break;
    + }
    + }
    + }
    + if (ttusbir->alt_setting != -1)
    + DPRINTK("alt setting: %d\n", ttusbir->alt_setting);
    + else {
    + err("Could not find alternate setting\n");
    + kfree(ttusbir);
    + return -EINVAL;
    + }
    +
    + /* OK lets setup this interface setting */
    + usb_set_interface(ttusbir->udev, 0, ttusbir->alt_setting);
    +
    + /* Store device info in interface structure */
    + usb_set_intfdata(intf, ttusbir);
    +
    + /* Register as a LIRC plugin */
    + if (lirc_buffer_init(&ttusbir->rbuf, sizeof(int), 256) < 0) {
    + err("Could not get memory for LIRC data buffer\n");
    + usb_set_intfdata(intf, NULL);
    + kfree(ttusbir);
    + return -ENOMEM;
    + }
    + strcpy(ttusbir->plugin.name, "TTUSBIR");
    + ttusbir->plugin.minor = -1;
    + ttusbir->plugin.code_length = 1;
    + ttusbir->plugin.sample_rate = 0;
    + ttusbir->plugin.data = ttusbir;
    + ttusbir->plugin.add_to_buf = NULL;
    + ttusbir->plugin.get_queue = NULL;
    + ttusbir->plugin.rbuf = &ttusbir->rbuf;
    + ttusbir->plugin.set_use_inc = set_use_inc;
    + ttusbir->plugin.set_use_dec = set_use_dec;
    + ttusbir->plugin.ioctl = NULL;
    + ttusbir->plugin.fops = NULL;
    + ttusbir->plugin.owner = THIS_MODULE;
    + ttusbir->plugin.features = LIRC_CAN_REC_MODE2;
    + ttusbir->minor = lirc_register_plugin(&ttusbir->plugin);
    + if (ttusbir->minor < 0) {
    + err("Error registering as LIRC plugin\n");
    + usb_set_intfdata(intf, NULL);
    + lirc_buffer_free(&ttusbir->rbuf);
    + kfree(ttusbir);
    + return -EIO;
    + }
    +
    + /* Allocate and setup the URB that we will use to talk to the device */
    + for (i = 0; i < num_urbs; i++) {
    + ttusbir->urb[i] = usb_alloc_urb(8, GFP_KERNEL);
    + if (!ttusbir->urb[i]) {
    + err("Could not allocate memory for the URB\n");
    + for (j = i - 1; j >= 0; j--)
    + kfree(ttusbir->urb[j]);
    + lirc_buffer_free(&ttusbir->rbuf);
    + lirc_unregister_plugin(ttusbir->minor);
    + kfree(ttusbir);
    + usb_set_intfdata(intf, NULL);
    + return -ENOMEM;
    + }
    + ttusbir->urb[i]->dev = ttusbir->udev;
    + ttusbir->urb[i]->context = ttusbir;
    + ttusbir->urb[i]->pipe = usb_rcvisocpipe(ttusbir->udev,
    + ttusbir->endpoint);
    + ttusbir->urb[i]->interval = 1;
    + ttusbir->urb[i]->transfer_flags = URB_ISO_ASAP;
    + ttusbir->urb[i]->transfer_buffer = &ttusbir->buffer[i][0];
    + ttusbir->urb[i]->complete = urb_complete;
    + ttusbir->urb[i]->number_of_packets = 8;
    + ttusbir->urb[i]->transfer_buffer_length = 128;
    + for (j = 0; j < 8; j++) {
    + ttusbir->urb[i]->iso_frame_desc[j].offset = j*16;
    + ttusbir->urb[i]->iso_frame_desc[j].length = 16;
    + }
    + }
    + return 0;
    +}
    +
    +/* Called when the driver is unloaded or the device is unplugged
    + */
    +static void disconnect(struct usb_interface *intf)
    +{
    + int i;
    + struct ttusbir_device *ttusbir;
    +
    + DPRINTK("Module ttusbir disconnect\n");
    +
    + ttusbir = (struct ttusbir_device *) usb_get_intfdata(intf);
    + usb_set_intfdata(intf, NULL);
    + lirc_unregister_plugin(ttusbir->minor);
    + DPRINTK("unregistered\n");
    +
    + for (i = 0; i < num_urbs; i++) {
    + usb_kill_urb(ttusbir->urb[i]);
    + usb_free_urb(ttusbir->urb[i]);
    + }
    + DPRINTK("URBs killed\n");
    + lirc_buffer_free(&ttusbir->rbuf);
    + kfree(ttusbir);
    +}
    +
    +static int ttusbir_init_module(void)
    +{
    + int result;
    +
    + DPRINTK(KERN_DEBUG "Module ttusbir init\n");
    +
    + /* register this driver with the USB subsystem */
    + result = usb_register(&driver);
    + if (result)
    + err("usb_register failed. Error number %d", result);
    + return result;
    +}
    +
    +static void ttusbir_exit_module(void)
    +{
    + printk(KERN_DEBUG "Module ttusbir exit\n");
    + /* deregister this driver with the USB subsystem */
    + usb_deregister(&driver);
    +}
    +
    +#ifdef MODULE
    +module_init(ttusbir_init_module);
    +module_exit(ttusbir_exit_module);
    +
    +#else
    +subsys_initcall(ttusbir_init_module);
    +
    +#endif /* MODULE */
    diff --git a/drivers/input/lirc/lirc_zilog.c b/drivers/input/lirc/lirc_zilog.c
    new file mode 100644
    index 0000000..20e6b27
    --- /dev/null
    +++ b/drivers/input/lirc/lirc_zilog.c
    @@ -0,0 +1,1395 @@
    +/*
    + * i2c IR lirc plugin for devices with zilog IR processors
    + *
    + * Copyright (c) 2000 Gerd Knorr <kraxel@goldbach.in-berlin.de>
    + * modified for PixelView (BT878P+W/FM) by
    + * Michal Kochanowicz <mkochano@pld.org.pl>
    + * Christoph Bartelmus <lirc@bartelmus.de>
    + * modified for KNC ONE TV Station/Anubis Typhoon TView Tuner by
    + * Ulrich Mueller <ulrich.mueller42@web.de>
    + * modified for Asus TV-Box and Creative/VisionTek BreakOut-Box by
    + * Stefan Jahn <stefan@lkcc.org>
    + * modified for inclusion into kernel sources by
    + * Jerome Brock <jbrock@users.sourceforge.net>
    + * modified for Leadtek Winfast PVR2000 by
    + * Thomas Reitmayr (treitmayr@yahoo.com)
    + * modified for Hauppauge PVR-150 IR TX device by
    + * Mark Weaver <mark@npsl.co.uk>
    + * changed name from lirc_pvr150 to lirc_zilog, works on more than pvr-150
    + * Jarod Wilson <jarod@redhat.com>
    + *
    + * parts are cut&pasted from the lirc_i2c.c driver
    + *
    + * This program is free software; you can redistribute it and/or modify
    + * it under the terms of the GNU General Public License as published by
    + * the Free Software Foundation; either version 2 of the License, or
    + * (at your option) any later version.
    + *
    + * This program is distributed in the hope that it will be useful,
    + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    + * GNU General Public License for more details.
    + *
    + * You should have received a copy of the GNU General Public License
    + * along with this program; if not, write to the Free Software
    + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
    + *
    + */
    +
    +
    +#include <linux/version.h>
    +#include <linux/module.h>
    +#include <linux/kmod.h>
    +#include <linux/kernel.h>
    +#include <linux/sched.h>
    +#include <linux/fs.h>
    +#include <linux/poll.h>
    +#include <linux/string.h>
    +#include <linux/timer.h>
    +#include <linux/delay.h>
    +#include <linux/completion.h>
    +#include <linux/errno.h>
    +#include <linux/slab.h>
    +#include <linux/i2c.h>
    +#include <linux/i2c-algo-bit.h>
    +#include <linux/firmware.h>
    +#include <linux/vmalloc.h>
    +
    +#include <linux/mutex.h>
    +#include <linux/kthread.h>
    +
    +#include "lirc_dev.h"
    +#include "lirc.h"
    +
    +struct IR {
    + struct lirc_plugin l;
    +
    + /* Device info */
    + struct mutex lock;
    + int open;
    +
    + /* RX device */
    + struct i2c_client c_rx;
    +
    + /* RX device buffer & lock */
    + struct lirc_buffer buf;
    + struct mutex buf_lock;
    +
    + /* RX polling thread data */
    + struct completion *t_notify;
    + struct completion *t_notify2;
    + int shutdown;
    + struct task_struct *task;
    +
    + /* RX read data */
    + unsigned char b[3];
    +
    + /* TX device */
    + struct i2c_client c_tx;
    + int need_boot;
    +
    + /* # devices, for shutdown */
    + int devs;
    +};
    +
    +/* Minor -> data mapping */
    +static struct IR *ir_devices[MAX_IRCTL_DEVICES];
    +
    +/* Block size for IR transmitter */
    +#define TX_BLOCK_SIZE 99
    +
    +/* Hauppuage IR transmitter data */
    +struct tx_data_struct {
    + /* Boot block */
    + unsigned char *boot_data;
    +
    + /* Start of binary data block */
    + unsigned char *datap;
    +
    + /* End of binary data block */
    + unsigned char *endp;
    +
    + /* Number of installed codesets */
    + unsigned int num_code_sets;
    +
    + /* Pointers to codesets */
    + unsigned char **code_sets;
    +
    + /* Global fixed data template */
    + int fixed[TX_BLOCK_SIZE];
    +};
    +
    +static struct tx_data_struct *tx_data;
    +struct mutex tx_data_lock;
    +
    +/* ----------------------------------------------------------------------- */
    +
    +#define DEVICE_NAME "lirc_zilog"
    +#define zilog_notify(s, args...) printk(KERN_NOTICE KBUILD_MODNAME ": " s, \
    + ## args)
    +#define zilog_error(s, args...) printk(KERN_ERR KBUILD_MODNAME ": " s, ## args)
    +
    +/* ----------------------------------------------------------------------- */
    +/* insmod parameters */
    +
    +static int debug; /* debug output */
    +static int disable_rx; /* disable RX device */
    +static int disable_tx; /* disable TX device */
    +static int minor = -1; /* minor number */
    +
    +#define dprintk(fmt, args...) \
    + do { \
    + if (debug) \
    + printk(KERN_DEBUG DEVICE_NAME ": " fmt, \
    + ## args); \
    + } while (0)
    +
    +/* ----------------------------------------------------------------------- */
    +
    +static int add_to_buf(struct IR *ir)
    +{
    + __u16 code;
    + unsigned char codes[2];
    + unsigned char keybuf[6];
    + int got_data = 0;
    + int ret;
    + int failures = 0;
    + unsigned char sendbuf[1] = { 0 };
    +
    + if (lirc_buffer_full(&ir->buf)) {
    + dprintk("buffer overflow\n");
    + return -EOVERFLOW;
    + }
    +
    + /* service the device as long as it is returning
    + * data and we have space
    + */
    + do {
    + /* Lock i2c bus for the duration. RX/TX chips interfere so
    + this is worth it
    + */
    + mutex_lock(&ir->lock);
    +
    + /* Send random "poll command" (?) Windows driver does this
    + and it is a good point to detect chip failure.
    + */
    + ret = i2c_master_send(&ir->c_rx, sendbuf, 1);
    + if (ret != 1) {
    + zilog_error("i2c_master_send failed with %d\n", ret);
    + if (failures >= 3) {
    + mutex_unlock(&ir->lock);
    + zilog_error("unable to read from the IR chip "
    + "after 3 resets, giving up\n");
    + return ret;
    + }
    +
    + /* Looks like the chip crashed, reset it */
    + zilog_error("polling the IR receiver chip failed, "
    + "trying reset\n");
    +
    + set_current_state(TASK_UNINTERRUPTIBLE);
    + schedule_timeout((100 * HZ + 999) / 1000);
    + ir->need_boot = 1;
    +
    + ++failures;
    + mutex_unlock(&ir->lock);
    + continue;
    + }
    +
    + ret = i2c_master_recv(&ir->c_rx, keybuf, sizeof(keybuf));
    + mutex_unlock(&ir->lock);
    + if (ret != sizeof(keybuf)) {
    + zilog_error("i2c_master_recv failed with %d -- "
    + "keeping last read buffer\n", ret);
    + } else {
    + ir->b[0] = keybuf[3];
    + ir->b[1] = keybuf[4];
    + ir->b[2] = keybuf[5];
    + dprintk("key (0x%02x/0x%02x)\n", ir->b[0], ir->b[1]);
    + }
    +
    + /* key pressed ? */
    + if ((ir->b[0] & 0x80) == 0)
    + return got_data ? 0 : -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(&ir->buf, codes);
    + ++got_data;
    + } while (!lirc_buffer_full(&ir->buf));
    + return 0;
    +}
    +
    +/* Main function of the polling thread -- from lirc_dev.
    + * We don't fit the LIRC model at all anymore. This is horrible, but
    + * basically we have a single RX/TX device with a nasty failure mode
    + * that needs to be accounted for across the pair. lirc lets us provide
    + * fops, but prevents us from using the internal polling, etc. if we do
    + * so. Hence the replication. Might be neater to extend the LIRC model
    + * to account for this but I'd think it's a very special case of seriously
    + * messed up hardware.
    + */
    +static int lirc_thread(void *arg)
    +{
    + struct IR *ir = arg;
    +
    + if (ir->t_notify != NULL)
    + complete(ir->t_notify);
    +
    + dprintk("poll thread started\n");
    +
    + do {
    + if (ir->open) {
    + set_current_state(TASK_INTERRUPTIBLE);
    +
    + /* This is ~113*2 + 24 + jitter (2*repeat gap +
    + code length). We use this interval as the chip
    + resets every time you poll it (bad!). This is
    + therefore just sufficient to catch all of the
    + button presses. It makes the remote much more
    + responsive. You can see the difference by
    + running irw and holding down a button. With
    + 100ms, the old polling interval, you'll notice
    + breaks in the repeat sequence corresponding to
    + lost keypresses.
    + */
    + schedule_timeout((260 * HZ) / 1000);
    + 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 */
    + set_current_state(TASK_INTERRUPTIBLE);
    + schedule_timeout(HZ/2);
    + }
    + } while (!ir->shutdown);
    +
    + if (ir->t_notify2 != NULL)
    + wait_for_completion(ir->t_notify2);
    +
    + ir->task = NULL;
    + if (ir->t_notify != NULL)
    + complete(ir->t_notify);
    +
    + dprintk("poll thread ended\n");
    + return 0;
    +}
    +
    +static int set_use_inc(void *data)
    +{
    + struct IR *ir = data;
    +
    + if (ir->l.owner == NULL || try_module_get(ir->l.owner) == 0)
    + return -ENODEV;
    +
    + /* 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 */
    + if (ir->c_rx.addr)
    + i2c_use_client(&ir->c_rx);
    + if (ir->c_tx.addr)
    + i2c_use_client(&ir->c_tx);
    +
    + return 0;
    +}
    +
    +static void set_use_dec(void *data)
    +{
    + struct IR *ir = data;
    +
    + if (ir->c_rx.addr)
    + i2c_release_client(&ir->c_rx);
    + if (ir->c_tx.addr)
    + i2c_release_client(&ir->c_tx);
    + if (ir->l.owner != NULL)
    + module_put(ir->l.owner);
    +}
    +
    +/* safe read of a uint32 (always network byte order) */
    +static inline int read_uint32(unsigned char **data,
    + unsigned char *endp, unsigned int *val)
    +{
    + if (*data + 4 > endp)
    + return 0;
    + *val = ((*data)[0] << 24) | ((*data)[1] << 16) |
    + ((*data)[2] << 8) | (*data)[3];
    + *data += 4;
    + return 1;
    +}
    +
    +/* safe read of a uint8 */
    +static inline int read_uint8(unsigned char **data,
    + unsigned char *endp, unsigned char *val)
    +{
    + if (*data + 1 > endp)
    + return 0;
    + *val = *((*data)++);
    + return 1;
    +}
    +
    +/* safe skipping of N bytes */
    +static inline int skip(unsigned char **data,
    + unsigned char *endp, unsigned int distance)
    +{
    + if (*data + distance > endp)
    + return 0;
    + *data += distance;
    + return 1;
    +}
    +
    +/* decompress key data into the given buffer */
    +static int get_key_data(unsigned char *buf,
    + unsigned int codeset, unsigned int key)
    +{
    + unsigned char *data, *endp, *diffs, *key_block;
    + unsigned char keys, ndiffs, id;
    + unsigned int base, lim, pos, i;
    +
    + /* Binary search for the codeset */
    + for (base = 0, lim = tx_data->num_code_sets; lim; lim >>= 1) {
    + pos = base + (lim >> 1);
    + data = tx_data->code_sets[pos];
    +
    + if (!read_uint32(&data, tx_data->endp, &i))
    + goto corrupt;
    +
    + if (i == codeset)
    + break;
    + else if (codeset > i) {
    + base = pos + 1;
    + --lim;
    + }
    + }
    + /* Not found? */
    + if (!lim)
    + return -EPROTO;
    +
    + /* Set end of data block */
    + endp = pos < tx_data->num_code_sets - 1 ?
    + tx_data->code_sets[pos + 1] : tx_data->endp;
    +
    + /* Read the block header */
    + if (!read_uint8(&data, endp, &keys) ||
    + !read_uint8(&data, endp, &ndiffs) ||
    + ndiffs > TX_BLOCK_SIZE || keys == 0)
    + goto corrupt;
    +
    + /* Save diffs & skip */
    + diffs = data;
    + if (!skip(&data, endp, ndiffs))
    + goto corrupt;
    +
    + /* Read the id of the first key */
    + if (!read_uint8(&data, endp, &id))
    + goto corrupt;
    +
    + /* Unpack the first key's data */
    + for (i = 0; i < TX_BLOCK_SIZE; ++i) {
    + if (tx_data->fixed[i] == -1) {
    + if (!read_uint8(&data, endp, &buf[i]))
    + goto corrupt;
    + } else {
    + buf[i] = (unsigned char)tx_data->fixed[i];
    + }
    + }
    +
    + /* Early out key found/not found */
    + if (key == id)
    + return 0;
    + if (keys == 1)
    + return -EPROTO;
    +
    + /* Sanity check */
    + key_block = data;
    + if (!skip(&data, endp, (keys - 1) * (ndiffs + 1)))
    + goto corrupt;
    +
    + /* Binary search for the key */
    + for (base = 0, lim = keys - 1; lim; lim >>= 1) {
    + /* Seek to block */
    + unsigned char *key_data;
    + pos = base + (lim >> 1);
    + key_data = key_block + (ndiffs + 1) * pos;
    +
    + if (*key_data == key) {
    + /* skip key id */
    + ++key_data;
    +
    + /* found, so unpack the diffs */
    + for (i = 0; i < ndiffs; ++i) {
    + unsigned char val;
    + if (!read_uint8(&key_data, endp, &val) ||
    + diffs[i] >= TX_BLOCK_SIZE)
    + goto corrupt;
    + buf[diffs[i]] = val;
    + }
    +
    + return 0;
    + } else if (key > *key_data) {
    + base = pos + 1;
    + --lim;
    + }
    + }
    + /* Key not found */
    + return -EPROTO;
    +
    +corrupt:
    + zilog_error("firmware is corrupt\n");
    + return -EFAULT;
    +}
    +
    +/* send a block of data to the IR TX device */
    +static int send_data_block(struct IR *ir, unsigned char *data_block)
    +{
    + int i, j, ret;
    + unsigned char buf[5];
    +
    + for (i = 0; i < TX_BLOCK_SIZE;) {
    + int tosend = TX_BLOCK_SIZE - i;
    + if (tosend > 4)
    + tosend = 4;
    + buf[0] = (unsigned char)(i + 1);
    + for (j = 0; j < tosend; ++j)
    + buf[1 + j] = data_block[i + j];
    + dprintk("%02x %02x %02x %02x %02x",
    + buf[0], buf[1], buf[2], buf[3], buf[4]);
    + ret = i2c_master_send(&ir->c_tx, buf, tosend + 1);
    + if (ret != tosend + 1) {
    + zilog_error("i2c_master_send failed with %d\n", ret);
    + return ret < 0 ? ret : -EFAULT;
    + }
    + i += tosend;
    + }
    + return 0;
    +}
    +
    +/* send boot data to the IR TX device */
    +static int send_boot_data(struct IR *ir)
    +{
    + int ret;
    + unsigned char buf[4];
    +
    + /* send the boot block */
    + ret = send_data_block(ir, tx_data->boot_data);
    + if (ret != 0)
    + return ret;
    +
    + /* kick it off? */
    + buf[0] = 0x00;
    + buf[1] = 0x20;
    + ret = i2c_master_send(&ir->c_tx, buf, 2);
    + if (ret != 2) {
    + zilog_error("i2c_master_send failed with %d\n", ret);
    + return ret < 0 ? ret : -EFAULT;
    + }
    + ret = i2c_master_send(&ir->c_tx, buf, 1);
    + if (ret != 1) {
    + zilog_error("i2c_master_send failed with %d\n", ret);
    + return ret < 0 ? ret : -EFAULT;
    + }
    +
    + /* Here comes the firmware version... (hopefully) */
    + ret = i2c_master_recv(&ir->c_tx, buf, 4);
    + if (ret != 4) {
    + zilog_error("i2c_master_recv failed with %d\n", ret);
    + return 0;
    + }
    + if (buf[0] != 0x80) {
    + zilog_error("unexpected IR TX response: %02x\n", buf[0]);
    + return 0;
    + }
    + zilog_notify("Zilog/Hauppauge IR blaster: firmware version "
    + "%d.%d.%d\n", buf[1], buf[2], buf[3]);
    +
    + return 0;
    +}
    +
    +/* unload "firmware", lock held */
    +static void fw_unload_locked(void)
    +{
    + if (tx_data) {
    + if (tx_data->code_sets)
    + vfree(tx_data->code_sets);
    +
    + if (tx_data->datap)
    + vfree(tx_data->datap);
    +
    + vfree(tx_data);
    + tx_data = NULL;
    + dprintk("successfully unloaded IR blaster firmware\n");
    + }
    +}
    +
    +/* unload "firmware" for the IR TX device */
    +static void fw_unload(void)
    +{
    + mutex_lock(&tx_data_lock);
    + fw_unload_locked();
    + mutex_unlock(&tx_data_lock);
    +}
    +
    +/* load "firmware" for the IR TX device */
    +static int fw_load(struct IR *ir)
    +{
    + int ret;
    + unsigned int i;
    + unsigned char *data, version, num_global_fixed;
    + const struct firmware *fw_entry = NULL;
    +
    + /* Already loaded? */
    + mutex_lock(&tx_data_lock);
    + if (tx_data) {
    + ret = 0;
    + goto out;
    + }
    +
    + /* Request codeset data file */
    + ret = request_firmware(&fw_entry, "haup-ir-blaster.bin", &ir->c_tx.dev);
    + if (ret != 0) {
    + zilog_error("firmware haup-ir-blaster.bin not available "
    + "(%d)\n", ret);
    + ret = ret < 0 ? ret : -EFAULT;
    + goto out;
    + }
    + zilog_notify("firmware of size %zu loaded\n", fw_entry->size);
    +
    + /* Parse the file */
    + tx_data = vmalloc(sizeof(*tx_data));
    + if (tx_data == NULL) {
    + zilog_error("out of memory\n");
    + release_firmware(fw_entry);
    + ret = -ENOMEM;
    + goto out;
    + }
    + tx_data->code_sets = NULL;
    +
    + /* Copy the data so hotplug doesn't get confused and timeout */
    + tx_data->datap = vmalloc(fw_entry->size);
    + if (tx_data->datap == NULL) {
    + zilog_error("out of memory\n");
    + release_firmware(fw_entry);
    + vfree(tx_data);
    + ret = -ENOMEM;
    + goto out;
    + }
    + memcpy(tx_data->datap, fw_entry->data, fw_entry->size);
    + tx_data->endp = tx_data->datap + fw_entry->size;
    + release_firmware(fw_entry); fw_entry = NULL;
    +
    + /* Check version */
    + data = tx_data->datap;
    + if (!read_uint8(&data, tx_data->endp, &version))
    + goto corrupt;
    + if (version != 1) {
    + zilog_error("unsupported code set file version (%u, expected"
    + "1) -- please upgrade to a newer driver",
    + version);
    + fw_unload_locked();
    + ret = -EFAULT;
    + goto out;
    + }
    +
    + /* Save boot block for later */
    + tx_data->boot_data = data;
    + if (!skip(&data, tx_data->endp, TX_BLOCK_SIZE))
    + goto corrupt;
    +
    + if (!read_uint32(&data, tx_data->endp,
    + &tx_data->num_code_sets))
    + goto corrupt;
    +
    + zilog_notify("%u codesets loaded\n", tx_data->num_code_sets);
    +
    + tx_data->code_sets = vmalloc(
    + tx_data->num_code_sets * sizeof(char *));
    + if (tx_data->code_sets == NULL) {
    + fw_unload_locked();
    + ret = -ENOMEM;
    + goto out;
    + }
    +
    + for (i = 0; i < TX_BLOCK_SIZE; ++i)
    + tx_data->fixed[i] = -1;
    +
    + /* Read global fixed data template */
    + if (!read_uint8(&data, tx_data->endp, &num_global_fixed) ||
    + num_global_fixed > TX_BLOCK_SIZE)
    + goto corrupt;
    + for (i = 0; i < num_global_fixed; ++i) {
    + unsigned char pos, val;
    + if (!read_uint8(&data, tx_data->endp, &pos) ||
    + !read_uint8(&data, tx_data->endp, &val) ||
    + pos >= TX_BLOCK_SIZE)
    + goto corrupt;
    + tx_data->fixed[pos] = (int)val;
    + }
    +
    + /* Filch out the position of each code set */
    + for (i = 0; i < tx_data->num_code_sets; ++i) {
    + unsigned int id;
    + unsigned char keys;
    + unsigned char ndiffs;
    +
    + /* Save the codeset position */
    + tx_data->code_sets[i] = data;
    +
    + /* Read header */
    + if (!read_uint32(&data, tx_data->endp, &id) ||
    + !read_uint8(&data, tx_data->endp, &keys) ||
    + !read_uint8(&data, tx_data->endp, &ndiffs) ||
    + ndiffs > TX_BLOCK_SIZE || keys == 0)
    + goto corrupt;
    +
    + /* skip diff positions */
    + if (!skip(&data, tx_data->endp, ndiffs))
    + goto corrupt;
    +
    + /* After the diffs we have the first key id + data -
    + global fixed */
    + if (!skip(&data, tx_data->endp,
    + 1 + TX_BLOCK_SIZE - num_global_fixed))
    + goto corrupt;
    +
    + /* Then we have keys-1 blocks of key id+diffs */
    + if (!skip(&data, tx_data->endp,
    + (ndiffs + 1) * (keys - 1)))
    + goto corrupt;
    + }
    + ret = 0;
    + goto out;
    +
    +corrupt:
    + zilog_error("firmware is corrupt\n");
    + fw_unload_locked();
    + ret = -EFAULT;
    +
    +out:
    + mutex_unlock(&tx_data_lock);
    + return ret;
    +}
    +
    +/* initialise the IR TX device */
    +static int tx_init(struct IR *ir)
    +{
    + int ret;
    +
    + /* Load 'firmware' */
    + ret = fw_load(ir);
    + if (ret != 0)
    + return ret;
    +
    + /* Send boot block */
    + ret = send_boot_data(ir);
    + if (ret != 0)
    + return ret;
    + ir->need_boot = 0;
    +
    + /* Looks good */
    + return 0;
    +}
    +
    +/* do nothing stub to make LIRC happy */
    +static loff_t lseek(struct file *filep, loff_t offset, int orig)
    +{
    + return -ESPIPE;
    +}
    +
    +/* copied from lirc_dev */
    +static ssize_t read(struct file *filep, char *outbuf, size_t n, loff_t *ppos)
    +{
    + struct IR *ir = (struct IR *)filep->private_data;
    + unsigned char buf[ir->buf.chunk_size];
    + int ret = 0, written = 0;
    + DECLARE_WAITQUEUE(wait, current);
    +
    + dprintk("read called\n");
    + if (ir->c_rx.addr == 0)
    + return -ENODEV;
    +
    + if (mutex_lock_interruptible(&ir->buf_lock))
    + return -ERESTARTSYS;
    +
    + if (n % ir->buf.chunk_size) {
    + dprintk("read result = -EINVAL\n");
    + mutex_unlock(&ir->buf_lock);
    + 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);
    + set_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 < n && ret == 0) {
    + if (lirc_buffer_empty(&ir->buf)) {
    + /* According to the read(2) man page, 'written' can be
    + * returned as less than 'n', instead of blocking
    + * again, returning -EWOULDBLOCK, or returning
    + * -ERESTARTSYS */
    + if (written)
    + break;
    + if (filep->f_flags & O_NONBLOCK) {
    + ret = -EWOULDBLOCK;
    + break;
    + }
    + if (signal_pending(current)) {
    + ret = -ERESTARTSYS;
    + break;
    + }
    + schedule();
    + set_current_state(TASK_INTERRUPTIBLE);
    + } else {
    + lirc_buffer_read_1(&ir->buf, buf);
    + ret = copy_to_user((void *)outbuf+written, buf,
    + ir->buf.chunk_size);
    + written += ir->buf.chunk_size;
    + }
    + }
    +
    + remove_wait_queue(&ir->buf.wait_poll, &wait);
    + set_current_state(TASK_RUNNING);
    + mutex_unlock(&ir->buf_lock);
    +
    + dprintk("read result = %s (%d)\n",
    + ret ? "-EFAULT" : "OK", ret);
    +
    + return ret ? ret : written;
    +}
    +
    +/* send a keypress to the IR TX device */
    +static int send_code(struct IR *ir, unsigned int code, unsigned int key)
    +{
    + unsigned char data_block[TX_BLOCK_SIZE];
    + unsigned char buf[2];
    + int i, ret;
    +
    + /* Get data for the codeset/key */
    + ret = get_key_data(data_block, code, key);
    +
    + if (ret == -EPROTO) {
    + zilog_error("failed to get data for code %u, key %u -- check "
    + "lircd.conf entries\n", code, key);
    + return ret;
    + } else if (ret != 0)
    + return ret;
    +
    + /* Send the data block */
    + ret = send_data_block(ir, data_block);
    + if (ret != 0)
    + return ret;
    +
    + /* Send data block length? */
    + buf[0] = 0x00;
    + buf[1] = 0x40;
    + ret = i2c_master_send(&ir->c_tx, buf, 2);
    + if (ret != 2) {
    + zilog_error("i2c_master_send failed with %d\n", ret);
    + return ret < 0 ? ret : -EFAULT;
    + }
    + ret = i2c_master_send(&ir->c_tx, buf, 1);
    + if (ret != 1) {
    + zilog_error("i2c_master_send failed with %d\n", ret);
    + return ret < 0 ? ret : -EFAULT;
    + }
    +
    + /* Send finished download? */
    + ret = i2c_master_recv(&ir->c_tx, buf, 1);
    + if (ret != 1) {
    + zilog_error("i2c_master_recv failed with %d\n", ret);
    + return ret < 0 ? ret : -EFAULT;
    + }
    + if (buf[0] != 0xA0) {
    + zilog_error("unexpected IR TX response #1: %02x\n",
    + buf[0]);
    + return ret < 0 ? ret : -EFAULT;
    + }
    +
    + /* Send prepare command? */
    + buf[0] = 0x00;
    + buf[1] = 0x80;
    + ret = i2c_master_send(&ir->c_tx, buf, 2);
    + if (ret != 2) {
    + zilog_error("i2c_master_send failed with %d\n", ret);
    + return ret < 0 ? ret : -EFAULT;
    + }
    +
    + /* This bit NAKs until the device is ready, so we retry it
    + sleeping a bit each time. This seems to be what the windows
    + driver does, approximately.
    + Try for up to 1s.
    + */
    + for (i = 0; i < 20; ++i) {
    + set_current_state(TASK_UNINTERRUPTIBLE);
    + schedule_timeout((50 * HZ + 999) / 1000);
    + ret = i2c_master_send(&ir->c_tx, buf, 1);
    + if (ret == 1)
    + break;
    + dprintk("NAK expected: i2c_master_send "
    + "failed with %d (try %d)\n", ret, i+1);
    + }
    + if (ret != 1) {
    + zilog_error("IR TX chip never got ready: last i2c_master_send "
    + "failed with %d\n", ret);
    + return ret < 0 ? ret : -EFAULT;
    + }
    +
    + /* Seems to be an 'ok' response */
    + i = i2c_master_recv(&ir->c_tx, buf, 1);
    + if (i != 1) {
    + zilog_error("i2c_master_recv failed with %d\n", ret);
    + return ret < 0 ? ret : -EFAULT;
    + }
    + if (buf[0] != 0x80) {
    + zilog_error("unexpected IR TX response #2: %02x\n", buf[0]);
    + return -EFAULT;
    + }
    +
    + /* Oh good, it worked */
    + dprintk("sent code %u, key %u\n", code, key);
    + return 0;
    +}
    +
    +/*
    + * Write a code to the device. We take in a 32-bit number (an int) and then
    + * decode this to a codeset/key index. The key data is then decompressed and
    + * sent to the device. We have a spin lock as per i2c documentation to prevent
    + * multiple concurrent sends which would probably cause the device to explode.
    + */
    +static ssize_t write(struct file *filep, const char *buf, size_t n,
    + loff_t *ppos)
    +{
    + struct IR *ir = (struct IR *)filep->private_data;
    + size_t i;
    + int failures = 0;
    +
    + if (ir->c_tx.addr == 0)
    + return -ENODEV;
    +
    + /* Validate user parameters */
    + if (n % sizeof(int))
    + return -EINVAL;
    +
    + /* Lock i2c bus for the duration */
    + mutex_lock(&ir->lock);
    +
    + /* Send each keypress */
    + for (i = 0; i < n;) {
    + int ret = 0;
    + int command;
    +
    + if (copy_from_user(&command, buf + i, sizeof(command))) {
    + mutex_unlock(&ir->lock);
    + return -EFAULT;
    + }
    +
    + /* Send boot data first if required */
    + if (ir->need_boot == 1) {
    + ret = send_boot_data(ir);
    + if (ret == 0)
    + ir->need_boot = 0;
    + }
    +
    + /* Send the code */
    + if (ret == 0) {
    + ret = send_code(ir, (unsigned)command >> 16,
    + (unsigned)command & 0xFFFF);
    + if (ret == -EPROTO) {
    + mutex_unlock(&ir->lock);
    + return ret;
    + }
    + }
    +
    + /* Hmm, a failure. If we've had a few then give up, otherwise
    + try a reset
    + */
    + if (ret != 0) {
    + /* Looks like the chip crashed, reset it */
    + zilog_error("sending to the IR transmitter chip "
    + "failed, trying reset\n");
    +
    + if (failures >= 3) {
    + zilog_error("unable to send to the IR chip "
    + "after 3 resets, giving up\n");
    + mutex_unlock(&ir->lock);
    + return ret;
    + }
    + set_current_state(TASK_UNINTERRUPTIBLE);
    + schedule_timeout((100 * HZ + 999) / 1000);
    + ir->need_boot = 1;
    + ++failures;
    + } else
    + i += sizeof(int);
    + }
    +
    + /* Release i2c bus */
    + mutex_unlock(&ir->lock);
    +
    + /* All looks good */
    + return n;
    +}
    +
    +/* copied from lirc_dev */
    +static unsigned int poll(struct file *filep, poll_table *wait)
    +{
    + struct IR *ir = (struct IR *)filep->private_data;
    + unsigned int ret;
    +
    + dprintk("poll called\n");
    + if (ir->c_rx.addr == 0)
    + return -ENODEV;
    +
    + mutex_lock(&ir->buf_lock);
    +
    + poll_wait(filep, &ir->buf.wait_poll, wait);
    +
    + dprintk("poll result = %s\n",
    + lirc_buffer_empty(&ir->buf) ? "0" : "POLLIN|POLLRDNORM");
    +
    + ret = lirc_buffer_empty(&ir->buf) ? 0 : (POLLIN|POLLRDNORM);
    +
    + mutex_unlock(&ir->buf_lock);
    + return ret;
    +}
    +
    +static int ioctl(struct inode *node, struct file *filep, unsigned int cmd,
    + unsigned long arg)
    +{
    + struct IR *ir = (struct IR *)filep->private_data;
    + int result;
    + unsigned long mode, features = 0;
    +
    + if (ir->c_rx.addr != 0)
    + features |= LIRC_CAN_REC_LIRCCODE;
    + if (ir->c_tx.addr != 0)
    + features |= LIRC_CAN_SEND_PULSE;
    +
    + switch (cmd) {
    + case LIRC_GET_LENGTH:
    + result = put_user((unsigned long)13,
    + (unsigned long *)arg);
    + break;
    + case LIRC_GET_FEATURES:
    + result = put_user(features, (unsigned long *) arg);
    + if (result)
    + return result;
    + break;
    + case LIRC_GET_REC_MODE:
    + if (!(features&LIRC_CAN_REC_MASK))
    + return -ENOSYS;
    +
    + result = put_user(LIRC_REC2MODE
    + (features&LIRC_CAN_REC_MASK),
    + (unsigned long *)arg);
    + break;
    + case LIRC_SET_REC_MODE:
    + if (!(features&LIRC_CAN_REC_MASK))
    + return -ENOSYS;
    +
    + result = get_user(mode, (unsigned long *)arg);
    + if (!result && !(LIRC_MODE2REC(mode) & features))
    + result = -EINVAL;
    + break;
    + case LIRC_GET_SEND_MODE:
    + if (!(features&LIRC_CAN_SEND_MASK))
    + return -ENOSYS;
    +
    + result = put_user(LIRC_MODE_PULSE, (unsigned long *) arg);
    + if (result)
    + return result;
    + break;
    + case LIRC_SET_SEND_MODE:
    + if (!(features&LIRC_CAN_SEND_MASK))
    + return -ENOSYS;
    +
    + result = get_user(mode, (unsigned long *) arg);
    + if (result)
    + return result;
    + if (mode != LIRC_MODE_PULSE)
    + return -EINVAL;
    + break;
    + default:
    + return -ENOIOCTLCMD;
    + }
    + return 0;
    +}
    +
    +/* Open the IR device. Get hold of our IR structure and
    + stash it in private_data for the file */
    +static int open(struct inode *node, struct file *filep)
    +{
    + struct IR *ir;
    + int ret;
    +
    + /* find our IR struct */
    + unsigned minor = MINOR(node->i_rdev);
    + if (minor >= MAX_IRCTL_DEVICES) {
    + dprintk("minor %d: open result = -ENODEV\n",
    + minor);
    + return -ENODEV;
    + }
    + ir = ir_devices[minor];
    +
    + /* increment in use count */
    + mutex_lock(&ir->lock);
    + ++ir->open;
    + ret = set_use_inc(ir);
    + if (ret != 0) {
    + --ir->open;
    + mutex_unlock(&ir->lock);
    + return ret;
    + }
    + mutex_unlock(&ir->lock);
    +
    + /* stash our IR struct */
    + filep->private_data = ir;
    +
    + return 0;
    +}
    +
    +/* Close the IR device */
    +static int close(struct inode *node, struct file *filep)
    +{
    + /* find our IR struct */
    + struct IR *ir = (struct IR *)filep->private_data;
    + if (ir == NULL) {
    + zilog_error("close: no private_data attached to the file!\n");
    + return -ENODEV;
    + }
    +
    + /* decrement in use count */
    + mutex_lock(&ir->lock);
    + --ir->open;
    + set_use_dec(ir);
    + mutex_unlock(&ir->lock);
    +
    + return 0;
    +}
    +
    +static struct lirc_plugin lirc_template = {
    + .name = "lirc_zilog",
    + .set_use_inc = set_use_inc,
    + .set_use_dec = set_use_dec,
    + .owner = THIS_MODULE
    +};
    +
    +/* ----------------------------------------------------------------------- */
    +
    +static int ir_attach(struct i2c_adapter *adap, int have_rx, int have_tx);
    +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 = {
    + .driver = {
    + .owner = THIS_MODULE,
    + .name = "i2c ir driver",
    + },
    + .attach_adapter = ir_probe,
    + .detach_client = ir_detach,
    + .command = ir_command,
    +};
    +
    +static struct i2c_client client_template = {
    + .name = "unset",
    + .driver = &driver
    +};
    +
    +static struct file_operations lirc_fops = {
    + .llseek = lseek,
    + .read = read,
    + .write = write,
    + .poll = poll,
    + .ioctl = ioctl,
    + .open = open,
    + .release = close
    +};
    +
    +static int i2c_attach(struct i2c_client *client, struct IR *ir)
    +{
    + int ret;
    +
    + i2c_set_clientdata(client, ir);
    +
    + ret = i2c_attach_client(client);
    + if (ret != 0) {
    + client->addr = 0;
    + return ret;
    + }
    + if (!i2c_use_client(client)) {
    + i2c_detach_client(client);
    + client->addr = 0;
    + return -EFAULT;
    + }
    + ++ir->devs;
    + return 0;
    +}
    +
    +static int ir_attach(struct i2c_adapter *adap, int have_rx, int have_tx)
    +{
    + struct IR *ir;
    + int ret, i;
    +
    + printk("lirc_zilog: chip found with %s\n",
    + have_rx && have_tx ? "RX and TX" :
    + have_rx ? "RX only" : "TX only");
    +
    + ir = kmalloc(sizeof(struct IR), GFP_KERNEL);
    + if (ir == NULL)
    + return -ENOMEM;
    + if (lirc_buffer_init(&ir->buf, 2, BUFLEN/2) != 0) {
    + kfree(ir);
    + return -ENOMEM;
    + }
    + mutex_init(&ir->lock);
    + mutex_init(&ir->buf_lock);
    + ir->open = 0;
    + ir->devs = 0;
    + ir->task = NULL;
    + ir->need_boot = 1;
    + ir->shutdown = 0;
    + ir->t_notify = ir->t_notify2 = NULL;
    + for (i = 0; i < sizeof(ir->b); ++i)
    + ir->b[0] = 0;
    +
    + memcpy(&ir->l, &lirc_template, sizeof(struct lirc_plugin));
    + ir->l.minor = -1;
    +
    + /* initialise RX device */
    + client_template.adapter = adap;
    + memcpy(&ir->c_rx, &client_template, sizeof(struct i2c_client));
    + if (have_rx) {
    + DECLARE_COMPLETION(tn);
    +
    + /* I2C attach to device */
    + ir->c_rx.addr = 0x71;
    + strncpy(ir->c_rx.name, "Zilog/Hauppauge RX", I2C_NAME_SIZE);
    + ret = i2c_attach(&ir->c_rx, ir);
    + if (ret != 0)
    + goto err;
    +
    + /* try to fire up polling thread */
    + ir->t_notify = &tn;
    + ir->task = kthread_run(lirc_thread, ir, "lirc_zilog");
    + ret = PTR_ERR(ir->task);
    + if (ret <= 0) {
    + zilog_error("lirc_register_plugin: cannot run "
    + "poll thread\n");
    + goto err;
    + }
    + wait_for_completion(&tn);
    + ir->t_notify = NULL;
    + }
    +
    + /* initialise TX device */
    + memcpy(&ir->c_tx, &client_template, sizeof(struct i2c_client));
    + if (have_tx) {
    + /* I2C attach to device */
    + ir->c_tx.addr = 0x70;
    + strncpy(ir->c_tx.name, "Zilog/Hauppauge TX", I2C_NAME_SIZE);
    + ret = i2c_attach(&ir->c_tx, ir);
    + if (ret != 0)
    + goto err;
    + }
    +
    + /* set lirc_dev stuff */
    + ir->l.code_length = 13;
    + ir->l.rbuf = &ir->buf;
    + ir->l.fops = &lirc_fops;
    + ir->l.data = ir;
    + ir->l.minor = minor;
    + ir->l.sample_rate = 0;
    +
    + /* register with lirc */
    + ir->l.minor = lirc_register_plugin(&ir->l);
    + if (ir->l.minor < 0 || ir->l.minor >= MAX_IRCTL_DEVICES) {
    + zilog_error("ir_attach: \"minor\" must be between 0 and %d "
    + "(%d)!\n", MAX_IRCTL_DEVICES-1, ir->l.minor);
    + ret = -EBADRQC;
    + goto err;
    + }
    +
    + /* store this for getting back in open() later on */
    + ir_devices[ir->l.minor] = ir;
    +
    + /* if we have the tx device, load the 'firmware'. We do this
    + after registering with lirc as otherwise hotplug seems to take
    + 10s to create the lirc device.
    + */
    + if (have_tx) {
    + /* Special TX init */
    + ret = tx_init(ir);
    + if (ret != 0)
    + goto err;
    + }
    + return 0;
    +
    +err:
    + /* undo everything, hopefully... */
    + if (ir->c_rx.addr)
    + ir_detach(&ir->c_rx);
    + if (ir->c_tx.addr)
    + ir_detach(&ir->c_tx);
    + return ret;
    +}
    +
    +static int ir_detach(struct i2c_client *client)
    +{
    + struct IR *ir = i2c_get_clientdata(client);
    + mutex_lock(&ir->lock);
    +
    + if (client == &ir->c_rx) {
    + DECLARE_COMPLETION(tn);
    + DECLARE_COMPLETION(tn2);
    +
    + /* end up polling thread */
    + if (ir->task && !IS_ERR(ir->task)) {
    + ir->t_notify = &tn;
    + ir->t_notify2 = &tn2;
    + ir->shutdown = 1;
    + wake_up_process(ir->task);
    + complete(&tn2);
    + wait_for_completion(&tn);
    + ir->t_notify = NULL;
    + ir->t_notify2 = NULL;
    + }
    +
    + /* unregister device */
    + i2c_detach_client(&ir->c_rx);
    + } else if (client == &ir->c_tx) {
    + i2c_detach_client(&ir->c_tx);
    + } else {
    + mutex_unlock(&ir->lock);
    + zilog_error("ir_detach: detached from something we didn't "
    + "attach to\n");
    + return -ENODEV;
    + }
    +
    + --ir->devs;
    + if (ir->devs < 0) {
    + mutex_unlock(&ir->lock);
    + zilog_error("ir_detach: invalid device count\n");
    + return -ENODEV;
    + } else if (ir->devs == 0) {
    + /* unregister lirc plugin */
    + if (ir->l.minor >= 0 && ir->l.minor < MAX_IRCTL_DEVICES) {
    + lirc_unregister_plugin(ir->l.minor);
    + ir_devices[ir->l.minor] = NULL;
    + }
    +
    + /* free memory */
    + lirc_buffer_free(&ir->buf);
    + mutex_unlock(&ir->lock);
    + kfree(ir);
    + return 0;
    + }
    + mutex_unlock(&ir->lock);
    + return 0;
    +}
    +
    +static int ir_probe(struct i2c_adapter *adap)
    +{
    + struct i2c_client c;
    + char buf;
    +
    + if (adap->id == I2C_HW_B_BT848 ||
    + adap->id == I2C_HW_B_CX2341X) {
    + int have_rx = 0, have_tx = 0;
    +
    + /*
    + * The external IR receiver is at i2c address 0x71.
    + * The IR transmitter is at 0x70.
    + */
    + memset(&c, 0, sizeof(c));
    + c.adapter = adap;
    + c.addr = 0x70;
    +
    + if (!disable_rx) {
    + if (i2c_master_recv(&c, &buf, 1) == 1)
    + have_rx = 1;
    + dprintk("probe 0x70 @ %s: %s\n",
    + adap->name,
    + have_rx ? "yes" : "no");
    + }
    +
    + if (!disable_tx) {
    + c.addr = 0x71;
    + if (i2c_master_recv(&c, &buf, 1) == 1)
    + have_tx = 1;
    + dprintk("probe 0x71 @ %s: %s\n",
    + adap->name,
    + have_tx ? "yes" : "no");
    + }
    +
    + if (have_rx || have_tx)
    + return ir_attach(adap, have_rx, have_tx);
    + else
    + zilog_error("%s: no devices found\n", adap->name);
    + }
    +
    + return 0;
    +}
    +
    +static int ir_command(struct i2c_client *client, unsigned int cmd, void *arg)
    +{
    + /* nothing */
    + return 0;
    +}
    +
    +/* ----------------------------------------------------------------------- */
    +#ifdef MODULE
    +
    +int init_module(void)
    +{
    + mutex_init(&tx_data_lock);
    + request_module("ivtv");
    + request_module("firmware_class");
    + i2c_add_driver(&driver);
    + return 0;
    +}
    +
    +void cleanup_module(void)
    +{
    + i2c_del_driver(&driver);
    + /* if loaded */
    + fw_unload();
    +}
    +
    +MODULE_DESCRIPTION("Zilog/Hauppauge infrared transmitter driver (i2c stack)");
    +MODULE_AUTHOR("Gerd Knorr, Michal Kochanowicz, Christoph Bartelmus, "
    + "Ulrich Mueller, Stefan Jahn, Jerome Brock, Mark Weaver");
    +MODULE_LICENSE("GPL");
    +/* for compat with old name, which isn't all that accurate anymore */
    +MODULE_ALIAS("lirc_pvr150");
    +
    +module_param(minor, int, 0444);
    +MODULE_PARM_DESC(minor, "Preferred minor device number");
    +
    +module_param(debug, bool, 0644);
    +MODULE_PARM_DESC(debug, "Enable debugging messages");
    +
    +module_param(disable_rx, bool, 0644);
    +MODULE_PARM_DESC(disable_rx, "Disable the IR receiver device");
    +
    +module_param(disable_tx, bool, 0644);
    +MODULE_PARM_DESC(disable_tx, "Disable the IR transmitter device");
    +
    +#endif /* MODULE */
    +
    +/*
    + * Overrides for Emacs so that we follow Linus's tabbing style.
    + * ---------------------------------------------------------------------------
    + * Local variables:
    + * c-basic-offset: 8
    + * End:
    + */
    --
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/
    \
     
     \ /
      Last update: 2008-09-09 06:11    [from the cache]
    ©2003-2011 Jasper Spaans