linux/drivers/net/wireless/broadcom/brcm80211/brcmfmac/commonring.c
<<
>>
Prefs
   1/* Copyright (c) 2014 Broadcom Corporation
   2 *
   3 * Permission to use, copy, modify, and/or distribute this software for any
   4 * purpose with or without fee is hereby granted, provided that the above
   5 * copyright notice and this permission notice appear in all copies.
   6 *
   7 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
   8 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
   9 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
  10 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  11 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
  12 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
  13 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  14 */
  15
  16#include <linux/types.h>
  17#include <linux/netdevice.h>
  18
  19#include <brcmu_utils.h>
  20#include <brcmu_wifi.h>
  21
  22#include "core.h"
  23#include "commonring.h"
  24
  25void brcmf_commonring_register_cb(struct brcmf_commonring *commonring,
  26                                  int (*cr_ring_bell)(void *ctx),
  27                                  int (*cr_update_rptr)(void *ctx),
  28                                  int (*cr_update_wptr)(void *ctx),
  29                                  int (*cr_write_rptr)(void *ctx),
  30                                  int (*cr_write_wptr)(void *ctx), void *ctx)
  31{
  32        commonring->cr_ring_bell = cr_ring_bell;
  33        commonring->cr_update_rptr = cr_update_rptr;
  34        commonring->cr_update_wptr = cr_update_wptr;
  35        commonring->cr_write_rptr = cr_write_rptr;
  36        commonring->cr_write_wptr = cr_write_wptr;
  37        commonring->cr_ctx = ctx;
  38}
  39
  40
  41void brcmf_commonring_config(struct brcmf_commonring *commonring, u16 depth,
  42                             u16 item_len, void *buf_addr)
  43{
  44        commonring->depth = depth;
  45        commonring->item_len = item_len;
  46        commonring->buf_addr = buf_addr;
  47        if (!commonring->inited) {
  48                spin_lock_init(&commonring->lock);
  49                commonring->inited = true;
  50        }
  51        commonring->r_ptr = 0;
  52        if (commonring->cr_write_rptr)
  53                commonring->cr_write_rptr(commonring->cr_ctx);
  54        commonring->w_ptr = 0;
  55        if (commonring->cr_write_wptr)
  56                commonring->cr_write_wptr(commonring->cr_ctx);
  57        commonring->f_ptr = 0;
  58}
  59
  60
  61void brcmf_commonring_lock(struct brcmf_commonring *commonring)
  62                __acquires(&commonring->lock)
  63{
  64        unsigned long flags;
  65
  66        spin_lock_irqsave(&commonring->lock, flags);
  67        commonring->flags = flags;
  68}
  69
  70
  71void brcmf_commonring_unlock(struct brcmf_commonring *commonring)
  72                __releases(&commonring->lock)
  73{
  74        spin_unlock_irqrestore(&commonring->lock, commonring->flags);
  75}
  76
  77
  78bool brcmf_commonring_write_available(struct brcmf_commonring *commonring)
  79{
  80        u16 available;
  81        bool retry = true;
  82
  83again:
  84        if (commonring->r_ptr <= commonring->w_ptr)
  85                available = commonring->depth - commonring->w_ptr +
  86                            commonring->r_ptr;
  87        else
  88                available = commonring->r_ptr - commonring->w_ptr;
  89
  90        if (available > 1) {
  91                if (!commonring->was_full)
  92                        return true;
  93                if (available > commonring->depth / 8) {
  94                        commonring->was_full = false;
  95                        return true;
  96                }
  97                if (retry) {
  98                        if (commonring->cr_update_rptr)
  99                                commonring->cr_update_rptr(commonring->cr_ctx);
 100                        retry = false;
 101                        goto again;
 102                }
 103                return false;
 104        }
 105
 106        if (retry) {
 107                if (commonring->cr_update_rptr)
 108                        commonring->cr_update_rptr(commonring->cr_ctx);
 109                retry = false;
 110                goto again;
 111        }
 112
 113        commonring->was_full = true;
 114        return false;
 115}
 116
 117
 118void *brcmf_commonring_reserve_for_write(struct brcmf_commonring *commonring)
 119{
 120        void *ret_ptr;
 121        u16 available;
 122        bool retry = true;
 123
 124again:
 125        if (commonring->r_ptr <= commonring->w_ptr)
 126                available = commonring->depth - commonring->w_ptr +
 127                            commonring->r_ptr;
 128        else
 129                available = commonring->r_ptr - commonring->w_ptr;
 130
 131        if (available > 1) {
 132                ret_ptr = commonring->buf_addr +
 133                          (commonring->w_ptr * commonring->item_len);
 134                commonring->w_ptr++;
 135                if (commonring->w_ptr == commonring->depth)
 136                        commonring->w_ptr = 0;
 137                return ret_ptr;
 138        }
 139
 140        if (retry) {
 141                if (commonring->cr_update_rptr)
 142                        commonring->cr_update_rptr(commonring->cr_ctx);
 143                retry = false;
 144                goto again;
 145        }
 146
 147        commonring->was_full = true;
 148        return NULL;
 149}
 150
 151
 152void *
 153brcmf_commonring_reserve_for_write_multiple(struct brcmf_commonring *commonring,
 154                                            u16 n_items, u16 *alloced)
 155{
 156        void *ret_ptr;
 157        u16 available;
 158        bool retry = true;
 159
 160again:
 161        if (commonring->r_ptr <= commonring->w_ptr)
 162                available = commonring->depth - commonring->w_ptr +
 163                            commonring->r_ptr;
 164        else
 165                available = commonring->r_ptr - commonring->w_ptr;
 166
 167        if (available > 1) {
 168                ret_ptr = commonring->buf_addr +
 169                          (commonring->w_ptr * commonring->item_len);
 170                *alloced = min_t(u16, n_items, available - 1);
 171                if (*alloced + commonring->w_ptr > commonring->depth)
 172                        *alloced = commonring->depth - commonring->w_ptr;
 173                commonring->w_ptr += *alloced;
 174                if (commonring->w_ptr == commonring->depth)
 175                        commonring->w_ptr = 0;
 176                return ret_ptr;
 177        }
 178
 179        if (retry) {
 180                if (commonring->cr_update_rptr)
 181                        commonring->cr_update_rptr(commonring->cr_ctx);
 182                retry = false;
 183                goto again;
 184        }
 185
 186        commonring->was_full = true;
 187        return NULL;
 188}
 189
 190
 191int brcmf_commonring_write_complete(struct brcmf_commonring *commonring)
 192{
 193        void *address;
 194
 195        address = commonring->buf_addr;
 196        address += (commonring->f_ptr * commonring->item_len);
 197        if (commonring->f_ptr > commonring->w_ptr) {
 198                address = commonring->buf_addr;
 199                commonring->f_ptr = 0;
 200        }
 201
 202        commonring->f_ptr = commonring->w_ptr;
 203
 204        if (commonring->cr_write_wptr)
 205                commonring->cr_write_wptr(commonring->cr_ctx);
 206        if (commonring->cr_ring_bell)
 207                return commonring->cr_ring_bell(commonring->cr_ctx);
 208
 209        return -EIO;
 210}
 211
 212
 213void brcmf_commonring_write_cancel(struct brcmf_commonring *commonring,
 214                                   u16 n_items)
 215{
 216        if (commonring->w_ptr == 0)
 217                commonring->w_ptr = commonring->depth - n_items;
 218        else
 219                commonring->w_ptr -= n_items;
 220}
 221
 222
 223void *brcmf_commonring_get_read_ptr(struct brcmf_commonring *commonring,
 224                                    u16 *n_items)
 225{
 226        if (commonring->cr_update_wptr)
 227                commonring->cr_update_wptr(commonring->cr_ctx);
 228
 229        *n_items = (commonring->w_ptr >= commonring->r_ptr) ?
 230                                (commonring->w_ptr - commonring->r_ptr) :
 231                                (commonring->depth - commonring->r_ptr);
 232
 233        if (*n_items == 0)
 234                return NULL;
 235
 236        return commonring->buf_addr +
 237               (commonring->r_ptr * commonring->item_len);
 238}
 239
 240
 241int brcmf_commonring_read_complete(struct brcmf_commonring *commonring,
 242                                   u16 n_items)
 243{
 244        commonring->r_ptr += n_items;
 245        if (commonring->r_ptr == commonring->depth)
 246                commonring->r_ptr = 0;
 247
 248        if (commonring->cr_write_rptr)
 249                return commonring->cr_write_rptr(commonring->cr_ctx);
 250
 251        return -EIO;
 252}
 253