linux/drivers/net/wireless/broadcom/brcm80211/brcmfmac/commonring.c
<<
>>
Prefs
   1// SPDX-License-Identifier: ISC
   2/*
   3 * Copyright (c) 2014 Broadcom Corporation
   4 */
   5
   6#include <linux/types.h>
   7#include <linux/netdevice.h>
   8
   9#include <brcmu_utils.h>
  10#include <brcmu_wifi.h>
  11
  12#include "core.h"
  13#include "commonring.h"
  14
  15void brcmf_commonring_register_cb(struct brcmf_commonring *commonring,
  16                                  int (*cr_ring_bell)(void *ctx),
  17                                  int (*cr_update_rptr)(void *ctx),
  18                                  int (*cr_update_wptr)(void *ctx),
  19                                  int (*cr_write_rptr)(void *ctx),
  20                                  int (*cr_write_wptr)(void *ctx), void *ctx)
  21{
  22        commonring->cr_ring_bell = cr_ring_bell;
  23        commonring->cr_update_rptr = cr_update_rptr;
  24        commonring->cr_update_wptr = cr_update_wptr;
  25        commonring->cr_write_rptr = cr_write_rptr;
  26        commonring->cr_write_wptr = cr_write_wptr;
  27        commonring->cr_ctx = ctx;
  28}
  29
  30
  31void brcmf_commonring_config(struct brcmf_commonring *commonring, u16 depth,
  32                             u16 item_len, void *buf_addr)
  33{
  34        commonring->depth = depth;
  35        commonring->item_len = item_len;
  36        commonring->buf_addr = buf_addr;
  37        if (!commonring->inited) {
  38                spin_lock_init(&commonring->lock);
  39                commonring->inited = true;
  40        }
  41        commonring->r_ptr = 0;
  42        if (commonring->cr_write_rptr)
  43                commonring->cr_write_rptr(commonring->cr_ctx);
  44        commonring->w_ptr = 0;
  45        if (commonring->cr_write_wptr)
  46                commonring->cr_write_wptr(commonring->cr_ctx);
  47        commonring->f_ptr = 0;
  48}
  49
  50
  51void brcmf_commonring_lock(struct brcmf_commonring *commonring)
  52                __acquires(&commonring->lock)
  53{
  54        unsigned long flags;
  55
  56        spin_lock_irqsave(&commonring->lock, flags);
  57        commonring->flags = flags;
  58}
  59
  60
  61void brcmf_commonring_unlock(struct brcmf_commonring *commonring)
  62                __releases(&commonring->lock)
  63{
  64        spin_unlock_irqrestore(&commonring->lock, commonring->flags);
  65}
  66
  67
  68bool brcmf_commonring_write_available(struct brcmf_commonring *commonring)
  69{
  70        u16 available;
  71        bool retry = true;
  72
  73again:
  74        if (commonring->r_ptr <= commonring->w_ptr)
  75                available = commonring->depth - commonring->w_ptr +
  76                            commonring->r_ptr;
  77        else
  78                available = commonring->r_ptr - commonring->w_ptr;
  79
  80        if (available > 1) {
  81                if (!commonring->was_full)
  82                        return true;
  83                if (available > commonring->depth / 8) {
  84                        commonring->was_full = false;
  85                        return true;
  86                }
  87                if (retry) {
  88                        if (commonring->cr_update_rptr)
  89                                commonring->cr_update_rptr(commonring->cr_ctx);
  90                        retry = false;
  91                        goto again;
  92                }
  93                return false;
  94        }
  95
  96        if (retry) {
  97                if (commonring->cr_update_rptr)
  98                        commonring->cr_update_rptr(commonring->cr_ctx);
  99                retry = false;
 100                goto again;
 101        }
 102
 103        commonring->was_full = true;
 104        return false;
 105}
 106
 107
 108void *brcmf_commonring_reserve_for_write(struct brcmf_commonring *commonring)
 109{
 110        void *ret_ptr;
 111        u16 available;
 112        bool retry = true;
 113
 114again:
 115        if (commonring->r_ptr <= commonring->w_ptr)
 116                available = commonring->depth - commonring->w_ptr +
 117                            commonring->r_ptr;
 118        else
 119                available = commonring->r_ptr - commonring->w_ptr;
 120
 121        if (available > 1) {
 122                ret_ptr = commonring->buf_addr +
 123                          (commonring->w_ptr * commonring->item_len);
 124                commonring->w_ptr++;
 125                if (commonring->w_ptr == commonring->depth)
 126                        commonring->w_ptr = 0;
 127                return ret_ptr;
 128        }
 129
 130        if (retry) {
 131                if (commonring->cr_update_rptr)
 132                        commonring->cr_update_rptr(commonring->cr_ctx);
 133                retry = false;
 134                goto again;
 135        }
 136
 137        commonring->was_full = true;
 138        return NULL;
 139}
 140
 141
 142void *
 143brcmf_commonring_reserve_for_write_multiple(struct brcmf_commonring *commonring,
 144                                            u16 n_items, u16 *alloced)
 145{
 146        void *ret_ptr;
 147        u16 available;
 148        bool retry = true;
 149
 150again:
 151        if (commonring->r_ptr <= commonring->w_ptr)
 152                available = commonring->depth - commonring->w_ptr +
 153                            commonring->r_ptr;
 154        else
 155                available = commonring->r_ptr - commonring->w_ptr;
 156
 157        if (available > 1) {
 158                ret_ptr = commonring->buf_addr +
 159                          (commonring->w_ptr * commonring->item_len);
 160                *alloced = min_t(u16, n_items, available - 1);
 161                if (*alloced + commonring->w_ptr > commonring->depth)
 162                        *alloced = commonring->depth - commonring->w_ptr;
 163                commonring->w_ptr += *alloced;
 164                if (commonring->w_ptr == commonring->depth)
 165                        commonring->w_ptr = 0;
 166                return ret_ptr;
 167        }
 168
 169        if (retry) {
 170                if (commonring->cr_update_rptr)
 171                        commonring->cr_update_rptr(commonring->cr_ctx);
 172                retry = false;
 173                goto again;
 174        }
 175
 176        commonring->was_full = true;
 177        return NULL;
 178}
 179
 180
 181int brcmf_commonring_write_complete(struct brcmf_commonring *commonring)
 182{
 183        void *address;
 184
 185        address = commonring->buf_addr;
 186        address += (commonring->f_ptr * commonring->item_len);
 187        if (commonring->f_ptr > commonring->w_ptr) {
 188                address = commonring->buf_addr;
 189                commonring->f_ptr = 0;
 190        }
 191
 192        commonring->f_ptr = commonring->w_ptr;
 193
 194        if (commonring->cr_write_wptr)
 195                commonring->cr_write_wptr(commonring->cr_ctx);
 196        if (commonring->cr_ring_bell)
 197                return commonring->cr_ring_bell(commonring->cr_ctx);
 198
 199        return -EIO;
 200}
 201
 202
 203void brcmf_commonring_write_cancel(struct brcmf_commonring *commonring,
 204                                   u16 n_items)
 205{
 206        if (commonring->w_ptr == 0)
 207                commonring->w_ptr = commonring->depth - n_items;
 208        else
 209                commonring->w_ptr -= n_items;
 210}
 211
 212
 213void *brcmf_commonring_get_read_ptr(struct brcmf_commonring *commonring,
 214                                    u16 *n_items)
 215{
 216        if (commonring->cr_update_wptr)
 217                commonring->cr_update_wptr(commonring->cr_ctx);
 218
 219        *n_items = (commonring->w_ptr >= commonring->r_ptr) ?
 220                                (commonring->w_ptr - commonring->r_ptr) :
 221                                (commonring->depth - commonring->r_ptr);
 222
 223        if (*n_items == 0)
 224                return NULL;
 225
 226        return commonring->buf_addr +
 227               (commonring->r_ptr * commonring->item_len);
 228}
 229
 230
 231int brcmf_commonring_read_complete(struct brcmf_commonring *commonring,
 232                                   u16 n_items)
 233{
 234        commonring->r_ptr += n_items;
 235        if (commonring->r_ptr == commonring->depth)
 236                commonring->r_ptr = 0;
 237
 238        if (commonring->cr_write_rptr)
 239                return commonring->cr_write_rptr(commonring->cr_ctx);
 240
 241        return -EIO;
 242}
 243