Hi,

this driver was long known to be unclean. This should fix all issues.
However, I have no hardware to test, so I'd appreciate any comment.

        Regards
                Oliver

-----

--- a/drivers/usb/serial/whiteheat.c    2007-03-20 20:34:36.000000000 +0100
+++ b/drivers/usb/serial/whiteheat.c    2007-03-22 13:05:42.000000000 +0100
@@ -74,6 +74,7 @@
 #include <linux/tty_flip.h>
 #include <linux/module.h>
 #include <linux/spinlock.h>
+#include <linux/mutex.h>
 #include <asm/uaccess.h>
 #include <asm/termbits.h>
 #include <linux/usb.h>
@@ -203,7 +204,8 @@
 
 
 struct whiteheat_command_private {
-       spinlock_t              lock;
+       struct mutex            mutex;
+       spinlock_t              buflock;
        __u8                    port_running;
        __u8                    command_finished;
        wait_queue_head_t       wait_command;   /* for handling sleeping while 
waiting for a command to finish */
@@ -225,6 +227,7 @@
        spinlock_t              lock;
        __u8                    flags;
        __u8                    mcr;
+       char                    shutting_down;
        struct list_head        rx_urbs_free;
        struct list_head        rx_urbs_submitted;
        struct list_head        rx_urb_q;
@@ -495,7 +498,8 @@
                goto no_command_private;
        }
 
-       spin_lock_init(&command_info->lock);
+       spin_lock_init(&command_info->buflock);
+       mutex_init(&command_info->mutex);
        command_info->port_running = 0;
        init_waitqueue_head(&command_info->wait_command);
        usb_set_serial_port_data(command_port, command_info);
@@ -552,7 +556,6 @@
        return -ENOMEM;
 }
 
-
 static void whiteheat_shutdown (struct usb_serial *serial)
 {
        struct usb_serial_port *command_port;
@@ -573,6 +576,11 @@
        for (i = 0; i < serial->num_ports; i++) {
                port = serial->port[i];
                info = usb_get_serial_port_data(port);
+
+               spin_lock_irq(&info->lock);
+               info->shutting_down = 1;
+               spin_unlock_irq(&info->lock);
+
                list_for_each_safe(tmp, tmp2, &info->rx_urbs_free) {
                        list_del(tmp);
                        wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
@@ -589,6 +597,7 @@
                        kfree(urb->transfer_buffer);
                        usb_free_urb(urb);
                }
+               flush_scheduled_work();
                kfree(info);
        }
 
@@ -685,21 +694,35 @@
 
        /* shutdown our bulk reads and writes */
        spin_lock_irqsave(&info->lock, flags);
+       info->shutting_down = 1;
+       smp_wmb(); 
+       /* this prevents new urbs in the queue
+        * thus we cannot miss an urb due to dropping
+        * the lock. We might doubly kill, but that's
+        * harmless */
        list_for_each_safe(tmp, tmp2, &info->rx_urbs_submitted) {
                wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
                urb = wrap->urb;
+               spin_unlock_irqrestore(&info->lock, flags);
                usb_kill_urb(urb);
-               list_move(tmp, &info->rx_urbs_free);
+               spin_lock_irqsave(&info->lock, flags);
        }
+       /* must walk again - cannot assume they stayed on the list */
+       list_for_each_safe(tmp, tmp2, &info->rx_urbs_submitted)
+               list_move(tmp, &info->rx_urbs_free);
+
        list_for_each_safe(tmp, tmp2, &info->rx_urb_q)
                list_move(tmp, &info->rx_urbs_free);
 
        list_for_each_safe(tmp, tmp2, &info->tx_urbs_submitted) {
                wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
                urb = wrap->urb;
+               spin_unlock_irqrestore(&info->lock, flags);
                usb_kill_urb(urb);
-               list_move(tmp, &info->tx_urbs_free);
+               spin_lock_irqsave(&info->lock, flags);
        }
+       list_for_each_safe(tmp, tmp2, &info->tx_urbs_submitted)
+               list_move(tmp, &info->tx_urbs_free);
        spin_unlock_irqrestore(&info->lock, flags);
 
        stop_command_port(port->serial);
@@ -724,7 +747,7 @@
 
        if (count == 0) {
                dbg("%s - write request of 0 bytes", __FUNCTION__);
-               return (0);
+               return 0;
        }
 
        while (count) {
@@ -746,24 +769,28 @@
 
                urb->dev = serial->dev;
                urb->transfer_buffer_length = bytes;
-               result = usb_submit_urb(urb, GFP_ATOMIC);
-               if (result) {
-                       err("%s - failed submitting write urb, error %d", 
__FUNCTION__, result);
-                       sent = result;
-                       spin_lock_irqsave(&info->lock, flags);
+               spin_lock_irqsave(&info->lock, flags);
+               if (!info->shutting_down) {
+                       result = usb_submit_urb(urb, GFP_ATOMIC);
+                       if (result) {
+                               err("%s - failed submitting write urb, error 
%d", __FUNCTION__, result);
+                               list_add(tmp, &info->tx_urbs_free);
+                               spin_unlock_irqrestore(&info->lock, flags);
+                               break;
+                       } else {
+                               sent += bytes;
+                               count -= bytes;
+                               list_add(tmp, &info->tx_urbs_submitted);
+                               spin_unlock_irqrestore(&info->lock, flags);
+                       }
+               } else {
                        list_add(tmp, &info->tx_urbs_free);
                        spin_unlock_irqrestore(&info->lock, flags);
                        break;
-               } else {
-                       sent += bytes;
-                       count -= bytes;
-                       spin_lock_irqsave(&info->lock, flags);
-                       list_add(tmp, &info->tx_urbs_submitted);
-                       spin_unlock_irqrestore(&info->lock, flags);
                }
        }
 
-       return sent;
+       return sent ? sent : result;
 }
 
 
@@ -865,10 +892,10 @@
                        break;
 
                default:
-                       break;
+                       return -ENOIOCTLCMD;
        }
 
-       return -ENOIOCTLCMD;
+       return 0;
 }
 
 
@@ -920,7 +947,7 @@
        spin_unlock_irqrestore(&info->lock, flags);
 
        dbg ("%s - returns %d", __FUNCTION__, chars);
-       return (chars);
+       return chars;
 }
 
 
@@ -979,7 +1006,6 @@
        struct whiteheat_command_private *command_info;
        unsigned char *data = urb->transfer_buffer;
        int result;
-       unsigned long flags;
 
        dbg("%s", __FUNCTION__);
 
@@ -995,7 +1021,7 @@
                dbg ("%s - command_info is NULL, exiting.", __FUNCTION__);
                return;
        }
-       spin_lock_irqsave(&command_info->lock, flags);
+       spin_lock(&command_info->buflock);
 
        if (data[0] == WHITEHEAT_CMD_COMPLETE) {
                command_info->command_finished = WHITEHEAT_CMD_COMPLETE;
@@ -1017,7 +1043,7 @@
        /* Continue trying to always read */
        command_port->read_urb->dev = command_port->serial->dev;
        result = usb_submit_urb(command_port->read_urb, GFP_ATOMIC);
-       spin_unlock_irqrestore(&command_info->lock, flags);
+       spin_unlock(&command_info->buflock);
        if (result)
                dbg("%s - failed resubmitting read urb, error %d", 
__FUNCTION__, result);
 }
@@ -1040,19 +1066,14 @@
                return;
        }
        list_del(&wrap->list);
-       spin_unlock(&info->lock);
 
        if (urb->status) {
                dbg("%s - nonzero read bulk status received: %d", __FUNCTION__, 
urb->status);
-               spin_lock(&info->lock);
                list_add(&wrap->list, &info->rx_urbs_free);
                spin_unlock(&info->lock);
                return;
        }
 
-       usb_serial_debug_data(debug, &port->dev, __FUNCTION__, 
urb->actual_length, data);
-
-       spin_lock(&info->lock);
        list_add_tail(&wrap->list, &info->rx_urb_q);
        if (info->flags & THROTTLED) {
                info->flags |= ACTUALLY_THROTTLED;
@@ -1060,6 +1081,7 @@
                return;
        }
        spin_unlock(&info->lock);
+       usb_serial_debug_data(debug, &port->dev, __FUNCTION__, 
urb->actual_length, data);
 
        schedule_work(&info->rx_work);
 }
@@ -1070,10 +1092,12 @@
        struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
        struct whiteheat_private *info = usb_get_serial_port_data(port);
        struct whiteheat_urb_wrap *wrap;
+       int status;
 
        dbg("%s - port %d", __FUNCTION__, port->number);
 
        spin_lock(&info->lock);
+       status = urb->status;
        wrap = urb_to_wrap(urb, &info->tx_urbs_submitted);
        if (!wrap) {
                spin_unlock(&info->lock);
@@ -1083,8 +1107,8 @@
        list_move(&wrap->list, &info->tx_urbs_free);
        spin_unlock(&info->lock);
 
-       if (urb->status) {
-               dbg("%s - nonzero write bulk status received: %d", 
__FUNCTION__, urb->status);
+       if (status) {
+               dbg("%s - nonzero write bulk status received: %d", 
__FUNCTION__, status);
                return;
        }
 
@@ -1102,13 +1126,12 @@
        struct whiteheat_private *info;
        __u8 *transfer_buffer;
        int retval = 0;
-       unsigned long flags;
 
        dbg("%s - command %d", __FUNCTION__, command);
 
        command_port = port->serial->port[COMMAND_PORT];
        command_info = usb_get_serial_port_data(command_port);
-       spin_lock_irqsave(&command_info->lock, flags);
+       mutex_lock(&command_info->mutex);
        command_info->command_finished = FALSE;
        
        transfer_buffer = (__u8 *)command_port->write_urb->transfer_buffer;
@@ -1116,29 +1139,28 @@
        memcpy (&transfer_buffer[1], data, datasize);
        command_port->write_urb->transfer_buffer_length = datasize + 1;
        command_port->write_urb->dev = port->serial->dev;
-       retval = usb_submit_urb (command_port->write_urb, GFP_KERNEL);
+       retval = usb_submit_urb(command_port->write_urb, GFP_NOIO);
        if (retval) {
                dbg("%s - submit urb failed", __FUNCTION__);
                goto exit;
        }
-       spin_unlock_irqrestore(&command_info->lock, flags);
 
        /* wait for the command to complete */
        wait_event_interruptible_timeout(command_info->wait_command, 
                (command_info->command_finished != FALSE), COMMAND_TIMEOUT);
 
-       spin_lock_irqsave(&command_info->lock, flags);
 
+       spin_lock_irq(&command_info->buflock);
        if (command_info->command_finished == FALSE) {
                dbg("%s - command timed out.", __FUNCTION__);
                retval = -ETIMEDOUT;
-               goto exit;
+               goto exit2;
        }
 
        if (command_info->command_finished == WHITEHEAT_CMD_FAILURE) {
                dbg("%s - command failed.", __FUNCTION__);
                retval = -EIO;
-               goto exit;
+               goto exit2;
        }
 
        if (command_info->command_finished == WHITEHEAT_CMD_COMPLETE) {
@@ -1150,9 +1172,10 @@
                                break;
                }
        }
-
+exit2:
+       spin_unlock_irq(&command_info->buflock);
 exit:
-       spin_unlock_irqrestore(&command_info->lock, flags);
+       mutex_unlock(&command_info->mutex);
        return retval;
 }
 
@@ -1305,12 +1328,11 @@
 {
        struct usb_serial_port *command_port;
        struct whiteheat_command_private *command_info;
-       unsigned long flags;
        int retval = 0;
        
        command_port = serial->port[COMMAND_PORT];
        command_info = usb_get_serial_port_data(command_port);
-       spin_lock_irqsave(&command_info->lock, flags);
+       mutex_lock(&command_info->mutex);
        if (!command_info->port_running) {
                /* Work around HCD bugs */
                usb_clear_halt(serial->dev, command_port->read_urb->pipe);
@@ -1325,7 +1347,7 @@
        command_info->port_running++;
 
 exit:
-       spin_unlock_irqrestore(&command_info->lock, flags);
+       mutex_unlock(&command_info->mutex);
        return retval;
 }
 
@@ -1334,15 +1356,14 @@
 {
        struct usb_serial_port *command_port;
        struct whiteheat_command_private *command_info;
-       unsigned long flags;
 
        command_port = serial->port[COMMAND_PORT];
        command_info = usb_get_serial_port_data(command_port);
-       spin_lock_irqsave(&command_info->lock, flags);
+       mutex_lock(&command_info->mutex);
        command_info->port_running--;
        if (!command_info->port_running)
                usb_kill_urb(command_port->read_urb);
-       spin_unlock_irqrestore(&command_info->lock, flags);
+       mutex_unlock(&command_info->mutex);
 }
 
 
@@ -1363,15 +1384,22 @@
                wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
                urb = wrap->urb;
                urb->dev = port->serial->dev;
-               retval = usb_submit_urb(urb, GFP_KERNEL);
+               retval = usb_submit_urb(urb, GFP_ATOMIC);
                if (retval) {
+                       info->shutting_down = 1;
+                       smp_wmb();
                        list_add(tmp, &info->rx_urbs_free);
                        list_for_each_safe(tmp, tmp2, &info->rx_urbs_submitted) 
{
                                wrap = list_entry(tmp, struct 
whiteheat_urb_wrap, list);
                                urb = wrap->urb;
+                               spin_unlock_irqrestore(&info->lock, flags);
                                usb_kill_urb(urb);
-                               list_move(tmp, &info->rx_urbs_free);
+                               spin_lock_irqsave(&info->lock, flags);
                        }
+                       list_for_each_safe(tmp, tmp2, &info->rx_urbs_submitted)
+                               list_move(tmp, &info->rx_urbs_free);
+                       smp_wmb();
+                       info->shutting_down = 0;
                        break;
                }
                list_add(tmp, &info->rx_urbs_submitted);
@@ -1426,7 +1454,6 @@
 
        list_for_each_safe(tmp, tmp2, &info->rx_urb_q) {
                list_del(tmp);
-               spin_unlock_irqrestore(&info->lock, flags);
 
                wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
                urb = wrap->urb;
@@ -1435,9 +1462,7 @@
                        int len = tty_buffer_request_room(tty, 
urb->actual_length);
                        /* This stuff can go away now I suspect */
                        if (unlikely(len < urb->actual_length)) {
-                               spin_lock_irqsave(&info->lock, flags);
                                list_add(tmp, &info->rx_urb_q);
-                               spin_unlock_irqrestore(&info->lock, flags);
                                tty_flip_buffer_push(tty);
                                schedule_work(&info->rx_work);
                                return;
@@ -1447,15 +1472,16 @@
                }
 
                urb->dev = port->serial->dev;
-               result = usb_submit_urb(urb, GFP_ATOMIC);
+               if (!info->shutting_down)
+                       result = usb_submit_urb(urb, GFP_ATOMIC);
+               else
+                       result = -EBUSY;
                if (result) {
                        err("%s - failed resubmitting read urb, error %d", 
__FUNCTION__, result);
-                       spin_lock_irqsave(&info->lock, flags);
                        list_add(tmp, &info->rx_urbs_free);
                        continue;
                }
 
-               spin_lock_irqsave(&info->lock, flags);
                list_add(tmp, &info->rx_urbs_submitted);
        }
        spin_unlock_irqrestore(&info->lock, flags);

-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys-and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
_______________________________________________
[email protected]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel

Reply via email to