I would like to submit this driver for inclusion as a usb/misc driver
for 2.6.0-test5

The Lego usb IR tower is used with Lego Mindstorms kits and this is used
by a number of Universities (including mine) and schools for teaching
computer science, robotics, software engineering, etc.

It was in 2.4, a one-shot interrupt driver and so was quite unusual in
that respect (one-shot as a concept is apparently no longer available in
2.6 as such). Two principle Lego RCX development environments have been
modified to work with the driver (LeJOS and NQC). It has the unofficial
support of Lego (they were reviewing their licensing for their SDK).

Comments would be gratefully received.

Thanks

David


-- 
David Glance <[EMAIL PROTECTED]>
diff -Nru a/arch/i386/defconfig b/arch/i386/defconfig
--- a/arch/i386/defconfig	2003-08-23 07:57:48.000000000 +0800
+++ b/arch/i386/defconfig	2003-09-03 13:13:14.688314360 +0800
@@ -1080,6 +1080,7 @@
 # CONFIG_USB_TIGL is not set
 # CONFIG_USB_AUERSWALD is not set
 # CONFIG_USB_RIO500 is not set
+# CONFIG_USB_LEGOTOWER is not set
 # CONFIG_USB_BRLVGER is not set
 # CONFIG_USB_LCD is not set
 # CONFIG_USB_TEST is not set
diff -Nru a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig
--- a/drivers/usb/misc/Kconfig	2003-08-23 08:01:28.000000000 +0800
+++ b/drivers/usb/misc/Kconfig	2003-09-03 13:17:04.980304656 +0800
@@ -66,6 +66,18 @@
 	  The module will be called rio500. If you want to compile it as
 	  a module, say M here and read <file:Documentation/modules.txt>.
 
+config USB_LEGOTOWER
+	tristate "USB Lego Infrared Tower support (EXPERIMENTAL)"
+	depends on USB && EXPERIMENTAL
+	help
+	  Say Y here if you want to connect a USB Lego Infrared Tower to your
+	  computer's USB port.
+
+	  This code is also available as a module ( = code which can be
+	  inserted in and removed from the running kernel whenever you want).
+	  The module will be called legousbtower. If you want to compile it as
+	  a module, say M here and read <file:Documentation/modules.txt>.
+
 config USB_BRLVGER
 	tristate "Tieman Voyager USB Braille display support (EXPERIMENTAL)"
 	depends on USB && EXPERIMENTAL
diff -Nru a/drivers/usb/Makefile b/drivers/usb/Makefile
--- a/drivers/usb/Makefile	2003-08-23 08:00:45.000000000 +0800
+++ b/drivers/usb/Makefile	2003-09-03 13:19:48.176495064 +0800
@@ -59,4 +59,6 @@
 obj-$(CONFIG_USB_TEST)		+= misc/
 obj-$(CONFIG_USB_TIGL)		+= misc/
 obj-$(CONFIG_USB_USS720)	+= misc/
+obj-$(CONFIG_USB_LEGOTOWER)	+= misc/
+
 
diff -Nru a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile
--- a/drivers/usb/misc/Makefile	2003-08-23 07:56:31.000000000 +0800
+++ b/drivers/usb/misc/Makefile	2003-09-03 13:18:45.676996432 +0800
@@ -12,3 +12,4 @@
 obj-$(CONFIG_USB_TEST)		+= usbtest.o
 obj-$(CONFIG_USB_TIGL)		+= tiglusb.o
 obj-$(CONFIG_USB_USS720)	+= uss720.o
+obj-$(CONFIG_USB_LEGOTOWER)	+= legousbtower.o
diff -Nru a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c
--- a/drivers/usb/misc/legousbtower.c	1970-01-01 08:00:00.000000000 +0800
+++ b/drivers/usb/misc/legousbtower.c	2003-09-03 13:21:17.157967824 +0800
@@ -0,0 +1,1046 @@
+/*
+ * LEGO USB Tower driver - 0.5
+ *
+ * Copyright (c) 2001 Juergen Stuber <[EMAIL PROTECTED]>
+ *
+ *	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.
+ *
+ * derived from USB Skeleton driver - 0.5
+ * Copyright (c) 2001 Greg Kroah-Hartman ([EMAIL PROTECTED])
+ *
+ * History:
+ *
+ * 2001-10-13 - 0.1 js
+ *   - first version
+ * 2001-11-03 - 0.2 js
+ *   - simplified buffering, one-shot URBs for writing
+ * 2001-11-10 - 0.3 js
+ *   - removed IOCTL (setting power/mode is more complicated, postponed)
+ * 2001-11-28 - 0.4 js
+ *   - added vendor commands for mode of operation and power level in open
+ * 2001-12-04 - 0.5 js
+ *   - set IR mode by default (by oversight 0.4 set VLL mode)
+ * 2002-01-11 - 0.5? pcchan
+ *   - make read buffer reusable and work around bytes_to_write issue between
+ *     uhci and legusbtower
+ * 2002-09-23 - 0.52 david ([EMAIL PROTECTED])
+ *   - imported into lejos project
+ *   - changed wake_up to wake_up_interruptible
+ *   - changed to use lego0 rather than tower0
+ *   - changed dbg() to use __func__ rather than deprecated __FUNCTION__
+ * 2003-01-12 - 0.53 david ([EMAIL PROTECTED])
+ *   - changed read and write to write everything or timeout (from a patch by Chris Riesen and 
+ *     Brett Thaeler driver)
+ *   - added ioctl functionality to set timeouts
+ * 2003-07-18 - 0.54 davidgsf ([EMAIL PROTECTED]) 
+ *   - initial import into LegoUSB project
+ *   - merge of existing LegoUSB.c driver
+ * 2003-07-18 - 0.55 davidgsf ([EMAIL PROTECTED]) 
+ *   - port to 2.6 style driver which means changing from one-shot interrupt
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/smp_lock.h>
+#include <linux/completion.h>
+#include <asm/uaccess.h>
+#include <linux/usb.h>
+
+
+#include "legousbtower.h"
+
+
+#ifdef CONFIG_USB_DEBUG
+	static int debug = 4;
+#else
+	static int debug = 1;
+#endif
+
+/* Use our own dbg macro */
+#undef dbg
+#define dbg(lvl, format, arg...) do { if (debug >= lvl) printk(KERN_DEBUG  __FILE__ " : " format " \n", ## arg); } while (0)
+
+
+/* Version Information */
+#define DRIVER_VERSION "v0.55"
+#define DRIVER_AUTHOR "David Glance, [EMAIL PROTECTED]"
+#define DRIVER_DESC "LEGO USB Tower Driver"
+
+/* Module paramaters */
+MODULE_PARM(debug, "i");
+MODULE_PARM_DESC(debug, "Debug enabled or not");
+
+
+/* Define these values to match your device */
+#define LEGO_USB_TOWER_VENDOR_ID	0x0694
+#define LEGO_USB_TOWER_PRODUCT_ID	0x0001
+
+/* table of devices that work with this driver */
+static struct usb_device_id tower_table [] = {
+	{ USB_DEVICE(LEGO_USB_TOWER_VENDOR_ID, LEGO_USB_TOWER_PRODUCT_ID) },
+	{ }					/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE (usb, tower_table);
+
+/* FIXME: Get a minor range for your devices from the usb maintainer */
+#define LEGO_USB_TOWER_MINOR_BASE	0xB3
+
+/* we can have up to this number of device plugged in at once */
+#define MAX_DEVICES		16
+
+#define COMMAND_TIMEOUT    (2*HZ)  /* 60 second timeout for a command */
+
+/* Structure to hold all of our device specific stuff */
+struct lego_usb_tower {
+	struct semaphore	sem;		/* locks this structure */
+	struct usb_device* 	udev;		/* save off the usb device pointer */
+	struct usb_interface*   interface;
+	unsigned char		minor;		/* the starting minor number for this device */
+
+	int			open_count;	/* number of times this port has been opened */
+
+	char*			read_buffer;
+        int			read_buffer_length;
+
+	wait_queue_head_t	read_wait;
+	wait_queue_head_t	write_wait;
+
+	char*			interrupt_in_buffer;
+	struct usb_endpoint_descriptor* interrupt_in_endpoint;
+	struct urb*		interrupt_in_urb;
+
+	char*			interrupt_out_buffer;
+	struct usb_endpoint_descriptor* interrupt_out_endpoint;
+	struct urb*		interrupt_out_urb;
+
+                                /* device specific: */
+
+	int                     read_timeout;
+	int                     write_timeout;
+};
+
+/* Note that no locking is needed:
+ * read_buffer is arbitrated by read_buffer_length == 0
+ * interrupt_out_buffer is arbitrated by interrupt_out_urb->status == -EINPROGRESS
+ * interrupt_in_buffer belongs to urb alone and is overwritten on overflow
+ */
+
+
+/* local function prototypes */
+static ssize_t tower_read	(struct file *file, char *buffer, size_t count, loff_t *ppos);
+static ssize_t tower_write	(struct file *file, const char *buffer, size_t count, loff_t *ppos);
+static int tower_ioctl		(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);
+static inline void tower_delete (struct lego_usb_tower *dev);
+static int tower_open		(struct inode *inode, struct file *file);
+static int tower_release	(struct inode *inode, struct file *file);
+static int tower_release_internal (struct lego_usb_tower *dev);
+static void tower_abort_transfers (struct lego_usb_tower *dev);
+static void tower_interrupt_in_callback (struct urb *urb, struct pt_regs *regs);
+static void tower_interrupt_out_callback (struct urb *urb, struct pt_regs *regs);
+
+static int  tower_probe	(struct usb_interface *interface, const struct usb_device_id *id);
+static void tower_disconnect	(struct usb_interface *interface);
+
+
+/* array of pointers to our devices that are currently connected */
+static struct lego_usb_tower * minor_table[MAX_DEVICES];
+
+/* prevent races between open() and disconnect */
+static DECLARE_MUTEX (disconnect_sem);
+static DECLARE_MUTEX (minor_table_mutex);
+
+/* file operations needed when we register this driver */
+static struct file_operations tower_fops = {
+	.owner = 	THIS_MODULE,
+	.read  = 	tower_read,
+	.write =	tower_write,
+	.ioctl =	tower_ioctl,
+	.open =		tower_open,
+	.release = 	tower_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 tower_class = {
+	.name =		"usb/legousbtower%d",
+	.fops =		&tower_fops,
+	.mode =		S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH,
+	.minor_base =	LEGO_USB_TOWER_MINOR_BASE,
+};
+
+
+/* usb specific object needed to register this driver with the usb subsystem */
+static struct usb_driver tower_driver = {
+	.owner =        THIS_MODULE,
+	.name =	        "legousbtower",
+	.probe = 	tower_probe,
+	.disconnect = 	tower_disconnect,
+	.id_table = 	tower_table,
+};
+
+
+/**
+ *	lego_usb_tower_debug_data
+ */
+static inline void lego_usb_tower_debug_data (int level, const char *function, int size, const unsigned char *data)
+{
+	int i;
+
+	if (debug < level)
+		return; 
+	
+	printk (KERN_DEBUG __FILE__": %s - length = %d, data = ", function, size);
+	for (i = 0; i < size; ++i) {
+		printk ("%.2x ", data[i]);
+	}
+	printk ("\n");
+}
+
+
+/**
+ *	tower_delete
+ */
+static inline void tower_delete (struct lego_usb_tower *dev)
+{
+	dbg(2, "%s enter", __func__);
+
+	minor_table[dev->minor] = NULL;
+        tower_abort_transfers (dev);
+
+        /* free data structures */
+	if (dev->interrupt_in_urb != NULL) {
+		usb_free_urb (dev->interrupt_in_urb);
+       }
+	if (dev->interrupt_out_urb != NULL) {
+		usb_free_urb (dev->interrupt_out_urb);
+        }
+	if (dev->read_buffer != NULL) {
+		kfree (dev->read_buffer);
+        }
+	if (dev->interrupt_in_buffer != NULL) {
+		kfree (dev->interrupt_in_buffer);
+        }
+	if (dev->interrupt_out_buffer != NULL) {
+		kfree (dev->interrupt_out_buffer);
+        }
+	kfree (dev);
+
+	dbg(2, "%s : leave", __func__);
+}
+
+
+/**
+ *	tower_open
+ */
+static int tower_open (struct inode *inode, struct file *file)
+{
+	struct lego_usb_tower *dev = NULL;
+	int subminor;
+	int retval = 0;
+	
+	dbg(2,"%s : enter", __func__);
+
+
+	subminor = minor (inode->i_rdev) - LEGO_USB_TOWER_MINOR_BASE;
+
+	if ((subminor < 0) ||
+	    (subminor >= MAX_DEVICES)) {
+		retval = -ENODEV;
+                goto exit;
+	}
+
+	/* lock our minor table and get our local data for this minor */
+	down (&minor_table_mutex);
+	dev = minor_table[subminor];
+
+	if (dev == NULL) {
+		retval = -ENODEV;
+                goto unlock_minor_table_exit;
+	}
+
+	/* lock this device */
+	down (&dev->sem);
+
+	if (dev->open_count == 0) {
+		dev->read_timeout = COMMAND_TIMEOUT;
+		dev->write_timeout = COMMAND_TIMEOUT;
+	}
+        
+	/* increment our usage count for the device */
+	++dev->open_count;
+
+	/* check that nobody else is using the device */
+        /* NOTE: the cleanup code will decrement the count to 1 */
+        if (dev->open_count > 1) {
+                retval = -EBUSY;
+                goto error;
+        }
+	/* save device in the file's private structure */
+	file->private_data = dev;
+
+
+        /* initialize in direction */
+        dev->read_buffer_length = 0;
+
+        goto unlock_exit;
+
+ error:
+        tower_release_internal (dev);
+
+ unlock_exit:
+
+	up (&dev->sem);
+
+ unlock_minor_table_exit:
+	up (&minor_table_mutex);
+ exit:
+	dbg(2,"%s : leave, return value %d ", __func__, retval);
+
+	return retval;
+}
+
+/**
+ *	tower_release
+ */
+static int tower_release (struct inode *inode, struct file *file)
+{
+	struct lego_usb_tower *dev;
+	int retval = 0;
+
+	dbg(2," %s : enter", __func__);
+
+	dev = (struct lego_usb_tower *)file->private_data;
+
+	if (dev == NULL) {
+	        dbg(1," %s : object is NULL", __func__);
+		retval = -ENODEV;
+                goto exit;
+	}
+
+	/* lock our minor table */
+	down (&minor_table_mutex);
+
+	/* lock our device */
+	down (&dev->sem);
+
+ 	if (dev->open_count <= 0) {
+        	dbg(1," %s : device not opened", __func__);
+                up (&dev->sem);
+                up (&minor_table_mutex);
+		retval = -ENODEV;
+		goto exit;
+	}
+
+        /* do the work */
+        retval = tower_release_internal (dev);
+
+	up (&dev->sem);
+	up (&minor_table_mutex);
+
+ exit:
+	dbg(2," %s : leave, return value %d", __func__, retval);
+	return retval;
+}
+
+
+/**
+ *	tower_release_internal
+ *      assumes minor_table and device are locked
+ */
+static int tower_release_internal (struct lego_usb_tower *dev)
+{
+	int retval = 0;
+
+	dbg(2," %s : enter", __func__);
+
+	if (dev->udev == NULL) {
+		/* the device was unplugged before the file was released */
+		tower_delete (dev);
+		goto exit;
+	}
+
+	/* decrement our usage count for the device */
+	--dev->open_count;
+	if (dev->open_count <= 0) {
+                tower_abort_transfers (dev);
+		dev->open_count = 0;
+	}
+
+ exit:
+	dbg(2," %s : leave", __func__);
+
+	return retval;
+}
+
+
+/**
+ *	tower_abort_transfers
+ *      aborts transfers and frees associated data structures
+ */
+static void tower_abort_transfers (struct lego_usb_tower *dev)
+{
+	dbg(2," %s : enter", __func__);
+
+        if (dev == NULL) {
+        	dbg(1," %s : dev is null", __func__);
+                goto exit;
+        }
+
+        /* shutdown transfer */
+	if (dev->interrupt_in_urb != NULL) {
+                usb_unlink_urb (dev->interrupt_in_urb);
+        }
+	if (dev->interrupt_out_urb != NULL) {
+                usb_unlink_urb (dev->interrupt_out_urb);
+        }
+
+ exit:
+	dbg(2," %s : leave", __func__);
+}
+
+
+/**
+ *	tower_read
+ */
+static ssize_t tower_read (struct file *file, char *buffer, size_t count, loff_t *ppos)
+{
+        struct lego_usb_tower *dev;
+        size_t bytes_read = 0;
+        size_t bytes_to_read;
+        int i;
+        int retval = 0;
+	int timeout = 0;
+
+	dbg(2," %s : enter, count = %d", __func__, count);
+
+	dev = (struct lego_usb_tower *)file->private_data;
+	
+	/* lock this object */
+	down (&dev->sem);
+
+	/* verify that the device wasn't unplugged */
+	if (dev->udev == NULL) {
+		retval = -ENODEV;
+		up (&dev->sem);
+		err("No device or device unplugged %d", retval);
+		return retval;
+	}
+	
+	/* verify that we actually have some data to read */
+	if (count == 0) {
+          	dbg(1," %s : read request of 0 bytes", __func__);
+        	goto exit;
+	}
+
+	// needs to be fixed
+
+	timeout = dev->read_timeout;
+
+	while (1) {
+		if (dev->read_buffer_length == 0) {
+
+			/* start reading */
+			usb_fill_int_urb (dev->interrupt_in_urb,dev->udev,
+					  usb_rcvintpipe(dev->udev, dev->interrupt_in_endpoint->bEndpointAddress),
+					  dev->interrupt_in_buffer,
+					  dev->interrupt_in_endpoint->wMaxPacketSize,
+					  tower_interrupt_in_callback,
+					  dev,
+					  dev->interrupt_in_endpoint->bInterval);
+			
+			retval = usb_submit_urb (dev->interrupt_in_urb, GFP_KERNEL);
+			
+			if (retval < 0) {
+				err("Couldn't submit interrupt_in_urb");
+				goto exit;
+			}
+
+
+
+			if (timeout <= 0) {
+			        retval = -ETIMEDOUT;
+			        goto exit;
+			}
+
+			if (signal_pending(current)) {
+
+				retval = -EINTR;
+				goto exit;
+			}
+
+			up (&dev->sem);
+			timeout = interruptible_sleep_on_timeout (&dev->read_wait, timeout);
+			down (&dev->sem);
+
+                } else {
+                        /* copy the data from read_buffer into userspace */
+                        bytes_to_read = count > dev->read_buffer_length ? dev->read_buffer_length : count;
+                        if (copy_to_user (buffer, dev->read_buffer, bytes_to_read) != 0) {
+                                retval = -EFAULT;
+                                goto exit;
+                        }
+                        dev->read_buffer_length -= bytes_to_read;
+                        for (i=0; i<dev->read_buffer_length; i++) {
+                                dev->read_buffer[i] = dev->read_buffer[i+bytes_to_read];
+                        }
+
+                        buffer += bytes_to_read;
+                        count -= bytes_to_read;
+                        bytes_read += bytes_to_read;
+			if (count == 0) {
+				break;
+			}
+                }
+
+
+        }
+
+        retval = bytes_read;
+
+ exit:
+	/* unlock the device */
+	up (&dev->sem);
+
+	dbg(2," %s : leave, return value %d", __func__, retval);
+	return retval;
+}
+
+
+/**
+ *	tower_write
+ */
+static ssize_t tower_write (struct file *file, const char *buffer, size_t count, loff_t *ppos)
+{
+	struct lego_usb_tower *dev;
+	size_t bytes_written = 0;
+	size_t bytes_to_write;
+        size_t buffer_size;
+	int retval = 0;
+	int timeout = 0;
+
+	dbg(2," %s : enter, count = %d", __func__, count);
+
+	dev = (struct lego_usb_tower *)file->private_data;
+
+	/* lock this object */
+	down (&dev->sem);
+
+	/* verify that the device wasn't unplugged */
+	if (dev->udev == NULL) {
+		retval = -ENODEV;
+		up (&dev->sem);
+		err("No device or device unplugged %d", retval);
+		return retval;
+	}
+
+	/* verify that we actually have some data to write */
+	if (count == 0) {
+        	dbg(1," %s : write request of 0 bytes", __func__);
+		goto exit;
+	}
+
+
+        while (count > 0) {
+                if (dev->interrupt_out_urb->status == -EINPROGRESS) {
+		        timeout = dev->write_timeout;
+			dbg(1," %s : enter timeout: %d", __func__, timeout);
+			while (timeout > 0) {
+				if (signal_pending(current)) {
+					dbg(1," %s : interrupted", __func__);
+					return -EINTR;
+				}
+				up (&dev->sem);
+				timeout = interruptible_sleep_on_timeout (&dev->write_wait, timeout);
+				down (&dev->sem);
+				if (timeout > 0) {
+					break;
+				}
+				dbg(1," %s : interrupted timeout: %d", __func__, timeout);
+			}
+
+
+			dbg(1," %s : final timeout: %d", __func__, timeout);
+
+			if (timeout == 0) {
+				dbg(1, "%s - command timed out.", __func__);
+				retval = -ETIMEDOUT;
+				goto exit;
+			}
+
+                	dbg(4," %s : in progress, count = %d", __func__, count);
+
+                } else {
+		        dbg(4," %s : sending, count = %d", __func__, count);
+
+                        /* write the data into interrupt_out_buffer from userspace */
+                        buffer_size = dev->interrupt_out_endpoint->wMaxPacketSize;
+                        bytes_to_write = count > buffer_size ? buffer_size : count;
+			dbg(4," %s : buffer_size = %d, count = %d, bytes_to_write = %d", __func__, buffer_size, count, bytes_to_write);
+
+                        if (copy_from_user (dev->interrupt_out_buffer, buffer, bytes_to_write) != 0) {
+                                retval = -EFAULT;
+                                goto exit;
+                        }
+
+                        /* send off the urb */
+                        usb_fill_int_urb(
+                                dev->interrupt_out_urb,
+                                dev->udev, 
+                                usb_sndintpipe(dev->udev, dev->interrupt_out_endpoint->bEndpointAddress),
+                                dev->interrupt_out_buffer,
+                                bytes_to_write,
+                                tower_interrupt_out_callback,
+                                dev,
+				dev->interrupt_in_endpoint->bInterval);
+
+                        dev->interrupt_out_urb->actual_length = bytes_to_write;
+                        retval = usb_submit_urb (dev->interrupt_out_urb, GFP_KERNEL);
+
+                        if (retval < 0) {
+                                err("Couldn't submit interrupt_out_urb %d", retval);
+                                goto exit;
+                        }
+
+                        buffer += bytes_to_write;
+                        count -= bytes_to_write;
+
+                        bytes_written += bytes_to_write;
+                }
+        }
+
+        retval = bytes_written;
+
+ exit:
+	/* unlock the device */
+	up (&dev->sem);
+
+	dbg(2," %s : leave, return value %d", __func__, retval);
+
+	return retval;
+}
+
+
+/**
+ *	tower_ioctl
+ */
+static int tower_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct lego_usb_tower *dev;
+        int retval =  -ENOTTY;  /* default: we don't understand ioctl */
+
+	dbg(2," %s : enter, cmd 0x%.4x, arg %ld", __func__, cmd, arg);
+
+	dev = (struct lego_usb_tower *)file->private_data;
+
+	/* lock this object */
+	down (&dev->sem);
+
+	/* verify that the device wasn't unplugged */
+	if (dev->udev == NULL) {
+		retval = -ENODEV;
+                goto unlock_exit;
+	}
+
+        switch (cmd) {
+
+	case LEGO_TOWER_SET_PARAM: {
+		struct request_reply b;
+
+          	dbg(2, "%s : SET_PARAM  0x%04lx", __func__, arg);
+
+		retval = usb_control_msg (dev->udev,
+					  usb_rcvctrlpipe(dev->udev, 0),
+					  LEGO_USB_TOWER_REQUEST_SET,
+					  USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_DEVICE,
+					  ((int) arg),
+					  0,
+					  &b,
+					  sizeof(b),
+					  COMMAND_TIMEOUT);
+
+		if (retval >= 0) {
+			if (b.error_code != 0) {
+				err("%s : LEGO_TOWER_SET_PARAM - error returned  0x%02x", __func__, 
+				    b.error_code);
+				retval = -b.error_code;
+			}
+		}
+	}
+		break;
+
+
+	case LEGO_TOWER_RESET: {
+		struct request_reply b;
+
+          	dbg(2, "%s : RESET", __func__);
+
+		retval = usb_control_msg (dev->udev,
+					  usb_rcvctrlpipe(dev->udev, 0),
+					  LEGO_USB_TOWER_REQUEST_RESET,
+					  USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_DEVICE,
+					  0,
+					  0,
+					  &b,
+					  sizeof(b),
+					  COMMAND_TIMEOUT);
+
+		if (retval >= 0) {
+			if (b.error_code != 0) {
+				err("%s : LEGO_TOWER_RESET - error 0x%02x", __func__, b.error_code);
+				retval = -b.error_code;
+			}
+		}
+	}
+		break;
+
+	case LEGO_TOWER_SET_READ_TIMEOUT:
+		retval = 0;
+		// dev->read_timeout = ((int) arg);
+		break;
+	case LEGO_TOWER_SET_WRITE_TIMEOUT:
+		retval = 0;
+		dev->write_timeout = ((int) arg);
+		break;
+	default:
+		retval = -ENOTTY;
+		break;
+
+        }
+	
+ unlock_exit:
+	/* unlock the device */
+	up (&dev->sem);
+
+	dbg(2," %s : leave, return value %d", __func__, retval);
+
+	return retval;
+}
+
+
+/**
+ *	tower_interrupt_in_callback
+ */
+static void tower_interrupt_in_callback (struct urb *urb, struct pt_regs *regs)
+{
+	struct lego_usb_tower *dev = (struct lego_usb_tower *)urb->context;
+
+	dbg(4," %s : enter, status %d", __func__, urb->status);
+
+        lego_usb_tower_debug_data(5,__func__, urb->actual_length, urb->transfer_buffer);
+
+        if (urb->status != 0) {
+                if ((urb->status != -ENOENT) && (urb->status != -ECONNRESET)) {
+		        dbg(1," %s : nonzero status received: %d", __func__, urb->status);
+                }
+                goto exit;
+        }
+
+        down (&dev->sem);
+
+        if (urb->actual_length > 0) {
+                if (dev->read_buffer_length < (4 * dev->interrupt_in_endpoint->wMaxPacketSize) - (urb->actual_length)) {
+
+                        memcpy (dev->read_buffer+dev->read_buffer_length, dev->interrupt_in_buffer, urb->actual_length);
+
+                        dev->read_buffer_length += urb->actual_length;
+		        dbg(1," %s reading  %d ", __func__, urb->actual_length);
+                        wake_up_interruptible (&dev->read_wait);
+
+                } else {
+		        dbg(1," %s : read_buffer overflow", __func__);
+                }
+        }
+
+        up (&dev->sem);
+
+ exit:
+        lego_usb_tower_debug_data(5,__func__, urb->actual_length, urb->transfer_buffer);
+	dbg(4," %s : leave, status %d", __func__, urb->status);
+}
+
+
+/**
+ *	tower_interrupt_out_callback
+ */
+static void tower_interrupt_out_callback (struct urb *urb, struct pt_regs *regs)
+{
+	struct lego_usb_tower *dev = (struct lego_usb_tower *)urb->context;
+
+	dbg(4," %s : enter, status %d", __func__, urb->status);
+        lego_usb_tower_debug_data(5,__func__, urb->actual_length, urb->transfer_buffer);
+
+        if (urb->status != 0) {
+                if ((urb->status != -ENOENT) && 
+                    (urb->status != -ECONNRESET)) {
+                        dbg(1, " %s :nonzero status received: %d", __func__, urb->status);
+                }
+                goto exit;
+        }                        
+
+        wake_up_interruptible(&dev->write_wait);
+ exit:
+
+        lego_usb_tower_debug_data(5,__func__, urb->actual_length, urb->transfer_buffer);
+	dbg(4," %s : leave, status %d", __func__, urb->status);
+}
+
+
+/**
+ *	tower_probe
+ *
+ *	Called by the usb core when a new device is connected that it thinks
+ *	this driver might be interested in.
+ */
+static int tower_probe (struct usb_interface *interface, const struct usb_device_id *id)
+{
+	struct usb_device *udev = interface_to_usbdev(interface);
+	struct lego_usb_tower *dev = NULL;
+	int minor;
+	struct usb_host_interface *iface_desc;
+        struct usb_endpoint_descriptor* endpoint;
+	int i;
+        int retval = -ENOMEM;
+
+	dbg(2," %s : enter", __func__);
+
+        if (udev == NULL) {
+		info ("udev is NULL.");
+        }
+	
+	/* See if the device offered us matches what we can accept */
+	if ((udev->descriptor.idVendor != LEGO_USB_TOWER_VENDOR_ID) ||
+	    (udev->descriptor.idProduct != LEGO_USB_TOWER_PRODUCT_ID)) {
+		goto exit;
+	}
+
+
+	/* 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 unlock_exit;
+	}
+
+	/* allocate memory for our device state and intialize it */
+	dev = kmalloc (sizeof(struct lego_usb_tower), GFP_KERNEL);
+	if (dev == NULL) {
+		err ("Out of memory");
+		goto unlock_minor_exit;
+	}
+	init_MUTEX (&dev->sem);
+        down (&dev->sem);
+	dev->udev = udev;
+	dev->minor = minor;
+        dev->open_count = 0;
+
+        dev->read_buffer = NULL;
+        dev->read_buffer_length = 0;
+
+        init_waitqueue_head (&dev->read_wait);
+        init_waitqueue_head (&dev->write_wait);
+
+        dev->interrupt_in_buffer = NULL;
+        dev->interrupt_in_endpoint = NULL;
+        dev->interrupt_in_urb = NULL;
+
+        dev->interrupt_out_buffer = NULL;
+        dev->interrupt_out_endpoint = NULL;
+        dev->interrupt_out_urb = NULL;
+
+
+	iface_desc = &interface->altsetting[0];
+
+	/* set up the endpoint information */
+	for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+		endpoint = &iface_desc->endpoint[i].desc;
+
+		if (((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) &&
+		    ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT)) {
+                        dev->interrupt_in_endpoint = endpoint;
+		}
+		
+		if (((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT) &&
+		    ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT)) {
+                        dev->interrupt_out_endpoint = endpoint;
+		}
+	}
+        if(dev->interrupt_in_endpoint == NULL) {
+                err("interrupt in endpoint not found");
+                goto unlock_exit;
+        }
+        if (dev->interrupt_out_endpoint == NULL) {
+                err("interrupt out endpoint not found");
+                goto unlock_exit;
+        }
+
+        dev->read_buffer = kmalloc ((4*dev->interrupt_in_endpoint->wMaxPacketSize), GFP_KERNEL);
+        if (!dev->read_buffer) {
+                err("Couldn't allocate read_buffer");
+                goto unlock_exit;
+        }
+        dev->interrupt_in_buffer = kmalloc (dev->interrupt_in_endpoint->wMaxPacketSize, GFP_KERNEL);
+        if (!dev->interrupt_in_buffer) {
+                err("Couldn't allocate interrupt_in_buffer");
+                goto unlock_exit;
+        }
+        dev->interrupt_in_urb = usb_alloc_urb(0, GFP_KERNEL);
+        if (!dev->interrupt_in_urb) {
+                err("Couldn't allocate interrupt_in_urb");
+                goto unlock_exit;
+        }
+        dev->interrupt_out_buffer = kmalloc (dev->interrupt_out_endpoint->wMaxPacketSize, GFP_KERNEL);
+        if (!dev->interrupt_out_buffer) {
+                err("Couldn't allocate interrupt_out_buffer");
+                goto unlock_exit;
+        }
+        dev->interrupt_out_urb = usb_alloc_urb(0, GFP_KERNEL);
+        if (!dev->interrupt_out_urb) {
+                err("Couldn't allocate interrupt_out_urb");
+                goto unlock_exit;
+        }                
+
+        /* publish it */
+	minor_table[minor] = dev;
+
+	/* we can register the device now, as it is ready */
+	usb_set_intfdata (interface, dev);
+	retval = usb_register_dev (interface, &tower_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;
+	}
+
+
+	/* let the user know what node this device is now attached to */
+	info ("LEGO USB Tower device now attached to /dev/usb/lego%d", dev->minor);
+
+
+
+ unlock_exit:
+        /* unlock device */
+        up (&dev->sem);
+
+ unlock_minor_exit:
+	up (&minor_table_mutex);
+
+ exit:
+	dbg(2," %s : leave, return value 0x%.8lx (dev)", __func__, (long) dev);
+
+	return retval;
+
+ error:
+	tower_delete(dev);
+	return retval;
+}
+
+
+/**
+ *	tower_disconnect
+ *
+ *	Called by the usb core when the device is removed from the system.
+ */
+static void tower_disconnect (struct usb_interface *interface)
+{
+	struct lego_usb_tower *dev;
+	int minor;
+
+	dbg(2," %s : enter", __func__);
+
+	down (&disconnect_sem);
+
+	dev = usb_get_intfdata (interface);
+	usb_set_intfdata (interface, NULL);
+
+
+	down (&minor_table_mutex);
+	down (&dev->sem);
+
+	minor = dev->minor;
+
+	/* give back our minor */
+	usb_deregister_dev (interface, &tower_class);
+
+	/* if the device is not opened, then we clean up right now */
+	if (!dev->open_count) {
+		up (&dev->sem);
+		tower_delete (dev);
+	} else {
+		dev->udev = NULL;
+		up (&dev->sem);
+	}
+
+	up (&disconnect_sem);
+
+	info("LEGO USB Tower #%d now disconnected", minor);
+	up (&minor_table_mutex);
+
+	dbg(2," %s : leave", __func__);
+}
+
+
+
+/**
+ *	lego_usb_tower_init
+ */
+static int __init lego_usb_tower_init(void)
+{
+	int result;
+        int retval = 0;
+
+	dbg(2," %s : enter", __func__);
+
+	/* register this driver with the USB subsystem */
+	result = usb_register(&tower_driver);
+	if (result < 0) {
+		err("usb_register failed for the "__FILE__" driver. Error number %d", result);
+		retval = -1;
+                goto exit;
+	}
+
+	info(DRIVER_DESC " " DRIVER_VERSION);
+
+ exit:
+	dbg(2," %s : leave, return value %d", __func__, retval);
+
+	return retval;
+}
+
+
+/**
+ *	lego_usb_tower_exit
+ */
+static void __exit lego_usb_tower_exit(void)
+{
+	dbg(2," %s : enter", __func__);
+
+	/* deregister this driver with the USB subsystem */
+	usb_deregister (&tower_driver);
+
+	dbg(2," %s : leave", __func__);
+}
+
+module_init (lego_usb_tower_init);
+module_exit (lego_usb_tower_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+#ifdef MODULE_LICENSE
+MODULE_LICENSE("GPL");
+#endif
diff -Nru a/drivers/usb/misc/legousbtower.h b/drivers/usb/misc/legousbtower.h
--- a/drivers/usb/misc/legousbtower.h	1970-01-01 08:00:00.000000000 +0800
+++ b/drivers/usb/misc/legousbtower.h	2003-09-03 13:21:21.066373656 +0800
@@ -0,0 +1,52 @@
+/*
+ * legousbtower Lego USB IR Tower Linux Driver 
+ *
+ *      Copyright (c) 2001-2002 The LegoUSB DevTeam <[EMAIL PROTECTED]>
+ * 
+ *
+ *	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.
+ *
+ */
+
+#ifndef __LEGOUSBTOWER_H
+#define __LEGOUSBTOWER_H
+
+#define LEGO_TOWER_SET_PARAM _IOW('u', 0xb0, int)
+#define LEGO_TOWER_GET_PARAM _IOW('u', 0xb1, int)
+#define LEGO_TOWER_RESET _IO('u', 0xb3)
+
+
+#define LEGO_TOWER_SET_READ_TIMEOUT _IOW('u', 0xc8, int)
+#define LEGO_TOWER_SET_WRITE_TIMEOUT _IOW('u', 0xc9, int)
+
+
+#define LEGO_USB_TOWER_REQUEST_GET		1
+#define LEGO_USB_TOWER_REQUEST_SET		2
+#define LEGO_USB_TOWER_REQUEST_RESET		4 
+
+
+#define LEGO_USB_TOWER_ADDRESS_MODE		1
+#define LEGO_USB_TOWER_ADDRESS_POWER_LEVEL	2
+
+#define LEGO_USB_TOWER_POWER_LEVEL_LOW		1
+#define LEGO_USB_TOWER_POWER_LEVEL_MEDIUM	2
+#define LEGO_USB_TOWER_POWER_LEVEL_HIGH		3
+
+#define LEGO_USB_TOWER_MODE_VLL			1
+#define LEGO_USB_TOWER_MODE_RCX			2
+
+
+
+struct request_reply
+{
+	unsigned short length;
+	unsigned char  error_code;
+	unsigned char  value;
+	unsigned char  buffer[16];   // variable replies from the tower
+	                   // at this stage we aren't interested in the data
+};
+
+#endif

Reply via email to