root/drivers/net/wimax/i2400m/usb-tx.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. i2400mu_tx
  2. i2400mu_txd
  3. i2400mu_bus_tx_kick
  4. i2400mu_tx_setup
  5. i2400mu_tx_release

   1 /*
   2  * Intel Wireless WiMAX Connection 2400m
   3  * USB specific TX handling
   4  *
   5  *
   6  * Copyright (C) 2007-2008 Intel Corporation. All rights reserved.
   7  *
   8  * Redistribution and use in source and binary forms, with or without
   9  * modification, are permitted provided that the following conditions
  10  * are met:
  11  *
  12  *   * Redistributions of source code must retain the above copyright
  13  *     notice, this list of conditions and the following disclaimer.
  14  *   * Redistributions in binary form must reproduce the above copyright
  15  *     notice, this list of conditions and the following disclaimer in
  16  *     the documentation and/or other materials provided with the
  17  *     distribution.
  18  *   * Neither the name of Intel Corporation nor the names of its
  19  *     contributors may be used to endorse or promote products derived
  20  *     from this software without specific prior written permission.
  21  *
  22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  23  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  24  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  25  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  26  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  27  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  28  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  29  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  30  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  31  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  32  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  33  *
  34  *
  35  * Intel Corporation <linux-wimax@intel.com>
  36  * Yanir Lubetkin <yanirx.lubetkin@intel.com>
  37  *  - Initial implementation
  38  * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
  39  *  - Split transport/device specific
  40  *
  41  *
  42  * Takes the TX messages in the i2400m's driver TX FIFO and sends them
  43  * to the device until there are no more.
  44  *
  45  * If we fail sending the message, we just drop it. There isn't much
  46  * we can do at this point. We could also retry, but the USB stack has
  47  * already retried and still failed, so there is not much of a
  48  * point. As well, most of the traffic is network, which has recovery
  49  * methods for dropped packets.
  50  *
  51  * For sending we just obtain a FIFO buffer to send, send it to the
  52  * USB bulk out, tell the TX FIFO code we have sent it; query for
  53  * another one, etc... until done.
  54  *
  55  * We use a thread so we can call usb_autopm_enable() and
  56  * usb_autopm_disable() for each transaction; this way when the device
  57  * goes idle, it will suspend. It also has less overhead than a
  58  * dedicated workqueue, as it is being used for a single task.
  59  *
  60  * ROADMAP
  61  *
  62  * i2400mu_tx_setup()
  63  * i2400mu_tx_release()
  64  *
  65  * i2400mu_bus_tx_kick()        - Called by the tx.c code when there
  66  *                                is new data in the FIFO.
  67  * i2400mu_txd()
  68  *   i2400m_tx_msg_get()
  69  *   i2400m_tx_msg_sent()
  70  */
  71 #include "i2400m-usb.h"
  72 
  73 
  74 #define D_SUBMODULE tx
  75 #include "usb-debug-levels.h"
  76 
  77 
  78 /*
  79  * Get the next TX message in the TX FIFO and send it to the device
  80  *
  81  * Note that any iteration consumes a message to be sent, no matter if
  82  * it succeeds or fails (we have no real way to retry or complain).
  83  *
  84  * Return: 0 if ok, < 0 errno code on hard error.
  85  */
  86 static
  87 int i2400mu_tx(struct i2400mu *i2400mu, struct i2400m_msg_hdr *tx_msg,
  88                size_t tx_msg_size)
  89 {
  90         int result = 0;
  91         struct i2400m *i2400m = &i2400mu->i2400m;
  92         struct device *dev = &i2400mu->usb_iface->dev;
  93         int usb_pipe, sent_size, do_autopm;
  94         struct usb_endpoint_descriptor *epd;
  95 
  96         d_fnstart(4, dev, "(i2400mu %p)\n", i2400mu);
  97         do_autopm = atomic_read(&i2400mu->do_autopm);
  98         result = do_autopm ?
  99                 usb_autopm_get_interface(i2400mu->usb_iface) : 0;
 100         if (result < 0) {
 101                 dev_err(dev, "TX: can't get autopm: %d\n", result);
 102                 do_autopm = 0;
 103         }
 104         epd = usb_get_epd(i2400mu->usb_iface, i2400mu->endpoint_cfg.bulk_out);
 105         usb_pipe = usb_sndbulkpipe(i2400mu->usb_dev, epd->bEndpointAddress);
 106 retry:
 107         result = usb_bulk_msg(i2400mu->usb_dev, usb_pipe,
 108                               tx_msg, tx_msg_size, &sent_size, 200);
 109         usb_mark_last_busy(i2400mu->usb_dev);
 110         switch (result) {
 111         case 0:
 112                 if (sent_size != tx_msg_size) { /* Too short? drop it */
 113                         dev_err(dev, "TX: short write (%d B vs %zu "
 114                                 "expected)\n", sent_size, tx_msg_size);
 115                         result = -EIO;
 116                 }
 117                 break;
 118         case -EPIPE:
 119                 /*
 120                  * Stall -- maybe the device is choking with our
 121                  * requests. Clear it and give it some time. If they
 122                  * happen to often, it might be another symptom, so we
 123                  * reset.
 124                  *
 125                  * No error handling for usb_clear_halt(0; if it
 126                  * works, the retry works; if it fails, this switch
 127                  * does the error handling for us.
 128                  */
 129                 if (edc_inc(&i2400mu->urb_edc,
 130                             10 * EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)) {
 131                         dev_err(dev, "BM-CMD: too many stalls in "
 132                                 "URB; resetting device\n");
 133                         usb_queue_reset_device(i2400mu->usb_iface);
 134                 } else {
 135                         usb_clear_halt(i2400mu->usb_dev, usb_pipe);
 136                         msleep(10);     /* give the device some time */
 137                         goto retry;
 138                 }
 139                 /* fall through */
 140         case -EINVAL:                   /* while removing driver */
 141         case -ENODEV:                   /* dev disconnect ... */
 142         case -ENOENT:                   /* just ignore it */
 143         case -ESHUTDOWN:                /* and exit */
 144         case -ECONNRESET:
 145                 result = -ESHUTDOWN;
 146                 break;
 147         default:                        /* Some error? */
 148                 if (edc_inc(&i2400mu->urb_edc,
 149                             EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)) {
 150                         dev_err(dev, "TX: maximum errors in URB "
 151                                 "exceeded; resetting device\n");
 152                         usb_queue_reset_device(i2400mu->usb_iface);
 153                 } else {
 154                         dev_err(dev, "TX: cannot send URB; retrying. "
 155                                 "tx_msg @%zu %zu B [%d sent]: %d\n",
 156                                 (void *) tx_msg - i2400m->tx_buf,
 157                                 tx_msg_size, sent_size, result);
 158                         goto retry;
 159                 }
 160         }
 161         if (do_autopm)
 162                 usb_autopm_put_interface(i2400mu->usb_iface);
 163         d_fnend(4, dev, "(i2400mu %p) = result\n", i2400mu);
 164         return result;
 165 }
 166 
 167 
 168 /*
 169  * Get the next TX message in the TX FIFO and send it to the device
 170  *
 171  * Note we exit the loop if i2400mu_tx() fails; that function only
 172  * fails on hard error (failing to tx a buffer not being one of them,
 173  * see its doc).
 174  *
 175  * Return: 0
 176  */
 177 static
 178 int i2400mu_txd(void *_i2400mu)
 179 {
 180         struct i2400mu *i2400mu = _i2400mu;
 181         struct i2400m *i2400m = &i2400mu->i2400m;
 182         struct device *dev = &i2400mu->usb_iface->dev;
 183         struct i2400m_msg_hdr *tx_msg;
 184         size_t tx_msg_size;
 185         unsigned long flags;
 186 
 187         d_fnstart(4, dev, "(i2400mu %p)\n", i2400mu);
 188 
 189         spin_lock_irqsave(&i2400m->tx_lock, flags);
 190         BUG_ON(i2400mu->tx_kthread != NULL);
 191         i2400mu->tx_kthread = current;
 192         spin_unlock_irqrestore(&i2400m->tx_lock, flags);
 193 
 194         while (1) {
 195                 d_printf(2, dev, "TX: waiting for messages\n");
 196                 tx_msg = NULL;
 197                 wait_event_interruptible(
 198                         i2400mu->tx_wq,
 199                         (kthread_should_stop()  /* check this first! */
 200                          || (tx_msg = i2400m_tx_msg_get(i2400m, &tx_msg_size)))
 201                         );
 202                 if (kthread_should_stop())
 203                         break;
 204                 WARN_ON(tx_msg == NULL);        /* should not happen...*/
 205                 d_printf(2, dev, "TX: submitting %zu bytes\n", tx_msg_size);
 206                 d_dump(5, dev, tx_msg, tx_msg_size);
 207                 /* Yeah, we ignore errors ... not much we can do */
 208                 i2400mu_tx(i2400mu, tx_msg, tx_msg_size);
 209                 i2400m_tx_msg_sent(i2400m);     /* ack it, advance the FIFO */
 210         }
 211 
 212         spin_lock_irqsave(&i2400m->tx_lock, flags);
 213         i2400mu->tx_kthread = NULL;
 214         spin_unlock_irqrestore(&i2400m->tx_lock, flags);
 215 
 216         d_fnend(4, dev, "(i2400mu %p)\n", i2400mu);
 217         return 0;
 218 }
 219 
 220 
 221 /*
 222  * i2400m TX engine notifies us that there is data in the FIFO ready
 223  * for TX
 224  *
 225  * If there is a URB in flight, don't do anything; when it finishes,
 226  * it will see there is data in the FIFO and send it. Else, just
 227  * submit a write.
 228  */
 229 void i2400mu_bus_tx_kick(struct i2400m *i2400m)
 230 {
 231         struct i2400mu *i2400mu = container_of(i2400m, struct i2400mu, i2400m);
 232         struct device *dev = &i2400mu->usb_iface->dev;
 233 
 234         d_fnstart(3, dev, "(i2400m %p) = void\n", i2400m);
 235         wake_up_all(&i2400mu->tx_wq);
 236         d_fnend(3, dev, "(i2400m %p) = void\n", i2400m);
 237 }
 238 
 239 
 240 int i2400mu_tx_setup(struct i2400mu *i2400mu)
 241 {
 242         int result = 0;
 243         struct i2400m *i2400m = &i2400mu->i2400m;
 244         struct device *dev = &i2400mu->usb_iface->dev;
 245         struct wimax_dev *wimax_dev = &i2400m->wimax_dev;
 246         struct task_struct *kthread;
 247 
 248         kthread = kthread_run(i2400mu_txd, i2400mu, "%s-tx",
 249                               wimax_dev->name);
 250         /* the kthread function sets i2400mu->tx_thread */
 251         if (IS_ERR(kthread)) {
 252                 result = PTR_ERR(kthread);
 253                 dev_err(dev, "TX: cannot start thread: %d\n", result);
 254         }
 255         return result;
 256 }
 257 
 258 void i2400mu_tx_release(struct i2400mu *i2400mu)
 259 {
 260         unsigned long flags;
 261         struct i2400m *i2400m = &i2400mu->i2400m;
 262         struct device *dev = i2400m_dev(i2400m);
 263         struct task_struct *kthread;
 264 
 265         spin_lock_irqsave(&i2400m->tx_lock, flags);
 266         kthread = i2400mu->tx_kthread;
 267         i2400mu->tx_kthread = NULL;
 268         spin_unlock_irqrestore(&i2400m->tx_lock, flags);
 269         if (kthread)
 270                 kthread_stop(kthread);
 271         else
 272                 d_printf(1, dev, "TX: kthread had already exited\n");
 273 }

/* [<][>][^][v][top][bottom][index][help] */