net: rkwifi: fix using wext protocol anomaly
[firefly-linux-kernel-4.4.55.git] / drivers / net / wireless / rockchip_wlan / rkwifi / bcmdhd / circularbuf.c
1 /** @file circularbuf.c
2  *
3  * PCIe host driver and dongle firmware need to communicate with each other. The mechanism consists
4  * of multiple circular buffers located in (DMA'able) host memory. A circular buffer is either used
5  * for host -> dongle (h2d) or dongle -> host communication. Both host driver and firmware make use
6  * of this source file. This source file contains functions to manage such a set of circular
7  * buffers, but does not contain the code to read or write the data itself into the buffers. It
8  * leaves that up to the software layer that uses this file, which can be implemented either using
9  * pio or DMA transfers. It also leaves the format of the data that is written and read to a higher
10  * layer. Typically the data is in the form of so-called 'message buffers'.
11  *
12  * $Copyright Open Broadcom Corporation$
13  *
14  * $Id: circularbuf.c 467150 2014-04-02 17:30:43Z $
15  */
16
17 #include <circularbuf.h>
18 #include <bcmmsgbuf.h>
19 #include <osl.h>
20
21 #define CIRCULARBUF_READ_SPACE_AT_END(x)                \
22                         ((x->w_ptr >= x->rp_ptr) ? (x->w_ptr - x->rp_ptr) : (x->e_ptr - x->rp_ptr))
23
24 #define CIRCULARBUF_READ_SPACE_AVAIL(x)         \
25                         (((CIRCULARBUF_READ_SPACE_AT_END(x) == 0) && (x->w_ptr < x->rp_ptr)) ? \
26                                 x->w_ptr : CIRCULARBUF_READ_SPACE_AT_END(x))
27
28 int cbuf_msg_level = CBUF_ERROR_VAL | CBUF_TRACE_VAL | CBUF_INFORM_VAL;
29
30 /* #define CBUF_DEBUG */
31 #ifdef CBUF_DEBUG
32 #define CBUF_DEBUG_CHECK(x)     x
33 #else
34 #define CBUF_DEBUG_CHECK(x)
35 #endif  /* CBUF_DEBUG */
36
37 /**
38  * -----------------------------------------------------------------------------
39  * Function   : circularbuf_init
40  * Description:
41  *
42  *
43  * Input Args : buf_base_addr: address of DMA'able host memory provided by caller
44  *
45  *
46  * Return Values :
47  *
48  * -----------------------------------------------------------------------------
49  */
50 void
51 circularbuf_init(circularbuf_t *handle, void *buf_base_addr, uint16 total_buf_len)
52 {
53         handle->buf_addr = buf_base_addr;
54
55         handle->depth = handle->e_ptr = HTOL32(total_buf_len);
56
57         /* Initialize Read and Write pointers */
58         handle->w_ptr = handle->r_ptr = handle->wp_ptr = handle->rp_ptr = HTOL32(0);
59         handle->mb_ring_bell = NULL;
60         handle->mb_ctx = NULL;
61
62         return;
63 }
64
65 /**
66  * When an item is added to the circular buffer by the producing party, the consuming party has to
67  * be notified by means of a 'door bell' or 'ring'. This function allows the caller to register a
68  * 'ring' function that will be called when a 'write complete' occurs.
69  */
70 void
71 circularbuf_register_cb(circularbuf_t *handle, mb_ring_t mb_ring_func, void *ctx)
72 {
73         handle->mb_ring_bell = mb_ring_func;
74         handle->mb_ctx = ctx;
75 }
76
77 #ifdef CBUF_DEBUG
78 static void
79 circularbuf_check_sanity(circularbuf_t *handle)
80 {
81         if ((handle->e_ptr > handle->depth) ||
82             (handle->r_ptr > handle->e_ptr) ||
83                 (handle->rp_ptr > handle->e_ptr) ||
84                 (handle->w_ptr > handle->e_ptr))
85         {
86                 printf("%s:%d: Pointers are corrupted.\n", __FUNCTION__, __LINE__);
87                 circularbuf_debug_print(handle);
88                 ASSERT(0);
89         }
90         return;
91 }
92 #endif /* CBUF_DEBUG */
93
94 /**
95  * -----------------------------------------------------------------------------
96  * Function   : circularbuf_reserve_for_write
97  *
98  * Description:
99  * This function reserves N bytes for write in the circular buffer. The circularbuf
100  * implementation will only reserve space in the circular buffer and return
101  * the pointer to the address where the new data can be written.
102  * The actual write implementation (bcopy/dma) is outside the scope of
103  * circularbuf implementation.
104  *
105  * Input Args :
106  *              size - No. of bytes to reserve for write
107  *
108  * Return Values :
109  *              void * : Pointer to the reserved location. This is the address
110  *                        that will be used for write (dma/bcopy)
111  *
112  * -----------------------------------------------------------------------------
113  */
114 void * BCMFASTPATH
115 circularbuf_reserve_for_write(circularbuf_t *handle, uint16 size)
116 {
117         int16 avail_space;
118         void *ret_ptr = NULL;
119
120         CBUF_DEBUG_CHECK(circularbuf_check_sanity(handle));
121         ASSERT(size < handle->depth);
122
123         if (handle->wp_ptr >= handle->r_ptr)
124                 avail_space = handle->depth - handle->wp_ptr;
125         else
126                 avail_space = handle->r_ptr - handle->wp_ptr;
127
128         ASSERT(avail_space <= handle->depth);
129         if (avail_space > size)
130         {
131                 /* Great. We have enough space. */
132                 ret_ptr = CIRCULARBUF_START(handle) + handle->wp_ptr;
133
134                 /*
135                  * We need to update the wp_ptr for the next guy to write.
136                  *
137                  * Please Note : We are not updating the write pointer here. This can be
138                  * done only after write is complete (In case of DMA, we can only schedule
139                  * the DMA. Actual completion will be known only on DMA complete interrupt).
140                  */
141                 handle->wp_ptr += size;
142                 return ret_ptr;
143         }
144
145         /*
146          * If there is no available space, we should check if there is some space left
147          * in the beginning of the circular buffer.  Wrap-around case, where there is
148          * not enough space in the end of the circular buffer. But, there might be
149          * room in the beginning of the buffer.
150          */
151         if (handle->wp_ptr >= handle->r_ptr)
152         {
153                 avail_space = handle->r_ptr;
154                 if (avail_space > size)
155                 {
156                         /* OK. There is room in the beginning. Let's go ahead and use that.
157                          * But, before that, we have left a hole at the end of the circular
158                          * buffer as that was not sufficient to accomodate the requested
159                          * size. Let's make sure this is updated in the circularbuf structure
160                          * so that consumer does not use the hole.
161                          */
162                         handle->e_ptr  = handle->wp_ptr;
163                         handle->wp_ptr = size;
164
165                         return CIRCULARBUF_START(handle);
166                 }
167         }
168
169         /* We have tried enough to accomodate the new packet. There is no room for now. */
170         return NULL;
171 }
172
173 /**
174  * -----------------------------------------------------------------------------
175  * Function   : circularbuf_write_complete
176  *
177  * Description:
178  * This function has to be called by the producer end of circularbuf to indicate to
179  * the circularbuf layer that data has been written and the write pointer can be
180  * updated. In the process, if there was a doorbell callback registered, that
181  * function would also be invoked as to notify the consuming party.
182  *
183  * Input Args :
184  *              dest_addr         : Address where the data was written. This would be the
185  *                                          same address that was reserved earlier.
186  *              bytes_written : Length of data written
187  *
188  * -----------------------------------------------------------------------------
189  */
190 void BCMFASTPATH
191 circularbuf_write_complete(circularbuf_t *handle, uint16 bytes_written)
192 {
193         CBUF_DEBUG_CHECK(circularbuf_check_sanity(handle));
194
195         /* Update the write pointer */
196         if ((handle->w_ptr + bytes_written) >= handle->depth) {
197                 OSL_CACHE_FLUSH((void *) CIRCULARBUF_START(handle), bytes_written);
198                 handle->w_ptr = bytes_written;
199         } else {
200                 OSL_CACHE_FLUSH((void *) (CIRCULARBUF_START(handle) + handle->w_ptr),
201                         bytes_written);
202                 handle->w_ptr += bytes_written;
203         }
204
205         /* And ring the door bell (mail box interrupt) to indicate to the peer that
206          * message is available for consumption.
207          */
208         if (handle->mb_ring_bell)
209                 handle->mb_ring_bell(handle->mb_ctx);
210 }
211
212 /**
213  * -----------------------------------------------------------------------------
214  * Function   : circularbuf_get_read_ptr
215  *
216  * Description:
217  * This function will be called by the consumer of circularbuf for reading data from
218  * the circular buffer. This will typically be invoked when the consumer gets a
219  * doorbell interrupt.
220  * Please note that the function only returns the pointer (and length) from
221  * where the data can be read. Actual read implementation is up to the
222  * consumer. It could be a bcopy or dma.
223  *
224  * Input Args :
225  *              void *                  : Address from where the data can be read.
226  *              available_len   : Length of data available for read.
227  *
228  * -----------------------------------------------------------------------------
229  */
230 void * BCMFASTPATH
231 circularbuf_get_read_ptr(circularbuf_t *handle, uint16 *available_len)
232 {
233         uint8 *ret_addr;
234
235         CBUF_DEBUG_CHECK(circularbuf_check_sanity(handle));
236
237         /* First check if there is any data available in the circular buffer */
238         *available_len = CIRCULARBUF_READ_SPACE_AVAIL(handle);
239         if (*available_len == 0)
240                 return NULL;
241
242         /*
243          * Although there might be data in the circular buffer for read, in
244          * cases of write wrap-around and read still in the end of the circular
245          * buffer, we might have to wrap around the read pending pointer also.
246          */
247         if (CIRCULARBUF_READ_SPACE_AT_END(handle) == 0)
248                 handle->rp_ptr = 0;
249
250         ret_addr = CIRCULARBUF_START(handle) + handle->rp_ptr;
251
252         /*
253          * Please note that we do not update the read pointer here. Only
254          * read pending pointer is updated, so that next reader knows where
255          * to read data from.
256          * read pointer can only be updated when the read is complete.
257          */
258         handle->rp_ptr = (uint16)(ret_addr - CIRCULARBUF_START(handle) + *available_len);
259
260         ASSERT(*available_len <= handle->depth);
261
262         OSL_CACHE_INV((void *) ret_addr, *available_len);
263
264         return ret_addr;
265 }
266
267 /**
268  * -----------------------------------------------------------------------------
269  * Function   : circularbuf_read_complete
270  * Description:
271  * This function has to be called by the consumer end of circularbuf to indicate
272  * that data has been consumed and the read pointer can be updated, so the producing side
273  * can can use the freed space for new entries.
274  *
275  *
276  * Input Args :
277  *              bytes_read : No. of bytes consumed by the consumer. This has to match
278  *                                       the length returned by circularbuf_get_read_ptr
279  *
280  * Return Values :
281  *              CIRCULARBUF_SUCCESS             : Otherwise
282  *
283  * -----------------------------------------------------------------------------
284  */
285 circularbuf_ret_t BCMFASTPATH
286 circularbuf_read_complete(circularbuf_t *handle, uint16 bytes_read)
287 {
288         CBUF_DEBUG_CHECK(circularbuf_check_sanity(handle));
289         ASSERT(bytes_read < handle->depth);
290
291         /* Update the read pointer */
292         if ((handle->w_ptr < handle->e_ptr) && (handle->r_ptr + bytes_read) > handle->e_ptr)
293                 handle->r_ptr = bytes_read;
294         else
295                 handle->r_ptr += bytes_read;
296
297         return CIRCULARBUF_SUCCESS;
298 }
299
300 /**
301  * -----------------------------------------------------------------------------
302  * Function     : circularbuf_revert_rp_ptr
303  *
304  * Description:
305  * The rp_ptr update during circularbuf_get_read_ptr() is done to reflect the amount of data
306  * that is sent out to be read by the consumer. But the consumer may not always read the
307  * entire data. In such a case, the rp_ptr needs to be reverted back by 'left' bytes, where
308  * 'left' is the no. of bytes left unread.
309  *
310  * Input args:
311  *      bytes : The no. of bytes left unread by the consumer
312  *
313  * -----------------------------------------------------------------------------
314  */
315 circularbuf_ret_t
316 circularbuf_revert_rp_ptr(circularbuf_t *handle, uint16 bytes)
317 {
318         CBUF_DEBUG_CHECK(circularbuf_check_sanity(handle));
319         ASSERT(bytes < handle->depth);
320
321         handle->rp_ptr -= bytes;
322
323         return CIRCULARBUF_SUCCESS;
324 }