summaryrefslogtreecommitdiff
path: root/drivers/net/wireless/bcmdhd/circularbuf.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/bcmdhd/circularbuf.c')
-rw-r--r--drivers/net/wireless/bcmdhd/circularbuf.c326
1 files changed, 326 insertions, 0 deletions
diff --git a/drivers/net/wireless/bcmdhd/circularbuf.c b/drivers/net/wireless/bcmdhd/circularbuf.c
new file mode 100644
index 000000000000..30c60db3bf6a
--- /dev/null
+++ b/drivers/net/wireless/bcmdhd/circularbuf.c
@@ -0,0 +1,326 @@
+/*
+ * Initialization and support routines for self-booting compressed image.
+ *
+ * Copyright (C) 1999-2016, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: circularbuf.c 452261 2014-01-29 19:30:23Z $
+ */
+
+#include <circularbuf.h>
+#include <bcmmsgbuf.h>
+#include <osl.h>
+
+#define CIRCULARBUF_READ_SPACE_AT_END(x) \
+ ((x->w_ptr >= x->rp_ptr) ? (x->w_ptr - x->rp_ptr) : (x->e_ptr - x->rp_ptr))
+
+#define CIRCULARBUF_READ_SPACE_AVAIL(x) \
+ (((CIRCULARBUF_READ_SPACE_AT_END(x) == 0) && (x->w_ptr < x->rp_ptr)) ? \
+ x->w_ptr : CIRCULARBUF_READ_SPACE_AT_END(x))
+
+int cbuf_msg_level = CBUF_ERROR_VAL | CBUF_TRACE_VAL | CBUF_INFORM_VAL;
+
+/* #define CBUF_DEBUG */
+#ifdef CBUF_DEBUG
+#define CBUF_DEBUG_CHECK(x) x
+#else
+#define CBUF_DEBUG_CHECK(x)
+#endif /* CBUF_DEBUG */
+
+/*
+ * -----------------------------------------------------------------------------
+ * Function : circularbuf_init
+ * Description:
+ *
+ *
+ * Input Args :
+ *
+ *
+ * Return Values :
+ *
+ * -----------------------------------------------------------------------------
+ */
+void
+circularbuf_init(circularbuf_t *handle, void *buf_base_addr, uint16 total_buf_len)
+{
+ handle->buf_addr = buf_base_addr;
+
+ handle->depth = handle->e_ptr = HTOL32(total_buf_len);
+
+ /* Initialize Read and Write pointers */
+ handle->w_ptr = handle->r_ptr = handle->wp_ptr = handle->rp_ptr = HTOL32(0);
+ handle->mb_ring_bell = NULL;
+ handle->mb_ctx = NULL;
+
+ return;
+}
+
+void
+circularbuf_register_cb(circularbuf_t *handle, mb_ring_t mb_ring_func, void *ctx)
+{
+ handle->mb_ring_bell = mb_ring_func;
+ handle->mb_ctx = ctx;
+}
+
+#ifdef CBUF_DEBUG
+static void
+circularbuf_check_sanity(circularbuf_t *handle)
+{
+ if ((handle->e_ptr > handle->depth) ||
+ (handle->r_ptr > handle->e_ptr) ||
+ (handle->rp_ptr > handle->e_ptr) ||
+ (handle->w_ptr > handle->e_ptr))
+ {
+ printf("%s:%d: Pointers are corrupted.\n", __FUNCTION__, __LINE__);
+ circularbuf_debug_print(handle);
+ ASSERT(0);
+ }
+ return;
+}
+#endif /* CBUF_DEBUG */
+
+/*
+ * -----------------------------------------------------------------------------
+ * Function : circularbuf_reserve_for_write
+ *
+ * Description:
+ * This function reserves N bytes for write in the circular buffer. The circularbuf
+ * implementation will only reserve space in the ciruclar buffer and return
+ * the pointer to the address where the new data can be written.
+ * The actual write implementation (bcopy/dma) is outside the scope of
+ * circularbuf implementation.
+ *
+ * Input Args :
+ * size - No. of bytes to reserve for write
+ *
+ * Return Values :
+ * void * : Pointer to the reserved location. This is the address
+ * that will be used for write (dma/bcopy)
+ *
+ * -----------------------------------------------------------------------------
+ */
+void * BCMFASTPATH
+circularbuf_reserve_for_write(circularbuf_t *handle, uint16 size)
+{
+ int16 avail_space;
+ void *ret_ptr = NULL;
+
+ CBUF_DEBUG_CHECK(circularbuf_check_sanity(handle));
+ ASSERT(size < handle->depth);
+
+ if (handle->wp_ptr >= handle->r_ptr)
+ avail_space = handle->depth - handle->wp_ptr;
+ else
+ avail_space = handle->r_ptr - handle->wp_ptr;
+
+ ASSERT(avail_space <= handle->depth);
+ if (avail_space > size)
+ {
+ /* Great. We have enough space. */
+ ret_ptr = CIRCULARBUF_START(handle) + handle->wp_ptr;
+
+ /*
+ * We need to update the wp_ptr for the next guy to write.
+ *
+ * Please Note : We are not updating the write pointer here. This can be
+ * done only after write is complete (In case of DMA, we can only schedule
+ * the DMA. Actual completion will be known only on DMA complete interrupt).
+ */
+ handle->wp_ptr += size;
+ return ret_ptr;
+ }
+
+ /*
+ * If there is no available space, we should check if there is some space left
+ * in the beginning of the circular buffer. Wrap-around case, where there is
+ * not enough space in the end of the circular buffer. But, there might be
+ * room in the beginning of the buffer.
+ */
+ if (handle->wp_ptr >= handle->r_ptr)
+ {
+ avail_space = handle->r_ptr;
+ if (avail_space > size)
+ {
+ /* OK. There is room in the beginning. Let's go ahead and use that.
+ * But, before that, we have left a hole at the end of the circular
+ * buffer as that was not sufficient to accomodate the requested
+ * size. Let's make sure this is updated in the circularbuf structure
+ * so that consumer does not use the hole.
+ */
+ handle->e_ptr = handle->wp_ptr;
+ handle->wp_ptr = size;
+
+ return CIRCULARBUF_START(handle);
+ }
+ }
+
+ /* We have tried enough to accomodate the new packet. There is no room for now. */
+ return NULL;
+}
+
+/*
+ * -----------------------------------------------------------------------------
+ * Function : circularbuf_write_complete
+ *
+ * Description:
+ * This function has to be called by the producer end of circularbuf to indicate to
+ * the circularbuf layer that data has been written and the write pointer can be
+ * updated. In the process, if there was a doorbell callback registered, that
+ * function would also be invoked.
+ *
+ * Input Args :
+ * dest_addr : Address where the data was written. This would be the
+ * same address that was reserved earlier.
+ * bytes_written : Length of data written
+ *
+ * -----------------------------------------------------------------------------
+ */
+void BCMFASTPATH
+circularbuf_write_complete(circularbuf_t *handle, uint16 bytes_written)
+{
+ CBUF_DEBUG_CHECK(circularbuf_check_sanity(handle));
+
+ /* Update the write pointer */
+ if ((handle->w_ptr + bytes_written) >= handle->depth) {
+ OSL_CACHE_FLUSH((void *) CIRCULARBUF_START(handle), bytes_written);
+ handle->w_ptr = bytes_written;
+ } else {
+ OSL_CACHE_FLUSH((void *) (CIRCULARBUF_START(handle) + handle->w_ptr),
+ bytes_written);
+ handle->w_ptr += bytes_written;
+ }
+
+ /* And ring the door bell (mail box interrupt) to indicate to the peer that
+ * message is available for consumption.
+ */
+ if (handle->mb_ring_bell)
+ handle->mb_ring_bell(handle->mb_ctx);
+}
+
+/*
+ * -----------------------------------------------------------------------------
+ * Function : circularbuf_get_read_ptr
+ *
+ * Description:
+ * This function will be called by the consumer of circularbuf for reading data from
+ * the circular buffer. This will typically be invoked when the consumer gets a
+ * doorbell interrupt.
+ * Please note that the function only returns the pointer (and length) from
+ * where the data can be read. Actual read implementation is upto the
+ * consumer. It could be a bcopy or dma.
+ *
+ * Input Args :
+ * void * : Address from where the data can be read.
+ * available_len : Length of data available for read.
+ *
+ * -----------------------------------------------------------------------------
+ */
+void * BCMFASTPATH
+circularbuf_get_read_ptr(circularbuf_t *handle, uint16 *available_len)
+{
+ uint8 *ret_addr;
+
+ CBUF_DEBUG_CHECK(circularbuf_check_sanity(handle));
+
+ /* First check if there is any data available in the circular buffer */
+ *available_len = CIRCULARBUF_READ_SPACE_AVAIL(handle);
+ if (*available_len == 0)
+ return NULL;
+
+ /*
+ * Although there might be data in the circular buffer for read, in
+ * cases of write wrap-around and read still in the end of the circular
+ * buffer, we might have to wrap around the read pending pointer also.
+ */
+ if (CIRCULARBUF_READ_SPACE_AT_END(handle) == 0)
+ handle->rp_ptr = 0;
+
+ ret_addr = CIRCULARBUF_START(handle) + handle->rp_ptr;
+
+ /*
+ * Please note that we do not update the read pointer here. Only
+ * read pending pointer is updated, so that next reader knows where
+ * to read data from.
+ * read pointer can only be updated when the read is complete.
+ */
+ handle->rp_ptr = (uint16)(ret_addr - CIRCULARBUF_START(handle) + *available_len);
+
+ ASSERT(*available_len <= handle->depth);
+
+ OSL_CACHE_INV((void *) ret_addr, *available_len);
+
+ return ret_addr;
+}
+
+/*
+ * -----------------------------------------------------------------------------
+ * Function : circularbuf_read_complete
+ * Description:
+ * This function has to be called by the consumer end of circularbuf to indicate
+ * that data has been consumed and the read pointer can be updated.
+ *
+ * Input Args :
+ * bytes_read : No. of bytes consumed by the consumer. This has to match
+ * the length returned by circularbuf_get_read_ptr
+ *
+ * Return Values :
+ * CIRCULARBUF_SUCCESS : Otherwise
+ *
+ * -----------------------------------------------------------------------------
+ */
+circularbuf_ret_t BCMFASTPATH
+circularbuf_read_complete(circularbuf_t *handle, uint16 bytes_read)
+{
+ CBUF_DEBUG_CHECK(circularbuf_check_sanity(handle));
+ ASSERT(bytes_read < handle->depth);
+
+ /* Update the read pointer */
+ if ((handle->r_ptr + bytes_read) >= handle->depth)
+ handle->r_ptr = bytes_read;
+ else
+ handle->r_ptr += bytes_read;
+
+ return CIRCULARBUF_SUCCESS;
+}
+/*
+ * -----------------------------------------------------------------------------
+ * Function : circularbuf_revert_rp_ptr
+ *
+ * Description:
+ * The rp_ptr update during circularbuf_get_read_ptr() is done to reflect the amount of data
+ * that is sent out to be read by the consumer. But the consumer may not always read the
+ * entire data. In such a case, the rp_ptr needs to be reverted back by 'left' bytes, where
+ * 'left' is the no. of bytes left unread.
+ *
+ * Input args:
+ * bytes : The no. of bytes left unread by the consumer
+ *
+ * -----------------------------------------------------------------------------
+ */
+circularbuf_ret_t
+circularbuf_revert_rp_ptr(circularbuf_t *handle, uint16 bytes)
+{
+ CBUF_DEBUG_CHECK(circularbuf_check_sanity(handle));
+ ASSERT(bytes < handle->depth);
+
+ handle->rp_ptr -= bytes;
+
+ return CIRCULARBUF_SUCCESS;
+}