linux/drivers/net/wimax/i2400m/usb-notif.c
<<
>>
Prefs
   1/*
   2 * Intel Wireless WiMAX Connection 2400m over USB
   3 * Notification 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 * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
  38 *  - Initial implementation
  39 *
  40 *
  41 * The notification endpoint is active when the device is not in boot
  42 * mode; in here we just read and get notifications; based on those,
  43 * we act to either reinitialize the device after a reboot or to
  44 * submit a RX request.
  45 *
  46 * ROADMAP
  47 *
  48 * i2400mu_usb_notification_setup()
  49 *
  50 * i2400mu_usb_notification_release()
  51 *
  52 * i2400mu_usb_notification_cb()        Called when a URB is ready
  53 *   i2400mu_notif_grok()
  54 *     i2400m_dev_reset_handle()
  55 *     i2400mu_rx_kick()
  56 */
  57#include <linux/usb.h>
  58#include "i2400m-usb.h"
  59
  60
  61#define D_SUBMODULE notif
  62#include "usb-debug-levels.h"
  63
  64
  65static const
  66__le32 i2400m_ZERO_BARKER[4] = { 0, 0, 0, 0 };
  67
  68
  69/*
  70 * Process a received notification
  71 *
  72 * In normal operation mode, we can only receive two types of payloads
  73 * on the notification endpoint:
  74 *
  75 *   - a reboot barker, we do a bootstrap (the device has reseted).
  76 *
  77 *   - a block of zeroes: there is pending data in the IN endpoint
  78 */
  79static
  80int i2400mu_notification_grok(struct i2400mu *i2400mu, const void *buf,
  81                                 size_t buf_len)
  82{
  83        int ret;
  84        struct device *dev = &i2400mu->usb_iface->dev;
  85        struct i2400m *i2400m = &i2400mu->i2400m;
  86
  87        d_fnstart(4, dev, "(i2400m %p buf %p buf_len %zu)\n",
  88                  i2400mu, buf, buf_len);
  89        ret = -EIO;
  90        if (buf_len < sizeof(i2400m_NBOOT_BARKER))
  91                /* Not a bug, just ignore */
  92                goto error_bad_size;
  93        if (!memcmp(i2400m_NBOOT_BARKER, buf, sizeof(i2400m_NBOOT_BARKER))
  94            || !memcmp(i2400m_SBOOT_BARKER, buf, sizeof(i2400m_SBOOT_BARKER)))
  95                ret = i2400m_dev_reset_handle(i2400m);
  96        else if (!memcmp(i2400m_ZERO_BARKER, buf, sizeof(i2400m_ZERO_BARKER))) {
  97                i2400mu_rx_kick(i2400mu);
  98                ret = 0;
  99        } else {        /* Unknown or unexpected data in the notif message */
 100                char prefix[64];
 101                ret = -EIO;
 102                dev_err(dev, "HW BUG? Unknown/unexpected data in notification "
 103                        "message (%zu bytes)\n", buf_len);
 104                snprintf(prefix, sizeof(prefix), "%s %s: ",
 105                         dev_driver_string(dev), dev_name(dev));
 106                if (buf_len > 64) {
 107                        print_hex_dump(KERN_ERR, prefix, DUMP_PREFIX_OFFSET,
 108                                       8, 4, buf, 64, 0);
 109                        printk(KERN_ERR "%s... (only first 64 bytes "
 110                               "dumped)\n", prefix);
 111                } else
 112                        print_hex_dump(KERN_ERR, prefix, DUMP_PREFIX_OFFSET,
 113                                       8, 4, buf, buf_len, 0);
 114        }
 115error_bad_size:
 116        d_fnend(4, dev, "(i2400m %p buf %p buf_len %zu) = %d\n",
 117                i2400mu, buf, buf_len, ret);
 118        return ret;
 119}
 120
 121
 122/*
 123 * URB callback for the notification endpoint
 124 *
 125 * @urb: the urb received from the notification endpoint
 126 *
 127 * This function will just process the USB side of the transaction,
 128 * checking everything is fine, pass the processing to
 129 * i2400m_notification_grok() and resubmit the URB.
 130 */
 131static
 132void i2400mu_notification_cb(struct urb *urb)
 133{
 134        int ret;
 135        struct i2400mu *i2400mu = urb->context;
 136        struct device *dev = &i2400mu->usb_iface->dev;
 137
 138        d_fnstart(4, dev, "(urb %p status %d actual_length %d)\n",
 139                  urb, urb->status, urb->actual_length);
 140        ret = urb->status;
 141        switch (ret) {
 142        case 0:
 143                ret = i2400mu_notification_grok(i2400mu, urb->transfer_buffer,
 144                                                urb->actual_length);
 145                if (ret == -EIO && edc_inc(&i2400mu->urb_edc, EDC_MAX_ERRORS,
 146                                           EDC_ERROR_TIMEFRAME))
 147                        goto error_exceeded;
 148                if (ret == -ENOMEM)     /* uff...power cycle? shutdown? */
 149                        goto error_exceeded;
 150                break;
 151        case -EINVAL:                   /* while removing driver */
 152        case -ENODEV:                   /* dev disconnect ... */
 153        case -ENOENT:                   /* ditto */
 154        case -ESHUTDOWN:                /* URB killed */
 155        case -ECONNRESET:               /* disconnection */
 156                goto out;               /* Notify around */
 157        default:                        /* Some error? */
 158                if (edc_inc(&i2400mu->urb_edc,
 159                            EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME))
 160                        goto error_exceeded;
 161                dev_err(dev, "notification: URB error %d, retrying\n",
 162                        urb->status);
 163        }
 164        usb_mark_last_busy(i2400mu->usb_dev);
 165        ret = usb_submit_urb(i2400mu->notif_urb, GFP_ATOMIC);
 166        switch (ret) {
 167        case 0:
 168        case -EINVAL:                   /* while removing driver */
 169        case -ENODEV:                   /* dev disconnect ... */
 170        case -ENOENT:                   /* ditto */
 171        case -ESHUTDOWN:                /* URB killed */
 172        case -ECONNRESET:               /* disconnection */
 173                break;                  /* just ignore */
 174        default:                        /* Some error? */
 175                dev_err(dev, "notification: cannot submit URB: %d\n", ret);
 176                goto error_submit;
 177        }
 178        d_fnend(4, dev, "(urb %p status %d actual_length %d) = void\n",
 179                urb, urb->status, urb->actual_length);
 180        return;
 181
 182error_exceeded:
 183        dev_err(dev, "maximum errors in notification URB exceeded; "
 184                "resetting device\n");
 185error_submit:
 186        usb_queue_reset_device(i2400mu->usb_iface);
 187out:
 188        d_fnend(4, dev, "(urb %p status %d actual_length %d) = void\n",
 189                urb, urb->status, urb->actual_length);
 190        return;
 191}
 192
 193
 194/*
 195 * setup the notification endpoint
 196 *
 197 * @i2400m: device descriptor
 198 *
 199 * This procedure prepares the notification urb and handler for receiving
 200 * unsolicited barkers from the device.
 201 */
 202int i2400mu_notification_setup(struct i2400mu *i2400mu)
 203{
 204        struct device *dev = &i2400mu->usb_iface->dev;
 205        int usb_pipe, ret = 0;
 206        struct usb_endpoint_descriptor *epd;
 207        char *buf;
 208
 209        d_fnstart(4, dev, "(i2400m %p)\n", i2400mu);
 210        buf = kmalloc(I2400MU_MAX_NOTIFICATION_LEN, GFP_KERNEL | GFP_DMA);
 211        if (buf == NULL) {
 212                dev_err(dev, "notification: buffer allocation failed\n");
 213                ret = -ENOMEM;
 214                goto error_buf_alloc;
 215        }
 216
 217        i2400mu->notif_urb = usb_alloc_urb(0, GFP_KERNEL);
 218        if (!i2400mu->notif_urb) {
 219                ret = -ENOMEM;
 220                dev_err(dev, "notification: cannot allocate URB\n");
 221                goto error_alloc_urb;
 222        }
 223        epd = usb_get_epd(i2400mu->usb_iface, I2400MU_EP_NOTIFICATION);
 224        usb_pipe = usb_rcvintpipe(i2400mu->usb_dev, epd->bEndpointAddress);
 225        usb_fill_int_urb(i2400mu->notif_urb, i2400mu->usb_dev, usb_pipe,
 226                         buf, I2400MU_MAX_NOTIFICATION_LEN,
 227                         i2400mu_notification_cb, i2400mu, epd->bInterval);
 228        ret = usb_submit_urb(i2400mu->notif_urb, GFP_KERNEL);
 229        if (ret != 0) {
 230                dev_err(dev, "notification: cannot submit URB: %d\n", ret);
 231                goto error_submit;
 232        }
 233        d_fnend(4, dev, "(i2400m %p) = %d\n", i2400mu, ret);
 234        return ret;
 235
 236error_submit:
 237        usb_free_urb(i2400mu->notif_urb);
 238error_alloc_urb:
 239        kfree(buf);
 240error_buf_alloc:
 241        d_fnend(4, dev, "(i2400m %p) = %d\n", i2400mu, ret);
 242        return ret;
 243}
 244
 245
 246/*
 247 * Tear down of the notification mechanism
 248 *
 249 * @i2400m: device descriptor
 250 *
 251 * Kill the interrupt endpoint urb, free any allocated resources.
 252 *
 253 * We need to check if we have done it before as for example,
 254 * _suspend() call this; if after a suspend() we get a _disconnect()
 255 * (as the case is when hibernating), nothing bad happens.
 256 */
 257void i2400mu_notification_release(struct i2400mu *i2400mu)
 258{
 259        struct device *dev = &i2400mu->usb_iface->dev;
 260
 261        d_fnstart(4, dev, "(i2400mu %p)\n", i2400mu);
 262        if (i2400mu->notif_urb != NULL) {
 263                usb_kill_urb(i2400mu->notif_urb);
 264                kfree(i2400mu->notif_urb->transfer_buffer);
 265                usb_free_urb(i2400mu->notif_urb);
 266                i2400mu->notif_urb = NULL;
 267        }
 268        d_fnend(4, dev, "(i2400mu %p)\n", i2400mu);
 269}
 270