ASR_BASE

Change-Id: Icf3719cc0afe3eeb3edc7fa80a2eb5199ca9dda1
diff --git a/package/kernel/aic-wl/aic-driver/Driver/aic8800d/rwnx_utils.c b/package/kernel/aic-wl/aic-driver/Driver/aic8800d/rwnx_utils.c
new file mode 100644
index 0000000..f97e257
--- /dev/null
+++ b/package/kernel/aic-wl/aic-driver/Driver/aic8800d/rwnx_utils.c
@@ -0,0 +1,1244 @@
+/**
+ * rwnx_utils.c
+ *
+ * IPC utility function definitions
+ *
+ * Copyright (C) RivieraWaves 2012-2021
+ */
+#include "rwnx_utils.h"
+#include "rwnx_defs.h"
+#include "rwnx_rx.h"
+#include "rwnx_tx.h"
+#include "rwnx_msg_rx.h"
+#include "rwnx_debugfs.h"
+#include "rwnx_prof.h"
+#include "ipc_host.h"
+
+#ifdef CONFIG_RWNX_FULLMAC
+#define FW_STR  "fmac"
+#endif
+
+/**
+ * rwnx_ipc_buf_pool_alloc() - Allocate and push to fw a pool of IPC buffer.
+ *
+ * @rwnx_hw: Main driver structure
+ * @pool: Pool to allocate
+ * @nb: Size of the pool to allocate
+ * @buf_size: Size of one pool element
+ * @pool_name: Name of the pool
+ * @push: Function to push one pool buffer to fw
+ *
+ * This function will allocate an array to store the list of IPC buffers,
+ * a dma pool and @nb element in the dma pool.
+ * Each buffer is initialized with '0' and then pushed to fw using the @push function.
+ *
+ * Return: 0 on success and <0 upon error. If error is returned any allocated
+ * memory is NOT freed and rwnx_ipc_buf_pool_dealloc() must be called.
+ */
+static int rwnx_ipc_buf_pool_alloc(struct rwnx_hw *rwnx_hw,
+                                   struct rwnx_ipc_buf_pool *pool,
+                                   int nb, size_t buf_size, char *pool_name,
+                                   int (*push)(struct ipc_host_env_tag *,
+                                               struct rwnx_ipc_buf *))
+{
+    struct rwnx_ipc_buf *buf;
+    int i;
+
+    pool->nb = 0;
+
+    /* allocate buf array */
+    pool->buffers = kmalloc(nb * sizeof(struct rwnx_ipc_buf), GFP_KERNEL);
+    if (!pool->buffers) {
+        dev_err(rwnx_hw->dev, "Allocation of buffer array for %s failed\n",
+                pool_name);
+        return -ENOMEM;
+    }
+
+    /* allocate dma pool */
+    pool->pool = dma_pool_create(pool_name, rwnx_hw->dev, buf_size,
+                                 cache_line_size(), 0);
+    if (!pool->pool) {
+        dev_err(rwnx_hw->dev, "Allocation of dma pool %s failed\n",
+                pool_name);
+        return -ENOMEM;
+    }
+
+    for (i = 0, buf = pool->buffers; i < nb; buf++, i++) {
+        /* allocate a buffer */
+        buf->size = buf_size;
+        buf->addr = dma_pool_alloc(pool->pool, GFP_KERNEL, &buf->dma_addr);
+        if (!buf->addr) {
+            dev_err(rwnx_hw->dev, "Allocation of block %d/%d in %s failed\n",
+                    (i + 1), nb, pool_name);
+            return -ENOMEM;
+        }
+        pool->nb++;
+
+        /* reset the buffer */
+        memset(buf->addr, 0, buf_size);
+
+        /* push it to FW */
+        push(rwnx_hw->ipc_env, buf);
+    }
+
+    return 0;
+}
+
+/**
+ * rwnx_ipc_buf_pool_dealloc() - Free all memory allocated for a pool
+ *
+ * @pool: Pool to free
+ *
+ * Must be call once after rwnx_ipc_buf_pool_alloc(), even if it returned
+ * an error
+ */
+static void rwnx_ipc_buf_pool_dealloc(struct rwnx_ipc_buf_pool *pool)
+{
+    struct rwnx_ipc_buf *buf;
+    int i;
+
+    for (i = 0, buf = pool->buffers; 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->buffers)
+        kfree(pool->buffers);
+    pool->buffers = NULL;
+}
+
+/**
+ * rwnx_ipc_buf_alloc - Alloc a single ipc buffer and MAP it for DMA access
+ *
+ * @rwnx_hw: Main driver structure
+ * @buf: IPC buffer to allocate
+ * @buf_size: Size of the buffer to allocate
+ * @dir: DMA direction
+ * @init: Pointer to initial data to write in buffer before DMA sync. Used
+ * only if direction is DMA_TO_DEVICE and it must be at least @buf_size long
+ *
+ * It allocates a buffer, initializes it if @init is set, and map it for DMA
+ * Use @rwnx_ipc_buf_dealloc when this buffer is no longer needed.
+ *
+ * @return: 0 on success and <0 upon error. If error is returned any allocated
+ * memory has been freed.
+ */
+int rwnx_ipc_buf_alloc(struct rwnx_hw *rwnx_hw, struct rwnx_ipc_buf *buf,
+                       size_t buf_size, enum dma_data_direction dir, const void *init)
+{
+    buf->addr = kmalloc(buf_size, GFP_KERNEL);
+    if (!buf->addr)
+        return -ENOMEM;
+
+    buf->size = buf_size;
+
+    if ((dir == DMA_TO_DEVICE) && init) {
+        memcpy(buf->addr, init, buf_size);
+    }
+
+    buf->dma_addr = dma_map_single(rwnx_hw->dev, buf->addr, buf_size, dir);
+    if (dma_mapping_error(rwnx_hw->dev, buf->dma_addr)) {
+        kfree(buf->addr);
+        buf->addr = NULL;
+        return -EIO;
+    }
+
+    return 0;
+}
+
+/**
+ * rwnx_ipc_buf_dealloc() - Free memory allocated for a single ipc buffer
+ *
+ * @rwnx_hw: Main driver structure
+ * @buf: IPC buffer to free
+ *
+ * IPC buffer must have been allocated by @rwnx_ipc_buf_alloc() or initialized
+ * by @rwnx_ipc_buf_init() and pointing to a buffer allocated by kmalloc.
+ */
+void rwnx_ipc_buf_dealloc(struct rwnx_hw *rwnx_hw, struct rwnx_ipc_buf *buf)
+{
+    if (!buf->addr)
+        return;
+    dma_unmap_single(rwnx_hw->dev, buf->dma_addr, buf->size, DMA_TO_DEVICE);
+    kfree(buf->addr);
+    buf->addr = NULL;
+}
+
+/**
+ * rwnx_ipc_buf_a2e_init - Initialize an Application to Embedded IPC buffer
+ * with a pre-allocated buffer
+ *
+ * @rwnx_hw: Main driver structure
+ * @buf: IPC buffer to initialize
+ * @data: Data buffer to use for the IPC buffer.
+ * @buf_size: Size of the buffer the @data buffer
+ *
+ * Initialize the IPC buffer with the provided buffer and map it for DMA transfer.
+ * The mapping direction is always DMA_TO_DEVICE as this an "a2e" buffer.
+ * Use @rwnx_ipc_buf_dealloc() when this buffer is no longer needed.
+ *
+ * @return: 0 on success and <0 upon error. If error is returned the @data buffer
+ * is freed.
+ */
+int rwnx_ipc_buf_a2e_init(struct rwnx_hw *rwnx_hw, struct rwnx_ipc_buf *buf,
+                          void *data, size_t buf_size)
+{
+    buf->addr = data;
+    buf->size = buf_size;
+    buf->dma_addr = dma_map_single(rwnx_hw->dev, buf->addr, buf_size,
+                                   DMA_TO_DEVICE);
+    if (dma_mapping_error(rwnx_hw->dev, buf->dma_addr)) {
+        buf->addr = NULL;
+        return -EIO;
+    }
+
+    return 0;
+}
+
+/**
+ * rwnx_ipc_buf_release() - Release DMA mapping for an IPC buffer
+ *
+ * @rwnx_hw: Main driver structure
+ * @buf: IPC buffer to release
+ * @dir: DMA direction.
+ *
+ * This also "release" the IPC buffer structure (i.e. its addr field is reset)
+ * so that it cannot be re-used except to map another buffer.
+ */
+void rwnx_ipc_buf_release(struct rwnx_hw *rwnx_hw, struct rwnx_ipc_buf *buf,
+                          enum dma_data_direction dir)
+{
+    if (!buf->addr)
+        return;
+    dma_unmap_single(rwnx_hw->dev, buf->dma_addr, buf->size, dir);
+    buf->addr = NULL;
+}
+
+/**
+ * rwnx_ipc_buf_e2a_sync() - Synchronize all (or part) of an IPC buffer before
+ * reading content written by the embedded
+ *
+ * @rwnx_hw: Main driver structure
+ * @buf: IPC buffer to sync
+ * @len: Length to read, 0 means the whole buffer
+ */
+void rwnx_ipc_buf_e2a_sync(struct rwnx_hw *rwnx_hw, struct rwnx_ipc_buf *buf,
+                           size_t len)
+{
+    if (!len)
+        len = buf->size;
+
+    dma_sync_single_for_cpu(rwnx_hw->dev, buf->dma_addr, len, DMA_FROM_DEVICE);
+}
+
+/**
+ * rwnx_ipc_buf_e2a_sync_back() - Synchronize back all (or part) of an IPC buffer
+ * to allow embedded updating its content.
+ *
+ * @rwnx_hw: Main driver structure
+ * @buf: IPC buffer to sync
+ * @len: Length to sync back, 0 means the whole buffer
+ *
+ * Must be called after each call to rwnx_ipc_buf_e2a_sync() even if host didn't
+ * modified the content of the buffer.
+ */
+void rwnx_ipc_buf_e2a_sync_back(struct rwnx_hw *rwnx_hw, struct rwnx_ipc_buf *buf,
+                            size_t len)
+{
+    if (!len)
+        len = buf->size;
+
+    dma_sync_single_for_device(rwnx_hw->dev, buf->dma_addr, len, DMA_FROM_DEVICE);
+}
+
+/**
+ * rwnx_ipc_rxskb_alloc() - Allocate a skb for RX path
+ *
+ * @rwnx_hw: Main driver data
+ * @buf: rwnx_ipc_buf structure to store skb address
+ * @skb_size: Size of the buffer to allocate
+ *
+ * Allocate a skb for RX path, meaning that the data buffer is written by the firmware
+ * and needs then to be DMA mapped.
+ *
+ * Note that even though the result is stored in a struct rwnx_ipc_buf, in this case the
+ * rwnx_ipc_buf.addr points to skb structure whereas the rwnx_ipc_buf.dma_addr is the
+ * DMA address of the skb data buffer (i.e. skb->data)
+ */
+static int rwnx_ipc_rxskb_alloc(struct rwnx_hw *rwnx_hw,
+                                struct rwnx_ipc_buf *buf, size_t skb_size)
+{
+    struct sk_buff *skb = dev_alloc_skb(skb_size);
+
+    if (unlikely(!skb)) {
+        dev_err(rwnx_hw->dev, "Allocation of RX skb failed\n");
+        buf->addr = NULL;
+        return -ENOMEM;
+    }
+
+    buf->dma_addr = dma_map_single(rwnx_hw->dev, skb->data, skb_size,
+                                   DMA_FROM_DEVICE);
+    if (unlikely(dma_mapping_error(rwnx_hw->dev, buf->dma_addr))) {
+        dev_err(rwnx_hw->dev, "DMA mapping of RX skb failed\n");
+        dev_kfree_skb(skb);
+        buf->addr = NULL;
+        return -EIO;
+    }
+
+    buf->addr = skb;
+    buf->size = skb_size;
+
+    return 0;
+}
+
+/**
+ * rwnx_ipc_rxskb_reset_pattern() - Reset pattern in a RX skb or unsupported
+ * RX vector buffer
+ *
+ * @rwnx_hw: Main driver data
+ * @buf: RX skb to reset
+ * @pattern_offset: Pattern location, in byte from the start of the buffer
+ *
+ * Reset the pattern in a RX/unsupported RX vector skb buffer to inform embedded
+ * that it has been processed by the host.
+ * Pattern in a 32bit value.
+ */
+static void rwnx_ipc_rxskb_reset_pattern(struct rwnx_hw *rwnx_hw,
+                                         struct rwnx_ipc_buf *buf,
+                                         size_t pattern_offset)
+{
+    struct sk_buff *skb = buf->addr;
+    u32 *pattern = (u32 *)(skb->data + pattern_offset);
+
+    *pattern = 0;
+	*(u32 *)(skb->data) = 0; // aic
+    dma_sync_single_for_device(rwnx_hw->dev, buf->dma_addr + pattern_offset,
+                               sizeof(u32), DMA_FROM_DEVICE);
+}
+
+/**
+ * rwnx_ipc_rxskb_dealloc() - Free a skb allocated for the RX path
+ *
+ * @rwnx_hw: Main driver data
+ * @buf: Rx skb to free
+ *
+ * Free a RX skb allocated by @rwnx_ipc_rxskb_alloc
+ */
+static void rwnx_ipc_rxskb_dealloc(struct rwnx_hw *rwnx_hw,
+                                   struct rwnx_ipc_buf *buf)
+{
+    if (!buf->addr)
+        return;
+
+    dma_unmap_single(rwnx_hw->dev, buf->dma_addr, buf->size, DMA_TO_DEVICE);
+    dev_kfree_skb((struct sk_buff *)buf->addr);
+    buf->addr = NULL;
+}
+
+
+/**
+ * rwnx_ipc_unsup_rx_vec_elem_allocs() - Allocate and push an unsupported
+ *                                       RX vector buffer for the FW
+ *
+ * @rwnx_hw: Main driver data
+ * @elem: Pointer to the skb elem that will contain the address of the buffer
+ */
+int rwnx_ipc_unsuprxvec_alloc(struct rwnx_hw *rwnx_hw, struct rwnx_ipc_buf *buf)
+{
+    int err;
+
+    err = rwnx_ipc_rxskb_alloc(rwnx_hw, buf, rwnx_hw->ipc_env->unsuprxvec_sz);
+    if (err)
+        return err;
+
+    rwnx_ipc_rxskb_reset_pattern(rwnx_hw, buf,
+                                 offsetof(struct rx_vector_desc, pattern));
+    ipc_host_unsuprxvec_push(rwnx_hw->ipc_env, buf);
+    return 0;
+}
+
+/**
+ * rwnx_ipc_unsuprxvec_repush() - Reset and repush an already allocated buffer
+ * for unsupported RX vector
+ *
+ * @rwnx_hw: Main driver data
+ * @buf: Buf to repush
+ */
+void rwnx_ipc_unsuprxvec_repush(struct rwnx_hw *rwnx_hw, struct rwnx_ipc_buf *buf)
+{
+    rwnx_ipc_rxskb_reset_pattern(rwnx_hw, buf,
+                                 offsetof(struct rx_vector_desc, pattern));
+    ipc_host_unsuprxvec_push(rwnx_hw->ipc_env, buf);
+}
+
+/**
+* rwnx_ipc_unsuprxvecs_alloc() - Allocate and push all unsupported RX
+* vector buffers for the FW
+*
+* @rwnx_hw: Main driver data
+*/
+static int rwnx_ipc_unsuprxvecs_alloc(struct rwnx_hw *rwnx_hw)
+{
+   struct rwnx_ipc_buf *buf;
+   int i;
+
+   memset(rwnx_hw->unsuprxvecs, 0, sizeof(rwnx_hw->unsuprxvecs));
+
+   for (i = 0, buf = rwnx_hw->unsuprxvecs; i < ARRAY_SIZE(rwnx_hw->unsuprxvecs); i++, buf++)
+   {
+       if (rwnx_ipc_unsuprxvec_alloc(rwnx_hw, buf)) {
+           dev_err(rwnx_hw->dev, "Failed to allocate unsuprxvec buf %d\n", i + 1);
+           return -ENOMEM;
+       }
+   }
+
+   return 0;
+}
+
+/**
+ * rwnx_ipc_unsuprxvecs_dealloc() - Free all unsupported RX vector buffers
+ * allocated for the FW
+ *
+ * @rwnx_hw: Main driver data
+ */
+static void rwnx_ipc_unsuprxvecs_dealloc(struct rwnx_hw *rwnx_hw)
+{
+    struct rwnx_ipc_buf *buf;
+    int i;
+
+    for (i = 0, buf = rwnx_hw->unsuprxvecs; i < ARRAY_SIZE(rwnx_hw->unsuprxvecs); i++, buf++)
+    {
+        rwnx_ipc_rxskb_dealloc(rwnx_hw, buf);
+    }
+}
+
+/**
+ * rwnx_ipc_rxbuf_alloc() - Allocate and push a rx buffer for the FW
+ *
+ * @rwnx_hw: Main driver data
+ * @buf: IPC buffer where to store address of the skb. In fullmac this
+ * parameter is not available so look for the first free IPC buffer
+ */
+int rwnx_ipc_rxbuf_alloc(struct rwnx_hw *rwnx_hw)
+{
+    int err;
+    struct rwnx_ipc_buf *buf;
+    int nb = 0, idx;
+
+    spin_lock_bh(&rwnx_hw->rxbuf_lock);
+
+    idx = rwnx_hw->rxbuf_idx;
+    while (rwnx_hw->rxbufs[idx].addr && (nb < RWNX_RXBUFF_MAX)) {
+        printk("w %d %p\n", idx, rwnx_hw->rxbufs[idx].addr);
+        idx = ( idx + 1 ) % RWNX_RXBUFF_MAX;
+        nb++;
+    }
+    if (nb == RWNX_RXBUFF_MAX) {
+        dev_err(rwnx_hw->dev, "No more free space for rxbuff");
+        printk("No more free space for rxbuff %d %d %d\n", rwnx_hw->rxbuf_idx, atomic_read(&rwnx_hw->rxbuf_cnt), rwnx_hw->ipc_env->rxbuf_idx);
+        spin_unlock_bh(&rwnx_hw->rxbuf_lock);
+        return -ENOMEM;
+    }
+
+    buf = &rwnx_hw->rxbufs[idx];
+
+    //printk("alloc %d\n", idx);
+    err = rwnx_ipc_rxskb_alloc(rwnx_hw, buf, rwnx_hw->ipc_env->rxbuf_sz);
+    if (err){
+        printk("ipc_rxskb_alloc fail %d %d %d %d\n", rwnx_hw->rxbuf_idx, atomic_read(&rwnx_hw->rxbuf_cnt), rwnx_hw->ipc_env->rxbuf_idx, err);
+        spin_unlock_bh(&rwnx_hw->rxbuf_lock);
+        return err;
+    }
+    /* Save idx so that on next push the free slot will be found quicker */
+    rwnx_hw->rxbuf_idx = ( idx + 1 ) % RWNX_RXBUFF_MAX;
+    atomic_inc(&rwnx_hw->rxbuf_cnt);
+
+    rwnx_ipc_rxskb_reset_pattern(rwnx_hw, buf, offsetof(struct hw_rxhdr, pattern));
+    RWNX_RXBUFF_HOSTID_SET(buf, RWNX_RXBUFF_IDX_TO_HOSTID(idx));
+    ipc_host_rxbuf_push(rwnx_hw->ipc_env, buf);
+    spin_unlock_bh(&rwnx_hw->rxbuf_lock);
+
+    return 0;
+}
+
+/**
+ * rwnx_ipc_rxbuf_dealloc() - Free a RX buffer for the FW
+ *
+ * @rwnx_hw: Main driver data
+ * @buf: IPC buffer associated to the RX buffer to free
+ */
+void rwnx_ipc_rxbuf_dealloc(struct rwnx_hw *rwnx_hw, struct rwnx_ipc_buf *buf)
+{
+    rwnx_ipc_rxskb_dealloc(rwnx_hw, buf);
+}
+
+/**
+ * rwnx_ipc_rxbuf_repush() - Reset and repush an already allocated RX buffer
+ *
+ * @rwnx_hw: Main driver data
+ * @buf: Buf to repush
+ *
+ * In case a skb is not forwarded to upper layer it can be re-used.
+ */
+void rwnx_ipc_rxbuf_repush(struct rwnx_hw *rwnx_hw, struct rwnx_ipc_buf *buf)
+{
+    rwnx_ipc_rxskb_reset_pattern(rwnx_hw, buf, offsetof(struct hw_rxhdr, pattern));
+    ipc_host_rxbuf_push(rwnx_hw->ipc_env, buf);
+}
+
+/**
+ * rwnx_ipc_rxbufs_alloc() - Allocate and push all RX buffer for the FW
+ *
+ * @rwnx_hw: Main driver data
+ */
+static int rwnx_ipc_rxbufs_alloc(struct rwnx_hw *rwnx_hw)
+{
+    int i, nb = rwnx_hw->ipc_env->rxbuf_nb;
+
+    memset(rwnx_hw->rxbufs, 0, sizeof(rwnx_hw->rxbufs));
+
+    for (i = 0; i < nb; i++) {
+        if (rwnx_ipc_rxbuf_alloc(rwnx_hw)) {
+            dev_err(rwnx_hw->dev, "Failed to allocate rx buf %d/%d\n",
+                    i + 1, nb);
+            return -ENOMEM;
+        }
+    }
+
+    return 0;
+}
+
+/**
+ * rwnx_ipc_rxbufs_dealloc() - Free all RX buffer allocated for the FW
+ *
+ * @rwnx_hw: Main driver data
+ */
+static void rwnx_ipc_rxbufs_dealloc(struct rwnx_hw *rwnx_hw)
+{
+    struct rwnx_ipc_buf *buf;
+    int i;
+
+    for (i = 0, buf = rwnx_hw->rxbufs; i < ARRAY_SIZE(rwnx_hw->rxbufs); i++, buf++) {
+        rwnx_ipc_rxskb_dealloc(rwnx_hw, buf);
+    }
+}
+
+/**
+ * rwnx_ipc_rxdesc_repush() - Repush a RX descriptor to FW
+ *
+ * @rwnx_hw: Main driver data
+ * @buf: RX descriptor to repush
+ *
+ * Once RX buffer has been received, the RX descriptor used by FW to upload this
+ * buffer can be re-used for another RX buffer.
+ */
+void rwnx_ipc_rxdesc_repush(struct rwnx_hw *rwnx_hw,
+                            struct rwnx_ipc_buf *buf)
+{
+    struct rxdesc_tag *rxdesc = buf->addr;
+    rxdesc->status = 0;
+    dma_sync_single_for_device(rwnx_hw->dev, buf->dma_addr,
+                               sizeof(struct rxdesc_tag), DMA_BIDIRECTIONAL);
+    ipc_host_rxdesc_push(rwnx_hw->ipc_env, buf);
+}
+
+/**
+ * rwnx_ipc_rxbuf_from_hostid() - Return IPC buffer of a RX buffer from a hostid
+ *
+ * @rwnx_hw: Main driver data
+ * @hostid: Hostid of the RX buffer
+ * @return: Pointer to the RX buffer with the provided hostid and NULL if the
+ * hostid is invalid or no buffer is associated.
+ */
+struct rwnx_ipc_buf *rwnx_ipc_rxbuf_from_hostid(struct rwnx_hw *rwnx_hw, u32 hostid)
+{
+    int rxbuf_idx = RWNX_RXBUFF_HOSTID_TO_IDX(hostid);
+
+    if (RWNX_RXBUFF_VALID_IDX(rxbuf_idx)) {
+        struct rwnx_ipc_buf *buf = &rwnx_hw->rxbufs[rxbuf_idx];
+        if (buf->addr && (RWNX_RXBUFF_HOSTID_GET(buf) == hostid))
+            return buf;
+
+        dev_err(rwnx_hw->dev, "Invalid Rx buff: hostid=%d addr=%p hostid_in_buff=%d\n",
+                hostid, buf->addr, (buf->addr) ? RWNX_RXBUFF_HOSTID_GET(buf): -1);
+
+        if (buf->addr)
+            rwnx_ipc_rxbuf_dealloc(rwnx_hw, buf);
+    }
+
+    dev_err(rwnx_hw->dev, "RX Buff invalid hostid [%d]\n", hostid);
+    return NULL;
+}
+
+/**
+ * rwnx_elems_deallocs() - Deallocate IPC storage elements.
+ * @rwnx_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.
+ */
+static void rwnx_elems_deallocs(struct rwnx_hw *rwnx_hw)
+{
+	printk("rwnx_elems_deallocs 1\n");
+    rwnx_ipc_rxbufs_dealloc(rwnx_hw);
+    rwnx_ipc_unsuprxvecs_dealloc(rwnx_hw);
+#ifdef CONFIG_RWNX_FULLMAC
+    rwnx_ipc_buf_pool_dealloc(&rwnx_hw->rxdesc_pool);
+#endif
+    rwnx_ipc_buf_pool_dealloc(&rwnx_hw->msgbuf_pool);
+    rwnx_ipc_buf_pool_dealloc(&rwnx_hw->dbgbuf_pool);
+    rwnx_ipc_buf_pool_dealloc(&rwnx_hw->radar_pool);
+    rwnx_ipc_buf_pool_dealloc(&rwnx_hw->txcfm_pool);
+    rwnx_ipc_buf_dealloc(rwnx_hw, &rwnx_hw->tx_pattern);
+    rwnx_ipc_buf_dealloc(rwnx_hw, &rwnx_hw->dbgdump.buf);
+
+	printk("rwnx_elems_deallocs end\n");
+}
+
+/**
+ * rwnx_elems_allocs() - Allocate IPC storage elements.
+ * @rwnx_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 rwnx_elems_allocs(struct rwnx_hw *rwnx_hw)
+{
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    if (dma_set_coherent_mask(rwnx_hw->dev, DMA_BIT_MASK(32)))
+        goto err_alloc;
+
+    if (rwnx_ipc_buf_pool_alloc(rwnx_hw, &rwnx_hw->msgbuf_pool,
+                                IPC_MSGE2A_BUF_CNT,
+                                sizeof(struct ipc_e2a_msg),
+                               	 "rwnx_ipc_msgbuf_pool",
+                                ipc_host_msgbuf_push))
+        goto err_alloc;
+
+    if (rwnx_ipc_buf_pool_alloc(rwnx_hw, &rwnx_hw->dbgbuf_pool,
+                                IPC_DBGBUF_CNT,
+                                sizeof(struct ipc_dbg_msg),
+                                	"rwnx_ipc_dbgbuf_pool",
+                                ipc_host_dbgbuf_push))
+        goto err_alloc;
+
+    if (rwnx_ipc_buf_pool_alloc(rwnx_hw, &rwnx_hw->radar_pool,
+                                IPC_RADARBUF_CNT,
+                                sizeof(struct radar_pulse_array_desc),
+                               	 "rwnx_ipc_radar_pool",
+                                ipc_host_radar_push))
+        goto err_alloc;
+
+    if (rwnx_ipc_unsuprxvecs_alloc(rwnx_hw))
+        goto err_alloc;
+
+
+    if (rwnx_ipc_buf_a2e_alloc(rwnx_hw, &rwnx_hw->tx_pattern, sizeof(u32),
+                               &rwnx_tx_pattern))
+        goto err_alloc;
+
+    ipc_host_pattern_push(rwnx_hw->ipc_env, &rwnx_hw->tx_pattern);
+
+    #if 0
+    printk("%s: 8\n", __func__);
+
+    if (rwnx_ipc_buf_e2a_alloc(rwnx_hw, &rwnx_hw->dbgdump.buf,
+                               sizeof(struct dbg_debug_dump_tag)))
+        goto err_alloc;
+
+
+    printk("%s: 9\n", __func__);
+    ipc_host_dbginfo_push(rwnx_hw->ipc_env, &rwnx_hw->dbgdump.buf);
+
+    /*
+     * 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 rwnx_handle_dynparams() execution)
+     */
+    printk("%s: 10\n", __func__);
+
+    #endif
+#ifdef CONFIG_RWNX_FULLMAC
+    if (rwnx_ipc_buf_pool_alloc(rwnx_hw, &rwnx_hw->rxdesc_pool,
+                                 rwnx_hw->ipc_env->rxdesc_nb,
+                                 sizeof(struct rxdesc_tag),
+                                	 "rwnx_ipc_rxdesc_pool",
+                                 ipc_host_rxdesc_push))
+        goto err_alloc;
+
+#endif /* CONFIG_RWNX_FULLMAC */
+
+    return 0;
+
+err_alloc:
+    dev_err(rwnx_hw->dev, "Error while allocating IPC buffers\n");
+    rwnx_elems_deallocs(rwnx_hw);
+    return -ENOMEM;
+}
+
+/**
+ * rwnx_ipc_msg_push() - Push a msg to IPC queue
+ *
+ * @rwnx_hw: Main driver data
+ * @msg_buf: Pointer to message
+ * @len: Size, in bytes, of message
+ */
+void rwnx_ipc_msg_push(struct rwnx_hw *rwnx_hw, void *msg_buf, uint16_t len)
+{
+    ipc_host_msg_push(rwnx_hw->ipc_env, msg_buf, len);
+}
+
+/**
+ * rwnx_ipc_txdesc_push() - Push a txdesc to FW
+ *
+ * @rwnx_hw: Main driver data
+ * @sw_txhdr: Pointer to the SW TX header associated to the descriptor to push
+ * @skb: TX Buffer associated. Pointer saved in ipc env to retrieve it upon confirmation.
+ * @hw_queue: Hw queue to push txdesc to
+ */
+#if 0
+void rwnx_ipc_txdesc_push(struct rwnx_hw *rwnx_hw, struct rwnx_sw_txhdr *sw_txhdr,
+                          struct sk_buff *skb, int hw_queue)
+{
+    struct txdesc_host *txdesc_host = &sw_txhdr->desc;
+    struct rwnx_ipc_buf *ipc_desc = &sw_txhdr->ipc_desc;
+
+    txdesc_host->ctrl.hwq = hw_queue;
+    txdesc_host->api.host.hostid = ipc_host_tx_host_ptr_to_id(rwnx_hw->ipc_env, skb);
+    txdesc_host->ready = 0xFFFFFFFF;
+    if (!txdesc_host->api.host.hostid) {
+        dev_err(rwnx_hw->dev, "No more tx_hostid available \n");
+        return;
+    }
+
+    if (rwnx_ipc_buf_a2e_init(rwnx_hw, ipc_desc, txdesc_host, sizeof(*txdesc_host)))
+        return ;
+
+    ipc_host_txdesc_push(rwnx_hw->ipc_env, ipc_desc);
+}
+#endif
+
+/**
+ * rwnx_ipc_get_skb_from_cfm() - Retrieve the TX buffer associated to a confirmation buffer
+ *
+ * @rwnx_hw: Main driver data
+ * @buf: IPC buffer for the confirmation buffer
+ * @return: Pointer to TX buffer associated to this confirmation and NULL if confirmation
+ * has not yet been updated by firmware
+ *
+ * To ensure that a confirmation has been processed by firmware check if the hostid field
+ * has been updated. If this is the case retrieve TX buffer from it and reset it, otherwise
+ * simply return NULL.
+ */
+struct sk_buff *rwnx_ipc_get_skb_from_cfm(struct rwnx_hw *rwnx_hw,
+                                          struct rwnx_ipc_buf *buf)
+{
+    struct sk_buff *skb = NULL;
+    struct tx_cfm_tag *cfm = buf->addr;
+
+    /* get ownership of confirmation */
+    rwnx_ipc_buf_e2a_sync(rwnx_hw, buf, 0);
+
+    /* Check host id in the confirmation. */
+    /* If 0 it means that this confirmation has not yet been updated by firmware */
+    if (cfm->hostid) {
+        skb = ipc_host_tx_host_id_to_ptr(rwnx_hw->ipc_env, cfm->hostid);
+        if (unlikely(!skb)) {
+            dev_err(rwnx_hw->dev, "Cannot retrieve skb from cfm=%p/0x%llx, hostid %d in confirmation\n",
+                    buf->addr, buf->dma_addr, cfm->hostid);
+        } else {
+            /* Unmap TX descriptor */
+            struct rwnx_ipc_buf *ipc_desc = &((struct rwnx_txhdr *)skb->data)->sw_hdr->ipc_desc;
+            rwnx_ipc_buf_a2e_release(rwnx_hw, ipc_desc);
+        }
+
+        cfm->hostid = 0;
+    }
+
+    /* always re-give ownership to firmware. */
+    rwnx_ipc_buf_e2a_sync_back(rwnx_hw, buf, 0);
+
+    return skb;
+}
+
+/**
+ * rwnx_ipc_sta_buffer_init - Initialize counter of buffered data for a given sta
+ *
+ * @rwnx_hw: Main driver data
+ * @sta_idx: Index of the station to initialize
+ */
+void rwnx_ipc_sta_buffer_init(struct rwnx_hw *rwnx_hw, int sta_idx)
+{
+    int i;
+    volatile u32_l *buffered;
+
+    if (sta_idx >= NX_REMOTE_STA_MAX)
+        return;
+
+    buffered = rwnx_hw->ipc_env->shared->buffered[sta_idx];
+
+    for (i = 0; i < TID_MAX; i++) {
+        *buffered++ = 0;
+    }
+}
+
+/**
+ * rwnx_ipc_sta_buffer - Update counter of buffered data for a given sta
+ *
+ * @rwnx_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 rwnx_ipc_sta_buffer(struct rwnx_hw *rwnx_hw, struct rwnx_sta *sta, int tid, int size)
+{
+    u32_l *buffered;
+
+    if (!sta)
+        return;
+
+    if ((sta->sta_idx >= NX_REMOTE_STA_MAX) || (tid >= TID_MAX))
+        return;
+
+    buffered = &rwnx_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;
+    }
+}
+
+/**
+ * rwnx_msgind() - IRQ handler callback for %IPC_IRQ_E2A_MSG
+ *
+ * @pthis: Pointer to main driver data
+ * @arg: Pointer to IPC buffer from msgbuf_pool
+ */
+static u8 rwnx_msgind(void *pthis, void *arg)
+{
+    struct rwnx_hw *rwnx_hw = pthis;
+    struct rwnx_ipc_buf *buf = arg;
+    struct ipc_e2a_msg *msg = buf->addr;
+    u8 ret = 0;
+
+    REG_SW_SET_PROFILING(rwnx_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;
+    }
+    /* Relay further actions to the msg parser */
+    rwnx_rx_handle_msg(rwnx_hw, msg);
+
+    /* Reset the msg buffer and re-use it */
+    msg->pattern = 0;
+    wmb();
+
+    /* Push back the buffer to the LMAC */
+    ipc_host_msgbuf_push(rwnx_hw->ipc_env, buf);
+
+msg_no_push:
+    REG_SW_CLEAR_PROFILING(rwnx_hw, SW_PROF_MSGIND);
+    return ret;
+}
+
+/**
+ * rwnx_msgackind() - IRQ handler callback for %IPC_IRQ_E2A_MSG_ACK
+ *
+ * @pthis: Pointer to main driver data
+ * @hostid: Pointer to command acknowledged
+ */
+static u8 rwnx_msgackind(void *pthis, void *hostid)
+{
+    struct rwnx_hw *rwnx_hw = (struct rwnx_hw *)pthis;
+    rwnx_hw->cmd_mgr->llind(rwnx_hw->cmd_mgr, (struct rwnx_cmd *)hostid);
+    return -1;
+}
+
+/**
+ * rwnx_radarind() - IRQ handler callback for %IPC_IRQ_E2A_RADAR
+ *
+ * @pthis: Pointer to main driver data
+ * @arg: Pointer to IPC buffer from radar_pool
+ */
+static u8 rwnx_radarind(void *pthis, void *arg)
+{
+#ifdef CONFIG_RWNX_RADAR
+    struct rwnx_hw *rwnx_hw = pthis;
+    struct rwnx_ipc_buf *buf = arg;
+    struct radar_pulse_array_desc *pulses = buf->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 (rwnx_radar_detection_is_enable(&rwnx_hw->radar, pulses->idx)) {
+        /* Save the received pulses only if radar detection is enabled */
+        for (i = 0; i < pulses->cnt; i++) {
+            struct rwnx_radar_pulses *p = &rwnx_hw->radar.pulses[pulses->idx];
+
+            p->buffer[p->index] = pulses->pulse[i];
+            p->index = (p->index + 1) % RWNX_RADAR_PULSE_MAX;
+            if (p->count < RWNX_RADAR_PULSE_MAX)
+                p->count++;
+        }
+
+        /* Defer pulse processing in separate work */
+        if (! work_pending(&rwnx_hw->radar.detection_work))
+            schedule_work(&rwnx_hw->radar.detection_work);
+    }
+
+    /* Reset the radar bufent and re-use it */
+    pulses->cnt = 0;
+    wmb();
+
+    /* Push back the buffer to the LMAC */
+    ipc_host_radar_push(rwnx_hw->ipc_env, buf);
+
+radar_no_push:
+    return ret;
+#else
+    return -1;
+#endif
+}
+
+/**
+ * rwnx_prim_tbtt_ind() - IRQ handler callback for %IPC_IRQ_E2A_TBTT_PRIM
+ *
+ * @pthis: Pointer to main driver data
+ */
+#if 0
+static void rwnx_prim_tbtt_ind(void *pthis)
+{
+#if 0
+    struct rwnx_hw *rwnx_hw = (struct rwnx_hw *)pthis;
+    rwnx_tx_bcns(rwnx_hw);
+#endif
+}
+#endif
+/**
+ * rwnx_sec_tbtt_ind() - IRQ handler callback for %IPC_IRQ_E2A_TBTT_SEC
+ *
+ * @pthis: Pointer to main driver data
+ */
+#if 0
+static void rwnx_sec_tbtt_ind(void *pthis)
+{
+}
+#endif
+/**
+ * rwnx_dbgind() - IRQ handler callback for %IPC_IRQ_E2A_DBG
+ *
+ * @pthis: Pointer to main driver data
+ * @hostid: Pointer to IPC buffer from dbgbuf_pool
+ */
+static u8 rwnx_dbgind(void *pthis, void *arg)
+{
+    struct rwnx_hw *rwnx_hw = (struct rwnx_hw *)pthis;
+    struct rwnx_ipc_buf *buf = arg;
+    struct ipc_dbg_msg *dbg_msg = buf->addr;
+    u8 ret = 0;
+
+    REG_SW_SET_PROFILING(rwnx_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 buffer and re-use it */
+    dbg_msg->pattern = 0;
+    wmb();
+
+    /* Push back the buffer to the LMAC */
+    ipc_host_dbgbuf_push(rwnx_hw->ipc_env, buf);
+
+dbg_no_push:
+    REG_SW_CLEAR_PROFILING(rwnx_hw, SW_PROF_DBGIND);
+
+    return ret;
+}
+
+/**
+ * rwnx_ipc_rxbuf_init() - Allocate and initialize RX buffers.
+ *
+ * @rwnx_hw: Main driver data
+ * @rxbuf_sz: Size of the buffer to be allocated
+ *
+ * This function updates the RX buffer size according to the parameter and allocates the
+ * RX buffers
+ */
+int rwnx_ipc_rxbuf_init(struct rwnx_hw *rwnx_hw, uint32_t rxbuf_sz)
+{
+    rwnx_hw->ipc_env->rxbuf_sz = rxbuf_sz;
+    return rwnx_ipc_rxbufs_alloc(rwnx_hw);
+}
+
+/**
+ * rwnx_ipc_init() - Initialize IPC interface.
+ *
+ * @rwnx_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 rwnx_ipc_init(struct rwnx_hw *rwnx_hw, u8 *shared_ram)
+{
+    struct ipc_host_cb_tag cb;
+    int res;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    /* initialize the API interface */
+    cb.recv_data_ind   = rwnx_rxdataind;
+    cb.recv_radar_ind  = rwnx_radarind;
+    cb.recv_msg_ind    = rwnx_msgind;
+    cb.recv_msgack_ind = rwnx_msgackind;
+    cb.recv_dbg_ind    = rwnx_dbgind;
+    cb.send_data_cfm   = rwnx_txdatacfm;
+    cb.recv_unsup_rx_vec_ind = rwnx_unsup_rx_vec_ind;
+
+    /* set the IPC environment */
+    rwnx_hw->ipc_env = (struct ipc_host_env_tag *)
+                       kzalloc(sizeof(struct ipc_host_env_tag), GFP_KERNEL);
+
+    if (!rwnx_hw->ipc_env)
+        return -ENOMEM;
+
+    /* call the initialization of the IPC */
+    ipc_host_init(rwnx_hw->ipc_env, &cb,
+                  (struct ipc_shared_env_tag *)shared_ram, rwnx_hw);
+
+    rwnx_cmd_mgr_init(rwnx_hw->cmd_mgr);
+
+    res = rwnx_elems_allocs(rwnx_hw);
+    if (res) {
+        kfree(rwnx_hw->ipc_env);
+        rwnx_hw->ipc_env = NULL;
+    }
+
+    return res;
+}
+
+/**
+ * rwnx_ipc_deinit() - Release IPC interface
+ *
+ * @rwnx_hw: Main driver data
+ */
+void rwnx_ipc_deinit(struct rwnx_hw *rwnx_hw)
+{
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    rwnx_ipc_tx_drain(rwnx_hw);
+    rwnx_cmd_mgr_deinit(rwnx_hw->cmd_mgr);
+    rwnx_elems_deallocs(rwnx_hw);
+    if (rwnx_hw->ipc_env) {
+        kfree(rwnx_hw->ipc_env);
+        rwnx_hw->ipc_env = NULL;
+    }
+}
+
+/**
+ * rwnx_ipc_start() - Start IPC interface
+ *
+ * @rwnx_hw: Main driver data
+ */
+void rwnx_ipc_start(struct rwnx_hw *rwnx_hw)
+{
+    ipc_host_enable_irq(rwnx_hw->ipc_env, IPC_IRQ_E2A_ALL);
+}
+
+/**
+ * rwnx_ipc_stop() - Stop IPC interface
+ *
+ * @rwnx_hw: Main driver data
+ */
+void rwnx_ipc_stop(struct rwnx_hw *rwnx_hw)
+{
+    ipc_host_disable_irq(rwnx_hw->ipc_env, IPC_IRQ_E2A_ALL);
+}
+
+/**
+ * rwnx_ipc_tx_drain() - Flush IPC TX buffers
+ *
+ * @rwnx_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 rwnx_send_reset.
+ */
+void rwnx_ipc_tx_drain(struct rwnx_hw *rwnx_hw)
+{
+    struct sk_buff *skb;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    if (!rwnx_hw->ipc_env) {
+        printk(KERN_CRIT "%s: bypassing (restart must have failed)\n", __func__);
+        return;
+    }
+
+    while ((skb = ipc_host_tx_flush(rwnx_hw->ipc_env))) {
+        struct rwnx_sw_txhdr *sw_txhdr = ((struct rwnx_txhdr *)skb->data)->sw_hdr;
+
+#ifdef CONFIG_RWNX_AMSDUS_TX
+        if (sw_txhdr->desc.api.host.packet_cnt > 1) {
+            struct rwnx_amsdu_txhdr *amsdu_txhdr;
+            list_for_each_entry(amsdu_txhdr, &sw_txhdr->amsdu.hdrs, list) {
+                rwnx_ipc_buf_a2e_release(rwnx_hw, &amsdu_txhdr->ipc_data);
+                dev_kfree_skb_any(amsdu_txhdr->skb);
+            }
+        }
+#endif
+        rwnx_ipc_buf_a2e_release(rwnx_hw, &sw_txhdr->ipc_data);
+        kmem_cache_free(rwnx_hw->sw_txhdr_cache, sw_txhdr);
+        skb_pull(skb, RWNX_TX_HEADROOM);
+        dev_kfree_skb_any(skb);
+    }
+}
+
+/**
+ * rwnx_ipc_tx_pending() - Check if TX frames are pending at FW level
+ *
+ * @rwnx_hw: Main driver data
+ */
+bool rwnx_ipc_tx_pending(struct rwnx_hw *rwnx_hw)
+{
+    return ipc_host_tx_frames_pending(rwnx_hw->ipc_env);
+}
+
+/**
+ * rwnx_error_ind() - %DBG_ERROR_IND message callback
+ *
+ * @rwnx_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 rwnx_umh_done() function has to be called.
+ */
+void rwnx_error_ind(struct rwnx_hw *rwnx_hw)
+{
+    struct rwnx_ipc_buf *buf = &rwnx_hw->dbgdump.buf;
+    struct dbg_debug_dump_tag *dump = buf->addr;
+
+    rwnx_ipc_buf_e2a_sync(rwnx_hw, buf, 0);
+    dev_err(rwnx_hw->dev, "(type %d): dump received\n",
+            dump->dbg_info.error_type);
+#ifdef CONFIG_RWNX_DEBUGFS
+    rwnx_hw->debugfs.trace_prst = true;
+#endif
+    //rwnx_trigger_um_helper(&rwnx_hw->debugfs);
+}
+
+/**
+ * rwnx_umh_done() - Indicate User Mode helper finished
+ *
+ * @rwnx_hw: Main driver data
+ *
+ */
+ 
+extern void aicwf_pcie_host_init(struct ipc_host_env_tag *env, void *cb,  struct ipc_shared_env_tag *shared_env_ptr, void *pthis);
+void rwnx_umh_done(struct rwnx_hw *rwnx_hw)
+{
+    if (!test_bit(RWNX_DEV_STARTED, &rwnx_hw->flags))
+        return;
+
+    /* this assumes error_ind won't trigger before ipc_host_dbginfo_push
+       is called and so does not irq protect (TODO) against error_ind */
+#ifdef CONFIG_RWNX_DEBUGFS
+    rwnx_hw->debugfs.trace_prst = false;
+#endif
+    ipc_host_dbginfo_push(rwnx_hw->ipc_env, &rwnx_hw->dbgdump.buf);
+}
+
+int rwnx_init_aic(struct rwnx_hw *rwnx_hw)
+{
+    int res = 0;
+    struct ipc_shared_env_tag *shared_env = NULL;
+
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+#ifdef AICWF_SDIO_SUPPORT
+    aicwf_sdio_host_init(&(rwnx_hw->sdio_env), NULL, NULL, rwnx_hw);
+#endif
+
+#ifdef AICWF_USB_SUPPORT
+    aicwf_usb_host_init(&(rwnx_hw->usb_env), NULL, NULL, rwnx_hw);
+#endif
+
+#ifdef AICWF_PCIE_SUPPORT
+    rwnx_hw->ipc_env = (struct ipc_host_env_tag *) kzalloc(sizeof(struct ipc_host_env_tag), GFP_KERNEL);
+
+    if (!rwnx_hw->ipc_env){
+        return -ENOMEM;
+    }
+
+    if(rwnx_hw->pcidev->chip_id == PRODUCT_ID_AIC8800D80) {
+        if (rwnx_hw->pcidev->bar_count == 1) {
+            aicwf_pcie_host_init(rwnx_hw->ipc_env, NULL, (struct ipc_shared_env_tag *)(rwnx_hw->pcidev->emb_shrm), rwnx_hw);
+            shared_env = (struct ipc_shared_env_tag *)(rwnx_hw->pcidev->emb_shrm);
+        } else {
+            aicwf_pcie_host_init(rwnx_hw->ipc_env, NULL, (struct ipc_shared_env_tag *)(rwnx_hw->pcidev->pci_bar0_vaddr + 0x1DC000), rwnx_hw);
+            shared_env = (struct ipc_shared_env_tag *)(rwnx_hw->pcidev->pci_bar0_vaddr + 0x1DC000);
+        }
+    } else {
+        aicwf_pcie_host_init(rwnx_hw->ipc_env, NULL, (struct ipc_shared_env_tag *)(rwnx_hw->pcidev->emb_shrm), rwnx_hw);
+        shared_env = (struct ipc_shared_env_tag *)(rwnx_hw->pcidev->emb_shrm);
+    }
+
+    res = rwnx_elems_allocs(rwnx_hw);
+    if (res) {
+        kfree(rwnx_hw->ipc_env);
+        rwnx_hw->ipc_env = NULL;
+    }
+    printk("sizeof struct ipc_shared_env_tag is %ld byte,  offset=%ld, %ld, %ld , txdesc %lx\n", sizeof(struct ipc_shared_env_tag),
+                                                (u8 *)&shared_env->host_rxdesc - (u8 *)shared_env,
+                                                (u8 *)&shared_env->host_rxbuf - (u8 *)shared_env,
+                                                (u8 *)&shared_env->buffered - (u8 *)shared_env,
+                                                (u8 *)&rwnx_hw->ipc_env->shared->txdesc - (u8 *)shared_env);
+    printk("txdesc size %ld\n", sizeof(rwnx_hw->ipc_env->shared->txdesc));
+
+#endif
+    rwnx_cmd_mgr_init(rwnx_hw->cmd_mgr);
+
+    return res;
+}
+
+void rwnx_aic_deinit(struct rwnx_hw *rwnx_hw)
+{
+    RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+    //rwnx_ipc_tx_drain(rwnx_hw);
+    rwnx_elems_deallocs(rwnx_hw);
+    if (rwnx_hw->ipc_env) {
+        kfree(rwnx_hw->ipc_env);
+        rwnx_hw->ipc_env = NULL;
+    }
+}
+
+