summaryrefslogtreecommitdiff
path: root/drivers/net/wireless/eswin/ecrnx_utils.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/eswin/ecrnx_utils.c')
-rw-r--r--drivers/net/wireless/eswin/ecrnx_utils.c1333
1 files changed, 1333 insertions, 0 deletions
diff --git a/drivers/net/wireless/eswin/ecrnx_utils.c b/drivers/net/wireless/eswin/ecrnx_utils.c
new file mode 100644
index 000000000000..e60785c66f4d
--- /dev/null
+++ b/drivers/net/wireless/eswin/ecrnx_utils.c
@@ -0,0 +1,1333 @@
+/**
+ * ecrnx_utils.c
+ *
+ * IPC utility function definitions
+ *
+ * Copyright (C) ESWIN 2015-2020
+ */
+#include "ecrnx_utils.h"
+#include "ecrnx_defs.h"
+#include "ecrnx_rx.h"
+#include "ecrnx_tx.h"
+#include "ecrnx_msg_rx.h"
+#include "ecrnx_debugfs.h"
+#include "ecrnx_prof.h"
+#include "ipc_host.h"
+
+#ifdef CONFIG_ECRNX_ESWIN_SDIO
+#include "eswin_utils.h"
+#include "ecrnx_sdio.h"
+#include "sdio.h"
+#elif defined(CONFIG_ECRNX_ESWIN_USB)
+#include "eswin_utils.h"
+#include "ecrnx_usb.h"
+#include "usb.h"
+#endif
+
+
+/**
+ * ecrnx_ipc_elem_pool_allocs() - Allocate and push to fw a pool of buffer.
+ *
+ * @ecrnx_hw: Main driver structure
+ * @pool: Pool to allocate
+ * @nb: Size of the pool to allocate
+ * @elem_size: SIze of one pool element
+ * @pool_name: Name of the pool
+ * @push: Function to push one pool element to fw
+ *
+ * This function will allocate an array to store the list of element addresses,
+ * a dma pool and @nb element in the dma pool.
+ * Each element is set with '0' and then push to fw using the @push function.
+ * It assumes that pointer inside @ipc parameter are set to NULL at start.
+ *
+ * Return: 0 on success and <0 upon error. If error is returned any allocated
+ * memory is NOT freed and ecrnx_ipc_elem_pool_deallocs() must be called.
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+static int ecrnx_ipc_elem_pool_allocs(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_ipc_elem_pool *pool,
+ int nb, size_t elem_size, char *pool_name,
+ int (*push)(struct ipc_host_env_tag *,
+ void *, uint32_t))
+{
+ struct ecrnx_ipc_elem *buf;
+ int i;
+
+ pool->nb = 0;
+
+ /* allocate buf array */
+ pool->buf = kmalloc(nb * sizeof(struct ecrnx_ipc_elem), GFP_KERNEL);
+ if (!pool->buf) {
+ dev_err(ecrnx_hw->dev, "Allocation of buffer array for %s failed\n",
+ pool_name);
+ return -ENOMEM;
+ }
+
+ /* allocate dma pool */
+ pool->pool = dma_pool_create(pool_name, ecrnx_hw->dev, elem_size,
+ cache_line_size(), 0);
+ if (!pool->pool) {
+ dev_err(ecrnx_hw->dev, "Allocation of dma pool %s failed\n",
+ pool_name);
+ return -ENOMEM;
+ }
+
+ for (i = 0, buf = pool->buf; i < nb; buf++, i++) {
+
+ /* allocate an elem */
+ buf->addr = dma_pool_alloc(pool->pool, GFP_KERNEL, &buf->dma_addr);
+ if (!buf->addr) {
+ dev_err(ecrnx_hw->dev, "Allocation of block %d/%d in %s failed\n",
+ (i + 1), nb, pool_name);
+ return -ENOMEM;
+ }
+ pool->nb++;
+
+ /* reset the element */
+ memset(buf->addr, 0, elem_size);
+
+ /* push it to FW */
+ push(ecrnx_hw->ipc_env, buf, (uint32_t)buf->dma_addr);
+ }
+
+ return 0;
+}
+#endif
+/**
+ * ecrnx_ipc_elem_pool_deallocs() - Free all memory allocated for a pool
+ *
+ * @pool: Pool to free
+ *
+ * Must be call once after ecrnx_ipc_elem_pool_allocs(), even if it returned
+ * an error
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+static void ecrnx_ipc_elem_pool_deallocs(struct ecrnx_ipc_elem_pool *pool)
+{
+ struct ecrnx_ipc_elem *buf;
+ int i;
+
+ for (i = 0, buf = pool->buf; i < pool->nb ; buf++, i++) {
+ dma_pool_free(pool->pool, buf->addr, buf->dma_addr);
+ }
+ pool->nb = 0;
+
+ if (pool->pool)
+ dma_pool_destroy(pool->pool);
+ pool->pool = NULL;
+
+ if (pool->buf)
+ kfree(pool->buf);
+ pool->buf = NULL;
+}
+#endif
+/**
+ * ecrnx_ipc_elem_var_allocs - Alloc a single ipc buffer and push it to fw
+ *
+ * @ecrnx_hw: Main driver structure
+ * @elem: Element to allocate
+ * @elem_size: Size of the element to allcoate
+ * @dir: DMA direction
+ * @buf: If not NULL, used this buffer instead of allocating a new one. It must
+ * be @elem_size long and be allocated by kmalloc as kfree will be called.
+ * @init: Pointer to initial data to write in buffer before DMA sync. Needed
+ * only if direction is DMA_TO_DEVICE. If set it is assume that its size is
+ * @elem_size.
+ * @push: Function to push the element to fw. May be set to NULL.
+ *
+ * It allocates a buffer (or use the one provided with @buf), initializes it if
+ * @init is set, map buffer for DMA transfer, initializes @elem and push buffer
+ * to FW if @push is seet.
+ *
+ * Return: 0 on success and <0 upon error. If error is returned any allocated
+ * memory has been freed (including @buf if set).
+ */
+int ecrnx_ipc_elem_var_allocs(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_ipc_elem_var *elem, size_t elem_size,
+ enum dma_data_direction dir,
+ void *buf, const void *init,
+ void (*push)(struct ipc_host_env_tag *, uint32_t))
+{
+ if (buf) {
+ elem->addr = buf;
+ } else {
+ elem->addr = kmalloc(elem_size, GFP_KERNEL);
+ if (!elem->addr) {
+ dev_err(ecrnx_hw->dev, "Allocation of ipc buffer failed\n");
+ return -ENOMEM;
+ }
+ }
+ elem->size = elem_size;
+
+ if ((dir == DMA_TO_DEVICE) && init) {
+ memcpy(elem->addr, init, elem_size);
+ }
+
+#ifdef CONFIG_ECRNX_ESWIN
+ elem->dma_addr = (ptr_addr)elem->addr;
+#else
+ elem->dma_addr = dma_map_single(ecrnx_hw->dev, elem->addr, elem_size, dir);
+ if (dma_mapping_error(ecrnx_hw->dev, elem->dma_addr)) {
+ dev_err(ecrnx_hw->dev, "DMA mapping failed\n");
+ kfree(elem->addr);
+ elem->addr = NULL;
+ return -EIO;
+ }
+
+ if (push)
+ push(ecrnx_hw->ipc_env, elem->dma_addr);
+#endif
+ return 0;
+}
+
+/**
+ * ecrnx_ipc_elem_var_deallocs() - Free memory allocated for a single ipc buffer
+ *
+ * @ecrnx_hw: Main driver structure
+ * @elem: Element to free
+ */
+void ecrnx_ipc_elem_var_deallocs(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_ipc_elem_var *elem)
+{
+ if (!elem->addr)
+ return;
+#ifndef CONFIG_ECRNX_ESWIN
+ dma_unmap_single(ecrnx_hw->dev, elem->dma_addr, elem->size, DMA_TO_DEVICE);
+#endif
+ kfree(elem->addr);
+ elem->addr = NULL;
+}
+
+/**
+ * ecrnx_ipc_skb_elem_allocs() - Allocate and push a skb buffer for the FW
+ *
+ * @ecrnx_hw: Main driver data
+ * @elem: Pointer to the skb elem that will contain the address of the buffer
+ */
+int ecrnx_ipc_skb_elem_allocs(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_ipc_skb_elem *elem, size_t skb_size,
+ enum dma_data_direction dir,
+ int (*push)(struct ipc_host_env_tag *,
+ void *, uint32_t))
+{
+ elem->skb = dev_alloc_skb(skb_size);
+ if (unlikely(!elem->skb)) {
+ dev_err(ecrnx_hw->dev, "Allocation of ipc skb failed\n");
+ return -ENOMEM;
+ }
+
+ elem->dma_addr = dma_map_single(ecrnx_hw->dev, elem->skb->data, skb_size, dir);
+ if (unlikely(dma_mapping_error(ecrnx_hw->dev, elem->dma_addr))) {
+ dev_err(ecrnx_hw->dev, "DMA mapping failed\n");
+ dev_kfree_skb(elem->skb);
+ elem->skb = NULL;
+ return -EIO;
+ }
+
+ if (push){
+ push(ecrnx_hw->ipc_env, elem, elem->dma_addr);
+ }
+ return 0;
+}
+
+/**
+ * ecrnx_ipc_skb_elem_deallocs() - Free a skb buffer allocated for the FW
+ *
+ * @ecrnx_hw: Main driver data
+ * @elem: Pointer to the skb elem that contains the address of the buffer
+ * @skb_size: size of the skb buffer data
+ * @dir: DMA direction
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+static void ecrnx_ipc_skb_elem_deallocs(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_ipc_skb_elem *elem,
+ size_t skb_size, enum dma_data_direction dir)
+{
+ if (elem->skb) {
+ dma_unmap_single(ecrnx_hw->dev, elem->dma_addr, skb_size, dir);
+ dev_kfree_skb(elem->skb);
+ elem->skb = NULL;
+ }
+}
+#endif
+/**
+ * ecrnx_ipc_unsup_rx_vec_elem_allocs() - Allocate and push an unsupported
+ * RX vector buffer for the FW
+ *
+ * @ecrnx_hw: Main driver data
+ * @elem: Pointer to the skb elem that will contain the address of the buffer
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+int ecrnx_ipc_unsup_rx_vec_elem_allocs(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_ipc_skb_elem *elem)
+{
+ struct rx_vector_desc *rxdesc;
+
+ if (ecrnx_ipc_skb_elem_allocs(ecrnx_hw, elem,
+ ecrnx_hw->ipc_env->unsuprxvec_bufsz, DMA_FROM_DEVICE, NULL))
+ return -ENOMEM;
+
+ rxdesc = (struct rx_vector_desc *) elem->skb->data;
+ rxdesc->pattern = 0;
+ dma_sync_single_for_device(ecrnx_hw->dev,
+ elem->dma_addr + offsetof(struct rx_vector_desc, pattern),
+ sizeof(rxdesc->pattern), DMA_BIDIRECTIONAL);
+
+ ipc_host_unsup_rx_vec_buf_push(ecrnx_hw->ipc_env, elem, (u32) elem->dma_addr);
+
+ return 0;
+}
+#endif
+
+/**
+ * ecrnx_ipc_rxbuf_elems_deallocs() - Free all unsupported rx vector buffer
+ * allocated for the FW
+ *
+ * @ecrnx_hw: Main driver data
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+static void ecrnx_ipc_unsup_rx_vec_elems_deallocs(struct ecrnx_hw *ecrnx_hw)
+{
+ struct ecrnx_ipc_skb_elem *elem;
+ int i, nb = ecrnx_hw->ipc_env->unsuprxvec_bufnb;
+
+ if (!ecrnx_hw->e2aunsuprxvec_elems)
+ return;
+
+ for (i = 0, elem = ecrnx_hw->e2aunsuprxvec_elems; i < nb; i++, elem++) {
+ ecrnx_ipc_skb_elem_deallocs(ecrnx_hw, elem, ecrnx_hw->ipc_env->unsuprxvec_bufsz, DMA_FROM_DEVICE);
+ }
+
+ kfree(ecrnx_hw->e2aunsuprxvec_elems);
+ ecrnx_hw->e2aunsuprxvec_elems = NULL;
+}
+#endif
+
+/**
+* ecrnx_ipc_unsup_rx_vec_elems_allocs() - Allocate and push all unsupported RX
+* vector buffer for the FW
+*
+* @ecrnx_hw: Main driver data
+*/
+#ifndef CONFIG_ECRNX_ESWIN
+static int ecrnx_ipc_unsup_rx_vec_elems_allocs(struct ecrnx_hw *ecrnx_hw)
+{
+ struct ecrnx_ipc_skb_elem *elem;
+ int i, nb = ecrnx_hw->ipc_env->unsuprxvec_bufnb;
+
+ ecrnx_hw->e2aunsuprxvec_elems = kzalloc(nb * sizeof(struct ecrnx_ipc_skb_elem),
+ GFP_KERNEL);
+ if (!ecrnx_hw->e2aunsuprxvec_elems) {
+ dev_err(ecrnx_hw->dev, "Failed to allocate unsuprxvec_elems\n");
+ return -ENOMEM;
+ }
+
+ for (i = 0, elem = ecrnx_hw->e2aunsuprxvec_elems; i < nb; i++, elem++)
+ {
+ if (ecrnx_ipc_unsup_rx_vec_elem_allocs(ecrnx_hw, elem)) {
+ dev_err(ecrnx_hw->dev, "Failed to allocate unsuprxvec buf %d/%d\n",
+ i + 1, nb);
+ return -ENOMEM;
+ }
+ }
+ return 0;
+}
+#endif
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+/**
+ * ecrnx_ipc_rxbuf_elem_allocs() - Allocate and push a rx buffer for the FW
+ *
+ * @ecrnx_hw: Main driver data
+ * @elem: Pointer to the skb elem that will contain the address of the buffer
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+int ecrnx_ipc_rxbuf_elem_allocs(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_ipc_skb_elem *elem)
+{
+ struct hw_rxhdr *hw_rxhdr;
+
+ if (ecrnx_ipc_skb_elem_allocs(ecrnx_hw, elem,
+ ecrnx_hw->ipc_env->rx_bufsz, DMA_FROM_DEVICE, NULL))
+ return -ENOMEM;
+
+ hw_rxhdr = (struct hw_rxhdr *) elem->skb->data;
+ hw_rxhdr->pattern = 0;
+ dma_sync_single_for_device(ecrnx_hw->dev,
+ elem->dma_addr + offsetof(struct hw_rxhdr, pattern),
+ sizeof(hw_rxhdr->pattern), DMA_BIDIRECTIONAL);
+
+ ipc_host_rxbuf_push(ecrnx_hw->ipc_env, elem, (u32) elem->dma_addr);
+
+ return 0;
+}
+#endif
+
+/**
+ * ecrnx_ipc_rxbuf_elem_repush() - Reset and repush an already allocated RX buffer
+ *
+ * @ecrnx_hw: Main driver data
+ * @elem: Pointer to the skb elem that contains the address of the buffer
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+void ecrnx_ipc_rxbuf_elem_repush(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_ipc_skb_elem *elem)
+{
+ struct sk_buff *skb = elem->skb;
+ int pattern_offset = sizeof(struct hw_rxhdr);
+
+ ((struct hw_rxhdr *)skb->data)->pattern = 0;
+ dma_sync_single_for_device(ecrnx_hw->dev, elem->dma_addr,
+ pattern_offset, DMA_BIDIRECTIONAL);
+ ipc_host_rxbuf_push(ecrnx_hw->ipc_env, elem, (u32)elem->dma_addr);
+}
+#endif
+
+/**
+ * ecrnx_ipc_rxbuf_elems_allocs() - Allocate and push all RX buffer for the FW
+ *
+ * @ecrnx_hw: Main driver data
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+static int ecrnx_ipc_rxbuf_elems_allocs(struct ecrnx_hw *ecrnx_hw)
+{
+ struct ecrnx_ipc_skb_elem *elem;
+ int i, nb = ecrnx_hw->ipc_env->rx_bufnb;
+
+ ecrnx_hw->rxbuf_elems = kzalloc(nb * sizeof(struct ecrnx_ipc_skb_elem),
+ GFP_KERNEL);
+ if (!ecrnx_hw->rxbuf_elems) {
+ dev_err(ecrnx_hw->dev, "Failed to allocate rx_elems\n");
+ return -ENOMEM;
+ }
+
+ for (i = 0, elem = ecrnx_hw->rxbuf_elems; i < nb; i++, elem++) {
+ if (ecrnx_ipc_rxbuf_elem_allocs(ecrnx_hw, elem)) {
+ dev_err(ecrnx_hw->dev, "Failed to allocate rx buf %d/%d\n",
+ i + 1, nb);
+ return -ENOMEM;
+ }
+ }
+
+ return 0;
+}
+#endif
+
+/**
+ * ecrnx_ipc_rxbuf_elems_deallocs() - Free all RX buffer allocated for the FW
+ *
+ * @ecrnx_hw: Main driver data
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+static void ecrnx_ipc_rxbuf_elems_deallocs(struct ecrnx_hw *ecrnx_hw)
+{
+ struct ecrnx_ipc_skb_elem *elem;
+ int i, nb = ecrnx_hw->ipc_env->rx_bufnb;
+
+ if (!ecrnx_hw->rxbuf_elems)
+ return;
+
+ for (i = 0, elem = ecrnx_hw->rxbuf_elems; i < nb; i++, elem++) {
+ ecrnx_ipc_skb_elem_deallocs(ecrnx_hw, elem, ecrnx_hw->ipc_env->rx_bufsz, DMA_FROM_DEVICE);
+ }
+
+ kfree(ecrnx_hw->rxbuf_elems);
+ ecrnx_hw->rxbuf_elems = NULL;
+}
+#endif
+
+#else /* ! CONFIG_ECRNX_SOFTMAC */
+
+/**
+ * ecrnx_ipc_rxdesc_elem_repush() - Repush a rxdesc to FW
+ *
+ * @ecrnx_hw: Main driver data
+ * @elem: Rx desc to repush
+ *
+ * Once rx buffer has been received, the rxdesc used by FW to upload this
+ * buffer can be re-used for another rx buffer.
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+void ecrnx_ipc_rxdesc_elem_repush(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_ipc_elem *elem)
+{
+ struct rxdesc_tag *rxdesc = elem->addr;
+ rxdesc->status = 0;
+ dma_sync_single_for_device(ecrnx_hw->dev, elem->dma_addr,
+ sizeof(struct rxdesc_tag), DMA_BIDIRECTIONAL);
+ ipc_host_rxdesc_push(ecrnx_hw->ipc_env, elem, (u32)elem->dma_addr);
+}
+#endif
+/**
+ * ecrnx_ipc_rxbuf_elem_allocs() - Allocate and push a RX buffer for the FW
+ *
+ * @ecrnx_hw: Main driver data
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+int ecrnx_ipc_rxbuf_elem_allocs(struct ecrnx_hw *ecrnx_hw)
+{
+ struct sk_buff *skb;
+ struct hw_rxhdr *hw_rxhdr;
+ dma_addr_t dma_addr;
+ int size = ecrnx_hw->ipc_env->rx_bufsz;
+ int nb, idx;
+
+ skb = dev_alloc_skb(size);
+ if (unlikely(!skb)) {
+ dev_err(ecrnx_hw->dev, "Failed to allocate rx buffer\n");
+ return -ENOMEM;
+ }
+
+ dma_addr = dma_map_single(ecrnx_hw->dev, skb->data, size, DMA_FROM_DEVICE);
+
+ if (unlikely(dma_mapping_error(ecrnx_hw->dev, dma_addr))) {
+ dev_err(ecrnx_hw->dev, "Failed to map rx buffer\n");
+ goto err_skb;
+ }
+
+ hw_rxhdr = (struct hw_rxhdr *)skb->data;
+ hw_rxhdr->pattern = 0;
+ dma_sync_single_for_device(ecrnx_hw->dev,
+ dma_addr + offsetof(struct hw_rxhdr, pattern),
+ sizeof(hw_rxhdr->pattern), DMA_BIDIRECTIONAL);
+
+ /* Find first free slot */
+ nb = 0;
+ idx = ecrnx_hw->rxbuf_elems.idx;
+ while (ecrnx_hw->rxbuf_elems.skb[idx] && nb < ECRNX_RXBUFF_MAX) {
+ idx = ( idx + 1 ) % ECRNX_RXBUFF_MAX;
+ nb++;
+ }
+
+ if (WARN((nb == ECRNX_RXBUFF_MAX), "No more free space for rxbuff")) {
+ goto err_dma;
+ }
+
+ ecrnx_hw->rxbuf_elems.skb[idx] = skb;
+
+ /* Save info in skb control buffer */
+ ECRNX_RXBUFF_DMA_ADDR_SET(skb, dma_addr);
+ ECRNX_RXBUFF_PATTERN_SET(skb, ecrnx_rxbuff_pattern);
+ ECRNX_RXBUFF_IDX_SET(skb, idx);
+
+ /* Push buffer to FW */
+ ipc_host_rxbuf_push(ecrnx_hw->ipc_env, ECRNX_RXBUFF_IDX_TO_HOSTID(idx),
+ dma_addr);
+
+ /* Save idx so that on next push the free slot will be found quicker */
+ ecrnx_hw->rxbuf_elems.idx = ( idx + 1 ) % ECRNX_RXBUFF_MAX;
+
+ return 0;
+
+ err_dma:
+ dma_unmap_single(ecrnx_hw->dev, dma_addr, size, DMA_FROM_DEVICE);
+ err_skb:
+ dev_kfree_skb(skb);
+ return -ENOMEM;
+
+ return 0;
+}
+#endif
+
+/**
+ * ecrnx_ipc_rxbuf_elem_repush() - Repush a rxbuf to FW
+ *
+ * @ecrnx_hw: Main driver data
+ * @skb: Skb to repush
+ *
+ * In case a skb is not forwarded to upper layer it can be re-used.
+ * It is assumed that @skb has been verified before calling this function and
+ * that it is a valid rx buffer
+ * (i.e. skb == ecrnx_hw->rxbuf_elems.skb[ECRNX_RXBUFF_IDX_GET(skb)])
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+void ecrnx_ipc_rxbuf_elem_repush(struct ecrnx_hw *ecrnx_hw,
+ struct sk_buff *skb)
+{
+ dma_addr_t dma_addr;
+ struct hw_rxhdr *hw_rxhdr = (struct hw_rxhdr *)skb->data;
+ int idx;
+
+ /* reset pattern */
+ hw_rxhdr->pattern = 0;
+ dma_addr = ECRNX_RXBUFF_DMA_ADDR_GET(skb);
+ dma_sync_single_for_device(ecrnx_hw->dev,
+ dma_addr + offsetof(struct hw_rxhdr, pattern),
+ sizeof(hw_rxhdr->pattern), DMA_BIDIRECTIONAL);
+
+ /* re-push buffer to FW */
+ idx = ECRNX_RXBUFF_IDX_GET(skb);
+
+ ipc_host_rxbuf_push(ecrnx_hw->ipc_env, ECRNX_RXBUFF_IDX_TO_HOSTID(idx),
+ dma_addr);
+}
+#endif
+
+/**
+ * ecrnx_ipc_rxbuf_elems_allocs() - Allocate and push all RX buffer for the FW
+ *
+ * @ecrnx_hw: Main driver data
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+static int ecrnx_ipc_rxbuf_elems_allocs(struct ecrnx_hw *ecrnx_hw)
+{
+ //int i, nb = ecrnx_hw->ipc_env->rx_bufnb;
+ int i, nb = 0;
+
+ for (i = 0; i < ECRNX_RXBUFF_MAX; i++) {
+ ecrnx_hw->rxbuf_elems.skb[i] = NULL;
+ }
+ ecrnx_hw->rxbuf_elems.idx = 0;
+
+ for (i = 0; i < nb; i++) {
+ if (ecrnx_ipc_rxbuf_elem_allocs(ecrnx_hw)) {
+ dev_err(ecrnx_hw->dev, "Failed to allocate rx buf %d/%d\n",
+ i + 1, nb);
+ return -ENOMEM;
+ }
+ }
+ return 0;
+}
+#endif
+
+/**
+ * ecrnx_ipc_rxbuf_elems_deallocs() - Free all RX buffer allocated for the FW
+ *
+ * @ecrnx_hw: Main driver data
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+static void ecrnx_ipc_rxbuf_elems_deallocs(struct ecrnx_hw *ecrnx_hw)
+{
+ struct sk_buff *skb;
+ int i;
+
+ for (i = 0; i < ECRNX_RXBUFF_MAX; i++) {
+ if (ecrnx_hw->rxbuf_elems.skb[i]) {
+ skb = ecrnx_hw->rxbuf_elems.skb[i];
+ dma_unmap_single(ecrnx_hw->dev, ECRNX_RXBUFF_DMA_ADDR_GET(skb),
+ ecrnx_hw->ipc_env->rx_bufsz, DMA_FROM_DEVICE);
+ dev_kfree_skb(skb);
+ ecrnx_hw->rxbuf_elems.skb[i] = NULL;
+ }
+ }
+}
+#endif
+
+/**
+ * ecrnx_ipc_rxbuf_elem_pull() - Extract a skb from local table
+ *
+ * @ecrnx_hw: Main driver data
+ * @skb: SKb to extract for table
+ *
+ * After checking that skb is actually a pointer of local table, extract it
+ * from the table.
+ * When buffer is removed, DMA mapping is remove which has the effect to
+ * synchronize the buffer for the cpu.
+ * To be called before passing skb to upper layer.
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+void ecrnx_ipc_rxbuf_elem_pull(struct ecrnx_hw *ecrnx_hw, struct sk_buff *skb)
+{
+ unsigned int idx = ECRNX_RXBUFF_IDX_GET(skb);
+
+ if (ECRNX_RXBUFF_VALID_IDX(idx) && (ecrnx_hw->rxbuf_elems.skb[idx] == skb)) {
+ dma_addr_t dma_addr = ECRNX_RXBUFF_DMA_ADDR_GET(skb);
+ ecrnx_hw->rxbuf_elems.skb[idx] = NULL;
+ dma_unmap_single(ecrnx_hw->dev, dma_addr,
+ ecrnx_hw->ipc_env->rx_bufsz, DMA_FROM_DEVICE);
+ } else {
+ WARN(1, "Incorrect rxbuff idx skb=%p table[%u]=%p", skb, idx,
+ idx < ECRNX_RXBUFF_MAX ? ecrnx_hw->rxbuf_elems.skb[idx] : NULL);
+ }
+
+ /* Reset the pattern and idx */
+ ECRNX_RXBUFF_PATTERN_SET(skb, 0);
+ ECRNX_RXBUFF_IDX_SET(skb, ECRNX_RXBUFF_MAX);
+}
+#endif
+
+/**
+ * ecrnx_ipc_rxbuf_elem_sync() - Sync part of a RX buffer
+ *
+ * @ecrnx_hw: Main driver data
+ * @skb: SKb to sync
+ * @len: Len to sync
+ *
+ * After checking that skb is actually a pointer of local table, sync @p len
+ * bytes of the buffer for CPU. Buffer is not removed from the table
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+void ecrnx_ipc_rxbuf_elem_sync(struct ecrnx_hw *ecrnx_hw, struct sk_buff *skb,
+ int len)
+{
+ unsigned int idx = ECRNX_RXBUFF_IDX_GET(skb);
+
+ if (ECRNX_RXBUFF_VALID_IDX(idx) && (ecrnx_hw->rxbuf_elems.skb[idx] == skb)) {
+ dma_addr_t dma_addr = ECRNX_RXBUFF_DMA_ADDR_GET(skb);
+ dma_sync_single_for_cpu(ecrnx_hw->dev, dma_addr, len, DMA_FROM_DEVICE);
+ } else {
+ WARN(1, "Incorrect rxbuff idx skb=%p table[%u]=%p", skb, idx,
+ idx < ECRNX_RXBUFF_MAX ? ecrnx_hw->rxbuf_elems.skb[idx] : NULL);
+ }
+}
+#endif
+#endif /* ! CONFIG_ECRNX_SOFTMAC */
+
+/**
+ * ecrnx_elems_deallocs() - Deallocate IPC storage elements.
+ * @ecrnx_hw: Main driver data
+ *
+ * This function deallocates all the elements required for communications with
+ * LMAC, such as Rx Data elements, MSGs elements, ...
+ * This function should be called in correspondence with the allocation function.
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+static void ecrnx_elems_deallocs(struct ecrnx_hw *ecrnx_hw)
+{
+ ecrnx_ipc_rxbuf_elems_deallocs(ecrnx_hw);
+ ecrnx_ipc_unsup_rx_vec_elems_deallocs(ecrnx_hw);
+#ifdef CONFIG_ECRNX_FULLMAC
+ ecrnx_ipc_elem_pool_deallocs(&ecrnx_hw->e2arxdesc_pool);
+#endif
+ ecrnx_ipc_elem_pool_deallocs(&ecrnx_hw->e2amsgs_pool);
+ ecrnx_ipc_elem_pool_deallocs(&ecrnx_hw->dbgmsgs_pool);
+ ecrnx_ipc_elem_pool_deallocs(&ecrnx_hw->e2aradars_pool);
+ ecrnx_ipc_elem_var_deallocs(ecrnx_hw, &ecrnx_hw->pattern_elem);
+ ecrnx_ipc_elem_var_deallocs(ecrnx_hw, &ecrnx_hw->dbgdump_elem.buf);
+}
+#endif
+
+/**
+ * ecrnx_elems_allocs() - Allocate IPC storage elements.
+ * @ecrnx_hw: Main driver data
+ *
+ * This function allocates all the elements required for communications with
+ * LMAC, such as Rx Data elements, MSGs elements, ...
+ * This function should be called in correspondence with the deallocation function.
+ */
+static int ecrnx_elems_allocs(struct ecrnx_hw *ecrnx_hw)
+{
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+#ifndef CONFIG_ECRNX_ESWIN
+ if (dma_set_coherent_mask(ecrnx_hw->dev, DMA_BIT_MASK(32)))
+ goto err_alloc;
+ if (ecrnx_ipc_elem_pool_allocs(ecrnx_hw, &ecrnx_hw->e2amsgs_pool,
+ ecrnx_hw->ipc_env->ipc_e2amsg_bufnb,
+ ecrnx_hw->ipc_env->ipc_e2amsg_bufsz,
+ "ecrnx_ipc_e2amsgs_pool",
+ ipc_host_msgbuf_push))
+ goto err_alloc;
+
+ if (ecrnx_ipc_elem_pool_allocs(ecrnx_hw, &ecrnx_hw->dbgmsgs_pool,
+ ecrnx_hw->ipc_env->ipc_dbg_bufnb,
+ ecrnx_hw->ipc_env->ipc_dbg_bufsz,
+ "ecrnx_ipc_dbgmsgs_pool",
+ ipc_host_dbgbuf_push))
+ goto err_alloc;
+
+ if (ecrnx_ipc_elem_pool_allocs(ecrnx_hw, &ecrnx_hw->e2aradars_pool,
+ ecrnx_hw->ipc_env->radar_bufnb,
+ ecrnx_hw->ipc_env->radar_bufsz,
+ "ecrnx_ipc_e2aradars_pool",
+ ipc_host_radarbuf_push))
+ goto err_alloc;
+
+ if (ecrnx_ipc_unsup_rx_vec_elems_allocs(ecrnx_hw))
+ #error 1111
+ goto err_alloc;
+
+ if (ecrnx_ipc_elem_var_allocs(ecrnx_hw, &ecrnx_hw->pattern_elem,
+ sizeof(u32), DMA_TO_DEVICE,
+ NULL, &ecrnx_rxbuff_pattern,
+ ipc_host_patt_addr_push))
+ goto err_alloc;
+
+ if (ecrnx_ipc_elem_var_allocs(ecrnx_hw, &ecrnx_hw->dbgdump_elem.buf,
+ sizeof(struct dbg_debug_dump_tag),
+ DMA_FROM_DEVICE, NULL, NULL,
+ ipc_host_dbginfobuf_push))
+ goto err_alloc;
+
+ /*
+ * Note that the RX buffers are no longer allocated here as their size depends on the
+ * FW configuration, which is not available at that time.
+ * They will be allocated when checking the parameter compatibility between the driver
+ * and the underlying components (i.e. during the ecrnx_handle_dynparams() execution)
+ */
+
+#ifdef CONFIG_ECRNX_FULLMAC
+ if (ecrnx_ipc_elem_pool_allocs(ecrnx_hw, &ecrnx_hw->e2arxdesc_pool,
+ ecrnx_hw->ipc_env->rxdesc_nb,
+ sizeof(struct rxdesc_tag),
+ "ecrnx_ipc_e2arxdesc_pool",
+ ipc_host_rxdesc_push))
+ goto err_alloc;
+
+#endif /* CONFIG_ECRNX_FULLMAC */
+
+ return 0;
+
+err_alloc:
+ ecrnx_elems_deallocs(ecrnx_hw);
+ return -ENOMEM;
+#else
+ return 0;
+#endif
+}
+
+/**
+ * ecrnx_ipc_msg_push() - Push a msg to IPC queue
+ *
+ * @ecrnx_hw: Main driver data
+ * @msg_buf: Pointer to message
+ * @len: Size, in bytes, of message
+ */
+void ecrnx_ipc_msg_push(struct ecrnx_hw *ecrnx_hw, void *msg_buf, uint16_t len)
+{
+ ecrnx_hw->msg_tx++;
+ ipc_host_msg_push(ecrnx_hw->ipc_env, msg_buf, len);
+}
+
+/**
+ * ecrnx_ipc_txdesc_push() - Push a txdesc to FW
+ *
+ * @ecrnx_hw: Main driver data
+ * @tx_desc: Pointer on &struct txdesc_api to push to FW
+ * @hostid: Pointer save in ipc env to retrieve tx buffer upon confirmation.
+ * @hw_queue: Hw queue to push txdesc to
+ * @user: User position to push the txdesc to. It must be set to 0 if MU-MIMMO
+ * is not used.
+ */
+void ecrnx_ipc_txdesc_push(struct ecrnx_hw *ecrnx_hw, void *tx_desc,
+ void *hostid, int hw_queue, int user)
+{
+
+#if !defined(CONFIG_ECRNX_ESWIN_SDIO) && !defined(CONFIG_ECRNX_ESWIN_USB)
+ volatile struct txdesc_host *txdesc_host;
+ u32 *src, *dst;
+ int i;
+
+ txdesc_host = ipc_host_txdesc_get(ecrnx_hw->ipc_env, hw_queue, user);
+ BUG_ON(!txdesc_host);
+
+ dst = (typeof(dst))&txdesc_host->api;
+ src = (typeof(src))tx_desc;
+ for (i = 0; i < sizeof(txdesc_host->api) / sizeof(*src); i++)
+ *dst++ = *src++;
+
+ wmb(); /* vs desc */
+
+ ipc_host_txdesc_push(ecrnx_hw->ipc_env, hw_queue, user, hostid);
+#else
+ ecrnx_frame_send(ecrnx_hw, tx_desc, hostid, hw_queue, user);
+#endif
+}
+
+/**
+ * ecrnx_ipc_fw_trace_desc_get() - Return pointer to the start of trace
+ * description in IPC environment
+ *
+ * @ecrnx_hw: Main driver data
+ */
+void *ecrnx_ipc_fw_trace_desc_get(struct ecrnx_hw *ecrnx_hw)
+{
+#ifndef CONFIG_ECRNX_ESWIN
+ return (void *)&(ecrnx_hw->ipc_env->shared->trace_pattern);
+#else
+ return NULL;
+#endif
+}
+
+#ifndef CONFIG_ECRNX_ESWIN
+/**
+ * ecrnx_ipc_sta_buffer_init - Initialize counter of bufferred data for a given sta
+ *
+ * @ecrnx_hw: Main driver data
+ * @sta_idx: Index of the station to initialize
+ */
+void ecrnx_ipc_sta_buffer_init(struct ecrnx_hw *ecrnx_hw, int sta_idx)
+{
+ int i;
+ volatile u32_l *buffered;
+
+ if (sta_idx >= NX_REMOTE_STA_MAX)
+ return;
+
+ buffered = ecrnx_hw->ipc_env->shared->buffered[sta_idx];
+
+ for (i = 0; i < TID_MAX; i++) {
+ *buffered++ = 0;
+ }
+}
+#endif
+/**
+ * ecrnx_ipc_sta_buffer - Update counter of bufferred data for a given sta
+ *
+ * @ecrnx_hw: Main driver data
+ * @sta: Managed station
+ * @tid: TID on which data has been added or removed
+ * @size: Size of data to add (or remove if < 0) to STA buffer.
+ */
+void ecrnx_ipc_sta_buffer(struct ecrnx_hw *ecrnx_hw, struct ecrnx_sta *sta, int tid, int size)
+{
+#ifndef CONFIG_ECRNX_ESWIN
+ u32_l *buffered;
+
+ if (!sta)
+ return;
+
+ if ((sta->sta_idx >= NX_REMOTE_STA_MAX) || (tid >= TID_MAX))
+ return;
+
+ buffered = &ecrnx_hw->ipc_env->shared->buffered[sta->sta_idx][tid];
+
+ if (size < 0) {
+ size = -size;
+ if (*buffered < size)
+ *buffered = 0;
+ else
+ *buffered -= size;
+ } else {
+ // no test on overflow
+ *buffered += size;
+ }
+#endif
+}
+
+/**
+ * ecrnx_msgind() - IRQ handler callback for %IPC_IRQ_E2A_MSG
+ *
+ * @pthis: Pointer to main driver data
+ * @hostid: Pointer to IPC elem from e2amsgs_pool
+ */
+static u8 ecrnx_msgind(void *pthis, void *hostid)
+{
+ struct ecrnx_hw *ecrnx_hw = pthis;
+ u8 ret = 0;
+#ifndef CONFIG_ECRNX_ESWIN
+ struct ecrnx_ipc_elem *elem = hostid;
+ struct ipc_e2a_msg *msg = elem->addr;
+
+ REG_SW_SET_PROFILING(ecrnx_hw, SW_PROF_MSGIND);
+
+ /* Look for pattern which means that this hostbuf has been used for a MSG */
+ if (msg->pattern != IPC_MSGE2A_VALID_PATTERN) {
+ ret = -1;
+ goto msg_no_push;
+ }
+#else
+ struct ipc_e2a_msg *msg = NULL;
+
+ ecrnx_hw->msg_rx++;
+ ECRNX_DBG("%s enter 0x%x, 0x%x!!\n", __func__, pthis, hostid);
+ if(!pthis || !hostid){
+ ECRNX_ERR(" %s input param error!! \n", __func__);
+ return ret;
+ }
+ msg = hostid;
+#endif
+ /* Relay further actions to the msg parser */
+ ecrnx_rx_handle_msg(ecrnx_hw, msg);
+
+#ifndef CONFIG_ECRNX_ESWIN
+ /* Reset the msg element and re-use it */
+ msg->pattern = 0;
+ wmb();
+
+ /* Push back the buffer to the LMAC */
+ ipc_host_msgbuf_push(ecrnx_hw->ipc_env, elem, elem->dma_addr);
+
+msg_no_push:
+ REG_SW_CLEAR_PROFILING(ecrnx_hw, SW_PROF_MSGIND);
+#endif
+ ECRNX_DBG("%s exit!!", __func__);
+ return ret;
+}
+
+/**
+ * ecrnx_msgackind() - IRQ handler callback for %IPC_IRQ_E2A_MSG_ACK
+ *
+ * @pthis: Pointer to main driver data
+ * @hostid: Pointer to command acknoledged
+ */
+static u8 ecrnx_msgackind(void *pthis, void *hostid)
+{
+ struct ecrnx_hw *ecrnx_hw = (struct ecrnx_hw *)pthis;
+
+ ecrnx_hw->msg_tx_done++;
+ ecrnx_hw->cmd_mgr.llind(&ecrnx_hw->cmd_mgr, (struct ecrnx_cmd *)hostid);
+ return -1;
+}
+
+/**
+ * ecrnx_radarind() - IRQ handler callback for %IPC_IRQ_E2A_RADAR
+ *
+ * @pthis: Pointer to main driver data
+ * @hostid: Pointer to IPC elem from e2aradars_pool
+ */
+static u8 ecrnx_radarind(void *pthis, void *hostid)
+{
+#ifdef CONFIG_ECRNX_RADAR
+ struct ecrnx_hw *ecrnx_hw = pthis;
+ struct ecrnx_ipc_elem *elem = hostid;
+ struct radar_pulse_array_desc *pulses = elem->addr;
+ u8 ret = 0;
+ int i;
+
+ /* Look for pulse count meaning that this hostbuf contains RADAR pulses */
+ if (pulses->cnt == 0) {
+ ret = -1;
+ goto radar_no_push;
+ }
+
+ if (ecrnx_radar_detection_is_enable(&ecrnx_hw->radar, pulses->idx)) {
+ /* Save the received pulses only if radar detection is enabled */
+ for (i = 0; i < pulses->cnt; i++) {
+ struct ecrnx_radar_pulses *p = &ecrnx_hw->radar.pulses[pulses->idx];
+
+ p->buffer[p->index] = pulses->pulse[i];
+ p->index = (p->index + 1) % ECRNX_RADAR_PULSE_MAX;
+ if (p->count < ECRNX_RADAR_PULSE_MAX)
+ p->count++;
+ }
+
+ /* Defer pulse processing in separate work */
+ if (! work_pending(&ecrnx_hw->radar.detection_work))
+ schedule_work(&ecrnx_hw->radar.detection_work);
+ }
+
+ /* Reset the radar element and re-use it */
+ pulses->cnt = 0;
+#ifndef CONFIG_ECRNX_ESWIN
+ wmb();
+
+ /* Push back the buffer to the LMAC */
+ ipc_host_radarbuf_push(ecrnx_hw->ipc_env, elem, (u32)elem->dma_addr);
+#endif
+radar_no_push:
+ return ret;
+#else
+ return -1;
+#endif
+}
+
+/**
+ * ecrnx_prim_tbtt_ind() - IRQ handler callback for %IPC_IRQ_E2A_TBTT_PRIM
+ *
+ * @pthis: Pointer to main driver data
+ */
+static void ecrnx_prim_tbtt_ind(void *pthis)
+{
+#ifdef CONFIG_ECRNX_SOFTMAC
+ struct ecrnx_hw *ecrnx_hw = (struct ecrnx_hw *)pthis;
+ ecrnx_tx_bcns(ecrnx_hw);
+#endif /* CONFIG_ECRNX_SOFTMAC */
+}
+
+/**
+ * ecrnx_sec_tbtt_ind() - IRQ handler callback for %IPC_IRQ_E2A_TBTT_SEC
+ *
+ * @pthis: Pointer to main driver data
+ */
+static void ecrnx_sec_tbtt_ind(void *pthis)
+{
+}
+
+/**
+ * ecrnx_dbgind() - IRQ handler callback for %IPC_IRQ_E2A_DBG
+ *
+ * @pthis: Pointer to main driver data
+ * @hostid: Pointer to IPC elem from dbgmsgs_pool
+ */
+
+#ifdef CONFIG_ECRNX_ESWIN_USB
+ extern void usb_dbg_printf(void * data, int len);
+#endif
+static u8 ecrnx_dbgind(void *pthis, void *hostid)
+{
+ u8 ret = 0;
+#ifndef CONFIG_ECRNX_ESWIN
+ struct ecrnx_hw *ecrnx_hw = (struct ecrnx_hw *)pthis;
+ struct ecrnx_ipc_elem *elem = hostid;
+ struct ipc_dbg_msg *dbg_msg = elem->addr;
+
+ REG_SW_SET_PROFILING(ecrnx_hw, SW_PROF_DBGIND);
+
+ /* Look for pattern which means that this hostbuf has been used for a MSG */
+ if (dbg_msg->pattern != IPC_DBG_VALID_PATTERN) {
+ ret = -1;
+ goto dbg_no_push;
+ }
+
+ /* Display the string */
+ //printk("%s %s", (char *)FW_STR, (char *)dbg_msg->string);
+
+ /* Reset the msg element and re-use it */
+ dbg_msg->pattern = 0;
+ wmb();
+
+ /* Push back the buffer to the LMAC */
+ ipc_host_dbgbuf_push(ecrnx_hw->ipc_env, elem, (u32)elem->dma_addr);
+
+dbg_no_push:
+ REG_SW_CLEAR_PROFILING(ecrnx_hw, SW_PROF_DBGIND);
+
+#else
+ struct sk_buff *skb = (struct sk_buff *)hostid;
+#ifdef CONFIG_ECRNX_ESWIN_USB
+ usb_dbg_printf(skb->data, skb->len);
+#else
+ uint8_t string[IPC_DBG_PARAM_SIZE] = {0};
+ if(skb->len < IPC_DBG_PARAM_SIZE)
+ {
+ memcpy(string, skb->data, skb->len);
+ }
+ else
+ {
+ printk("waring: string buff no enough \n");
+ memcpy(string, skb->data, IPC_DBG_PARAM_SIZE-1);
+ }
+ ECRNX_PRINT("%s %s", (char *)FW_STR, (char *)string);
+#endif
+#endif
+
+ return ret;
+}
+
+/**
+ * ecrnx_ipc_rxbuf_init() - Allocate and initialize RX buffers.
+ *
+ * @ecrnx_hw: Main driver data
+ * @rx_bufsz: Size of the buffer to be allocated
+ *
+ * This function updates the RX buffer size according to the parameter and allocates the
+ * RX buffers
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+int ecrnx_ipc_rxbuf_init(struct ecrnx_hw *ecrnx_hw, uint32_t rx_bufsz)
+{
+ ecrnx_hw->ipc_env->rx_bufsz = rx_bufsz;
+ return(ecrnx_ipc_rxbuf_elems_allocs(ecrnx_hw));
+}
+#endif
+/**
+ * ecrnx_ipc_init() - Initialize IPC interface.
+ *
+ * @ecrnx_hw: Main driver data
+ * @shared_ram: Pointer to shared memory that contains IPC shared struct
+ *
+ * This function initializes IPC interface by registering callbacks, setting
+ * shared memory area and calling IPC Init function.
+ * It should be called only once during driver's lifetime.
+ */
+int ecrnx_ipc_init(struct ecrnx_hw *ecrnx_hw, u8 *shared_ram)
+{
+ struct ipc_host_cb_tag cb;
+ int res;
+ ECRNX_DBG("%s entry!!", __func__);
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* initialize the API interface */
+ cb.recv_data_ind = ecrnx_rxdataind;
+ cb.recv_radar_ind = ecrnx_radarind;
+ cb.recv_msg_ind = ecrnx_msgind;
+ cb.recv_msgack_ind = ecrnx_msgackind;
+ cb.recv_dbg_ind = ecrnx_dbgind;
+ cb.send_data_cfm = ecrnx_txdatacfm;
+ cb.handle_data_cfm = ecrnx_handle_tx_datacfm;
+
+ cb.prim_tbtt_ind = ecrnx_prim_tbtt_ind;
+ cb.sec_tbtt_ind = ecrnx_sec_tbtt_ind;
+ cb.recv_unsup_rx_vec_ind = ecrnx_unsup_rx_vec_ind;
+
+ /* set the IPC environment */
+ ecrnx_hw->ipc_env = (struct ipc_host_env_tag *)
+ kzalloc(sizeof(struct ipc_host_env_tag), GFP_KERNEL);
+
+ if (!ecrnx_hw->ipc_env)
+ return -ENOMEM;
+
+ /* call the initialization of the IPC */
+ ipc_host_init(ecrnx_hw->ipc_env, &cb,
+ (struct ipc_shared_env_tag *)shared_ram, ecrnx_hw);
+
+ ecrnx_cmd_mgr_init(&ecrnx_hw->cmd_mgr);
+
+ ecrnx_rx_reord_init(ecrnx_hw);
+#ifdef CONFIG_ECRNX_ESWIN_SDIO
+ ecrnx_sdio_init(ecrnx_hw);
+#elif defined(CONFIG_ECRNX_ESWIN_USB)
+ ecrnx_usb_init(ecrnx_hw);
+#endif
+
+ res = ecrnx_elems_allocs(ecrnx_hw);
+ if (res) {
+
+ kfree(ecrnx_hw->ipc_env);
+ ecrnx_hw->ipc_env = NULL;
+ }
+ ECRNX_DBG("%s exit!!", __func__);
+ return res;
+}
+
+/**
+ * ecrnx_ipc_deinit() - Release IPC interface
+ *
+ * @ecrnx_hw: Main driver data
+ */
+void ecrnx_ipc_deinit(struct ecrnx_hw *ecrnx_hw)
+{
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ ecrnx_ipc_tx_drain(ecrnx_hw);
+ ecrnx_cmd_mgr_deinit(&ecrnx_hw->cmd_mgr);
+#ifndef CONFIG_ECRNX_ESWIN
+ ecrnx_elems_deallocs(ecrnx_hw);
+#endif
+
+ ecrnx_rx_reord_deinit(ecrnx_hw);
+#ifdef CONFIG_ECRNX_ESWIN_SDIO
+ if (ecrnx_hw->ipc_env->shared) {
+ kfree(ecrnx_hw->ipc_env->shared);
+ ecrnx_hw->ipc_env->shared = NULL;
+ }
+ ecrnx_sdio_deinit(ecrnx_hw);
+#elif defined(CONFIG_ECRNX_ESWIN_USB)
+ ecrnx_usb_deinit(ecrnx_hw);
+#endif
+
+ if (ecrnx_hw->ipc_env) {
+ kfree(ecrnx_hw->ipc_env);
+ ecrnx_hw->ipc_env = NULL;
+ }
+}
+
+/**
+ * ecrnx_ipc_start() - Start IPC interface
+ *
+ * @ecrnx_hw: Main driver data
+ */
+void ecrnx_ipc_start(struct ecrnx_hw *ecrnx_hw)
+{
+ ipc_host_enable_irq(ecrnx_hw->ipc_env, IPC_IRQ_E2A_ALL);
+}
+
+/**
+ * ecrnx_ipc_stop() - Stop IPC interface
+ *
+ * @ecrnx_hw: Main driver data
+ */
+void ecrnx_ipc_stop(struct ecrnx_hw *ecrnx_hw)
+{
+ ipc_host_disable_irq(ecrnx_hw->ipc_env, IPC_IRQ_E2A_ALL);
+}
+
+/**
+ * ecrnx_ipc_tx_drain() - Flush IPC TX buffers
+ *
+ * @ecrnx_hw: Main driver data
+ *
+ * This assumes LMAC is still (tx wise) and there's no TX race until LMAC is up
+ * tx wise.
+ * This also lets both IPC sides remain in sync before resetting the LMAC,
+ * e.g with ecrnx_send_reset.
+ */
+void ecrnx_ipc_tx_drain(struct ecrnx_hw *ecrnx_hw)
+{
+ int i, j;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ if (!ecrnx_hw->ipc_env) {
+ printk(KERN_CRIT "%s: bypassing (restart must have failed)\n", __func__);
+ return;
+ }
+
+ for (i = 0; i < ECRNX_HWQ_NB; i++) {
+ for (j = 0; j < nx_txuser_cnt[i]; j++) {
+ struct sk_buff *skb;
+ while ((skb = (struct sk_buff *)ipc_host_tx_flush(ecrnx_hw->ipc_env, i, j))) {
+ struct ecrnx_sw_txhdr *sw_txhdr =
+ ((struct ecrnx_txhdr *)skb->data)->sw_hdr;
+#ifndef CONFIG_ECRNX_ESWIN
+ #ifdef CONFIG_ECRNX_AMSDUS_TX
+ if (sw_txhdr->desc.host.packet_cnt > 1) {
+ struct ecrnx_amsdu_txhdr *amsdu_txhdr;
+ list_for_each_entry(amsdu_txhdr, &sw_txhdr->amsdu.hdrs, list) {
+ dma_unmap_single(ecrnx_hw->dev, amsdu_txhdr->dma_addr,
+ amsdu_txhdr->map_len, DMA_TO_DEVICE);
+ dev_kfree_skb_any(amsdu_txhdr->skb);
+ }
+ }
+ #endif
+ kmem_cache_free(ecrnx_hw->sw_txhdr_cache, sw_txhdr);
+ dma_unmap_single(ecrnx_hw->dev, sw_txhdr->dma_addr,
+ sw_txhdr->map_len, DMA_TO_DEVICE);
+#endif
+ skb_pull(skb, sw_txhdr->headroom);
+#ifdef CONFIG_ECRNX_SOFTMAC
+ ieee80211_free_txskb(ecrnx_hw->hw, skb);
+#else
+ dev_kfree_skb_any(skb);
+#endif /* CONFIG_ECRNX_SOFTMAC */
+ }
+ }
+ }
+}
+
+/**
+ * ecrnx_ipc_tx_pending() - Check if TX pframes are pending at FW level
+ *
+ * @ecrnx_hw: Main driver data
+ */
+bool ecrnx_ipc_tx_pending(struct ecrnx_hw *ecrnx_hw)
+{
+ return ipc_host_tx_frames_pending(ecrnx_hw->ipc_env);
+}
+
+/**
+ * ecrnx_error_ind() - %DBG_ERROR_IND message callback
+ *
+ * @ecrnx_hw: Main driver data
+ *
+ * This function triggers the UMH script call that will indicate to the user
+ * space the error that occurred and stored the debug dump. Once the UMH script
+ * is executed, the ecrnx_umh_done() function has to be called.
+ */
+void ecrnx_error_ind(struct ecrnx_hw *ecrnx_hw)
+{
+ struct ecrnx_ipc_elem_var *elem = &ecrnx_hw->dbgdump_elem.buf;
+ struct dbg_debug_dump_tag *dump = elem->addr;
+
+ dma_sync_single_for_device(ecrnx_hw->dev, elem->dma_addr, elem->size,
+ DMA_FROM_DEVICE);
+ dev_err(ecrnx_hw->dev, "(type %d): dump received\n",
+ dump->dbg_info.error_type);
+
+#ifdef CONFIG_ECRNX_DEBUGFS
+ ecrnx_hw->debugfs.trace_prst = true;
+ ecrnx_trigger_um_helper(&ecrnx_hw->debugfs);
+#endif
+}
+
+/**
+ * ecrnx_umh_done() - Indicate User Mode helper finished
+ *
+ * @ecrnx_hw: Main driver data
+ *
+ */
+void ecrnx_umh_done(struct ecrnx_hw *ecrnx_hw)
+{
+ if (!test_bit(ECRNX_DEV_STARTED, &ecrnx_hw->flags))
+ return;
+
+ /* this assumes error_ind won't trigger before ipc_host_dbginfobuf_push
+ is called and so does not irq protect (TODO) against error_ind */
+#ifdef CONFIG_ECRNX_DEBUGFS
+ ecrnx_hw->debugfs.trace_prst = false;
+#ifndef CONFIG_ECRNX_ESWIN
+ ipc_host_dbginfobuf_push(ecrnx_hw->ipc_env, ecrnx_hw->dbgdump_elem.buf.dma_addr);
+#endif
+#endif
+}