Hi, here is a patch against 2.6.4 that corrects problems in the LEGO USB Tower driver, in particular the locking and sleeping in interrupt context, and generally overhauls and cleans it up.
I have tested it with UHCI, OHCI and EHCI with and without hubs on x86 machines. It works flawlessly except for proper EHCI over a 2.0 hub, which gives me a -ENOSPC on submitting the first interrupt in URB. This needs to be investigated, but may take some time because I need to learn how USB 2.0 works. As for applications it works for me for Lejos and NQC with firmware and program download and datalog upload, and I have a report that it also works with a suitably patched brickos. I'll maintain this driver from now on, David Glance who has worked on it until now has agreed to that. Please apply. J�rgen diff -uprN -X dontdiff linux-2.6.4/MAINTAINERS linux-2.6.4-legousbtower-0.9/MAINTAINERS --- linux-2.6.4/MAINTAINERS 2004-03-11 03:55:33.000000000 +0100 +++ linux-2.6.4-legousbtower-0.9/MAINTAINERS 2004-03-12 14:10:22.000000000 +0100 @@ -1212,6 +1212,13 @@ M: [EMAIL PROTECTED] L: [EMAIL PROTECTED] S: Maintained +LEGO USB Tower driver +P: Juergen Stuber +M: [EMAIL PROTECTED] +L: [EMAIL PROTECTED] +W: http://legousb.sourceforge.net/ +S: Maintained + LINUX FOR IBM pSERIES (RS/6000) P: Paul Mackerras M: [EMAIL PROTECTED] diff -uprN -X dontdiff linux-2.6.4/drivers/usb/misc/legousbtower.c linux-2.6.4-legousbtower-0.9/drivers/usb/misc/legousbtower.c --- linux-2.6.4/drivers/usb/misc/legousbtower.c 2004-03-11 03:55:27.000000000 +0100 +++ linux-2.6.4-legousbtower-0.9/drivers/usb/misc/legousbtower.c 2004-03-12 14:10:22.000000000 +0100 @@ -1,8 +1,8 @@ /* * LEGO USB Tower driver * - * Copyright (C) 2003 David Glance <[EMAIL PROTECTED]> - * 2001 Juergen Stuber <[EMAIL PROTECTED]> + * Copyright (C) 2003 David Glance <[EMAIL PROTECTED]> + * 2001-2004 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 @@ -33,14 +33,33 @@ * - 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) + * - 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]) + * 2003-07-18 - 0.54 davidgsf ([EMAIL PROTECTED]) * - initial import into LegoUSB project * - merge of existing LegoUSB.c driver - * 2003-07-18 - 0.56 davidgsf ([EMAIL PROTECTED]) + * 2003-07-18 - 0.56 davidgsf ([EMAIL PROTECTED]) * - port to 2.6 style driver + * 2004-02-29 - 0.6 Juergen Stuber <[EMAIL PROTECTED]> + * - fix locking + * - unlink read URBs which are no longer needed + * - allow increased buffer size, eliminates need for timeout on write + * - have read URB running continuously + * - added poll + * - forbid seeking + * - added nonblocking I/O + * - changed back __func__ to __FUNCTION__ + * - read and log tower firmware version + * - reset tower on probe, avoids failure of first write + * 2004-03-09 - 0.7 Juergen Stuber <[EMAIL PROTECTED]> + * - timeout read now only after inactivity, shorten default accordingly + * 2004-03-11 - 0.8 Juergen Stuber <[EMAIL PROTECTED]> + * - log major, minor instead of possibly confusing device filename + * - whitespace cleanup + * 2004-03-12 - 0.9 Juergen Stuber <[EMAIL PROTECTED]> + * - normalize whitespace in debug messages + * - take care about endianness in control message responses */ #include <linux/config.h> @@ -53,33 +72,98 @@ #include <linux/completion.h> #include <asm/uaccess.h> #include <linux/usb.h> +#include <linux/poll.h> #ifdef CONFIG_USB_DEBUG static int debug = 4; #else - static int debug = 1; + static int debug = 0; #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) +#define dbg(lvl, format, arg...) do { if (debug >= lvl) printk(KERN_DEBUG __FILE__ ": " format "\n", ## arg); } while (0) /* Version Information */ -#define DRIVER_VERSION "v0.56" -#define DRIVER_AUTHOR "David Glance, [EMAIL PROTECTED]" +#define DRIVER_VERSION "v0.9" +#define DRIVER_AUTHOR "Juergen Stuber <[EMAIL PROTECTED]>" #define DRIVER_DESC "LEGO USB Tower Driver" -/* Module paramaters */ +/* Module parameters */ MODULE_PARM(debug, "i"); MODULE_PARM_DESC(debug, "Debug enabled or not"); +/* The defaults are chosen to work with current versions of leJOS and NQC, + * and so that behaviour is close to older versions of the driver. + */ + +/* Some legacy software likes to receive packets in one piece. + * In this case read_buffer_size should exceed the maximal packet length + * (417 for datalog uploads), and packet_timeout should be set. + */ +static size_t read_buffer_size = 480; +MODULE_PARM(read_buffer_size, "i"); +MODULE_PARM_DESC(read_buffer_size, "Read buffer size"); + +/* Some legacy software likes to send packets in one piece. + * In this case write_buffer_size should exceed the maximal packet length + * (417 for firmware and program downloads). + * A problem with long writes is that the following read may time out + * if the software is not prepared to wait long enough. + */ +static size_t write_buffer_size = 480; +MODULE_PARM(write_buffer_size, "i"); +MODULE_PARM_DESC(write_buffer_size, "Write buffer size"); + +/* Some legacy software expects reads to contain whole LASM packets. + * To achieve this, characters which arrive before a packet timeout + * occurs will be returned in a single read operation. + * A problem with long reads is that the software may time out + * if it is not prepared to wait long enough. + * The packet timeout should be greater than the time between the + * reception of subsequent characters, which should arrive about + * every 5ms for the standard 2400 baud. + * Set it to 0 to disable. + */ +static int packet_timeout = 50; +MODULE_PARM(packet_timeout, "i"); +MODULE_PARM_DESC(packet_timeout, "Packet timeout in ms"); + +/* Some legacy software expects blocking reads to time out. + * Timeout occurs after the specified time of read and write inactivity. + * Set it to 0 to disable. + */ +static int read_timeout = 200; +MODULE_PARM(read_timeout, "i"); +MODULE_PARM_DESC(read_timeout, "Read timeout in ms"); + /* Define these values to match your device */ #define LEGO_USB_TOWER_VENDOR_ID 0x0694 #define LEGO_USB_TOWER_PRODUCT_ID 0x0001 +/* Vendor requests */ +#define LEGO_USB_TOWER_REQUEST_RESET 0x04 +#define LEGO_USB_TOWER_REQUEST_GET_VERSION 0xFD + +struct tower_reset_reply { + __u16 size; /* little-endian */ + __u8 err_code; + __u8 spare; +} __attribute__ ((packed)); + +struct tower_get_version_reply { + __u16 size; /* little-endian */ + __u8 err_code; + __u8 spare; + __u8 major; + __u8 minor; + __u16 build_no; /* little-endian */ +} __attribute__ ((packed)); + + /* 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) }, @@ -90,22 +174,22 @@ MODULE_DEVICE_TABLE (usb, tower_table); #define LEGO_USB_TOWER_MINOR_BASE 160 -/* we can have up to this number of device plugged in at once */ -#define MAX_DEVICES 16 - -#define COMMAND_TIMEOUT (2*HZ) /* 2 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_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; + size_t read_buffer_length; /* this much came in */ + size_t read_packet_length; /* this much will be returned on read */ + spinlock_t read_buffer_lock; + int packet_timeout_jiffies; + unsigned long read_last_arrival; wait_queue_head_t read_wait; wait_queue_head_t write_wait; @@ -113,18 +197,16 @@ struct lego_usb_tower { char* interrupt_in_buffer; struct usb_endpoint_descriptor* interrupt_in_endpoint; struct urb* interrupt_in_urb; + atomic_t interrupt_in_running; + atomic_t interrupt_in_done; char* interrupt_out_buffer; struct usb_endpoint_descriptor* interrupt_out_endpoint; struct urb* interrupt_out_urb; + atomic_t interrupt_out_busy; }; -/* 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); @@ -132,8 +214,11 @@ static ssize_t tower_write (struct file 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 unsigned int tower_poll (struct file *file, poll_table *wait); +static loff_t tower_llseek (struct file *file, loff_t off, int whence); + static void tower_abort_transfers (struct lego_usb_tower *dev); +static void tower_check_for_read_packet (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); @@ -146,14 +231,16 @@ static DECLARE_MUTEX (disconnect_sem); /* file operations needed when we register this driver */ static struct file_operations tower_fops = { - .owner = THIS_MODULE, - .read = tower_read, + .owner = THIS_MODULE, + .read = tower_read, .write = tower_write, .open = tower_open, - .release = tower_release, + .release = tower_release, + .poll = tower_poll, + .llseek = tower_llseek, }; -/* +/* * 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 */ @@ -167,11 +254,11 @@ static struct usb_class_driver tower_cla /* 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, + .owner = THIS_MODULE, + .name = "legousbtower", + .probe = tower_probe, + .disconnect = tower_disconnect, + .id_table = tower_table, }; @@ -183,8 +270,8 @@ static inline void lego_usb_tower_debug_ int i; if (debug < level) - return; - + return; + printk (KERN_DEBUG __FILE__": %s - length = %d, data = ", function, size); for (i = 0; i < size; ++i) { printk ("%.2x ", data[i]); @@ -198,7 +285,7 @@ static inline void lego_usb_tower_debug_ */ static inline void tower_delete (struct lego_usb_tower *dev) { - dbg(2, "%s enter", __func__); + dbg(2, "%s: enter", __FUNCTION__); tower_abort_transfers (dev); @@ -214,7 +301,7 @@ static inline void tower_delete (struct kfree (dev->interrupt_out_buffer); kfree (dev); - dbg(2, "%s : leave", __func__); + dbg(2, "%s: leave", __FUNCTION__); } @@ -228,7 +315,7 @@ static int tower_open (struct inode *ino int retval = 0; struct usb_interface *interface; - dbg(2,"%s : enter", __func__); + dbg(2, "%s: enter", __FUNCTION__); subminor = iminor(inode); @@ -253,24 +340,44 @@ static int tower_open (struct inode *ino /* lock this device */ down (&dev->sem); - - /* increment our usage count for the device */ - ++dev->open_count; - - /* save device in the file's private structure */ - file->private_data = dev; - + /* allow opening only once */ + if (dev->open_count) { + retval = -EBUSY; + goto exit_unlock; + } + dev->open_count = 1; /* initialize in direction */ dev->read_buffer_length = 0; + dev->read_packet_length = 0; + 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); + atomic_set (&dev->interrupt_in_running, 1); + atomic_set (&dev->interrupt_in_done, 0); + retval = usb_submit_urb (dev->interrupt_in_urb, GFP_KERNEL); + if (retval) { + err("Couldn't submit interrupt_in_urb %d", retval); + atomic_set (&dev->interrupt_in_running, 0); + dev->open_count = 0; + goto exit_unlock; + } + /* save device in the file's private structure */ + file->private_data = dev; + +exit_unlock: up (&dev->sem); exit_no_device: - up (&disconnect_sem); - dbg(2,"%s : leave, return value %d ", __func__, retval); + dbg(2, "%s: leave, return value %d ", __FUNCTION__, retval); return retval; } @@ -283,87 +390,129 @@ static int tower_release (struct inode * struct lego_usb_tower *dev; int retval = 0; - dbg(2," %s : enter", __func__); + dbg(2, "%s: enter", __FUNCTION__); dev = (struct lego_usb_tower *)file->private_data; if (dev == NULL) { - dbg(1," %s : object is NULL", __func__); + dbg(1, "%s: object is NULL", __FUNCTION__); retval = -ENODEV; goto exit; } - - /* lock our device */ down (&dev->sem); - if (dev->open_count <= 0) { - dbg(1," %s : device not opened", __func__); + if (dev->open_count != 1) { + dbg(1, "%s: device not opened exactly once", __FUNCTION__); retval = -ENODEV; + goto exit_unlock; + } + if (dev->udev == NULL) { + /* the device was unplugged before the file was released */ + up (&dev->sem); /* unlock here as tower_delete frees dev */ + tower_delete (dev); goto exit; } + tower_abort_transfers (dev); + dev->open_count = 0; - /* do the work */ - retval = tower_release_internal (dev); +exit_unlock: + up (&dev->sem); exit: - up (&dev->sem); - dbg(2," %s : leave, return value %d", __func__, retval); + dbg(2, "%s: leave, return value %d", __FUNCTION__, retval); return retval; } /** - * tower_release_internal + * tower_abort_transfers + * aborts transfers and frees associated data structures */ -static int tower_release_internal (struct lego_usb_tower *dev) +static void tower_abort_transfers (struct lego_usb_tower *dev) { - int retval = 0; + dbg(2, "%s: enter", __FUNCTION__); - dbg(2," %s : enter", __func__); - - if (dev->udev == NULL) { - /* the device was unplugged before the file was released */ - tower_delete (dev); + if (dev == NULL) { + dbg(1, "%s: dev is null", __FUNCTION__); 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; + /* shutdown transfer */ + if (atomic_read(&dev->interrupt_in_running)) { + atomic_set (&dev->interrupt_in_running, 0); + if (dev->interrupt_in_urb != NULL) { + usb_unlink_urb (dev->interrupt_in_urb); + } + } + if (atomic_read(&dev->interrupt_out_busy)) { + atomic_set (&dev->interrupt_out_busy, 0); + if (dev->interrupt_out_urb != NULL) { + usb_unlink_urb (dev->interrupt_out_urb); + } } exit: - dbg(2," %s : leave", __func__); - return retval; + dbg(2, "%s: leave", __FUNCTION__); } /** - * tower_abort_transfers - * aborts transfers and frees associated data structures + * tower_check_for_read_packet + * + * To get correct semantics for signals and non-blocking I/O + * with packetizing we pretend not to see any data in the read buffer + * until it has been there unchanged for at least + * dev->packet_timeout_jiffies, or until the buffer is full. */ -static void tower_abort_transfers (struct lego_usb_tower *dev) +static void tower_check_for_read_packet (struct lego_usb_tower *dev) { - dbg(2," %s : enter", __func__); - - if (dev == NULL) { - dbg(1," %s : dev is null", __func__); - goto exit; + spin_lock_irq (&dev->read_buffer_lock); + if (!packet_timeout + || time_after(jiffies, dev->read_last_arrival + dev->packet_timeout_jiffies) + || dev->read_buffer_length == read_buffer_size) { + dev->read_packet_length = dev->read_buffer_length; } + atomic_set (&dev->interrupt_in_done, 0); + spin_unlock_irq (&dev->read_buffer_lock); +} - /* shutdown transfer */ - if (dev->interrupt_in_urb != NULL) { - usb_unlink_urb (dev->interrupt_in_urb); + +/** + * tower_poll + */ +static unsigned int tower_poll (struct file *file, poll_table *wait) +{ + struct lego_usb_tower *dev; + unsigned int mask = 0; + + dbg(2, "%s: enter", __FUNCTION__); + + dev = file->private_data; + + poll_wait(file, &dev->read_wait, wait); + poll_wait(file, &dev->write_wait, wait); + + tower_check_for_read_packet(dev); + if (dev->read_packet_length > 0) { + mask |= POLLIN | POLLRDNORM; } - if (dev->interrupt_out_urb != NULL) { - usb_unlink_urb (dev->interrupt_out_urb); + if (!atomic_read(&dev->interrupt_out_busy)) { + mask |= POLLOUT | POLLWRNORM; } -exit: - dbg(2," %s : leave", __func__); + dbg(2, "%s: leave, mask = %d", __FUNCTION__, mask); + + return mask; +} + + +/** + * tower_llseek + */ +static loff_t tower_llseek (struct file *file, loff_t off, int whence) +{ + return -ESPIPE; /* unseekable */ } @@ -373,16 +522,15 @@ exit: 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; + unsigned long timeout = 0; - dbg(2," %s : enter, count = %Zd", __func__, count); + dbg(2, "%s: enter, count = %Zd", __FUNCTION__, count); dev = (struct lego_usb_tower *)file->private_data; - + /* lock this object */ down (&dev->sem); @@ -395,74 +543,63 @@ static ssize_t tower_read (struct file * /* verify that we actually have some data to read */ if (count == 0) { - dbg(1," %s : read request of 0 bytes", __func__); + dbg(1, "%s: read request of 0 bytes", __FUNCTION__); goto exit; } + if (read_timeout) { + timeout = jiffies + read_timeout * HZ / 1000; + } - timeout = COMMAND_TIMEOUT; + /* wait for data */ + tower_check_for_read_packet (dev); + while (dev->read_packet_length == 0) { + if (file->f_flags & O_NONBLOCK) { + retval = -EAGAIN; + goto exit; + } + retval = wait_event_interruptible_timeout (dev->read_wait, atomic_read (&dev->interrupt_in_done), dev->packet_timeout_jiffies); + if (retval < 0) { + goto exit; + } - 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; - } + /* reset read timeout during read or write activity */ + if (read_timeout + && (dev->read_packet_length != dev->read_buffer_length + || atomic_read(&dev->interrupt_out_busy))) { + timeout = jiffies + read_timeout * HZ / 1000; + } + /* check for read timeout */ + if (read_timeout && time_after (jiffies, timeout)) { + retval = -ETIMEDOUT; + goto exit; } + tower_check_for_read_packet (dev); } - retval = bytes_read; + /* copy the data from read_buffer into userspace */ + bytes_to_read = min(count, dev->read_packet_length); + + if (copy_to_user (buffer, dev->read_buffer, bytes_to_read)) { + retval = -EFAULT; + goto exit; + } + + spin_lock_irq (&dev->read_buffer_lock); + dev->read_buffer_length -= bytes_to_read; + dev->read_packet_length -= bytes_to_read; + for (i=0; i<dev->read_buffer_length; i++) { + dev->read_buffer[i] = dev->read_buffer[i+bytes_to_read]; + } + spin_unlock_irq (&dev->read_buffer_lock); + + retval = bytes_to_read; exit: /* unlock the device */ up (&dev->sem); - dbg(2," %s : leave, return value %d", __func__, retval); + dbg(2, "%s: leave, return value %d", __FUNCTION__, retval); return retval; } @@ -473,13 +610,10 @@ exit: 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 = %Zd", __func__, count); + dbg(2, "%s: enter, count = %Zd", __FUNCTION__, count); dev = (struct lego_usb_tower *)file->private_data; @@ -495,85 +629,54 @@ static ssize_t tower_write (struct file /* verify that we actually have some data to write */ if (count == 0) { - dbg(1," %s : write request of 0 bytes", __func__); + dbg(1, "%s: write request of 0 bytes", __FUNCTION__); goto exit; } + /* wait until previous transfer is finished */ + while (atomic_read(&dev->interrupt_out_busy)) { + if (file->f_flags & O_NONBLOCK) { + retval = -EAGAIN; + goto exit; + } + retval = wait_event_interruptible (dev->write_wait, !atomic_read(&dev->interrupt_out_busy)); + if (retval) { + goto exit; + } + } - while (count > 0) { - if (dev->interrupt_out_urb->status == -EINPROGRESS) { - timeout = COMMAND_TIMEOUT; - - while (timeout > 0) { - if (signal_pending(current)) { - dbg(1," %s : interrupted", __func__); - retval = -EINTR; - goto exit; - } - 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 = %Zd", __func__, count); - } else { - dbg(4," %s : sending, count = %Zd", __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 = %Zd, count = %Zd, bytes_to_write = %Zd", __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; + /* write the data into interrupt_out_buffer from userspace */ + bytes_to_write = min(count, write_buffer_size); + dbg(4, "%s: count = %Zd, bytes_to_write = %Zd", __FUNCTION__, count, bytes_to_write); - bytes_written += bytes_to_write; - } + if (copy_from_user (dev->interrupt_out_buffer, buffer, bytes_to_write)) { + retval = -EFAULT; + goto exit; } - retval = bytes_written; + /* 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_out_endpoint->bInterval); + atomic_set (&dev->interrupt_out_busy, 1); + retval = usb_submit_urb (dev->interrupt_out_urb, GFP_KERNEL); + if (retval) { + atomic_set (&dev->interrupt_out_busy, 0); + err("Couldn't submit interrupt_out_urb %d", retval); + goto exit; + } + retval = bytes_to_write; exit: /* unlock the device */ up (&dev->sem); - dbg(2," %s : leave, return value %d", __func__, retval); + dbg(2, "%s: leave, return value %d", __FUNCTION__, retval); return retval; } @@ -585,39 +688,48 @@ exit: static void tower_interrupt_in_callback (struct urb *urb, struct pt_regs *regs) { struct lego_usb_tower *dev = (struct lego_usb_tower *)urb->context; + int retval; - dbg(4," %s : enter, status %d", __func__, urb->status); + dbg(4, "%s: enter, status %d", __FUNCTION__, urb->status); - lego_usb_tower_debug_data(5,__func__, urb->actual_length, urb->transfer_buffer); + lego_usb_tower_debug_data(5, __FUNCTION__, 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); + dbg(1, "%s: nonzero status received: %d", __FUNCTION__, 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); - + spin_lock (&dev->read_buffer_lock); + if (dev->read_buffer_length + urb->actual_length < read_buffer_size) { + 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__); + dev->read_last_arrival = jiffies; + dbg(3, "%s: received %d bytes", __FUNCTION__, urb->actual_length); + } + else { + printk(KERN_WARNING "%s: read_buffer overflow, %d bytes dropped", __FUNCTION__, urb->actual_length); } + spin_unlock (&dev->read_buffer_lock); } - 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); + /* resubmit if we're still running */ + if (atomic_read (&dev->interrupt_in_running)) { + retval = usb_submit_urb (dev->interrupt_in_urb, GFP_ATOMIC); + if (retval) { + err("%s: usb_submit_urb failed (%d)", __FUNCTION__, retval); + } + } + atomic_set (&dev->interrupt_in_done, 1); + wake_up_interruptible (&dev->read_wait); + + lego_usb_tower_debug_data(5, __FUNCTION__, urb->actual_length, urb->transfer_buffer); + dbg(4, "%s: leave, status %d", __FUNCTION__, urb->status); } @@ -628,22 +740,23 @@ static void tower_interrupt_out_callback { 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); + dbg(4, "%s: enter, status %d", __FUNCTION__, urb->status); + lego_usb_tower_debug_data(5, __FUNCTION__, urb->actual_length, urb->transfer_buffer); if (urb->status != 0) { - if ((urb->status != -ENOENT) && + if ((urb->status != -ENOENT) && (urb->status != -ECONNRESET)) { - dbg(1, " %s :nonzero status received: %d", __func__, urb->status); + dbg(1, "%s:nonzero status received: %d", __FUNCTION__, urb->status); } goto exit; - } + } - wake_up_interruptible(&dev->write_wait); exit: + atomic_set (&dev->interrupt_out_busy, 0); + wake_up_interruptible(&dev->write_wait); - lego_usb_tower_debug_data(5,__func__, urb->actual_length, urb->transfer_buffer); - dbg(4," %s : leave, status %d", __func__, urb->status); + lego_usb_tower_debug_data(5, __FUNCTION__, urb->actual_length, urb->transfer_buffer); + dbg(4, "%s: leave, status %d", __FUNCTION__, urb->status); } @@ -659,15 +772,18 @@ static int tower_probe (struct usb_inter struct lego_usb_tower *dev = NULL; struct usb_host_interface *iface_desc; struct usb_endpoint_descriptor* endpoint; + struct tower_reset_reply reset_reply; + struct tower_get_version_reply get_version_reply; int i; int retval = -ENOMEM; + int result; - dbg(2," %s : enter", __func__); + dbg(2, "%s: enter", __FUNCTION__); 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)) { @@ -691,6 +807,10 @@ static int tower_probe (struct usb_inter dev->read_buffer = NULL; dev->read_buffer_length = 0; + dev->read_packet_length = 0; + dev->read_buffer_lock = SPIN_LOCK_UNLOCKED; + dev->packet_timeout_jiffies = packet_timeout * HZ / 1000; + dev->read_last_arrival = jiffies; init_waitqueue_head (&dev->read_wait); init_waitqueue_head (&dev->write_wait); @@ -698,10 +818,12 @@ static int tower_probe (struct usb_inter dev->interrupt_in_buffer = NULL; dev->interrupt_in_endpoint = NULL; dev->interrupt_in_urb = NULL; + atomic_set (&dev->interrupt_in_done, 0); dev->interrupt_out_buffer = NULL; dev->interrupt_out_endpoint = NULL; dev->interrupt_out_urb = NULL; + atomic_set (&dev->interrupt_out_busy, 0); iface_desc = &interface->altsetting[0]; @@ -714,7 +836,7 @@ static int tower_probe (struct usb_inter ((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; @@ -729,7 +851,7 @@ static int tower_probe (struct usb_inter goto error; } - dev->read_buffer = kmalloc ((4*dev->interrupt_in_endpoint->wMaxPacketSize), GFP_KERNEL); + dev->read_buffer = kmalloc (read_buffer_size, GFP_KERNEL); if (!dev->read_buffer) { err("Couldn't allocate read_buffer"); goto error; @@ -744,7 +866,7 @@ static int tower_probe (struct usb_inter err("Couldn't allocate interrupt_in_urb"); goto error; } - dev->interrupt_out_buffer = kmalloc (dev->interrupt_out_endpoint->wMaxPacketSize, GFP_KERNEL); + dev->interrupt_out_buffer = kmalloc (write_buffer_size, GFP_KERNEL); if (!dev->interrupt_out_buffer) { err("Couldn't allocate interrupt_out_buffer"); goto error; @@ -753,7 +875,7 @@ static int tower_probe (struct usb_inter if (!dev->interrupt_out_urb) { err("Couldn't allocate interrupt_out_urb"); goto error; - } + } /* we can register the device now, as it is ready */ usb_set_intfdata (interface, dev); @@ -766,16 +888,50 @@ static int tower_probe (struct usb_inter usb_set_intfdata (interface, NULL); goto error; } - dev->minor = interface->minor; /* 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 - LEGO_USB_TOWER_MINOR_BASE)); + info ("LEGO USB Tower #%d now attached to major %d minor %d", (dev->minor - LEGO_USB_TOWER_MINOR_BASE), USB_MAJOR, dev->minor); + + /* reset the tower */ + result = usb_control_msg (udev, + usb_rcvctrlpipe(udev, 0), + LEGO_USB_TOWER_REQUEST_RESET, + USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_DEVICE, + 0, + 0, + &reset_reply, + sizeof(reset_reply), + HZ); + if (result < 0) { + err("LEGO USB Tower reset control request failed"); + retval = result; + goto error; + } + /* get the firmware version and log it */ + result = usb_control_msg (udev, + usb_rcvctrlpipe(udev, 0), + LEGO_USB_TOWER_REQUEST_GET_VERSION, + USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_DEVICE, + 0, + 0, + &get_version_reply, + sizeof(get_version_reply), + HZ); + if (result < 0) { + err("LEGO USB Tower get version control request failed"); + retval = result; + goto error; + } + info("LEGO USB Tower firmware version is %d.%d build %d", + get_version_reply.major, + get_version_reply.minor, + le16_to_cpu(get_version_reply.build_no)); exit: - dbg(2," %s : leave, return value 0x%.8lx (dev)", __func__, (long) dev); + dbg(2, "%s: leave, return value 0x%.8lx (dev)", __FUNCTION__, (long) dev); return retval; @@ -795,7 +951,7 @@ static void tower_disconnect (struct usb struct lego_usb_tower *dev; int minor; - dbg(2," %s : enter", __func__); + dbg(2, "%s: enter", __FUNCTION__); down (&disconnect_sem); @@ -823,7 +979,7 @@ static void tower_disconnect (struct usb info("LEGO USB Tower #%d now disconnected", (minor - LEGO_USB_TOWER_MINOR_BASE)); - dbg(2," %s : leave", __func__); + dbg(2, "%s: leave", __FUNCTION__); } @@ -836,7 +992,7 @@ static int __init lego_usb_tower_init(vo int result; int retval = 0; - dbg(2," %s : enter", __func__); + dbg(2, "%s: enter", __FUNCTION__); /* register this driver with the USB subsystem */ result = usb_register(&tower_driver); @@ -849,7 +1005,7 @@ static int __init lego_usb_tower_init(vo info(DRIVER_DESC " " DRIVER_VERSION); exit: - dbg(2," %s : leave, return value %d", __func__, retval); + dbg(2, "%s: leave, return value %d", __FUNCTION__, retval); return retval; } @@ -860,12 +1016,12 @@ exit: */ static void __exit lego_usb_tower_exit(void) { - dbg(2," %s : enter", __func__); + dbg(2, "%s: enter", __FUNCTION__); /* deregister this driver with the USB subsystem */ usb_deregister (&tower_driver); - dbg(2," %s : leave", __func__); + dbg(2, "%s: leave", __FUNCTION__); } module_init (lego_usb_tower_init); -- J�rgen Stuber <[EMAIL PROTECTED]> http://www.loria.fr/~stuber/ ------------------------------------------------------- This SF.Net email is sponsored by: IBM Linux Tutorials Free Linux tutorial presented by Daniel Robbins, President and CEO of GenToo technologies. Learn everything from fundamentals to system administration.http://ads.osdn.com/?ad_id=1470&alloc_id=3638&op=click _______________________________________________ [EMAIL PROTECTED] To unsubscribe, use the last form field at: https://lists.sourceforge.net/lists/listinfo/linux-usb-devel
