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