lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:	Tue,  9 Sep 2008 00:05:52 -0400
From:	Jarod Wilson <jwilson@...hat.com>
To:	linux-kernel@...r.kernel.org
Cc:	Jarod Wilson <jwilson@...hat.com>, Jarod Wilson <jarod@...hat.com>,
	Janne Grunau <j@...nau.net>,
	Christoph Bartelmus <lirc@...telmus.de>
Subject: [PATCH 07/18] lirc driver for the CommandIR USB Transceiver

Signed-off-by: Jarod Wilson <jarod@...hat.com>
Signed-off-by: Janne Grunau <j@...nau.net>
CC: Christoph Bartelmus <lirc@...telmus.de>
---
 drivers/input/lirc/Kconfig      |    7 +
 drivers/input/lirc/Makefile     |    1 +
 drivers/input/lirc/commandir.c  |  982 +++++++++++++++++++++++++++++++++++++++
 drivers/input/lirc/commandir.h  |   68 +++
 drivers/input/lirc/lirc_cmdir.c |  596 ++++++++++++++++++++++++
 drivers/input/lirc/lirc_cmdir.h |   25 +
 6 files changed, 1679 insertions(+), 0 deletions(-)
 create mode 100644 drivers/input/lirc/commandir.c
 create mode 100644 drivers/input/lirc/commandir.h
 create mode 100644 drivers/input/lirc/lirc_cmdir.c
 create mode 100644 drivers/input/lirc/lirc_cmdir.h

diff --git a/drivers/input/lirc/Kconfig b/drivers/input/lirc/Kconfig
index e17b39a..67802fe 100644
--- a/drivers/input/lirc/Kconfig
+++ b/drivers/input/lirc/Kconfig
@@ -25,6 +25,13 @@ config LIRC_ATIUSB
 	help
 	  Driver for the ATI USB RF remote receiver
 
+config LIRC_CMDIR
+	tristate "CommandIR USB Transceiver"
+	default n
+	depends on LIRC_DEV
+	help
+	  Driver for the CommandIR USB Transceiver
+
 config LIRC_I2C
 	tristate "I2C Based IR Receivers"
 	default n
diff --git a/drivers/input/lirc/Makefile b/drivers/input/lirc/Makefile
index ba8d445..86df97d 100644
--- a/drivers/input/lirc/Makefile
+++ b/drivers/input/lirc/Makefile
@@ -7,6 +7,7 @@ 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_CMDIR)	+= lirc_cmdir.o
 obj-$(CONFIG_LIRC_I2C)		+= lirc_i2c.o
 obj-$(CONFIG_LIRC_MCEUSB)	+= lirc_mceusb.o
 obj-$(CONFIG_LIRC_MCEUSB2)	+= lirc_mceusb2.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_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
-- 
1.6.0.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ