- patches.suse/slab-handle-memoryless-nodes-v2a.patch: Refresh.
[linux-flexiantxendom0-3.2.10.git] / drivers / net / wimax / i2400m / usb-tx.c
index dfd8933..c65b997 100644 (file)
@@ -101,11 +101,11 @@ int i2400mu_tx(struct i2400mu *i2400mu, struct i2400m_msg_hdr *tx_msg,
                dev_err(dev, "TX: can't get autopm: %d\n", result);
                do_autopm = 0;
        }
-       epd = usb_get_epd(i2400mu->usb_iface, I2400MU_EP_BULK_OUT);
+       epd = usb_get_epd(i2400mu->usb_iface, i2400mu->endpoint_cfg.bulk_out);
        usb_pipe = usb_sndbulkpipe(i2400mu->usb_dev, epd->bEndpointAddress);
 retry:
        result = usb_bulk_msg(i2400mu->usb_dev, usb_pipe,
-                             tx_msg, tx_msg_size, &sent_size, HZ);
+                             tx_msg, tx_msg_size, &sent_size, 200);
        usb_mark_last_busy(i2400mu->usb_dev);
        switch (result) {
        case 0:
@@ -115,6 +115,28 @@ retry:
                        result = -EIO;
                }
                break;
+       case -EPIPE:
+               /*
+                * Stall -- maybe the device is choking with our
+                * requests. Clear it and give it some time. If they
+                * happen to often, it might be another symptom, so we
+                * reset.
+                *
+                * No error handling for usb_clear_halt(0; if it
+                * works, the retry works; if it fails, this switch
+                * does the error handling for us.
+                */
+               if (edc_inc(&i2400mu->urb_edc,
+                           10 * EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)) {
+                       dev_err(dev, "BM-CMD: too many stalls in "
+                               "URB; resetting device\n");
+                       usb_queue_reset_device(i2400mu->usb_iface);
+                       /* fallthrough */
+               } else {
+                       usb_clear_halt(i2400mu->usb_dev, usb_pipe);
+                       msleep(10);     /* give the device some time */
+                       goto retry;
+               }
        case -EINVAL:                   /* while removing driver */
        case -ENODEV:                   /* dev disconnect ... */
        case -ENOENT:                   /* just ignore it */
@@ -161,9 +183,15 @@ int i2400mu_txd(void *_i2400mu)
        struct device *dev = &i2400mu->usb_iface->dev;
        struct i2400m_msg_hdr *tx_msg;
        size_t tx_msg_size;
+       unsigned long flags;
 
        d_fnstart(4, dev, "(i2400mu %p)\n", i2400mu);
 
+       spin_lock_irqsave(&i2400m->tx_lock, flags);
+       BUG_ON(i2400mu->tx_kthread != NULL);
+       i2400mu->tx_kthread = current;
+       spin_unlock_irqrestore(&i2400m->tx_lock, flags);
+
        while (1) {
                d_printf(2, dev, "TX: waiting for messages\n");
                tx_msg = NULL;
@@ -183,6 +211,11 @@ int i2400mu_txd(void *_i2400mu)
                if (result < 0)
                        break;
        }
+
+       spin_lock_irqsave(&i2400m->tx_lock, flags);
+       i2400mu->tx_kthread = NULL;
+       spin_unlock_irqrestore(&i2400m->tx_lock, flags);
+
        d_fnend(4, dev, "(i2400mu %p) = %d\n", i2400mu, result);
        return result;
 }
@@ -213,11 +246,13 @@ int i2400mu_tx_setup(struct i2400mu *i2400mu)
        struct i2400m *i2400m = &i2400mu->i2400m;
        struct device *dev = &i2400mu->usb_iface->dev;
        struct wimax_dev *wimax_dev = &i2400m->wimax_dev;
+       struct task_struct *kthread;
 
-       i2400mu->tx_kthread = kthread_run(i2400mu_txd, i2400mu, "%s-tx",
-                                         wimax_dev->name);
-       if (IS_ERR(i2400mu->tx_kthread)) {
-               result = PTR_ERR(i2400mu->tx_kthread);
+       kthread = kthread_run(i2400mu_txd, i2400mu, "%s-tx",
+                             wimax_dev->name);
+       /* the kthread function sets i2400mu->tx_thread */
+       if (IS_ERR(kthread)) {
+               result = PTR_ERR(kthread);
                dev_err(dev, "TX: cannot start thread: %d\n", result);
        }
        return result;
@@ -225,5 +260,17 @@ int i2400mu_tx_setup(struct i2400mu *i2400mu)
 
 void i2400mu_tx_release(struct i2400mu *i2400mu)
 {
-       kthread_stop(i2400mu->tx_kthread);
+       unsigned long flags;
+       struct i2400m *i2400m = &i2400mu->i2400m;
+       struct device *dev = i2400m_dev(i2400m);
+       struct task_struct *kthread;
+
+       spin_lock_irqsave(&i2400m->tx_lock, flags);
+       kthread = i2400mu->tx_kthread;
+       i2400mu->tx_kthread = NULL;
+       spin_unlock_irqrestore(&i2400m->tx_lock, flags);
+       if (kthread)
+               kthread_stop(kthread);
+       else
+               d_printf(1, dev, "TX: kthread had already exited\n");
 }