linux/drivers/net/wimax/i2400m/usb-fw.c
<<
>>
Prefs
   1/*
   2 * Intel Wireless WiMAX Connection 2400m
   3 * Firmware uploader's USB specifics
   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 * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
  38 *  - Initial implementation
  39 *
  40 * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
  41 *  - bus generic/specific split
  42 *
  43 * THE PROCEDURE
  44 *
  45 * See fw.c for the generic description of this procedure.
  46 *
  47 * This file implements only the USB specifics. It boils down to how
  48 * to send a command and waiting for an acknowledgement from the
  49 * device.
  50 *
  51 * This code (and process) is single threaded. It assumes it is the
  52 * only thread poking around (guaranteed by fw.c).
  53 *
  54 * COMMAND EXECUTION
  55 *
  56 * A write URB is posted with the buffer to the bulk output endpoint.
  57 *
  58 * ACK RECEPTION
  59 *
  60 * We just post a URB to the notification endpoint and wait for
  61 * data. We repeat until we get all the data we expect (as indicated
  62 * by the call from the bus generic code).
  63 *
  64 * The data is not read from the bulk in endpoint for boot mode.
  65 *
  66 * ROADMAP
  67 *
  68 * i2400mu_bus_bm_cmd_send
  69 *   i2400m_bm_cmd_prepare...
  70 *   i2400mu_tx_bulk_out
  71 *
  72 * i2400mu_bus_bm_wait_for_ack
  73 *   i2400m_notif_submit
  74 */
  75#include <linux/usb.h>
  76#include <linux/gfp.h>
  77#include "i2400m-usb.h"
  78
  79
  80#define D_SUBMODULE fw
  81#include "usb-debug-levels.h"
  82
  83
  84/*
  85 * Synchronous write to the device
  86 *
  87 * Takes care of updating EDC counts and thus, handle device errors.
  88 */
  89static
  90ssize_t i2400mu_tx_bulk_out(struct i2400mu *i2400mu, void *buf, size_t buf_size)
  91{
  92        int result;
  93        struct device *dev = &i2400mu->usb_iface->dev;
  94        int len;
  95        struct usb_endpoint_descriptor *epd;
  96        int pipe, do_autopm = 1;
  97
  98        result = usb_autopm_get_interface(i2400mu->usb_iface);
  99        if (result < 0) {
 100                dev_err(dev, "BM-CMD: can't get autopm: %d\n", result);
 101                do_autopm = 0;
 102        }
 103        epd = usb_get_epd(i2400mu->usb_iface, i2400mu->endpoint_cfg.bulk_out);
 104        pipe = usb_sndbulkpipe(i2400mu->usb_dev, epd->bEndpointAddress);
 105retry:
 106        result = usb_bulk_msg(i2400mu->usb_dev, pipe, buf, buf_size, &len, 200);
 107        switch (result) {
 108        case 0:
 109                if (len != buf_size) {
 110                        dev_err(dev, "BM-CMD: short write (%u B vs %zu "
 111                                "expected)\n", len, buf_size);
 112                        result = -EIO;
 113                        break;
 114                }
 115                result = len;
 116                break;
 117        case -EPIPE:
 118                /*
 119                 * Stall -- maybe the device is choking with our
 120                 * requests. Clear it and give it some time. If they
 121                 * happen to often, it might be another symptom, so we
 122                 * reset.
 123                 *
 124                 * No error handling for usb_clear_halt(0; if it
 125                 * works, the retry works; if it fails, this switch
 126                 * does the error handling for us.
 127                 */
 128                if (edc_inc(&i2400mu->urb_edc,
 129                            10 * EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)) {
 130                        dev_err(dev, "BM-CMD: too many stalls in "
 131                                "URB; resetting device\n");
 132                        usb_queue_reset_device(i2400mu->usb_iface);
 133                        /* fallthrough */
 134                } else {
 135                        usb_clear_halt(i2400mu->usb_dev, pipe);
 136                        msleep(10);     /* give the device some time */
 137                        goto retry;
 138                }
 139        case -EINVAL:                   /* while removing driver */
 140        case -ENODEV:                   /* dev disconnect ... */
 141        case -ENOENT:                   /* just ignore it */
 142        case -ESHUTDOWN:                /* and exit */
 143        case -ECONNRESET:
 144                result = -ESHUTDOWN;
 145                break;
 146        case -ETIMEDOUT:                        /* bah... */
 147                break;
 148        default:                                /* any other? */
 149                if (edc_inc(&i2400mu->urb_edc,
 150                            EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)) {
 151                                dev_err(dev, "BM-CMD: maximum errors in "
 152                                        "URB exceeded; resetting device\n");
 153                                usb_queue_reset_device(i2400mu->usb_iface);
 154                                result = -ENODEV;
 155                                break;
 156                }
 157                dev_err(dev, "BM-CMD: URB error %d, retrying\n",
 158                        result);
 159                goto retry;
 160        }
 161        if (do_autopm)
 162                usb_autopm_put_interface(i2400mu->usb_iface);
 163        return result;
 164}
 165
 166
 167/*
 168 * Send a boot-mode command over the bulk-out pipe
 169 *
 170 * Command can be a raw command, which requires no preparation (and
 171 * which might not even be following the command format). Checks that
 172 * the right amount of data was transferred.
 173 *
 174 * To satisfy USB requirements (no onstack, vmalloc or in data segment
 175 * buffers), we copy the command to i2400m->bm_cmd_buf and send it from
 176 * there.
 177 *
 178 * @flags: pass thru from i2400m_bm_cmd()
 179 * @return: cmd_size if ok, < 0 errno code on error.
 180 */
 181ssize_t i2400mu_bus_bm_cmd_send(struct i2400m *i2400m,
 182                                const struct i2400m_bootrom_header *_cmd,
 183                                size_t cmd_size, int flags)
 184{
 185        ssize_t result;
 186        struct device *dev = i2400m_dev(i2400m);
 187        struct i2400mu *i2400mu = container_of(i2400m, struct i2400mu, i2400m);
 188        int opcode = _cmd == NULL ? -1 : i2400m_brh_get_opcode(_cmd);
 189        struct i2400m_bootrom_header *cmd;
 190        size_t cmd_size_a = ALIGN(cmd_size, 16);        /* USB restriction */
 191
 192        d_fnstart(8, dev, "(i2400m %p cmd %p size %zu)\n",
 193                  i2400m, _cmd, cmd_size);
 194        result = -E2BIG;
 195        if (cmd_size > I2400M_BM_CMD_BUF_SIZE)
 196                goto error_too_big;
 197        if (_cmd != i2400m->bm_cmd_buf)
 198                memmove(i2400m->bm_cmd_buf, _cmd, cmd_size);
 199        cmd = i2400m->bm_cmd_buf;
 200        if (cmd_size_a > cmd_size)                      /* Zero pad space */
 201                memset(i2400m->bm_cmd_buf + cmd_size, 0, cmd_size_a - cmd_size);
 202        if ((flags & I2400M_BM_CMD_RAW) == 0) {
 203                if (WARN_ON(i2400m_brh_get_response_required(cmd) == 0))
 204                        dev_warn(dev, "SW BUG: response_required == 0\n");
 205                i2400m_bm_cmd_prepare(cmd);
 206        }
 207        result = i2400mu_tx_bulk_out(i2400mu, i2400m->bm_cmd_buf, cmd_size);
 208        if (result < 0) {
 209                dev_err(dev, "boot-mode cmd %d: cannot send: %zd\n",
 210                        opcode, result);
 211                goto error_cmd_send;
 212        }
 213        if (result != cmd_size) {               /* all was transferred? */
 214                dev_err(dev, "boot-mode cmd %d: incomplete transfer "
 215                        "(%zd vs %zu submitted)\n",  opcode, result, cmd_size);
 216                result = -EIO;
 217                goto error_cmd_size;
 218        }
 219error_cmd_size:
 220error_cmd_send:
 221error_too_big:
 222        d_fnend(8, dev, "(i2400m %p cmd %p size %zu) = %zd\n",
 223                i2400m, _cmd, cmd_size, result);
 224        return result;
 225}
 226
 227
 228static
 229void __i2400mu_bm_notif_cb(struct urb *urb)
 230{
 231        complete(urb->context);
 232}
 233
 234
 235/*
 236 * submit a read to the notification endpoint
 237 *
 238 * @i2400m: device descriptor
 239 * @urb: urb to use
 240 * @completion: completion varible to complete when done
 241 *
 242 * Data is always read to i2400m->bm_ack_buf
 243 */
 244static
 245int i2400mu_notif_submit(struct i2400mu *i2400mu, struct urb *urb,
 246                         struct completion *completion)
 247{
 248        struct i2400m *i2400m = &i2400mu->i2400m;
 249        struct usb_endpoint_descriptor *epd;
 250        int pipe;
 251
 252        epd = usb_get_epd(i2400mu->usb_iface,
 253                          i2400mu->endpoint_cfg.notification);
 254        pipe = usb_rcvintpipe(i2400mu->usb_dev, epd->bEndpointAddress);
 255        usb_fill_int_urb(urb, i2400mu->usb_dev, pipe,
 256                         i2400m->bm_ack_buf, I2400M_BM_ACK_BUF_SIZE,
 257                         __i2400mu_bm_notif_cb, completion,
 258                         epd->bInterval);
 259        return usb_submit_urb(urb, GFP_KERNEL);
 260}
 261
 262
 263/*
 264 * Read an ack from  the notification endpoint
 265 *
 266 * @i2400m:
 267 * @_ack: pointer to where to store the read data
 268 * @ack_size: how many bytes we should read
 269 *
 270 * Returns: < 0 errno code on error; otherwise, amount of received bytes.
 271 *
 272 * Submits a notification read, appends the read data to the given ack
 273 * buffer and then repeats (until @ack_size bytes have been
 274 * received).
 275 */
 276ssize_t i2400mu_bus_bm_wait_for_ack(struct i2400m *i2400m,
 277                                    struct i2400m_bootrom_header *_ack,
 278                                    size_t ack_size)
 279{
 280        ssize_t result = -ENOMEM;
 281        struct device *dev = i2400m_dev(i2400m);
 282        struct i2400mu *i2400mu = container_of(i2400m, struct i2400mu, i2400m);
 283        struct urb notif_urb;
 284        void *ack = _ack;
 285        size_t offset, len;
 286        long val;
 287        int do_autopm = 1;
 288        DECLARE_COMPLETION_ONSTACK(notif_completion);
 289
 290        d_fnstart(8, dev, "(i2400m %p ack %p size %zu)\n",
 291                  i2400m, ack, ack_size);
 292        BUG_ON(_ack == i2400m->bm_ack_buf);
 293        result = usb_autopm_get_interface(i2400mu->usb_iface);
 294        if (result < 0) {
 295                dev_err(dev, "BM-ACK: can't get autopm: %d\n", (int) result);
 296                do_autopm = 0;
 297        }
 298        usb_init_urb(&notif_urb);       /* ready notifications */
 299        usb_get_urb(&notif_urb);
 300        offset = 0;
 301        while (offset < ack_size) {
 302                init_completion(&notif_completion);
 303                result = i2400mu_notif_submit(i2400mu, &notif_urb,
 304                                              &notif_completion);
 305                if (result < 0)
 306                        goto error_notif_urb_submit;
 307                val = wait_for_completion_interruptible_timeout(
 308                        &notif_completion, HZ);
 309                if (val == 0) {
 310                        result = -ETIMEDOUT;
 311                        usb_kill_urb(&notif_urb);       /* Timedout */
 312                        goto error_notif_wait;
 313                }
 314                if (val == -ERESTARTSYS) {
 315                        result = -EINTR;                /* Interrupted */
 316                        usb_kill_urb(&notif_urb);
 317                        goto error_notif_wait;
 318                }
 319                result = notif_urb.status;              /* How was the ack? */
 320                switch (result) {
 321                case 0:
 322                        break;
 323                case -EINVAL:                   /* while removing driver */
 324                case -ENODEV:                   /* dev disconnect ... */
 325                case -ENOENT:                   /* just ignore it */
 326                case -ESHUTDOWN:                /* and exit */
 327                case -ECONNRESET:
 328                        result = -ESHUTDOWN;
 329                        goto error_dev_gone;
 330                default:                                /* any other? */
 331                        usb_kill_urb(&notif_urb);       /* Timedout */
 332                        if (edc_inc(&i2400mu->urb_edc,
 333                                    EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME))
 334                                goto error_exceeded;
 335                        dev_err(dev, "BM-ACK: URB error %d, "
 336                                "retrying\n", notif_urb.status);
 337                        continue;       /* retry */
 338                }
 339                if (notif_urb.actual_length == 0) {
 340                        d_printf(6, dev, "ZLP received, retrying\n");
 341                        continue;
 342                }
 343                /* Got data, append it to the buffer */
 344                len = min(ack_size - offset, (size_t) notif_urb.actual_length);
 345                memcpy(ack + offset, i2400m->bm_ack_buf, len);
 346                offset += len;
 347        }
 348        result = offset;
 349error_notif_urb_submit:
 350error_notif_wait:
 351error_dev_gone:
 352out:
 353        if (do_autopm)
 354                usb_autopm_put_interface(i2400mu->usb_iface);
 355        d_fnend(8, dev, "(i2400m %p ack %p size %zu) = %ld\n",
 356                i2400m, ack, ack_size, (long) result);
 357        return result;
 358
 359error_exceeded:
 360        dev_err(dev, "bm: maximum errors in notification URB exceeded; "
 361                "resetting device\n");
 362        usb_queue_reset_device(i2400mu->usb_iface);
 363        goto out;
 364}
 365