Linux wireless drivers development
 help / color / mirror / Atom feed
From: "Yanli Yang" <yanli.yang@bedmex.com>
To: <linux-wireless@vger.kernel.org>
Cc: "Zhirun Liu" <zhirunliu@aicsemi.com>,
	 "Johannes Berg" <johannes@sipsolutions.net>,
	 "Dijia Xu" <dijiaxu@aicsemi.com>,
	"Chunqiu Liu" <chunqiuliu@aicsemi.com>,
	 "Liheng Wei" <liheng.wei@bedmex.com>,
	 "Yanli Yang" <yanli.yang@bedmex.com>
Subject: [RFC PATCH wireless-next v4 2/4] wifi: aic: add FullMAC firmware and platform interface
Date: Sat, 29 Aug 2026 16:21:04 +0800	[thread overview]
Message-ID: <9d002f2a238c38ef40a3434ae47b4d91fa191840.1787991457.git.yanli.yang@bedmex.com> (raw)
In-Reply-To: <cover.1787991457.git.yanli.yang@bedmex.com>

From: Zhirun Liu <zhirunliu@aicsemi.com>

Add the LMAC message definitions, IPC host support, firmware command/event
handling and platform bring-up required by the AIC8800D80 U02 FullMAC
firmware.

The interface is scoped to the current in-tree kernel APIs and the SDIO
station baseline selected by the first patch.

Signed-off-by: Zhirun Liu <zhirunliu@aicsemi.com>
Signed-off-by: Yanli Yang <yanli.yang@bedmex.com>
---
 .../aic/aic8800_fdrv/aic_bsp_export.h         |   62 +
 .../net/wireless/aic/aic8800_fdrv/aic_bus.h   |  191 +
 .../wireless/aic/aic8800_fdrv/aicwf_debug.h   |   37 +
 .../net/wireless/aic/aic8800_fdrv/hal_desc.h  |  359 ++
 .../wireless/aic/aic8800_fdrv/ipc_compat.h    |   25 +
 .../net/wireless/aic/aic8800_fdrv/ipc_host.c  |   42 +
 .../net/wireless/aic/aic8800_fdrv/ipc_host.h  |  169 +
 .../wireless/aic/aic8800_fdrv/ipc_shared.h    |  770 ++++
 .../net/wireless/aic/aic8800_fdrv/lmac_mac.h  |  437 ++
 .../net/wireless/aic/aic8800_fdrv/lmac_msg.h  | 3101 ++++++++++++++
 .../wireless/aic/aic8800_fdrv/lmac_types.h    |   22 +
 .../wireless/aic/aic8800_fdrv/reg_access.h    |  146 +
 .../net/wireless/aic/aic8800_fdrv/rwnx_cmds.c |  471 +++
 .../net/wireless/aic/aic8800_fdrv/rwnx_cmds.h |   99 +
 .../wireless/aic/aic8800_fdrv/rwnx_compat.h   |   80 +
 .../net/wireless/aic/aic8800_fdrv/rwnx_defs.h |  704 ++++
 .../wireless/aic/aic8800_fdrv/rwnx_events.h   |  938 +++++
 .../net/wireless/aic/aic8800_fdrv/rwnx_irqs.c |   49 +
 .../net/wireless/aic/aic8800_fdrv/rwnx_irqs.h |   12 +
 .../wireless/aic/aic8800_fdrv/rwnx_msg_rx.c   | 1614 ++++++++
 .../wireless/aic/aic8800_fdrv/rwnx_msg_rx.h   |    8 +
 .../wireless/aic/aic8800_fdrv/rwnx_msg_tx.c   | 3619 +++++++++++++++++
 .../wireless/aic/aic8800_fdrv/rwnx_msg_tx.h   |  266 ++
 .../wireless/aic/aic8800_fdrv/rwnx_platform.c | 2868 +++++++++++++
 .../wireless/aic/aic8800_fdrv/rwnx_platform.h |  183 +
 .../net/wireless/aic/aic8800_fdrv/rwnx_prof.h |  142 +
 .../net/wireless/aic/aic8800_fdrv/rwnx_strs.c |  269 ++
 .../net/wireless/aic/aic8800_fdrv/rwnx_strs.h |   31 +
 .../wireless/aic/aic8800_fdrv/rwnx_utils.c    |   35 +
 .../wireless/aic/aic8800_fdrv/rwnx_utils.h    |  131 +
 .../net/wireless/aic/aic8800_fdrv/rwnx_v7.c   |  181 +
 .../net/wireless/aic/aic8800_fdrv/rwnx_v7.h   |   21 +
 32 files changed, 17082 insertions(+)
 create mode 100644 drivers/net/wireless/aic/aic8800_fdrv/aic_bsp_export.h
 create mode 100644 drivers/net/wireless/aic/aic8800_fdrv/aic_bus.h
 create mode 100644 drivers/net/wireless/aic/aic8800_fdrv/aicwf_debug.h
 create mode 100644 drivers/net/wireless/aic/aic8800_fdrv/hal_desc.h
 create mode 100644 drivers/net/wireless/aic/aic8800_fdrv/ipc_compat.h
 create mode 100644 drivers/net/wireless/aic/aic8800_fdrv/ipc_host.c
 create mode 100644 drivers/net/wireless/aic/aic8800_fdrv/ipc_host.h
 create mode 100644 drivers/net/wireless/aic/aic8800_fdrv/ipc_shared.h
 create mode 100644 drivers/net/wireless/aic/aic8800_fdrv/lmac_mac.h
 create mode 100644 drivers/net/wireless/aic/aic8800_fdrv/lmac_msg.h
 create mode 100644 drivers/net/wireless/aic/aic8800_fdrv/lmac_types.h
 create mode 100644 drivers/net/wireless/aic/aic8800_fdrv/reg_access.h
 create mode 100644 drivers/net/wireless/aic/aic8800_fdrv/rwnx_cmds.c
 create mode 100644 drivers/net/wireless/aic/aic8800_fdrv/rwnx_cmds.h
 create mode 100644 drivers/net/wireless/aic/aic8800_fdrv/rwnx_compat.h
 create mode 100644 drivers/net/wireless/aic/aic8800_fdrv/rwnx_defs.h
 create mode 100644 drivers/net/wireless/aic/aic8800_fdrv/rwnx_events.h
 create mode 100644 drivers/net/wireless/aic/aic8800_fdrv/rwnx_irqs.c
 create mode 100644 drivers/net/wireless/aic/aic8800_fdrv/rwnx_irqs.h
 create mode 100644 drivers/net/wireless/aic/aic8800_fdrv/rwnx_msg_rx.c
 create mode 100644 drivers/net/wireless/aic/aic8800_fdrv/rwnx_msg_rx.h
 create mode 100644 drivers/net/wireless/aic/aic8800_fdrv/rwnx_msg_tx.c
 create mode 100644 drivers/net/wireless/aic/aic8800_fdrv/rwnx_msg_tx.h
 create mode 100644 drivers/net/wireless/aic/aic8800_fdrv/rwnx_platform.c
 create mode 100644 drivers/net/wireless/aic/aic8800_fdrv/rwnx_platform.h
 create mode 100644 drivers/net/wireless/aic/aic8800_fdrv/rwnx_prof.h
 create mode 100644 drivers/net/wireless/aic/aic8800_fdrv/rwnx_strs.c
 create mode 100644 drivers/net/wireless/aic/aic8800_fdrv/rwnx_strs.h
 create mode 100644 drivers/net/wireless/aic/aic8800_fdrv/rwnx_utils.c
 create mode 100644 drivers/net/wireless/aic/aic8800_fdrv/rwnx_utils.h
 create mode 100644 drivers/net/wireless/aic/aic8800_fdrv/rwnx_v7.c
 create mode 100644 drivers/net/wireless/aic/aic8800_fdrv/rwnx_v7.h

diff --git a/drivers/net/wireless/aic/aic8800_fdrv/aic_bsp_export.h b/drivers/net/wireless/aic/aic8800_fdrv/aic_bsp_export.h
new file mode 100644
index 0000000000000..11b414df42ce3
--- /dev/null
+++ b/drivers/net/wireless/aic/aic8800_fdrv/aic_bsp_export.h
@@ -0,0 +1,62 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __AIC_BSP_EXPORT_H
+#define __AIC_BSP_EXPORT_H
+
+#include <linux/types.h>
+
+enum aicbsp_subsys {
+	AIC_BLUETOOTH,
+	AIC_WIFI,
+};
+
+enum aicbsp_pwr_state {
+	AIC_PWR_OFF,
+	AIC_PWR_ON,
+};
+
+struct aicbsp_feature_t {
+	int hwinfo;
+	u32 sdio_clock;
+	u8 sdio_phase;
+	int fwlog_en;
+	u8 irqf;
+	u8 cpmode;
+	int adapt;
+};
+
+enum skb_buff_id {
+	AIC_RESV_MEM_TXDATA,
+};
+
+#ifdef CONFIG_DPD
+struct rf_misc_ram_t {
+	u32 bit_mask[3];
+	u32 reserved;
+	u32 dpd_high[96];
+	u32 dpd_11b[96];
+	u32 dpd_low[96];
+	u32 idac_11b[48];
+	u32 idac_high[48];
+	u32 idac_low[48];
+	u32 loft_res[18];
+	u32 rx_iqim_res[16];
+};
+
+struct rf_misc_ram_lite_t {
+	u32 bit_mask[4];
+	u32 dpd_high[96];
+	u32 loft_res[18];
+};
+
+#define MEMBER_SIZE(type, member) sizeof_field(type, member)
+#define DPD_RESULT_SIZE_8800DC    sizeof(struct rf_misc_ram_lite_t)
+
+extern struct rf_misc_ram_lite_t dpd_res;
+#endif
+
+int aicbsp_set_subsys(int, int);
+int aicbsp_get_feature(struct aicbsp_feature_t *feature);
+struct sk_buff *aicbsp_resv_mem_alloc_skb(unsigned int length, uint32_t id);
+void aicbsp_resv_mem_kfree_skb(struct sk_buff *skb, uint32_t id);
+
+#endif
diff --git a/drivers/net/wireless/aic/aic8800_fdrv/aic_bus.h b/drivers/net/wireless/aic/aic8800_fdrv/aic_bus.h
new file mode 100644
index 0000000000000..2498d3d592b42
--- /dev/null
+++ b/drivers/net/wireless/aic/aic8800_fdrv/aic_bus.h
@@ -0,0 +1,191 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2024 AIC semiconductor.
+ *
+ * @file aic_bus.h
+ * @brief Common bus abstraction layer for AIC8800 series.
+ *
+ * This header defines the bus-level operations that abstract the
+ * underlying transport (SDIO, USB, etc.) from the upper driver layers.
+ * The design follows the pattern used by silabs/wfx and broadcom/b43.
+ *
+ * Key principles:
+ *  - struct aicwf_bus_ops provides a complete set of low-level bus
+ *    primitives (read/write registers, send/recv packets, flow control,
+ *    power management, interrupt handling).
+ *  - Upper-layer code accesses the bus only through the ops table and
+ *    inline wrappers defined here.  It MUST NOT dereference the
+ *    bus-private structure (aic_sdio_dev etc.) directly.
+ *  - When a new transport (e.g. USB) is added, only the bus_ops
+ *    implementation needs to change; upper layers are unaffected.
+ */
+
+#ifndef _AIC_BUS_H_
+#define _AIC_BUS_H_
+
+#include <linux/device.h>
+#include <linux/skbuff.h>
+#include <linux/types.h>
+
+/* Bus operational state */
+enum aicwf_bus_state { BUS_DOWN_ST, BUS_UP_ST };
+
+struct aicwf_bus {
+	union {
+		struct aic_sdio_dev *sdio;
+		struct aic_usb_dev *usb;
+	} bus_priv;
+	struct device *dev;
+	struct aicwf_bus_ops *ops;
+	enum aicwf_bus_state state;
+	u8 *cmd_buf;
+	struct completion bustx_trgg;
+	struct completion busrx_trgg;
+	struct completion busirq_trgg; // new oob feature
+	struct task_struct *bustx_thread;
+	struct task_struct *busrx_thread;
+	struct task_struct *busirq_thread; // new oob feature
+};
+
+/* ================================================================
+ *  Bus-level operation vectors
+ *
+ *  Every transport (SDIO, USB, ...) provides one instance of this
+ *  struct.  The bus-private data pointer is carried inside
+ *  struct aicwf_bus so that ops callbacks can retrieve it via
+ *  bus->bus_priv.
+ * ================================================================
+ */
+
+struct aicwf_bus_ops {
+	/* ----- Lifecycle ----- */
+	int  (*start)(struct device *dev);
+	void (*stop)(struct device *dev);
+
+	/* ----- Data / message transmission ----- */
+	int (*txdata)(struct device *dev, struct sk_buff *skb);
+	int (*txmsg)(struct device *dev, u8 *msg, uint len);
+
+	/* ----- Low-level register access -----
+	 * These replace the direct aicwf_sdio_readb/writeb calls.
+	 */
+	int (*read_reg)(struct device *dev, u32 regaddr, u8 *val);
+	int (*write_reg)(struct device *dev, u32 regaddr, u8 val);
+
+	/* ----- Bulk data transfer -----
+	 * send_pkt  – write a buffer to the chip's TX FIFO
+	 * recv_pkt  – read  a buffer from the chip's RX FIFO
+	 */
+	int (*send_pkt)(struct device *dev, u8 *buf, uint count);
+	int (*recv_pkt)(struct device *dev, u8 *buf, u32 size);
+
+	/* ----- Interrupt handling ----- */
+	int  (*enable_irq)(struct device *dev);
+	void (*disable_irq)(struct device *dev);
+
+	/* ----- Flow control -----
+	 * Returns true if the device is ready to accept more data.
+	 */
+	bool (*flow_ctrl)(struct device *dev);
+
+	/* ----- Power management ----- */
+	int (*sleep_allow)(struct device *dev);
+	int (*wakeup)(struct device *dev);
+
+	/* ----- Chip-specific hardware info -----
+	 * Returns a pointer to chip hardware properties (SDIO register
+	 * layout, feature flags, etc.).  The caller treats this as
+	 * opaque; only the bus layer interprets it.
+	 */
+	const void *(*get_hw_props)(struct device *dev);
+};
+
+/* ================================================================
+ *  Inline wrappers – upper layers call these instead of touching
+ *  bus_priv internals.
+ * ================================================================
+ */
+
+static inline int aicwf_bus_start(struct aicwf_bus *bus)
+{
+	return bus->ops->start(bus->dev);
+}
+
+static inline void aicwf_bus_stop(struct aicwf_bus *bus)
+{
+	bus->ops->stop(bus->dev);
+}
+
+static inline int aicwf_bus_txdata(struct aicwf_bus *bus, struct sk_buff *skb)
+{
+	return bus->ops->txdata(bus->dev, skb);
+}
+
+static inline int aicwf_bus_txmsg(struct aicwf_bus *bus, u8 *msg, uint len)
+{
+	return bus->ops->txmsg(bus->dev, msg, len);
+}
+
+static inline int aicwf_bus_read_reg(struct aicwf_bus *bus,
+				     u32 regaddr, u8 *val)
+{
+	return bus->ops->read_reg(bus->dev, regaddr, val);
+}
+
+static inline int aicwf_bus_write_reg(struct aicwf_bus *bus,
+				      u32 regaddr, u8 val)
+{
+	return bus->ops->write_reg(bus->dev, regaddr, val);
+}
+
+static inline int aicwf_bus_send_pkt(struct aicwf_bus *bus,
+				     u8 *buf, uint count)
+{
+	return bus->ops->send_pkt(bus->dev, buf, count);
+}
+
+static inline int aicwf_bus_recv_pkt(struct aicwf_bus *bus,
+				     u8 *buf, u32 size)
+{
+	return bus->ops->recv_pkt(bus->dev, buf, size);
+}
+
+static inline int aicwf_bus_enable_irq(struct aicwf_bus *bus)
+{
+	return bus->ops->enable_irq(bus->dev);
+}
+
+static inline void aicwf_bus_disable_irq(struct aicwf_bus *bus)
+{
+	bus->ops->disable_irq(bus->dev);
+}
+
+static inline bool aicwf_bus_flow_ctrl(struct aicwf_bus *bus)
+{
+	return bus->ops->flow_ctrl(bus->dev);
+}
+
+static inline int aicwf_bus_sleep_allow(struct aicwf_bus *bus)
+{
+	return bus->ops->sleep_allow(bus->dev);
+}
+
+static inline int aicwf_bus_wakeup(struct aicwf_bus *bus)
+{
+	return bus->ops->wakeup(bus->dev);
+}
+
+static inline const void *aicwf_bus_get_hw_props(struct aicwf_bus *bus)
+{
+	return bus->ops->get_hw_props(bus->dev);
+}
+
+/* ================================================================
+ *  Bus-level init / deinit (shared across transports)
+ * ================================================================
+ */
+
+int aicwf_bus_init(uint bus_hdrlen, struct device *dev);
+void aicwf_bus_deinit(struct device *dev);
+
+#endif /* _AIC_BUS_H_ */
diff --git a/drivers/net/wireless/aic/aic8800_fdrv/aicwf_debug.h b/drivers/net/wireless/aic/aic8800_fdrv/aicwf_debug.h
new file mode 100644
index 0000000000000..4a49c16f15a31
--- /dev/null
+++ b/drivers/net/wireless/aic/aic8800_fdrv/aicwf_debug.h
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _AICWF_DEBUG_H_
+#define _AICWF_DEBUG_H_
+
+#define RWNX_FN_ENTRY_STR ">>> %s()\n", __func__
+
+/* message levels */
+#define LOGERROR    0x0001
+#define LOGINFO     0x0002
+#define LOGTRACE    0x0004
+#define LOGDEBUG    0x0008
+#define LOGDATA     0x0010
+#define LOGIRQ      0x0020
+#define LOGSDPWRC   0x0040
+#define LOGWAKELOCK 0x0080
+#define LOGRXPOLL   0x0100
+
+extern int aicwf_dbg_level;
+void rwnx_data_dump(char *tag, void *data, unsigned long len);
+
+#define AICWF_LOG "AICWFDBG("
+
+#define AICWFDBG(level, args, arg...)                                          \
+	do {                                                                       \
+		if (aicwf_dbg_level & (level)) {                                         \
+			pr_debug(AICWF_LOG #level ")\t" args, ##arg);                        \
+		}                                                                      \
+	} while (0)
+
+#define RWNX_DBG(fmt, ...)                                                     \
+	do {                                                                       \
+		if (aicwf_dbg_level & LOGTRACE) {                                      \
+			pr_debug(AICWF_LOG "LOGTRACE)\t" fmt, ##__VA_ARGS__);                \
+		}                                                                      \
+	} while (0)
+
+#endif /* _AICWF_DEBUG_H_ */
diff --git a/drivers/net/wireless/aic/aic8800_fdrv/hal_desc.h b/drivers/net/wireless/aic/aic8800_fdrv/hal_desc.h
new file mode 100644
index 0000000000000..35809ccd5cef5
--- /dev/null
+++ b/drivers/net/wireless/aic/aic8800_fdrv/hal_desc.h
@@ -0,0 +1,359 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ ******************************************************************************
+ *
+ * Copyright (C) 2020 AIC semiconductor.
+ *
+ * @file hal_desc.c
+ *
+ * @brief File containing the definition of HW descriptors
+ *
+ ******************************************************************************
+ */
+
+#ifndef _HAL_DESC_H_
+#define _HAL_DESC_H_
+
+#include "lmac_types.h"
+#include "rwnx_compat.h"
+
+/* Rate and policy table */
+
+#define N_CCK   8
+#define N_OFDM  8
+#define N_HT    (8 * 2 * 2 * 4)
+#define N_VHT   (10 * 4 * 2 * 8)
+#define N_HE_SU (12 * 4 * 3 * 8)
+#define N_HE_MU (12 * 6 * 3 * 8)
+#define N_HE_ER (3 * 3 + 3) // RU242 + RU106
+
+/* conversion table from NL80211 to MACHW enum */
+extern const int chnl2bw[];
+
+/* conversion table from MACHW to NL80211 enum */
+extern const int bw2chnl[];
+
+/* Values for formatModTx */
+#define FORMATMOD_NON_HT          0
+#define FORMATMOD_NON_HT_DUP_OFDM 1
+#define FORMATMOD_HT_MF           2
+#define FORMATMOD_HT_GF           3
+#define FORMATMOD_VHT             4
+#define FORMATMOD_HE_SU           5
+#define FORMATMOD_HE_MU           6
+#define FORMATMOD_HE_ER           7
+#define FORMATMOD_HE_TB           8
+
+/* Values for navProtFrmEx */
+#define NAV_PROT_NO_PROT_BIT          0
+#define NAV_PROT_SELF_CTS_BIT         1
+#define NAV_PROT_RTS_CTS_BIT          2
+#define NAV_PROT_RTS_CTS_WITH_QAP_BIT 3
+#define NAV_PROT_STBC_BIT             4
+
+/* THD MACCTRLINFO2 fields, used in  struct umacdesc umac.flags */
+/// WhichDescriptor definition - contains aMPDU bit and position value
+/// Offset of WhichDescriptor field in the MAC CONTROL INFO 2 word
+#define WHICHDESC_OFT                   19
+/// Mask of the WhichDescriptor field
+#define WHICHDESC_MSK                   (0x07 << WHICHDESC_OFT)
+/// Only 1 THD possible, describing an unfragmented MSDU
+#define WHICHDESC_UNFRAGMENTED_MSDU     (0x00 << WHICHDESC_OFT)
+/// THD describing the first MPDU of a fragmented MSDU
+#define WHICHDESC_FRAGMENTED_MSDU_FIRST (0x01 << WHICHDESC_OFT)
+/// THD describing intermediate MPDUs of a fragmented MSDU
+#define WHICHDESC_FRAGMENTED_MSDU_INT   (0x02 << WHICHDESC_OFT)
+/// THD describing the last MPDU of a fragmented MSDU
+#define WHICHDESC_FRAGMENTED_MSDU_LAST  (0x03 << WHICHDESC_OFT)
+/// THD for extra descriptor starting an AMPDU
+#define WHICHDESC_AMPDU_EXTRA           (0x04 << WHICHDESC_OFT)
+/// THD describing the first MPDU of an A-MPDU
+#define WHICHDESC_AMPDU_FIRST           (0x05 << WHICHDESC_OFT)
+/// THD describing intermediate MPDUs of an A-MPDU
+#define WHICHDESC_AMPDU_INT             (0x06 << WHICHDESC_OFT)
+/// THD describing the last MPDU of an A-MPDU
+#define WHICHDESC_AMPDU_LAST            (0x07 << WHICHDESC_OFT)
+
+/// aMPDU bit offset
+#define AMPDU_OFT 21
+/// aMPDU bit
+#define AMPDU_BIT CO_BIT(AMPDU_OFT)
+
+union rwnx_mcs_index {
+	struct {
+		u32 mcs : 3;
+		u32 nss : 2;
+	} ht;
+	struct {
+		u32 mcs : 4;
+		u32 nss : 3;
+	} vht;
+	struct {
+		u32 mcs : 4;
+		u32 nss : 3;
+	} he;
+	u32 legacy : 7;
+};
+
+/* c.f RW-WLAN-nX-MAC-HW-UM */
+union rwnx_rate_ctrl_info {
+	struct {
+		u32 mcs_index_tx : 7;
+		u32 bw_tx : 2;
+		u32 gi_and_pre_type_tx : 2;
+		u32 format_mod_tx : 3;
+		u32 nav_prot_frm_ex : 3;
+		u32 mcs_index_prot_tx : 7;
+		u32 bw_prot_tx : 2;
+		u32 format_mod_prot_tx : 3;
+		u32 n_retry : 3;
+	};
+	u32 value;
+};
+
+/* c.f RW-WLAN-nX-MAC-HW-UM */
+struct rwnx_power_ctrl_info {
+	u32 tx_pwr_level_pt : 8;
+	u32 tx_pwr_level_prot_pt : 8;
+	u32 reserved : 16;
+};
+
+/* c.f RW-WLAN-nX-MAC-HW-UM */
+union rwnx_pol_phy_ctrl_info_1 {
+	struct {
+		u32 rsvd1 : 3;
+		u32 bf_frm_ex : 1;
+		u32 num_extn_ss : 2;
+		u32 fec_coding : 1;
+		u32 stbc : 2;
+		u32 rsvd2 : 5;
+		u32 n_tx : 3;
+		u32 n_tx_prot : 3;
+	};
+	u32 value;
+};
+
+/* c.f RW-WLAN-nX-MAC-HW-UM */
+union rwnx_pol_phy_ctrl_info_2 {
+	struct {
+		u32 antenna_set : 8;
+		u32 smm_index : 8;
+		u32 beam_formed : 1;
+	};
+	u32 value;
+};
+
+/* c.f RW-WLAN-nX-MAC-HW-UM */
+union rwnx_pol_mac_ctrl_info_1 {
+	struct {
+		u32 key_sram_index : 10;
+		u32 key_sram_index_ra : 10;
+	};
+	u32 value;
+};
+
+/* c.f RW-WLAN-nX-MAC-HW-UM */
+union rwnx_pol_mac_ctrl_info_2 {
+	struct {
+		u32 long_retry_limit : 8;
+		u32 short_retry_limit : 8;
+		u32 rts_threshold : 12;
+	};
+	u32 value;
+};
+
+#define POLICY_TABLE_PATTERN 0xBADCAB1E
+
+struct tx_policy_tbl {
+	/* Unique Pattern at the start of Policy Table */
+	u32 upatterntx;
+	/* PHY Control 1 Information used by MAC HW */
+	union rwnx_pol_phy_ctrl_info_1 phyctrlinfo_1;
+	/* PHY Control 2 Information used by MAC HW */
+	union rwnx_pol_phy_ctrl_info_2 phyctrlinfo_2;
+	/* MAC Control 1 Information used by MAC HW */
+	union rwnx_pol_mac_ctrl_info_1 macctrlinfo_1;
+	/* MAC Control 2 Information used by MAC HW */
+	union rwnx_pol_mac_ctrl_info_2 macctrlinfo_2;
+
+	union rwnx_rate_ctrl_info ratectrlinfos[NX_TX_MAX_RATES];
+	struct rwnx_power_ctrl_info powerctrlinfos[NX_TX_MAX_RATES];
+};
+
+#ifdef CONFIG_RWNX_FULLMAC
+
+/**
+ * union rwnx_hw_txstatus - Bitfield of confirmation status
+ *
+ * @tx_done: Packet has been processed by the firmware
+ * @retry_required: Packet was transmitted but not acknowledged and must be
+ *                  submitted again by the driver
+ * @sw_retry_required: Packet was not transmitted by the firmware and must be
+ *                     submitted again by the driver
+ * @acknowledged: Packet was acknowledged by the peer
+ * @reserved: Reserved status bits
+ * @value: Complete status word
+ */
+union rwnx_hw_txstatus {
+	struct {
+		u32 tx_done : 1;
+		u32 retry_required : 1;
+		u32 sw_retry_required : 1;
+		u32 acknowledged : 1;
+		u32 reserved : 28;
+	};
+	u32 value;
+};
+
+/**
+ * struct tx_cfm_tag - Structure indicating the status and other
+ * information about the transmission
+ *
+ * @credits: Number of credits returned to the TX queue (0 or 1)
+ * @ampdu_size: Number of frames in the A-MPDU when this was its last frame,
+ *              0 when this was not the last frame, or 1 for a singleton
+ * @amsdu_size: Maximum A-MSDU size in bytes
+ * @status: Transmission status
+ * @hostid: Host identifier of the transmitted buffer
+ */
+struct tx_cfm_tag {
+	/*
+	 *	u16 pn[4];
+	 *	u16 sn;
+	 *	u16 timestamp;
+	 */
+	s8 credits;
+	u8 ampdu_size;
+#ifdef CONFIG_RWNX_SPLIT_TX_BUF
+	u16 amsdu_size;
+#endif
+	union rwnx_hw_txstatus status;
+	u32 hostid;
+};
+
+/**
+ * struct rwnx_hw_txhdr - Hardware part of tx header
+ *
+ * @cfm: Information updated by fw/hardware after sending a frame
+ */
+struct rwnx_hw_txhdr {
+	struct tx_cfm_tag cfm;
+};
+
+#endif /* CONFIG_RWNX_FULLMAC */
+
+/* Modem */
+
+#define MDM_PHY_CONFIG_TRIDENT 0
+#define MDM_PHY_CONFIG_ELMA    1
+#define MDM_PHY_CONFIG_KARST   2
+
+// MODEM features (from reg_mdm_stat.h)
+/// MUMIMOTX field bit
+#define MDM_MUMIMOTX_BIT ((u32)0x80000000)
+/// MUMIMOTX field position
+#define MDM_MUMIMOTX_POS 31
+/// MUMIMORX field bit
+#define MDM_MUMIMORX_BIT ((u32)0x40000000)
+/// MUMIMORX field position
+#define MDM_MUMIMORX_POS 30
+/// BFMER field bit
+#define MDM_BFMER_BIT    ((u32)0x20000000)
+/// BFMER field position
+#define MDM_BFMER_POS    29
+/// BFMEE field bit
+#define MDM_BFMEE_BIT    ((u32)0x10000000)
+/// BFMEE field position
+#define MDM_BFMEE_POS    28
+/// LDPCDEC field bit
+#define MDM_LDPCDEC_BIT  ((u32)0x08000000)
+/// LDPCDEC field position
+#define MDM_LDPCDEC_POS  27
+/// LDPCENC field bit
+#define MDM_LDPCENC_BIT  ((u32)0x04000000)
+/// LDPCENC field position
+#define MDM_LDPCENC_POS  26
+/// CHBW field mask
+#define MDM_CHBW_MASK    ((u32)0x03000000)
+/// CHBW field LSB position
+#define MDM_CHBW_LSB     24
+/// CHBW field width
+#define MDM_CHBW_WIDTH   ((u32)0x00000002)
+/// DSSSCCK field bit
+#define MDM_DSSSCCK_BIT  ((u32)0x00800000)
+/// DSSSCCK field position
+#define MDM_DSSSCCK_POS  23
+/// VHT field bit
+#define MDM_VHT_BIT      ((u32)0x00400000)
+/// VHT field position
+#define MDM_VHT_POS      22
+/// HE field bit
+#define MDM_HE_BIT       ((u32)0x00200000)
+/// HE field position
+#define MDM_HE_POS       21
+/// ESS field bit
+#define MDM_ESS_BIT      ((u32)0x00100000)
+/// ESS field position
+#define MDM_ESS_POS      20
+/// RFMODE field mask
+#define MDM_RFMODE_MASK  ((u32)0x000F0000)
+/// RFMODE field LSB position
+#define MDM_RFMODE_LSB   16
+/// RFMODE field width
+#define MDM_RFMODE_WIDTH ((u32)0x00000004)
+/// NSTS field mask
+#define MDM_NSTS_MASK    ((u32)0x0000F000)
+/// NSTS field LSB position
+#define MDM_NSTS_LSB     12
+/// NSTS field width
+#define MDM_NSTS_WIDTH   ((u32)0x00000004)
+/// NSS field mask
+#define MDM_NSS_MASK     ((u32)0x00000F00)
+/// NSS field LSB position
+#define MDM_NSS_LSB      8
+/// NSS field width
+#define MDM_NSS_WIDTH    ((u32)0x00000004)
+/// NTX field mask
+#define MDM_NTX_MASK     ((u32)0x000000F0)
+/// NTX field LSB position
+#define MDM_NTX_LSB      4
+/// NTX field width
+#define MDM_NTX_WIDTH    ((u32)0x00000004)
+/// NRX field mask
+#define MDM_NRX_MASK     ((u32)0x0000000F)
+/// NRX field LSB position
+#define MDM_NRX_LSB      0
+/// NRX field width
+#define MDM_NRX_WIDTH    ((u32)0x00000004)
+
+#define __MDM_PHYCFG_FROM_VERS(v) (((v) & MDM_RFMODE_MASK) >> MDM_RFMODE_LSB)
+
+#define RIU_FCU_PRESENT_MASK ((u32)0xFF000000)
+#define RIU_FCU_PRESENT_LSB  24
+
+#define __RIU_FCU_PRESENT(v)                                                   \
+	(((v) & RIU_FCU_PRESENT_MASK) >> RIU_FCU_PRESENT_LSB == 5)
+
+/// AGC load version field mask
+#define RIU_AGC_LOAD_MASK ((u32)0x00C00000)
+/// AGC load version field LSB position
+#define RIU_AGC_LOAD_LSB  22
+
+#define __RIU_AGCLOAD_FROM_VERS(v)                                             \
+	(((v) & RIU_AGC_LOAD_MASK) >> RIU_AGC_LOAD_LSB)
+
+#define __FPGA_TYPE(v) (((v) & 0xFFFF0000) >> 16)
+
+#define __MDM_MAJOR_VERSION(v) (((v) & 0xFF000000) >> 24)
+#define __MDM_MINOR_VERSION(v) (((v) & 0x00FF0000) >> 16)
+/*
+ * #define __MDM_VERSION(v)                                                       \
+ *	((__MDM_MAJOR_VERSION(v) + 2) * 10 + __MDM_MINOR_VERSION(v))
+ */
+#define __MDM_VERSION(v)	\
+({								\
+	u32 __v = (v);						\
+	(__MDM_MAJOR_VERSION(__v) + 2) * 10 + __MDM_MINOR_VERSION(__v);	\
+})
+
+#endif // _HAL_DESC_H_
diff --git a/drivers/net/wireless/aic/aic8800_fdrv/ipc_compat.h b/drivers/net/wireless/aic/aic8800_fdrv/ipc_compat.h
new file mode 100644
index 0000000000000..cf1fe0d737b84
--- /dev/null
+++ b/drivers/net/wireless/aic/aic8800_fdrv/ipc_compat.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ ****************************************************************************************
+ *
+ * Copyright (C) 2020 AIC semiconductor.
+ *
+ ****************************************************************************************
+ */
+
+#ifndef _IPC_H_
+#define _IPC_H_
+
+#define __INLINE inline
+
+#define __ALIGN4 __aligned(4)
+
+#define ASSERT_ERR(condition)                                                  \
+	do {                                                                       \
+		if (unlikely(!(condition))) {                                          \
+			pr_err("%s:%d:ASSERT_ERR(" #condition ")\n", __FILE__,    \
+				   __LINE__);                                                  \
+		}                                                                      \
+	} while (0)
+
+#endif /* _IPC_H_ */
diff --git a/drivers/net/wireless/aic/aic8800_fdrv/ipc_host.c b/drivers/net/wireless/aic/aic8800_fdrv/ipc_host.c
new file mode 100644
index 0000000000000..5962d8b3fcf3f
--- /dev/null
+++ b/drivers/net/wireless/aic/aic8800_fdrv/ipc_host.c
@@ -0,0 +1,42 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ ******************************************************************************
+ *
+ * Copyright (C) 2020 AIC semiconductor.
+ *
+ * @brief IPC module
+ *
+ ******************************************************************************
+ */
+
+#ifndef __KERNEL__
+#include <stdio.h>
+#else
+#include "rwnx_defs.h"
+#include "rwnx_prof.h"
+#include <linux/spinlock.h>
+#endif
+
+#include "ipc_host.h"
+
+/*
+ * TYPES DEFINITION
+ ******************************************************************************
+ */
+
+const int nx_txdesc_cnt[] = {
+	NX_TXDESC_CNT0, NX_TXDESC_CNT1, NX_TXDESC_CNT2, NX_TXDESC_CNT3,
+#if NX_TXQ_CNT == 5
+	NX_TXDESC_CNT4,
+#endif
+};
+
+const int nx_txuser_cnt[] = {
+	CONFIG_USER_MAX,
+	CONFIG_USER_MAX,
+	CONFIG_USER_MAX,
+	CONFIG_USER_MAX,
+#if NX_TXQ_CNT == 5
+	1,
+#endif
+};
diff --git a/drivers/net/wireless/aic/aic8800_fdrv/ipc_host.h b/drivers/net/wireless/aic/aic8800_fdrv/ipc_host.h
new file mode 100644
index 0000000000000..b1cafbf30f73d
--- /dev/null
+++ b/drivers/net/wireless/aic/aic8800_fdrv/ipc_host.h
@@ -0,0 +1,169 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ ******************************************************************************
+ *
+ * Copyright (C) 2020 AIC semiconductor.
+ *
+ * @brief IPC module
+ *
+ ******************************************************************************
+ */
+
+#ifndef _IPC_HOST_H_
+#define _IPC_HOST_H_
+
+/*
+ * INCLUDE FILES
+ ******************************************************************************
+ */
+#include "ipc_shared.h"
+#ifndef __KERNEL__
+#include "arch.h"
+#else
+#include "ipc_compat.h"
+#endif
+
+/*
+ ******************************************************************************
+ * @brief This structure is used to initialize the MAC SW
+ *
+ * The WLAN device driver provides functions call-back with this structure
+ ******************************************************************************
+ */
+struct ipc_host_cb_tag {
+	/// WLAN driver call-back function: send_data_cfm
+	int (*send_data_cfm)(void *pthis, void *host_id);
+
+	/// WLAN driver call-back function: recv_data_ind
+	uint8_t (*recv_data_ind)(void *pthis, void *host_id);
+
+	/// WLAN driver call-back function: recv_radar_ind
+	uint8_t (*recv_radar_ind)(void *pthis, void *host_id);
+
+	/// WLAN driver call-back function: recv_unsup_rx_vec_ind
+	uint8_t (*recv_unsup_rx_vec_ind)(void *pthis, void *host_id);
+
+	/// WLAN driver call-back function: recv_msg_ind
+	uint8_t (*recv_msg_ind)(void *pthis, void *host_id);
+
+	/// WLAN driver call-back function: recv_msgack_ind
+	uint8_t (*recv_msgack_ind)(void *pthis, void *host_id);
+
+	/// WLAN driver call-back function: recv_dbg_ind
+	uint8_t (*recv_dbg_ind)(void *pthis, void *host_id);
+
+	/// WLAN driver call-back function: prim_tbtt_ind
+	void (*prim_tbtt_ind)(void *pthis);
+
+	/// WLAN driver call-back function: sec_tbtt_ind
+	void (*sec_tbtt_ind)(void *pthis);
+};
+
+/*
+ * Struct used to store information about host buffers (DMA Address and local
+ * pointer)
+ */
+struct ipc_hostbuf {
+	void *hostid;      ///< ptr to hostbuf client (ipc_host client) structure
+	u32 dma_addr; ///< ptr to real hostbuf dma address
+};
+
+/// Definition of the IPC Host environment structure.
+struct ipc_host_env_tag {
+	/// Structure containing the callback pointers
+	struct ipc_host_cb_tag cb;
+
+	/// Pointer to the shared environment
+	struct ipc_shared_env_tag *shared;
+
+#ifdef CONFIG_RWNX_FULLMAC
+	// Array used to store the descriptor addresses
+	struct ipc_hostbuf ipc_host_rxdesc_array[IPC_RXDESC_CNT];
+	// Index of the host RX descriptor array (ipc_shared environment)
+	u8 ipc_host_rxdesc_idx;
+	/// Store the number of RX Descriptors
+	u8 rxdesc_nb;
+#endif //(CONFIG_RWNX_FULLMAC)
+
+	/// Fields for Data Rx handling
+	// Index used for ipc_host_rxbuf_array to point to current buffer
+	u8 ipc_host_rxbuf_idx;
+	// Store the number of Rx Data buffers
+	u32 rx_bufnb;
+	// Store the size of the Rx Data buffers
+	u32 rx_bufsz;
+
+	/// Fields for Radar events handling
+	// Global array used to store the hostid and hostbuf addresses
+	struct ipc_hostbuf ipc_host_radarbuf_array[IPC_RADARBUF_CNT];
+	// Index used for ipc_host_rxbuf_array to point to current buffer
+	u8 ipc_host_radarbuf_idx;
+	// Store the number of radar event buffers
+	u32 radar_bufnb;
+	// Store the size of the radar event buffers
+	u32 radar_bufsz;
+
+	/// Fields for Unsupported frame handling
+	// Global array used to store the hostid and hostbuf addresses
+	struct ipc_hostbuf ipc_host_unsuprxvecbuf_array[IPC_UNSUPRXVECBUF_CNT];
+	// Index used for ipc_host_unsuprxvecbuf_array to point to current buffer
+	u8 ipc_host_unsuprxvecbuf_idx;
+	// Store the number of unsupported rx vector buffers
+	u32 unsuprxvec_bufnb;
+	// Store the size of unsupported rx vector buffers
+	u32 unsuprxvec_bufsz;
+
+	// Index used that points to the first free TX desc
+	u32 txdesc_free_idx[IPC_TXQUEUE_CNT][CONFIG_USER_MAX];
+	// Index used that points to the first used TX desc
+	u32 txdesc_used_idx[IPC_TXQUEUE_CNT][CONFIG_USER_MAX];
+	// Array storing the currently pushed host ids for the BK queue
+	void *tx_host_id0[CONFIG_USER_MAX][NX_TXDESC_CNT0];
+	// Array storing the currently pushed host ids for the BE queue
+	void *tx_host_id1[CONFIG_USER_MAX][NX_TXDESC_CNT1];
+	// Array storing the currently pushed host ids for the VI queue
+	void *tx_host_id2[CONFIG_USER_MAX][NX_TXDESC_CNT2];
+	// Array storing the currently pushed host ids for the VO queue
+	void *tx_host_id3[CONFIG_USER_MAX][NX_TXDESC_CNT3];
+#if NX_TXQ_CNT == 5
+	// Array storing the currently pushed host ids for the BCN queue
+	void *tx_host_id4[1][NX_TXDESC_CNT4];
+#endif
+	// Pointer to the different host ids arrays, per IPC queue
+	void **tx_host_id[IPC_TXQUEUE_CNT][CONFIG_USER_MAX];
+	// Pointer to the different TX descriptor arrays, per IPC queue
+	struct txdesc_host *txdesc[IPC_TXQUEUE_CNT][CONFIG_USER_MAX];
+
+	/// Fields for Emb->App MSGs handling
+	// Global array used to store the hostid and hostbuf addresses for msg/ind
+	struct ipc_hostbuf ipc_host_msgbuf_array[IPC_MSGE2A_BUF_CNT];
+	// Index of the MSG E2A buffers array to point to current buffer
+	u8 ipc_host_msge2a_idx;
+	// Store the number of E2A MSG buffers
+	u32 ipc_e2amsg_bufnb;
+	// Store the size of the E2A MSG buffers
+	u32 ipc_e2amsg_bufsz;
+
+	/// E2A ACKs of A2E MSGs
+	u8 msga2e_cnt;
+	void *msga2e_hostid;
+
+	/// Fields for Debug MSGs handling
+	// Global array used to store the hostid and hostbuf addresses for Debug
+	// messages
+	struct ipc_hostbuf ipc_host_dbgbuf_array[IPC_DBGBUF_CNT];
+	// Index of the Debug messages buffers array to point to current buffer
+	u8 ipc_host_dbg_idx;
+	// Store the number of Debug messages buffers
+	u32 ipc_dbg_bufnb;
+	// Store the size of the Debug messages buffers
+	u32 ipc_dbg_bufsz;
+
+	/// Pointer to the attached object (used in callbacks and register accesses)
+	void *pthis;
+};
+
+extern const int nx_txdesc_cnt[];
+extern const int nx_txuser_cnt[];
+
+#endif // _IPC_HOST_H_
diff --git a/drivers/net/wireless/aic/aic8800_fdrv/ipc_shared.h b/drivers/net/wireless/aic/aic8800_fdrv/ipc_shared.h
new file mode 100644
index 0000000000000..5773567c65adc
--- /dev/null
+++ b/drivers/net/wireless/aic/aic8800_fdrv/ipc_shared.h
@@ -0,0 +1,770 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ ******************************************************************************
+ *
+ * Copyright (C) 2020 AIC semiconductor.
+ *
+ * @brief Shared data between both IPC modules
+ *
+ ******************************************************************************
+ */
+
+#ifndef _IPC_SHARED_H_
+#define _IPC_SHARED_H_
+
+/*
+ * INCLUDE FILES
+ ****************************************************************************************
+ */
+#include "ipc_compat.h"
+#include "lmac_mac.h"
+#include "rwnx_compat.h"
+
+/*
+ * DEFINES AND MACROS
+ ****************************************************************************************
+ */
+#define CO_BIT(pos) (1U << (pos))
+
+#define IPC_TXQUEUE_CNT NX_TXQ_CNT
+#define NX_TXDESC_CNT0  8
+#define NX_TXDESC_CNT1  64
+#define NX_TXDESC_CNT2  64
+#define NX_TXDESC_CNT3  32
+#if NX_TXQ_CNT == 5
+#define NX_TXDESC_CNT4 8
+#endif
+
+/*
+ * Number of Host buffers available for Data Rx handling (through DMA)
+ */
+#define IPC_RXBUF_CNT 128
+
+/*
+ * Number of shared descriptors available for Data RX handling
+ */
+#define IPC_RXDESC_CNT 128
+
+/*
+ * Number of Host buffers available for Radar events handling (through DMA)
+ */
+#define IPC_RADARBUF_CNT 16
+
+/*
+ * Number of Host buffers available for unsupported Rx vectors handling (through
+ * DMA)
+ */
+#define IPC_UNSUPRXVECBUF_CNT 8
+
+/*
+ *  Size of RxVector
+ */
+#define IPC_RXVEC_SIZE 16
+
+/*
+ * Number of Host buffers available for Emb->App MSGs sending (through DMA)
+ */
+#ifdef CONFIG_RWNX_FULLMAC
+#define IPC_MSGE2A_BUF_CNT 64
+#endif
+/*
+ * Number of Host buffers available for Debug Messages sending (through DMA)
+ */
+#define IPC_DBGBUF_CNT 32
+
+/*
+ * Length used in MSGs structures
+ */
+#define IPC_A2E_MSG_BUF_SIZE 127 // size in 4-byte words
+#ifdef CONFIG_RWNX_FULLMAC
+#define IPC_E2A_MSG_SIZE_BASE 256 // size in 4-byte words
+#endif
+
+#define IPC_E2A_MSG_PARAM_SIZE IPC_E2A_MSG_SIZE_BASE
+
+/*
+ * Debug messages buffers size (in bytes)
+ */
+#define IPC_DBG_PARAM_SIZE 256
+
+/*
+ * Define used for Rx hostbuf validity.
+ * This value should appear only when hostbuf was used for a Reception.
+ */
+#define RX_DMA_OVER_PATTERN 0xAAAAAA00
+
+/*
+ * Define used for MSG buffers validity.
+ * This value will be written only when a MSG buffer is used for sending from
+ * Emb to App.
+ */
+#define IPC_MSGE2A_VALID_PATTERN 0xADDEDE2A
+
+/*
+ * Define used for Debug messages buffers validity.
+ * This value will be written only when a DBG buffer is used for sending from
+ * Emb to App.
+ */
+#define IPC_DBG_VALID_PATTERN 0x000CACA0
+
+/*
+ *  Length of the receive vectors, in bytes
+ */
+#define DMA_HDR_PHYVECT_LEN 36
+
+/*
+ * Maximum number of payload addresses and lengths present in the descriptor
+ */
+#ifdef CONFIG_RWNX_SPLIT_TX_BUF
+#define NX_TX_PAYLOAD_MAX 6
+#else
+#define NX_TX_PAYLOAD_MAX 1
+#endif
+
+/*
+ * Message struct/ID API version
+ */
+#define MSG_API_VER 33
+
+/*
+ ****************************************************************************************
+ */
+// c.f LMAC/src/tx/tx_swdesc.h
+/// Descriptor filled by the Host
+struct hostdesc {
+	/// Pointer to packet payload
+	// u32 packet_addr;
+	/// Size of the payload
+	u16 packet_len;
+	u16 flags_ext;
+
+	u32 hostid;
+#ifdef CONFIG_RWNX_FULLMAC
+	/// Address of the status descriptor in host memory (used for confirmation
+	/// upload)
+	// u32 status_desc_addr;
+	/// Destination Address
+	struct mac_addr eth_dest_addr;
+	/// Source Address
+	struct mac_addr eth_src_addr;
+	/// Ethernet Type
+	__be16 ethertype;
+#else /* ! CONFIG_RWNX_FULLMAC */
+#ifdef CONFIG_RWNX_AGG_TX
+	/// Sequence Number for AMPDU MPDUs - for quick check if it's allowed within
+	/// window
+	u16 sn;
+#endif /* CONFIG_RWNX_AGG_TX */
+	/// Padding between the buffer control structure and the MPDU in host memory
+	u8 padding;
+#endif /* CONFIG_RWNX_FULLMAC */
+	u8 ac;
+	/// Packet TID (0xFF if not a QoS frame)
+	u8 tid;
+	/// Interface Id
+	u8 vif_idx;
+	/// Station Id (0xFF if station is unknown)
+	u8 staid;
+#ifdef CONFIG_RWNX_MUMIMO_TX
+	/// MU-MIMO information (GroupId and User Position in the group) - The
+	/// GroupId is located on bits 0-5 and the User Position on bits 6-7. The
+	/// GroupId value is set to 63 if MU-MIMO shall not be used
+	u8 mumimo_info;
+#endif /* CONFIG_RWNX_MUMIMO_TX */
+#ifdef CONFIG_RWNX_FULLMAC
+	/// TX flags
+	u16 flags;
+#endif /* CONFIG_RWNX_FULLMAC */
+};
+
+/// Descriptor filled by the UMAC
+struct umacdesc {
+#ifdef CONFIG_RWNX_AGG_TX
+	/// First Sequence Number of the BlockAck window
+	u16 sn_win;
+	/// Flags from UMAC (match tx_hd.macctrlinfo2 format)
+	u32 flags;
+	/// PHY related flags field - rate, GI type, BW type - filled by driver
+	u32 phy_flags;
+#endif //(CONFIG_RWNX_AGG_TX)
+};
+
+struct txdesc_api {
+	/// Information provided by Host
+	struct hostdesc host;
+};
+
+struct txdesc_host {
+	u32 ready;
+
+	/// API of the embedded part
+	struct txdesc_api api;
+};
+
+/// Comes from ipc_dma.h
+/// Element in the pool of TX DMA bridge descriptors.
+struct dma_desc {
+	/* Application subsystem address which is used as source address for DMA
+	 * payload transfer
+	 */
+	u32 src;
+	/** Points to the start of the embedded data buffer associated with this
+	 * descriptor. This address acts as the destination address for the DMA
+	 * payload transfer
+	 */
+	u32 dest;
+	/// Complete length of the buffer in memory
+	u16 length;
+	/// Control word for the DMA engine (e.g. for interrupt generation)
+	u16 ctrl;
+	/// Pointer to the next element of the chained list
+	u32 next;
+};
+
+// Comes from la.h
+/// Length of the configuration data of a logic analyzer
+#define LA_CONF_LEN 10
+
+/// Structure containing the configuration data of a logic analyzer
+struct la_conf_tag {
+	u32 conf[LA_CONF_LEN];
+	u32 trace_len;
+	u32 diag_conf;
+};
+
+/// Size of a logic analyzer memory
+#define LA_MEM_LEN (1024 * 1024)
+
+/// Type of errors
+enum {
+	/// Recoverable error, not requiring any action from Upper MAC
+	DBG_ERROR_RECOVERABLE = 0,
+	/// Fatal error, requiring Upper MAC to reset Lower MAC and HW and restart
+	/// operation
+	DBG_ERROR_FATAL
+};
+
+/// Maximum length of the SW diag trace
+#define DBG_SW_DIAG_MAX_LEN 1024
+
+/// Maximum length of the error trace
+#define DBG_ERROR_TRACE_SIZE 256
+
+/// Number of MAC diagnostic port banks
+#define DBG_DIAGS_MAC_MAX 48
+
+/// Number of PHY diagnostic port banks
+#define DBG_DIAGS_PHY_MAX 32
+
+/// Maximum size of the RX header descriptor information in the debug dump
+#define DBG_RHD_MEM_LEN (5 * 1024)
+
+/// Maximum size of the RX buffer descriptor information in the debug dump
+#define DBG_RBD_MEM_LEN (5 * 1024)
+
+/// Maximum size of the TX header descriptor information in the debug dump
+#define DBG_THD_MEM_LEN (10 * 1024)
+
+/// Structure containing the information about the PHY channel that is used
+struct phy_channel_info {
+	/// PHY channel information 1
+	u32 info1;
+	/// PHY channel information 2
+	u32 info2;
+};
+
+/// Debug information forwarded to host when an error occurs
+struct dbg_debug_info_tag {
+	/// Type of error (0: recoverable, 1: fatal)
+	u32 error_type;
+	/// Pointer to the first RX Header Descriptor chained to the MAC HW
+	u32 rhd;
+	/// Size of the RX header descriptor buffer
+	u32 rhd_len;
+	/// Pointer to the first RX Buffer Descriptor chained to the MAC HW
+	u32 rbd;
+	/// Size of the RX buffer descriptor buffer
+	u32 rbd_len;
+	/// Pointer to the first TX Header Descriptors chained to the MAC HW
+	u32 thd[NX_TXQ_CNT];
+	/// Size of the TX header descriptor buffer
+	u32 thd_len[NX_TXQ_CNT];
+	/// MAC HW diag configuration
+	u32 hw_diag;
+	/// Error message
+	u32 error[DBG_ERROR_TRACE_SIZE / 4];
+	/// SW diag configuration length
+	u32 sw_diag_len;
+	/// SW diag configuration
+	u32 sw_diag[DBG_SW_DIAG_MAX_LEN / 4];
+	/// PHY channel information
+	struct phy_channel_info chan_info;
+	/// Embedded LA configuration
+	struct la_conf_tag la_conf;
+	/// MAC diagnostic port state
+	u16 diags_mac[DBG_DIAGS_MAC_MAX];
+	/// PHY diagnostic port state
+	u16 diags_phy[DBG_DIAGS_PHY_MAX];
+	/// MAC HW RX Header descriptor pointer
+	u32 rhd_hw_ptr;
+	/// MAC HW RX Buffer descriptor pointer
+	u32 rbd_hw_ptr;
+};
+
+/// Full debug dump that is forwarded to host in case of error
+struct dbg_debug_dump_tag {
+	/// Debug information
+	struct dbg_debug_info_tag dbg_info;
+
+	/// RX header descriptor memory
+	u32 rhd_mem[DBG_RHD_MEM_LEN / 4];
+
+	/// RX buffer descriptor memory
+	u32 rbd_mem[DBG_RBD_MEM_LEN / 4];
+
+	/// TX header descriptor memory
+	u32 thd_mem[NX_TXQ_CNT][DBG_THD_MEM_LEN / 4];
+
+	/// Logic analyzer memory
+	u32 la_mem[LA_MEM_LEN / 4];
+};
+
+/// Number of pulses in a radar event structure
+#define RADAR_PULSE_MAX 4
+
+/// Definition of an array of radar pulses
+struct radar_pulse_array_desc {
+	/// Buffer containing the radar pulses
+	u32 pulse[RADAR_PULSE_MAX];
+	/// Index of the radar detection chain that detected those pulses
+	u32 idx;
+	/// Number of valid pulses in the buffer
+	u32 cnt;
+};
+
+/// Bit mapping inside a radar pulse element
+struct radar_pulse {
+	s32
+		freq : 6;  /** Freq (resolution is 2Mhz range is [-Fadc/4 .. Fadc/4]) */
+	u32 fom : 4; /** Figure of Merit */
+	u32 len : 6; /** Length of the current radar pulse (resolution is 2us) */
+	u32 rep : 16; /* Time interval between the previous radar event
+			 * and the current one (in us)
+			 */
+};
+
+/// Definition of a RX vector descriptor
+struct rx_vector_desc {
+	/// PHY channel information
+	struct phy_channel_info phy_info;
+
+	/// RX vector 1
+	u32 rx_vect1[IPC_RXVEC_SIZE / 4];
+
+	/// Used to print a valid rx vector
+	u32 pattern;
+};
+
+///
+struct rxdesc_tag {
+	/// Host Buffer Address
+	u32 host_id;
+	/// Length
+	u32 frame_len;
+	/// Status
+	u16 status;
+};
+
+/*
+ ****************************************************************************************
+ * @addtogroup IPC_TX IPC Tx path
+ *  @ingroup IPC
+ *  @brief IPC Tx path structures and functions
+ *
+ * A typical use case of the IPC Tx path API:
+ * @msc
+ * hscale = "2";
+ *
+ * a [label=Driver],
+ * b [label="IPC host"],
+ * c [label="IPC emb"],
+ * d [label=Firmware];
+ *
+ * ---   [label="Tx descriptor queue example"];
+ * a=>a  [label="Driver receives a Tx packet from OS"];
+ * a=>b  [label="ipc_host_txdesc_get()"];
+ * a<<b  [label="struct txdesc_host *"];
+ * a=>a  [label="Driver fill the descriptor"];
+ * a=>b  [label="ipc_host_txdesc_push()"];
+ * ...   [label="(several Tx desc can be pushed)"];
+ * b:>c  [label="Tx desc queue filled IRQ"];
+ * c=>>d [label="EDCA sub-scheduler callback"];
+ * c<<d  [label="Tx desc queue to pop"];
+ * c=>>d [label="UMAC Tx desc callback"];
+ * ...   [label="(several Tx desc can be popped)"];
+ * d=>d  [label="Packets are sent or discarded"];
+ * ---   [label="Tx confirm queue example"];
+ * c<=d  [label="ipc_emb_txcfm_push()"];
+ * c>>d  [label="Request accepted"];
+ * ...   [label="(several Tx cfm can be pushed)"];
+ * b<:c  [label="Tx cfm queue filled IRQ"];
+ * a<<=b [label="Driver's Tx Confirm callback"];
+ * a=>b  [label="ipc_host_txcfm_pop()"];
+ * a<<b  [label="struct ipc_txcfm"];
+ * a<=a  [label="Packets are freed by the driver"];
+ * @endmsc
+ *
+ * @{
+ ****************************************************************************************
+ */
+
+/// @} IPC_TX
+
+/*
+ ****************************************************************************************
+ *  @defgroup IPC_RX IPC Rx path
+ *  @ingroup IPC
+ *  @brief IPC Rx path functions and structures
+ *
+ * A typical use case of the IPC Rx path API:
+ * @msc
+ * hscale = "2";
+ *
+ * a [label=Firmware],
+ * b [label="IPC emb"],
+ * c [label="IPC host"],
+ * d [label=Driver];
+ *
+ * ---   [label="Rx buffer and desc queues usage example"];
+ * d=>c  [label="ipc_host_rxbuf_push()"];
+ * d=>c  [label="ipc_host_rxbuf_push()"];
+ * d=>c  [label="ipc_host_rxbuf_push()"];
+ * ...   [label="(several Rx buffer are pushed)"];
+ * a=>a  [label=" Frame is received\n from the medium"];
+ * a<<b  [label="struct ipc_rxbuf"];
+ * a=>a  [label=" Firmware fill the buffer\n with received frame"];
+ * a<<b  [label="Push accepted"];
+ * ...   [label="(several Rx desc can be pushed)"];
+ * b:>c  [label="Rx desc queue filled IRQ"];
+ * c=>>d [label="Driver Rx packet callback"];
+ * c<=d  [label="ipc_host_rxdesc_pop()"];
+ * d=>d  [label="Rx packet is handed\nover to the OS "];
+ * ...   [label="(several Rx desc can be poped)"];
+ * ---   [label="Rx buffer request exemple"];
+ * b:>c  [label="Low Rx buffer count IRQ"];
+ * a<<b  [label="struct ipc_rxbuf"];
+ * c=>>d [label="Driver Rx buffer callback"];
+ * d=>c  [label="ipc_host_rxbuf_push()"];
+ * d=>c  [label="ipc_host_rxbuf_push()"];
+ * d=>c  [label="ipc_host_rxbuf_push()"];
+ * ...   [label="(several Rx buffer are pushed)"];
+ * @endmsc
+ *
+ * @addtogroup IPC_RX
+ * @{
+ ****************************************************************************************
+ */
+
+/// @} IPC_RX
+
+/*
+ ****************************************************************************************
+ *  @defgroup IPC_MISC IPC Misc
+ *  @ingroup IPC
+ *  @brief IPC miscellaneous functions
+ ****************************************************************************************
+ */
+/** IPC header structure.  This structure is stored at the beginning of every
+ * IPC message.
+ * @warning This structure's size must NOT exceed 4 bytes in length.
+ */
+struct ipc_header {
+	/// IPC message type.
+	u16 type;
+	/// IPC message size in number of bytes.
+	u16 size;
+};
+
+struct ipc_msg_elt {
+	/// Message header (alignment forced on word size, see allocation in shared
+	/// env).
+	struct ipc_header header __ALIGN4;
+};
+
+/// Message structure for MSGs from Emb to App
+struct ipc_e2a_msg {
+	u16 id; ///< Message id.
+	u16 dummy_dest_id;
+	u16 dummy_src_id;
+	u16 param_len;                     ///< Parameter embedded struct length.
+	u32 pattern;                       ///< Used to stamp a valid MSG buffer
+	u32 param[IPC_E2A_MSG_PARAM_SIZE]; ///< Parameter embedded struct. Must be
+										 ///< word-aligned.
+};
+
+/// Message structure for Debug messages from Emb to App
+struct ipc_dbg_msg {
+	u32 string[IPC_DBG_PARAM_SIZE / 4]; ///< Debug string
+	u32 pattern;                        ///< Used to stamp a valid buffer
+};
+
+/// Message structure for MSGs from App to Emb.
+/// Actually a sub-structure will be used when filling the messages.
+struct ipc_a2e_msg {
+	u32 dummy_word; // used to cope with kernel message structure
+	u32 msg[IPC_A2E_MSG_BUF_SIZE]; // body of the msg
+};
+
+struct ipc_shared_rx_buf {
+	/// < ptr to hostbuf client (ipc_host client) structure
+	u32 hostid;
+	/// < ptr to real hostbuf dma address
+	u32 dma_addr;
+};
+
+struct ipc_shared_rx_desc {
+	/// DMA Address
+	u32 dma_addr;
+};
+
+/// Structure containing FW characteristics for compatibility checking
+struct compatibility_tag {
+	/// Size of IPC shared memory
+	u16 ipc_shared_size;
+	/// Message struct/ID API version
+	u16 msg_api;
+	/// Version of IPC shared
+	u8 ipc_shared_version;
+	/// Number of host buffers available for Emb->App MSGs sending
+	u8 msge2a_buf_cnt;
+	/// Number of host buffers available for Debug Messages sending
+	u8 dbgbuf_cnt;
+	/// Number of host buffers available for Radar events handling
+	u8 radarbuf_cnt;
+	/// Number of host buffers available for unsupported Rx vectors handling
+	u8 unsuprxvecbuf_cnt;
+	/// Number of shared descriptors available for Data RX handling
+	u8 rxdesc_cnt;
+	/// Number of host buffers available for Data Rx handling
+	u8 rxbuf_cnt;
+	/// Number of descriptors in BK TX queue (power of 2, min 4, max 64)
+	u8 bk_txq;
+	/// Number of descriptors in BE TX queue (power of 2, min 4, max 64)
+	u8 be_txq;
+	/// Number of descriptors in VI TX queue (power of 2, min 4, max 64)
+	u8 vi_txq;
+	/// Number of descriptors in VO TX queue (power of 2, min 4, max 64)
+	u8 vo_txq;
+	/// Number of descriptors in BCN TX queue (power of 2, min 4, max 64)
+	u8 bcn_txq;
+};
+
+/*
+ * TYPE and STRUCT DEFINITIONS
+ ****************************************************************************************
+ */
+
+// Indexes are defined in the MIB shared structure
+struct ipc_shared_env_tag {
+	struct compatibility_tag comp_info; // FW characteristics
+
+	struct ipc_a2e_msg
+		msg_a2e_buf; // room for MSG to be sent from App to Emb
+
+	// Fields for MSGs sending from Emb to App
+	struct ipc_e2a_msg
+		msg_e2a_buf; // room to build the MSG to be DMA Xferred
+	struct dma_desc
+		msg_dma_desc; // DMA descriptor for Emb->App MSGs Xfers
+	u32 msg_e2a_hostbuf_addr[IPC_MSGE2A_BUF_CNT]; // buffers @ for DMA Xfers
+
+	// Fields for Debug MSGs sending from Emb to App
+	struct ipc_dbg_msg
+		dbg_buf; // room to build the MSG to be DMA Xferred
+	struct dma_desc
+		dbg_dma_desc; // DMA descriptor for Emb->App MSGs Xfers
+	u32 dbg_hostbuf_addr[IPC_DBGBUF_CNT]; // buffers @ for MSGs DMA Xfers
+	u32 la_dbginfo_addr; // Host buffer address for the debug information
+	u32 pattern_addr;
+
+	u32 radarbuf_hostbuf[IPC_RADARBUF_CNT]; // buffers @ for Radar Events
+	u32 unsuprxvecbuf_hostbuf[IPC_UNSUPRXVECBUF_CNT]; // buffers @ for
+	// unsupported Rx vectors
+	struct txdesc_host txdesc0[CONFIG_USER_MAX][NX_TXDESC_CNT0];
+	struct txdesc_host txdesc1[CONFIG_USER_MAX][NX_TXDESC_CNT1];
+	struct txdesc_host txdesc2[CONFIG_USER_MAX][NX_TXDESC_CNT2];
+	struct txdesc_host txdesc3[CONFIG_USER_MAX][NX_TXDESC_CNT3];
+#if NX_TXQ_CNT == 5
+	struct txdesc_host txdesc4[1][NX_TXDESC_CNT4];
+#endif
+#ifdef CONFIG_RWNX_FULLMAC
+	// RX Descriptors Array
+	struct ipc_shared_rx_desc host_rxdesc[IPC_RXDESC_CNT];
+	// RX Buffers Array
+	struct ipc_shared_rx_buf host_rxbuf[IPC_RXBUF_CNT];
+#else
+	// buffers @ for Data Rx
+	u32 host_rxbuf[IPC_RXBUF_CNT];
+#endif /* CONFIG_RWNX_FULLMAC */
+
+	u32 buffered[NX_REMOTE_STA_MAX][TID_MAX];
+
+	u16 trace_pattern;
+	u32 trace_start;
+	u32 trace_end;
+	u32 trace_size;
+	u32 trace_offset;
+	u32 trace_nb_compo;
+	u32 trace_offset_compo;
+};
+
+extern struct ipc_shared_env_tag ipc_shared_env;
+
+/*
+ * TYPE and STRUCT DEFINITIONS
+ ****************************************************************************************
+ */
+
+// IRQs from app to emb
+/// Interrupts bits used for the TX descriptors of the AC queues
+#ifdef CONFIG_RWNX_MUMIMO_TX
+/// Interrupts bits used
+#if CONFIG_USER_MAX > 3
+#define IPC_IRQ_A2E_USER_MSK 0xF
+#elif CONFIG_USER_MAX > 2
+#define IPC_IRQ_A2E_USER_MSK 0x7
+#else
+#define IPC_IRQ_A2E_USER_MSK 0x3
+#endif
+
+/// Offset of the interrupts for AC0
+#define IPC_IRQ_A2E_AC0_OFT 8
+/// Mask of the interrupts for AC0
+#define IPC_IRQ_A2E_AC0_MSK (IPC_IRQ_A2E_USER_MSK << IPC_IRQ_A2E_AC0_OFT)
+/// Offset of the interrupts for AC1
+#define IPC_IRQ_A2E_AC1_OFT (IPC_IRQ_A2E_AC0_OFT + CONFIG_USER_MAX)
+/// Mask of the interrupts for AC1
+#define IPC_IRQ_A2E_AC1_MSK (IPC_IRQ_A2E_USER_MSK << IPC_IRQ_A2E_AC1_OFT)
+/// Offset of the interrupts for AC2
+#define IPC_IRQ_A2E_AC2_OFT (IPC_IRQ_A2E_AC1_OFT + CONFIG_USER_MAX)
+/// Mask of the interrupts for AC2
+#define IPC_IRQ_A2E_AC2_MSK (IPC_IRQ_A2E_USER_MSK << IPC_IRQ_A2E_AC2_OFT)
+/// Offset of the interrupts for AC3
+#define IPC_IRQ_A2E_AC3_OFT (IPC_IRQ_A2E_AC2_OFT + CONFIG_USER_MAX)
+/// Mask of the interrupts for AC3
+#define IPC_IRQ_A2E_AC3_MSK (IPC_IRQ_A2E_USER_MSK << IPC_IRQ_A2E_AC3_OFT)
+/// Offset of the interrupts for BCN
+#define IPC_IRQ_A2E_BCN_OFT (IPC_IRQ_A2E_AC3_OFT + CONFIG_USER_MAX)
+/// Mask of the interrupts for BCN
+#define IPC_IRQ_A2E_BCN_MSK CO_BIT(IPC_IRQ_A2E_BCN_OFT)
+
+#define IPC_IRQ_A2E_AC_TXDESC                                                  \
+	(IPC_IRQ_A2E_AC0_MSK | IPC_IRQ_A2E_AC1_MSK | IPC_IRQ_A2E_AC2_MSK |         \
+	 IPC_IRQ_A2E_AC3_MSK)
+
+/// Interrupts bits used for the TX descriptors of the BCN queue
+#if NX_TXQ_CNT < 5
+#define IPC_IRQ_A2E_BCN_TXDESC 0
+#else
+#define IPC_IRQ_A2E_BCN_TXDESC (0x01 << IPC_IRQ_A2E_BCN_OFT)
+#endif
+
+/// IPC TX descriptor interrupt mask
+#define IPC_IRQ_A2E_TXDESC (IPC_IRQ_A2E_AC_TXDESC | IPC_IRQ_A2E_BCN_TXDESC)
+#else
+/// IPC TX descriptor interrupt mask
+#define IPC_IRQ_A2E_TXDESC 0xFF00
+#endif
+
+#define IPC_IRQ_A2E_TXDESC_FIRSTBIT (8)
+#define IPC_IRQ_A2E_RXBUF_BACK      CO_BIT(5)
+#define IPC_IRQ_A2E_RXDESC_BACK     CO_BIT(4)
+
+#define IPC_IRQ_A2E_MSG CO_BIT(1)
+#define IPC_IRQ_A2E_DBG CO_BIT(0)
+
+#define IPC_IRQ_A2E_ALL (IPC_IRQ_A2E_TXDESC | IPC_IRQ_A2E_MSG | IPC_IRQ_A2E_DBG)
+
+// IRQs from emb to app
+#define IPC_IRQ_E2A_TXCFM_POS 7
+
+#ifdef CONFIG_RWNX_MUMIMO_TX
+/// Interrupts bits used
+#if CONFIG_USER_MAX > 3
+#define IPC_IRQ_E2A_USER_MSK 0xF
+#elif CONFIG_USER_MAX > 2
+#define IPC_IRQ_E2A_USER_MSK 0x7
+#else
+#define IPC_IRQ_E2A_USER_MSK 0x3
+#endif
+
+/// Offset of the interrupts for AC0
+#define IPC_IRQ_E2A_AC0_OFT IPC_IRQ_E2A_TXCFM_POS
+/// Mask of the interrupts for AC0
+#define IPC_IRQ_E2A_AC0_MSK (IPC_IRQ_E2A_USER_MSK << IPC_IRQ_E2A_AC0_OFT)
+/// Offset of the interrupts for AC1
+#define IPC_IRQ_E2A_AC1_OFT (IPC_IRQ_E2A_AC0_OFT + CONFIG_USER_MAX)
+/// Mask of the interrupts for AC1
+#define IPC_IRQ_E2A_AC1_MSK (IPC_IRQ_E2A_USER_MSK << IPC_IRQ_E2A_AC1_OFT)
+/// Offset of the interrupts for AC2
+#define IPC_IRQ_E2A_AC2_OFT (IPC_IRQ_E2A_AC1_OFT + CONFIG_USER_MAX)
+/// Mask of the interrupts for AC2
+#define IPC_IRQ_E2A_AC2_MSK (IPC_IRQ_E2A_USER_MSK << IPC_IRQ_E2A_AC2_OFT)
+/// Offset of the interrupts for AC3
+#define IPC_IRQ_E2A_AC3_OFT (IPC_IRQ_E2A_AC2_OFT + CONFIG_USER_MAX)
+/// Mask of the interrupts for AC3
+#define IPC_IRQ_E2A_AC3_MSK (IPC_IRQ_E2A_USER_MSK << IPC_IRQ_E2A_AC3_OFT)
+/// Offset of the interrupts for BCN
+#define IPC_IRQ_E2A_BCN_OFT (IPC_IRQ_E2A_AC3_OFT + CONFIG_USER_MAX)
+/// Mask of the interrupts for BCN
+#define IPC_IRQ_E2A_BCN_MSK CO_BIT(IPC_IRQ_E2A_BCN_OFT)
+
+#define IPC_IRQ_E2A_AC_TXCFM                                                   \
+	(IPC_IRQ_E2A_AC0_MSK | IPC_IRQ_E2A_AC1_MSK | IPC_IRQ_E2A_AC2_MSK |         \
+	 IPC_IRQ_E2A_AC3_MSK)
+
+/// Interrupts bits used for the TX descriptors of the BCN queue
+#if NX_TXQ_CNT < 5
+#define IPC_IRQ_E2A_BCN_TXCFM 0
+#else
+#define IPC_IRQ_E2A_BCN_TXCFM (0x01 << IPC_IRQ_E2A_BCN_OFT)
+#endif
+
+/// IPC TX descriptor interrupt mask
+#define IPC_IRQ_E2A_TXCFM (IPC_IRQ_E2A_AC_TXCFM | IPC_IRQ_E2A_BCN_TXCFM)
+
+#else
+
+#define IPC_IRQ_E2A_TXCFM (((1 << NX_TXQ_CNT) - 1) << IPC_IRQ_E2A_TXCFM_POS)
+
+#endif /* CONFIG_RWNX_MUMIMO_TX */
+
+#define IPC_IRQ_E2A_UNSUP_RX_VEC CO_BIT(7)
+#define IPC_IRQ_E2A_RADAR        CO_BIT(6)
+#define IPC_IRQ_E2A_TBTT_SEC     CO_BIT(5)
+#define IPC_IRQ_E2A_TBTT_PRIM    CO_BIT(4)
+#define IPC_IRQ_E2A_RXDESC       CO_BIT(3)
+#define IPC_IRQ_E2A_MSG_ACK      CO_BIT(2)
+#define IPC_IRQ_E2A_MSG          CO_BIT(1)
+#define IPC_IRQ_E2A_DBG          CO_BIT(0)
+
+#define IPC_IRQ_E2A_ALL                                                        \
+	(IPC_IRQ_E2A_TXCFM | IPC_IRQ_E2A_RXDESC | IPC_IRQ_E2A_MSG_ACK |            \
+	 IPC_IRQ_E2A_MSG | IPC_IRQ_E2A_DBG | IPC_IRQ_E2A_TBTT_PRIM |               \
+	 IPC_IRQ_E2A_TBTT_SEC | IPC_IRQ_E2A_RADAR | IPC_IRQ_E2A_UNSUP_RX_VEC)
+
+// FLAGS for RX desc
+#define IPC_RX_FORWARD  CO_BIT(1)
+#define IPC_RX_INTRABSS CO_BIT(0)
+
+// IPC message TYPE
+enum {
+	IPC_MSG_NONE = 0,
+	IPC_MSG_WRAP,
+	IPC_MSG_KMSG,
+
+	IPC_DBG_STRING,
+
+};
+
+#endif // _IPC_SHARED_H_
diff --git a/drivers/net/wireless/aic/aic8800_fdrv/lmac_mac.h b/drivers/net/wireless/aic/aic8800_fdrv/lmac_mac.h
new file mode 100644
index 0000000000000..114ed14f4ad55
--- /dev/null
+++ b/drivers/net/wireless/aic/aic8800_fdrv/lmac_mac.h
@@ -0,0 +1,437 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ ****************************************************************************************
+ *
+ * Copyright (C) 2020 AIC semiconductor.
+ *
+ * @brief MAC related definitions.
+ *
+ * Adapted from mac_types.h to used lmac_types.h instead of standard types
+ * eg: perl -pi -e '$_ =~ s/uint(\d{1,2})_t/u$1_l/g; \
+ *                  $_ =~ s/int(\d{1,2})_t/s$1_l/g; \
+ *                  $_ =~ s/CO_BIT/BIT/g;'
+ *
+ ****************************************************************************************
+ */
+
+#ifndef LMAC_MAC_H_
+#define LMAC_MAC_H_
+
+#include "lmac_types.h"
+
+/// Interface types
+enum mac_vif_type {
+	/// ESS STA interface
+	VIF_STA,
+	/// IBSS STA interface
+	VIF_IBSS,
+	/// AP interface
+	VIF_AP,
+	/// Mesh Point interface
+	VIF_MESH_POINT,
+	/// Monitor interface
+	VIF_MONITOR,
+	/// Unknown type
+	VIF_UNKNOWN
+};
+
+/// MAC address length in bytes.
+#define MAC_ADDR_LEN 6
+
+/// MAC address structure.
+struct mac_addr {
+	/// Array of 16-bit words that make up the MAC address.
+	u16 array[MAC_ADDR_LEN / 2];
+};
+
+/// SSID maximum length.
+#define MAC_SSID_LEN 32
+
+/// SSID.
+struct mac_ssid {
+	/// Actual length of the SSID.
+	u8 length;
+	/// Array containing the SSID name.
+	u8 array[MAC_SSID_LEN];
+};
+
+/// BSS type
+enum mac_bss_type {
+	INFRASTRUCTURE_MODE = 1,
+	INDEPENDENT_BSS_MODE,
+	ANY_BSS_MODE
+};
+
+/// Channel Band
+enum mac_chan_band {
+	/// 2.4GHz Band
+	PHY_BAND_2G4,
+	/// 5GHz band
+	PHY_BAND_5G,
+	/// Number of bands
+	PHY_BAND_MAX,
+};
+
+/// Operating Channel Bandwidth
+enum mac_chan_bandwidth {
+	/// 20MHz BW
+	PHY_CHNL_BW_20,
+	/// 40MHz BW
+	PHY_CHNL_BW_40,
+	/// 80MHz BW
+	PHY_CHNL_BW_80,
+	/// 160MHz BW
+	PHY_CHNL_BW_160,
+	/// 80+80MHz BW
+	PHY_CHNL_BW_80P80,
+	/// Reserved BW
+	PHY_CHNL_BW_OTHER,
+};
+
+/// max number of channels in the 2.4 GHZ band
+#define MAC_DOMAINCHANNEL_24G_MAX 14
+
+/// max number of channels in the 5 GHZ band
+#define MAC_DOMAINCHANNEL_5G_MAX 28
+
+/// Channel Flag
+enum mac_chan_flags {
+	/// Cannot initiate radiation on this channel
+	CHAN_NO_IR = BIT(0),
+	/// Channel is not allowed
+	CHAN_DISABLED = BIT(1),
+	/// Radar detection required on this channel
+	CHAN_RADAR = BIT(2),
+};
+
+/// Primary Channel definition
+struct mac_chan_def {
+	/// Frequency of the channel (in MHz)
+	u16 freq;
+	/// RF band (@ref mac_chan_band)
+	u8 band;
+	/// Additional information (@ref mac_chan_flags)
+	u8 flags;
+	/// Max transmit power allowed on this channel (dBm)
+	s8 tx_power;
+};
+
+/// Operating Channel
+struct mac_chan_op {
+	/// Band (@ref mac_chan_band)
+	u8 band;
+	/// Channel type (@ref mac_chan_bandwidth)
+	u8 type;
+	/// Frequency for Primary 20MHz channel (in MHz)
+	u16 prim20_freq;
+	/// Frequency center of the contiguous channel or center of Primary 80+80
+	/// (in MHz)
+	u16 center1_freq;
+	/// Frequency center of the non-contiguous secondary 80+80 (in MHz)
+	u16 center2_freq;
+	/// Max transmit power allowed on this channel (dBm)
+	s8 tx_power;
+	/// Additional information (@ref mac_chan_flags)
+	u8 flags;
+};
+
+/// Cipher suites (order is important as it is used by MACHW)
+enum mac_cipher_suite {
+	/// 00-0F-AC 1
+	MAC_CIPHER_WEP40 = 0,
+	/// 00-0F-AC 2
+	MAC_CIPHER_TKIP = 1,
+	/// 00-0F-AC 4
+	MAC_CIPHER_CCMP = 2,
+	/// 00-0F-AC 5
+	MAC_CIPHER_WEP104 = 3,
+	/// 00-14-72 1
+	MAC_CIPHER_WPI_SMS4 = 4,
+	/// 00-0F-AC 6  (aka AES_CMAC)
+	MAC_CIPHER_BIP_CMAC_128 = 5,
+
+	// following cipher are not supported by MACHW
+	/// 00-0F-AC 08
+	MAC_CIPHER_GCMP_128,
+	/// 00-0F-AC 09
+	MAC_CIPHER_GCMP_256,
+	/// 00-0F-AC 10
+	MAC_CIPHER_CCMP_256,
+	/// 00-0F-AC 11
+	MAC_CIPHER_BIP_GMAC_128,
+	/// 00-0F-AC 12
+	MAC_CIPHER_BIP_GMAC_256,
+	/// 00-0F-AC 13
+	MAC_CIPHER_BIP_CMAC_256,
+
+	MAC_CIPHER_INVALID = 0xFF
+};
+
+/// Authentication and Key Management suite
+enum mac_akm_suite {
+	/// No security
+	MAC_AKM_NONE,
+	/// Pre RSN (WEP or WPA)
+	MAC_AKM_PRE_RSN,
+	/// 00-0F-AC 1
+	MAC_AKM_8021X,
+	/// 00-0F-AC 2
+	MAC_AKM_PSK,
+	/// 00-0F-AC 3
+	MAC_AKM_FT_8021X,
+	/// 00-0F-AC 4
+	MAC_AKM_FT_PSK,
+	/// 00-0F-AC 5
+	MAC_AKM_8021X_SHA256,
+	/// 00-0F-AC 6
+	MAC_AKM_PSK_SHA256,
+	/// 00-0F-AC 7
+	MAC_AKM_TDLS,
+	/// 00-0F-AC 8
+	MAC_AKM_SAE,
+	/// 00-0F-AC 9
+	MAC_AKM_FT_OVER_SAE,
+	/// 00-0F-AC 11
+	MAC_AKM_8021X_SUITE_B,
+	/// 00-0F-AC 12
+	MAC_AKM_8021X_SUITE_B_192,
+	/// 00-0F-AC 14
+	MAC_AKM_FILS_SHA256,
+	/// 00-0F-AC 15
+	MAC_AKM_FILS_SHA384,
+	/// 00-0F-AC 16
+	MAC_AKM_FT_FILS_SHA256,
+	/// 00-0F-AC 17
+	MAC_AKM_FT_FILS_SHA384,
+	/// 00-0F-AC 18
+	MAC_AKM_OWE,
+
+	/// 00-14-72 1
+	MAC_AKM_WAPI_CERT,
+	/// 00-14-72 2
+	MAC_AKM_WAPI_PSK,
+};
+
+/// Scan result element, parsed from beacon or probe response frames.
+struct mac_scan_result {
+	/// Scan result is valid
+	bool valid_flag;
+	/// Network BSSID.
+	struct mac_addr bssid;
+	/// Network name.
+	struct mac_ssid ssid;
+	/// Network type (@ref mac_bss_type).
+	u16 bsstype;
+	/// Network channel.
+	struct mac_chan_def *chan;
+	/// Network beacon period (in TU).
+	u16 beacon_period;
+	/// Capability information
+	u16 cap_info;
+	/// Supported AKM (bit-field of @ref mac_akm_suite)
+	u32 akm;
+	/// Group cipher (bit-field of @ref mac_cipher_suite)
+	u16 group_cipher;
+	/// Group cipher (bit-field of @ref mac_cipher_suite)
+	u16 pairwise_cipher;
+	/// RSSI of the scanned BSS (in dBm)
+	s8 rssi;
+};
+
+/// Legacy rate 802.11 definitions
+enum mac_legacy_rates {
+	/// DSSS/CCK 1Mbps
+	MAC_RATE_1MBPS = 2,
+	/// DSSS/CCK 2Mbps
+	MAC_RATE_2MBPS = 4,
+	/// DSSS/CCK 5.5Mbps
+	MAC_RATE_5_5MBPS = 11,
+	/// OFDM 6Mbps
+	MAC_RATE_6MBPS = 12,
+	/// OFDM 9Mbps
+	MAC_RATE_9MBPS = 18,
+	/// DSSS/CCK 11Mbps
+	MAC_RATE_11MBPS = 22,
+	/// OFDM 12Mbps
+	MAC_RATE_12MBPS = 24,
+	/// OFDM 18Mbps
+	MAC_RATE_18MBPS = 36,
+	/// OFDM 24Mbps
+	MAC_RATE_24MBPS = 48,
+	/// OFDM 36Mbps
+	MAC_RATE_36MBPS = 72,
+	/// OFDM 48Mbps
+	MAC_RATE_48MBPS = 96,
+	/// OFDM 54Mbps
+	MAC_RATE_54MBPS = 108
+};
+
+/// BSS Membership Selector definitions
+enum mac_bss_membership {
+	/// HT PHY
+	MAC_BSS_MEMBERSHIP_HT_PHY = 127,
+	/// VHT PHY
+	MAC_BSS_MEMBERSHIP_VHT_PHY = 126,
+};
+
+/// MAC rateset maximum length
+#define MAC_RATESET_LEN 12
+
+/// Structure containing the legacy rateset of a station
+struct mac_rateset {
+	/// Number of legacy rates supported
+	u8 length;
+	/// Array of legacy rates
+	u8 array[MAC_RATESET_LEN];
+};
+
+/// MAC Security Key maximum length
+#define MAC_SEC_KEY_LEN 32 // TKIP keys 256 bits (max length) with MIC keys
+
+/// Structure defining a security key
+struct mac_sec_key {
+	/// Key material length
+	u8 length;
+	/// Key material
+	u32 array[MAC_SEC_KEY_LEN / 4];
+};
+
+/// Access Category enumeration
+enum mac_ac {
+	/// Background
+	AC_BK = 0,
+	/// Best-effort
+	AC_BE,
+	/// Video
+	AC_VI,
+	/// Voice
+	AC_VO,
+	/// Number of access categories
+	AC_MAX
+};
+
+/// Traffic ID enumeration
+enum mac_tid {
+	/// TID_0. Mapped to @ref AC_BE as per 802.11 standard.
+	TID_0,
+	/// TID_1. Mapped to @ref AC_BK as per 802.11 standard.
+	TID_1,
+	/// TID_2. Mapped to @ref AC_BK as per 802.11 standard.
+	TID_2,
+	/// TID_3. Mapped to @ref AC_BE as per 802.11 standard.
+	TID_3,
+	/// TID_4. Mapped to @ref AC_VI as per 802.11 standard.
+	TID_4,
+	/// TID_5. Mapped to @ref AC_VI as per 802.11 standard.
+	TID_5,
+	/// TID_6. Mapped to @ref AC_VO as per 802.11 standard.
+	TID_6,
+	/// TID_7. Mapped to @ref AC_VO as per 802.11 standard.
+	TID_7,
+	/// Non standard Management TID used internally
+	TID_MGT,
+	/// Number of TID supported
+	TID_MAX
+};
+
+/// MCS bitfield maximum size (in bytes)
+#define MAX_MCS_LEN 16 // 16 * 8 = 128
+
+/// MAC HT capability information element
+struct mac_htcapability {
+	/// HT capability information
+	__le16 ht_capa_info;
+	/// A-MPDU parameters
+	u8 a_mpdu_param;
+	/// Supported MCS
+	u8 mcs_rate[MAX_MCS_LEN];
+	/// HT extended capability information
+	__le16 ht_extended_capa;
+	/// Beamforming capability information
+	__le32 tx_beamforming_capa;
+	/// Antenna selection capability information
+	u8 asel_capa;
+};
+
+/// MAC VHT capability information element
+struct mac_vhtcapability {
+	/// VHT capability information
+	__le32 vht_capa_info;
+	/// RX MCS map
+	__le16 rx_mcs_map;
+	/// RX highest data rate
+	__le16 rx_highest;
+	/// TX MCS map
+	__le16 tx_mcs_map;
+	/// TX highest data rate
+	__le16 tx_highest;
+};
+
+/// Length (in bytes) of the MAC HE capability field
+#define MAC_HE_MAC_CAPA_LEN      6
+/// Length (in bytes) of the PHY HE capability field
+#define MAC_HE_PHY_CAPA_LEN      11
+/// Maximum length (in bytes) of the PPE threshold data
+#define MAC_HE_PPE_THRES_MAX_LEN 25
+
+/// Structure listing the per-NSS, per-BW supported MCS combinations
+struct mac_he_mcs_nss_supp {
+	/// per-NSS supported MCS in RX, for BW <= 80MHz
+	__le16 rx_mcs_80;
+	/// per-NSS supported MCS in TX, for BW <= 80MHz
+	__le16 tx_mcs_80;
+	/// per-NSS supported MCS in RX, for BW = 160MHz
+	__le16 rx_mcs_160;
+	/// per-NSS supported MCS in TX, for BW = 160MHz
+	__le16 tx_mcs_160;
+	/// per-NSS supported MCS in RX, for BW = 80+80MHz
+	__le16 rx_mcs_80p80;
+	/// per-NSS supported MCS in TX, for BW = 80+80MHz
+	__le16 tx_mcs_80p80;
+};
+
+/// MAC HE capability information element
+struct mac_hecapability {
+	/// MAC HE capabilities
+	u8 mac_cap_info[MAC_HE_MAC_CAPA_LEN];
+	/// PHY HE capabilities
+	u8 phy_cap_info[MAC_HE_PHY_CAPA_LEN];
+	/// Supported MCS combinations
+	struct mac_he_mcs_nss_supp mcs_supp;
+	/// PPE Thresholds data
+	u8 ppe_thres[MAC_HE_PPE_THRES_MAX_LEN];
+};
+
+/// Station flags
+enum mac_sta_flags {
+	/// Bit indicating that a STA has QoS (WMM) capability
+	STA_QOS_CAPA = BIT(0),
+	/// Bit indicating that a STA has HT capability
+	STA_HT_CAPA = BIT(1),
+	/// Bit indicating that a STA has VHT capability
+	STA_VHT_CAPA = BIT(2),
+	/// Bit indicating that a STA has MFP capability
+	STA_MFP_CAPA = BIT(3),
+	/// Bit indicating that the STA included the Operation Notification IE
+	STA_OPMOD_NOTIF = BIT(4),
+	/// Bit indicating that a STA has HE capability
+	STA_HE_CAPA = BIT(5),
+};
+
+/// Connection flags
+enum mac_connection_flags {
+	/// Flag indicating whether the control port is controlled by host or not
+	CONTROL_PORT_HOST = BIT(0),
+	/// Flag indicating whether the control port frame shall be sent unencrypted
+	CONTROL_PORT_NO_ENC = BIT(1),
+	/// Flag indicating whether HT and VHT shall be disabled or not
+	DISABLE_HT = BIT(2),
+	/// Flag indicating whether WPA or WPA2 authentication is in use
+	WPA_WPA2_IN_USE = BIT(3),
+	/// Flag indicating whether MFP is in use
+	MFP_IN_USE = BIT(4),
+	//  Flag indicating Roam
+	REASSOCIATION = BIT(5),
+};
+
+#endif // LMAC_MAC_H_
diff --git a/drivers/net/wireless/aic/aic8800_fdrv/lmac_msg.h b/drivers/net/wireless/aic/aic8800_fdrv/lmac_msg.h
new file mode 100644
index 0000000000000..8253fd8152fd1
--- /dev/null
+++ b/drivers/net/wireless/aic/aic8800_fdrv/lmac_msg.h
@@ -0,0 +1,3101 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ ****************************************************************************************
+ *
+ * Copyright (C) 2020 AIC semiconductor.
+ *
+ * @brief Main definitions for message exchanges with LMAC
+ *
+ ****************************************************************************************
+ */
+
+#ifndef LMAC_MSG_H_
+#define LMAC_MSG_H_
+
+#include "lmac_mac.h"
+
+/* Task identifiers for communication between LMAC and DRIVER */
+enum {
+	TASK_NONE = 0xff,
+
+	// MAC Management task.
+	TASK_MM = 0,
+	// DEBUG task
+	TASK_DBG,
+	/// SCAN task
+	TASK_SCAN,
+	/// TDLS task
+	TASK_TDLS,
+	/// SCANU task
+	TASK_SCANU,
+	/// ME task
+	TASK_ME,
+	/// SM task
+	TASK_SM,
+	/// APM task
+	TASK_APM,
+	/// BAM task
+	TASK_BAM,
+	/// MESH task
+	TASK_MESH,
+	/// RXU task
+	TASK_RXU,
+	/// RM_task
+	TASK_RM,
+	/// TWT task
+	TASK_TWT,
+#if defined CONFIG_RWNX_FULLMAC
+	// This is used to define the last task that is running on the EMB processor
+	TASK_LAST_EMB = TASK_TWT,
+#else
+#error "Need to define SOFTMAC or FULLMAC"
+#endif
+	// nX API task
+	TASK_API,
+	TASK_MAX,
+};
+
+/// For MAC HW States copied from "hal_machw.h"
+enum {
+	/// MAC HW IDLE State.
+	HW_IDLE = 0,
+	/// MAC HW RESERVED State.
+	HW_RESERVED,
+	/// MAC HW DOZE State.
+	HW_DOZE,
+	/// MAC HW ACTIVE State.
+	HW_ACTIVE
+};
+
+/// Power Save mode setting
+enum mm_ps_mode_state {
+	MM_PS_MODE_OFF,
+	MM_PS_MODE_ON,
+	MM_PS_MODE_ON_DYN,
+};
+
+/// Status/error codes used in the MAC software.
+enum {
+	CO_OK,
+	CO_FAIL,
+	CO_EMPTY,
+	CO_FULL,
+	CO_BAD_PARAM,
+	CO_NOT_FOUND,
+	CO_NO_MORE_ELT_AVAILABLE,
+	CO_NO_ELT_IN_USE,
+	CO_BUSY,
+	CO_OP_IN_PROGRESS,
+};
+
+/// Remain on channel operation codes
+enum mm_remain_on_channel_op {
+	MM_ROC_OP_START = 0,
+	MM_ROC_OP_CANCEL,
+};
+
+#define DRV_TASK_ID 100
+
+/// Message Identifier. The number of messages is limited to 0xFFFF.
+/// The message ID is divided in two parts:
+/// - bits[15..10] : task index (no more than 64 tasks supported).
+/// - bits[9..0] : message index (no more that 1024 messages per task).
+typedef u16 lmac_msg_id_t;
+
+typedef u16 lmac_task_id_t;
+
+/// Build the first message ID of a task.
+#define LMAC_FIRST_MSG(task) ((lmac_msg_id_t)((task) << 10))
+
+#define MSG_T(msg) ((lmac_task_id_t)((msg) >> 10))
+#define MSG_I(msg) ((msg) & ((1 << 10) - 1))
+
+/// Message structure.
+struct lmac_msg {
+	lmac_msg_id_t id;       ///< Message id.
+	lmac_task_id_t dest_id; ///< Destination kernel identifier.
+	lmac_task_id_t src_id;  ///< Source kernel identifier.
+	u16 param_len;          ///< Parameter embedded struct length.
+	u32 param[]; ///< Parameter embedded struct. Must be word-aligned.
+};
+
+/// List of messages related to the task.
+enum mm_msg_tag {
+	/// RESET Request.
+	MM_RESET_REQ = LMAC_FIRST_MSG(TASK_MM),
+	/// RESET Confirmation.
+	MM_RESET_CFM,
+	/// START Request.
+	MM_START_REQ,
+	/// START Confirmation.
+	MM_START_CFM,
+	/// Read Version Request.
+	MM_VERSION_REQ,
+	/// Read Version Confirmation.
+	MM_VERSION_CFM,
+	/// ADD INTERFACE Request.
+	MM_ADD_IF_REQ,
+	/// ADD INTERFACE Confirmation.
+	MM_ADD_IF_CFM,
+	/// REMOVE INTERFACE Request.
+	MM_REMOVE_IF_REQ,
+	/// REMOVE INTERFACE Confirmation.
+	MM_REMOVE_IF_CFM,
+	/// STA ADD Request.
+	MM_STA_ADD_REQ,
+	/// STA ADD Confirm.
+	MM_STA_ADD_CFM,
+	/// STA DEL Request.
+	MM_STA_DEL_REQ,
+	/// STA DEL Confirm.
+	MM_STA_DEL_CFM,
+	/// RX FILTER CONFIGURATION Request.
+	MM_SET_FILTER_REQ,
+	/// RX FILTER CONFIGURATION Confirmation.
+	MM_SET_FILTER_CFM,
+	/// CHANNEL CONFIGURATION Request.
+	MM_SET_CHANNEL_REQ,
+	/// CHANNEL CONFIGURATION Confirmation.
+	MM_SET_CHANNEL_CFM,
+	/// DTIM PERIOD CONFIGURATION Request.
+	MM_SET_DTIM_REQ,
+	/// DTIM PERIOD CONFIGURATION Confirmation.
+	MM_SET_DTIM_CFM,
+	/// BEACON INTERVAL CONFIGURATION Request.
+	MM_SET_BEACON_INT_REQ,
+	/// BEACON INTERVAL CONFIGURATION Confirmation.
+	MM_SET_BEACON_INT_CFM,
+	/// BASIC RATES CONFIGURATION Request.
+	MM_SET_BASIC_RATES_REQ,
+	/// BASIC RATES CONFIGURATION Confirmation.
+	MM_SET_BASIC_RATES_CFM,
+	/// BSSID CONFIGURATION Request.
+	MM_SET_BSSID_REQ,
+	/// BSSID CONFIGURATION Confirmation.
+	MM_SET_BSSID_CFM,
+	/// EDCA PARAMETERS CONFIGURATION Request.
+	MM_SET_EDCA_REQ,
+	/// EDCA PARAMETERS CONFIGURATION Confirmation.
+	MM_SET_EDCA_CFM,
+	/// ABGN MODE CONFIGURATION Request.
+	MM_SET_MODE_REQ,
+	/// ABGN MODE CONFIGURATION Confirmation.
+	MM_SET_MODE_CFM,
+	/// Request setting the VIF active state (i.e associated or AP started)
+	MM_SET_VIF_STATE_REQ,
+	/// Confirmation of the @ref MM_SET_VIF_STATE_REQ message.
+	MM_SET_VIF_STATE_CFM,
+	/// SLOT TIME PARAMETERS CONFIGURATION Request.
+	MM_SET_SLOTTIME_REQ,
+	/// SLOT TIME PARAMETERS CONFIGURATION Confirmation.
+	MM_SET_SLOTTIME_CFM,
+	/// Power Mode Change Request.
+	MM_SET_IDLE_REQ,
+	/// Power Mode Change Confirm.
+	MM_SET_IDLE_CFM,
+	/// KEY ADD Request.
+	MM_KEY_ADD_REQ,
+	/// KEY ADD Confirm.
+	MM_KEY_ADD_CFM,
+	/// KEY DEL Request.
+	MM_KEY_DEL_REQ,
+	/// KEY DEL Confirm.
+	MM_KEY_DEL_CFM,
+	/// Block Ack agreement info addition
+	MM_BA_ADD_REQ,
+	/// Block Ack agreement info addition confirmation
+	MM_BA_ADD_CFM,
+	/// Block Ack agreement info deletion
+	MM_BA_DEL_REQ,
+	/// Block Ack agreement info deletion confirmation
+	MM_BA_DEL_CFM,
+	/// Indication of the primary TBTT to the upper MAC. Upon the reception of
+	/// this
+	// message the upper MAC has to push the beacon(s) to the beacon
+	// transmission queue.
+	MM_PRIMARY_TBTT_IND,
+	/// Indication of the secondary TBTT to the upper MAC. Upon the reception of
+	/// this
+	// message the upper MAC has to push the beacon(s) to the beacon
+	// transmission queue.
+	MM_SECONDARY_TBTT_IND,
+	/// Request for changing the TX power
+	MM_SET_POWER_REQ,
+	/// Confirmation of the TX power change
+	MM_SET_POWER_CFM,
+	/// Request to the LMAC to trigger the embedded logic analyzer and forward
+	/// the debug
+	/// dump.
+	MM_DBG_TRIGGER_REQ,
+	/// Set Power Save mode
+	MM_SET_PS_MODE_REQ,
+	/// Set Power Save mode confirmation
+	MM_SET_PS_MODE_CFM,
+	/// Request to add a channel context
+	MM_CHAN_CTXT_ADD_REQ,
+	/// Confirmation of the channel context addition
+	MM_CHAN_CTXT_ADD_CFM,
+	/// Request to delete a channel context
+	MM_CHAN_CTXT_DEL_REQ,
+	/// Confirmation of the channel context deletion
+	MM_CHAN_CTXT_DEL_CFM,
+	/// Request to link a channel context to a VIF
+	MM_CHAN_CTXT_LINK_REQ,
+	/// Confirmation of the channel context link
+	MM_CHAN_CTXT_LINK_CFM,
+	/// Request to unlink a channel context from a VIF
+	MM_CHAN_CTXT_UNLINK_REQ,
+	/// Confirmation of the channel context unlink
+	MM_CHAN_CTXT_UNLINK_CFM,
+	/// Request to update a channel context
+	MM_CHAN_CTXT_UPDATE_REQ,
+	/// Confirmation of the channel context update
+	MM_CHAN_CTXT_UPDATE_CFM,
+	/// Request to schedule a channel context
+	MM_CHAN_CTXT_SCHED_REQ,
+	/// Confirmation of the channel context scheduling
+	MM_CHAN_CTXT_SCHED_CFM,
+	/// Request to change the beacon template in LMAC
+	MM_BCN_CHANGE_REQ,
+	/// Confirmation of the beacon change
+	MM_BCN_CHANGE_CFM,
+	/// Request to update the TIM in the beacon (i.e to indicate traffic
+	/// bufferized at AP)
+	MM_TIM_UPDATE_REQ,
+	/// Confirmation of the TIM update
+	MM_TIM_UPDATE_CFM,
+	/// Connection loss indication
+	MM_CONNECTION_LOSS_IND,
+	/// Channel context switch indication to the upper layers
+	MM_CHANNEL_SWITCH_IND,
+	/// Channel context pre-switch indication to the upper layers
+	MM_CHANNEL_PRE_SWITCH_IND,
+	/// Request to remain on channel or cancel remain on channel
+	MM_REMAIN_ON_CHANNEL_REQ,
+	/// Confirmation of the (cancel) remain on channel request
+	MM_REMAIN_ON_CHANNEL_CFM,
+	/// Remain on channel expired indication
+	MM_REMAIN_ON_CHANNEL_EXP_IND,
+	/// Indication of a PS state change of a peer device
+	MM_PS_CHANGE_IND,
+	/// Indication that some buffered traffic should be sent to the peer device
+	MM_TRAFFIC_REQ_IND,
+	/// Request to modify the STA Power-save mode options
+	MM_SET_PS_OPTIONS_REQ,
+	/// Confirmation of the PS options setting
+	MM_SET_PS_OPTIONS_CFM,
+	/// Indication of PS state change for a P2P VIF
+	MM_P2P_VIF_PS_CHANGE_IND,
+	/// Indication that CSA counter has been updated
+	MM_CSA_COUNTER_IND,
+	/// Channel occupation report indication
+	MM_CHANNEL_SURVEY_IND,
+	/// Message containing Beamformer Information
+	MM_BFMER_ENABLE_REQ,
+	/// Request to Start/Stop/Update NOA - GO Only
+	MM_SET_P2P_NOA_REQ,
+	/// Request to Start/Stop/Update Opportunistic PS - GO Only
+	MM_SET_P2P_OPPPS_REQ,
+	/// Start/Stop/Update NOA Confirmation
+	MM_SET_P2P_NOA_CFM,
+	/// Start/Stop/Update Opportunistic PS Confirmation
+	MM_SET_P2P_OPPPS_CFM,
+	/// P2P NoA Update Indication - GO Only
+	MM_P2P_NOA_UPD_IND,
+	/// Request to set RSSI threshold and RSSI hysteresis
+	MM_CFG_RSSI_REQ,
+	/// Indication that RSSI level is below or above the threshold
+	MM_RSSI_STATUS_IND,
+	/// Indication that CSA is done
+	MM_CSA_FINISH_IND,
+	/// Indication that CSA is in prorgess (resp. done) and traffic must be
+	/// stopped (resp. restarted)
+	MM_CSA_TRAFFIC_IND,
+	/// Request to update the group information of a station
+	MM_MU_GROUP_UPDATE_REQ,
+	/// Confirmation of the @ref MM_MU_GROUP_UPDATE_REQ message
+	MM_MU_GROUP_UPDATE_CFM,
+	/// Request to initialize the antenna diversity algorithm
+	MM_ANT_DIV_INIT_REQ,
+	/// Request to stop the antenna diversity algorithm
+	MM_ANT_DIV_STOP_REQ,
+	/// Request to update the antenna switch status
+	MM_ANT_DIV_UPDATE_REQ,
+	/// Request to switch the antenna connected to path_0
+	MM_SWITCH_ANTENNA_REQ,
+	/// Indication that a packet loss has occurred
+	MM_PKTLOSS_IND,
+
+	MM_SET_ARPOFFLOAD_REQ,
+	MM_SET_ARPOFFLOAD_CFM,
+	MM_SET_AGG_DISABLE_REQ,
+	MM_SET_AGG_DISABLE_CFM,
+	MM_SET_COEX_REQ,
+	MM_SET_COEX_CFM,
+	MM_SET_RF_CONFIG_REQ,
+	MM_SET_RF_CONFIG_CFM,
+	MM_SET_RF_CALIB_REQ,
+	MM_SET_RF_CALIB_CFM,
+
+	/// MU EDCA PARAMETERS Configuration Request.
+	MM_SET_MU_EDCA_REQ,
+	/// MU EDCA PARAMETERS Configuration Confirmation.
+	MM_SET_MU_EDCA_CFM,
+	/// UORA PARAMETERS Configuration Request.
+	MM_SET_UORA_REQ,
+	/// UORA PARAMETERS Configuration Confirmation.
+	MM_SET_UORA_CFM,
+	/// TXOP RTS THRESHOLD Configuration Request.
+	MM_SET_TXOP_RTS_THRES_REQ,
+	/// TXOP RTS THRESHOLD Configuration Confirmation.
+	MM_SET_TXOP_RTS_THRES_CFM,
+	/// HE BSS Color Configuration Request.
+	MM_SET_BSS_COLOR_REQ,
+	/// HE BSS Color Configuration Confirmation.
+	MM_SET_BSS_COLOR_CFM,
+
+	MM_GET_MAC_ADDR_REQ,
+	MM_GET_MAC_ADDR_CFM,
+
+	MM_GET_STA_INFO_REQ,
+	MM_GET_STA_INFO_CFM,
+
+	MM_SET_TXPWR_IDX_LVL_REQ,
+	MM_SET_TXPWR_IDX_LVL_CFM,
+
+	MM_SET_TXPWR_OFST_REQ,
+	MM_SET_TXPWR_OFST_CFM,
+
+	MM_SET_STACK_START_REQ,
+	MM_SET_STACK_START_CFM,
+
+	MM_APM_STALOSS_IND,
+
+	MM_SET_VENDOR_HWCONFIG_REQ,
+	MM_SET_VENDOR_HWCONFIG_CFM,
+
+	MM_GET_FW_VERSION_REQ,
+	MM_GET_FW_VERSION_CFM,
+
+	MM_SET_RESUME_RESTORE_REQ,
+	MM_SET_RESUME_RESTORE_CFM,
+
+	MM_GET_WIFI_DISABLE_REQ,
+	MM_GET_WIFI_DISABLE_CFM,
+
+	MM_CFG_RSSI_CFM,
+
+	MM_SET_VENDOR_SWCONFIG_REQ,
+	MM_SET_VENDOR_SWCONFIG_CFM,
+
+	MM_SET_TXPWR_LVL_ADJ_REQ,
+	MM_SET_TXPWR_LVL_ADJ_CFM,
+
+	MM_RADAR_DETECT_IND,
+
+	MM_SET_APF_PROG_REQ,
+	MM_SET_APF_PROG_CFM,
+
+	MM_GET_APF_PROG_REQ,
+	MM_GET_APF_PROG_CFM,
+
+	/// arp announcement frame reception indication
+	MM_ARP_ANNOUNCEMENT_IND,
+
+	/// MAX number of messages
+	MM_MAX,
+};
+
+/// Interface types
+enum {
+	/// ESS STA interface
+	MM_STA,
+	/// IBSS STA interface
+	MM_IBSS,
+	/// AP interface
+	MM_AP,
+	// Mesh Point interface
+	MM_MESH_POINT,
+	// Monitor interface
+	MM_MONITOR,
+};
+
+/// BA agreement types
+enum {
+	/// BlockAck agreement for TX
+	BA_AGMT_TX,
+	/// BlockAck agreement for RX
+	BA_AGMT_RX,
+};
+
+/// BA agreement related status
+enum {
+	/// Correct BA agreement establishment
+	BA_AGMT_ESTABLISHED,
+	/// BA agreement already exists for STA+TID requested, cannot override it
+	/// (should have been deleted first)
+	BA_AGMT_ALREADY_EXISTS,
+	/// Correct BA agreement deletion
+	BA_AGMT_DELETED,
+	/// BA agreement for the (STA, TID) doesn't exist so nothing to delete
+	BA_AGMT_DOESNT_EXIST,
+};
+
+/// Features supported by LMAC - Positions
+enum mm_features {
+	/// Beaconing
+	MM_FEAT_BCN_BIT = 0,
+	/*
+	 *	/// Autonomous Beacon Transmission
+	 *	MM_FEAT_AUTOBCN_BIT,
+	 *	/// Scan in LMAC
+	 *	MM_FEAT_HWSCAN_BIT,
+	 *	/// Connection Monitoring
+	 *	MM_FEAT_CMON_BIT,
+	 *	/// Multi Role
+	 *	MM_FEAT_MROLE_BIT,
+	 */
+	/// Radar Detection
+	MM_FEAT_RADAR_BIT,
+	/// Power Save
+	MM_FEAT_PS_BIT,
+	/// UAPSD
+	MM_FEAT_UAPSD_BIT,
+	/// DPSM
+	//  MM_FEAT_DPSM_BIT,
+	/// A-MPDU
+	MM_FEAT_AMPDU_BIT,
+	/// A-MSDU
+	MM_FEAT_AMSDU_BIT,
+	/// Channel Context
+	//  MM_FEAT_CHNL_CTXT_BIT,
+	/// Packet reordering
+	//  MM_FEAT_REORD_BIT,
+	/// P2P
+	MM_FEAT_P2P_BIT,
+	/// P2P Go
+	MM_FEAT_P2P_GO_BIT,
+	/// UMAC Present
+	MM_FEAT_UMAC_BIT,
+	/// VHT support
+	MM_FEAT_VHT_BIT,
+	/// Beamformee
+	MM_FEAT_BFMEE_BIT,
+	/// Beamformer
+	MM_FEAT_BFMER_BIT,
+	/// WAPI
+	MM_FEAT_WAPI_BIT,
+	/// MFP
+	MM_FEAT_MFP_BIT,
+	/// Mu-MIMO RX support
+	MM_FEAT_MU_MIMO_RX_BIT,
+	/// Mu-MIMO TX support
+	MM_FEAT_MU_MIMO_TX_BIT,
+	/// Wireless Mesh Networking
+	MM_FEAT_MESH_BIT,
+	/// TDLS support
+	MM_FEAT_TDLS_BIT,
+	/// Antenna Diversity support
+	MM_FEAT_ANT_DIV_BIT,
+	/// UF support
+	MM_FEAT_UF_BIT,
+	/// A-MSDU maximum size (bit0)
+	MM_AMSDU_MAX_SIZE_BIT0,
+	/// A-MSDU maximum size (bit1)
+	MM_AMSDU_MAX_SIZE_BIT1,
+	/// MON_DATA support
+	MM_FEAT_MON_DATA_BIT,
+	/// HE (802.11ax) support
+	MM_FEAT_HE_BIT,
+	/// TWT support
+	MM_FEAT_TWT_BIT,
+};
+
+/// Maximum number of words in the configuration buffer
+#define PHY_CFG_BUF_SIZE 16
+
+/// Structure containing the parameters of the PHY configuration
+struct phy_cfg_tag {
+	/// Buffer containing the parameters specific for the PHY used
+	u32 parameters[PHY_CFG_BUF_SIZE];
+};
+
+/// Structure containing the parameters of the Trident PHY configuration
+struct phy_trd_cfg_tag {
+	/// MDM type(nxm)(upper nibble) and MDM2RF path mapping(lower nibble)
+	u8 path_mapping;
+	/// TX DC offset compensation
+	u32 tx_dc_off_comp;
+};
+
+/// Structure containing the parameters of the Karst PHY configuration
+struct phy_karst_cfg_tag {
+	/// TX IQ mismatch compensation in 2.4GHz
+	u32 tx_iq_comp_2_4G[2];
+	/// RX IQ mismatch compensation in 2.4GHz
+	u32 rx_iq_comp_2_4G[2];
+	/// TX IQ mismatch compensation in 5GHz
+	u32 tx_iq_comp_5G[2];
+	/// RX IQ mismatch compensation in 5GHz
+	u32 rx_iq_comp_5G[2];
+	/// RF path used by default (0 or 1)
+	u8 path_used;
+};
+
+/// Structure containing the parameters of the @ref MM_START_REQ message
+struct mm_start_req {
+	/// PHY configuration
+	struct phy_cfg_tag phy_cfg;
+	/// UAPSD timeout
+	u32 uapsd_timeout;
+	/// Local LP clock accuracy (in ppm)
+	u16 lp_clk_accuracy;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_CHANNEL_REQ message
+struct mm_set_channel_req {
+	/// Channel information
+	struct mac_chan_op chan;
+	/// Index of the RF for which the channel has to be set (0: operating
+	/// (primary), 1: secondary RF (used for additional radar detection). This
+	/// parameter is reserved if no secondary RF is available in the system
+	u8 index;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_CHANNEL_CFM message
+struct mm_set_channel_cfm {
+	/// Radio index to be used in policy table
+	u8 radio_idx;
+	/// TX power configured (in dBm)
+	s8 power;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_DTIM_REQ message
+struct mm_set_dtim_req {
+	/// DTIM period
+	u8 dtim_period;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_POWER_REQ message
+struct mm_set_power_req {
+	/// Index of the interface for which the parameter is configured
+	u8 inst_nbr;
+	/// TX power (in dBm)
+	s8 power;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_POWER_CFM message
+struct mm_set_power_cfm {
+	/// Radio index to be used in policy table
+	u8 radio_idx;
+	/// TX power configured (in dBm)
+	s8 power;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_BEACON_INT_REQ
+/// message
+struct mm_set_beacon_int_req {
+	/// Beacon interval
+	u16 beacon_int;
+	/// Index of the interface for which the parameter is configured
+	u8 inst_nbr;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_BASIC_RATES_REQ
+/// message
+struct mm_set_basic_rates_req {
+	/// Basic rate set (as expected by bssBasicRateSet field of Rates MAC HW
+	/// register)
+	u32 rates;
+	/// Index of the interface for which the parameter is configured
+	u8 inst_nbr;
+	/// Band on which the interface will operate
+	u8 band;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_BSSID_REQ message
+struct mm_set_bssid_req {
+	/// BSSID to be configured in HW
+	struct mac_addr bssid;
+	/// Index of the interface for which the parameter is configured
+	u8 inst_nbr;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_FILTER_REQ message
+struct mm_set_filter_req {
+	/// RX filter to be put into rxCntrlReg HW register
+	u32 filter;
+};
+
+/// Structure containing the parameters of the @ref MM_ADD_IF_REQ message.
+struct mm_add_if_req {
+	/// Type of the interface (AP, STA, ADHOC, ...)
+	u8 type;
+	/// MAC ADDR of the interface to start
+	struct mac_addr addr;
+	/// P2P Interface
+	bool p2p;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_EDCA_REQ message
+struct mm_set_edca_req {
+	/// EDCA parameters of the queue (as expected by edcaACxReg HW register)
+	u32 ac_param;
+	/// Flag indicating if UAPSD can be used on this queue
+	bool uapsd;
+	/// HW queue for which the parameters are configured
+	u8 hw_queue;
+	/// Index of the interface for which the parameters are configured
+	u8 inst_nbr;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_MU_EDCA_REQ message
+struct mm_set_mu_edca_req {
+	/// MU EDCA parameters of the different HE queues
+	u32 param[AC_MAX];
+};
+
+/// Structure containing the parameters of the @ref MM_SET_UORA_REQ message
+struct mm_set_uora_req {
+	/// Minimum exponent of OFDMA Contention Window.
+	u8 eocw_min;
+	/// Maximum exponent of OFDMA Contention Window.
+	u8 eocw_max;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_TXOP_RTS_THRES_REQ
+/// message
+struct mm_set_txop_rts_thres_req {
+	/// TXOP RTS threshold
+	u16 txop_dur_rts_thres;
+	/// Index of the interface for which the parameter is configured
+	u8 inst_nbr;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_BSS_COLOR_REQ message
+struct mm_set_bss_color_req {
+	/// HE BSS color, formatted as per BSS_COLOR MAC HW register
+	u32 bss_color;
+};
+
+struct mm_set_idle_req {
+	u8 hw_idle;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_SLOTTIME_REQ message
+struct mm_set_slottime_req {
+	/// Slot time expressed in us
+	u8 slottime;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_MODE_REQ message
+struct mm_set_mode_req {
+	/// abgnMode field of macCntrl1Reg register
+	u8 abgnmode;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_VIF_STATE_REQ message
+struct mm_set_vif_state_req {
+	/// Association Id received from the AP (valid only if the VIF is of STA
+	/// type)
+	__le16 aid;
+	/// Flag indicating if the VIF is active or not
+	bool active;
+	/// Interface index
+	u8 inst_nbr;
+};
+
+/// Structure containing the parameters of the @ref MM_ADD_IF_CFM message.
+struct mm_add_if_cfm {
+	/// Status of operation (different from 0 if unsuccessful)
+	u8 status;
+	/// Interface index assigned by the LMAC
+	u8 inst_nbr;
+};
+
+/// Structure containing the parameters of the @ref MM_REMOVE_IF_REQ message.
+struct mm_remove_if_req {
+	/// Interface index assigned by the LMAC
+	u8 inst_nbr;
+};
+
+/// Structure containing the parameters of the @ref MM_VERSION_CFM message.
+struct mm_version_cfm {
+	/// Version of the LMAC FW
+	u32 version_lmac;
+	/// Version1 of the MAC HW (as encoded in version1Reg MAC HW register)
+	u32 version_machw_1;
+	/// Version2 of the MAC HW (as encoded in version2Reg MAC HW register)
+	u32 version_machw_2;
+	/// Version1 of the PHY (depends on actual PHY)
+	u32 version_phy_1;
+	/// Version2 of the PHY (depends on actual PHY)
+	u32 version_phy_2;
+	/// Supported Features
+	u32 features;
+	/// Maximum number of supported stations
+	u16 max_sta_nb;
+	/// Maximum number of supported virtual interfaces
+	u8 max_vif_nb;
+};
+
+/// Structure containing the parameters of the @ref MM_STA_ADD_REQ message.
+struct mm_sta_add_req {
+	/// Bitfield showing some capabilities of the STA (@ref enum mac_sta_flags)
+	u32 capa_flags;
+	/// Maximum A-MPDU size, in bytes, for HE frames
+	u32 ampdu_size_max_he;
+	/// Maximum A-MPDU size, in bytes, for VHT frames
+	u32 ampdu_size_max_vht;
+	/// PAID/GID
+	u32 paid_gid;
+	/// Maximum A-MPDU size, in bytes, for HT frames
+	u16 ampdu_size_max_ht;
+	/// MAC address of the station to be added
+	struct mac_addr mac_addr;
+	/// A-MPDU spacing, in us
+	u8 ampdu_spacing_min;
+	/// Interface index
+	u8 inst_nbr;
+	/// TDLS station
+	bool tdls_sta;
+	/// Indicate if the station is TDLS link initiator station
+	bool tdls_sta_initiator;
+	/// Indicate if the TDLS Channel Switch is allowed
+	bool tdls_chsw_allowed;
+	/// nonTransmitted BSSID index, set to the BSSID index in case the STA added
+	/// is an AP that is a nonTransmitted BSSID. Should be set to 0 otherwise
+	u8 bssid_index;
+	/// Maximum BSSID indicator, valid if the STA added is an AP that is a
+	/// nonTransmitted BSSID
+	u8 max_bssid_ind;
+};
+
+/// Structure containing the parameters of the @ref MM_STA_ADD_CFM message.
+struct mm_sta_add_cfm {
+	/// Status of the operation (different from 0 if unsuccessful)
+	u8 status;
+	/// Index assigned by the LMAC to the newly added station
+	u8 sta_idx;
+	/// MAC HW index of the newly added station
+	u8 hw_sta_idx;
+};
+
+/// Structure containing the parameters of the @ref MM_STA_DEL_REQ message.
+struct mm_sta_del_req {
+	/// Index of the station to be deleted
+	u8 sta_idx;
+};
+
+/// Structure containing the parameters of the @ref MM_STA_DEL_CFM message.
+struct mm_sta_del_cfm {
+	/// Status of the operation (different from 0 if unsuccessful)
+	u8 status;
+};
+
+/// Structure containing the parameters of the SET_POWER_MODE REQ message.
+struct mm_setpowermode_req {
+	u8 mode;
+	u8 sta_idx;
+};
+
+/// Structure containing the parameters of the SET_POWER_MODE CFM message.
+struct mm_setpowermode_cfm {
+	u8 status;
+};
+
+/// Structure containing the parameters of the @ref MM_KEY_ADD REQ message.
+struct mm_key_add_req {
+	/// Key index (valid only for default keys)
+	u8 key_idx;
+	/// STA index (valid only for pairwise or mesh group keys)
+	u8 sta_idx;
+	/// Key material
+	struct mac_sec_key key;
+	/// Cipher suite (WEP64, WEP128, TKIP, CCMP)
+	u8 cipher_suite;
+	/// Index of the interface for which the key is set (valid only for default
+	/// keys or mesh group keys)
+	u8 inst_nbr;
+	/// A-MSDU SPP parameter
+	u8 spp;
+	/// Indicate if provided key is a pairwise key or not
+	bool pairwise;
+};
+
+/// Structure containing the parameters of the @ref MM_KEY_ADD_CFM message.
+struct mm_key_add_cfm {
+	/// Status of the operation (different from 0 if unsuccessful)
+	u8 status;
+	/// HW index of the key just added
+	u8 hw_key_idx;
+};
+
+/// Structure containing the parameters of the @ref MM_KEY_DEL_REQ message.
+struct mm_key_del_req {
+	/// HW index of the key to be deleted
+	u8 hw_key_idx;
+};
+
+/// Structure containing the parameters of the @ref MM_BA_ADD_REQ message.
+struct mm_ba_add_req {
+	/// Type of agreement (0: TX, 1: RX)
+	u8 type;
+	/// Index of peer station with which the agreement is made
+	u8 sta_idx;
+	/// TID for which the agreement is made with peer station
+	u8 tid;
+	/// Buffer size - number of MPDUs that can be held in its buffer per TID
+	u8 bufsz;
+	/// Start sequence number negotiated during BA setup - the one in first
+	/// aggregated MPDU counts more
+	u16 ssn;
+};
+
+/// Structure containing the parameters of the @ref MM_BA_ADD_CFM message.
+struct mm_ba_add_cfm {
+	/// Index of peer station for which the agreement is being confirmed
+	u8 sta_idx;
+	/// TID for which the agreement is being confirmed
+	u8 tid;
+	/// Status of ba establishment
+	u8 status;
+};
+
+/// Structure containing the parameters of the @ref MM_BA_DEL_REQ message.
+struct mm_ba_del_req {
+	/// Type of agreement (0: TX, 1: RX)
+	u8 type;
+	/// Index of peer station for which the agreement is being deleted
+	u8 sta_idx;
+	/// TID for which the agreement is being deleted
+	u8 tid;
+};
+
+/// Structure containing the parameters of the @ref MM_BA_DEL_CFM message.
+struct mm_ba_del_cfm {
+	/// Index of peer station for which the agreement deletion is being
+	/// confirmed
+	u8 sta_idx;
+	/// TID for which the agreement deletion is being confirmed
+	u8 tid;
+	/// Status of ba deletion
+	u8 status;
+};
+
+/// Structure containing the parameters of the @ref MM_CHAN_CTXT_ADD_REQ message
+struct mm_chan_ctxt_add_req {
+	/// Operating channel
+	struct mac_chan_op chan;
+};
+
+/// Structure containing the parameters of the @ref MM_CHAN_CTXT_ADD_REQ message
+struct mm_chan_ctxt_add_cfm {
+	/// Status of the addition
+	u8 status;
+	/// Index of the new channel context
+	u8 index;
+};
+
+/// Structure containing the parameters of the @ref MM_CHAN_CTXT_DEL_REQ message
+struct mm_chan_ctxt_del_req {
+	/// Index of the new channel context to be deleted
+	u8 index;
+};
+
+/// Structure containing the parameters of the @ref MM_CHAN_CTXT_LINK_REQ
+/// message
+struct mm_chan_ctxt_link_req {
+	/// VIF index
+	u8 vif_index;
+	/// Channel context index
+	u8 chan_index;
+	/// Indicate if this is a channel switch (unlink current ctx first if true)
+	u8 chan_switch;
+};
+
+/// Structure containing the parameters of the @ref MM_CHAN_CTXT_UNLINK_REQ
+/// message
+struct mm_chan_ctxt_unlink_req {
+	/// VIF index
+	u8 vif_index;
+};
+
+/// Structure containing the parameters of the @ref MM_CHAN_CTXT_UPDATE_REQ
+/// message
+struct mm_chan_ctxt_update_req {
+	/// Channel context index
+	u8 chan_index;
+	/// New channel information
+	struct mac_chan_op chan;
+};
+
+/// Structure containing the parameters of the @ref MM_CHAN_CTXT_SCHED_REQ
+/// message
+struct mm_chan_ctxt_sched_req {
+	/// VIF index
+	u8 vif_index;
+	/// Channel context index
+	u8 chan_index;
+	/// Type of the scheduling request (0: normal scheduling, 1: derogatory
+	/// scheduling)
+	u8 type;
+};
+
+/// Structure containing the parameters of the @ref MM_CHANNEL_SWITCH_IND
+/// message
+struct mm_channel_switch_ind {
+	/// Index of the channel context we will switch to
+	u8 chan_index;
+	/// Indicate if the switch has been triggered by a Remain on channel request
+	bool roc;
+	/// VIF on which remain on channel operation has been started (if roc == 1)
+	u8 vif_index;
+	/// Indicate if the switch has been triggered by a TDLS Remain on channel
+	/// request
+	bool roc_tdls;
+};
+
+/// Structure containing the parameters of the @ref MM_CHANNEL_PRE_SWITCH_IND
+/// message
+struct mm_channel_pre_switch_ind {
+	/// Index of the channel context we will switch to
+	u8 chan_index;
+};
+
+/// Structure containing the parameters of the @ref MM_CONNECTION_LOSS_IND
+/// message.
+struct mm_connection_loss_ind {
+	/// VIF instance number
+	u8 inst_nbr;
+};
+
+/// Structure containing the parameters of the @ref MM_DBG_TRIGGER_REQ message.
+struct mm_dbg_trigger_req {
+	/// Error trace to be reported by the LMAC
+	char error[64];
+};
+
+/// Structure containing the parameters of the @ref MM_SET_PS_MODE_REQ message.
+struct mm_set_ps_mode_req {
+	/// Power Save is activated or deactivated
+	u8 new_state;
+};
+
+/// Structure containing the parameters of the @ref MM_BCN_CHANGE_REQ message.
+#define BCN_MAX_CSA_CPT 2
+struct mm_bcn_change_req {
+	/// Pointer, in host memory, to the new beacon template
+	u32 bcn_ptr;
+	/// Length of the beacon template
+	u16 bcn_len;
+	/// Offset of the TIM IE in the beacon
+	u16 tim_oft;
+	/// Length of the TIM IE
+	u8 tim_len;
+	/// Index of the VIF for which the beacon is updated
+	u8 inst_nbr;
+	/// Offset of CSA (channel switch announcement) counters (0 means no
+	/// counter)
+	u8 csa_oft[BCN_MAX_CSA_CPT];
+};
+
+/// Structure containing the parameters of the @ref MM_TIM_UPDATE_REQ message.
+struct mm_tim_update_req {
+	/// Association ID of the STA the bit of which has to be updated (0 for
+	/// BC/MC traffic)
+	u16 aid;
+	/// Flag indicating the availability of data packets for the given STA
+	u8 tx_avail;
+	/// Index of the VIF for which the TIM is updated
+	u8 inst_nbr;
+};
+
+/// Structure containing the parameters of the @ref MM_REMAIN_ON_CHANNEL_REQ
+/// message.
+struct mm_remain_on_channel_req {
+	/// Operation Code
+	u8 op_code;
+	/// VIF Index
+	u8 vif_index;
+	/// Band (2.4GHz or 5GHz)
+	u8 band;
+	/// Channel type: 20,40,80,160 or 80+80 MHz
+	u8 type;
+	/// Frequency for Primary 20MHz channel (in MHz)
+	u16 prim20_freq;
+	/// Frequency for Center of the contiguous channel or center of Primary
+	/// 80+80
+	u16 center1_freq;
+	/// Frequency for Center of the non-contiguous secondary 80+80
+	u16 center2_freq;
+	/// Duration (in ms)
+	u32 duration_ms;
+	/// TX power (in dBm)
+	s8 tx_power;
+};
+
+/// Structure containing the parameters of the @ref MM_REMAIN_ON_CHANNEL_CFM
+/// message
+struct mm_remain_on_channel_cfm {
+	/// Operation Code
+	u8 op_code;
+	/// Status of the operation
+	u8 status;
+	/// Channel Context index
+	u8 chan_ctxt_index;
+};
+
+/// Structure containing the parameters of the @ref MM_REMAIN_ON_CHANNEL_EXP_IND
+/// message
+struct mm_remain_on_channel_exp_ind {
+	/// VIF Index
+	u8 vif_index;
+	/// Channel Context index
+	u8 chan_ctxt_index;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_UAPSD_TMR_REQ
+/// message.
+struct mm_set_uapsd_tmr_req {
+	/// action: Start or Stop the timer
+	u8 action;
+	/// timeout value, in milliseconds
+	u32 timeout;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_UAPSD_TMR_CFM
+/// message.
+struct mm_set_uapsd_tmr_cfm {
+	/// Status of the operation (different from 0 if unsuccessful)
+	u8 status;
+};
+
+/// Structure containing the parameters of the @ref MM_PS_CHANGE_IND message
+struct mm_ps_change_ind {
+	/// Index of the peer device that is switching its PS state
+	u8 sta_idx;
+	/// New PS state of the peer device (0: active, 1: sleeping)
+	u8 ps_state;
+};
+
+/// Structure containing the parameters of the @ref MM_P2P_VIF_PS_CHANGE_IND
+/// message
+struct mm_p2p_vif_ps_change_ind {
+	/// Index of the P2P VIF that is switching its PS state
+	u8 vif_index;
+	/// New PS state of the P2P VIF interface (0: active, 1: sleeping)
+	u8 ps_state;
+};
+
+/// Structure containing the parameters of the @ref MM_TRAFFIC_REQ_IND message
+struct mm_traffic_req_ind {
+	/// Index of the peer device that needs traffic
+	u8 sta_idx;
+	/// Number of packets that need to be sent (if 0, all buffered traffic shall
+	/// be sent and if set to @ref PS_SP_INTERRUPTED, it means that current
+	/// service period has been interrupted)
+	u8 pkt_cnt;
+	/// Flag indicating if the traffic request concerns U-APSD queues or not
+	bool uapsd;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_PS_OPTIONS_REQ
+/// message.
+struct mm_set_ps_options_req {
+	/// VIF Index
+	u8 vif_index;
+	/// Listen interval (0 if wake up shall be based on DTIM period)
+	u16 listen_interval;
+	/// Flag indicating if we shall listen the BC/MC traffic or not
+	bool dont_listen_bc_mc;
+};
+
+/// Structure containing the parameters of the @ref MM_CSA_COUNTER_IND message
+struct mm_csa_counter_ind {
+	/// Index of the VIF
+	u8 vif_index;
+	/// Updated CSA counter value
+	u8 csa_count;
+};
+
+/// Structure containing the parameters of the @ref MM_CHANNEL_SURVEY_IND
+/// message
+struct mm_channel_survey_ind {
+	/// Frequency of the channel
+	u16 freq;
+	/// Noise in dbm
+	s8 noise_dbm;
+	/// Amount of time spent of the channel (in ms)
+	u32 chan_time_ms;
+	/// Amount of time the primary channel was sensed busy
+	u32 chan_time_busy_ms;
+};
+
+/// Structure containing the parameters of the @ref MM_BFMER_ENABLE_REQ message.
+struct mm_bfmer_enable_req {
+	/**
+	 * Address of the beamforming report space allocated in host memory
+	 * (Valid only if vht_su_bfmee is true)
+	 */
+	u32 host_bfr_addr;
+	/**
+	 * Size of the beamforming report space allocated in host memory. This space
+	 * should be twice the maximum size of the expected beamforming reports as
+	 * the FW will divide it in two in order to be able to upload a new report
+	 * while another one is used in transmission
+	 */
+	u16 host_bfr_size;
+	/// AID
+	u16 aid;
+	/// Station Index
+	u8 sta_idx;
+	/// Maximum number of spatial streams the station can receive
+	u8 rx_nss;
+	/**
+	 * Indicate if peer STA is MU Beamformee (VHT) capable
+	 * (Valid only if vht_su_bfmee is true)
+	 */
+	bool vht_mu_bfmee;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_P2P_NOA_REQ message.
+struct mm_set_p2p_noa_req {
+	/// VIF Index
+	u8 vif_index;
+	/// Allocated NOA Instance Number - Valid only if count = 0
+	u8 noa_inst_nb;
+	/// Count
+	u8 count;
+	/// Indicate if NoA can be paused for traffic reason
+	bool dyn_noa;
+	/// Duration (in us)
+	u32 duration_us;
+	/// Interval (in us)
+	u32 interval_us;
+	/// Start Time offset from next TBTT (in us)
+	u32 start_offset;
+};
+
+#ifdef AICWF_ARP_OFFLOAD
+struct mm_set_arpoffload_en_req {
+	u32 ipaddr;
+	u8 enable;
+	u8 vif_idx;
+};
+
+struct mm_set_arpoffload_en_cfm {
+	u8 status;
+};
+#endif
+
+struct mm_set_agg_disable_req {
+	u8 disable;
+	u8 staidx;
+	u8 disable_rx;
+};
+
+struct mm_set_coex_req {
+	u8 bt_on;
+	u8 disable_coexnull;
+	u8 enable_nullcts;
+	u8 enable_periodic_timer;
+	u8 coex_timeslot_set;
+	u32 coex_timeslot[2];
+};
+
+struct mm_set_rf_config_req {
+	u8 table_sel;
+	u8 table_ofst;
+	u8 table_num;
+	u8 deft_page;
+	u32 data[64];
+};
+
+struct mm_set_rf_calib_req {
+	u32 cal_cfg_24g;
+	u32 cal_cfg_5g;
+	u32 param_alpha;
+	u32 bt_calib_en;
+	u32 bt_calib_param;
+	u8 xtal_cap;
+	u8 xtal_cap_fine;
+};
+
+struct mm_set_rf_calib_cfm {
+	u32 rxgain_24g_addr;
+	u32 rxgain_5g_addr;
+	u32 txgain_24g_addr;
+	u32 txgain_5g_addr;
+};
+
+struct mm_get_mac_addr_req {
+	u32 get;
+};
+
+struct mm_get_mac_addr_cfm {
+	u8 mac_addr[6];
+};
+
+struct mm_get_sta_info_req {
+	u8 sta_idx;
+};
+
+struct mm_get_sta_info_cfm {
+	u32 rate_info;
+	u32 txfailed;
+	u8 rssi;
+};
+
+#define REGION_NUM_MAX 5
+
+struct txpwr_lvl_conf {
+	u8 enable;
+	u8 dsss;
+	u8 ofdmlowrate_2g4;
+	u8 ofdm64qam_2g4;
+	u8 ofdm256qam_2g4;
+	u8 ofdm1024qam_2g4;
+	u8 ofdmlowrate_5g;
+	u8 ofdm64qam_5g;
+	u8 ofdm256qam_5g;
+	u8 ofdm1024qam_5g;
+};
+
+struct txpwr_lvl_conf_v2 {
+	u8 enable;
+	s8 pwrlvl_11b_11ag_2g4[12];
+	s8 pwrlvl_11n_11ac_2g4[10];
+	s8 pwrlvl_11ax_2g4[12];
+};
+
+struct txpwr_lvl_conf_v3 {
+	u8 enable;
+	s8 pwrlvl_11b_11ag_2g4[12];
+	s8 pwrlvl_11n_11ac_2g4[10];
+	s8 pwrlvl_11ax_2g4[12];
+	s8 pwrlvl_11a_5g[12];
+	s8 pwrlvl_11n_11ac_5g[10];
+	s8 pwrlvl_11ax_5g[12];
+};
+
+struct txpwr_lvl_adj_conf {
+	u8 enable;
+	s8 pwrlvl_adj_tbl_2g4[3];
+	s8 pwrlvl_adj_tbl_5g[6];
+};
+
+struct txpwr_loss_conf {
+	u8 loss_enable_2g4;
+	s8 loss_value_2g4;
+	u8 loss_enable_5g;
+	s8 loss_value_5g;
+};
+
+struct mm_set_txpwr_lvl_req {
+	union {
+		struct txpwr_lvl_conf txpwr_lvl;
+		struct txpwr_lvl_conf_v2 txpwr_lvl_v2;
+		struct txpwr_lvl_conf_v3 txpwr_lvl_v3;
+	};
+};
+
+struct mm_set_txpwr_lvl_adj_req {
+	struct txpwr_lvl_adj_conf txpwr_lvl_adj;
+};
+
+struct txpwr_idx_conf {
+	u8 enable;
+	u8 dsss;
+	u8 ofdmlowrate_2g4;
+	u8 ofdm64qam_2g4;
+	u8 ofdm256qam_2g4;
+	u8 ofdm1024qam_2g4;
+	u8 ofdmlowrate_5g;
+	u8 ofdm64qam_5g;
+	u8 ofdm256qam_5g;
+	u8 ofdm1024qam_5g;
+};
+
+struct mm_set_txpwr_idx_req {
+	struct txpwr_idx_conf txpwr_idx;
+};
+
+struct txpwr_ofst_conf {
+	u8 enable;
+	s8 chan_1_4;
+	s8 chan_5_9;
+	s8 chan_10_13;
+	s8 chan_36_64;
+	s8 chan_100_120;
+	s8 chan_122_140;
+	s8 chan_142_165;
+};
+
+/*
+ * pwrofst2x_tbl_2g4[3][3]:
+ * +---------------+----------+----------+----------+
+ * | RateTyp\ChGrp |  CH_1_4  |  CH_5_9  | CH_10_13 |
+ * +---------------+----------+----------+----------+
+ * | DSSS          |  [0][0]  |  [0][1]  |  [0][2]  |
+ * +---------------+----------+----------+----------+
+ * | OFDM_HIGHRATE |  [1][0]  |  [1][1]  |  [1][2]  |
+ * +---------------+----------+----------+----------+
+ * | OFDM_LOWRATE  |  [2][0]  |  [2][1]  |  [2][2]  |
+ * +---------------+----------+----------+----------+
+ * pwrofst2x_tbl_5g[3][6]:
+ * +---------------+--------------+--------------+----------------+----------------+--+--+
+ * | RateTyp\ChGrp | CH_42(36~50) | CH_58(51~64) | CH_106(98~114) |
+ * CH_122(115~130)| CH_138(131~146)| CH_155(147~166)|
+ * +---------------+--------------+--------------+----------------+----------------+--+--+
+ * | OFDM_LOWRATE  |    [0][0]    |    [0][1]    |     [0][2]     |     [0][3]
+ * |     [0][4]     |     [0][5]     |
+ * +---------------+--------------+--------------+----------------+----------------+--+--+
+ * | OFDM_HIGHRATE |    [1][0]    |    [1][1]    |     [1][2]     |     [1][3]
+ * |     [1][4]     |     [1][5]     |
+ * +---------------+--------------+--------------+----------------+----------------+--+--+
+ * | OFDM_MIDRATE  |    [2][0]    |    [2][1]    |     [2][2]     |     [2][3]
+ * |     [2][4]     |     [2][5]     |
+ * +---------------+--------------+--------------+----------------+----------------+--+--+
+ */
+
+struct txpwr_ofst2x_conf {
+	s8 enable;
+	s8 pwrofst2x_tbl_2g4[3][3];
+	s8 pwrofst2x_tbl_5g[3][6];
+};
+
+struct xtal_cap_conf {
+	u8 enable;
+	u8 xtal_cap;
+	u8 xtal_cap_fine;
+};
+
+struct mm_set_txpwr_ofst_req {
+	union {
+		struct txpwr_ofst_conf txpwr_ofst;
+		struct txpwr_ofst2x_conf txpwr_ofst2x;
+	};
+};
+
+struct mm_set_stack_start_req {
+	u8 is_stack_start;
+	u8 efuse_valid;
+	u8 set_vendor_info;
+	u8 fwtrace_redir;
+};
+
+struct mm_set_stack_start_cfm {
+	u8 is_5g_support;
+	u8 vendor_info;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_P2P_OPPPS_REQ
+/// message.
+struct mm_set_p2p_oppps_req {
+	/// VIF Index
+	u8 vif_index;
+	/// CTWindow
+	u8 ctwindow;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_P2P_NOA_CFM message.
+struct mm_set_p2p_noa_cfm {
+	/// Request status
+	u8 status;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_P2P_OPPPS_CFM
+/// message.
+struct mm_set_p2p_oppps_cfm {
+	/// Request status
+	u8 status;
+};
+
+/// Structure containing the parameters of the @ref MM_P2P_NOA_UPD_IND message.
+struct mm_p2p_noa_upd_ind {
+	/// VIF Index
+	u8 vif_index;
+	/// NOA Instance Number
+	u8 noa_inst_nb;
+	/// NoA Type
+	u8 noa_type;
+	/// Count
+	u8 count;
+	/// Duration (in us)
+	u32 duration_us;
+	/// Interval (in us)
+	u32 interval_us;
+	/// Start Time
+	u32 start_time;
+};
+
+/// Structure containing the parameters of the @ref MM_CFG_RSSI_REQ message
+struct mm_cfg_rssi_req {
+	/// Index of the VIF
+	u8 vif_index;
+	/// RSSI threshold
+	s8 rssi_thold;
+	/// RSSI hysteresis
+	u8 rssi_hyst;
+};
+
+/// Structure containing the parameters of the @ref MM_RSSI_STATUS_IND message
+struct mm_rssi_status_ind {
+	/// Index of the VIF
+	u8 vif_index;
+	/// Status of the RSSI
+	bool rssi_status;
+	/// Current RSSI
+	s8 rssi;
+};
+
+/// Structure containing the parameters of the @ref MM_PKTLOSS_IND message
+struct mm_pktloss_ind {
+	/// Index of the VIF
+	u8 vif_index;
+	/// Address of the STA for which there is a packet loss
+	struct mac_addr mac_addr;
+	/// Number of packets lost
+	u32 num_packets;
+};
+
+/// Structure containing the parameters of the @ref MM_CSA_FINISH_IND message
+struct mm_csa_finish_ind {
+	/// Index of the VIF
+	u8 vif_index;
+	/// Status of the operation
+	u8 status;
+	/// New channel ctx index
+	u8 chan_idx;
+};
+
+/// Structure containing the parameters of the @ref MM_CSA_TRAFFIC_IND message
+struct mm_csa_traffic_ind {
+	/// Index of the VIF
+	u8 vif_index;
+	/// Is tx traffic enable or disable
+	bool enable;
+};
+
+/// Structure containing the parameters of the @ref MM_MU_GROUP_UPDATE_REQ
+/// message. Size allocated for the structure depends of the number of group
+struct mm_mu_group_update_req {
+	/// Station index
+	u8 sta_idx;
+	/// Number of groups the STA belongs to
+	u8 group_cnt;
+	/// Group information
+	struct {
+		/// Group Id
+		u8 group_id;
+		/// User position
+		u8 user_pos;
+	} groups[];
+};
+
+///////////////////////////////////////////////////////////////////////////////
+/////////// For Scan messages
+///////////////////////////////////////////////////////////////////////////////
+enum scan_msg_tag {
+	/// Scanning start Request.
+	SCAN_START_REQ = LMAC_FIRST_MSG(TASK_SCAN),
+	/// Scanning start Confirmation.
+	SCAN_START_CFM,
+	/// End of scanning indication.
+	SCAN_DONE_IND,
+	/// Cancel scan request
+	SCAN_CANCEL_REQ,
+	/// Cancel scan confirmation
+	SCAN_CANCEL_CFM,
+
+	/// MAX number of messages
+	SCAN_MAX,
+};
+
+/// Maximum number of SSIDs in a scan request
+#define SCAN_SSID_MAX 3
+
+/// Maximum number of channels in a scan request
+#define SCAN_CHANNEL_MAX (MAC_DOMAINCHANNEL_24G_MAX + MAC_DOMAINCHANNEL_5G_MAX)
+
+/// Maximum length of the ProbeReq IEs (SoftMAC mode)
+#define SCAN_MAX_IE_LEN 300
+
+/// Maximum number of PHY bands supported
+#define SCAN_BAND_MAX 2
+
+/// Structure containing the parameters of the @ref SCAN_START_REQ message
+struct scan_start_req {
+	/// List of channel to be scanned
+	struct mac_chan_def chan[SCAN_CHANNEL_MAX];
+	/// List of SSIDs to be scanned
+	struct mac_ssid ssid[SCAN_SSID_MAX];
+	/// BSSID to be scanned
+	struct mac_addr bssid;
+	/// Pointer (in host memory) to the additional IEs that need to be added to
+	/// the ProbeReq (following the SSID element)
+	u32 add_ies;
+	/// Length of the additional IEs
+	u16 add_ie_len;
+	/// Index of the VIF that is scanning
+	u8 vif_idx;
+	/// Number of channels to scan
+	u8 chan_cnt;
+	/// Number of SSIDs to scan for
+	u8 ssid_cnt;
+	/// no CCK - For P2P frames not being sent at CCK rate in 2GHz band.
+	bool no_cck;
+	/// Scan duration, in us
+	u32 duration;
+};
+
+/// Structure containing the parameters of the @ref SCAN_START_CFM message
+struct scan_start_cfm {
+	/// Status of the request
+	u8 status;
+};
+
+/// Structure containing the parameters of the @ref SCAN_CANCEL_REQ message
+struct scan_cancel_req {
+};
+
+/// Structure containing the parameters of the @ref SCAN_START_CFM message
+struct scan_cancel_cfm {
+	/// Status of the request
+	u8 status;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+/////////// For Scanu messages
+///////////////////////////////////////////////////////////////////////////////
+/// Messages that are logically related to the task.
+enum {
+	/// Scan request from host.
+	SCANU_START_REQ = LMAC_FIRST_MSG(TASK_SCANU),
+	/// Scanning start Confirmation.
+	SCANU_START_CFM,
+	/// Join request
+	SCANU_JOIN_REQ,
+	/// Join confirmation.
+	SCANU_JOIN_CFM,
+	/// Scan result indication.
+	SCANU_RESULT_IND,
+	/// Fast scan request from any other module.
+	SCANU_FAST_REQ,
+	/// Confirmation of fast scan request.
+	SCANU_FAST_CFM,
+
+	SCANU_VENDOR_IE_REQ,
+	SCANU_VENDOR_IE_CFM,
+	SCANU_START_CFM_ADDTIONAL,
+	SCANU_CANCEL_REQ,
+	SCANU_CANCEL_CFM,
+
+	/// MAX number of messages
+	SCANU_MAX,
+};
+
+/// Maximum length of the additional ProbeReq IEs (FullMAC mode)
+#define SCANU_MAX_IE_LEN 200
+
+/// Structure containing the parameters of the @ref SCANU_START_REQ message
+struct scanu_start_req {
+	/// List of channel to be scanned
+	struct mac_chan_def chan[SCAN_CHANNEL_MAX];
+	/// List of SSIDs to be scanned
+	struct mac_ssid ssid[SCAN_SSID_MAX];
+	/// BSSID to be scanned (or WILDCARD BSSID if no BSSID is searched in
+	/// particular)
+	struct mac_addr bssid;
+	/// Address (in host memory) of the additional IEs that need to be added to
+	/// the ProbeReq (following the SSID element)
+	u32 add_ies;
+	/// Length of the additional IEs
+	u16 add_ie_len;
+	/// Index of the VIF that is scanning
+	u8 vif_idx;
+	/// Number of channels to scan
+	u8 chan_cnt;
+	/// Number of SSIDs to scan for
+	u8 ssid_cnt;
+	/// no CCK - For P2P frames not being sent at CCK rate in 2GHz band.
+	bool no_cck;
+	/// Scan duration, in us
+	u32 duration;
+};
+
+struct scanu_vendor_ie_req {
+	u16 add_ie_len;
+	u8 vif_idx;
+	u8 ie[256];
+};
+
+/// Structure containing the parameters of the @ref SCANU_START_CFM message
+struct scanu_start_cfm {
+	/// Index of the VIF that was scanning
+	u8 vif_idx;
+	/// Status of the request
+	u8 status;
+	/// Number of scan results available
+	u8 result_cnt;
+};
+
+/// Parameters of the @SCANU_RESULT_IND message
+struct scanu_result_ind {
+	/// Length of the frame
+	u16 length;
+	/// Frame control field of the frame.
+	u16 framectrl;
+	/// Center frequency on which we received the packet
+	u16 center_freq;
+	/// PHY band
+	u8 band;
+	/// Index of the station that sent the frame. 0xFF if unknown.
+	u8 sta_idx;
+	/// Index of the VIF that received the frame. 0xFF if unknown.
+	u8 inst_nbr;
+	/// RSSI of the received frame.
+	s8 rssi;
+	/// Frame payload.
+	u32 payload[];
+};
+
+struct rxu_arp_announcement_ind {
+	/// index of the vif
+	u8 vif_index;
+	u8 offset;
+	/// length of the frame
+	u16 length;
+	u32 rx_status;
+	/// frame payload
+	u8 payload[];
+};
+
+/// Structure containing the parameters of the message.
+struct scanu_fast_req {
+	/// The SSID to scan in the channel.
+	struct mac_ssid ssid;
+	/// BSSID.
+	struct mac_addr bssid;
+	/// Probe delay.
+	u16 probe_delay;
+	/// Minimum channel time.
+	u16 minch_time;
+	/// Maximum channel time.
+	u16 maxch_time;
+	/// The channel number to scan.
+	u16 ch_nbr;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+/////////// For ME messages
+///////////////////////////////////////////////////////////////////////////////
+/// Messages that are logically related to the task.
+enum {
+	/// Configuration request from host.
+	ME_CONFIG_REQ = LMAC_FIRST_MSG(TASK_ME),
+	/// Configuration confirmation.
+	ME_CONFIG_CFM,
+	/// Configuration request from host.
+	ME_CHAN_CONFIG_REQ,
+	/// Configuration confirmation.
+	ME_CHAN_CONFIG_CFM,
+	/// Set control port state for a station.
+	ME_SET_CONTROL_PORT_REQ,
+	/// Control port setting confirmation.
+	ME_SET_CONTROL_PORT_CFM,
+	/// TKIP MIC failure indication.
+	ME_TKIP_MIC_FAILURE_IND,
+	/// Add a station to the FW (AP mode)
+	ME_STA_ADD_REQ,
+	/// Confirmation of the STA addition
+	ME_STA_ADD_CFM,
+	/// Delete a station from the FW (AP mode)
+	ME_STA_DEL_REQ,
+	/// Confirmation of the STA deletion
+	ME_STA_DEL_CFM,
+	/// Indication of a TX RA/TID queue credit update
+	ME_TX_CREDITS_UPDATE_IND,
+	/// Request indicating to the FW that there is traffic buffered on host
+	ME_TRAFFIC_IND_REQ,
+	/// Confirmation that the @ref ME_TRAFFIC_IND_REQ has been executed
+	ME_TRAFFIC_IND_CFM,
+	/// Request of RC statistics to a station
+	ME_RC_STATS_REQ,
+	/// RC statistics confirmation
+	ME_RC_STATS_CFM,
+	/// RC fixed rate request
+	ME_RC_SET_RATE_REQ,
+	/// Configure monitor interface
+	ME_CONFIG_MONITOR_REQ,
+	/// Configure monitor interface response
+	ME_CONFIG_MONITOR_CFM,
+	/// Setting power Save mode request from host
+	ME_SET_PS_MODE_REQ,
+	/// Set power Save mode confirmation
+	ME_SET_PS_MODE_CFM,
+	/// Setting Low Power level request from host
+	ME_SET_LP_LEVEL_REQ,
+	/// Set Low Power level confirmation
+	ME_SET_LP_LEVEL_CFM,
+	/// MAX number of messages
+	ME_MAX,
+};
+
+/// Structure containing the parameters of the @ref ME_START_REQ message
+struct me_config_req {
+	/// HT Capabilities
+	struct mac_htcapability ht_cap;
+	/// VHT Capabilities
+	struct mac_vhtcapability vht_cap;
+	/// HE capabilities
+	struct mac_hecapability he_cap;
+	/// Lifetime of packets sent under a BlockAck agreement (expressed in TUs)
+	u16 tx_lft;
+	/// Maximum supported BW
+	u8 phy_bw_max;
+	/// Boolean indicating if HT is supported or not
+	bool ht_supp;
+	/// Boolean indicating if VHT is supported or not
+	bool vht_supp;
+	/// Boolean indicating if HE is supported or not
+	bool he_supp;
+	/// Boolean indicating if HE OFDMA UL is enabled or not
+	bool he_ul_on;
+	/// Boolean indicating if PS mode shall be enabled or not
+	bool ps_on;
+	/// Boolean indicating if Antenna Diversity shall be enabled or not
+	bool ant_div_on;
+	/// Boolean indicating if Dynamic PS mode shall be used or not
+	bool dpsm;
+};
+
+/// Structure containing the parameters of the @ref ME_CHAN_CONFIG_REQ message
+struct me_chan_config_req {
+	/// List of 2.4GHz supported channels
+	struct mac_chan_def chan2G4[MAC_DOMAINCHANNEL_24G_MAX];
+	/// List of 5GHz supported channels
+	struct mac_chan_def chan5G[MAC_DOMAINCHANNEL_5G_MAX];
+	/// Number of 2.4GHz channels in the list
+	u8 chan2G4_cnt;
+	/// Number of 5GHz channels in the list
+	u8 chan5G_cnt;
+};
+
+/// Structure containing the parameters of the @ref ME_SET_CONTROL_PORT_REQ
+/// message
+struct me_set_control_port_req {
+	/// Index of the station for which the control port is opened
+	u8 sta_idx;
+	/// Control port state
+	bool control_port_open;
+};
+
+/// Structure containing the parameters of the @ref ME_TKIP_MIC_FAILURE_IND
+/// message
+struct me_tkip_mic_failure_ind {
+	/// Address of the sending STA
+	struct mac_addr addr;
+	/// TSC value
+	u64 tsc;
+	/// Boolean indicating if the packet was a group or unicast one (true if
+	/// group)
+	bool ga;
+	/// Key Id
+	u8 keyid;
+	/// VIF index
+	u8 vif_idx;
+};
+
+/// Structure containing the parameters of the @ref ME_STA_ADD_REQ message
+struct me_sta_add_req {
+	/// MAC address of the station to be added
+	struct mac_addr mac_addr;
+	/// Supported legacy rates
+	struct mac_rateset rate_set;
+	/// HT Capabilities
+	struct mac_htcapability ht_cap;
+	/// VHT Capabilities
+	struct mac_vhtcapability vht_cap;
+	/// HE capabilities
+	struct mac_hecapability he_cap;
+	/// Flags giving additional information about the station (@ref
+	/// mac_sta_flags)
+	u32 flags;
+	/// Association ID of the station
+	__le16 aid;
+	/// Bit field indicating which queues have U-APSD enabled
+	u8 uapsd_queues;
+	/// Maximum size, in frames, of a APSD service period
+	u8 max_sp_len;
+	/// Operation mode information (valid if bit @ref STA_OPMOD_NOTIF is
+	/// set in the flags)
+	u8 opmode;
+	/// Index of the VIF the station is attached to
+	u8 vif_idx;
+	/// Whether the station is TDLS station
+	bool tdls_sta;
+	/// Indicate if the station is TDLS link initiator station
+	bool tdls_sta_initiator;
+	/// Indicate if the TDLS Channel Switch is allowed
+	bool tdls_chsw_allowed;
+};
+
+/// Structure containing the parameters of the @ref ME_STA_ADD_CFM message
+struct me_sta_add_cfm {
+	/// Station index
+	u8 sta_idx;
+	/// Status of the station addition
+	u8 status;
+	/// PM state of the station
+	u8 pm_state;
+};
+
+/// Structure containing the parameters of the @ref ME_STA_DEL_REQ message.
+struct me_sta_del_req {
+	/// Index of the station to be deleted
+	u8 sta_idx;
+	/// Whether the station is TDLS station
+	bool tdls_sta;
+};
+
+/// Structure containing the parameters of the @ref ME_TX_CREDITS_UPDATE_IND
+/// message.
+struct me_tx_credits_update_ind {
+	/// Index of the station for which the credits are updated
+	u8 sta_idx;
+	/// TID for which the credits are updated
+	u8 tid;
+	/// Offset to be applied on the credit count
+	s8 credits;
+};
+
+/// Structure containing the parameters of the @ref ME_TRAFFIC_IND_REQ message.
+struct me_traffic_ind_req {
+	/// Index of the station for which UAPSD traffic is available on host
+	u8 sta_idx;
+	/// Flag indicating the availability of UAPSD packets for the given STA
+	u8 tx_avail;
+	/// Indicate if traffic is on uapsd-enabled queues
+	bool uapsd;
+};
+
+struct mm_apm_staloss_ind {
+	u8 sta_idx;
+	u8 vif_idx;
+	u8 mac_addr[6];
+};
+
+enum vendor_hwconfig_tag {
+	ACS_TXOP_REQ = 0,
+	CHANNEL_ACCESS_REQ,
+	MAC_TIMESCALE_REQ,
+	CCA_THRESHOLD_REQ,
+	BWMODE_REQ,
+	CHIP_TEMP_GET_REQ,
+	AP_PS_LEVEL_SET_REQ,
+	CUSTOMIZED_FREQ_REQ,
+	WAKEUP_INFO_REQ,
+	KEEPALIVE_PKT_REQ,
+};
+
+enum {
+	BWMODE20M = 0,
+	BWMODE10M,
+	BWMODE5M,
+};
+
+struct mm_set_acs_txop_req {
+	u32 hwconfig_id;
+	u16 txop_bk;
+	u16 txop_be;
+	u16 txop_vi;
+	u16 txop_vo;
+};
+
+struct mm_set_channel_access_req {
+	u32 hwconfig_id;
+	u32 edca[4];
+	u8 vif_idx;
+	u8 retry_cnt;
+	u8 rts_en;
+	u8 long_nav_en;
+	u8 cfe_en;
+	u8 rc_retry_cnt[3];
+	s8 ccademod_th;
+};
+
+struct mm_set_mac_timescale_req {
+	u32 hwconfig_id;
+	u8 sifs_a_time;
+	u8 sifs_b_time;
+	u8 slot_time;
+	u8 rx_startdelay_ofdm;
+	u8 rx_startdelay_long;
+	u8 rx_startdelay_short;
+};
+
+struct mm_set_cca_threshold_req {
+	u32 hwconfig_id;
+	u8 auto_cca_en;
+	s8 cca20p_rise_th;
+	s8 cca20s_rise_th;
+	s8 cca20p_fall_th;
+	s8 cca20s_fall_th;
+};
+
+struct mm_set_bwmode_req {
+	u32 hwconfig_id;
+	u8 bwmode;
+};
+
+struct mm_get_chip_temp_req {
+	u32 hwconfig_id;
+};
+
+struct mm_get_chip_temp_cfm {
+	/// Temp degree val
+	s8 degree;
+};
+
+struct mm_set_ap_ps_level_req {
+	u32 hwconfig_id;
+	u8 ap_ps_level;
+};
+
+struct mm_set_vendor_hwconfig_cfm {
+	u32 hwconfig_id;
+	union {
+		struct mm_get_chip_temp_cfm chip_temp_cfm;
+	};
+};
+
+struct mm_set_customized_freq_req {
+	u32 hwconfig_id;
+	u16 raw_freq[4];
+	u16 map_freq[4];
+};
+
+struct mm_set_keepalive_req {
+	u32 hwconfig_id;
+	u16 code;
+	u16 length;
+	u32 intv;
+	u8 payload[];
+};
+
+struct mm_set_txop_req {
+	u16 txop_bk;
+	u16 txop_be;
+	u16 txop_vi;
+	u16 txop_vo;
+	u8 long_nav_en;
+	u8 cfe_en;
+};
+
+struct mm_set_wakeup_info_req {
+	u32 hwconfig_id;
+	u16 code;
+	u16 offset;
+	u16 length;
+	u16 length_win;
+	u8 mask_and_pattern[];
+};
+
+struct mm_get_fw_version_cfm {
+	u8 fw_version_len;
+	u8 fw_version[63];
+};
+
+struct mm_get_wifi_disable_cfm {
+	u8 wifi_disable;
+};
+
+enum vendor_swconfig_tag {
+	BCN_CFG_REQ = 0,
+	TEMP_COMP_SET_REQ,
+	TEMP_COMP_GET_REQ,
+	EXT_FLAGS_SET_REQ,
+	EXT_FLAGS_GET_REQ,
+	EXT_FLAGS_MASK_SET_REQ,
+};
+
+struct mm_set_bcn_cfg_req {
+	/// Ignore or not bcn tim bcmc bit
+	bool tim_bcmc_ignored_enable;
+};
+
+struct mm_set_bcn_cfg_cfm {
+	/// Request status
+	bool tim_bcmc_ignored_status;
+};
+
+struct mm_set_temp_comp_req {
+	/// Enable or not temp comp
+	u8 enable;
+	u8 reserved[3];
+	u32 tmr_period_ms;
+};
+
+struct mm_set_temp_comp_cfm {
+	/// Request status
+	u8 status;
+};
+
+struct mm_get_temp_comp_cfm {
+	/// Request status
+	u8 status;
+	/// Temp degree val
+	s8 degree;
+};
+
+struct mm_set_ext_flags_req {
+	u32 user_flags;
+};
+
+struct mm_set_ext_flags_cfm {
+	u32 user_flags;
+};
+
+struct mm_get_ext_flags_cfm {
+	u32 user_flags;
+};
+
+struct mm_mask_set_ext_flags_req {
+	u32 user_flags_mask;
+	u32 user_flags_val;
+};
+
+struct mm_mask_set_ext_flags_cfm {
+	u32 user_flags;
+};
+
+struct mm_set_vendor_swconfig_req {
+	u32 swconfig_id;
+	union {
+		struct mm_set_bcn_cfg_req bcn_cfg_req;
+		struct mm_set_temp_comp_req temp_comp_set_req;
+		struct mm_set_ext_flags_req ext_flags_set_req;
+		struct mm_mask_set_ext_flags_req ext_flags_mask_set_req;
+	};
+};
+
+struct mm_set_vendor_swconfig_cfm {
+	u32 swconfig_id;
+	union {
+		struct mm_set_bcn_cfg_cfm bcn_cfg_cfm;
+		struct mm_set_temp_comp_cfm temp_comp_set_cfm;
+		struct mm_get_temp_comp_cfm temp_comp_get_cfm;
+		struct mm_set_ext_flags_cfm ext_flags_set_cfm;
+		struct mm_get_ext_flags_cfm ext_flags_get_cfm;
+		struct mm_mask_set_ext_flags_cfm ext_flags_mask_set_cfm;
+	};
+};
+
+/// Structure containing the parameters of the @ref ME_RC_STATS_REQ message.
+struct me_rc_stats_req {
+	/// Index of the station for which the RC statistics are requested
+	u8 sta_idx;
+};
+
+/// Structure containing the rate control statistics
+struct rc_rate_stats {
+	/// Number of attempts (per sampling interval)
+	u16 attempts;
+	/// Number of success (per sampling interval)
+	u16 success;
+	/// Estimated probability of success (EWMA)
+	u16 probability;
+	/// Rate configuration of the sample
+	u16 rate_config;
+	union {
+		struct {
+			/// Number of times the sample has been skipped (per sampling
+			/// interval)
+			u8 sample_skipped;
+			/// Whether the old probability is available
+			bool old_prob_available;
+			/// Whether the rate can be used in the retry chain
+			bool rate_allowed;
+		};
+		struct {
+			/// RU size and UL length received in the latest HE trigger frame
+			u16 ru_and_length;
+		};
+	};
+};
+
+/// Number of RC samples
+#define RC_MAX_N_SAMPLE 10
+/// Index of the HE statistics element in the table
+#define RC_HE_STATS_IDX RC_MAX_N_SAMPLE
+
+/// Structure containing the parameters of the @ref ME_RC_STATS_CFM message.
+struct me_rc_stats_cfm {
+	/// Index of the station for which the RC statistics are provided
+	u8 sta_idx;
+	/// Number of samples used in the RC algorithm
+	u16 no_samples;
+	/// Number of MPDUs transmitted (per sampling interval)
+	u16 ampdu_len;
+	/// Number of AMPDUs transmitted (per sampling interval)
+	u16 ampdu_packets;
+	/// Average number of MPDUs in each AMPDU frame (EWMA)
+	u32 avg_ampdu_len;
+	// Current step 0 of the retry chain
+	u8 sw_retry_step;
+	/// Trial transmission period
+	u8 sample_wait;
+	/// Retry chain steps
+	u16 retry_step_idx[4];
+	/// RC statistics - Max number of RC samples, plus one for the HE TB
+	/// statistics
+	struct rc_rate_stats rate_stats[RC_MAX_N_SAMPLE + 1];
+	/// Throughput - Max number of RC samples, plus one for the HE TB statistics
+	u32 tp[RC_MAX_N_SAMPLE + 1];
+};
+
+/// Structure containing the parameters of the @ref ME_RC_SET_RATE_REQ message.
+struct me_rc_set_rate_req {
+	/// Index of the station for which the fixed rate is set
+	u8 sta_idx;
+	/// Rate configuration to be set
+	u16 fixed_rate_cfg;
+};
+
+/// Structure containing the parameters of the @ref ME_CONFIG_MONITOR_REQ
+/// message.
+struct me_config_monitor_req {
+	/// Channel to configure
+	struct mac_chan_op chan;
+	/// Is channel data valid
+	bool chan_set;
+	/// Enable report of unsupported HT frames
+	bool uf;
+	/// Enable auto-reply as the mac_addr matches
+	bool auto_reply;
+};
+
+/// Structure containing the parameters of the @ref ME_CONFIG_MONITOR_CFM
+/// message.
+struct me_config_monitor_cfm {
+	/// Channel context index
+	u8 chan_index;
+	/// Channel parameters
+	struct mac_chan_op chan;
+};
+
+/// Structure containing the parameters of the @ref ME_SET_PS_MODE_REQ message.
+struct me_set_ps_mode_req {
+	/// Power Save is activated or deactivated
+	u8 ps_state;
+};
+
+/// Structure containing the parameters of the @ref ME_SET_LP_LEVEL_REQ message.
+struct me_set_lp_level_req {
+	/// Low Power level
+	u8 lp_level;
+	u8 disable_filter;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+/////////// For SM messages
+///////////////////////////////////////////////////////////////////////////////
+/// Message API of the SM task
+enum sm_msg_tag {
+	/// Request to connect to an AP
+	SM_CONNECT_REQ = LMAC_FIRST_MSG(TASK_SM),
+	/// Confirmation of connection
+	SM_CONNECT_CFM,
+	/// Indicates that the SM associated to the AP
+	SM_CONNECT_IND,
+	/// Request to disconnect
+	SM_DISCONNECT_REQ,
+	/// Confirmation of disconnection
+	SM_DISCONNECT_CFM,
+	/// Indicates that the SM disassociated the AP
+	SM_DISCONNECT_IND,
+	/// Request to start external authentication
+	SM_EXTERNAL_AUTH_REQUIRED_IND,
+	/// Response to external authentication request
+	SM_EXTERNAL_AUTH_REQUIRED_RSP,
+	/// Request to update assoc elements after FT over the air authentication
+	SM_FT_AUTH_IND,
+	/// Response to FT authentication with updated assoc elements
+	SM_FT_AUTH_RSP,
+
+	SM_RSP_TIMEOUT_IND,
+
+	SM_COEX_TS_TIMEOUT_IND,
+
+	SM_EXTERNAL_AUTH_REQUIRED_RSP_CFM,
+	/// MAX number of messages
+	SM_MAX,
+};
+
+/// Structure containing the parameters of @ref SM_CONNECT_REQ message.
+struct sm_connect_req {
+	/// SSID to connect to
+	struct mac_ssid ssid;
+	/// BSSID to connect to (if not specified, set this field to WILDCARD BSSID)
+	struct mac_addr bssid;
+	/// Channel on which we have to connect (if not specified, set -1 in the
+	/// chan.freq field)
+	struct mac_chan_def chan;
+	/// Connection flags (see @ref mac_connection_flags)
+	u32 flags;
+	/// Control port Ethertype (in network endianness)
+	__be16 ctrl_port_ethertype;
+	/// Length of the association request IEs
+	u16 ie_len;
+	/// Listen interval to be used for this connection
+	u16 listen_interval;
+	/// Flag indicating if the we have to wait for the BC/MC traffic after
+	/// beacon or not
+	bool dont_wait_bcmc;
+	/// Authentication type
+	u8 auth_type;
+	/// UAPSD queues (bit0: VO, bit1: VI, bit2: BE, bit3: BK)
+	u8 uapsd_queues;
+	/// VIF index
+	u8 vif_idx;
+	/// Buffer containing the additional information elements to be put in the
+	/// association request
+	u32 ie_buf[64];
+};
+
+/// Structure containing the parameters of the @ref SM_CONNECT_CFM message.
+struct sm_connect_cfm {
+	/// Status. If 0, it means that the connection procedure will be performed
+	/// and that a subsequent @ref SM_CONNECT_IND message will be forwarded once
+	/// the procedure is completed
+	u8 status;
+};
+
+#define SM_ASSOC_IE_LEN 800
+/// Structure containing the parameters of the @ref SM_CONNECT_IND message.
+struct sm_connect_ind {
+	/// Status code of the connection procedure
+	u16 status_code;
+	/// BSSID
+	struct mac_addr bssid;
+	/// Flag indicating if the indication refers to an internal roaming or from
+	/// a host request
+	bool roamed;
+	/// Index of the VIF for which the association process is complete
+	u8 vif_idx;
+	/// Index of the STA entry allocated for the AP
+	u8 ap_idx;
+	/// Index of the LMAC channel context the connection is attached to
+	u8 ch_idx;
+	/// Flag indicating if the AP is supporting QoS
+	bool qos;
+	/// ACM bits set in the AP WMM parameter element
+	u8 acm;
+	/// Length of the AssocReq IEs
+	u16 assoc_req_ie_len;
+	/// Length of the AssocRsp IEs
+	u16 assoc_rsp_ie_len;
+	/// IE buffer
+	u32 assoc_ie_buf[SM_ASSOC_IE_LEN / 4];
+
+	u16 aid;
+	u8 band;
+	u16 center_freq;
+	u8 width;
+	u32 center_freq1;
+	u32 center_freq2;
+
+	/// EDCA parameters
+	u32 ac_param[AC_MAX];
+};
+
+/// Structure containing the parameters of the @ref SM_DISCONNECT_REQ message.
+struct sm_disconnect_req {
+	/// Reason of the deauthentication.
+	u16 reason_code;
+	/// Index of the VIF.
+	u8 vif_idx;
+};
+
+/// Structure containing the parameters of SM_ASSOCIATION_IND the message
+struct sm_association_ind {
+	// MAC ADDR of the STA
+	struct mac_addr me_mac_addr;
+};
+
+/// Structure containing the parameters of the @ref SM_DISCONNECT_IND message.
+struct sm_disconnect_ind {
+	/// Reason of the disconnection.
+	u16 reason_code;
+	/// Index of the VIF.
+	u8 vif_idx;
+	/// FT over DS is ongoing
+	bool ft_over_ds;
+	u8 reassoc;
+};
+
+/// Structure containing the parameters of the @ref
+/// SM_EXTERNAL_AUTH_REQUIRED_IND
+struct sm_external_auth_required_ind {
+	/// Index of the VIF.
+	u8 vif_idx;
+	/// SSID to authenticate to
+	struct mac_ssid ssid;
+	/// BSSID to authenticate to
+	struct mac_addr bssid;
+	/// AKM suite of the respective authentication
+	u32 akm;
+};
+
+/// Structure containing the parameters of the @ref
+/// SM_EXTERNAL_AUTH_REQUIRED_RSP
+struct sm_external_auth_required_rsp {
+	/// Index of the VIF.
+	u8 vif_idx;
+	/// Authentication status
+	u16 status;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+/////////// For APM messages
+///////////////////////////////////////////////////////////////////////////////
+/// Message API of the APM task
+enum apm_msg_tag {
+	/// Request to start the AP.
+	APM_START_REQ = LMAC_FIRST_MSG(TASK_APM),
+	/// Confirmation of the AP start.
+	APM_START_CFM,
+	/// Request to stop the AP.
+	APM_STOP_REQ,
+	/// Confirmation of the AP stop.
+	APM_STOP_CFM,
+	/// Request to start CAC
+	APM_START_CAC_REQ,
+	/// Confirmation of the CAC start
+	APM_START_CAC_CFM,
+	/// Request to stop CAC
+	APM_STOP_CAC_REQ,
+	/// Confirmation of the CAC stop
+	APM_STOP_CAC_CFM,
+
+	APM_SET_BEACON_IE_REQ,
+	APM_SET_BEACON_IE_CFM,
+
+	/// MAX number of messages
+	APM_MAX,
+};
+
+/// Structure containing the parameters of the @ref APM_START_REQ message.
+struct apm_start_req {
+	/// Basic rate set
+	struct mac_rateset basic_rates;
+	/// Control channel on which we have to enable the AP
+	struct mac_chan_def chan;
+	/// Center frequency of the first segment
+	u32 center_freq1;
+	/// Center frequency of the second segment (only in 80+80 configuration)
+	u32 center_freq2;
+	/// Width of channel
+	u8 ch_width;
+	/// Address, in host memory, to the beacon template
+	u32 bcn_addr;
+	/// Length of the beacon template
+	u16 bcn_len;
+	/// Offset of the TIM IE in the beacon
+	u16 tim_oft;
+	/// Beacon interval
+	u16 bcn_int;
+	/// Flags (@ref mac_connection_flags)
+	u32 flags;
+	/// Control port Ethertype
+	__be16 ctrl_port_ethertype;
+	/// Length of the TIM IE
+	u8 tim_len;
+	/// Index of the VIF for which the AP is started
+	u8 vif_idx;
+};
+
+struct apm_set_bcn_ie_req {
+	u8 vif_idx;
+	u16 bcn_ie_len;
+	u8 bcn_ie[512];
+};
+
+/// Structure containing the parameters of the @ref APM_START_CFM message.
+struct apm_start_cfm {
+	/// Status of the AP starting procedure
+	u8 status;
+	/// Index of the VIF for which the AP is started
+	u8 vif_idx;
+	/// Index of the channel context attached to the VIF
+	u8 ch_idx;
+	/// Index of the STA used for BC/MC traffic
+	u8 bcmc_idx;
+};
+
+/// Structure containing the parameters of the @ref APM_STOP_REQ message.
+struct apm_stop_req {
+	/// Index of the VIF for which the AP has to be stopped
+	u8 vif_idx;
+};
+
+/// Structure containing the parameters of the @ref APM_START_CAC_REQ message.
+struct apm_start_cac_req {
+	/// Control channel on which we have to start the CAC
+	struct mac_chan_def chan;
+	/// Center frequency of the first segment
+	u32 center_freq1;
+	/// Center frequency of the second segment (only in 80+80 configuration)
+	u32 center_freq2;
+	/// Width of channel
+	u8 ch_width;
+	/// Index of the VIF for which the CAC is started
+	u8 vif_idx;
+};
+
+/// Structure containing the parameters of the @ref APM_START_CAC_CFM message.
+struct apm_start_cac_cfm {
+	/// Status of the CAC starting procedure
+	u8 status;
+	/// Index of the channel context attached to the VIF for CAC
+	u8 ch_idx;
+};
+
+/// Structure containing the parameters of the @ref APM_STOP_CAC_REQ message.
+struct apm_stop_cac_req {
+	/// Index of the VIF for which the CAC has to be stopped
+	u8 vif_idx;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+/////////// For MESH messages
+///////////////////////////////////////////////////////////////////////////////
+
+/// Maximum length of the Mesh ID
+#define MESH_MESHID_MAX_LEN (32)
+
+/// Message API of the MESH task
+enum mesh_msg_tag {
+	/// Request to start the MP
+	MESH_START_REQ = LMAC_FIRST_MSG(TASK_MESH),
+	/// Confirmation of the MP start.
+	MESH_START_CFM,
+
+	/// Request to stop the MP.
+	MESH_STOP_REQ,
+	/// Confirmation of the MP stop.
+	MESH_STOP_CFM,
+
+	// Request to update the MP
+	MESH_UPDATE_REQ,
+	/// Confirmation of the MP update
+	MESH_UPDATE_CFM,
+
+	/// Request information about a given link
+	MESH_PEER_INFO_REQ,
+	/// Response to the MESH_PEER_INFO_REQ message
+	MESH_PEER_INFO_CFM,
+
+	/// Request automatic establishment of a path with a given mesh STA
+	MESH_PATH_CREATE_REQ,
+	/// Confirmation to the MESH_PATH_CREATE_REQ message
+	MESH_PATH_CREATE_CFM,
+
+	/// Request a path update (delete path, modify next hop mesh STA)
+	MESH_PATH_UPDATE_REQ,
+	/// Confirmation to the MESH_PATH_UPDATE_REQ message
+	MESH_PATH_UPDATE_CFM,
+
+	/// Indication from Host that the indicated Mesh Interface is a proxy for an
+	/// external STA
+	MESH_PROXY_ADD_REQ,
+
+	/// Indicate that a connection has been established or lost
+	MESH_PEER_UPDATE_IND,
+	/// Notification that a connection has been established or lost (when MPM
+	/// handled by userspace)
+	MESH_PEER_UPDATE_NTF = MESH_PEER_UPDATE_IND,
+
+	/// Indicate that a path is now active or inactive
+	MESH_PATH_UPDATE_IND,
+	/// Indicate that proxy information have been updated
+	MESH_PROXY_UPDATE_IND,
+
+	/// MAX number of messages
+	MESH_MAX,
+};
+
+/// Structure containing the parameters of the @ref MESH_START_REQ message.
+struct mesh_start_req {
+	/// Basic rate set
+	struct mac_rateset basic_rates;
+	/// Control channel on which we have to enable the AP
+	struct mac_chan_def chan;
+	/// Center frequency of the first segment
+	u32 center_freq1;
+	/// Center frequency of the second segment (only in 80+80 configuration)
+	u32 center_freq2;
+	/// Width of channel
+	u8 ch_width;
+	/// DTIM Period
+	u8 dtim_period;
+	/// Beacon Interval
+	u16 bcn_int;
+	/// Index of the VIF for which the MP is started
+	u8 vif_index;
+	/// Length of the Mesh ID
+	u8 mesh_id_len;
+	/// Mesh ID
+	u8 mesh_id[MESH_MESHID_MAX_LEN];
+	/// Address of the IEs to download
+	u32 ie_addr;
+	/// Length of the provided IEs
+	u8 ie_len;
+	/// Indicate if Mesh Peering Management (MPM) protocol is handled in
+	/// userspace
+	bool user_mpm;
+	/// Indicate if Mesh Point is using authentication
+	bool is_auth;
+	/// Indicate which authentication method is used
+	u8 auth_id;
+};
+
+/// Structure containing the parameters of the @ref MESH_START_CFM message.
+struct mesh_start_cfm {
+	/// Status of the MP starting procedure
+	u8 status;
+	/// Index of the VIF for which the MP is started
+	u8 vif_idx;
+	/// Index of the channel context attached to the VIF
+	u8 ch_idx;
+	/// Index of the STA used for BC/MC traffic
+	u8 bcmc_idx;
+};
+
+/// Structure containing the parameters of the @ref MESH_STOP_REQ message.
+struct mesh_stop_req {
+	/// Index of the VIF for which the MP has to be stopped
+	u8 vif_idx;
+};
+
+/// Structure containing the parameters of the @ref MESH_STOP_CFM message.
+struct mesh_stop_cfm {
+	/// Index of the VIF for which the MP has to be stopped
+	u8 vif_idx;
+	/// Status
+	u8 status;
+};
+
+/// Bit fields for mesh_update_req message's flags value
+enum mesh_update_flags_bit {
+	/// Root Mode
+	MESH_UPDATE_FLAGS_ROOT_MODE_BIT = 0,
+	/// Gate Mode
+	MESH_UPDATE_FLAGS_GATE_MODE_BIT,
+	/// Mesh Forwarding
+	MESH_UPDATE_FLAGS_MESH_FWD_BIT,
+	/// Local Power Save Mode
+	MESH_UPDATE_FLAGS_LOCAL_PSM_BIT,
+};
+
+/// Structure containing the parameters of the @ref MESH_UPDATE_REQ message.
+struct mesh_update_req {
+	/// Flags, indicate fields which have been updated
+	u8 flags;
+	/// VIF Index
+	u8 vif_idx;
+	/// Root Mode
+	u8 root_mode;
+	/// Gate Announcement
+	bool gate_announ;
+	/// Mesh Forwarding
+	bool mesh_forward;
+	/// Local PS Mode
+	u8 local_ps_mode;
+};
+
+/// Structure containing the parameters of the @ref MESH_UPDATE_CFM message.
+struct mesh_update_cfm {
+	/// Status
+	u8 status;
+};
+
+/// Structure containing the parameters of the @ref MESH_PEER_INFO_REQ message.
+struct mesh_peer_info_req {
+	/// Index of the station allocated for the peer
+	u8 sta_idx;
+};
+
+/// Structure containing the parameters of the @ref MESH_PEER_INFO_CFM message.
+struct mesh_peer_info_cfm {
+	/// Response status
+	u8 status;
+	/// Index of the station allocated for the peer
+	u8 sta_idx;
+	/// Local Link ID
+	u16 local_link_id;
+	/// Peer Link ID
+	u16 peer_link_id;
+	/// Local PS Mode
+	u8 local_ps_mode;
+	/// Peer PS Mode
+	u8 peer_ps_mode;
+	/// Non-peer PS Mode
+	u8 non_peer_ps_mode;
+	/// Link State
+	u8 link_state;
+};
+
+/// Structure containing the parameters of the @ref MESH_PATH_CREATE_REQ
+/// message.
+struct mesh_path_create_req {
+	/// Index of the interface on which path has to be created
+	u8 vif_idx;
+	/// Indicate if originator MAC Address is provided
+	bool has_orig_addr;
+	/// Path Target MAC Address
+	struct mac_addr tgt_mac_addr;
+	/// Originator MAC Address
+	struct mac_addr orig_mac_addr;
+};
+
+/// Structure containing the parameters of the @ref MESH_PATH_CREATE_CFM
+/// message.
+struct mesh_path_create_cfm {
+	/// Confirmation status
+	u8 status;
+	/// VIF Index
+	u8 vif_idx;
+};
+
+/// Structure containing the parameters of the @ref MESH_PATH_UPDATE_REQ
+/// message.
+struct mesh_path_update_req {
+	/// Indicate if path must be deleted
+	bool delete;
+	/// Index of the interface on which path has to be created
+	u8 vif_idx;
+	/// Path Target MAC Address
+	struct mac_addr tgt_mac_addr;
+	/// Next Hop MAC Address
+	struct mac_addr nhop_mac_addr;
+};
+
+/// Structure containing the parameters of the @ref MESH_PATH_UPDATE_CFM
+/// message.
+struct mesh_path_update_cfm {
+	/// Confirmation status
+	u8 status;
+	/// VIF Index
+	u8 vif_idx;
+};
+
+/// Structure containing the parameters of the @ref MESH_PROXY_ADD_REQ message.
+struct mesh_proxy_add_req {
+	/// VIF Index
+	u8 vif_idx;
+	/// MAC Address of the External STA
+	struct mac_addr ext_sta_addr;
+};
+
+/// Structure containing the parameters of the @ref MESH_PROXY_UPDATE_IND
+struct mesh_proxy_update_ind {
+	/// Indicate if proxy information has been added or deleted
+	bool delete;
+	/// Indicate if we are a proxy for the external STA
+	bool local;
+	/// VIF Index
+	u8 vif_idx;
+	/// MAC Address of the External STA
+	struct mac_addr ext_sta_addr;
+	/// MAC Address of the proxy (only valid if local is false)
+	struct mac_addr proxy_mac_addr;
+};
+
+/// Structure containing the parameters of the @ref MESH_PEER_UPDATE_IND
+/// message.
+struct mesh_peer_update_ind {
+	/// Indicate if connection has been established or lost
+	bool estab;
+	/// VIF Index
+	u8 vif_idx;
+	/// STA Index
+	u8 sta_idx;
+	/// Peer MAC Address
+	struct mac_addr peer_addr;
+};
+
+/// Structure containing the parameters of the @ref MESH_PEER_UPDATE_NTF
+/// message.
+struct mesh_peer_update_ntf {
+	/// VIF Index
+	u8 vif_idx;
+	/// STA Index
+	u8 sta_idx;
+	/// Mesh Link State
+	u8 state;
+};
+
+/// Structure containing the parameters of the @ref MESH_PATH_UPDATE_IND
+/// message.
+struct mesh_path_update_ind {
+	/// Indicate if path is deleted or not
+	bool delete;
+	/// Indicate if path is towards an external STA (not part of MBSS)
+	bool ext_sta;
+	/// VIF Index
+	u8 vif_idx;
+	/// Path Index
+	u8 path_idx;
+	/// Target MAC Address
+	struct mac_addr tgt_mac_addr;
+	/// External STA MAC Address (only if ext_sta is true)
+	struct mac_addr ext_sta_mac_addr;
+	/// Next Hop STA Index
+	u8 nhop_sta_idx;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+/////////// For Debug messages
+///////////////////////////////////////////////////////////////////////////////
+
+/// Messages related to Debug Task
+enum dbg_msg_tag {
+	/// Memory read request
+	DBG_MEM_READ_REQ = LMAC_FIRST_MSG(TASK_DBG),
+	/// Memory read confirm
+	DBG_MEM_READ_CFM,
+	/// Memory write request
+	DBG_MEM_WRITE_REQ,
+	/// Memory write confirm
+	DBG_MEM_WRITE_CFM,
+	/// Module filter request
+	DBG_SET_MOD_FILTER_REQ,
+	/// Module filter confirm
+	DBG_SET_MOD_FILTER_CFM,
+	/// Severity filter request
+	DBG_SET_SEV_FILTER_REQ,
+	/// Severity filter confirm
+	DBG_SET_SEV_FILTER_CFM,
+	/// LMAC/MAC HW fatal error indication
+	DBG_ERROR_IND,
+	/// Request to get system statistics
+	DBG_GET_SYS_STAT_REQ,
+	/// COnfirmation of system statistics
+	DBG_GET_SYS_STAT_CFM,
+	/// Memory block write request
+	DBG_MEM_BLOCK_WRITE_REQ,
+	/// Memory block write confirm
+	DBG_MEM_BLOCK_WRITE_CFM,
+	/// Start app request
+	DBG_START_APP_REQ,
+	/// Start app confirm
+	DBG_START_APP_CFM,
+	/// Start npc request
+	DBG_START_NPC_REQ,
+	/// Start npc confirm
+	DBG_START_NPC_CFM,
+	/// Memory mask write request
+	DBG_MEM_MASK_WRITE_REQ,
+	/// Memory mask write confirm
+	DBG_MEM_MASK_WRITE_CFM,
+
+	DBG_RFTEST_CMD_REQ,
+	DBG_RFTEST_CMD_CFM,
+	DBG_BINDING_REQ,
+	DBG_BINDING_CFM,
+	DBG_BINDING_IND,
+
+	DBG_CUSTOM_MSG_REQ,
+	DBG_CUSTOM_MSG_CFM,
+	DBG_CUSTOM_MSG_IND,
+
+	DBG_GPIO_WRITE_REQ,
+	DBG_GPIO_WRITE_CFM,
+	DBG_GPIO_READ_REQ,
+	DBG_GPIO_READ_CFM,
+	DBG_GPIO_INIT_REQ,
+	DBG_GPIO_INIT_CFM,
+
+	/// EF usrdata read request
+	DBG_EF_USRDATA_READ_REQ,
+	/// EF usrdata read confirm
+	DBG_EF_USRDATA_READ_CFM,
+	/// Memory block read request
+	DBG_MEM_BLOCK_READ_REQ,
+	/// Memory block read confirm
+	DBG_MEM_BLOCK_READ_CFM,
+
+	DBG_PWM_INIT_REQ,
+	DBG_PWM_INIT_CFM,
+	DBG_PWM_DEINIT_REQ,
+	DBG_PWM_DEINIT_CFM,
+
+	/// Max number of Debug messages
+	DBG_MAX,
+};
+
+/// Structure containing the parameters of the @ref DBG_MEM_READ_REQ message.
+struct dbg_mem_read_req {
+	u32 memaddr;
+};
+
+/// Structure containing the parameters of the @ref DBG_MEM_READ_CFM message.
+struct dbg_mem_read_cfm {
+	u32 memaddr;
+	u32 memdata;
+};
+
+/// Structure containing the parameters of the @ref DBG_MEM_WRITE_REQ message.
+struct dbg_mem_write_req {
+	u32 memaddr;
+	u32 memdata;
+};
+
+/// Structure containing the parameters of the @ref DBG_MEM_WRITE_CFM message.
+struct dbg_mem_write_cfm {
+	u32 memaddr;
+	u32 memdata;
+};
+
+/// Structure containing the parameters of the @ref DBG_MEM_MASK_WRITE_REQ
+/// message.
+struct dbg_mem_mask_write_req {
+	u32 memaddr;
+	u32 memmask;
+	u32 memdata;
+};
+
+/// Structure containing the parameters of the @ref DBG_MEM_MASK_WRITE_CFM
+/// message.
+struct dbg_mem_mask_write_cfm {
+	u32 memaddr;
+	u32 memdata;
+};
+
+struct dbg_rftest_cmd_req {
+	u32 cmd;
+	u32 argc;
+	u8 argv[30];
+};
+
+struct dbg_rftest_cmd_cfm {
+	u32 rftest_result[18];
+};
+
+struct dbg_gpio_write_req {
+	u8 gpio_idx;
+	u8 gpio_val;
+};
+
+struct dbg_gpio_read_req {
+	u8 gpio_idx;
+};
+
+struct dbg_gpio_read_cfm {
+	u8 gpio_idx;
+	u8 gpio_val;
+};
+
+struct dbg_gpio_init_req {
+	u8 gpio_idx;
+	u8 gpio_dir; // 1 output, 0 input;
+	u8 gpio_val; // for output, 1 high, 0 low;
+};
+
+/// Structure containing the parameters of the @ref DBG_SET_MOD_FILTER_REQ
+/// message.
+struct dbg_set_mod_filter_req {
+	/// Bit field indicating for each module if the traces are enabled or not
+	u32 mod_filter;
+};
+
+/// Structure containing the parameters of the @ref DBG_SEV_MOD_FILTER_REQ
+/// message.
+struct dbg_set_sev_filter_req {
+	/// Bit field indicating the severity threshold for the traces
+	u32 sev_filter;
+};
+
+/// Structure containing the parameters of the @ref DBG_GET_SYS_STAT_CFM
+/// message.
+struct dbg_get_sys_stat_cfm {
+	/// Time spent in CPU sleep since last reset of the system statistics
+	u32 cpu_sleep_time;
+	/// Time spent in DOZE since last reset of the system statistics
+	u32 doze_time;
+	/// Total time spent since last reset of the system statistics
+	u32 stats_time;
+};
+
+/// Structure containing the parameters of the @ref DBG_MEM_BLOCK_WRITE_REQ
+/// message.
+struct dbg_mem_block_write_req {
+	u32 memaddr;
+	u32 memsize;
+	u32 memdata[1024 / sizeof(u32)];
+};
+
+/// Structure containing the parameters of the @ref DBG_MEM_BLOCK_WRITE_CFM
+/// message.
+struct dbg_mem_block_write_cfm {
+	u32 wstatus;
+};
+
+/// Structure containing the parameters of the @ref DBG_START_APP_REQ message.
+struct dbg_start_app_req {
+	u32 bootaddr;
+	u32 boottype;
+};
+
+/// Structure containing the parameters of the @ref DBG_START_APP_CFM message.
+struct dbg_start_app_cfm {
+	u32 bootstatus;
+};
+
+enum {
+	HOST_START_APP_AUTO = 1,
+	HOST_START_APP_CUSTOM,
+	HOST_START_APP_FNCALL = 4,
+	HOST_START_APP_DUMMY = 5,
+};
+
+///////////////////////////////////////////////////////////////////////////////
+/////////// For TDLS messages
+///////////////////////////////////////////////////////////////////////////////
+
+/// List of messages related to the task.
+enum tdls_msg_tag {
+	/// TDLS channel Switch Request.
+	TDLS_CHAN_SWITCH_REQ = LMAC_FIRST_MSG(TASK_TDLS),
+	/// TDLS channel switch confirmation.
+	TDLS_CHAN_SWITCH_CFM,
+	/// TDLS channel switch indication.
+	TDLS_CHAN_SWITCH_IND,
+	/// TDLS channel switch to base channel indication.
+	TDLS_CHAN_SWITCH_BASE_IND,
+	/// TDLS cancel channel switch request.
+	TDLS_CANCEL_CHAN_SWITCH_REQ,
+	/// TDLS cancel channel switch confirmation.
+	TDLS_CANCEL_CHAN_SWITCH_CFM,
+	/// TDLS peer power save indication.
+	TDLS_PEER_PS_IND,
+	/// TDLS peer traffic indication request.
+	TDLS_PEER_TRAFFIC_IND_REQ,
+	/// TDLS peer traffic indication confirmation.
+	TDLS_PEER_TRAFFIC_IND_CFM,
+
+	/// MAX number of messages
+	TDLS_MAX
+};
+
+/// Structure containing the parameters of the @ref TDLS_CHAN_SWITCH_REQ message
+struct tdls_chan_switch_req {
+	/// Index of the VIF
+	u8 vif_index;
+	/// STA Index
+	u8 sta_idx;
+	/// MAC address of the TDLS station
+	struct mac_addr peer_mac_addr;
+	bool initiator;
+	/// Band (2.4GHz or 5GHz)
+	u8 band;
+	/// Channel type: 20,40,80,160 or 80+80 MHz
+	u8 type;
+	/// Frequency for Primary 20MHz channel (in MHz)
+	u16 prim20_freq;
+	/// Frequency for Center of the contiguous channel or center of Primary
+	/// 80+80
+	u16 center1_freq;
+	/// Frequency for Center of the non-contiguous secondary 80+80
+	u16 center2_freq;
+	/// TX power (in dBm)
+	s8 tx_power;
+	/// Operating class
+	u8 op_class;
+};
+
+/// Structure containing the parameters of the @ref TDLS_CANCEL_CHAN_SWITCH_REQ
+/// message
+struct tdls_cancel_chan_switch_req {
+	/// Index of the VIF
+	u8 vif_index;
+	/// STA Index
+	u8 sta_idx;
+	/// MAC address of the TDLS station
+	struct mac_addr peer_mac_addr;
+};
+
+/// Structure containing the parameters of the @ref TDLS_CHAN_SWITCH_CFM message
+struct tdls_chan_switch_cfm {
+	/// Status of the operation
+	u8 status;
+};
+
+/// Structure containing the parameters of the @ref TDLS_CANCEL_CHAN_SWITCH_CFM
+/// message
+struct tdls_cancel_chan_switch_cfm {
+	/// Status of the operation
+	u8 status;
+};
+
+/// Structure containing the parameters of the @ref TDLS_CHAN_SWITCH_IND message
+struct tdls_chan_switch_ind {
+	/// VIF Index
+	u8 vif_index;
+	/// Channel Context Index
+	u8 chan_ctxt_index;
+	/// Status of the operation
+	u8 status;
+};
+
+/// Structure containing the parameters of the @ref TDLS_CHAN_SWITCH_BASE_IND
+/// message
+struct tdls_chan_switch_base_ind {
+	/// VIF Index
+	u8 vif_index;
+	/// Channel Context index
+	u8 chan_ctxt_index;
+};
+
+/// Structure containing the parameters of the @ref TDLS_PEER_PS_IND message
+struct tdls_peer_ps_ind {
+	/// VIF Index
+	u8 vif_index;
+	/// STA Index
+	u8 sta_idx;
+	/// MAC ADDR of the TDLS STA
+	struct mac_addr peer_mac_addr;
+	/// Flag to indicate if the TDLS peer is going to sleep
+	bool ps_on;
+};
+
+/// Structure containing the parameters of the @ref TDLS_PEER_TRAFFIC_IND_REQ
+/// message
+struct tdls_peer_traffic_ind_req {
+	/// VIF Index
+	u8 vif_index;
+	/// STA Index
+	u8 sta_idx;
+	// MAC ADDR of the TDLS STA
+	struct mac_addr peer_mac_addr;
+	/// Dialog token
+	u8 dialog_token;
+	/// TID of the latest MPDU transmitted over the TDLS direct link to the TDLS
+	/// STA
+	u8 last_tid;
+	/// Sequence number of the latest MPDU transmitted over the TDLS direct link
+	/// to the TDLS STA
+	u16 last_sn;
+};
+
+/// Structure containing the parameters of the @ref TDLS_PEER_TRAFFIC_IND_CFM
+/// message
+struct tdls_peer_traffic_ind_cfm {
+	/// Status of the operation
+	u8 status;
+};
+
+#endif // LMAC_MSG_H_
diff --git a/drivers/net/wireless/aic/aic8800_fdrv/lmac_types.h b/drivers/net/wireless/aic/aic8800_fdrv/lmac_types.h
new file mode 100644
index 0000000000000..76f251607a7b9
--- /dev/null
+++ b/drivers/net/wireless/aic/aic8800_fdrv/lmac_types.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ ****************************************************************************************
+ *
+ * Copyright (C) 2020 AIC semiconductor.
+ *
+ * @file co_types.h
+ *
+ * @brief Provides the kernel fixed-width types used by LMAC interfaces.
+ *
+ ****************************************************************************************
+ */
+
+#ifndef _LMAC_INT_H_
+#define _LMAC_INT_H_
+
+#include <linux/types.h>
+#include <linux/version.h>
+#include <linux/bits.h>
+
+/// @} CO_INT
+#endif // _LMAC_INT_H_
diff --git a/drivers/net/wireless/aic/aic8800_fdrv/reg_access.h b/drivers/net/wireless/aic/aic8800_fdrv/reg_access.h
new file mode 100644
index 0000000000000..2654ab8523436
--- /dev/null
+++ b/drivers/net/wireless/aic/aic8800_fdrv/reg_access.h
@@ -0,0 +1,146 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ ******************************************************************************
+ * Copyright (C) 2020 AIC semiconductor.
+ *
+ * @brief Definitions and macros for MAC HW and platform register accesses
+ *
+ ******************************************************************************
+ */
+
+#ifndef REG_ACCESS_H_
+#define REG_ACCESS_H_
+
+/*****************************************************************************
+ * Addresses within RWNX_ADDR_SYSTEM
+ *****************************************************************************/
+/* Shard RAM */
+#define SHARED_RAM_START_ADDR 0x00000000
+
+/* IPC registers */
+#define IPC_REG_BASE_ADDR 0x00800000
+
+/* System Controller Registers */
+#define SYSCTRL_SIGNATURE_ADDR    0x00900000
+// old diag register name
+#define SYSCTRL_DIAG_CONF_ADDR    0x00900068
+#define SYSCTRL_PHYDIAG_CONF_ADDR 0x00900074
+#define SYSCTRL_RIUDIAG_CONF_ADDR 0x00900078
+// new diag register name
+#define SYSCTRL_DIAG_CONF0        0x00900064
+#define SYSCTRL_DIAG_CONF1        0x00900068
+#define SYSCTRL_DIAG_CONF2        0x00900074
+#define SYSCTRL_DIAG_CONF3        0x00900078
+#define SYSCTRL_MISC_CNTL_ADDR    0x009000E0
+#define BOOTROM_ENABLE            BIT(4)
+#define FPGA_B_RESET              BIT(1)
+#define SOFT_RESET                BIT(0)
+
+/* MAC platform */
+#define NXMAC_VERSION_1_ADDR         0x00B00004
+#define NXMAC_MU_MIMO_TX_BIT         BIT(19)
+#define NXMAC_BFMER_BIT              BIT(18)
+#define NXMAC_BFMEE_BIT              BIT(17)
+#define NXMAC_MAC_80211MH_FORMAT_BIT BIT(16)
+#define NXMAC_COEX_BIT               BIT(14)
+#define NXMAC_WAPI_BIT               BIT(13)
+#define NXMAC_TPC_BIT                BIT(12)
+#define NXMAC_VHT_BIT                BIT(11)
+#define NXMAC_HT_BIT                 BIT(10)
+#define NXMAC_RCE_BIT                BIT(8)
+#define NXMAC_CCMP_BIT               BIT(7)
+#define NXMAC_TKIP_BIT               BIT(6)
+#define NXMAC_WEP_BIT                BIT(5)
+#define NXMAC_SECURITY_BIT           BIT(4)
+#define NXMAC_SME_BIT                BIT(3)
+#define NXMAC_HCCA_BIT               BIT(2)
+#define NXMAC_EDCA_BIT               BIT(1)
+#define NXMAC_QOS_BIT                BIT(0)
+
+#define NXMAC_RX_CNTRL_ADDR                   0x00B00060
+#define NXMAC_EN_DUPLICATE_DETECTION_BIT      BIT(31)
+#define NXMAC_ACCEPT_UNKNOWN_BIT              BIT(30)
+#define NXMAC_ACCEPT_OTHER_DATA_FRAMES_BIT    BIT(29)
+#define NXMAC_ACCEPT_QO_S_NULL_BIT            BIT(28)
+#define NXMAC_ACCEPT_QCFWO_DATA_BIT           BIT(27)
+#define NXMAC_ACCEPT_Q_DATA_BIT               BIT(26)
+#define NXMAC_ACCEPT_CFWO_DATA_BIT            BIT(25)
+#define NXMAC_ACCEPT_DATA_BIT                 BIT(24)
+#define NXMAC_ACCEPT_OTHER_CNTRL_FRAMES_BIT   BIT(23)
+#define NXMAC_ACCEPT_CF_END_BIT               BIT(22)
+#define NXMAC_ACCEPT_ACK_BIT                  BIT(21)
+#define NXMAC_ACCEPT_CTS_BIT                  BIT(20)
+#define NXMAC_ACCEPT_RTS_BIT                  BIT(19)
+#define NXMAC_ACCEPT_PS_POLL_BIT              BIT(18)
+#define NXMAC_ACCEPT_BA_BIT                   BIT(17)
+#define NXMAC_ACCEPT_BAR_BIT                  BIT(16)
+#define NXMAC_ACCEPT_OTHER_MGMT_FRAMES_BIT    BIT(15)
+#define NXMAC_ACCEPT_BFMEE_FRAMES_BIT         BIT(14)
+#define NXMAC_ACCEPT_ALL_BEACON_BIT           BIT(13)
+#define NXMAC_ACCEPT_NOT_EXPECTED_BA_BIT      BIT(12)
+#define NXMAC_ACCEPT_DECRYPT_ERROR_FRAMES_BIT BIT(11)
+#define NXMAC_ACCEPT_BEACON_BIT               BIT(10)
+#define NXMAC_ACCEPT_PROBE_RESP_BIT           BIT(9)
+#define NXMAC_ACCEPT_PROBE_REQ_BIT            BIT(8)
+#define NXMAC_ACCEPT_MY_UNICAST_BIT           BIT(7)
+#define NXMAC_ACCEPT_UNICAST_BIT              BIT(6)
+#define NXMAC_ACCEPT_ERROR_FRAMES_BIT         BIT(5)
+#define NXMAC_ACCEPT_OTHER_BSSID_BIT          BIT(4)
+#define NXMAC_ACCEPT_BROADCAST_BIT            BIT(3)
+#define NXMAC_ACCEPT_MULTICAST_BIT            BIT(2)
+#define NXMAC_DONT_DECRYPT_BIT                BIT(1)
+#define NXMAC_EXC_UNENCRYPTED_BIT             BIT(0)
+
+#define NXMAC_DEBUG_PORT_SEL_ADDR     0x00B00510
+#define NXMAC_SW_SET_PROFILING_ADDR   0x00B08564
+#define NXMAC_SW_CLEAR_PROFILING_ADDR 0x00B08568
+
+/* Modem Status */
+#define MDM_HDMCONFIG_ADDR 0x00C00000
+
+/* Clock gating configuration */
+#define MDM_MEMCLKCTRL0_ADDR   0x00C00848
+#define MDM_CLKGATEFCTRL0_ADDR 0x00C00874
+#define CRM_CLKGATEFCTRL0_ADDR 0x00940010
+
+/* AGC (trident) */
+#define AGC_RWNXAGCCNTL_ADDR 0x00C02060
+
+/* LDPC RAM*/
+#define PHY_LDPC_RAM_ADDR 0x00C09000
+
+/* FCU (elma )*/
+#define FCU_RWNXFCAGCCNTL_ADDR 0x00C09034
+
+/* AGC RAM */
+#define PHY_AGC_UCODE_ADDR 0x00C0A000
+
+/* RIU */
+#define RIU_RWNXVERSION_ADDR         0x00C0B000
+#define RIU_RWNXDYNAMICCONFIG_ADDR   0x00C0B008
+#define RIU_AGCMEMBISTSTAT_ADDR      0x00C0B238
+#define RIU_AGCMEMSIGNATURESTAT_ADDR 0x00C0B23C
+#define RIU_RWNXAGCCNTL_ADDR         0x00C0B390
+
+/* FCU RAM */
+#define PHY_FCU_UCODE_ADDR 0x00C0E000
+
+/* RF ITF */
+#define FPGAB_MPIF_SEL_ADDR       0x00C10030
+#define RF_V6_DIAGPORT_CONF1_ADDR 0x00C10010
+#define RF_v6_PHYDIAG_CONF1_ADDR  0x00C10018
+
+#define RF_V7_DIAGPORT_CONF1_ADDR 0x00F10010
+#define RF_v7_PHYDIAG_CONF1_ADDR  0x00F10018
+
+/*****************************************************************************
+ * Macros for generated register files
+ *****************************************************************************/
+/* Macros for IPC registers access (used in reg_ipc_app.h) */
+#define REG_IPC_APP_RD(env, INDEX)                                             \
+	(*(u32 *)((u8 *)(env) + IPC_REG_BASE_ADDR + 4 * (INDEX)))
+
+#define REG_IPC_APP_WR(env, INDEX, value)                                      \
+	(*(u32 *)((u8 *)(env) + IPC_REG_BASE_ADDR + 4 * (INDEX)) = value)
+
+#endif /* REG_ACCESS_H_ */
diff --git a/drivers/net/wireless/aic/aic8800_fdrv/rwnx_cmds.c b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_cmds.c
new file mode 100644
index 0000000000000..2bb0c58dc984e
--- /dev/null
+++ b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_cmds.c
@@ -0,0 +1,471 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ ******************************************************************************
+ * Copyright (C) 2020 AIC semiconductor.
+ *
+ * @brief Handles queueing (push to IPC, ack/cfm from IPC) of commands issued to
+ * LMAC FW
+ *
+ ******************************************************************************
+ */
+
+#include "rwnx_cmds.h"
+#include "aicwf_txrxif.h"
+#include "rwnx_defs.h"
+#include "rwnx_events.h"
+#include "rwnx_strs.h"
+#include <linux/list.h>
+#ifdef AICWF_SDIO_SUPPORT
+#include "aicwf_sdio.h"
+#else
+#include "aicwf_usb.h"
+#endif
+
+static void cmd_dump(const struct rwnx_cmd *cmd)
+{
+	if (!cmd) {
+		AICWFDBG(LOGDEBUG, KERN_ERR "AICWF %s cmd is null\n", __func__);
+		return;
+	}
+	AICWFDBG(LOGDEBUG, KERN_CRIT
+		 "tkn[%d]  flags:%04x  result:%3d  cmd:%4d-%-24s - reqcfm(%4d-%-s)\n",
+		 cmd->tkn, cmd->flags, cmd->result, cmd->id, RWNX_ID2STR(cmd->id),
+		 cmd->reqid,
+		 cmd->reqid != (lmac_msg_id_t)-1 ? RWNX_ID2STR(cmd->reqid) : "none");
+}
+
+static void cmd_complete(struct rwnx_cmd_mgr *cmd_mgr, struct rwnx_cmd *cmd)
+{
+	// RWNX_DBG(RWNX_FN_ENTRY_STR); //debug trace
+	lockdep_assert_held(&cmd_mgr->lock);
+
+	list_del(&cmd->list);
+	cmd_mgr->queue_sz--;
+
+	cmd->flags |= RWNX_CMD_FLAG_DONE;
+	if (cmd->flags & RWNX_CMD_FLAG_NONBLOCK) {
+		rwnx_cmd_free(cmd); // kfree(cmd);
+	} else {
+		if (RWNX_CMD_WAIT_COMPLETE(cmd->flags)) {
+			cmd->result = 0;
+			complete(&cmd->complete);
+		}
+	}
+}
+
+int cmd_mgr_queue_force_defer(struct rwnx_cmd_mgr *cmd_mgr,
+			      struct rwnx_cmd *cmd)
+{
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+#ifdef AIC8800_CREATE_TRACE_POINTS
+	trace_msg_send(cmd->id);
+#endif
+	spin_lock_bh(&cmd_mgr->lock);
+
+	if (cmd_mgr->state == RWNX_CMD_MGR_STATE_CRASHED) {
+		AICWFDBG(LOGDEBUG, KERN_CRIT "AICWF cmd queue crashed\n");
+		cmd->result = -EPIPE;
+		spin_unlock_bh(&cmd_mgr->lock);
+		return -EPIPE;
+	}
+
+	if (!list_empty(&cmd_mgr->cmds)) {
+		if (cmd_mgr->queue_sz == cmd_mgr->max_queue_sz) {
+			AICWFDBG(LOGDEBUG, KERN_CRIT "AICWF Too many cmds (%d) already queued\n",
+				 cmd_mgr->max_queue_sz);
+			cmd->result = -ENOMEM;
+			spin_unlock_bh(&cmd_mgr->lock);
+			return -ENOMEM;
+		}
+	}
+
+	cmd->flags |= RWNX_CMD_FLAG_WAIT_PUSH;
+
+	if (cmd->flags & RWNX_CMD_FLAG_REQ_CFM)
+		cmd->flags |= RWNX_CMD_FLAG_WAIT_CFM;
+
+	cmd->tkn = cmd_mgr->next_tkn++;
+	cmd->result = -EINTR;
+
+	if (!(cmd->flags & RWNX_CMD_FLAG_NONBLOCK))
+		init_completion(&cmd->complete);
+
+	list_add_tail(&cmd->list, &cmd_mgr->cmds);
+	cmd_mgr->queue_sz++;
+	spin_unlock_bh(&cmd_mgr->lock);
+
+	WAKE_CMD_WORK(cmd_mgr);
+	return 0;
+}
+
+static int cmd_mgr_queue(struct rwnx_cmd_mgr *cmd_mgr, struct rwnx_cmd *cmd)
+{
+#ifdef AICWF_SDIO_SUPPORT
+	int ret;
+	struct aic_sdio_dev *sdiodev =
+		container_of(cmd_mgr, struct aic_sdio_dev, cmd_mgr);
+#endif
+	bool defer_push = false;
+
+	// RWNX_DBG(RWNX_FN_ENTRY_STR); //debug trace
+#ifdef AIC8800_CREATE_TRACE_POINTS
+	trace_msg_send(cmd->id);
+#endif
+	spin_lock_bh(&cmd_mgr->lock);
+
+	if (cmd_mgr->state == RWNX_CMD_MGR_STATE_CRASHED) {
+		AICWFDBG(LOGDEBUG, KERN_CRIT "AICWF cmd queue crashed\n");
+		cmd->result = -EPIPE;
+		spin_unlock_bh(&cmd_mgr->lock);
+		return -EPIPE;
+	}
+
+	if (!list_empty(&cmd_mgr->cmds)) {
+		struct rwnx_cmd *last;
+
+		if (cmd_mgr->queue_sz == cmd_mgr->max_queue_sz) {
+			AICWFDBG(LOGDEBUG, KERN_CRIT "AICWF Too many cmds (%d) already queued\n",
+				 cmd_mgr->max_queue_sz);
+			cmd->result = -ENOMEM;
+			spin_unlock_bh(&cmd_mgr->lock);
+			return -ENOMEM;
+		}
+		last = list_entry(cmd_mgr->cmds.prev, struct rwnx_cmd, list);
+		if (last->flags & (RWNX_CMD_FLAG_WAIT_ACK | RWNX_CMD_FLAG_WAIT_PUSH |
+				   RWNX_CMD_FLAG_WAIT_CFM)) {
+			cmd->flags |= RWNX_CMD_FLAG_WAIT_PUSH;
+			defer_push = true;
+		}
+	}
+
+	if (cmd->flags & RWNX_CMD_FLAG_REQ_CFM)
+		cmd->flags |= RWNX_CMD_FLAG_WAIT_CFM;
+
+	cmd->tkn = cmd_mgr->next_tkn++;
+	cmd->result = -EINTR;
+
+	if (!(cmd->flags & RWNX_CMD_FLAG_NONBLOCK))
+		init_completion(&cmd->complete);
+
+	list_add_tail(&cmd->list, &cmd_mgr->cmds);
+	cmd_mgr->queue_sz++;
+
+#ifdef AICWF_ARP_OFFLOAD
+	if (cmd->a2e_msg->id == ME_TRAFFIC_IND_REQ ||
+	    cmd->a2e_msg->id == MM_SET_ARPOFFLOAD_REQ) {
+#else
+	if (cmd->a2e_msg->id == ME_TRAFFIC_IND_REQ) {
+#endif
+		defer_push = true;
+		cmd->flags |= RWNX_CMD_FLAG_WAIT_PUSH;
+	}
+
+	spin_unlock_bh(&cmd_mgr->lock);
+	if (!defer_push) {
+#ifdef AICWF_SDIO_SUPPORT
+		aicwf_set_cmd_tx((void *)(sdiodev), cmd->a2e_msg,
+				 sizeof(struct lmac_msg) + cmd->a2e_msg->param_len);
+#else
+		aicwf_set_cmd_tx((void *)(usbdev), cmd->a2e_msg,
+				 sizeof(struct lmac_msg) + cmd->a2e_msg->param_len);
+#endif
+		kfree(cmd->a2e_msg);
+	} else {
+		if (cmd_mgr->queue_sz <= 1)
+			WAKE_CMD_WORK(cmd_mgr);
+		return 0;
+	}
+
+	if (!(cmd->flags & RWNX_CMD_FLAG_NONBLOCK)) {
+		unsigned long tout =
+			msecs_to_jiffies(RWNX_80211_CMD_TIMEOUT_MS * cmd_mgr->queue_sz);
+		if (!wait_for_completion_timeout(&cmd->complete, tout)) {
+			AICWFDBG(LOGDEBUG, KERN_CRIT "AICWF cmd timed-out\n");
+#ifdef AICWF_SDIO_SUPPORT
+			ret = aicwf_sdio_writeb(sdiodev, sdiodev->sdio_reg.wakeup_reg, 2);
+			if (ret < 0) {
+				sdio_err("reg:%d write failed!\n",
+					 sdiodev->sdio_reg.wakeup_reg);
+			}
+#endif
+
+			cmd_dump(cmd);
+			spin_lock_bh(&cmd_mgr->lock);
+			cmd_mgr->state = RWNX_CMD_MGR_STATE_CRASHED;
+			if (!(cmd->flags & RWNX_CMD_FLAG_DONE)) {
+				cmd->result = -ETIMEDOUT;
+				cmd_complete(cmd_mgr, cmd);
+			}
+			spin_unlock_bh(&cmd_mgr->lock);
+		} else {
+			rwnx_cmd_free(cmd); // kfree(cmd);
+			if (!list_empty(&cmd_mgr->cmds))
+				WAKE_CMD_WORK(cmd_mgr);
+		}
+	} else {
+		cmd->result = 0;
+	}
+
+	return 0;
+}
+
+static int cmd_mgr_llind(struct rwnx_cmd_mgr *cmd_mgr, struct rwnx_cmd *cmd)
+{
+	struct rwnx_cmd *cur, *acked = NULL;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	spin_lock_bh(&cmd_mgr->lock);
+	list_for_each_entry(cur, &cmd_mgr->cmds, list) {
+		if (!acked) {
+			if (cur->tkn == cmd->tkn) {
+				if (WARN_ON_ONCE(cur != cmd))
+					cmd_dump(cmd);
+				acked = cur;
+				continue;
+			}
+		}
+		if (cur->flags & RWNX_CMD_FLAG_WAIT_PUSH)
+			break;
+	}
+	if (!acked) {
+		AICWFDBG(LOGDEBUG, KERN_CRIT "Error: acked cmd not found\n");
+	} else {
+		cmd->flags &= ~RWNX_CMD_FLAG_WAIT_ACK;
+		if (RWNX_CMD_WAIT_COMPLETE(cmd->flags))
+			cmd_complete(cmd_mgr, cmd);
+	}
+	spin_unlock(&cmd_mgr->lock);
+
+	return 0;
+}
+
+void cmd_mgr_task_process(struct work_struct *work)
+{
+	struct rwnx_cmd_mgr *cmd_mgr =
+		container_of(work, struct rwnx_cmd_mgr, cmd_work);
+	struct rwnx_cmd *cur, *next = NULL;
+	unsigned long tout;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	while (1) {
+		next = NULL;
+		spin_lock_bh(&cmd_mgr->lock);
+
+		list_for_each_entry(cur, &cmd_mgr->cmds, list) {
+			if (cur->flags & RWNX_CMD_FLAG_WAIT_PUSH)
+				next = cur;
+			break;
+		}
+		spin_unlock_bh(&cmd_mgr->lock);
+
+		if (!next)
+			break;
+
+		if (next) {
+#ifdef AICWF_SDIO_SUPPORT
+			struct aic_sdio_dev *sdiodev =
+				container_of(cmd_mgr, struct aic_sdio_dev, cmd_mgr);
+#endif
+			next->flags &= ~RWNX_CMD_FLAG_WAIT_PUSH;
+
+#ifdef AICWF_SDIO_SUPPORT
+			aicwf_set_cmd_tx((void *)(sdiodev), next->a2e_msg,
+					 sizeof(struct lmac_msg) +
+						next->a2e_msg->param_len);
+#else
+			aicwf_set_cmd_tx((void *)(usbdev), next->a2e_msg,
+					 sizeof(struct lmac_msg) +
+						next->a2e_msg->param_len);
+#endif
+			kfree(next->a2e_msg);
+
+			tout =
+				msecs_to_jiffies(RWNX_80211_CMD_TIMEOUT_MS * cmd_mgr->queue_sz);
+			if (!wait_for_completion_timeout(&next->complete, tout)) {
+				AICWFDBG(LOGDEBUG, KERN_CRIT "AICWF cmd timed-out\n");
+#ifdef AICWF_SDIO_SUPPORT
+				if (aicwf_sdio_writeb(sdiodev, sdiodev->sdio_reg.wakeup_reg,
+						      2) < 0) {
+					sdio_err("reg:%d write failed!\n",
+						 sdiodev->sdio_reg.wakeup_reg);
+				}
+#endif
+				cmd_dump(next);
+				spin_lock_bh(&cmd_mgr->lock);
+				cmd_mgr->state = RWNX_CMD_MGR_STATE_CRASHED;
+				if (!(next->flags & RWNX_CMD_FLAG_DONE)) {
+					next->result = -ETIMEDOUT;
+					cmd_complete(cmd_mgr, next);
+				}
+				spin_unlock_bh(&cmd_mgr->lock);
+			} else {
+				rwnx_cmd_free(next); // kfree(next);
+			}
+		}
+	}
+}
+
+static int cmd_mgr_run_callback(struct rwnx_hw *rwnx_hw, struct rwnx_cmd *cmd,
+				struct rwnx_cmd_e2amsg *msg, msg_cb_fct cb)
+{
+	int res;
+
+	if (!cb)
+		return 0;
+
+	res = cb(rwnx_hw, cmd, msg);
+
+	return res;
+}
+
+static int cmd_mgr_msgind(struct rwnx_cmd_mgr *cmd_mgr,
+			  struct rwnx_cmd_e2amsg *msg, msg_cb_fct cb)
+{
+#ifdef AICWF_SDIO_SUPPORT
+	struct aic_sdio_dev *sdiodev =
+		container_of(cmd_mgr, struct aic_sdio_dev, cmd_mgr);
+	struct rwnx_hw *rwnx_hw = sdiodev->rwnx_hw;
+#endif
+	struct rwnx_cmd *cmd, *pos;
+	bool found = false;
+
+	// RWNX_DBG(RWNX_FN_ENTRY_STR); //debug trace
+#ifdef AIC8800_CREATE_TRACE_POINTS
+	trace_msg_recv(msg->id);
+#endif
+	// AICWFDBG(LOGDEBUG, "cmd->id=%x\n", msg->id); //debug trace
+	spin_lock_bh(&cmd_mgr->lock);
+	list_for_each_entry_safe(cmd, pos, &cmd_mgr->cmds, list) {
+		if (cmd->reqid == msg->id && (cmd->flags & RWNX_CMD_FLAG_WAIT_CFM)) {
+			if (!cmd_mgr_run_callback(rwnx_hw, cmd, msg, cb)) {
+				found = true;
+				cmd->flags &= ~RWNX_CMD_FLAG_WAIT_CFM;
+				if (WARN(msg->param_len > RWNX_CMD_E2AMSG_LEN_MAX,
+					 "Unexpect E2A msg len %d > %d\n", msg->param_len,
+					 RWNX_CMD_E2AMSG_LEN_MAX))
+					msg->param_len = RWNX_CMD_E2AMSG_LEN_MAX;
+
+				if (cmd->e2a_msg && msg->param_len)
+					memcpy(cmd->e2a_msg, &msg->param, msg->param_len);
+
+				if (RWNX_CMD_WAIT_COMPLETE(cmd->flags))
+					cmd_complete(cmd_mgr, cmd);
+
+				break;
+			}
+		}
+	}
+	spin_unlock_bh(&cmd_mgr->lock);
+
+	if (!found)
+		cmd_mgr_run_callback(rwnx_hw, NULL, msg, cb);
+
+	return 0;
+}
+
+static void cmd_mgr_print(struct rwnx_cmd_mgr *cmd_mgr)
+{
+	struct rwnx_cmd *cur;
+
+	spin_lock_bh(&cmd_mgr->lock);
+	RWNX_DBG("q_sz/max: %2d / %2d - next tkn: %d\n", cmd_mgr->queue_sz,
+		 cmd_mgr->max_queue_sz, cmd_mgr->next_tkn);
+	list_for_each_entry(cur, &cmd_mgr->cmds, list) {
+		cmd_dump(cur);
+	}
+	spin_unlock_bh(&cmd_mgr->lock);
+}
+
+static void cmd_mgr_drain(struct rwnx_cmd_mgr *cmd_mgr)
+{
+	struct rwnx_cmd *cur, *nxt;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	spin_lock_bh(&cmd_mgr->lock);
+	list_for_each_entry_safe(cur, nxt, &cmd_mgr->cmds, list) {
+		list_del(&cur->list);
+		cmd_mgr->queue_sz--;
+		if (!(cur->flags & RWNX_CMD_FLAG_NONBLOCK))
+			complete(&cur->complete);
+	}
+	spin_unlock_bh(&cmd_mgr->lock);
+}
+
+void rwnx_cmd_mgr_init(struct rwnx_cmd_mgr *cmd_mgr)
+{
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	INIT_LIST_HEAD(&cmd_mgr->cmds);
+	cmd_mgr->state = RWNX_CMD_MGR_STATE_INITED;
+	spin_lock_init(&cmd_mgr->lock);
+	cmd_mgr->max_queue_sz = RWNX_CMD_MAX_QUEUED;
+	cmd_mgr->queue = &cmd_mgr_queue;
+	cmd_mgr->print = &cmd_mgr_print;
+	cmd_mgr->drain = &cmd_mgr_drain;
+	cmd_mgr->llind = &cmd_mgr_llind;
+	cmd_mgr->msgind = &cmd_mgr_msgind;
+
+	INIT_WORK(&cmd_mgr->cmd_work, cmd_mgr_task_process);
+	cmd_mgr->cmd_wq = create_singlethread_workqueue("cmd_wq");
+	if (!cmd_mgr->cmd_wq) {
+		txrx_err("insufficient memory to create cmd workqueue.\n");
+		return;
+	}
+}
+
+void rwnx_cmd_mgr_deinit(struct rwnx_cmd_mgr *cmd_mgr)
+{
+	cmd_mgr->print(cmd_mgr);
+	cmd_mgr->drain(cmd_mgr);
+	cmd_mgr->print(cmd_mgr);
+	flush_workqueue(cmd_mgr->cmd_wq);
+	destroy_workqueue(cmd_mgr->cmd_wq);
+	memset(cmd_mgr, 0, sizeof(*cmd_mgr));
+}
+
+void aicwf_set_cmd_tx(void *dev, struct lmac_msg *msg, uint len)
+{
+	u8 *buffer = NULL;
+	u16 index = 0;
+#ifdef AICWF_SDIO_SUPPORT
+	struct aic_sdio_dev *sdiodev = (struct aic_sdio_dev *)dev;
+	struct aicwf_bus *bus = sdiodev->bus_if;
+#else
+	struct aic_usb_dev *usbdev = (struct aic_usb_dev *)dev;
+	struct aicwf_bus *bus = NULL;
+
+	if (!usbdev->state) {
+		AICWFDBG(LOGDEBUG, "down msg\n");
+		return;
+	}
+	bus = usbdev->bus_if;
+#endif
+	buffer = bus->cmd_buf;
+
+	memset(buffer, 0, CMD_BUF_MAX);
+	buffer[0] = (len + 4) & 0x00ff;
+	buffer[1] = ((len + 4) >> 8) & 0x0f;
+	buffer[2] = 0x11;
+
+	buffer[3] = aic_chip_cmd_hdr_checksum(sdiodev->rwnx_hw, &buffer[0], 3);
+
+	index += 4;
+	// there is a dummy word
+	index += 4;
+
+	// make sure little endian
+	put_u16(&buffer[index], msg->id);
+	index += 2;
+	put_u16(&buffer[index], msg->dest_id);
+	index += 2;
+	put_u16(&buffer[index], msg->src_id);
+	index += 2;
+	put_u16(&buffer[index], msg->param_len);
+	index += 2;
+	memcpy(&buffer[index], (u8 *)msg->param, msg->param_len);
+
+	aicwf_bus_txmsg(bus, buffer, len + 8);
+}
diff --git a/drivers/net/wireless/aic/aic8800_fdrv/rwnx_cmds.h b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_cmds.h
new file mode 100644
index 0000000000000..adb4bc49c188a
--- /dev/null
+++ b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_cmds.h
@@ -0,0 +1,99 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _RWNX_CMDS_H_
+#define _RWNX_CMDS_H_
+
+#include "lmac_msg.h"
+#include "ipc_shared.h"
+#include <linux/completion.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+
+#define RWNX_80211_CMD_TIMEOUT_MS 6000
+
+#define RWNX_CMD_FLAG_NONBLOCK  BIT(0)
+#define RWNX_CMD_FLAG_REQ_CFM   BIT(1)
+#define RWNX_CMD_FLAG_WAIT_PUSH BIT(2)
+#define RWNX_CMD_FLAG_WAIT_ACK  BIT(3)
+#define RWNX_CMD_FLAG_WAIT_CFM  BIT(4)
+#define RWNX_CMD_FLAG_DONE      BIT(5)
+/* ATM IPC design makes it possible to get the CFM before the ACK,
+ * otherwise this could have simply been a state enum
+ */
+#define RWNX_CMD_WAIT_COMPLETE(flags)                                          \
+	(!((flags) & (RWNX_CMD_FLAG_WAIT_ACK | RWNX_CMD_FLAG_WAIT_CFM)))
+
+#define RWNX_CMD_MAX_QUEUED 16
+
+#define rwnx_cmd_e2amsg         ipc_e2a_msg
+#define rwnx_cmd_a2emsg         lmac_msg
+#define RWNX_CMD_A2EMSG_LEN(m)  (sizeof(struct lmac_msg) + (m)->param_len)
+#define RWNX_CMD_E2AMSG_LEN_MAX (IPC_E2A_MSG_PARAM_SIZE * 4)
+
+struct rwnx_hw;
+struct rwnx_cmd;
+typedef int (*msg_cb_fct)(struct rwnx_hw *rwnx_hw, struct rwnx_cmd *cmd,
+			  struct rwnx_cmd_e2amsg *msg);
+
+static inline void put_u16(u8 *buf, u16 data)
+{
+	buf[0] = (u8)(data & 0x00ff);
+	buf[1] = (u8)((data >> 8) & 0x00ff);
+}
+
+enum rwnx_cmd_mgr_state {
+	RWNX_CMD_MGR_STATE_DEINIT,
+	RWNX_CMD_MGR_STATE_INITED,
+	RWNX_CMD_MGR_STATE_CRASHED,
+};
+
+struct rwnx_cmd {
+	struct list_head list;
+	lmac_msg_id_t id;
+	lmac_msg_id_t reqid;
+	struct rwnx_cmd_a2emsg *a2e_msg;
+	char *e2a_msg;
+	u32 tkn;
+	u16 flags;
+
+	struct completion complete;
+	u32 result;
+	u8 used;
+	int array_id;
+};
+
+struct rwnx_cmd_mgr {
+	enum rwnx_cmd_mgr_state state;
+	spinlock_t lock; //
+	u32 next_tkn;
+	u32 queue_sz;
+	u32 max_queue_sz;
+
+	struct list_head cmds;
+
+	int (*queue)(struct rwnx_cmd_mgr *mgr, struct rwnx_cmd *cmd);
+	int (*llind)(struct rwnx_cmd_mgr *mgr, struct rwnx_cmd *cmd);
+	int (*msgind)(struct rwnx_cmd_mgr *mgr, struct rwnx_cmd_e2amsg *msg, msg_cb_fct cb);
+	void (*print)(struct rwnx_cmd_mgr *mgr);
+	void (*drain)(struct rwnx_cmd_mgr *mgr);
+
+	struct work_struct cmd_work;
+	struct workqueue_struct *cmd_wq;
+};
+
+#define WAKE_CMD_WORK(_cmd_mgr)							\
+	do {									\
+		struct rwnx_cmd_mgr *__mgr = (_cmd_mgr);                        \
+		queue_work((__mgr)->cmd_wq, &__mgr->cmd_work);		        \
+	} while (0)
+
+void rwnx_cmd_mgr_init(struct rwnx_cmd_mgr *cmd_mgr);
+void rwnx_cmd_mgr_deinit(struct rwnx_cmd_mgr *cmd_mgr);
+int cmd_mgr_queue_force_defer(struct rwnx_cmd_mgr *cmd_mgr,
+			      struct rwnx_cmd *cmd);
+void aicwf_set_cmd_tx(void *dev, struct lmac_msg *msg, uint len);
+
+void cmd_mgr_task_process(struct work_struct *work);
+
+void rwnx_cmd_free(struct rwnx_cmd *cmd);
+
+#endif /* _RWNX_CMDS_H_ */
diff --git a/drivers/net/wireless/aic/aic8800_fdrv/rwnx_compat.h b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_compat.h
new file mode 100644
index 0000000000000..9a127b659e2b6
--- /dev/null
+++ b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_compat.h
@@ -0,0 +1,80 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ ******************************************************************************
+ *
+ * Copyright (C) 2020 AIC semiconductor.
+ *
+ * @brief Ensure driver compilation for linux 3.16 to 3.19
+ * To avoid too many #if LINUX_VERSION_CODE if the code, when prototype change
+ * between different kernel version:
+ * - For external function, define a macro whose name is the function name with
+ *   _compat suffix and prototype (actually the number of parameter) of the
+ *   latest version. Then latest version this macro simply call the function
+ *   and for older kernel version it call the function adapting the api.
+ * - For internal function (e.g. cfg80211_ops) do the same but the macro name
+ *   doesn't need to have the _compat suffix when the function is not used
+ *   directly by the driver
+ *
+ ******************************************************************************
+ */
+
+#ifndef _RWNX_COMPAT_H_
+#define _RWNX_COMPAT_H_
+#include <linux/version.h>
+
+#include <linux/bitfield.h>
+
+/* CFG80211 */
+
+#ifdef CONFIG_RWNX_MUMIMO_TX
+#define CONFIG_USER_MAX 2
+#else
+#define CONFIG_USER_MAX 1
+#endif
+
+#define NX_VIRT_DEV_MAX 4
+
+#define NX_REMOTE_STA_MAX_FOR_OLD_IC 10
+#define NX_REMOTE_STA_MAX 32
+
+#define NX_MU_GROUP_MAX 62
+#define NX_TXDESC_CNT 64
+#define NX_TX_MAX_RATES 4
+#define NX_CHAN_CTXT_CNT 3
+
+#ifdef CONFIG_RWNX_BCMC
+#define NX_TXQ_CNT 5
+#else
+#define NX_TXQ_CNT 4
+#endif
+
+#define IEEE80211_MAX_AMPDU_BUF IEEE80211_MAX_AMPDU_BUF_HE
+#define IEEE80211_HE_PHY_CAP6_TRIG_MU_BEAMFORMER_FB                            \
+	IEEE80211_HE_PHY_CAP6_TRIG_MU_BEAMFORMING_PARTIAL_BW_FB
+#define IEEE80211_HE_PHY_CAP6_TRIG_SU_BEAMFORMER_FB                            \
+	IEEE80211_HE_PHY_CAP6_TRIG_SU_BEAMFORMING_FB
+#define IEEE80211_HE_PHY_CAP3_RX_HE_MU_PPDU_FROM_NON_AP_STA                    \
+	IEEE80211_HE_PHY_CAP3_RX_PARTIAL_BW_SU_IN_20MHZ_MU
+
+#define CCFS0(vht) ((vht)->center_freq_seg0_idx)
+#define CCFS1(vht) ((vht)->center_freq_seg1_idx)
+
+#define SURVEY_TIME(s)      ((s)->time)
+#define SURVEY_TIME_BUSY(s) ((s)->time_busy)
+#define STA_TDLS_INITIATOR(sta) ((sta)->tdls_initiator)
+
+#define RX_ENC_HT(s) ((s)->encoding = RX_ENC_HT)
+#define RX_ENC_VHT(s) ((s)->encoding = RX_ENC_VHT)
+#define RX_ENC_HE(s) ((s)->encoding = RX_ENC_HE)
+#define RX_ENC_FLAG_SHORT_GI(s)  ((s)->enc_flags |= RX_ENC_FLAG_SHORT_GI)
+#define RX_ENC_FLAG_SHORT_PRE(s) ((s)->enc_flags |= RX_ENC_FLAG_SHORTPRE)
+#define RX_ENC_FLAG_LDPC(s)      ((s)->enc_flags |= RX_ENC_FLAG_LDPC)
+#define RX_BW_40MHZ(s)           ((s)->bw = RATE_INFO_BW_40)
+#define RX_BW_80MHZ(s)           ((s)->bw = RATE_INFO_BW_80)
+#define RX_BW_160MHZ(s)          ((s)->bw = RATE_INFO_BW_160)
+#define RX_NSS(s)                ((s)->nss)
+
+#ifndef CONFIG_VENDOR_RWNX_AMSDUS_TX
+#endif /* CONFIG_VENDOR_RWNX_AMSDUS_TX */
+
+#endif /* _RWNX_COMPAT_H_ */
diff --git a/drivers/net/wireless/aic/aic8800_fdrv/rwnx_defs.h b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_defs.h
new file mode 100644
index 0000000000000..eae8d04bee9dd
--- /dev/null
+++ b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_defs.h
@@ -0,0 +1,704 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ ******************************************************************************
+ *
+ * Copyright (C) 2020 AIC semiconductor.
+ *
+ * @brief Main driver structure declarations for fullmac driver
+ *
+ ******************************************************************************
+ */
+
+#ifndef _RWNX_DEFS_H_
+#define _RWNX_DEFS_H_
+
+#include <linux/device.h>
+#include <linux/dmapool.h>
+#include <linux/interrupt.h>
+#include <linux/skbuff.h>
+#include <linux/slab.h>
+#include <net/cfg80211.h>
+
+#include "rwnx_cmds.h"
+#include "rwnx_mod_params.h"
+#include "rwnx_mu_group.h"
+#include "rwnx_platform.h"
+#include "rwnx_radar.h"
+#include "rwnx_rx.h"
+#include "rwnx_tx.h"
+#include "rwnx_utils.h"
+#include "rwnx_compat.h"
+#ifdef CONFIG_AIC8800_FILTER_TCP_ACK
+#include "aicwf_tcp_ack.h"
+#endif
+
+#ifdef AICWF_SDIO_SUPPORT
+#include "aicwf_sdio.h"
+#include "sdio_host.h"
+#endif
+
+#include "lmac_mac.h"
+#include "aicwf_chip_ops.h"
+
+#define WPI_HDR_LEN    18
+#define WPI_PN_LEN     16
+#define WPI_PN_OFST    2
+#define WPI_MIC_LEN    16
+#define WPI_KEY_LEN    32
+#define WPI_SUBKEY_LEN 16 // WPI key is actually two 16bytes key
+
+#define LEGACY_PS_ID 0
+#define UAPSD_ID     1
+
+#define PS_SP_INTERRUPTED 255
+#define MAC_ADDR_LEN      6
+
+#ifdef CONFIG_AIC8800_AUTO_CUSTREG
+#define MAX_SEARCH_CNT 10
+
+/// Structure containing the parameters of the auto ccode
+struct scanu_ccode {
+	/// ccode auto/manual switch, auto--true, manual--false
+	bool auto_set;
+	/// ccode find
+	bool ccode_find;
+	/// ccode set
+	bool ccode_set;
+	/// ccode find cnt
+	u8 ccode_cnt;
+	/// ccode rssi
+	s8 ccode_rssi;
+	/// link scan
+	atomic_t link_scan;
+	/// record mac addr
+	struct mac_addr bssid;
+};
+#endif
+
+struct wf_bss_info {
+	u32 length;
+	s8 rssi;
+	u16 chan;
+	enum nl80211_band band; /* Band */
+	u8 width;             /* Channel width  0/1/2--20/40/80 */
+	u16 center_freq;        /* Center frequency */
+	u32 center_freq1;       /* Center frequency 1 */
+	u32 center_freq2;       /* Center frequency 2 */
+	bool ht;
+	bool vht;
+	u8 bssid[ETH_ALEN];
+	u8 ssid_len;
+	unsigned char ssid[33];
+};
+
+/**
+ * struct rwnx_bcn - Information of the beacon in used (AP mode)
+ *
+ * @head: head portion of beacon (before TIM IE)
+ * @tail: tail portion of beacon (after TIM IE)
+ * @ies: extra IEs (not used ?)
+ * @head_len: length of head data
+ * @tail_len: length of tail data
+ * @ies_len: length of extra IEs data
+ * @tim_len: length of TIM IE
+ * @len: Total beacon len (head + tim + tail + extra)
+ * @dtim: dtim period
+ */
+struct rwnx_bcn {
+	u8 *head;
+	u8 *tail;
+	u8 *ies;
+	size_t head_len;
+	size_t tail_len;
+	size_t ies_len;
+	size_t tim_len;
+	size_t len;
+	u8 dtim;
+};
+
+/**
+ * struct rwnx_key - Key information
+ *
+ * @hw_idx: Idx of the key from hardware point of view
+ */
+struct rwnx_key {
+	u8 hw_idx;
+};
+
+/*
+ * Structure containing information about a Mesh Path
+ */
+struct rwnx_mesh_path {
+	struct list_head list;        /* For rwnx_vif.mesh_paths */
+	u8 path_idx;                  /* Path Index */
+	struct mac_addr tgt_mac_addr; /* Target MAC Address */
+	struct rwnx_sta *p_nhop_sta;  /* Pointer to the Next Hop STA */
+};
+
+struct rwnx_mesh_proxy {
+	struct list_head list;        /* For rwnx_vif.mesh_proxy */
+	struct mac_addr ext_sta_addr; /* Address of the External STA */
+	struct mac_addr proxy_addr;   /* Proxy MAC Address */
+	bool local; /* Indicate if interface is a proxy for the device */
+};
+
+/**
+ * struct rwnx_csa - Information for CSA (Channel Switch Announcement)
+ *
+ * @vif: Pointer to the vif doing the CSA
+ * @bcn: Beacon to use after CSA
+ * @elem: IPC buffer to send the new beacon to the fw
+ * @chandef: defines the channel to use after the switch
+ * @count: Current csa counter
+ * @status: Status of the CSA at fw level
+ * @ch_idx: Index of the new channel context
+ * @work: work scheduled at the end of CSA
+ */
+struct rwnx_csa {
+	struct rwnx_vif *vif;
+	struct rwnx_bcn bcn;
+	struct rwnx_ipc_elem_var elem;
+	struct cfg80211_chan_def chandef;
+	int count;
+	int status;
+	int ch_idx;
+	struct work_struct work;
+};
+
+struct apm_probe_sta {
+	u8 sta_mac_addr[6];
+	u8 vif_idx;
+	u64 probe_id;
+	struct work_struct apmprobesta_work;
+	struct workqueue_struct *apmprobesta_wq;
+};
+
+/// Possible States of the TDLS link.
+enum tdls_status_tag {
+	/// TDLS link is not active (no TDLS peer connected)
+	TDLS_LINK_IDLE,
+	/// TDLS Setup Request transmitted
+	TDLS_SETUP_REQ_TX,
+	/// TDLS Setup Response transmitted
+	TDLS_SETUP_RSP_TX,
+	/// TDLS link is active (TDLS peer connected)
+	TDLS_LINK_ACTIVE,
+	/// TDLS Max Number of states.
+	TDLS_STATE_MAX
+};
+
+/*
+ * Structure used to save information relative to the TDLS peer.
+ * This is also linked within the rwnx_hw vifs list.
+ *
+ */
+struct rwnx_tdls {
+	bool active;       /* Indicate if TDLS link is active */
+	bool initiator;    /* Indicate if TDLS peer is the TDLS initiator */
+	bool chsw_en;      /* Indicate if channel switch is enabled */
+	u8 last_tid;       /* TID of the latest MPDU transmitted over the
+			    * TDLS direct link to the TDLS STA
+			    */
+	u16 last_sn;       /* Sequence number of the latest MPDU transmitted
+			    * over the TDLS direct link to the TDLS STA
+			    */
+	bool ps_on;        /* Indicate if the power save is enabled on the
+			    * TDLS STA
+			    */
+	bool chsw_allowed; /* Indicate if TDLS channel switch is allowed */
+};
+
+/**
+ * enum rwnx_ap_flags - AP flags
+ *
+ * @RWNX_AP_ISOLATE: Isolate clients (i.e. don't bridge packets transmitted by
+ *                   one client for another one)
+ */
+enum rwnx_ap_flags {
+	RWNX_AP_ISOLATE = BIT(0),
+};
+
+/*
+ * Structure used to save information relative to the managed interfaces.
+ * This is also linked within the rwnx_hw vifs list.
+ *
+ */
+struct rwnx_vif {
+	struct list_head list;
+	struct rwnx_hw *rwnx_hw;
+	struct wireless_dev wdev;
+	struct net_device *ndev;
+	struct net_device_stats net_stats;
+	struct rwnx_key key[6];
+	unsigned long drv_flags;
+	atomic_t drv_conn_state;
+	u8 drv_vif_index;  /* Identifier of the VIF in driver */
+	u8 vif_index;      /* Identifier of the station in FW */
+	u8 ch_index;       /* Channel context identifier */
+	bool up;           /* Indicate if associated netdev is up
+			    * (i.e. Interface is created at fw level)
+			    */
+	bool use_4addr;    /* Should we use 4addresses mode */
+	bool is_resending; /* Indicate if a frame is being resent on this interface */
+	bool user_mpm;  /* In case of Mesh Point VIF, indicate if MPM is handled by
+			 * userspace
+			 */
+	bool roc_tdls;  /* Indicate if the ROC has been called by a
+			 * TDLS station
+			 */
+	u8 tdls_status; /* Status of the TDLS link */
+	bool tdls_chsw_prohibited; /* Indicate if TDLS Channel Switch is prohibited */
+	bool wep_enabled;          /* 1 if WEP is enabled */
+	bool wep_auth_err; /* 1 if auth status code is not supported auth alg when
+			    * WEP enabled
+			    */
+	enum nl80211_auth_type
+		last_auth_type; /* Authentication type (algorithm) sent in the last
+				 * connection when WEP enabled
+				 */
+	union {
+		struct {
+			struct rwnx_sta *ap; /* Pointer to the peer STA entry allocated for
+					      * the AP
+					      */
+			struct rwnx_sta *tdls_sta; /* Pointer to the TDLS station */
+			bool external_auth; /* Indicate if external authentication is in
+					     * progress
+					     */
+			u32 group_cipher_type;
+			u32 paired_cipher_type;
+			// connected network info start
+			char ssid[32];
+			int ssid_len;
+			u8 bssid[ETH_ALEN];
+			u32 conn_owner_nlportid;
+			bool is_roam;
+			unsigned long link_time;
+			u16 b_interval;
+			u8 dtim_peroid;
+			u16 capability;
+			enum nl80211_auth_type auth_type;
+			// connected network info end
+		} sta;
+		struct {
+			u16 flags;                 /* see rwnx_ap_flags */
+			struct list_head sta_list; /* List of STA connected to the AP */
+			struct rwnx_bcn bcn;       /* beacon */
+			u8 bcmc_index;             /* Index of the BCMC sta to use */
+			struct rwnx_csa *csa;
+
+			struct list_head
+				mpath_list; /* List of Mesh Paths used on this interface */
+			struct list_head proxy_list; /* List of Proxies Information used on
+						      * this interface
+						      */
+			bool create_path;            /* Indicate if we are waiting for a
+						      * MESH_CREATE_PATH_CFM message
+						      */
+			int generation; /* Increased each time the list of Mesh Paths is
+					 * updated
+					 */
+			enum nl80211_mesh_power_mode
+				mesh_pm; /* mesh power save mode currently set in firmware */
+			enum nl80211_mesh_power_mode
+				next_mesh_pm; /* mesh power save mode for next peer */
+		} ap;
+		struct {
+			struct rwnx_vif *master; /* pointer on master interface */
+			struct rwnx_sta *sta_4a;
+		} ap_vlan;
+	};
+
+	u8 key_has_add;
+	u8 is_p2p_vif;
+	struct apm_probe_sta sta_probe;
+
+};
+
+#define RWNX_VIF_TYPE(rwnx_vif) ((rwnx_vif)->wdev.iftype)
+
+/*
+ * Structure used to store information relative to PS mode.
+ *
+ * @active: True when the sta is in PS mode.
+ *          If false, other values should be ignored
+ * @pkt_ready: Number of packets buffered for the sta in drv's txq
+ *             (1 counter for Legacy PS and 1 for U-APSD)
+ * @sp_cnt: Number of packets that remain to be pushed in the service period.
+ *          0 means that no service period is in progress
+ *          (1 counter for Legacy PS and 1 for U-APSD)
+ */
+struct rwnx_sta_ps {
+	bool active;
+	u16 pkt_ready[2];
+	u16 sp_cnt[2];
+};
+
+/**
+ * struct rwnx_rx_rate_stats - Store statistics for RX rates
+ *
+ * @table: Number of received frames for each rate index
+ * @size: Number of entries in @table
+ * @cpt: Total number of received frames
+ * @rate_cnt: Number of rate indexes with at least one received frame
+ */
+struct rwnx_rx_rate_stats {
+	int *table;
+	int size;
+	int cpt;
+	int rate_cnt;
+};
+
+/**
+ * struct rwnx_sta_stats - Structure Used to store statistics specific to a STA
+ *
+ * @last_rx: Hardware vector of the last received frame
+ * @rx_rate: Statistics of the received rates
+ */
+struct rwnx_sta_stats {
+	struct hw_vect last_rx;
+	struct rwnx_rx_rate_stats rx_rate;
+};
+
+/*
+ * Structure used to save information relative to the managed stations.
+ */
+struct rwnx_sta {
+	struct list_head list;
+	u16 aid;                /* association ID */
+	u8 sta_idx;             /* Identifier of the station */
+	u8 vif_idx;             /* Identifier of the VIF (fw id) the station
+				 * belongs to
+				 */
+	u8 vlan_idx;            /* Identifier of the VLAN VIF (fw id) the station
+				 * belongs to (= vif_idx if no vlan in used)
+				 */
+	enum nl80211_band band; /* Band */
+	enum nl80211_chan_width width; /* Channel width */
+	u16 center_freq;               /* Center frequency */
+	u32 center_freq1;              /* Center frequency 1 */
+	u32 center_freq2;              /* Center frequency 2 */
+	u8 ch_idx;                     /* Identifier of the channel
+					* context the station belongs to
+					*/
+	bool qos;                      /* Flag indicating if the station
+					* supports QoS
+					*/
+	u8 acm;                        /* Bitfield indicating which queues
+					* have AC mandatory
+					*/
+	u16 uapsd_tids;        /* Bitfield indicating which tids are subject to
+				* UAPSD
+				*/
+	u8 mac_addr[ETH_ALEN]; /* MAC address of the station */
+	struct rwnx_key key;
+	bool valid;            /* Flag indicating if the entry is valid */
+	struct rwnx_sta_ps ps; /* Information when STA is in PS (AP only) */
+#ifdef CONFIG_RWNX_BFMER
+	struct rwnx_bfmer_report *bfm_report; /* Beamforming report to be used for
+					       * VHT TX Beamforming
+					       */
+#ifdef CONFIG_RWNX_MUMIMO_TX
+	struct rwnx_sta_group_info
+		group_info; /* MU grouping information for the STA */
+#endif              /* CONFIG_RWNX_MUMIMO_TX */
+#endif              /* CONFIG_RWNX_BFMER */
+
+	bool ht;               /* Flag indicating if the station
+				* supports HT
+				*/
+	bool vht;              /* Flag indicating if the station
+				* supports VHT
+				*/
+	u32 ac_param[AC_MAX];  /* EDCA parameters */
+	struct rwnx_tdls tdls; /* TDLS station information */
+	struct rwnx_sta_stats stats;
+	enum nl80211_mesh_power_mode
+		mesh_pm; /*  link-specific mesh power save mode */
+};
+
+static inline const u8 *rwnx_sta_addr(struct rwnx_sta *rwnx_sta)
+{
+	return rwnx_sta->mac_addr;
+}
+
+#ifdef CONFIG_RWNX_SPLIT_TX_BUF
+struct rwnx_amsdu_stats {
+	int done;
+	int failed;
+};
+#endif
+
+struct rwnx_stats {
+	int cfm_balance[NX_TXQ_CNT];
+	unsigned long last_rx, last_tx; /* jiffies */
+	int ampdus_tx[IEEE80211_MAX_AMPDU_BUF];
+	int ampdus_rx[IEEE80211_MAX_AMPDU_BUF];
+	int ampdus_rx_map[4];
+	int ampdus_rx_miss;
+#ifdef CONFIG_RWNX_SPLIT_TX_BUF
+	struct rwnx_amsdu_stats amsdus[NX_TX_PAYLOAD_MAX];
+#endif
+	int amsdus_rx[64];
+};
+
+struct rwnx_sec_phy_chan {
+	u16 prim20_freq;
+	u16 center_freq1;
+	u16 center_freq2;
+	enum nl80211_band band;
+	u8 type;
+};
+
+/* Structure that will contains all RoC information received from cfg80211 */
+struct rwnx_roc_elem {
+	struct wireless_dev *wdev;
+	struct ieee80211_channel *chan;
+	unsigned int duration;
+	u64 cookie;
+	/* Used to avoid call of CFG80211 callback upon expiration of RoC */
+	bool mgmt_roc;
+	/* Indicate if we have switch on the RoC channel */
+	bool on_chan;
+};
+
+/* Structure containing channel survey information received from MAC */
+struct rwnx_survey_info {
+	// Filled
+	u32 filled;
+	// Amount of time in ms the radio spent on the channel
+	u32 chan_time_ms;
+	// Amount of time the primary channel was sensed busy
+	u32 chan_time_busy_ms;
+	// Noise in dbm
+	s8 noise_dbm;
+};
+
+#define RWNX_CH_NOT_SET  0xFF
+#define RWNX_INVALID_VIF 0xFF
+#define RWNX_INVALID_STA 0xFF
+
+/* Structure containing channel context information */
+struct rwnx_chanctx {
+	struct cfg80211_chan_def chan_def; /* channel description */
+	u8 count;                          /* number of vif using this ctxt */
+};
+
+/**
+ * struct rwnx_phy_info - PHY information
+ *
+ * @cnt: Number of PHY interfaces
+ * @cfg: Configuration sent to the firmware
+ * @sec_chan: Channel configuration of the second PHY interface, when present
+ * @limit_bw: Whether to limit the requested channel bandwidth for legacy PHYs
+ */
+struct rwnx_phy_info {
+	u8 cnt;
+	struct phy_cfg_tag cfg;
+	struct rwnx_sec_phy_chan sec_chan;
+	bool limit_bw;
+};
+
+struct defrag_ctrl_info {
+	struct list_head list;
+	u8 sta_idx;
+	u8 tid;
+	u16 sn;
+	u8 next_fn;
+	u16 frm_len;
+	struct sk_buff *skb;
+	struct timer_list defrag_timer;
+	struct rwnx_hw *rwnx_hw;
+};
+
+struct amsdu_subframe_hdr {
+	u8 da[6];
+	u8 sa[6];
+	u16 sublen;
+};
+
+/* rwnx driver status */
+void rwnx_set_conn_state(atomic_t *drv_conn_state, int state);
+
+enum rwnx_drv_connect_status {
+	RWNX_DRV_STATUS_DISCONNECTED = 0,
+	RWNX_DRV_STATUS_DISCONNECTING,
+	RWNX_DRV_STATUS_CONNECTING,
+	RWNX_DRV_STATUS_CONNECTED,
+	RWNX_DRV_STATUS_ROAMING,
+};
+
+struct rwnx_hw {
+	struct rwnx_mod_params *mod_params;
+	struct device *dev;
+#ifdef AICWF_SDIO_SUPPORT
+	struct aic_sdio_dev *sdiodev;
+#endif
+	struct wiphy *wiphy;
+	struct list_head vifs;
+	struct rwnx_vif *
+		vif_table[NX_VIRT_DEV_MAX + NX_REMOTE_STA_MAX]; /* indexed with fw id */
+	struct rwnx_sta sta_table[NX_REMOTE_STA_MAX + NX_VIRT_DEV_MAX];
+	struct rwnx_survey_info survey[SCAN_CHANNEL_MAX];
+	struct cfg80211_scan_request *scan_request;
+	/* The normal scan path owns one SDIO activity until its terminal event. */
+	atomic_t scan_activity_held;
+#ifdef CONFIG_AIC8800_SCHED_SCAN
+	struct cfg80211_sched_scan_request *sched_scan_req;
+#endif
+	struct rwnx_chanctx chanctx_table[NX_CHAN_CTXT_CNT];
+	u8 cur_chanctx;
+
+	u8 monitor_vif; /* FW id of the monitor interface, RWNX_INVALID_VIF if no
+			 * monitor vif at fw level
+			 */
+
+#ifdef CONFIG_AIC8800_FILTER_TCP_ACK
+	/* tcp ack management */
+	struct tcp_ack_manage ack_m;
+#endif
+
+	/* RoC Management */
+	struct rwnx_roc_elem *roc_elem; /* Information provided by cfg80211 in its
+					 * remain on channel request
+					 */
+
+	struct rwnx_cmd_mgr *cmd_mgr;
+
+	struct rwnx_plat *plat;
+
+	spinlock_t tx_lock; //
+	spinlock_t cb_lock; //
+	struct mutex mutex; /* per-device perimeter lock */
+
+	struct tasklet_struct task;
+	struct mm_version_cfm
+		version_cfm; /* Lower layers versions - obtained via MM_VERSION_REQ */
+
+	u32 tcp_pacing_shift;
+
+	/* IPC */
+	struct ipc_host_env_tag *ipc_env;
+#ifdef AICWF_SDIO_SUPPORT
+	struct sdio_host_env_tag sdio_env;
+#endif
+
+	struct rwnx_ipc_elem_pool e2amsgs_pool;
+	struct rwnx_ipc_elem_pool dbgmsgs_pool;
+	struct rwnx_ipc_elem_pool e2aradars_pool;
+	struct rwnx_ipc_elem_var pattern_elem;
+	struct rwnx_ipc_dbgdump_elem dbgdump_elem;
+	struct rwnx_ipc_elem_pool e2arxdesc_pool;
+	struct rwnx_ipc_skb_elem *e2aunsuprxvec_elems;
+	// struct rwnx_ipc_rxbuf_elems rxbuf_elems;
+	struct rwnx_ipc_elem_var scan_ie;
+
+	struct kmem_cache *sw_txhdr_cache;
+
+	struct rwnx_stats stats;
+
+#ifdef CONFIG_PREALLOC_TXQ
+	struct rwnx_txq *txq;
+#else
+	struct rwnx_txq txq[NX_NB_TXQ];
+#endif
+
+	struct rwnx_hwq hwq[NX_TXQ_CNT];
+
+	u64 avail_idx_map;
+	u8 vif_started;
+	bool adding_sta;
+	struct rwnx_phy_info phy;
+
+	struct rwnx_radar radar;
+
+	/* extended capabilities supported */
+	u8 ext_capa[8];
+
+#ifdef CONFIG_RWNX_MUMIMO_TX
+	struct rwnx_mu_info mu;
+#endif
+	u8 is_p2p_alive;
+	u8 is_p2p_connected;
+	struct timer_list p2p_alive_timer;
+	struct rwnx_vif *p2p_dev_vif;
+	atomic_t p2p_alive_timer_count;
+	bool band_5g_support;
+	u8 vendor_info;
+	bool fwlog_en;
+	int testmode;
+	u8 scanning;
+	char country_abbr[4];
+	const struct aic_chip_ops *chip_ops;
+
+	struct list_head defrag_list;
+	spinlock_t defrag_lock; //
+
+	struct work_struct apm_staloss_work;
+	struct workqueue_struct *apm_staloss_wq;
+	u8 apm_vif_idx;
+	u8 sta_mac_addr[6];
+
+	struct wakeup_source *ws_rx;
+	struct wakeup_source *ws_irqrx;
+	struct wakeup_source *ws_tx;
+	struct wakeup_source *ws_pwrctrl;
+	struct wakeup_source *ws_scan;
+
+#ifdef CONFIG_AIC8800_SCHED_SCAN
+	bool is_sched_scan;
+#endif
+
+#ifdef CONFIG_AIC8800_AUTO_CUSTREG
+	struct scanu_ccode ccode;
+#endif
+
+#ifdef CONFIG_AIC8800_TEMP_CONTROL
+	unsigned long started_jiffies;
+	s8 temp;
+#endif
+#ifdef CONFIG_AIC8800_POWER_LIMIT
+	struct work_struct pwlt_wk;
+#endif
+#ifdef CONFIG_AIC8800_REGION_PW
+	struct work_struct region_wk;
+#endif
+	char fw_version[32];
+
+};
+
+u8 *rwnx_build_bcn(struct rwnx_bcn *bcn, struct cfg80211_beacon_data *new);
+
+void rwnx_chanctx_link(struct rwnx_vif *vif, u8 idx,
+		       struct cfg80211_chan_def *chandef);
+void rwnx_chanctx_unlink(struct rwnx_vif *vif);
+int rwnx_chanctx_valid(struct rwnx_hw *rwnx_hw, u8 idx);
+
+extern u8 chip_id;
+
+static inline bool is_multicast_sta(int sta_idx)
+{
+	if (g_rwnx_plat->sdiodev->rwnx_hw->chip_ops->is_old_ic)
+		return (sta_idx >= NX_REMOTE_STA_MAX_FOR_OLD_IC);
+	else
+		return (sta_idx >= NX_REMOTE_STA_MAX);
+}
+
+struct rwnx_sta *rwnx_get_sta(struct rwnx_hw *rwnx_hw, const u8 *mac_addr);
+
+static inline uint8_t master_vif_idx(struct rwnx_vif *vif)
+{
+#if IS_ENABLED(CONFIG_AIC8800_STA_ONLY)
+	return vif->vif_index;
+#else
+	if (unlikely(vif->wdev.iftype == NL80211_IFTYPE_AP_VLAN))
+		return vif->ap_vlan.master->vif_index;
+	else
+		return vif->vif_index;
+#endif
+}
+
+void rwnx_external_auth_enable(struct rwnx_vif *vif);
+void rwnx_external_auth_disable(struct rwnx_vif *vif);
+
+#endif /* _RWNX_DEFS_H_ */
diff --git a/drivers/net/wireless/aic/aic8800_fdrv/rwnx_events.h b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_events.h
new file mode 100644
index 0000000000000..c40751ea8b980
--- /dev/null
+++ b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_events.h
@@ -0,0 +1,938 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ ******************************************************************************
+ *
+ * Copyright (C) 2020 AIC semiconductor.
+ *
+ * @brief Trace events definition
+ *
+ ******************************************************************************
+ */
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM rwnx
+
+#if !defined(_RWNX_EVENTS_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _RWNX_EVENTS_H
+
+#include <linux/tracepoint.h>
+#include "rwnx_tx.h"
+
+#include "rwnx_compat.h"
+
+/*****************************************************************************
+ * TRACE function for MGMT TX (FULLMAC)
+ ****************************************************************************/
+#ifdef CONFIG_RWNX_FULLMAC
+#include "linux/ieee80211.h"
+#if defined(CONFIG_TRACEPOINTS) && defined(AIC8800_CREATE_TRACE_POINTS) && \
+	!defined(TRACE_HEADER_MULTI_READ)
+#include <linux/trace_seq.h>
+
+/* P2P Public Action Frames Definitions (see WiFi P2P Technical Specification,
+ * section 4.2.8)
+ */
+/* IEEE 802.11 Public Action Usage Category - Define P2P public action frames */
+#define MGMT_ACTION_PUBLIC_CAT         (0x04)
+/* Offset of OUI Subtype field in P2P Action Frame format */
+#define MGMT_ACTION_OUI_SUBTYPE_OFFSET (6)
+/* P2P Public Action Frame Types */
+enum p2p_action_type {
+	P2P_ACTION_GO_NEG_REQ = 0, /* GO Negociation Request */
+	P2P_ACTION_GO_NEG_RSP,     /* GO Negociation Response */
+	P2P_ACTION_GO_NEG_CFM,     /* GO Negociation Confirmation */
+	P2P_ACTION_INVIT_REQ,      /* P2P Invitation Request */
+	P2P_ACTION_INVIT_RSP,      /* P2P Invitation Response */
+	P2P_ACTION_DEV_DISC_REQ,   /* Device Discoverability Request */
+	P2P_ACTION_DEV_DISC_RSP,   /* Device Discoverability Response */
+	P2P_ACTION_PROV_DISC_REQ,  /* Provision Discovery Request */
+	P2P_ACTION_PROV_DISC_RSP,  /* Provision Discovery Response */
+};
+
+static inline const char *ftrace_print_mgmt_info(struct trace_seq *p,
+						 u16 frame_control, u8 cat, u8 type,
+						 u8 p2p)
+{
+	const char *ret = trace_seq_buffer_ptr(p);
+
+	switch (frame_control & IEEE80211_FCTL_STYPE) {
+	case (IEEE80211_STYPE_ASSOC_REQ):
+		trace_seq_printf(p, "Association Request");
+		break;
+	case (IEEE80211_STYPE_ASSOC_RESP):
+		trace_seq_printf(p, "Association Response");
+		break;
+	case (IEEE80211_STYPE_REASSOC_REQ):
+		trace_seq_printf(p, "Reassociation Request");
+		break;
+	case (IEEE80211_STYPE_REASSOC_RESP):
+		trace_seq_printf(p, "Reassociation Response");
+		break;
+	case (IEEE80211_STYPE_PROBE_REQ):
+		trace_seq_printf(p, "Probe Request");
+		break;
+	case (IEEE80211_STYPE_PROBE_RESP):
+		trace_seq_printf(p, "Probe Response");
+		break;
+	case (IEEE80211_STYPE_BEACON):
+		trace_seq_printf(p, "Beacon");
+		break;
+	case (IEEE80211_STYPE_ATIM):
+		trace_seq_printf(p, "ATIM");
+		break;
+	case (IEEE80211_STYPE_DISASSOC):
+		trace_seq_printf(p, "Disassociation");
+		break;
+	case (IEEE80211_STYPE_AUTH):
+		trace_seq_printf(p, "Authentication");
+		break;
+	case (IEEE80211_STYPE_DEAUTH):
+		trace_seq_printf(p, "Deauthentication");
+		break;
+	case (IEEE80211_STYPE_ACTION):
+		trace_seq_printf(p, "Action");
+		if (cat == MGMT_ACTION_PUBLIC_CAT && type == 0x9) {
+			switch (p2p) {
+			case (P2P_ACTION_GO_NEG_REQ):
+				trace_seq_printf(p, ": GO Negociation Request");
+				break;
+			case (P2P_ACTION_GO_NEG_RSP):
+				trace_seq_printf(p, ": GO Negociation Response");
+				break;
+			case (P2P_ACTION_GO_NEG_CFM):
+				trace_seq_printf(p, ": GO Negociation Confirmation");
+				break;
+			case (P2P_ACTION_INVIT_REQ):
+				trace_seq_printf(p, ": P2P Invitation Request");
+				break;
+			case (P2P_ACTION_INVIT_RSP):
+				trace_seq_printf(p, ": P2P Invitation Response");
+				break;
+			case (P2P_ACTION_DEV_DISC_REQ):
+				trace_seq_printf(p, ": Device Discoverability Request");
+				break;
+			case (P2P_ACTION_DEV_DISC_RSP):
+				trace_seq_printf(p, ": Device Discoverability Response");
+				break;
+			case (P2P_ACTION_PROV_DISC_REQ):
+				trace_seq_printf(p, ": Provision Discovery Request");
+				break;
+			case (P2P_ACTION_PROV_DISC_RSP):
+				trace_seq_printf(p, ": Provision Discovery Response");
+				break;
+			default:
+				trace_seq_printf(p, "Unknown p2p %d", p2p);
+				break;
+			}
+		} else {
+			switch (cat) {
+			case 0:
+				trace_seq_printf(p, ":Spectrum %d", type);
+				break;
+			case 1:
+				trace_seq_printf(p, ":QOS %d", type);
+				break;
+			case 2:
+				trace_seq_printf(p, ":DLS %d", type);
+				break;
+			case 3:
+				trace_seq_printf(p, ":BA %d", type);
+				break;
+			case 4:
+				trace_seq_printf(p, ":Public %d", type);
+				break;
+			case 5:
+				trace_seq_printf(p, ":Radio Measure %d", type);
+				break;
+			case 6:
+				trace_seq_printf(p, ":Fast BSS %d", type);
+				break;
+			case 7:
+				trace_seq_printf(p, ":HT Action %d", type);
+				break;
+			case 8:
+				trace_seq_printf(p, ":SA Query %d", type);
+				break;
+			case 9:
+				trace_seq_printf(p, ":Protected Public %d", type);
+				break;
+			case 10:
+				trace_seq_printf(p, ":WNM %d", type);
+				break;
+			case 11:
+				trace_seq_printf(p, ":Unprotected WNM %d", type);
+				break;
+			case 12:
+				trace_seq_printf(p, ":TDLS %d", type);
+				break;
+			case 13:
+				trace_seq_printf(p, ":Mesh %d", type);
+				break;
+			case 14:
+				trace_seq_printf(p, ":MultiHop %d", type);
+				break;
+			case 15:
+				trace_seq_printf(p, ":Self Protected %d", type);
+				break;
+			case 126:
+				trace_seq_printf(p, ":Vendor protected");
+				break;
+			case 127:
+				trace_seq_printf(p, ":Vendor");
+				break;
+			default:
+				trace_seq_printf(p, ":Unknown category %d", cat);
+				break;
+			}
+		}
+		break;
+	default:
+		trace_seq_printf(p, "Unknown subtype %d",
+				 frame_control & IEEE80211_FCTL_STYPE);
+		break;
+	}
+
+	trace_seq_putc(p, 0);
+
+	return ret;
+}
+#endif /* defined(CONFIG_TRACEPOINTS) && defined(AIC8800_CREATE_TRACE_POINTS) */
+
+#undef __print_mgmt_info
+#define __print_mgmt_info(frame_control, cat, type, p2p)                       \
+	ftrace_print_mgmt_info(p, frame_control, cat, type, p2p)
+
+TRACE_EVENT(roc, TP_PROTO(u8 vif_idx, u16 freq, unsigned int duration),
+	    TP_ARGS(vif_idx, freq, duration),
+	    TP_STRUCT__entry(__field(u8, vif_idx) __field(u16, freq)
+			     __field(unsigned int, duration)),
+	    TP_fast_assign(__entry->vif_idx = vif_idx; __entry->freq = freq;
+			   __entry->duration = duration;),
+	    TP_printk("f=%d vif=%d dur=%d", __entry->freq, __entry->vif_idx,
+		      __entry->duration));
+
+TRACE_EVENT(cancel_roc, TP_PROTO(u8 vif_idx), TP_ARGS(vif_idx),
+	    TP_STRUCT__entry(__field(u8, vif_idx)),
+	    TP_fast_assign(__entry->vif_idx = vif_idx;),
+	    TP_printk("vif=%d", __entry->vif_idx));
+
+TRACE_EVENT(roc_exp, TP_PROTO(u8 vif_idx), TP_ARGS(vif_idx),
+	    TP_STRUCT__entry(__field(u8, vif_idx)),
+	    TP_fast_assign(__entry->vif_idx = vif_idx;),
+	    TP_printk("vif=%d", __entry->vif_idx));
+
+TRACE_EVENT(switch_roc, TP_PROTO(u8 vif_idx), TP_ARGS(vif_idx),
+	    TP_STRUCT__entry(__field(u8, vif_idx)),
+	    TP_fast_assign(__entry->vif_idx = vif_idx;),
+	    TP_printk("vif=%d", __entry->vif_idx));
+
+DECLARE_EVENT_CLASS(mgmt_template,
+		    TP_PROTO(u16 freq, u8 vif_idx, u8 sta_idx, struct ieee80211_mgmt *mgmt),
+		    TP_ARGS(freq, vif_idx, sta_idx, mgmt),
+		    TP_STRUCT__entry(__field(u16, freq) __field(u8, vif_idx)
+				     __field(u8, sta_idx) __field(u16, frame_control)
+				     __field(u8, action_cat) __field(u8, action_type)
+				     __field(u8, action_p2p)),
+		    TP_fast_assign(__entry->freq = freq; __entry->vif_idx = vif_idx;
+				   __entry->sta_idx = sta_idx;
+				   __entry->frame_control = le16_to_cpu(mgmt->frame_control);
+				   __entry->action_cat = mgmt->u.action.category;
+				   __entry->action_type = mgmt->u.action.action_code;
+				   __entry->action_p2p = *((u8 *)&mgmt->u.action.category +
+								MGMT_ACTION_OUI_SUBTYPE_OFFSET);),
+		    TP_printk("f=%d vif=%d sta=%d -> %s", __entry->freq, __entry->vif_idx,
+			      __entry->sta_idx,
+			      __print_mgmt_info(__entry->frame_control, __entry->action_cat,
+						__entry->action_type, __entry->action_p2p)));
+
+DEFINE_EVENT(mgmt_template, mgmt_tx,
+	     TP_PROTO(u16 freq, u8 vif_idx, u8 sta_idx,
+		      struct ieee80211_mgmt *mgmt),
+	     TP_ARGS(freq, vif_idx, sta_idx, mgmt));
+
+DEFINE_EVENT(mgmt_template, mgmt_rx,
+	     TP_PROTO(u16 freq, u8 vif_idx, u8 sta_idx,
+		      struct ieee80211_mgmt *mgmt),
+	     TP_ARGS(freq, vif_idx, sta_idx, mgmt));
+
+TRACE_EVENT(mgmt_cfm, TP_PROTO(u8 vif_idx, u8 sta_idx, bool acked),
+	    TP_ARGS(vif_idx, sta_idx, acked),
+	    TP_STRUCT__entry(__field(u8, vif_idx) __field(u8, sta_idx)
+			     __field(bool, acked)),
+	    TP_fast_assign(__entry->vif_idx = vif_idx;
+			   __entry->sta_idx = sta_idx; __entry->acked = acked;),
+	    TP_printk("vif=%d sta=%d ack=%d", __entry->vif_idx,
+		      __entry->sta_idx, __entry->acked));
+#endif /* CONFIG_RWNX_FULLMAC */
+
+/*****************************************************************************
+ * TRACE function for TXQ
+ ****************************************************************************/
+
+#if defined(CONFIG_TRACEPOINTS) && defined(AIC8800_CREATE_TRACE_POINTS) && \
+	!defined(TRACE_HEADER_MULTI_READ)
+
+#include <linux/trace_seq.h>
+#include <linux/trace_events.h>
+
+static inline const char *ftrace_print_txq(struct trace_seq *p, int txq_idx)
+{
+	const char *ret = trace_seq_buffer_ptr(p);
+
+	if (txq_idx == TXQ_INACTIVE) {
+		trace_seq_printf(p, "[INACTIVE]");
+	} else if (txq_idx < NX_FIRST_VIF_TXQ_IDX) {
+		trace_seq_printf(p, "[STA %d/%d]", txq_idx / NX_NB_TXQ_PER_STA,
+				 txq_idx % NX_NB_TXQ_PER_STA);
+#ifdef CONFIG_RWNX_FULLMAC
+	} else if (txq_idx < NX_FIRST_UNK_TXQ_IDX) {
+		trace_seq_printf(p, "[BC/MC %d]", txq_idx - NX_FIRST_BCMC_TXQ_IDX);
+	} else if (txq_idx < NX_OFF_CHAN_TXQ_IDX) {
+		trace_seq_printf(p, "[UNKNOWN %d]", txq_idx - NX_FIRST_UNK_TXQ_IDX);
+	} else if (txq_idx == NX_OFF_CHAN_TXQ_IDX) {
+		trace_seq_printf(p, "[OFFCHAN]");
+#else
+	} else if (txq_idx < NX_NB_TXQ) {
+		txq_idx -= NX_FIRST_VIF_TXQ_IDX;
+		trace_seq_printf(p, "[VIF %d/%d]", txq_idx / NX_NB_TXQ_PER_VIF,
+				 txq_idx % NX_NB_TXQ_PER_VIF);
+#endif
+	} else {
+		trace_seq_printf(p, "[ERROR %d]", txq_idx);
+	}
+
+	trace_seq_putc(p, 0);
+
+	return ret;
+}
+
+static inline const char *ftrace_print_sta(struct trace_seq *p, int sta_idx)
+{
+	const char *ret = trace_seq_buffer_ptr(p);
+
+	if (sta_idx < NX_REMOTE_STA_MAX)
+		trace_seq_printf(p, "[STA %d]", sta_idx);
+	else
+		trace_seq_printf(p, "[BC/MC %d]", sta_idx - NX_REMOTE_STA_MAX);
+
+	trace_seq_putc(p, 0);
+
+	return ret;
+}
+
+static inline const char *ftrace_print_hwq(struct trace_seq *p, int hwq_idx)
+{
+	static const struct trace_print_flags symbols[] = {{RWNX_HWQ_BK, "BK"},
+							   {RWNX_HWQ_BE, "BE"},
+							   {RWNX_HWQ_VI, "VI"},
+							   {RWNX_HWQ_VO, "VO"},
+#ifdef CONFIG_RWNX_FULLMAC
+							   {RWNX_HWQ_BCMC, "BCMC"},
+#else
+							   {RWNX_HWQ_BCN, "BCN"},
+#endif
+							   {-1, NULL}};
+	return trace_print_symbols_seq(p, hwq_idx, symbols, ARRAY_SIZE(symbols));
+}
+
+static inline const char *ftrace_print_hwq_cred(struct trace_seq *p, u8 *cred)
+{
+	const char *ret = trace_seq_buffer_ptr(p);
+
+#if CONFIG_USER_MAX == 1
+	trace_seq_printf(p, "%d", cred[0]);
+#else
+	int i;
+
+	for (i = 0; i < CONFIG_USER_MAX - 1; i++)
+		trace_seq_printf(p, "%d-", cred[i]);
+	trace_seq_printf(p, "%d", cred[i]);
+#endif
+
+	trace_seq_putc(p, 0);
+	return ret;
+}
+
+static inline const char *ftrace_print_mu_info(struct trace_seq *p, u8 mu_info)
+{
+	const char *ret = trace_seq_buffer_ptr(p);
+
+	if (mu_info)
+		trace_seq_printf(p, "MU: %d-%d", (mu_info & 0x3f), (mu_info >> 6));
+
+	trace_seq_putc(p, 0);
+	return ret;
+}
+
+static inline const char *ftrace_print_mu_group(struct trace_seq *p, int nb_user,
+						u8 *users)
+{
+	const char *ret = trace_seq_buffer_ptr(p);
+	int i;
+
+	if (users[0] != 0xff)
+		trace_seq_printf(p, "(%d", users[0]);
+	else
+		trace_seq_printf(p, "(-");
+	for (i = 1; i < CONFIG_USER_MAX; i++) {
+		if (users[i] != 0xff)
+			trace_seq_printf(p, ",%d", users[i]);
+		else
+			trace_seq_printf(p, ",-");
+	}
+
+	trace_seq_printf(p, ")");
+	trace_seq_putc(p, 0);
+	return ret;
+}
+
+static inline const char *ftrace_print_amsdu(struct trace_seq *p, u16 nb_pkt)
+{
+	const char *ret = trace_seq_buffer_ptr(p);
+
+	if (nb_pkt > 1)
+		trace_seq_printf(p, "(AMSDU %d)", nb_pkt);
+
+	trace_seq_putc(p, 0);
+	return ret;
+}
+#endif /* defined(CONFIG_TRACEPOINTS) && defined(AIC8800_CREATE_TRACE_POINTS) */
+
+#undef __print_txq
+#define __print_txq(txq_idx) ftrace_print_txq(p, txq_idx)
+
+#undef __print_sta
+#define __print_sta(sta_idx) ftrace_print_sta(p, sta_idx)
+
+#undef __print_hwq
+#define __print_hwq(hwq) ftrace_print_hwq(p, hwq)
+
+#undef __print_hwq_cred
+#define __print_hwq_cred(cred) ftrace_print_hwq_cred(p, cred)
+
+#undef __print_mu_info
+#define __print_mu_info(mu_info) ftrace_print_mu_info(p, mu_info)
+
+#undef __print_mu_group
+#define __print_mu_group(nb, users) ftrace_print_mu_group(p, nb, users)
+
+#undef __print_amsdu
+#define __print_amsdu(nb_pkt) ftrace_print_amsdu(p, nb_pkt)
+
+#ifdef CONFIG_RWNX_FULLMAC
+
+TRACE_EVENT(txq_select, TP_PROTO(int txq_idx, u16 pkt_ready_up, struct sk_buff *skb),
+	    TP_ARGS(txq_idx, pkt_ready_up, skb),
+	    TP_STRUCT__entry(__field(u16, txq_idx) __field(u16, pkt_ready)
+			     __field(struct sk_buff *, skb)),
+	    TP_fast_assign(__entry->txq_idx = txq_idx;
+			   __entry->pkt_ready = pkt_ready_up; __entry->skb = skb;),
+	    TP_printk("%s pkt_ready_up=%d skb=%p", __print_txq(__entry->txq_idx),
+		      __entry->pkt_ready, __entry->skb));
+
+#endif /* CONFIG_RWNX_FULLMAC */
+
+DECLARE_EVENT_CLASS(hwq_template, TP_PROTO(u8 hwq_idx), TP_ARGS(hwq_idx),
+		    TP_STRUCT__entry(__field(u8, hwq_idx)),
+		    TP_fast_assign(__entry->hwq_idx = hwq_idx;),
+		    TP_printk("%s", __print_hwq(__entry->hwq_idx)));
+
+DEFINE_EVENT(hwq_template, hwq_flowctrl_stop, TP_PROTO(u8 hwq_idx),
+	     TP_ARGS(hwq_idx));
+
+DEFINE_EVENT(hwq_template, hwq_flowctrl_start, TP_PROTO(u8 hwq_idx),
+	     TP_ARGS(hwq_idx));
+
+DECLARE_EVENT_CLASS(txq_template, TP_PROTO(struct rwnx_txq *txq), TP_ARGS(txq),
+		    TP_STRUCT__entry(__field(u16, txq_idx)),
+		    TP_fast_assign(__entry->txq_idx = txq->idx;),
+		    TP_printk("%s", __print_txq(__entry->txq_idx)));
+
+DEFINE_EVENT(txq_template, txq_add_to_hw, TP_PROTO(struct rwnx_txq *txq),
+	     TP_ARGS(txq));
+
+DEFINE_EVENT(txq_template, txq_del_from_hw, TP_PROTO(struct rwnx_txq *txq),
+	     TP_ARGS(txq));
+
+#ifdef CONFIG_RWNX_FULLMAC
+
+DEFINE_EVENT(txq_template, txq_flowctrl_stop, TP_PROTO(struct rwnx_txq *txq),
+	     TP_ARGS(txq));
+
+DEFINE_EVENT(txq_template, txq_flowctrl_restart, TP_PROTO(struct rwnx_txq *txq),
+	     TP_ARGS(txq));
+
+#endif /* CONFIG_RWNX_FULLMAC */
+
+TRACE_EVENT(process_txq, TP_PROTO(struct rwnx_txq *txq), TP_ARGS(txq),
+	    TP_STRUCT__entry(__field(u16, txq_idx) __field(u16, len)
+			     __field(u16, len_retry) __field(s8, credit)
+#ifdef CONFIG_RWNX_FULLMAC
+			     __field(u16, limit)
+#endif /* CONFIG_RWNX_FULLMAC */
+						),
+	    TP_fast_assign(__entry->txq_idx = txq->idx;
+			   __entry->len = skb_queue_len(&txq->sk_list);
+#ifdef CONFIG_MAC80211_TXQ
+			   __entry->len += txq->nb_ready_mac80211;
+#endif
+			   __entry->len_retry = txq->nb_retry;
+			   __entry->credit = txq->credits;
+#ifdef CONFIG_RWNX_FULLMAC
+			   __entry->limit = txq->push_limit;
+#endif /* CONFIG_RWNX_FULLMAC */
+						),
+#ifdef CONFIG_RWNX_FULLMAC
+	    TP_printk("%s txq_credits=%d, len=%d, retry_len=%d, push_limit=%d",
+		      __print_txq(__entry->txq_idx), __entry->credit,
+		      __entry->len, __entry->len_retry, __entry->limit)
+#else
+	    TP_printk("%s txq_credits=%d, len=%d, retry_len=%d",
+		      __print_txq(__entry->txq_idx), __entry->credit,
+		      __entry->len, __entry->len_retry)
+#endif /* CONFIG_RWNX_FULLMAC */
+);
+
+DECLARE_EVENT_CLASS(txq_reason_template, TP_PROTO(struct rwnx_txq *txq, u16 reason),
+		    TP_ARGS(txq, reason),
+		    TP_STRUCT__entry(__field(u16, txq_idx) __field(u16, reason)
+				     __field(u16, status)),
+		    TP_fast_assign(__entry->txq_idx = txq->idx; __entry->reason = reason;
+				   __entry->status = txq->status;),
+		    TP_printk("%s reason=%s status=%s", __print_txq(__entry->txq_idx),
+			      __print_symbolic(__entry->reason, {RWNX_TXQ_STOP_FULL, "FULL"},
+					       {RWNX_TXQ_STOP_CSA, "CSA"},
+					       {RWNX_TXQ_STOP_STA_PS, "PS"},
+					       {RWNX_TXQ_STOP_VIF_PS, "VPS"},
+					       {RWNX_TXQ_STOP_CHAN, "CHAN"},
+					       {RWNX_TXQ_STOP_MU_POS, "MU"}),
+			      __print_flags(__entry->status, "|",
+					    {RWNX_TXQ_IN_HWQ_LIST, "IN LIST"},
+					    {RWNX_TXQ_STOP_FULL, "FULL"},
+					    {RWNX_TXQ_STOP_CSA, "CSA"},
+					    {RWNX_TXQ_STOP_STA_PS, "PS"},
+					    {RWNX_TXQ_STOP_VIF_PS, "VPS"},
+					    {RWNX_TXQ_STOP_CHAN, "CHAN"},
+					    {RWNX_TXQ_STOP_MU_POS, "MU"},
+					    {RWNX_TXQ_NDEV_FLOW_CTRL, "FLW_CTRL"})));
+
+DEFINE_EVENT(txq_reason_template, txq_start,
+	     TP_PROTO(struct rwnx_txq *txq, u16 reason), TP_ARGS(txq, reason));
+
+DEFINE_EVENT(txq_reason_template, txq_stop,
+	     TP_PROTO(struct rwnx_txq *txq, u16 reason), TP_ARGS(txq, reason));
+
+TRACE_EVENT(push_desc,
+	    TP_PROTO(struct sk_buff *skb, struct rwnx_sw_txhdr *sw_txhdr, int push_flags),
+	    TP_ARGS(skb, sw_txhdr, push_flags),
+	    TP_STRUCT__entry(__field(struct sk_buff *, skb) __field(unsigned int, len)
+			     __field(u16, tx_queue) __field(u8, hw_queue)
+			     __field(u8, push_flag) __field(u32, flag)
+			     __field(s8, txq_cred) __field(u8, hwq_cred)
+			     __field(u16, pkt_cnt)
+			     __field(u8, mu_info)),
+	    TP_fast_assign(__entry->skb = skb; __entry->tx_queue = sw_txhdr->txq->idx;
+			   __entry->push_flag = push_flags;
+			   __entry->hw_queue = sw_txhdr->txq->hwq->id;
+			   __entry->txq_cred = sw_txhdr->txq->credits;
+#ifdef CONFIG_RWNX_SPLIT_TX_BUF
+			   __entry->pkt_cnt = sw_txhdr->desc.host.packet_cnt;
+#endif
+#ifdef CONFIG_RWNX_FULLMAC
+			   __entry->flag = sw_txhdr->desc.host.flags;
+#ifdef CONFIG_RWNX_SPLIT_TX_BUF
+#ifdef CONFIG_RWNX_AMSDUS_TX
+			if (sw_txhdr->amsdu.len)
+				__entry->len = sw_txhdr->amsdu.len;
+			else
+#endif /* CONFIG_RWNX_AMSDUS_TX */
+				__entry->len = sw_txhdr->desc.host.packet_len[0];
+#else
+			   __entry->len = sw_txhdr->desc.host.packet_len;
+#endif /* CONFIG_RWNX_SPLIT_TX_BUF */
+#else  /* !CONFIG_RWNX_FULLMAC */
+			   __entry->flag = sw_txhdr->desc.umac.flags;
+			   __entry->len = sw_txhdr->frame_len;
+			   __entry->sn = sw_txhdr->sn;
+#endif /* CONFIG_RWNX_FULLMAC */
+#ifdef CONFIG_RWNX_MUMIMO_TX
+			   __entry->mu_info = sw_txhdr->desc.host.mumimo_info;
+#else
+			   __entry->mu_info = 0;
+#endif
+					),
+#ifdef CONFIG_RWNX_FULLMAC
+	TP_printk("%s skb=%p (len=%d) hw_queue=%s cred_txq=%d cred_hwq=%d %s flag=%s %s %s %s",
+		  __print_txq(__entry->tx_queue), __entry->skb, __entry->len,
+		  __print_hwq(__entry->hw_queue), __entry->txq_cred,
+		  __entry->hwq_cred, __print_mu_info(__entry->mu_info),
+		  __print_flags(__entry->flag, "|", {TXU_CNTRL_RETRY, "RETRY"},
+				{TXU_CNTRL_MORE_DATA, "MOREDATA"}, {TXU_CNTRL_MGMT, "MGMT"},
+				{TXU_CNTRL_MGMT_NO_CCK, "NO_CCK"},
+				{TXU_CNTRL_MGMT_ROBUST, "ROBUST"}, {TXU_CNTRL_AMSDU, "AMSDU"},
+				{TXU_CNTRL_USE_4ADDR, "4ADDR"}, {TXU_CNTRL_EOSP, "EOSP"},
+				{TXU_CNTRL_MESH_FWD, "MESH_FWD"}, {TXU_CNTRL_TDLS, "TDLS"}),
+		  (__entry->push_flag & RWNX_PUSH_IMMEDIATE) ? "(IMMEDIATE)" : "",
+		  (!(__entry->flag & TXU_CNTRL_RETRY) &&
+		  (__entry->push_flag & RWNX_PUSH_RETRY))
+		  ? "(SW_RETRY)" : "",
+		  __print_amsdu(__entry->pkt_cnt))
+#else
+	TP_printk("%s skb=%p (len=%d) hw_queue=%s cred_txq=%d cred_hwq=%d %s flag=%x (%s) sn=%d %s",
+		  __print_txq(__entry->tx_queue), __entry->skb, __entry->len,
+		  __print_hwq(__entry->hw_queue), __entry->txq_cred,
+		  __entry->hwq_cred, __print_mu_info(__entry->mu_info),
+		  __entry->flag,
+		  __print_flags(__entry->push_flag, "|", {RWNX_PUSH_RETRY, "RETRY"},
+				{RWNX_PUSH_IMMEDIATE, "IMMEDIATE"}),
+		  __entry->sn, __print_amsdu(__entry->pkt_cnt))
+#endif /* CONFIG_RWNX_FULLMAC */
+);
+
+TRACE_EVENT(txq_queue_skb, TP_PROTO(struct sk_buff *skb, struct rwnx_txq *txq, bool retry),
+	    TP_ARGS(skb, txq, retry),
+	    TP_STRUCT__entry(__field(struct sk_buff *, skb) __field(u16, txq_idx)
+			     __field(s8, credit) __field(u16, q_len)
+			     __field(u16, q_len_retry) __field(bool, retry)),
+	TP_fast_assign(__entry->skb = skb; __entry->txq_idx = txq->idx;
+		       __entry->credit = txq->credits;
+		       __entry->q_len = skb_queue_len(&txq->sk_list);
+		       __entry->q_len_retry = txq->nb_retry;
+		       __entry->retry = retry;),
+	TP_printk("%s skb=%p retry=%d txq_credits=%d queue_len=%d (retry = %d)",
+		  __print_txq(__entry->txq_idx), __entry->skb, __entry->retry,
+		  __entry->credit, __entry->q_len, __entry->q_len_retry));
+
+#ifdef CONFIG_MAC80211_TXQ
+TRACE_EVENT(txq_wake, TP_PROTO(struct rwnx_txq *txq), TP_ARGS(txq),
+	    TP_STRUCT__entry(__field(u16, txq_idx) __field(u16, q_len)),
+	    TP_fast_assign(__entry->txq_idx = txq->idx;
+			   __entry->q_len = txq->nb_ready_mac80211;),
+	    TP_printk("%s mac80211_queue_len=%d", __print_txq(__entry->txq_idx),
+		      __entry->q_len));
+
+TRACE_EVENT(txq_drop, TP_PROTO(struct rwnx_txq *txq, unsigned long nb_drop),
+	    TP_ARGS(txq, nb_drop),
+	    TP_STRUCT__entry(__field(u16, txq_idx) __field(u16, nb_drop)),
+	    TP_fast_assign(__entry->txq_idx = txq->idx;
+			   __entry->nb_drop = nb_drop;),
+	    TP_printk("%s %u pkt have been dropped by codel in mac80211 txq",
+		      __print_txq(__entry->txq_idx), __entry->nb_drop));
+
+#endif
+
+DECLARE_EVENT_CLASS(idx_template, TP_PROTO(u16 idx), TP_ARGS(idx),
+		    TP_STRUCT__entry(__field(u16, idx)),
+		    TP_fast_assign(__entry->idx = idx;),
+		    TP_printk("idx=%d", __entry->idx));
+
+DEFINE_EVENT(idx_template, txq_vif_start, TP_PROTO(u16 idx), TP_ARGS(idx));
+
+DEFINE_EVENT(idx_template, txq_vif_stop, TP_PROTO(u16 idx), TP_ARGS(idx));
+
+TRACE_EVENT(process_hw_queue, TP_PROTO(struct rwnx_hwq *hwq), TP_ARGS(hwq),
+	    TP_STRUCT__entry(__field(u16, hwq)
+			     __array(u8, credits, CONFIG_USER_MAX)),
+	    TP_fast_assign(__entry->hwq = hwq->id;),
+	    TP_printk("hw_queue=%s hw_credits=%s", __print_hwq(__entry->hwq),
+		      __print_hwq_cred(__entry->credits)));
+
+DECLARE_EVENT_CLASS(sta_idx_template, TP_PROTO(u16 idx), TP_ARGS(idx),
+		    TP_STRUCT__entry(__field(u16, idx)),
+		    TP_fast_assign(__entry->idx = idx;),
+		    TP_printk("%s", __print_sta(__entry->idx)));
+
+DEFINE_EVENT(sta_idx_template, txq_sta_start, TP_PROTO(u16 idx), TP_ARGS(idx));
+
+DEFINE_EVENT(sta_idx_template, txq_sta_stop, TP_PROTO(u16 idx), TP_ARGS(idx));
+
+#ifdef CONFIG_RWNX_FULLMAC
+
+DEFINE_EVENT(sta_idx_template, ps_disable, TP_PROTO(u16 idx), TP_ARGS(idx));
+
+#endif /* CONFIG_RWNX_FULLMAC */
+
+TRACE_EVENT(skb_confirm,
+	    #ifdef CONFIG_RWNX_FULLMAC
+	    TP_PROTO(struct sk_buff *skb, struct rwnx_txq *txq,
+		     struct rwnx_hwq *hwq, struct tx_cfm_tag *cfm),
+	    #else
+	    TP_PROTO(struct sk_buff *skb, struct rwnx_txq *txq,
+		     struct rwnx_hwq *hwq, u8 cfm),
+	    #endif
+	TP_ARGS(skb, txq, hwq, cfm),
+	TP_STRUCT__entry(__field(struct sk_buff *, skb) __field(u16, txq_idx)
+			 __field(u8, hw_queue) __array(u8, hw_credit, CONFIG_USER_MAX)
+			 __field(s8, sw_credit) __field(s8, sw_credit_up)
+#ifdef CONFIG_RWNX_FULLMAC
+			 __field(u8, ampdu_size)
+#ifdef CONFIG_RWNX_SPLIT_TX_BUF
+			 __field(u16, amsdu)
+#endif /* CONFIG_RWNX_SPLIT_TX_BUF */
+			 __field(u16, sn)
+#endif /* CONFIG_RWNX_FULLMAC */
+					),
+	TP_fast_assign(__entry->skb = skb; __entry->txq_idx = txq->idx;
+		       __entry->hw_queue = hwq->id;
+		       __entry->sw_credit = txq->credits;
+#if defined CONFIG_RWNX_FULLMAC
+		       __entry->sw_credit_up = cfm->credits;
+		       __entry->ampdu_size = cfm->ampdu_size;
+#ifdef CONFIG_RWNX_SPLIT_TX_BUF
+		       __entry->amsdu = cfm->amsdu_size; __entry->sn = cfm->sn;
+#endif
+#else
+		       __entry->sw_credit_up = cfm
+#endif /* CONFIG_RWNX_FULLMAC */
+				),
+	TP_printk("%s skb=%p hw_queue=%s, hw_credits=%s, txq_credits=%d (+%d)"
+#ifdef CONFIG_RWNX_FULLMAC
+		  " sn=%u ampdu=%d"
+#ifdef CONFIG_RWNX_SPLIT_TX_BUF
+		  " amsdu=%u"
+#endif
+#endif
+		,
+		  __print_txq(__entry->txq_idx), __entry->skb,
+		  __print_hwq(__entry->hw_queue),
+		  __print_hwq_cred(__entry->hw_credit), __entry->sw_credit,
+		  __entry->sw_credit_up
+#ifdef CONFIG_RWNX_FULLMAC
+		,
+		  __entry->sn, __entry->ampdu_size
+#ifdef CONFIG_RWNX_SPLIT_TX_BUF
+		,
+		  __entry->amsdu
+#endif
+#endif
+			));
+
+TRACE_EVENT(credit_update, TP_PROTO(struct rwnx_txq *txq, s8 cred_up),
+	    TP_ARGS(txq, cred_up),
+	    TP_STRUCT__entry(__field(struct sk_buff *, skb)
+			     __field(u16, txq_idx) __field(s8, sw_credit)
+			     __field(s8, sw_credit_up)),
+	    TP_fast_assign(__entry->txq_idx = txq->idx;
+			   __entry->sw_credit = txq->credits;
+			   __entry->sw_credit_up = cred_up;),
+	    TP_printk("%s txq_credits=%d (%+d)", __print_txq(__entry->txq_idx),
+		      __entry->sw_credit, __entry->sw_credit_up))
+#ifdef CONFIG_RWNX_FULLMAC
+DECLARE_EVENT_CLASS(ps_template, TP_PROTO(struct rwnx_sta *sta), TP_ARGS(sta),
+		    TP_STRUCT__entry(__field(u16, idx) __field(u16, ready_ps)
+				     __field(u16, sp_ps) __field(u16, ready_uapsd)
+				     __field(u16, sp_uapsd)),
+		    TP_fast_assign(__entry->idx = sta->sta_idx;
+				   __entry->ready_ps = sta->ps.pkt_ready[LEGACY_PS_ID];
+				   __entry->sp_ps = sta->ps.sp_cnt[LEGACY_PS_ID];
+				   __entry->ready_uapsd = sta->ps.pkt_ready[UAPSD_ID];
+				   __entry->sp_uapsd = sta->ps.sp_cnt[UAPSD_ID];),
+		    TP_printk("%s [PS] ready=%d sp=%d [UAPSD] ready=%d sp=%d",
+			      __print_sta(__entry->idx), __entry->ready_ps, __entry->sp_ps,
+			      __entry->ready_uapsd, __entry->sp_uapsd));
+
+DEFINE_EVENT(ps_template, ps_queue, TP_PROTO(struct rwnx_sta *sta), TP_ARGS(sta));
+
+DEFINE_EVENT(ps_template, ps_push, TP_PROTO(struct rwnx_sta *sta), TP_ARGS(sta));
+
+DEFINE_EVENT(ps_template, ps_enable, TP_PROTO(struct rwnx_sta *sta), TP_ARGS(sta));
+
+TRACE_EVENT(ps_traffic_update, TP_PROTO(u16 sta_idx, u8 traffic, bool uapsd),
+	    TP_ARGS(sta_idx, traffic, uapsd),
+	    TP_STRUCT__entry(__field(u16, sta_idx) __field(u8, traffic)
+			     __field(bool, uapsd)),
+	    TP_fast_assign(__entry->sta_idx = sta_idx;
+			   __entry->traffic = traffic; __entry->uapsd = uapsd;),
+	    TP_printk("%s %s%s traffic available ",
+		      __print_sta(__entry->sta_idx),
+		      __entry->traffic ? "" : "no more ",
+		      __entry->uapsd ? "U-APSD" : "legacy PS"));
+
+TRACE_EVENT(ps_traffic_req, TP_PROTO(struct rwnx_sta *sta, u16 pkt_req, u8 ps_id),
+	    TP_ARGS(sta, pkt_req, ps_id),
+	    TP_STRUCT__entry(__field(u16, idx) __field(u16, pkt_req)
+			     __field(u8, ps_id) __field(u16, ready)
+			     __field(u16, sp)),
+	    TP_fast_assign(__entry->idx = sta->sta_idx;
+			   __entry->pkt_req = pkt_req; __entry->ps_id = ps_id;
+			   __entry->ready = sta->ps.pkt_ready[ps_id];
+			   __entry->sp = sta->ps.sp_cnt[ps_id];),
+	    TP_printk("%s %s traffic request %d pkt (ready=%d, sp=%d)",
+		      __print_sta(__entry->idx),
+		      __entry->ps_id == UAPSD_ID ? "U-APSD" : "legacy PS",
+		      __entry->pkt_req, __entry->ready, __entry->sp));
+
+#ifdef CONFIG_RWNX_AMSDUS_TX
+TRACE_EVENT(amsdu_subframe, TP_PROTO(struct rwnx_sw_txhdr *sw_txhdr),
+	    TP_ARGS(sw_txhdr),
+	    TP_STRUCT__entry(__field(struct sk_buff *, skb)
+			     __field(u16, txq_idx) __field(u8, nb)
+			     __field(u32, len)),
+	    TP_fast_assign(__entry->skb = sw_txhdr->skb;
+			   __entry->nb = sw_txhdr->amsdu.nb;
+			   __entry->len = sw_txhdr->amsdu.len;
+			   __entry->txq_idx = sw_txhdr->txq->idx;),
+	    TP_printk("%s skb=%p %s nb_subframe=%d, len=%u",
+		      __print_txq(__entry->txq_idx), __entry->skb,
+		      (__entry->nb == 2) ? "Start new AMSDU" : "Add subframe",
+		      __entry->nb, __entry->len));
+#endif
+
+#endif /* CONFIG_RWNX_FULLMAC */
+
+#ifdef CONFIG_RWNX_MUMIMO_TX
+TRACE_EVENT(mu_group_update, TP_PROTO(struct rwnx_mu_group *group),
+	    TP_ARGS(group),
+	    TP_STRUCT__entry(__field(u8, nb_user) __field(u8, group_id)
+			     __array(u8, users, CONFIG_USER_MAX)),
+	    TP_fast_assign(int i; __entry->nb_user = group->user_cnt;
+			for (i = 0; i < CONFIG_USER_MAX; i++) {
+				if (group->users[i])
+					__entry->users[i] = group->users[i]->sta_idx;
+				else
+					__entry->users[i] = 0xff;
+			}
+			__entry->group_id = group->group_id;),
+	    TP_printk("Group-id = %d, Users = %s", __entry->group_id,
+		      __print_mu_group(__entry->nb_user, __entry->users)));
+
+TRACE_EVENT(mu_group_delete, TP_PROTO(int group_id), TP_ARGS(group_id),
+	    TP_STRUCT__entry(__field(u8, group_id)),
+	    TP_fast_assign(__entry->group_id = group_id;),
+	    TP_printk("Group-id = %d", __entry->group_id));
+
+TRACE_EVENT(mu_group_selection, TP_PROTO(struct rwnx_sta *sta, int group_id),
+	    TP_ARGS(sta, group_id),
+	    TP_STRUCT__entry(__field(u8, sta_idx) __field(u8, group_id)),
+	    TP_fast_assign(__entry->sta_idx = sta->sta_idx;
+			   __entry->group_id = group_id;),
+	    TP_printk("[Sta %d] Group-id = %d", __entry->sta_idx,
+		      __entry->group_id));
+
+TRACE_EVENT(txq_select_mu_group,
+	    TP_PROTO(struct rwnx_txq *txq, int group_id, int pos),
+	    TP_ARGS(txq, group_id, pos),
+	    TP_STRUCT__entry(__field(u16, txq_idx) __field(u8, group_id)
+			     __field(u8, pos)),
+	    TP_fast_assign(__entry->txq_idx = txq->idx;
+			   __entry->group_id = group_id; __entry->pos = pos;),
+	    TP_printk("%s: group=%d pos=%d", __print_txq(__entry->txq_idx),
+		      __entry->group_id, __entry->pos));
+
+#endif /* CONFIG_RWNX_MUMIMO_TX */
+
+/*****************************************************************************
+ * TRACE functions for MESH
+ ****************************************************************************/
+#ifdef CONFIG_RWNX_FULLMAC
+DECLARE_EVENT_CLASS(mesh_path_template, TP_PROTO(struct rwnx_mesh_path *mesh_path),
+		    TP_ARGS(mesh_path),
+		    TP_STRUCT__entry(__field(u8, idx) __field(u8, next_hop_sta)
+				     __array(u8, tgt_mac, ETH_ALEN)),
+		    TP_fast_assign(__entry->idx = mesh_path->path_idx;
+				   memcpy(__entry->tgt_mac, &mesh_path->tgt_mac_addr, ETH_ALEN);
+				if (mesh_path->p_nhop_sta)
+					__entry->next_hop_sta = mesh_path->p_nhop_sta->sta_idx;
+				else
+					__entry->next_hop_sta = 0xff;),
+		    TP_printk("Mpath(%d): target=%pM next_hop=STA-%d", __entry->idx,
+			      __entry->tgt_mac, __entry->next_hop_sta));
+
+DEFINE_EVENT(mesh_path_template, mesh_create_path,
+	     TP_PROTO(struct rwnx_mesh_path *mesh_path), TP_ARGS(mesh_path));
+
+DEFINE_EVENT(mesh_path_template, mesh_delete_path,
+	     TP_PROTO(struct rwnx_mesh_path *mesh_path), TP_ARGS(mesh_path));
+
+DEFINE_EVENT(mesh_path_template, mesh_update_path,
+	     TP_PROTO(struct rwnx_mesh_path *mesh_path), TP_ARGS(mesh_path));
+
+#endif /* CONFIG_RWNX_FULLMAC */
+
+/*****************************************************************************
+ * TRACE functions for RADAR
+ ****************************************************************************/
+#ifdef CONFIG_RWNX_RADAR
+TRACE_EVENT(radar_pulse, TP_PROTO(u8 chain, struct radar_pulse *pulse),
+	    TP_ARGS(chain, pulse),
+	    TP_STRUCT__entry(__field(u8, chain) __field(s16, freq)
+			     __field(u16, pri) __field(u8, len)
+			     __field(u8, fom)),
+	    TP_fast_assign(__entry->freq = pulse->freq * 2;
+			   __entry->len = pulse->len * 2;
+			   __entry->fom = pulse->fom * 6;
+			   __entry->pri = pulse->rep; __entry->chain = chain;),
+	    TP_printk("%s: PRI=%.5d LEN=%.3d FOM=%.2d%% freq=%dMHz ",
+		      __print_symbolic(__entry->chain, {RWNX_RADAR_RIU, "RIU"},
+				       {RWNX_RADAR_FCU, "FCU"}),
+				       __entry->pri, __entry->len, __entry->fom, __entry->freq));
+
+TRACE_EVENT(radar_detected, TP_PROTO(u8 chain, u8 region, s16 freq, u8 type, u16 pri),
+	    TP_ARGS(chain, region, freq, type, pri),
+	    TP_STRUCT__entry(__field(u8, chain) __field(u8, region) __field(s16, freq)
+			     __field(u8, type) __field(u16, pri)),
+	    TP_fast_assign(__entry->chain = chain; __entry->region = region;
+			   __entry->freq = freq; __entry->type = type;
+			   __entry->pri = pri;),
+	    TP_printk("%s: region=%s type=%d freq=%dMHz (pri=%dus)",
+		      __print_symbolic(__entry->chain, {RWNX_RADAR_RIU, "RIU"},
+				       {RWNX_RADAR_FCU, "FCU"}),
+		      __print_symbolic(__entry->region, {NL80211_DFS_UNSET, "UNSET"},
+				       {NL80211_DFS_FCC, "FCC"},
+				       {NL80211_DFS_ETSI, "ETSI"},
+				       {NL80211_DFS_JP, "JP"}),
+		      __entry->type, __entry->freq, __entry->pri));
+
+TRACE_EVENT(radar_set_region, TP_PROTO(u8 region), TP_ARGS(region),
+	    TP_STRUCT__entry(__field(u8, region)),
+	    TP_fast_assign(__entry->region = region;),
+	    TP_printk("region=%s",
+		      __print_symbolic(__entry->region,
+				       {NL80211_DFS_UNSET, "UNSET"},
+				       {NL80211_DFS_FCC, "FCC"},
+				       {NL80211_DFS_ETSI, "ETSI"},
+				       {NL80211_DFS_JP, "JP"})));
+
+TRACE_EVENT(radar_enable_detection, TP_PROTO(u8 region, u8 enable, u8 chain),
+	    TP_ARGS(region, enable, chain),
+	    TP_STRUCT__entry(__field(u8, region) __field(u8, chain)
+			     __field(u8, enable)),
+	    TP_fast_assign(__entry->chain = chain; __entry->enable = enable;
+			   __entry->region = region;),
+	    TP_printk("%s: %s radar detection %s",
+		      __print_symbolic(__entry->chain, {RWNX_RADAR_RIU, "RIU"},
+				       {RWNX_RADAR_FCU, "FCU"}),
+		      __print_symbolic(__entry->enable,
+				       {RWNX_RADAR_DETECT_DISABLE, "Disable"},
+				       {RWNX_RADAR_DETECT_ENABLE,
+				       "Enable (no report)"},
+				       {RWNX_RADAR_DETECT_REPORT, "Enable"}),
+		      __entry->enable == RWNX_RADAR_DETECT_DISABLE
+				? ""	: __print_symbolic(__entry->region,
+							   {NL80211_DFS_UNSET, "UNSET"},
+							   {NL80211_DFS_FCC, "FCC"},
+							   {NL80211_DFS_ETSI, "ETSI"},
+							   {NL80211_DFS_JP, "JP"})));
+#endif /* CONFIG_RWNX_RADAR */
+
+/*****************************************************************************
+ * TRACE functions for IPC message
+ ****************************************************************************/
+#include "rwnx_strs.h"
+
+DECLARE_EVENT_CLASS(ipc_msg_template, TP_PROTO(u16 id), TP_ARGS(id),
+		    TP_STRUCT__entry(__field(u16, id)),
+		    TP_fast_assign(__entry->id = id;),
+		    TP_printk("%s (%d - %d)", RWNX_ID2STR(__entry->id),
+			      MSG_T(__entry->id), MSG_I(__entry->id)));
+
+DEFINE_EVENT(ipc_msg_template, msg_send, TP_PROTO(u16 id), TP_ARGS(id));
+
+DEFINE_EVENT(ipc_msg_template, msg_recv, TP_PROTO(u16 id), TP_ARGS(id));
+
+#endif /* !defined(_RWNX_EVENTS_H) || defined(TRACE_HEADER_MULTI_READ) */
+
+#undef TRACE_INCLUDE_PATH
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_PATH AIC_TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_FILE rwnx_events
+#include <trace/define_trace.h>
diff --git a/drivers/net/wireless/aic/aic8800_fdrv/rwnx_irqs.c b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_irqs.c
new file mode 100644
index 0000000000000..994b6f84cdaac
--- /dev/null
+++ b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_irqs.c
@@ -0,0 +1,49 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ ******************************************************************************
+ *
+ * Copyright (C) 2020 AIC semiconductor.
+ *
+ * @brief IRQ handler
+ *
+ ******************************************************************************
+ */
+#include <linux/interrupt.h>
+
+#include "rwnx_irqs.h"
+#include "ipc_host.h"
+#include "rwnx_defs.h"
+#include "rwnx_prof.h"
+
+/**
+ * rwnx_irq_hdlr - IRQ handler
+ * @irq: Interrupt number
+ * @dev_id: Driver private data registered for the interrupt
+ *
+ * Handler registered by the platform driver
+ *
+ * Return: %IRQ_HANDLED after scheduling the interrupt bottom half.
+ */
+irqreturn_t rwnx_irq_hdlr(int irq, void *dev_id)
+{
+	struct rwnx_hw *rwnx_hw = (struct rwnx_hw *)dev_id;
+
+	disable_irq_nosync(irq);
+	tasklet_schedule(&rwnx_hw->task);
+	return IRQ_HANDLED;
+}
+
+/**
+ * rwnx_task - Bottom half for IRQ handler
+ * @data: Driver private data passed to the tasklet
+ *
+ * Read irq status and process accordingly
+ */
+void rwnx_task(unsigned long data)
+{
+	struct rwnx_hw *rwnx_hw = (struct rwnx_hw *)data;
+
+	spin_lock_bh(&rwnx_hw->tx_lock);
+	rwnx_hwq_process_all(rwnx_hw);
+	spin_unlock_bh(&rwnx_hw->tx_lock);
+}
diff --git a/drivers/net/wireless/aic/aic8800_fdrv/rwnx_irqs.h b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_irqs.h
new file mode 100644
index 0000000000000..ff6c766c727c3
--- /dev/null
+++ b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_irqs.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _RWNX_IRQS_H_
+#define _RWNX_IRQS_H_
+
+#include <linux/interrupt.h>
+
+/* IRQ handler to be registered by platform driver */
+irqreturn_t rwnx_irq_hdlr(int irq, void *dev_id);
+
+void rwnx_task(unsigned long data);
+
+#endif /* _RWNX_IRQS_H_ */
diff --git a/drivers/net/wireless/aic/aic8800_fdrv/rwnx_msg_rx.c b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_msg_rx.c
new file mode 100644
index 0000000000000..cc124c8a34e8b
--- /dev/null
+++ b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_msg_rx.c
@@ -0,0 +1,1614 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ ******************************************************************************
+ *
+ * Copyright (C) 2020 AIC semiconductor.
+ *
+ * @brief RX function definitions
+ *
+ ******************************************************************************
+ */
+
+#include "rwnx_defs.h"
+#include "rwnx_prof.h"
+#include "rwnx_tx.h"
+#include "rwnx_rx.h"
+#include <linux/rtnetlink.h>
+#include <linux/vmalloc.h>
+#ifdef CONFIG_RWNX_BFMER
+#include "rwnx_bfmer.h"
+#endif //(CONFIG_RWNX_BFMER)
+#ifdef CONFIG_RWNX_FULLMAC
+#include "rwnx_msg_tx.h"
+#include "rwnx_tdls.h"
+#endif /* CONFIG_RWNX_FULLMAC */
+#include "aicwf_txrxif.h"
+#include "aicwf_sdio.h"
+#include "rwnx_compat.h"
+#include "rwnx_events.h"
+#include "rwnx_msg_rx.h"
+#include "rwnx_mod_params.h"
+#include <linux/pm.h>
+#ifdef CONFIG_AIC8800_POWER_LIMIT
+#include "aicwf_compat_8800d80.h"
+#endif
+
+void rwnx_cfg80211_unlink_bss(struct rwnx_hw *rwnx_hw,
+			      struct rwnx_vif *rwnx_vif);
+
+static int rwnx_freq_to_idx(struct rwnx_hw *rwnx_hw, int freq)
+{
+	struct ieee80211_supported_band *sband = NULL;
+	int band, ch, idx = 0;
+
+	for (band = NL80211_BAND_2GHZ; band < NUM_NL80211_BANDS; band++) {
+#ifdef CONFIG_RWNX_FULLMAC
+		sband = rwnx_hw->wiphy->bands[band];
+#endif /* CONFIG_RWNX_FULLMAC */
+		if (!sband)
+			continue;
+
+		for (ch = 0; ch < sband->n_channels; ch++, idx++) {
+			if (sband->channels[ch].center_freq == freq)
+				goto exit;
+		}
+	}
+
+	WARN_ON_ONCE(1);
+
+exit:
+	// Channel has been found, return the index
+	return idx;
+}
+
+/***************************************************************************
+ * Messages from MM task
+ **************************************************************************/
+static inline int rwnx_rx_chan_pre_switch_ind(struct rwnx_hw *rwnx_hw,
+					      struct rwnx_cmd *cmd,
+					      struct ipc_e2a_msg *msg)
+{
+	struct rwnx_vif *rwnx_vif;
+	int chan_idx = ((struct mm_channel_pre_switch_ind *)msg->param)->chan_index;
+
+	REG_SW_SET_PROFILING_CHAN(rwnx_hw, SW_PROF_CHAN_CTXT_PSWTCH_BIT);
+
+#ifdef CONFIG_RWNX_FULLMAC
+	list_for_each_entry(rwnx_vif, &rwnx_hw->vifs, list) {
+		if (rwnx_vif->up && rwnx_vif->ch_index == chan_idx)
+			rwnx_txq_vif_stop(rwnx_vif, RWNX_TXQ_STOP_CHAN, rwnx_hw);
+	}
+#endif /* CONFIG_RWNX_FULLMAC */
+
+	REG_SW_CLEAR_PROFILING_CHAN(rwnx_hw, SW_PROF_CHAN_CTXT_PSWTCH_BIT);
+
+	return 0;
+}
+
+static inline int rwnx_rx_chan_switch_ind(struct rwnx_hw *rwnx_hw,
+					  struct rwnx_cmd *cmd,
+					  struct ipc_e2a_msg *msg)
+{
+	struct rwnx_vif *rwnx_vif;
+	int chan_idx = ((struct mm_channel_switch_ind *)msg->param)->chan_index;
+	bool roc = ((struct mm_channel_switch_ind *)msg->param)->roc;
+	bool roc_tdls = ((struct mm_channel_switch_ind *)msg->param)->roc_tdls;
+
+	REG_SW_SET_PROFILING_CHAN(rwnx_hw, SW_PROF_CHAN_CTXT_SWTCH_BIT);
+
+#ifdef CONFIG_RWNX_FULLMAC
+	if (roc_tdls) {
+		u8 vif_index = ((struct mm_channel_switch_ind *)msg->param)->vif_index;
+
+		list_for_each_entry(rwnx_vif, &rwnx_hw->vifs, list) {
+			if (rwnx_vif->vif_index == vif_index) {
+				rwnx_vif->roc_tdls = true;
+				rwnx_txq_tdls_sta_start(rwnx_vif, RWNX_TXQ_STOP_CHAN, rwnx_hw);
+			}
+		}
+	} else if (!roc) {
+		list_for_each_entry(rwnx_vif, &rwnx_hw->vifs, list) {
+			if (rwnx_vif->up && rwnx_vif->ch_index == chan_idx)
+				rwnx_txq_vif_start(rwnx_vif, RWNX_TXQ_STOP_CHAN, rwnx_hw);
+		}
+	} else {
+		/* Retrieve the allocated RoC element */
+		struct rwnx_roc_elem *roc_elem = rwnx_hw->roc_elem;
+		/* Get VIF on which RoC has been started */
+		// rwnx_vif = netdev_priv(roc_elem->wdev->netdev);
+
+		/* For debug purpose (use ftrace kernel option) */
+		// trace_switch_roc(rwnx_vif->vif_index);
+
+		if (roc_elem) {
+			/* If mgmt_roc is true, remain on channel has been started by
+			 * ourself
+			 */
+			if (!roc_elem->mgmt_roc) {
+				/* Inform the host that we have switch on the indicated
+				 * off-channel
+				 */
+				cfg80211_ready_on_channel(roc_elem->wdev,
+							  roc_elem->cookie,
+							  roc_elem->chan, roc_elem->duration,
+							  GFP_ATOMIC);
+			}
+
+			/* Keep in mind that we have switched on the channel */
+			roc_elem->on_chan = true;
+		} else {
+			AICWFDBG(LOGDEBUG, "roc_elem == null\n");
+		}
+		// Enable traffic on OFF channel queue
+		rwnx_txq_offchan_start(rwnx_hw);
+	}
+
+	tasklet_schedule(&rwnx_hw->task);
+
+	rwnx_hw->cur_chanctx = chan_idx;
+	rwnx_radar_detection_enable_on_cur_channel(rwnx_hw);
+
+#endif /* CONFIG_RWNX_FULLMAC */
+
+	REG_SW_CLEAR_PROFILING_CHAN(rwnx_hw, SW_PROF_CHAN_CTXT_SWTCH_BIT);
+
+	return 0;
+}
+
+static inline int rwnx_rx_tdls_chan_switch_cfm(struct rwnx_hw *rwnx_hw,
+					       struct rwnx_cmd *cmd,
+					       struct ipc_e2a_msg *msg)
+{
+	return 0;
+}
+
+static inline int rwnx_rx_tdls_chan_switch_ind(struct rwnx_hw *rwnx_hw,
+					       struct rwnx_cmd *cmd,
+					       struct ipc_e2a_msg *msg)
+{
+#ifdef CONFIG_RWNX_FULLMAC
+	// Enable traffic on OFF channel queue
+	rwnx_txq_offchan_start(rwnx_hw);
+
+	return 0;
+#endif
+}
+
+static inline int rwnx_rx_tdls_chan_switch_base_ind(struct rwnx_hw *rwnx_hw,
+						    struct rwnx_cmd *cmd,
+						    struct ipc_e2a_msg *msg)
+{
+	struct rwnx_vif *rwnx_vif;
+	u8 vif_index = ((struct tdls_chan_switch_base_ind *)msg->param)->vif_index;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+#ifdef CONFIG_RWNX_FULLMAC
+	list_for_each_entry(rwnx_vif, &rwnx_hw->vifs, list) {
+		if (rwnx_vif->vif_index == vif_index) {
+			rwnx_vif->roc_tdls = false;
+			rwnx_txq_tdls_sta_stop(rwnx_vif, RWNX_TXQ_STOP_CHAN, rwnx_hw);
+		}
+	}
+	return 0;
+#endif
+}
+
+static inline int rwnx_rx_tdls_peer_ps_ind(struct rwnx_hw *rwnx_hw,
+					   struct rwnx_cmd *cmd,
+					   struct ipc_e2a_msg *msg)
+{
+	struct rwnx_vif *rwnx_vif;
+	u8 vif_index = ((struct tdls_peer_ps_ind *)msg->param)->vif_index;
+	bool ps_on = ((struct tdls_peer_ps_ind *)msg->param)->ps_on;
+
+#ifdef CONFIG_RWNX_FULLMAC
+	list_for_each_entry(rwnx_vif, &rwnx_hw->vifs, list) {
+		if (rwnx_vif->vif_index == vif_index) {
+			rwnx_vif->sta.tdls_sta->tdls.ps_on = ps_on;
+			// Update PS status for the TDLS station
+			rwnx_ps_bh_enable(rwnx_hw, rwnx_vif->sta.tdls_sta, ps_on);
+		}
+	}
+
+	return 0;
+#endif
+}
+
+static inline int rwnx_rx_remain_on_channel_exp_ind(struct rwnx_hw *rwnx_hw,
+						    struct rwnx_cmd *cmd,
+						    struct ipc_e2a_msg *msg)
+{
+#ifdef CONFIG_RWNX_FULLMAC
+	/* Retrieve the allocated RoC element */
+	struct rwnx_roc_elem *roc_elem = rwnx_hw->roc_elem;
+	/* Get VIF on which RoC has been started */
+	struct rwnx_vif *rwnx_vif;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	if (!roc_elem)
+		return 0;
+
+	rwnx_vif = container_of(roc_elem->wdev, struct rwnx_vif, wdev);
+	/* For debug purpose (use ftrace kernel option) */
+#ifdef AIC8800_CREATE_TRACE_POINTS
+	trace_roc_exp(rwnx_vif->vif_index);
+#endif
+	/* If mgmt_roc is true, remain on channel has been started by ourself */
+	/* If RoC has been cancelled before we switched on channel, do not call
+	 * cfg80211
+	 */
+	if (!roc_elem->mgmt_roc && roc_elem->on_chan) {
+		/* Inform the host that off-channel period has expired */
+		cfg80211_remain_on_channel_expired(roc_elem->wdev,
+						   roc_elem->cookie,
+						   roc_elem->chan, GFP_ATOMIC);
+	}
+
+	/* De-init offchannel TX queue */
+	rwnx_txq_offchan_deinit(rwnx_vif);
+
+	/* Free the allocated RoC element */
+	kfree(roc_elem);
+	rwnx_hw->roc_elem = NULL;
+
+#endif /* CONFIG_RWNX_FULLMAC */
+	return 0;
+}
+
+static inline int rwnx_rx_p2p_vif_ps_change_ind(struct rwnx_hw *rwnx_hw,
+						struct rwnx_cmd *cmd,
+						struct ipc_e2a_msg *msg)
+{
+	int vif_idx = ((struct mm_p2p_vif_ps_change_ind *)msg->param)->vif_index;
+	int ps_state = ((struct mm_p2p_vif_ps_change_ind *)msg->param)->ps_state;
+	struct rwnx_vif *vif_entry;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+#ifdef CONFIG_RWNX_FULLMAC
+	vif_entry = rwnx_hw->vif_table[vif_idx];
+
+	if (vif_entry)
+		goto found_vif;
+#endif /* CONFIG_RWNX_FULLMAC */
+
+	goto exit;
+found_vif:
+
+#ifdef CONFIG_RWNX_FULLMAC
+	if (ps_state == MM_PS_MODE_OFF) {
+		// Start TX queues for provided VIF
+		rwnx_txq_vif_start(vif_entry, RWNX_TXQ_STOP_VIF_PS, rwnx_hw);
+	} else {
+		// Stop TX queues for provided VIF
+		rwnx_txq_vif_stop(vif_entry, RWNX_TXQ_STOP_VIF_PS, rwnx_hw);
+	}
+#endif /* CONFIG_RWNX_FULLMAC */
+
+exit:
+	return 0;
+}
+
+static inline int rwnx_rx_channel_survey_ind(struct rwnx_hw *rwnx_hw,
+					     struct rwnx_cmd *cmd,
+					     struct ipc_e2a_msg *msg)
+{
+	struct mm_channel_survey_ind *ind =
+		(struct mm_channel_survey_ind *)msg->param;
+	// Get the channel index
+	int idx = rwnx_freq_to_idx(rwnx_hw, ind->freq);
+	// Get the survey
+	struct rwnx_survey_info *rwnx_survey;
+
+	if (idx > ARRAY_SIZE(rwnx_hw->survey))
+		return 0;
+
+	rwnx_survey = &rwnx_hw->survey[idx];
+
+	// Store the received parameters
+	rwnx_survey->chan_time_ms = ind->chan_time_ms;
+	rwnx_survey->chan_time_busy_ms = ind->chan_time_busy_ms;
+	rwnx_survey->noise_dbm = ind->noise_dbm;
+	rwnx_survey->filled = (SURVEY_INFO_TIME | SURVEY_INFO_TIME_BUSY);
+
+	if (ind->noise_dbm != 0)
+		rwnx_survey->filled |= SURVEY_INFO_NOISE_DBM;
+
+	return 0;
+}
+
+static inline int rwnx_rx_p2p_noa_upd_ind(struct rwnx_hw *rwnx_hw,
+					  struct rwnx_cmd *cmd,
+					  struct ipc_e2a_msg *msg)
+{
+	return 0;
+}
+
+static inline int rwnx_rx_rssi_status_ind(struct rwnx_hw *rwnx_hw,
+					  struct rwnx_cmd *cmd,
+					  struct ipc_e2a_msg *msg)
+{
+	struct mm_rssi_status_ind *ind = (struct mm_rssi_status_ind *)msg->param;
+	int vif_idx = ind->vif_index;
+	bool rssi_status = ind->rssi_status;
+
+	struct rwnx_vif *vif_entry;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+#ifdef CONFIG_RWNX_FULLMAC
+	vif_entry = rwnx_hw->vif_table[vif_idx];
+	if (vif_entry) {
+		cfg80211_cqm_rssi_notify(vif_entry->ndev,
+					 rssi_status ? NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW
+					 : NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH,
+					 ind->rssi, GFP_ATOMIC);
+	}
+#endif /* CONFIG_RWNX_FULLMAC */
+
+	return 0;
+}
+
+static inline int rwnx_rx_pktloss_notify_ind(struct rwnx_hw *rwnx_hw,
+					     struct rwnx_cmd *cmd,
+					     struct ipc_e2a_msg *msg)
+{
+#ifdef CONFIG_RWNX_FULLMAC
+	struct mm_pktloss_ind *ind = (struct mm_pktloss_ind *)msg->param;
+	struct rwnx_vif *vif_entry;
+	int vif_idx = ind->vif_index;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	vif_entry = rwnx_hw->vif_table[vif_idx];
+	if (vif_entry) {
+		cfg80211_cqm_pktloss_notify(vif_entry->ndev,
+					    (const u8 *)ind->mac_addr.array,
+					    ind->num_packets, GFP_ATOMIC);
+	}
+#endif /* CONFIG_RWNX_FULLMAC */
+
+	return 0;
+}
+
+static inline int rwnx_apm_staloss_ind(struct rwnx_hw *rwnx_hw,
+				       struct rwnx_cmd *cmd,
+				       struct ipc_e2a_msg *msg)
+{
+	struct mm_apm_staloss_ind *ind = (struct mm_apm_staloss_ind *)msg->param;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	memcpy(rwnx_hw->sta_mac_addr, ind->mac_addr, 6);
+	rwnx_hw->apm_vif_idx = ind->vif_idx;
+
+	queue_work(rwnx_hw->apm_staloss_wq, &rwnx_hw->apm_staloss_work);
+
+	return 0;
+}
+
+static struct sk_buff *arp_80211_to_8023(struct sk_buff *skb, u8 hdrlen)
+{
+	struct ethhdr *eth;
+	size_t payload_len;
+	struct sk_buff *new_skb = NULL;
+	char *llc;
+	u8 da[MAC_ADDR_LEN] = {0};
+	u8 sa[MAC_ADDR_LEN] = {0};
+
+	if (skb->len <= hdrlen) {
+		AICWFDBG(LOGERROR, "%s skb_len err1\n", __func__);
+		return NULL;
+	}
+
+	memcpy(da, &skb->data[4], 6);
+	memcpy(sa, &skb->data[16], 6);
+
+	skb_pull(skb, hdrlen);
+	llc = skb->data;
+	if (llc[0] == 0xAA && llc[1] == 0xAA && llc[2] == 0x03) {
+		if (skb->len < 8) {
+			AICWFDBG(LOGERROR, "%s skb_len err2\n", __func__);
+			return NULL;
+		}
+		skb_pull(skb, 8);
+	}
+	payload_len = skb->len;
+
+	new_skb = __dev_alloc_skb(ETH_HLEN + payload_len, GFP_ATOMIC);
+	if (!new_skb) {
+		AICWFDBG(LOGERROR, "%s skb alloc fail\n", __func__);
+		return NULL;
+	}
+
+	skb_reserve(new_skb, ETH_HLEN);
+	skb_put(new_skb, payload_len);
+	memcpy(new_skb->data, skb->data, payload_len);
+
+	eth = (struct ethhdr *)skb_push(new_skb, ETH_HLEN);
+
+	memcpy(eth->h_dest, da, ETH_ALEN);
+	memcpy(eth->h_source, sa, ETH_ALEN);
+
+	eth->h_proto = htons(ETH_P_ARP);
+
+	new_skb->protocol = eth->h_proto;
+	new_skb->dev = skb->dev;
+
+	return new_skb;
+}
+
+static inline int rwnx_arp_announcement_ind(struct rwnx_hw *rwnx_hw,
+					    struct rwnx_cmd *cmd,
+					    struct ipc_e2a_msg *msg)
+{
+	struct rwnx_vif *vif;
+	struct sk_buff *skb = NULL;
+	struct sk_buff *new_skb = NULL;
+	struct hw_rxhdr rxhdr;
+	int hdrlen = 24;
+	struct rxu_arp_announcement_ind *ind = (struct rxu_arp_announcement_ind *)msg->param;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	//AICWFDBG(LOGDEBUG, "vif: %d, length: %d, offset: %d\n",
+	// ind->vif_index, ind->length, ind->offset);
+
+	AICWFDBG(LOGDEBUG, "%s, vif: %d, length: %d, offset: %d\n",
+		 __func__, ind->vif_index, ind->length, ind->offset);
+
+	vif = rwnx_hw->vif_table[ind->vif_index];
+	rxhdr.flags_is_amsdu = false;
+	rxhdr.flags_dst_idx = 0xff;
+
+	if (ind->payload[0] & 0x80)
+		hdrlen += 2;
+
+	switch ((ind->rx_status >> 2) & 0x7) {
+	case RWNX_RX_HD_DECR_CCMP128:
+		hdrlen += 8;
+		break;
+	case RWNX_RX_HD_DECR_TKIP:
+		hdrlen += 8;
+		break;
+	case RWNX_RX_HD_DECR_WEP:
+		hdrlen += 4;
+		break;
+	case RWNX_RX_HD_DECR_WAPI:
+		hdrlen += 18;
+		break;
+	default:
+		break;
+	}
+
+	skb = __dev_alloc_skb(ind->length, GFP_ATOMIC);
+	if (!skb) {
+		AICWFDBG(LOGERROR, "%s skb alloc fail\n", __func__);
+		return -1;
+	}
+	skb_put(skb, ind->length);
+	memcpy(skb->data, ind->payload, ind->length);
+	skb->dev = vif->ndev;
+
+	new_skb = arp_80211_to_8023(skb, hdrlen);
+	if (!new_skb) {
+		AICWFDBG(LOGERROR, "arp_80211_to_8023 fail\n");
+		dev_kfree_skb(skb);
+		return -1;
+	}
+	if (!rwnx_rx_data_skb(rwnx_hw, vif, new_skb, &rxhdr)) {
+		AICWFDBG(LOGERROR, "rwnx_rx_data_skb fail\n");
+		dev_kfree_skb(new_skb);
+		dev_kfree_skb(skb);
+		return -1;
+	}
+
+	dev_kfree_skb(skb);
+	return 0;
+}
+
+static inline int rwnx_rx_csa_counter_ind(struct rwnx_hw *rwnx_hw,
+					  struct rwnx_cmd *cmd,
+					  struct ipc_e2a_msg *msg)
+{
+	struct mm_csa_counter_ind *ind = (struct mm_csa_counter_ind *)msg->param;
+	struct rwnx_vif *vif;
+	bool found = false;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	// Look for VIF entry
+	list_for_each_entry(vif, &rwnx_hw->vifs, list) {
+		if (vif->vif_index == ind->vif_index) {
+			found = true;
+			break;
+		}
+	}
+
+	if (found) {
+#ifdef CONFIG_RWNX_FULLMAC
+		if (vif->ap.csa)
+			vif->ap.csa->count = ind->csa_count;
+		else
+			netdev_err(vif->ndev, "CSA counter update but no active CSA");
+
+#endif
+	}
+
+	return 0;
+}
+
+#ifdef CONFIG_RWNX_FULLMAC
+static inline int rwnx_rx_csa_finish_ind(struct rwnx_hw *rwnx_hw,
+					 struct rwnx_cmd *cmd,
+					 struct ipc_e2a_msg *msg)
+{
+	struct mm_csa_finish_ind *ind = (struct mm_csa_finish_ind *)msg->param;
+	struct rwnx_vif *vif;
+	bool found = false;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	// Look for VIF entry
+	list_for_each_entry(vif, &rwnx_hw->vifs, list) {
+		if (vif->vif_index == ind->vif_index) {
+			found = true;
+			break;
+		}
+	}
+
+	if (found) {
+		if (RWNX_VIF_TYPE(vif) == NL80211_IFTYPE_AP ||
+		    RWNX_VIF_TYPE(vif) == NL80211_IFTYPE_P2P_GO) {
+			if (vif->ap.csa) {
+				vif->ap.csa->status = ind->status;
+				vif->ap.csa->ch_idx = ind->chan_idx;
+				schedule_work(&vif->ap.csa->work);
+			} else {
+				netdev_err(vif->ndev,
+					   "CSA finish indication but no active CSA");
+			}
+		} else {
+			if (ind->status == 0) {
+				rwnx_chanctx_unlink(vif);
+				rwnx_chanctx_link(vif, ind->chan_idx, NULL);
+				if (rwnx_hw->cur_chanctx == ind->chan_idx) {
+					rwnx_radar_detection_enable_on_cur_channel(rwnx_hw);
+					rwnx_txq_vif_start(vif, RWNX_TXQ_STOP_CHAN, rwnx_hw);
+				} else {
+					rwnx_txq_vif_stop(vif, RWNX_TXQ_STOP_CHAN, rwnx_hw);
+				}
+			}
+		}
+	}
+
+	return 0;
+}
+
+static inline int rwnx_rx_csa_traffic_ind(struct rwnx_hw *rwnx_hw,
+					  struct rwnx_cmd *cmd,
+					  struct ipc_e2a_msg *msg)
+{
+	struct mm_csa_traffic_ind *ind = (struct mm_csa_traffic_ind *)msg->param;
+	struct rwnx_vif *vif;
+	bool found = false;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	// Look for VIF entry
+	list_for_each_entry(vif, &rwnx_hw->vifs, list) {
+		if (vif->vif_index == ind->vif_index) {
+			found = true;
+			break;
+		}
+	}
+
+	if (found) {
+		if (ind->enable)
+			rwnx_txq_vif_start(vif, RWNX_TXQ_STOP_CSA, rwnx_hw);
+		else
+			rwnx_txq_vif_stop(vif, RWNX_TXQ_STOP_CSA, rwnx_hw);
+	}
+
+	return 0;
+}
+
+static inline int rwnx_rx_ps_change_ind(struct rwnx_hw *rwnx_hw,
+					struct rwnx_cmd *cmd,
+					struct ipc_e2a_msg *msg)
+{
+	struct mm_ps_change_ind *ind = (struct mm_ps_change_ind *)msg->param;
+	struct rwnx_sta *sta = &rwnx_hw->sta_table[ind->sta_idx];
+
+	if (ind->sta_idx >= (NX_REMOTE_STA_MAX + NX_VIRT_DEV_MAX)) {
+		wiphy_err(rwnx_hw->wiphy, "Invalid sta index reported by fw %d\n",
+			  ind->sta_idx);
+		return 1;
+	}
+
+	netdev_dbg(rwnx_hw->vif_table[sta->vif_idx]->ndev,
+		   "Sta %d, change PS mode to %s", sta->sta_idx,
+		   ind->ps_state ? "ON" : "OFF");
+
+	if (sta->valid) {
+		rwnx_ps_bh_enable(rwnx_hw, sta, ind->ps_state);
+	} else if (rwnx_hw->adding_sta) {
+		sta->ps.active = ind->ps_state ? true : false;
+	} else {
+		if (rwnx_hw->vif_table[sta->vif_idx] &&
+		    rwnx_hw->vif_table[sta->vif_idx]->ndev)
+			netdev_err(rwnx_hw->vif_table[sta->vif_idx]->ndev,
+				   "Ignore PS mode change on invalid sta\n");
+	}
+
+	return 0;
+}
+
+static inline int rwnx_rx_traffic_req_ind(struct rwnx_hw *rwnx_hw,
+					  struct rwnx_cmd *cmd,
+					  struct ipc_e2a_msg *msg)
+{
+	struct mm_traffic_req_ind *ind = (struct mm_traffic_req_ind *)msg->param;
+	struct rwnx_sta *sta = &rwnx_hw->sta_table[ind->sta_idx];
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	netdev_dbg(rwnx_hw->vif_table[sta->vif_idx]->ndev,
+		   "Sta %d, asked for %d pkt", sta->sta_idx, ind->pkt_cnt);
+
+	rwnx_ps_bh_traffic_req(rwnx_hw, sta, ind->pkt_cnt,
+			       ind->uapsd ? UAPSD_ID : LEGACY_PS_ID);
+
+	return 0;
+}
+#endif /* CONFIG_RWNX_FULLMAC */
+
+/***************************************************************************
+ * Messages from SCANU task
+ **************************************************************************/
+#ifdef CONFIG_RWNX_FULLMAC
+
+static inline int rwnx_rx_scanu_start_cfm(struct rwnx_hw *rwnx_hw,
+					  struct rwnx_cmd *cmd,
+					  struct ipc_e2a_msg *msg)
+{
+#ifdef CONFIG_AIC8800_AUTO_CUSTREG
+	const struct ieee80211_regdomain *regdomain;
+#endif
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+#ifdef CONFIG_AIC8800_SCHED_SCAN
+	if (rwnx_hw->scan_request && !rwnx_hw->is_sched_scan)
+#endif // CONFIG_AIC8800_SCHED_SCAN
+	{
+		struct cfg80211_scan_info info = {
+			.aborted = false,
+		};
+
+		cfg80211_scan_done(rwnx_hw->scan_request, &info);
+	}
+
+#ifdef CONFIG_AIC8800_SCHED_SCAN
+	if (rwnx_hw->is_sched_scan) {
+		AICWFDBG(LOGINFO, "%s cfg80211_sched_scan_results \r\n", __func__);
+		cfg80211_sched_scan_results(rwnx_hw->scan_request->wiphy,
+					    rwnx_hw->sched_scan_req->reqid);
+		kfree(rwnx_hw->scan_request);
+		rwnx_hw->is_sched_scan = false;
+	}
+#endif // CONFIG_AIC8800_SCHED_SCAN
+
+#ifdef CONFIG_AIC8800_AUTO_CUSTREG
+	if (rwnx_hw->ccode.auto_set && rwnx_hw->ccode.ccode_find) {
+#ifdef CONFIG_AIC8800_REGION_PW
+		schedule_work(&rwnx_hw->region_wk);
+#endif
+#ifdef CONFIG_AIC8800_POWER_LIMIT
+		if (!work_pending(&rwnx_hw->pwlt_wk))
+			schedule_work(&rwnx_hw->pwlt_wk);
+#endif
+		regdomain = get_regdomain_from_rwnx_db(rwnx_hw->wiphy, rwnx_hw->country_abbr);
+		rwnx_regulatory_set_wiphy_regd(rwnx_hw->wiphy, regdomain);
+		rwnx_hw->ccode.ccode_cnt = 0;
+		rwnx_hw->ccode.ccode_find = false;
+		rwnx_hw->ccode.ccode_set = true;
+	}
+#endif
+
+	rwnx_hw->scan_request = NULL;
+	rwnx_hw->scanning = 0;
+	aicwf_sdio_scan_activity_put(rwnx_hw);
+
+	return 0;
+}
+
+#ifdef CONFIG_AIC8800_AUTO_CUSTREG
+static void parse_ssid_from_id(const u8 *ssid_id, char ssid_name[33], u8 *ssid_len)
+{
+	u8 len;
+
+	if (!ssid_id)
+		return;
+
+	len = *(ssid_id + 1);
+	if (len > 32)
+		len = 32;
+
+	strscpy(ssid_name, ssid_id + 2, len);
+	ssid_name[len] = '\0';
+	*ssid_len = len;
+
+	if (len > 0)
+		AICWFDBG(LOGDEBUG, "find_ccode ssid: %s\n", ssid_name);
+	else
+		AICWFDBG(LOGDEBUG, "find_ccode ssid is null\n");
+}
+
+static void rwnx_update_country(struct rwnx_hw *rwnx_hw,
+				const u8 *country_id,
+				const struct ieee80211_regdomain **regdomain)
+{
+	if (!country_id)
+		return;
+
+	if (memcmp(rwnx_hw->country_abbr, country_id + 2, 2) != 0) {
+		AICWFDBG(LOGINFO, "update ccode of the connected ap\n");
+
+		strscpy(rwnx_hw->country_abbr, country_id + 2, 2);
+
+		*regdomain = get_regdomain_from_rwnx_db(rwnx_hw->wiphy, rwnx_hw->country_abbr);
+
+#ifdef CONFIG_AIC8800_REGION_PW
+		schedule_work(&rwnx_hw->region_wk);
+#endif
+
+#ifdef CONFIG_AIC8800_POWER_LIMIT
+		if (!work_pending(&rwnx_hw->pwlt_wk))
+			schedule_work(&rwnx_hw->pwlt_wk);
+#endif
+
+		rwnx_regulatory_set_wiphy_regd(rwnx_hw->wiphy, *regdomain);
+	}
+}
+#endif
+
+static inline int rwnx_rx_scanu_result_ind(struct rwnx_hw *rwnx_hw,
+					   struct rwnx_cmd *cmd,
+					   struct ipc_e2a_msg *msg)
+{
+	struct cfg80211_bss *bss = NULL;
+	struct ieee80211_channel *chan;
+	struct scanu_result_ind *ind = (struct scanu_result_ind *)msg->param;
+	struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)ind->payload;
+	u64 tsf;
+
+#ifdef CONFIG_AIC8800_AUTO_CUSTREG
+	const u8 *country_id;
+	const u8 *ssid_id;
+	char ssid_name[33];
+	u8 ssid_len;
+	const struct ieee80211_regdomain *regdomain;
+#endif
+
+
+	// RWNX_DBG(RWNX_FN_ENTRY_STR); //debug trace
+
+	chan = ieee80211_get_channel(rwnx_hw->wiphy, ind->center_freq);
+
+	if (chan) {
+		struct timespec64 ts;
+
+		ts = ktime_to_timespec64(ktime_get_boottime());
+		tsf = (u64)ts.tv_sec * 1000000 + div_u64(ts.tv_nsec, 1000);
+		mgmt->u.probe_resp.timestamp = cpu_to_le64(tsf);
+
+#ifdef CONFIG_AIC8800_AUTO_CUSTREG
+		if (rwnx_hw->ccode.auto_set && !rwnx_hw->ccode.ccode_set &&
+		    rwnx_hw->ccode.ccode_cnt < MAX_SEARCH_CNT) {
+			country_id =
+				cfg80211_find_ie(WLAN_EID_COUNTRY, mgmt->u.beacon.variable,
+						 ind->length - offsetof(struct ieee80211_mgmt,
+									u.beacon.variable));
+			if (country_id) {
+				if (ind->rssi > rwnx_hw->ccode.ccode_rssi) {
+					rwnx_hw->ccode.ccode_rssi = ind->rssi;
+					// AICWFDBG(LOGINFO, "find_ccode, bssid: %pM\n",
+					// mgmt->bssid); //debug trace
+					if (!rwnx_hw->ccode.ccode_find)
+						rwnx_hw->ccode.ccode_find = true;
+					strscpy(rwnx_hw->country_abbr, country_id + 2, 2);
+					ssid_id =
+					cfg80211_find_ie(WLAN_EID_SSID, mgmt->u.beacon.variable,
+							 ind->length -
+							 offsetof(struct ieee80211_mgmt,
+								  u.beacon.variable));
+					parse_ssid_from_id(ssid_id, ssid_name, &ssid_len);
+				}
+			}
+			rwnx_hw->ccode.ccode_cnt += 1;
+			AICWFDBG(LOGDEBUG, "find cnt: %d\n",
+				 rwnx_hw->ccode.ccode_cnt); // debug trace
+		} else if (rwnx_hw->ccode.auto_set && !rwnx_hw->ccode.ccode_find &&
+			   !rwnx_hw->ccode.ccode_set &&
+			   rwnx_hw->ccode.ccode_cnt >= MAX_SEARCH_CNT) {
+			AICWFDBG(LOGINFO, "find cnt over !!!\n");
+			strscpy(rwnx_hw->country_abbr, "XX", 2);
+			rwnx_hw->ccode.ccode_find = true;
+		}
+
+		if (rwnx_hw->ccode.auto_set &&
+		    atomic_read(&rwnx_hw->ccode.link_scan) == 1) {
+			if (memcmp(&rwnx_hw->ccode.bssid, mgmt->bssid, ETH_ALEN) == 0) {
+				country_id =
+				cfg80211_find_ie(WLAN_EID_COUNTRY,
+						 mgmt->u.probe_resp.variable,
+						 ind->length - offsetof(struct ieee80211_mgmt,
+									u.probe_resp.variable));
+				rwnx_update_country(rwnx_hw, country_id, &regdomain);
+				memset(&rwnx_hw->ccode.bssid, 0, sizeof(rwnx_hw->ccode.bssid));
+			}
+			atomic_set(&rwnx_hw->ccode.link_scan, 0);
+			rwnx_hw->ccode.ccode_cnt = 0;
+		}
+#endif
+		bss =
+		cfg80211_inform_bss_frame(rwnx_hw->wiphy, chan,
+					  (struct ieee80211_mgmt *)ind->payload,
+					  ind->length, ind->rssi * 100, GFP_ATOMIC);
+
+	}
+	if (bss)
+		cfg80211_put_bss(rwnx_hw->wiphy, bss);
+	return 0;
+}
+#endif /* CONFIG_RWNX_FULLMAC */
+
+/***************************************************************************
+ * Messages from ME task
+ **************************************************************************/
+#ifdef CONFIG_RWNX_FULLMAC
+static inline int rwnx_rx_me_tkip_mic_failure_ind(struct rwnx_hw *rwnx_hw,
+						  struct rwnx_cmd *cmd,
+						  struct ipc_e2a_msg *msg)
+{
+	struct me_tkip_mic_failure_ind *ind =
+		(struct me_tkip_mic_failure_ind *)msg->param;
+	struct rwnx_vif *rwnx_vif = rwnx_hw->vif_table[ind->vif_idx];
+	struct net_device *dev = rwnx_vif->ndev;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	cfg80211_michael_mic_failure(dev, (u8 *)&ind->addr,
+				     (ind->ga ? NL80211_KEYTYPE_GROUP : NL80211_KEYTYPE_PAIRWISE),
+				     ind->keyid, (u8 *)&ind->tsc, GFP_ATOMIC);
+
+	return 0;
+}
+
+static inline int rwnx_rx_me_tx_credits_update_ind(struct rwnx_hw *rwnx_hw,
+						   struct rwnx_cmd *cmd,
+						   struct ipc_e2a_msg *msg)
+{
+	struct me_tx_credits_update_ind *ind =
+		(struct me_tx_credits_update_ind *)msg->param;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	rwnx_txq_credit_update(rwnx_hw, ind->sta_idx, ind->tid, ind->credits);
+
+	return 0;
+}
+#endif /* CONFIG_RWNX_FULLMAC */
+
+/***************************************************************************
+ * Messages from SM task
+ **************************************************************************/
+#ifdef CONFIG_RWNX_FULLMAC
+static inline int rwnx_rx_sm_connect_ind(struct rwnx_hw *rwnx_hw,
+					 struct rwnx_cmd *cmd,
+					 struct ipc_e2a_msg *msg)
+{
+	struct sm_connect_ind *ind = (struct sm_connect_ind *)msg->param;
+	struct rwnx_vif *rwnx_vif = rwnx_hw->vif_table[ind->vif_idx];
+	struct net_device *dev = rwnx_vif->ndev;
+	const u8 *req_ie, *rsp_ie;
+	const u8 *extcap_ie;
+	const struct ieee_types_extcap *extcap;
+	struct ieee80211_channel *chan;
+	const u8 *ht_ie = NULL;
+	const u8 *vht_ie = NULL;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	/* Retrieve IE addresses and lengths */
+	req_ie = (const u8 *)ind->assoc_ie_buf;
+	rsp_ie = req_ie + ind->assoc_req_ie_len;
+
+	// Fill-in the AP information
+	AICWFDBG(LOGINFO, "%s ind->status_code:%d \r\n", __func__,
+		 ind->status_code);
+
+	if (ind->status_code == 0) {
+		struct rwnx_sta *sta = &rwnx_hw->sta_table[ind->ap_idx];
+		u8 txq_status;
+		struct cfg80211_chan_def chandef;
+
+		sta->valid = true;
+		sta->sta_idx = ind->ap_idx;
+		sta->ch_idx = ind->ch_idx;
+		sta->vif_idx = ind->vif_idx;
+		sta->vlan_idx = sta->vif_idx;
+		sta->qos = ind->qos;
+		sta->acm = ind->acm;
+		sta->ps.active = false;
+		sta->aid = ind->aid;
+		sta->band = ind->band;                 // ind->chan.band;
+		sta->width = ind->width;               // ind->chan.type;
+		sta->center_freq = ind->center_freq;   // ind->chan.prim20_freq;
+		sta->center_freq1 = ind->center_freq1; // ind->chan.center1_freq;
+		sta->center_freq2 = ind->center_freq2; // ind->chan.center2_freq;
+		rwnx_vif->sta.ap = sta;
+		// ind->chan.prim20_freq);
+		chan = ieee80211_get_channel(rwnx_hw->wiphy, ind->center_freq);
+		cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_NO_HT);
+		if (!rwnx_hw->mod_params->ht_on)
+			chandef.width = NL80211_CHAN_WIDTH_20_NOHT;
+		else
+			chandef.width = chnl2bw[ind->width];  //[ind->chan.type];
+		chandef.center_freq1 = ind->center_freq1; // ind->chan.center1_freq;
+		chandef.center_freq2 = ind->center_freq2; // ind->chan.center2_freq;
+		rwnx_chanctx_link(rwnx_vif, ind->ch_idx, &chandef);
+		memcpy(sta->mac_addr, ind->bssid.array, ETH_ALEN);
+		if (ind->ch_idx == rwnx_hw->cur_chanctx)
+			txq_status = 0;
+		else
+			txq_status = RWNX_TXQ_STOP_CHAN;
+		memcpy(sta->ac_param, ind->ac_param, sizeof(sta->ac_param));
+		rwnx_txq_sta_init(rwnx_hw, sta, txq_status);
+		rwnx_txq_tdls_vif_init(rwnx_vif);
+		rwnx_mu_group_sta_init(sta, NULL);
+		/* Look for TDLS Channel Switch Prohibited flag in the Extended
+		 * Capability Information Element
+		 */
+		extcap_ie = cfg80211_find_ie(WLAN_EID_EXT_CAPABILITY, rsp_ie,
+					     ind->assoc_rsp_ie_len);
+		if (extcap_ie && extcap_ie[1] >= 5) {
+			extcap = (void *)(extcap_ie);
+			rwnx_vif->tdls_chsw_prohibited =
+				extcap->ext_capab[4] & WLAN_EXT_CAPA5_TDLS_CH_SW_PROHIBITED;
+		}
+
+		if (rwnx_vif->wep_enabled)
+			rwnx_vif->wep_auth_err = false;
+
+#ifdef CONFIG_RWNX_BFMER
+		/* If Beamformer feature is activated, check if features can be used
+		 * with the new peer device
+		 */
+		if (rwnx_hw->mod_params->bfmer) {
+			const u8 *vht_capa_ie;
+			const struct ieee80211_vht_cap *vht_cap;
+
+			do {
+				/* Look for VHT Capability Information Element */
+				vht_capa_ie = cfg80211_find_ie(WLAN_EID_VHT_CAPABILITY, rsp_ie,
+							       ind->assoc_rsp_ie_len);
+
+				/* Stop here if peer device does not support VHT */
+				if (!vht_capa_ie)
+					break;
+
+				vht_cap = (const struct ieee80211_vht_cap *)(vht_capa_ie + 2);
+
+				/* Send MM_BFMER_ENABLE_REQ message if needed */
+				rwnx_send_bfmer_enable(rwnx_hw, sta, vht_cap);
+			} while (0);
+		}
+#endif //(CONFIG_RWNX_BFMER)
+
+		ht_ie = cfg80211_find_ie(WLAN_EID_HT_CAPABILITY, rsp_ie,
+					 ind->assoc_rsp_ie_len);
+		if (ht_ie)
+			rwnx_vif->sta.ap->ht = true;
+		vht_ie = cfg80211_find_ie(WLAN_EID_VHT_CAPABILITY, rsp_ie,
+					  ind->assoc_rsp_ie_len);
+		if (vht_ie)
+			rwnx_vif->sta.ap->vht = true;
+
+#ifdef CONFIG_RWNX_MON_DATA
+		// If there are 1 sta and 1 monitor interface active at the same time
+		// then monitor interface channel context is always the same as the STA
+		// interface. This doesn't work with 2 STA interfaces but we don't want
+		// to support it.
+		if (rwnx_hw->monitor_vif != RWNX_INVALID_VIF) {
+			struct rwnx_vif *rwnx_mon_vif =
+				rwnx_hw->vif_table[rwnx_hw->monitor_vif];
+			rwnx_chanctx_unlink(rwnx_mon_vif);
+			rwnx_chanctx_link(rwnx_mon_vif, ind->ch_idx, NULL);
+		}
+#endif
+		// atomic_set(&rwnx_vif->drv_conn_state,
+		// (int)RWNX_DRV_STATUS_CONNECTED);
+	} else if (ind->status_code == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG) {
+		if (rwnx_vif->wep_enabled) {
+			rwnx_vif->wep_auth_err = true;
+			AICWFDBG(LOGINFO, "con ind wep_auth_err %d\n",
+				 rwnx_vif->wep_auth_err);
+		}
+		rwnx_set_conn_state(&rwnx_vif->drv_conn_state, (int)RWNX_DRV_STATUS_DISCONNECTED);
+	} else {
+		rwnx_set_conn_state(&rwnx_vif->drv_conn_state, (int)RWNX_DRV_STATUS_DISCONNECTED);
+	}
+
+	AICWFDBG(LOGINFO,
+		 "%s ind->roamed:%d ind->status_code:%d rwnx_vif->drv_conn_state:%d\r\n",
+		 __func__, ind->roamed, ind->status_code,
+		 (int)atomic_read(&rwnx_vif->drv_conn_state));
+
+	if (ind->status_code == 0 &&
+	    (int)atomic_read(&rwnx_vif->drv_conn_state) == RWNX_DRV_STATUS_DISCONNECTING) {
+		AICWFDBG(LOGINFO, "%s the disconnection has been requested, return it\r\n",
+			 __func__);
+		goto exit;
+	}
+
+	if (!ind->roamed) { // not roaming
+		cfg80211_connect_result(dev, (const u8 *)ind->bssid.array,
+					req_ie, ind->assoc_req_ie_len,
+					rsp_ie, ind->assoc_rsp_ie_len, ind->status_code,
+					GFP_ATOMIC);
+		if (ind->status_code == 0) {
+			rwnx_set_conn_state(&rwnx_vif->drv_conn_state,
+					    (int)RWNX_DRV_STATUS_CONNECTED);
+			AICWFDBG(LOGINFO,
+				 "%s cfg80211_connect_result pass, rwnx_vif->drv_conn_state:%d\r\n",
+				 __func__, (int)atomic_read(&rwnx_vif->drv_conn_state));
+			rwnx_vif->sta.link_time = jiffies;
+		} else {
+			rwnx_set_conn_state(&rwnx_vif->drv_conn_state,
+					    (int)RWNX_DRV_STATUS_DISCONNECTED);
+			rwnx_external_auth_disable(rwnx_vif);
+		}
+	} else { // roaming
+		if (ind->status_code != 0) {
+			AICWFDBG(LOGINFO, "%s roaming fail to notify disconnect \r\n",
+				 __func__);
+			cfg80211_disconnected(dev, 0, NULL, 0, 1, GFP_ATOMIC);
+			rwnx_set_conn_state(&rwnx_vif->drv_conn_state,
+					    (int)RWNX_DRV_STATUS_DISCONNECTED);
+			rwnx_external_auth_disable(rwnx_vif);
+		} else {
+			struct cfg80211_roam_info info;
+
+			memset(&info, 0, sizeof(info));
+			if (rwnx_vif->ch_index < NX_CHAN_CTXT_CNT)
+				info.links[0].channel =
+					rwnx_hw->chanctx_table[rwnx_vif->ch_index].chan_def.chan;
+			info.links[0].bssid = (const u8 *)ind->bssid.array;
+			info.req_ie = req_ie;
+			info.req_ie_len = ind->assoc_req_ie_len;
+			info.resp_ie = rsp_ie;
+			info.resp_ie_len = ind->assoc_rsp_ie_len;
+			cfg80211_roamed(dev, &info, GFP_ATOMIC);
+			rwnx_vif->sta.link_time = jiffies;
+
+			rwnx_set_conn_state(&rwnx_vif->drv_conn_state,
+					    (int)RWNX_DRV_STATUS_CONNECTED);
+			rwnx_vif->sta.link_time = jiffies;
+		}
+		rwnx_vif->sta.is_roam = false;
+	}
+
+	if (ind->status_code == 0) {
+		netif_tx_start_all_queues(dev);
+		netif_carrier_on(dev);
+	}
+
+exit:
+	rwnx_vif->sta.is_roam = false;
+	return 0;
+}
+
+void rwnx_cfg80211_unlink_bss(struct rwnx_hw *rwnx_hw,
+			      struct rwnx_vif *rwnx_vif)
+{
+	struct wiphy *wiphy = rwnx_hw->wiphy;
+	struct cfg80211_bss *bss = NULL;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	bss = cfg80211_get_bss(wiphy, NULL /*notify_channel */, rwnx_vif->sta.bssid,
+			       rwnx_vif->sta.ssid, rwnx_vif->sta.ssid_len,
+			       IEEE80211_BSS_TYPE_ESS,
+			       IEEE80211_PRIVACY(true)); // temp set true
+	if (bss) {
+		cfg80211_unlink_bss(wiphy, bss);
+		AICWFDBG(LOGINFO, "%s(): cfg80211_unlink %s!!\n", __func__,
+			 rwnx_vif->sta.ssid);
+		cfg80211_put_bss(wiphy, bss);
+	} else {
+		AICWFDBG(LOGINFO, "%s(): cfg80211_unlink error %s!!\n", __func__,
+			 rwnx_vif->sta.ssid);
+	}
+}
+
+static inline int rwnx_rx_sm_disconnect_ind(struct rwnx_hw *rwnx_hw,
+					    struct rwnx_cmd *cmd,
+					    struct ipc_e2a_msg *msg)
+{
+	struct sm_disconnect_ind *ind = (struct sm_disconnect_ind *)msg->param;
+	struct rwnx_vif *rwnx_vif = rwnx_hw->vif_table[ind->vif_idx];
+	struct net_device *dev;
+#ifdef AICWF_RX_REORDER
+	struct reord_ctrl_info *reord_info, *tmp;
+	u8 *macaddr;
+	struct aicwf_rx_priv *rx_priv;
+#endif
+
+	AICWFDBG(LOGINFO, "%s reason code:%d \r\n", __func__, ind->reason_code);
+
+	if (!rwnx_vif)
+		return 0;
+
+	if ((int)atomic_read(&rwnx_vif->drv_conn_state) ==
+		(int)RWNX_DRV_STATUS_DISCONNECTED) {
+		AICWFDBG(LOGINFO, "%s, is already disconnected, drop disconnect ind",
+			 __func__);
+		return 0;
+	}
+
+	dev = rwnx_vif->ndev;
+
+	if (!rwnx_vif->sta.is_roam)
+		rwnx_cfg80211_unlink_bss(rwnx_hw, rwnx_vif);
+	else
+		AICWFDBG(LOGINFO, "%s roaming no rwnx_cfg80211_unlink_bss \r\n",
+			 __func__);
+	if (rwnx_vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT)
+		rwnx_hw->is_p2p_connected = 0;
+	/* if vif is not up, rwnx_close has already been called */
+	if (rwnx_vif->up) {
+		if (!ind->ft_over_ds && !ind->reassoc) {
+			cfg80211_disconnected(dev, ind->reason_code, NULL, 0,
+					      (ind->reason_code < 1), GFP_ATOMIC);
+		}
+		netif_tx_stop_all_queues(dev);
+		netif_carrier_off(dev);
+	}
+
+#ifdef CONFIG_RWNX_BFMER
+	/* Disable Beamformer if supported */
+	rwnx_bfmer_report_del(rwnx_hw, rwnx_vif->sta.ap);
+#endif //(CONFIG_RWNX_BFMER)
+
+#ifdef AICWF_RX_REORDER
+#ifdef AICWF_SDIO_SUPPORT
+	rx_priv = rwnx_hw->sdiodev->rx_priv;
+#else
+	rx_priv = rwnx_hw->usbdev->rx_priv;
+#endif
+	if (rwnx_vif->wdev.iftype == NL80211_IFTYPE_STATION ||
+	    rwnx_vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) {
+		macaddr = (u8 *)rwnx_vif->ndev->dev_addr;
+		AICWFDBG(LOGDEBUG, "deinit:macaddr:%x,%x,%x,%x,%x,%x\r\n", macaddr[0], macaddr[1],
+			 macaddr[2], macaddr[3], macaddr[4], macaddr[5]);
+
+		// spin_lock_bh(&rx_priv->stas_reord_lock);
+		list_for_each_entry_safe(reord_info, tmp, &rx_priv->stas_reord_list,
+					 list) {
+			macaddr = (u8 *)rwnx_vif->ndev->dev_addr;
+			AICWFDBG(LOGDEBUG, "reord_mac:%x,%x,%x,%x,%x,%x\r\n",
+				 reord_info->mac_addr[0], reord_info->mac_addr[1],
+				 reord_info->mac_addr[2], reord_info->mac_addr[3],
+				 reord_info->mac_addr[4], reord_info->mac_addr[5]);
+			if (!memcmp(reord_info->mac_addr, macaddr, 6)) {
+				reord_deinit_sta(rx_priv, reord_info);
+				break;
+			}
+		}
+		// spin_unlock_bh(&rx_priv->stas_reord_lock);
+	} else if (rwnx_vif->wdev.iftype == NL80211_IFTYPE_AP ||
+		   rwnx_vif->wdev.iftype == NL80211_IFTYPE_P2P_GO) {
+		// WARN_ON_ONCE(); // should be not here: del_sta function
+	}
+#endif
+
+	rwnx_txq_sta_deinit(rwnx_hw, rwnx_vif->sta.ap);
+	rwnx_txq_tdls_vif_deinit(rwnx_vif);
+	rwnx_vif->sta.ap->valid = false;
+	rwnx_vif->sta.ap = NULL;
+	rwnx_external_auth_disable(rwnx_vif);
+	rwnx_chanctx_unlink(rwnx_vif);
+
+	// msleep(200);
+	if (!rwnx_vif->sta.is_roam)
+		rwnx_set_conn_state(&rwnx_vif->drv_conn_state, (int)RWNX_DRV_STATUS_DISCONNECTED);
+
+	return 0;
+}
+
+static inline int rwnx_rx_sm_external_auth_required_ind(struct rwnx_hw *rwnx_hw,
+							struct rwnx_cmd *cmd,
+							struct ipc_e2a_msg *msg)
+{
+	struct sm_external_auth_required_ind *ind =
+		(struct sm_external_auth_required_ind *)msg->param;
+	struct rwnx_vif *rwnx_vif = rwnx_hw->vif_table[ind->vif_idx];
+	struct net_device *dev = rwnx_vif->ndev;
+	struct cfg80211_external_auth_params params;
+	int ret = 0;
+	struct wireless_dev *wdev = dev->ieee80211_ptr;
+	int retry_counter = 10;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	params.action = NL80211_EXTERNAL_AUTH_START;
+	memcpy(params.bssid, ind->bssid.array, ETH_ALEN);
+	params.ssid.ssid_len = ind->ssid.length;
+	memcpy(params.ssid.ssid, ind->ssid.array,
+	       min_t(size_t, ind->ssid.length, sizeof(params.ssid.ssid)));
+	params.key_mgmt_suite = ind->akm;
+
+	while (wdev->conn_owner_nlportid == 0) {
+		AICWFDBG(LOGINFO,
+			 "%s WARNING conn_owner_nlportid = 0, msleep 100ms.\r\n",
+			 __func__);
+		msleep(100);
+		retry_counter--;
+		if (retry_counter == 0)
+			break;
+	}
+	AICWFDBG(LOGINFO, "%s wdev->conn_owner_nlportid:%d \r\n", __func__,
+		 (int)wdev->conn_owner_nlportid);
+	ret = cfg80211_external_auth_request(dev, &params, GFP_ATOMIC);
+	if (ind->vif_idx > NX_VIRT_DEV_MAX || !rwnx_vif->up ||
+	    RWNX_VIF_TYPE(rwnx_vif) != NL80211_IFTYPE_STATION ||
+	    ret) {
+		wiphy_err(rwnx_hw->wiphy,
+			  "Failed to start external auth on vif %d, rwnx_vif->up %d, iftype:%d, ret %d",
+			  ind->vif_idx, rwnx_vif->up, RWNX_VIF_TYPE(rwnx_vif), ret);
+		rwnx_send_sm_external_auth_required_rsp(rwnx_hw, rwnx_vif,
+							WLAN_STATUS_UNSPECIFIED_FAILURE);
+		return 0;
+	}
+
+	rwnx_external_auth_enable(rwnx_vif);
+	return 0;
+}
+
+static inline int rwnx_rx_mesh_path_create_cfm(struct rwnx_hw *rwnx_hw,
+					       struct rwnx_cmd *cmd,
+					       struct ipc_e2a_msg *msg)
+{
+	struct mesh_path_create_cfm *cfm =
+		(struct mesh_path_create_cfm *)msg->param;
+	struct rwnx_vif *rwnx_vif = rwnx_hw->vif_table[cfm->vif_idx];
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	/* Check we well have a Mesh Point Interface */
+	if (rwnx_vif && RWNX_VIF_TYPE(rwnx_vif) == NL80211_IFTYPE_MESH_POINT)
+		rwnx_vif->ap.create_path = false;
+
+	return 0;
+}
+
+static inline int rwnx_rx_mesh_peer_update_ind(struct rwnx_hw *rwnx_hw,
+					       struct rwnx_cmd *cmd,
+					       struct ipc_e2a_msg *msg)
+{
+	struct mesh_peer_update_ind *ind =
+		(struct mesh_peer_update_ind *)msg->param;
+	struct rwnx_vif *rwnx_vif = rwnx_hw->vif_table[ind->vif_idx];
+	struct rwnx_sta *rwnx_sta = &rwnx_hw->sta_table[ind->sta_idx];
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	if (ind->vif_idx >= (NX_VIRT_DEV_MAX + NX_REMOTE_STA_MAX) ||
+	    (rwnx_vif && RWNX_VIF_TYPE(rwnx_vif) != NL80211_IFTYPE_MESH_POINT) ||
+	    ind->sta_idx >= NX_REMOTE_STA_MAX)
+		return 1;
+
+	/* Check we well have a Mesh Point Interface */
+	if (!rwnx_vif->user_mpm) {
+		/* Check if peer link has been established or lost */
+		if (ind->estab) {
+			if (!rwnx_sta->valid) {
+				u8 txq_status;
+
+				rwnx_sta->valid = true;
+				rwnx_sta->sta_idx = ind->sta_idx;
+				rwnx_sta->ch_idx = rwnx_vif->ch_index;
+				rwnx_sta->vif_idx = ind->vif_idx;
+				rwnx_sta->vlan_idx = rwnx_sta->vif_idx;
+				rwnx_sta->ps.active = false;
+				rwnx_sta->qos = true;
+				rwnx_sta->aid = ind->sta_idx + 1;
+				// rwnx_sta->acm = ind->acm;
+				memcpy(rwnx_sta->mac_addr, ind->peer_addr.array, ETH_ALEN);
+
+				rwnx_chanctx_link(rwnx_vif, rwnx_sta->ch_idx, NULL);
+
+				/* Add the station in the list of VIF's stations */
+				INIT_LIST_HEAD(&rwnx_sta->list);
+				list_add_tail(&rwnx_sta->list, &rwnx_vif->ap.sta_list);
+
+				/* Initialize the TX queues */
+				if (rwnx_sta->ch_idx == rwnx_hw->cur_chanctx)
+					txq_status = 0;
+				else
+					txq_status = RWNX_TXQ_STOP_CHAN;
+
+				rwnx_txq_sta_init(rwnx_hw, rwnx_sta, txq_status);
+#ifdef CONFIG_RWNX_BFMER
+				// TODO: update indication to contains vht capabilities
+				if (rwnx_hw->mod_params->bfmer)
+					rwnx_send_bfmer_enable(rwnx_hw, rwnx_sta, NULL);
+
+				rwnx_mu_group_sta_init(rwnx_sta, NULL);
+#endif /* CONFIG_RWNX_BFMER */
+
+			} else {
+				WARN_ON(0);
+			}
+		} else {
+			if (rwnx_sta->valid) {
+				rwnx_sta->ps.active = false;
+				rwnx_sta->valid = false;
+
+				/* Remove the station from the list of VIF's station */
+				list_del_init(&rwnx_sta->list);
+
+				rwnx_txq_sta_deinit(rwnx_hw, rwnx_sta);
+			} else {
+				WARN_ON(0);
+			}
+		}
+	} else {
+		if (!ind->estab && rwnx_sta->valid) {
+			/* There is no way to inform upper layer for lost of peer, still
+			 * clean everything in the driver
+			 */
+			rwnx_sta->ps.active = false;
+			rwnx_sta->valid = false;
+
+			/* Remove the station from the list of VIF's station */
+			list_del_init(&rwnx_sta->list);
+
+			rwnx_txq_sta_deinit(rwnx_hw, rwnx_sta);
+		} else {
+			WARN_ON(0);
+		}
+	}
+
+	return 0;
+}
+
+static inline int rwnx_rx_mesh_path_update_ind(struct rwnx_hw *rwnx_hw,
+					       struct rwnx_cmd *cmd,
+					       struct ipc_e2a_msg *msg)
+{
+	struct mesh_path_update_ind *ind =
+		(struct mesh_path_update_ind *)msg->param;
+	struct rwnx_vif *rwnx_vif = rwnx_hw->vif_table[ind->vif_idx];
+	struct rwnx_mesh_path *mesh_path;
+	bool found = false;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	if (ind->vif_idx >= (NX_VIRT_DEV_MAX + NX_REMOTE_STA_MAX))
+		return 1;
+
+	if (!rwnx_vif || (RWNX_VIF_TYPE(rwnx_vif) != NL80211_IFTYPE_MESH_POINT))
+		return 0;
+
+	/* Look for path with provided target address */
+	list_for_each_entry(mesh_path, &rwnx_vif->ap.mpath_list, list) {
+		if (mesh_path->path_idx == ind->path_idx) {
+			found = true;
+			break;
+		}
+	}
+
+	/* Check if element has been deleted */
+	if (ind->delete) {
+		if (found) {
+#ifdef AIC8800_CREATE_TRACE_POINTS
+			trace_mesh_delete_path(mesh_path);
+#endif
+			/* Remove element from list */
+			list_del_init(&mesh_path->list);
+			/* Free the element */
+			kfree(mesh_path);
+		}
+	} else {
+		if (found) {
+			// Update the Next Hop STA
+			mesh_path->p_nhop_sta = &rwnx_hw->sta_table[ind->nhop_sta_idx];
+#ifdef AIC8800_CREATE_TRACE_POINTS
+			trace_mesh_update_path(mesh_path);
+#endif
+		} else {
+			// Allocate a Mesh Path structure
+			mesh_path =
+				kmalloc_obj(struct rwnx_mesh_path, GFP_ATOMIC);
+
+			if (mesh_path) {
+				INIT_LIST_HEAD(&mesh_path->list);
+
+				mesh_path->path_idx = ind->path_idx;
+				mesh_path->p_nhop_sta = &rwnx_hw->sta_table[ind->nhop_sta_idx];
+				memcpy(&mesh_path->tgt_mac_addr, &ind->tgt_mac_addr,
+				       MAC_ADDR_LEN);
+
+				// Insert the path in the list of path
+				list_add_tail(&mesh_path->list, &rwnx_vif->ap.mpath_list);
+#ifdef AIC8800_CREATE_TRACE_POINTS
+				trace_mesh_create_path(mesh_path);
+#endif
+			}
+		}
+	}
+
+	return 0;
+}
+
+static inline int rwnx_rx_mesh_proxy_update_ind(struct rwnx_hw *rwnx_hw,
+						struct rwnx_cmd *cmd,
+						struct ipc_e2a_msg *msg)
+{
+	struct mesh_proxy_update_ind *ind =
+		(struct mesh_proxy_update_ind *)msg->param;
+	struct rwnx_vif *rwnx_vif = rwnx_hw->vif_table[ind->vif_idx];
+	struct rwnx_mesh_proxy *mesh_proxy;
+	bool found = false;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	if (ind->vif_idx >= (NX_VIRT_DEV_MAX + NX_REMOTE_STA_MAX))
+		return 1;
+
+	if (!rwnx_vif || RWNX_VIF_TYPE(rwnx_vif) != NL80211_IFTYPE_MESH_POINT)
+		return 0;
+
+	/* Look for path with provided external STA address */
+	list_for_each_entry(mesh_proxy, &rwnx_vif->ap.proxy_list, list) {
+		if (!memcmp(&ind->ext_sta_addr, &mesh_proxy->ext_sta_addr, ETH_ALEN)) {
+			found = true;
+			break;
+		}
+	}
+
+	if (ind->delete && found) {
+		/* Delete mesh path */
+		list_del_init(&mesh_proxy->list);
+		kfree(mesh_proxy);
+	} else if (!ind->delete && !found) {
+		/* Allocate a Mesh Path structure */
+		mesh_proxy =
+			kmalloc_obj(*mesh_proxy, GFP_ATOMIC);
+
+		if (mesh_proxy) {
+			INIT_LIST_HEAD(&mesh_proxy->list);
+
+			memcpy(&mesh_proxy->ext_sta_addr, &ind->ext_sta_addr, MAC_ADDR_LEN);
+			mesh_proxy->local = ind->local;
+
+			if (!ind->local)
+				memcpy(&mesh_proxy->proxy_addr, &ind->proxy_mac_addr,
+				       MAC_ADDR_LEN);
+
+			/* Insert the path in the list of path */
+			list_add_tail(&mesh_proxy->list, &rwnx_vif->ap.proxy_list);
+		}
+	}
+
+	return 0;
+}
+#endif /* CONFIG_RWNX_FULLMAC */
+
+/***************************************************************************
+ * Messages from DEBUG task
+ **************************************************************************/
+static inline int rwnx_rx_dbg_error_ind(struct rwnx_hw *rwnx_hw,
+					struct rwnx_cmd *cmd,
+					struct ipc_e2a_msg *msg)
+{
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	return 0;
+}
+
+#ifdef CONFIG_RWNX_FULLMAC
+
+static msg_cb_fct mm_hdlrs[MSG_I(MM_MAX)] = {
+	[MSG_I(MM_CHANNEL_SWITCH_IND)] = rwnx_rx_chan_switch_ind,
+	[MSG_I(MM_CHANNEL_PRE_SWITCH_IND)] = rwnx_rx_chan_pre_switch_ind,
+	[MSG_I(MM_REMAIN_ON_CHANNEL_EXP_IND)] = rwnx_rx_remain_on_channel_exp_ind,
+	[MSG_I(MM_PS_CHANGE_IND)] = rwnx_rx_ps_change_ind,
+	[MSG_I(MM_TRAFFIC_REQ_IND)] = rwnx_rx_traffic_req_ind,
+	[MSG_I(MM_P2P_VIF_PS_CHANGE_IND)] = rwnx_rx_p2p_vif_ps_change_ind,
+	[MSG_I(MM_CSA_COUNTER_IND)] = rwnx_rx_csa_counter_ind,
+	[MSG_I(MM_CSA_FINISH_IND)] = rwnx_rx_csa_finish_ind,
+	[MSG_I(MM_CSA_TRAFFIC_IND)] = rwnx_rx_csa_traffic_ind,
+	[MSG_I(MM_CHANNEL_SURVEY_IND)] = rwnx_rx_channel_survey_ind,
+	[MSG_I(MM_P2P_NOA_UPD_IND)] = rwnx_rx_p2p_noa_upd_ind,
+	[MSG_I(MM_RSSI_STATUS_IND)] = rwnx_rx_rssi_status_ind,
+	[MSG_I(MM_PKTLOSS_IND)] = rwnx_rx_pktloss_notify_ind,
+	[MSG_I(MM_APM_STALOSS_IND)] = rwnx_apm_staloss_ind,
+	[MSG_I(MM_ARP_ANNOUNCEMENT_IND)] = rwnx_arp_announcement_ind,
+};
+
+static msg_cb_fct scan_hdlrs[MSG_I(SCANU_MAX)] = {
+	[MSG_I(SCANU_START_CFM)] = rwnx_rx_scanu_start_cfm,
+	[MSG_I(SCANU_RESULT_IND)] = rwnx_rx_scanu_result_ind,
+};
+
+static msg_cb_fct me_hdlrs[MSG_I(ME_MAX)] = {
+	[MSG_I(ME_TKIP_MIC_FAILURE_IND)] = rwnx_rx_me_tkip_mic_failure_ind,
+	[MSG_I(ME_TX_CREDITS_UPDATE_IND)] = rwnx_rx_me_tx_credits_update_ind,
+};
+
+static msg_cb_fct sm_hdlrs[MSG_I(SM_MAX)] = {
+	[MSG_I(SM_CONNECT_IND)] = rwnx_rx_sm_connect_ind,
+	[MSG_I(SM_DISCONNECT_IND)] = rwnx_rx_sm_disconnect_ind,
+	[MSG_I(SM_EXTERNAL_AUTH_REQUIRED_IND)] =
+		rwnx_rx_sm_external_auth_required_ind,
+};
+
+static msg_cb_fct apm_hdlrs[MSG_I(APM_MAX)] = {};
+
+static msg_cb_fct mesh_hdlrs[MSG_I(MESH_MAX)] = {
+	[MSG_I(MESH_PATH_CREATE_CFM)] = rwnx_rx_mesh_path_create_cfm,
+	[MSG_I(MESH_PEER_UPDATE_IND)] = rwnx_rx_mesh_peer_update_ind,
+	[MSG_I(MESH_PATH_UPDATE_IND)] = rwnx_rx_mesh_path_update_ind,
+	[MSG_I(MESH_PROXY_UPDATE_IND)] = rwnx_rx_mesh_proxy_update_ind,
+};
+
+#endif /* CONFIG_RWNX_FULLMAC */
+
+static msg_cb_fct dbg_hdlrs[MSG_I(DBG_MAX)] = {
+	[MSG_I(DBG_ERROR_IND)] = rwnx_rx_dbg_error_ind,
+};
+
+static msg_cb_fct tdls_hdlrs[MSG_I(TDLS_MAX)] = {
+	[MSG_I(TDLS_CHAN_SWITCH_CFM)] = rwnx_rx_tdls_chan_switch_cfm,
+	[MSG_I(TDLS_CHAN_SWITCH_IND)] = rwnx_rx_tdls_chan_switch_ind,
+	[MSG_I(TDLS_CHAN_SWITCH_BASE_IND)] = rwnx_rx_tdls_chan_switch_base_ind,
+	[MSG_I(TDLS_PEER_PS_IND)] = rwnx_rx_tdls_peer_ps_ind,
+};
+
+static msg_cb_fct *msg_hdlrs[] = {
+	[TASK_MM] = mm_hdlrs,     [TASK_DBG] = dbg_hdlrs,
+#ifdef CONFIG_RWNX_FULLMAC
+	[TASK_TDLS] = tdls_hdlrs, [TASK_SCANU] = scan_hdlrs,
+	[TASK_ME] = me_hdlrs,     [TASK_SM] = sm_hdlrs,
+	[TASK_APM] = apm_hdlrs,   [TASK_MESH] = mesh_hdlrs,
+#endif /* CONFIG_RWNX_FULLMAC */
+};
+
+/*
+ *
+ */
+void rwnx_rx_handle_msg(struct rwnx_hw *rwnx_hw, struct ipc_e2a_msg *msg)
+{
+	// AICWFDBG(LOGDEBUG, "%s msg->id:0x%x \r\n", __func__, msg->id);
+
+	rwnx_hw->cmd_mgr->msgind(rwnx_hw->cmd_mgr, msg,
+				 msg_hdlrs[MSG_T(msg->id)][MSG_I(msg->id)]);
+}
+
+void rwnx_rx_handle_print(struct rwnx_hw *rwnx_hw, u8 *msg, u32 len)
+{
+	(void)len;
+
+	if (!rwnx_hw || !rwnx_hw->fwlog_en) {
+		pr_err("FWLOG-OVFL: %s", msg);
+		return;
+	}
+
+	AICWFDBG(LOGDEBUG, "FWLOG: %s", msg);
+}
diff --git a/drivers/net/wireless/aic/aic8800_fdrv/rwnx_msg_rx.h b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_msg_rx.h
new file mode 100644
index 0000000000000..211f2e616cbf4
--- /dev/null
+++ b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_msg_rx.h
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _RWNX_MSG_RX_H_
+#define _RWNX_MSG_RX_H_
+
+void rwnx_rx_handle_msg(struct rwnx_hw *rwnx_hw, struct ipc_e2a_msg *msg);
+void rwnx_rx_handle_print(struct rwnx_hw *rwnx_hw, u8 *msg, u32 len);
+
+#endif /* _RWNX_MSG_RX_H_ */
diff --git a/drivers/net/wireless/aic/aic8800_fdrv/rwnx_msg_tx.c b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_msg_tx.c
new file mode 100644
index 0000000000000..08dd58442b8a7
--- /dev/null
+++ b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_msg_tx.c
@@ -0,0 +1,3619 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ ******************************************************************************
+ *
+ * Copyright (C) 2020 AIC semiconductor.
+ *
+ * @brief TX function definitions
+ *
+ ******************************************************************************
+ */
+
+#include "rwnx_msg_tx.h"
+#include "reg_access.h"
+#include "rwnx_mod_params.h"
+#ifdef CONFIG_RWNX_BFMER
+#include "rwnx_bfmer.h"
+#endif //(CONFIG_RWNX_BFMER)
+#include "aicwf_txrxif.h"
+#include "rwnx_cmds.h"
+#include "rwnx_compat.h"
+#include "rwnx_main.h"
+#include "rwnx_strs.h"
+#include <linux/pm.h>
+#include "aicwf_sdio.h"
+#include "aicwf_chip_ops.h"
+
+static const struct mac_addr mac_addr_bcst = {{0xFFFF, 0xFFFF, 0xFFFF}};
+
+/* Default MAC Rx filters that can be changed by mac80211
+ * (via the configure_filter() callback)
+ */
+#define RWNX_MAC80211_CHANGEABLE                                               \
+	(NXMAC_ACCEPT_BA_BIT | NXMAC_ACCEPT_BAR_BIT |                              \
+	 NXMAC_ACCEPT_OTHER_DATA_FRAMES_BIT | NXMAC_ACCEPT_PROBE_REQ_BIT |         \
+	 NXMAC_ACCEPT_PS_POLL_BIT)
+
+/* Default MAC Rx filters that cannot be changed by mac80211 */
+#define RWNX_MAC80211_NOT_CHANGEABLE                                           \
+	(NXMAC_ACCEPT_QO_S_NULL_BIT | NXMAC_ACCEPT_Q_DATA_BIT |                    \
+	 NXMAC_ACCEPT_DATA_BIT | NXMAC_ACCEPT_OTHER_MGMT_FRAMES_BIT |              \
+	 NXMAC_ACCEPT_MY_UNICAST_BIT | NXMAC_ACCEPT_BROADCAST_BIT |                \
+	 NXMAC_ACCEPT_BEACON_BIT | NXMAC_ACCEPT_PROBE_RESP_BIT)
+
+/* Default MAC Rx filter */
+#define RWNX_DEFAULT_RX_FILTER                                                 \
+	(RWNX_MAC80211_CHANGEABLE | RWNX_MAC80211_NOT_CHANGEABLE)
+
+const int bw2chnl[] = {
+	[NL80211_CHAN_WIDTH_20_NOHT] = PHY_CHNL_BW_20,
+	[NL80211_CHAN_WIDTH_20] = PHY_CHNL_BW_20,
+	[NL80211_CHAN_WIDTH_40] = PHY_CHNL_BW_40,
+	[NL80211_CHAN_WIDTH_80] = PHY_CHNL_BW_80,
+	[NL80211_CHAN_WIDTH_160] = PHY_CHNL_BW_160,
+	[NL80211_CHAN_WIDTH_80P80] = PHY_CHNL_BW_80P80,
+};
+
+const int chnl2bw[] = {
+	[PHY_CHNL_BW_20] = NL80211_CHAN_WIDTH_20,
+	[PHY_CHNL_BW_40] = NL80211_CHAN_WIDTH_40,
+	[PHY_CHNL_BW_80] = NL80211_CHAN_WIDTH_80,
+	[PHY_CHNL_BW_160] = NL80211_CHAN_WIDTH_160,
+	[PHY_CHNL_BW_80P80] = NL80211_CHAN_WIDTH_80P80,
+};
+
+#define RWNX_CMD_ARRAY_SIZE      20
+#define RWNX_CMD_HIGH_WATER_SIZE (RWNX_CMD_ARRAY_SIZE / 2)
+
+static struct rwnx_cmd cmd_array[RWNX_CMD_ARRAY_SIZE];
+static spinlock_t cmd_array_lock;
+static int cmd_array_index;
+
+/*****************************************************************************/
+/*
+ * Parse the ampdu density to retrieve the value in usec, according to the
+ * values defined in ieee80211.h
+ */
+static inline u8 rwnx_ampdudensity2usec(u8 ampdudensity)
+{
+	switch (ampdudensity) {
+	case IEEE80211_HT_MPDU_DENSITY_NONE:
+		return 0;
+		/* 1 microsecond is our granularity */
+	case IEEE80211_HT_MPDU_DENSITY_0_25:
+	case IEEE80211_HT_MPDU_DENSITY_0_5:
+	case IEEE80211_HT_MPDU_DENSITY_1:
+		return 1;
+	case IEEE80211_HT_MPDU_DENSITY_2:
+		return 2;
+	case IEEE80211_HT_MPDU_DENSITY_4:
+		return 4;
+	case IEEE80211_HT_MPDU_DENSITY_8:
+		return 8;
+	case IEEE80211_HT_MPDU_DENSITY_16:
+		return 16;
+	default:
+		return 0;
+	}
+}
+
+static inline bool use_pairwise_key(struct cfg80211_crypto_settings *crypto)
+{
+	if (crypto->cipher_group == WLAN_CIPHER_SUITE_WEP40 ||
+	    crypto->cipher_group == WLAN_CIPHER_SUITE_WEP104)
+		return false;
+
+	return true;
+}
+
+static inline bool is_non_blocking_msg(int id)
+{
+	return (id == MM_TIM_UPDATE_REQ || id == ME_RC_SET_RATE_REQ ||
+		id == MM_BFMER_ENABLE_REQ || id == ME_TRAFFIC_IND_REQ ||
+		id == TDLS_PEER_TRAFFIC_IND_REQ || id == MESH_PATH_CREATE_REQ ||
+		id == MESH_PROXY_ADD_REQ || id == SM_EXTERNAL_AUTH_REQUIRED_RSP);
+}
+
+static inline u8 get_chan_flags(uint32_t flags)
+{
+	u8 chan_flags = 0;
+#ifdef AIC8800_RADAR_OR_IR_DETECT
+
+	if (flags & IEEE80211_CHAN_NO_IR)
+		chan_flags |= CHAN_NO_IR;
+
+	if (flags & IEEE80211_CHAN_RADAR)
+		chan_flags |= CHAN_RADAR;
+#endif
+	return chan_flags;
+}
+
+static inline s8 chan_to_fw_pwr(int power)
+{
+	return power > 127 ? 127 : (s8)power;
+}
+
+static inline void limit_chan_bw(u8 *bw, u16 primary, u16 *center1)
+{
+	int oft, new_oft = 10;
+
+	if (*bw <= PHY_CHNL_BW_40)
+		return;
+
+	oft = *center1 - primary;
+	*bw = PHY_CHNL_BW_40;
+
+	if (oft < 0)
+		new_oft = new_oft * -1;
+	if (abs(oft) == 10 || abs(oft) == 50)
+		new_oft = new_oft * -1;
+
+	*center1 = primary + new_oft;
+}
+
+struct rwnx_cmd *rwnx_cmd_malloc(void)
+{
+	struct rwnx_cmd *cmd = NULL;
+	unsigned long flags = 0;
+
+	spin_lock_irqsave(&cmd_array_lock, flags);
+
+	for (cmd_array_index = 0; cmd_array_index < RWNX_CMD_ARRAY_SIZE;
+		 cmd_array_index++) {
+		if (cmd_array[cmd_array_index].used == 0) {
+			AICWFDBG(LOGTRACE, "%s get cmd_array[%d]:%p \r\n", __func__,
+				 cmd_array_index, &cmd_array[cmd_array_index]);
+			cmd = &cmd_array[cmd_array_index];
+			cmd_array[cmd_array_index].used = 1;
+			break;
+		}
+	}
+
+	if (cmd_array_index >= RWNX_CMD_HIGH_WATER_SIZE) {
+		AICWFDBG(LOGERROR, "%s cmd(%d) was pending...\r\n", __func__,
+			 cmd_array_index);
+		mdelay(100);
+	}
+
+	if (!cmd)
+		AICWFDBG(LOGERROR, "%s array is empty...\r\n", __func__);
+
+	spin_unlock_irqrestore(&cmd_array_lock, flags);
+
+	return cmd;
+}
+
+void rwnx_cmd_free(struct rwnx_cmd *cmd)
+{
+	unsigned long flags = 0;
+
+	spin_lock_irqsave(&cmd_array_lock, flags);
+	cmd->used = 0;
+	AICWFDBG(LOGTRACE, "%s cmd_array[%d]:%p \r\n", __func__, cmd->array_id,
+		 cmd);
+	spin_unlock_irqrestore(&cmd_array_lock, flags);
+}
+
+int rwnx_init_cmd_array(void)
+{
+	AICWFDBG(LOGTRACE, "%s Enter \r\n", __func__);
+	spin_lock_init(&cmd_array_lock);
+
+	for (cmd_array_index = 0; cmd_array_index < RWNX_CMD_ARRAY_SIZE;
+	     cmd_array_index++) {
+		AICWFDBG(LOGTRACE, "%s cmd_queue[%d]:%p \r\n", __func__,
+			 cmd_array_index, &cmd_array[cmd_array_index]);
+		cmd_array[cmd_array_index].used = 0;
+		cmd_array[cmd_array_index].array_id = cmd_array_index;
+	}
+	AICWFDBG(LOGTRACE, "%s Exit \r\n", __func__);
+
+	return 0;
+}
+
+void rwnx_free_cmd_array(void)
+{
+	AICWFDBG(LOGTRACE, "%s Enter \r\n", __func__);
+
+	for (cmd_array_index = 0; cmd_array_index < RWNX_CMD_ARRAY_SIZE;
+	     cmd_array_index++)
+		cmd_array[cmd_array_index].used = 0;
+
+	AICWFDBG(LOGTRACE, "%s Exit \r\n", __func__);
+}
+
+/*
+ ******************************************************************************
+ * @brief Allocate memory for a message
+ *
+ * This primitive allocates memory for a message that has to be sent. The memory
+ * is allocated dynamically on the heap and the length of the variable parameter
+ * structure has to be provided in order to allocate the correct size.
+ *
+ * Several additional parameters are provided which will be preset in the
+ *message and which may be used internally to choose the kind of memory to
+ *allocate.
+ *
+ * The memory allocated will be automatically freed by the kernel, after the
+ * pointer has been sent to ke_msg_send(). If the message is not sent, it must
+ * be freed explicitly with ke_msg_free().
+ *
+ * Allocation failure is considered critical and should not happen.
+ *
+ * @param[in] id        Message identifier
+ * @param[in] dest_id   Destination Task Identifier
+ * @param[in] src_id    Source Task Identifier
+ * @param[in] param_len Size of the message parameters to be allocated
+ *
+ * @return Pointer to the parameter member of the ke_msg. If the parameter
+ *         structure is empty, the pointer will point to the end of the message
+ *         and should not be used (except to retrieve the message pointer or to
+ *         send the message)
+ ******************************************************************************
+ */
+static inline void *rwnx_msg_zalloc(lmac_msg_id_t const id,
+				    lmac_task_id_t const dest_id,
+				    lmac_task_id_t const src_id,
+				    uint16_t const param_len)
+{
+	struct lmac_msg *msg;
+	gfp_t flags;
+
+	//  if (is_non_blocking_msg(id) && (in_softirq()||in_atomic()))
+	flags = GFP_ATOMIC;
+	//  else
+	//      flags = GFP_KERNEL;
+
+	// msg = (struct lmac_msg *)kzalloc(sizeof(struct lmac_msg) + param_len, flags);
+	msg = kzalloc(sizeof(*msg) + param_len, flags);
+	if (!msg) {
+		AICWFDBG(LOGERROR, "%s: msg allocation failed\n", __func__);
+		return NULL;
+	}
+	msg->id = id;
+	msg->dest_id = dest_id;
+	msg->src_id = src_id;
+	msg->param_len = param_len;
+	// AICWFDBG(LOGDEBUG, "rwnx_msg_zalloc size=%d  id=%d\n",msg->param_len,msg->id);
+
+	return msg->param;
+}
+
+static void rwnx_msg_free(struct rwnx_hw *rwnx_hw, const void *msg_params)
+{
+	struct lmac_msg *msg = container_of((void *)msg_params, struct lmac_msg, param);
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+	/* Free the message */
+	kfree(msg);
+}
+
+// void rwnx_pm_relax(struct aic_sdio_dev *sdiodev);
+// void rwnx_pm_stay_awake(struct aic_sdio_dev *sdiodev);
+
+static int rwnx_send_msg(struct rwnx_hw *rwnx_hw, const void *msg_params,
+			 int reqcfm, lmac_msg_id_t reqid, void *cfm)
+{
+	struct lmac_msg *msg;
+	struct rwnx_cmd *cmd;
+	bool nonblock;
+	int ret = 0;
+	u8 empty = 0;
+
+	// RWNX_DBG(RWNX_FN_ENTRY_STR);
+	AICWFDBG(LOGTRACE,
+		 "%s (%d)%s reqcfm:%d in_hardirq:%d in_softirq:%d irqs_disabled:%d\r\n",
+		 __func__, reqid, RWNX_ID2STR(reqid), reqcfm, (int)in_hardirq(),
+		 (int)in_softirq(), (int)irqs_disabled());
+
+#ifdef AICWF_SDIO_SUPPORT
+	if (!aicwf_sdio_activity_get(rwnx_hw, rwnx_hw->ws_tx)) {
+		rwnx_msg_free(rwnx_hw, msg_params);
+		return -EBUSY;
+	}
+	if (rwnx_hw->sdiodev->bus_if->state == BUS_DOWN_ST) {
+		rwnx_msg_free(rwnx_hw, msg_params);
+		sdio_err("bus is down\n");
+		aicwf_sdio_activity_put(rwnx_hw, rwnx_hw->ws_tx);
+		return 0;
+	}
+#endif
+
+	msg = container_of((void *)msg_params, struct lmac_msg, param);
+
+	// nonblock = is_non_blocking_msg(msg->id);
+	nonblock = 0;
+	cmd = rwnx_cmd_malloc(); // kzalloc_obj(struct rwnx_cmd, nonblock ?
+							 // GFP_ATOMIC : GFP_KERNEL);
+	cmd->result = -EINTR;
+	cmd->id = msg->id;
+	cmd->reqid = reqid;
+	cmd->a2e_msg = msg;
+	cmd->e2a_msg = cfm;
+	if (nonblock)
+		cmd->flags = RWNX_CMD_FLAG_NONBLOCK;
+	if (reqcfm)
+		cmd->flags |= RWNX_CMD_FLAG_REQ_CFM;
+
+	if (cfm) {
+		do {
+			if (rwnx_hw->cmd_mgr->state == RWNX_CMD_MGR_STATE_CRASHED)
+				break;
+			spin_lock_bh(&rwnx_hw->cmd_mgr->lock);
+			empty = list_empty(&rwnx_hw->cmd_mgr->cmds);
+			spin_unlock_bh(&rwnx_hw->cmd_mgr->lock);
+			if (!empty) {
+				if (in_softirq()) {
+					AICWFDBG(LOGDEBUG,
+						 "AICWF_LOG in_softirq:check cmdqueue empty\n");
+					mdelay(10);
+				} else {
+					AICWFDBG(LOGDEBUG, "AICWF_LOG check cmdqueue empty\n");
+					msleep(50);
+				}
+			}
+		} while (!empty); // wait for cmd queue empty
+	}
+
+	if (reqcfm) {
+		cmd->flags &= ~RWNX_CMD_FLAG_WAIT_ACK; // we don't need ack any more
+		ret = rwnx_hw->cmd_mgr->queue(rwnx_hw->cmd_mgr, cmd);
+	} else {
+#ifdef AICWF_SDIO_SUPPORT
+		aicwf_set_cmd_tx((void *)(rwnx_hw->sdiodev), cmd->a2e_msg,
+				 sizeof(struct lmac_msg) + cmd->a2e_msg->param_len);
+#else
+		aicwf_set_cmd_tx((void *)(rwnx_hw->usbdev), cmd->a2e_msg,
+				 sizeof(struct lmac_msg) + cmd->a2e_msg->param_len);
+#endif
+	}
+
+	if (!reqcfm || ret)
+		rwnx_cmd_free(cmd); // kfree(cmd);
+
+	aicwf_sdio_activity_put(rwnx_hw, rwnx_hw->ws_tx);
+	return 0;
+}
+
+static int rwnx_send_msg1(struct rwnx_hw *rwnx_hw, const void *msg_params,
+			  int reqcfm, lmac_msg_id_t reqid, void *cfm,
+			  bool defer)
+{
+	struct lmac_msg *msg;
+	struct rwnx_cmd *cmd;
+	bool nonblock;
+	int ret = 0;
+
+	//  RWNX_DBG(RWNX_FN_ENTRY_STR);
+	AICWFDBG(LOGDEBUG,
+		 "AICWF_LOG %s (%d)%s reqcfm:%d in_hardirq:%d in_softirq:%d irqs_disabled:%d\r\n",
+		 __func__, reqid, RWNX_ID2STR(reqid), reqcfm, (int)in_hardirq(),
+		 (int)in_softirq(), (int)irqs_disabled());
+
+	if (!aicwf_sdio_activity_get(rwnx_hw, rwnx_hw->ws_tx)) {
+		rwnx_msg_free(rwnx_hw, msg_params);
+		return -EBUSY;
+	}
+	msg = container_of((void *)msg_params, struct lmac_msg, param);
+
+	// nonblock = is_non_blocking_msg(msg->id);
+	nonblock = 0;
+	cmd = rwnx_cmd_malloc(); // kzalloc_obj(struct rwnx_cmd, nonblock ?
+							 // GFP_ATOMIC : GFP_KERNEL);
+	cmd->result = -EINTR;
+	cmd->id = msg->id;
+	cmd->reqid = reqid;
+	cmd->a2e_msg = msg;
+	cmd->e2a_msg = cfm;
+	if (nonblock)
+		cmd->flags = RWNX_CMD_FLAG_NONBLOCK;
+	if (reqcfm)
+		cmd->flags |= RWNX_CMD_FLAG_REQ_CFM;
+
+	if (reqcfm) {
+		cmd->flags &= ~RWNX_CMD_FLAG_WAIT_ACK; // we don't need ack any more
+		if (!defer)
+			ret = rwnx_hw->cmd_mgr->queue(rwnx_hw->cmd_mgr, cmd);
+		else
+			ret = cmd_mgr_queue_force_defer(rwnx_hw->cmd_mgr, cmd);
+	}
+
+	if (!reqcfm || ret)
+		rwnx_cmd_free(cmd); // kfree(cmd);
+
+	if (!ret)
+		ret = cmd->result;
+
+	aicwf_sdio_activity_put(rwnx_hw, rwnx_hw->ws_tx);
+	// return ret;
+	return 0;
+}
+
+/******************************************************************************
+ *    Control messages handling functions (FULLMAC)
+ *****************************************************************************/
+int rwnx_send_reset(struct rwnx_hw *rwnx_hw)
+{
+	void *void_param;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	/* RESET REQ has no parameter */
+	void_param = rwnx_msg_zalloc(MM_RESET_REQ, TASK_MM, DRV_TASK_ID, 0);
+	if (!void_param)
+		return -ENOMEM;
+
+	return rwnx_send_msg(rwnx_hw, void_param, 1, MM_RESET_CFM, NULL);
+}
+
+int rwnx_send_start(struct rwnx_hw *rwnx_hw)
+{
+	struct mm_start_req *start_req_param;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	/* Build the START REQ message */
+	start_req_param = rwnx_msg_zalloc(MM_START_REQ, TASK_MM, DRV_TASK_ID,
+					  sizeof(struct mm_start_req));
+	if (!start_req_param)
+		return -ENOMEM;
+
+	/* Set parameters for the START message */
+	memcpy(&start_req_param->phy_cfg, &rwnx_hw->phy.cfg,
+	       sizeof(rwnx_hw->phy.cfg));
+	start_req_param->uapsd_timeout = (u32)rwnx_hw->mod_params->uapsd_timeout;
+	start_req_param->lp_clk_accuracy = (u16)rwnx_hw->mod_params->lp_clk_ppm;
+
+	/* Send the START REQ message to LMAC FW */
+	return rwnx_send_msg(rwnx_hw, start_req_param, 1, MM_START_CFM, NULL);
+}
+
+int rwnx_send_version_req(struct rwnx_hw *rwnx_hw, struct mm_version_cfm *cfm)
+{
+	void *void_param;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	/* VERSION REQ has no parameter */
+	void_param = rwnx_msg_zalloc(MM_VERSION_REQ, TASK_MM, DRV_TASK_ID, 0);
+	if (!void_param)
+		return -ENOMEM;
+
+	return rwnx_send_msg(rwnx_hw, void_param, 1, MM_VERSION_CFM, cfm);
+}
+
+int rwnx_send_add_if(struct rwnx_hw *rwnx_hw, const unsigned char *mac,
+		     enum nl80211_iftype iftype, bool p2p,
+		     struct mm_add_if_cfm *cfm)
+{
+	struct mm_add_if_req *add_if_req_param;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	/* Build the ADD_IF_REQ message */
+	add_if_req_param = rwnx_msg_zalloc(MM_ADD_IF_REQ, TASK_MM, DRV_TASK_ID,
+					   sizeof(struct mm_add_if_req));
+	if (!add_if_req_param)
+		return -ENOMEM;
+
+	/* Set parameters for the ADD_IF_REQ message */
+	memcpy(&add_if_req_param->addr.array[0], mac, ETH_ALEN);
+	switch (iftype) {
+#if !IS_ENABLED(CONFIG_AIC8800_STA_ONLY)
+#ifdef CONFIG_RWNX_FULLMAC
+		// case NL80211_IFTYPE_P2P_DEVICE:
+	case NL80211_IFTYPE_P2P_CLIENT:
+		add_if_req_param->p2p = true;
+		fallthrough;
+		// no break
+#endif /* CONFIG_RWNX_FULLMAC */
+#endif
+	case NL80211_IFTYPE_STATION:
+		add_if_req_param->type = MM_STA;
+		break;
+
+#if !IS_ENABLED(CONFIG_AIC8800_STA_ONLY)
+	case NL80211_IFTYPE_ADHOC:
+		add_if_req_param->type = MM_IBSS;
+		break;
+
+#ifdef CONFIG_RWNX_FULLMAC
+	case NL80211_IFTYPE_P2P_GO:
+		add_if_req_param->p2p = true;
+		fallthrough;
+		// no break
+#endif /* CONFIG_RWNX_FULLMAC */
+	case NL80211_IFTYPE_AP:
+		add_if_req_param->type = MM_AP;
+		break;
+	case NL80211_IFTYPE_MESH_POINT:
+		add_if_req_param->type = MM_MESH_POINT;
+		break;
+	case NL80211_IFTYPE_AP_VLAN:
+		return -1;
+	case NL80211_IFTYPE_MONITOR:
+		add_if_req_param->type = MM_MONITOR;
+		break;
+#endif
+	default:
+		rwnx_msg_free(rwnx_hw, add_if_req_param);
+		return -EOPNOTSUPP;
+	}
+
+	/* Send the ADD_IF_REQ message to LMAC FW */
+	return rwnx_send_msg(rwnx_hw, add_if_req_param, 1, MM_ADD_IF_CFM, cfm);
+}
+
+int rwnx_send_remove_if(struct rwnx_hw *rwnx_hw, u8 vif_index, bool defer)
+{
+	struct mm_remove_if_req *remove_if_req;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	/* Build the MM_REMOVE_IF_REQ message */
+	remove_if_req = rwnx_msg_zalloc(MM_REMOVE_IF_REQ, TASK_MM, DRV_TASK_ID,
+					sizeof(struct mm_remove_if_req));
+	if (!remove_if_req)
+		return -ENOMEM;
+
+	/* Set parameters for the MM_REMOVE_IF_REQ message */
+	remove_if_req->inst_nbr = vif_index;
+
+	/* Send the MM_REMOVE_IF_REQ message to LMAC FW */
+	return rwnx_send_msg1(rwnx_hw, remove_if_req, 1, MM_REMOVE_IF_CFM, NULL,
+						  defer);
+}
+
+int rwnx_send_set_channel(struct rwnx_hw *rwnx_hw, int phy_idx,
+			  struct mm_set_channel_cfm *cfm)
+{
+	struct mm_set_channel_req *req;
+	enum nl80211_chan_width width = NL80211_CHAN_WIDTH_20_NOHT;
+	u16 center_freq = 2412, center_freq1 = 2412, center_freq2 = 2412;
+	s8 tx_power = 0;
+	u8 flags = 0;
+	enum nl80211_band band = NL80211_BAND_2GHZ;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	if (phy_idx >= rwnx_hw->phy.cnt)
+		return -EOPNOTSUPP;
+
+	req = rwnx_msg_zalloc(MM_SET_CHANNEL_REQ, TASK_MM, DRV_TASK_ID,
+			      sizeof(struct mm_set_channel_req));
+	if (!req)
+		return -ENOMEM;
+
+	if (phy_idx == 0) {
+#ifdef CONFIG_RWNX_FULLMAC
+		/* On FULLMAC only setting channel of secondary chain */
+		wiphy_err(rwnx_hw->wiphy, "Trying to set channel of primary chain");
+		return 0;
+#endif /* CONFIG_RWNX_FULLMAC */
+	} else {
+		struct rwnx_sec_phy_chan *chan = &rwnx_hw->phy.sec_chan;
+
+		width = chnl2bw[chan->type];
+		band = chan->band;
+		center_freq = chan->prim20_freq;
+		center_freq1 = chan->center_freq1;
+		center_freq2 = chan->center_freq2;
+		flags = 0;
+	}
+
+	req->chan.band = band;
+	req->chan.type = bw2chnl[width];
+	req->chan.prim20_freq = center_freq;
+	req->chan.center1_freq = center_freq1;
+	req->chan.center2_freq = center_freq2;
+	req->chan.tx_power = tx_power;
+	req->chan.flags = flags;
+	req->index = phy_idx;
+
+	if (rwnx_hw->phy.limit_bw)
+		limit_chan_bw(&req->chan.type, req->chan.prim20_freq,
+			      &req->chan.center1_freq);
+
+	RWNX_DBG("mac80211:   freq=%d(c1:%d - c2:%d)/width=%d - band=%d\n"
+		 "   hw(%d): prim20=%d(c1:%d - c2:%d)/ type=%d - band=%d\n",
+		 center_freq, center_freq1, center_freq2, width, band, phy_idx,
+		 req->chan.prim20_freq, req->chan.center1_freq,
+		 req->chan.center2_freq, req->chan.type, req->chan.band);
+
+	/* Send the MM_SET_CHANNEL_REQ REQ message to LMAC FW */
+	return rwnx_send_msg(rwnx_hw, req, 1, MM_SET_CHANNEL_CFM, cfm);
+}
+
+int rwnx_send_key_add(struct rwnx_hw *rwnx_hw, u8 vif_idx, u8 sta_idx,
+		      bool pairwise, u8 *key, u8 key_len, u8 key_idx,
+		      u8 cipher_suite, struct mm_key_add_cfm *cfm)
+{
+	struct mm_key_add_req *key_add_req;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	/* Build the MM_KEY_ADD_REQ message */
+	key_add_req = rwnx_msg_zalloc(MM_KEY_ADD_REQ, TASK_MM, DRV_TASK_ID,
+				      sizeof(struct mm_key_add_req));
+	if (!key_add_req)
+		return -ENOMEM;
+
+	/* Set parameters for the MM_KEY_ADD_REQ message */
+	if (sta_idx != 0xFF) {
+		/* Pairwise key */
+		key_add_req->sta_idx = sta_idx;
+	} else {
+		/* Default key */
+		key_add_req->sta_idx = sta_idx;
+		key_add_req->key_idx = (u8)key_idx; /* only useful for default keys */
+	}
+	key_add_req->pairwise = pairwise;
+	key_add_req->inst_nbr = vif_idx;
+	key_add_req->key.length = key_len;
+	memcpy(&key_add_req->key.array[0], key, key_len);
+
+	key_add_req->cipher_suite = cipher_suite;
+
+	RWNX_DBG("%s: sta_idx:%d key_idx:%d inst_nbr:%d cipher:%d key_len:%d\n",
+		 __func__, key_add_req->sta_idx, key_add_req->key_idx,
+		 key_add_req->inst_nbr, key_add_req->cipher_suite,
+		 key_add_req->key.length);
+#if defined(CONFIG_RWNX_DBG) || defined(CONFIG_DYNAMIC_DEBUG)
+	print_hex_dump_bytes("key: ", DUMP_PREFIX_OFFSET, key_add_req->key.array,
+			     key_add_req->key.length);
+#endif
+
+	/* Send the MM_KEY_ADD_REQ message to LMAC FW */
+	return rwnx_send_msg(rwnx_hw, key_add_req, 1, MM_KEY_ADD_CFM, cfm);
+}
+
+int rwnx_send_key_del(struct rwnx_hw *rwnx_hw, uint8_t hw_key_idx)
+{
+	struct mm_key_del_req *key_del_req;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	/* Build the MM_KEY_DEL_REQ message */
+	key_del_req = rwnx_msg_zalloc(MM_KEY_DEL_REQ, TASK_MM, DRV_TASK_ID,
+				      sizeof(struct mm_key_del_req));
+	if (!key_del_req)
+		return -ENOMEM;
+
+	/* Set parameters for the MM_KEY_DEL_REQ message */
+	key_del_req->hw_key_idx = hw_key_idx;
+
+	/* Send the MM_KEY_DEL_REQ message to LMAC FW */
+	return rwnx_send_msg(rwnx_hw, key_del_req, 1, MM_KEY_DEL_CFM, NULL);
+}
+
+int rwnx_send_bcn(struct rwnx_hw *rwnx_hw, u8 *buf, u8 vif_idx, u16 bcn_len)
+{
+	struct apm_set_bcn_ie_req *bcn_ie_req;
+
+	bcn_ie_req = rwnx_msg_zalloc(APM_SET_BEACON_IE_REQ, TASK_APM, DRV_TASK_ID,
+				     sizeof(struct apm_set_bcn_ie_req));
+	if (!bcn_ie_req)
+		return -ENOMEM;
+
+	bcn_ie_req->vif_idx = vif_idx;
+	bcn_ie_req->bcn_ie_len = bcn_len;
+	memcpy(bcn_ie_req->bcn_ie, (u8 *)buf, bcn_len);
+	kfree(buf);
+
+	return rwnx_send_msg(rwnx_hw, bcn_ie_req, 1, APM_SET_BEACON_IE_CFM, NULL);
+}
+
+int rwnx_send_bcn_change(struct rwnx_hw *rwnx_hw, u8 vif_idx, u32 bcn_addr,
+			 u16 bcn_len, u16 tim_oft, u16 tim_len, u16 *csa_oft)
+{
+	struct mm_bcn_change_req *req;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	/* Build the MM_BCN_CHANGE_REQ message */
+	req = rwnx_msg_zalloc(MM_BCN_CHANGE_REQ, TASK_MM, DRV_TASK_ID,
+			      sizeof(struct mm_bcn_change_req));
+	if (!req)
+		return -ENOMEM;
+
+	/* Set parameters for the MM_BCN_CHANGE_REQ message */
+	req->bcn_ptr = bcn_addr;
+	req->bcn_len = bcn_len;
+	req->tim_oft = tim_oft;
+	req->tim_len = tim_len;
+	req->inst_nbr = vif_idx;
+
+	if (csa_oft) {
+		int i;
+
+		for (i = 0; i < BCN_MAX_CSA_CPT; i++)
+			req->csa_oft[i] = csa_oft[i];
+	}
+
+	/* Send the MM_BCN_CHANGE_REQ message to LMAC FW */
+	return rwnx_send_msg(rwnx_hw, req, 1, MM_BCN_CHANGE_CFM, NULL);
+}
+
+int rwnx_send_roc(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif,
+		  struct ieee80211_channel *chan, unsigned int duration,
+		  struct mm_remain_on_channel_cfm *roc_cfm)
+{
+	struct mm_remain_on_channel_req *req;
+	struct cfg80211_chan_def chandef;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	/* Create channel definition structure */
+	cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_NO_HT);
+
+	/* Build the MM_REMAIN_ON_CHANNEL_REQ message */
+	req = rwnx_msg_zalloc(MM_REMAIN_ON_CHANNEL_REQ, TASK_MM, DRV_TASK_ID,
+			      sizeof(struct mm_remain_on_channel_req));
+	if (!req)
+		return -ENOMEM;
+
+	/* Set parameters for the MM_REMAIN_ON_CHANNEL_REQ message */
+	req->op_code = MM_ROC_OP_START;
+	req->vif_index = vif->vif_index;
+	req->duration_ms = duration;
+	req->band = chan->band;
+	req->type = bw2chnl[chandef.width];
+	req->prim20_freq = chan->center_freq;
+	req->center1_freq = chandef.center_freq1;
+	req->center2_freq = chandef.center_freq2;
+	req->tx_power = chan_to_fw_pwr(chan->max_power);
+
+	/* Send the MM_REMAIN_ON_CHANNEL_REQ message to LMAC FW */
+	return rwnx_send_msg(rwnx_hw, req, 1, MM_REMAIN_ON_CHANNEL_CFM, roc_cfm);
+}
+
+int rwnx_send_cancel_roc(struct rwnx_hw *rwnx_hw)
+{
+	struct mm_remain_on_channel_req *req;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	/* Build the MM_REMAIN_ON_CHANNEL_REQ message */
+	req = rwnx_msg_zalloc(MM_REMAIN_ON_CHANNEL_REQ, TASK_MM, DRV_TASK_ID,
+			      sizeof(struct mm_remain_on_channel_req));
+	if (!req)
+		return -ENOMEM;
+
+	/* Set parameters for the MM_REMAIN_ON_CHANNEL_REQ message */
+	req->op_code = MM_ROC_OP_CANCEL;
+
+	/* Send the MM_REMAIN_ON_CHANNEL_REQ message to LMAC FW */
+	return rwnx_send_msg(rwnx_hw, req, 1, MM_REMAIN_ON_CHANNEL_CFM, NULL);
+}
+
+int rwnx_send_set_power(struct rwnx_hw *rwnx_hw, u8 vif_idx, s8 pwr,
+			struct mm_set_power_cfm *cfm)
+{
+	struct mm_set_power_req *req;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	/* Build the MM_SET_POWER_REQ message */
+	req = rwnx_msg_zalloc(MM_SET_POWER_REQ, TASK_MM, DRV_TASK_ID,
+			      sizeof(struct mm_set_power_req));
+	if (!req)
+		return -ENOMEM;
+
+	/* Set parameters for the MM_SET_POWER_REQ message */
+	req->inst_nbr = vif_idx;
+	req->power = pwr;
+
+	/* Send the MM_SET_POWER_REQ message to LMAC FW */
+	return rwnx_send_msg(rwnx_hw, req, 1, MM_SET_POWER_CFM, cfm);
+}
+
+int rwnx_send_set_edca(struct rwnx_hw *rwnx_hw, u8 hw_queue, u32 param,
+		       bool uapsd, u8 inst_nbr)
+{
+	struct mm_set_edca_req *set_edca_req;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	/* Build the MM_SET_EDCA_REQ message */
+	set_edca_req = rwnx_msg_zalloc(MM_SET_EDCA_REQ, TASK_MM, DRV_TASK_ID,
+				       sizeof(struct mm_set_edca_req));
+	if (!set_edca_req)
+		return -ENOMEM;
+
+	/* Set parameters for the MM_SET_EDCA_REQ message */
+	set_edca_req->ac_param = param;
+	set_edca_req->uapsd = uapsd;
+	set_edca_req->hw_queue = hw_queue;
+	set_edca_req->inst_nbr = inst_nbr;
+
+	/* Send the MM_SET_EDCA_REQ message to LMAC FW */
+	return rwnx_send_msg(rwnx_hw, set_edca_req, 1, MM_SET_EDCA_CFM, NULL);
+}
+
+#ifdef AICWF_ARP_OFFLOAD
+int rwnx_send_arpoffload_en_req(struct rwnx_hw *rwnx_hw,
+				struct rwnx_vif *rwnx_vif, u32 ipaddr,
+				u8 enable)
+{
+	struct mm_set_arpoffload_en_req *arp_offload_req;
+	int error;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	/* Build the MM_SET_P2P_NOA_REQ message */
+	arp_offload_req =
+		rwnx_msg_zalloc(MM_SET_ARPOFFLOAD_REQ, TASK_MM, DRV_TASK_ID,
+				sizeof(struct mm_set_arpoffload_en_req));
+
+	if (!arp_offload_req)
+		return -ENOMEM;
+
+	/* Fill the message parameters */
+	arp_offload_req->enable = enable;
+	arp_offload_req->vif_idx = rwnx_vif->vif_index;
+	arp_offload_req->ipaddr = ipaddr;
+
+	/* Send the MM_ARPOFFLOAD_EN_REQ message to UMAC FW */
+	error =
+		rwnx_send_msg(rwnx_hw, arp_offload_req, 1, MM_SET_ARPOFFLOAD_CFM, NULL);
+
+	return error;
+}
+#endif
+
+int rwnx_send_disable_agg_req(struct rwnx_hw *rwnx_hw, u8 agg_disable,
+			      u8 agg_disable_rx, u8 sta_idx)
+{
+	struct mm_set_agg_disable_req *req;
+	int error;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	/* Build the MM_SET_AGG_DISABLE_REQ message */
+	req = rwnx_msg_zalloc(MM_SET_AGG_DISABLE_REQ, TASK_MM, DRV_TASK_ID,
+			      sizeof(struct mm_set_agg_disable_req));
+
+	if (!req)
+		return -ENOMEM;
+
+	req->disable = agg_disable;
+	req->staidx = sta_idx;
+	req->disable_rx = agg_disable_rx;
+
+	/* Send the MM_SET_AGG_DISABLE_REQ message to UMAC FW */
+	error = rwnx_send_msg(rwnx_hw, req, 1, MM_SET_AGG_DISABLE_CFM, NULL);
+
+	return error;
+};
+
+int rwnx_send_coex_req(struct rwnx_hw *rwnx_hw, u8 disable_coexnull,
+		       u8 enable_nullcts)
+{
+	struct mm_set_coex_req *coex_req;
+	int error;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	/* Build the MM_SET_COEX_REQ message */
+	coex_req = rwnx_msg_zalloc(MM_SET_COEX_REQ, TASK_MM, DRV_TASK_ID,
+				   sizeof(struct mm_set_coex_req));
+
+	if (!coex_req)
+		return -ENOMEM;
+
+	coex_req->bt_on = 1;
+	coex_req->disable_coexnull = disable_coexnull;
+	coex_req->enable_nullcts = enable_nullcts;
+	coex_req->enable_periodic_timer = 0;
+	coex_req->coex_timeslot_set = 0;
+
+	/* Send the MM_SET_COEX_REQ message to UMAC FW */
+	error = rwnx_send_msg(rwnx_hw, coex_req, 1, MM_SET_COEX_CFM, NULL);
+
+	return error;
+};
+
+int rwnx_send_rf_config_req(struct rwnx_hw *rwnx_hw, u8 ofst, u8 sel,
+			    u8 *tbl, u16 len)
+{
+	struct mm_set_rf_config_req *rf_config_req;
+	int error;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	/* Build the MM_SET_RF_CONFIG_REQ message */
+	rf_config_req = rwnx_msg_zalloc(MM_SET_RF_CONFIG_REQ, TASK_MM, DRV_TASK_ID,
+					sizeof(struct mm_set_rf_config_req));
+
+	if (!rf_config_req)
+		return -ENOMEM;
+
+	rf_config_req->table_sel = sel;
+	rf_config_req->table_ofst = ofst;
+	rf_config_req->table_num = 16;
+	rf_config_req->deft_page = 0;
+
+	memcpy(rf_config_req->data, tbl, len);
+
+	/* Send the MM_SET_RF_CONFIG_REQ message to UMAC FW */
+	error =
+		rwnx_send_msg(rwnx_hw, rf_config_req, 1, MM_SET_RF_CONFIG_CFM, NULL);
+
+	return error;
+}
+
+int rwnx_send_rf_calib_req(struct rwnx_hw *rwnx_hw,
+			   struct mm_set_rf_calib_cfm *cfm)
+{
+	struct mm_set_rf_calib_req *rf_calib_req;
+	struct xtal_cap_conf xtal_cap = {
+		0,
+	};
+	int error;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	/* Build the MM_SET_P2P_NOA_REQ message */
+	rf_calib_req = rwnx_msg_zalloc(MM_SET_RF_CALIB_REQ, TASK_MM, DRV_TASK_ID,
+				       sizeof(struct mm_set_rf_calib_req));
+
+	if (!rf_calib_req)
+		return -ENOMEM;
+
+	aic_chip_set_rf_calib_cfg(rwnx_hw, rf_calib_req);
+
+	rf_calib_req->param_alpha = 0x0c34c008;
+	rf_calib_req->bt_calib_en = 0;
+	rf_calib_req->bt_calib_param = 0x264203;
+
+	get_userconfig_xtal_cap(&xtal_cap);
+
+	if (xtal_cap.enable) {
+		AICWFDBG(LOGDEBUG, "user xtal cap: %d, cap_fine: %d\n", xtal_cap.xtal_cap,
+			 xtal_cap.xtal_cap_fine);
+		rf_calib_req->xtal_cap = xtal_cap.xtal_cap;
+		rf_calib_req->xtal_cap_fine = xtal_cap.xtal_cap_fine;
+	} else {
+		rf_calib_req->xtal_cap = 0;
+		rf_calib_req->xtal_cap_fine = 0;
+	}
+
+	/* Send the MM_SET_RF_CALIB_REQ message to UMAC FW */
+	error = rwnx_send_msg(rwnx_hw, rf_calib_req, 1, MM_SET_RF_CALIB_CFM, cfm);
+
+	return error;
+};
+
+int rwnx_send_get_macaddr_req(struct rwnx_hw *rwnx_hw,
+			      struct mm_get_mac_addr_cfm *cfm)
+{
+	struct mm_get_mac_addr_req *get_macaddr_req;
+	int error;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	/* Build the MM_GET_MAC_ADDR_REQ message */
+	get_macaddr_req = rwnx_msg_zalloc(MM_GET_MAC_ADDR_REQ, TASK_MM, DRV_TASK_ID,
+					  sizeof(struct mm_get_mac_addr_req));
+
+	if (!get_macaddr_req)
+		return -ENOMEM;
+
+	get_macaddr_req->get = 1;
+
+	/* Send the MM_GET_MAC_ADDR_REQ  message to UMAC FW */
+	error =
+		rwnx_send_msg(rwnx_hw, get_macaddr_req, 1, MM_GET_MAC_ADDR_CFM, cfm);
+
+	return error;
+};
+
+int rwnx_send_get_sta_info_req(struct rwnx_hw *rwnx_hw, u8 sta_idx,
+			       struct mm_get_sta_info_cfm *cfm)
+{
+	struct mm_get_sta_info_req *get_info_req;
+	int error;
+
+	/* Build the MM_GET_STA_INFO_REQ message */
+	get_info_req = rwnx_msg_zalloc(MM_GET_STA_INFO_REQ, TASK_MM, DRV_TASK_ID,
+				       sizeof(struct mm_get_sta_info_req));
+
+	if (!get_info_req)
+		return -ENOMEM;
+
+	get_info_req->sta_idx = sta_idx;
+
+	/* Send the MM_GET_STA_INFO_REQ  message to UMAC FW */
+	error = rwnx_send_msg(rwnx_hw, get_info_req, 1, MM_GET_STA_INFO_CFM, cfm);
+
+	return error;
+};
+
+int rwnx_send_set_stack_start_req(struct rwnx_hw *rwnx_hw, u8 on,
+				  u8 efuse_valid, u8 set_vendor_info,
+				  u8 fwtrace_redir_en,
+				  struct mm_set_stack_start_cfm *cfm)
+{
+	struct mm_set_stack_start_req *req;
+	int error;
+
+	/* Build the MM_SET_STACK_START_REQ message */
+	req = rwnx_msg_zalloc(MM_SET_STACK_START_REQ, TASK_MM, DRV_TASK_ID,
+			      sizeof(struct mm_set_stack_start_req));
+
+	if (!req)
+		return -ENOMEM;
+
+	req->is_stack_start = on;
+	req->efuse_valid = efuse_valid;
+	req->set_vendor_info = set_vendor_info;
+	req->fwtrace_redir = fwtrace_redir_en;
+	/* Send the MM_SET_STACK_START_REQ  message to UMAC FW */
+	error = rwnx_send_msg(rwnx_hw, req, 1, MM_SET_STACK_START_CFM, cfm);
+
+	return error;
+}
+
+int rwnx_send_get_temp_req(struct rwnx_hw *rwnx_hw,
+			   struct mm_set_vendor_swconfig_cfm *cfm)
+{
+	struct mm_set_vendor_swconfig_req *req;
+	int ret;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	/* Build the TEMP_COMP_GET_REQ message */
+	req = rwnx_msg_zalloc(MM_SET_VENDOR_SWCONFIG_REQ, TASK_MM, DRV_TASK_ID,
+			      sizeof(struct mm_set_vendor_swconfig_req));
+	if (!req) {
+		AICWFDBG(LOGINFO, "%s msg_alloc fail\n", __func__);
+		return -ENOMEM;
+	}
+	req->swconfig_id = TEMP_COMP_GET_REQ;
+
+	ret = rwnx_send_msg(rwnx_hw, req, 1, MM_SET_VENDOR_SWCONFIG_CFM, cfm);
+	if (!ret) {
+		AICWFDBG(LOGDEBUG, "status=%d, temp=%d\n", cfm->temp_comp_get_cfm.status,
+			 cfm->temp_comp_get_cfm.degree);
+	} else {
+		AICWFDBG(LOGINFO, "%s msg_fail\n", __func__);
+		return ret;
+	}
+#ifdef CONFIG_AIC8800_TEMP_CONTROL
+	// rwnx_hw->started_jiffies = jiffies;
+	rwnx_hw->temp = cfm->temp_comp_get_cfm.degree;
+#endif
+
+	return ret;
+}
+
+int rwnx_send_get_temp_hwconfig_req(struct rwnx_hw *rwnx_hw,
+				    struct mm_set_vendor_hwconfig_cfm *cfm)
+{
+	struct mm_get_chip_temp_req *req;
+	int ret;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+	/* Build the TEMP_COMP_HWCONFIG_GET_REQ message */
+	req = rwnx_msg_zalloc(MM_SET_VENDOR_HWCONFIG_REQ, TASK_MM, DRV_TASK_ID,
+			      sizeof(struct mm_get_chip_temp_req));
+	if (!req) {
+		AICWFDBG(LOGINFO, "%s msg_alloc fail\n", __func__);
+		return -ENOMEM;
+	}
+	req->hwconfig_id = CHIP_TEMP_GET_REQ;
+
+	ret = rwnx_send_msg(rwnx_hw, req, 1, MM_SET_VENDOR_HWCONFIG_CFM, cfm);
+	if (!ret) {
+		AICWFDBG(LOGDEBUG, "get_chip_temp degree=%d\n",
+			 cfm->chip_temp_cfm.degree);
+	} else {
+		AICWFDBG(LOGINFO, "%s msg_fail\n", __func__);
+		return ret;
+	}
+	return ret;
+}
+
+int rwnx_send_set_temp_comp_req(struct rwnx_hw *rwnx_hw,
+				struct mm_set_vendor_swconfig_cfm *cfm)
+{
+	struct mm_set_vendor_swconfig_req *req;
+	int ret;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+	/* Build the TEMP_COMP_SET_REQ message */
+	req = rwnx_msg_zalloc(MM_SET_VENDOR_SWCONFIG_REQ, TASK_MM, DRV_TASK_ID,
+			      sizeof(struct mm_set_vendor_swconfig_req));
+	if (!req) {
+		AICWFDBG(LOGERROR, "%s msg_alloc fail\n", __func__);
+		return -ENOMEM;
+	}
+	req->swconfig_id = TEMP_COMP_SET_REQ;
+	req->temp_comp_set_req.enable = 1;
+#ifdef CONFIG_AIC8800_TEMP_CONTROL
+	req->temp_comp_set_req.tmr_period_ms = TEMP_GET_INTERVAL;
+#else
+	req->temp_comp_set_req.tmr_period_ms = 15 * 1000;
+#endif
+
+	ret = rwnx_send_msg(rwnx_hw, req, 1, MM_SET_VENDOR_SWCONFIG_CFM, cfm);
+	if (!ret) {
+		AICWFDBG(LOGINFO, "temp_comp status: %d\n", cfm->temp_comp_set_cfm.status);
+	} else {
+		AICWFDBG(LOGINFO, "%s msg_fail\n", __func__);
+		return ret;
+	}
+	return ret;
+}
+
+int rwnx_send_vendor_hwconfig_req(struct rwnx_hw *rwnx_hw, u32 hwconfig_id,
+				  s32 *param, s32 *param_out)
+{
+	struct mm_set_acs_txop_req *req0;
+	struct mm_set_channel_access_req *req1;
+	struct mm_set_mac_timescale_req *req2;
+	struct mm_set_cca_threshold_req *req3;
+	struct mm_set_bwmode_req *req4;
+	struct mm_set_ap_ps_level_req *req5 = NULL;
+	struct mm_set_customized_freq_req *req6;
+
+	int error = 0;
+	int i;
+
+	switch (hwconfig_id) {
+	case ACS_TXOP_REQ:
+		/* Build the ACS_TXOP_REQ message */
+		req0 = rwnx_msg_zalloc(MM_SET_VENDOR_HWCONFIG_REQ, TASK_MM, DRV_TASK_ID,
+				       sizeof(struct mm_set_acs_txop_req));
+		if (!req0)
+			return -ENOMEM;
+		req0->hwconfig_id = hwconfig_id;
+		req0->txop_be = param[0];
+		req0->txop_bk = param[1];
+		req0->txop_vi = param[2];
+		req0->txop_vo = param[3];
+		AICWFDBG(LOGDEBUG, "set_acs_txop_req: be: %x,bk: %x,vi: %x,vo: %x\n", req0->txop_be,
+			 req0->txop_bk, req0->txop_vi, req0->txop_vo);
+		/* Send the MM_SET_VENDOR_HWCONFIG_CFM  message to UMAC FW */
+		error =
+			rwnx_send_msg(rwnx_hw, req0, 1, MM_SET_VENDOR_HWCONFIG_CFM, NULL);
+		break;
+
+	case CHANNEL_ACCESS_REQ:
+		/* Build the CHANNEL_ACCESS_REQ message */
+		req1 = rwnx_msg_zalloc(MM_SET_VENDOR_HWCONFIG_REQ, TASK_MM, DRV_TASK_ID,
+				       sizeof(struct mm_set_channel_access_req));
+		if (!req1)
+			return -ENOMEM;
+		req1->hwconfig_id = hwconfig_id;
+		req1->edca[0] = param[0];
+		req1->edca[1] = param[1];
+		req1->edca[2] = param[2];
+		req1->edca[3] = param[3];
+		req1->vif_idx = param[4];
+		req1->retry_cnt = param[5];
+		req1->rts_en = param[6];
+		req1->long_nav_en = param[7];
+		req1->cfe_en = param[8];
+		req1->rc_retry_cnt[0] = param[9];
+		req1->rc_retry_cnt[1] = param[10];
+		req1->rc_retry_cnt[2] = param[11];
+		req1->ccademod_th = param[12];
+		AICWFDBG(LOGDEBUG,
+			 "set_channel_access_req:edca[]= %x %x %x %x\nvif_idx: %x, retry_cnt: %x, rts_en: %x, long_nav_en: %x, cfe_en: %x, rc_retry_cnt: %x:%x:%x, ccademod_th = %d\n",
+			 req1->edca[0], req1->edca[1], req1->edca[2], req1->edca[3],
+			 req1->vif_idx, req1->retry_cnt, req1->rts_en, req1->long_nav_en,
+			 req1->cfe_en, req1->rc_retry_cnt[0], req1->rc_retry_cnt[1],
+			 req1->rc_retry_cnt[2], req1->ccademod_th);
+		/* Send the MM_SET_VENDOR_HWCONFIG_CFM  message to UMAC FW */
+		error =
+			rwnx_send_msg(rwnx_hw, req1, 1, MM_SET_VENDOR_HWCONFIG_CFM, NULL);
+		break;
+
+	case MAC_TIMESCALE_REQ:
+		/* Build the MAC_TIMESCALE_REQ message */
+		req2 = rwnx_msg_zalloc(MM_SET_VENDOR_HWCONFIG_REQ, TASK_MM, DRV_TASK_ID,
+				       sizeof(struct mm_set_mac_timescale_req));
+		if (!req2)
+			return -ENOMEM;
+		req2->hwconfig_id = hwconfig_id;
+		req2->sifs_a_time = param[0];
+		req2->sifs_b_time = param[1];
+		req2->slot_time = param[2];
+		req2->rx_startdelay_ofdm = param[3];
+		req2->rx_startdelay_long = param[4];
+		req2->rx_startdelay_short = param[5];
+		AICWFDBG(LOGDEBUG,
+			 "set_mac_timescale_req:sifsA_time: %x, sifsB_time: %x, slot_time: %x, rx_startdelay ofdm:%x long %x short %x\n",
+			 req2->sifs_a_time, req2->sifs_b_time, req2->slot_time,
+			 req2->rx_startdelay_ofdm, req2->rx_startdelay_long,
+			 req2->rx_startdelay_short);
+		/* Send the MM_SET_VENDOR_HWCONFIG_CFM  message to UMAC FW */
+		error =
+			rwnx_send_msg(rwnx_hw, req2, 1, MM_SET_VENDOR_HWCONFIG_CFM, NULL);
+		break;
+
+	case CCA_THRESHOLD_REQ:
+		/* Build the CCA_THRESHOLD_REQ message */
+		req3 = rwnx_msg_zalloc(MM_SET_VENDOR_HWCONFIG_REQ, TASK_MM, DRV_TASK_ID,
+				       sizeof(struct mm_set_cca_threshold_req));
+		if (!req3)
+			return -ENOMEM;
+		req3->hwconfig_id = hwconfig_id;
+		req3->auto_cca_en = param[0];
+		req3->cca20p_rise_th = param[1];
+		req3->cca20s_rise_th = param[2];
+		req3->cca20p_fall_th = param[3];
+		req3->cca20s_fall_th = param[4];
+		AICWFDBG(LOGDEBUG,
+			 "cca_threshold_req: auto_cca_en:%d\ncca20p_rise_th = %d\ncca20s_rise_th = %d\ncca20p_fall_th = %d\ncca20s_fall_th = %d\n",
+			 req3->auto_cca_en, req3->cca20p_rise_th, req3->cca20s_rise_th,
+			 req3->cca20p_fall_th, req3->cca20s_fall_th);
+		/* Send the MM_SET_VENDOR_HWCONFIG_CFM  message to UMAC FW */
+		error =
+			rwnx_send_msg(rwnx_hw, req3, 1, MM_SET_VENDOR_HWCONFIG_CFM, NULL);
+		break;
+	case BWMODE_REQ:
+		/* Build the SET_BWMODE_REQ message */
+		req4 = rwnx_msg_zalloc(MM_SET_VENDOR_HWCONFIG_REQ, TASK_MM, DRV_TASK_ID,
+				       sizeof(struct mm_set_bwmode_req));
+		if (!req4)
+			return -ENOMEM;
+		req4->hwconfig_id = hwconfig_id;
+		req4->bwmode = param[0];
+		AICWFDBG(LOGDEBUG, "bwmode :%d\n", req4->bwmode);
+		/* Send the MM_SET_VENDOR_HWCONFIG_CFM  message to UMAC FW */
+		error =
+			rwnx_send_msg(rwnx_hw, req4, 1, MM_SET_VENDOR_HWCONFIG_CFM, NULL);
+		break;
+	case CHIP_TEMP_GET_REQ:
+		aic_chip_get_temp(rwnx_hw, param_out);
+		break;
+	case AP_PS_LEVEL_SET_REQ:
+		/*
+		 * AP_PS_CLK_1 = 1,  // 3:7 -> active 3, doze 7
+		 * AP_PS_CLK_2,      // 5:5 -> active 5, doze 5
+		 * AP_PS_CLK_3,      // 7:3 -> active 7, doze 3
+		 * AP_PS_CLK_4,      // 8:2 -> active 8, doze 2
+		 * AP_PS_CLK_5,      // 9:1 -> active 9, doze 1
+		 */
+		/* Build the MM_SET_VENDOR_HWCONFIG_REQ message */
+		req5 = rwnx_msg_zalloc(MM_SET_VENDOR_HWCONFIG_REQ, TASK_MM, DRV_TASK_ID,
+				       sizeof(struct mm_set_ap_ps_level_req));
+		if (!req5)
+			return -ENOMEM;
+		req5->hwconfig_id = hwconfig_id;
+		req5->ap_ps_level = param[0];
+
+		/* Send the MM_GET_MAC_ADDR_REQ  message to UMAC FW */
+		error =
+			rwnx_send_msg(rwnx_hw, req5, 1, MM_SET_VENDOR_HWCONFIG_CFM, NULL);
+		if (!error)
+			AICWFDBG(LOGDEBUG, "set ap_ps_level=%d\n", param[0]);
+		break;
+
+	case CUSTOMIZED_FREQ_REQ:
+		/* Build the CUSTOMIZED_FREQ_REQ message */
+		req6 = rwnx_msg_zalloc(MM_SET_VENDOR_HWCONFIG_REQ, TASK_MM, DRV_TASK_ID,
+				       sizeof(struct mm_set_customized_freq_req));
+		if (!req6)
+			return -ENOMEM;
+		req5->hwconfig_id = hwconfig_id;
+		for (i = 0; i < 4; i++) {
+			req6->raw_freq[i] = param[i];
+			req6->map_freq[i] = param[i + 4];
+		}
+		AICWFDBG(LOGDEBUG,
+			 "customized_freq_req: raw_freq[ %d %d %d %d ], map_freq[ %d %d %d %d]\n",
+			 param[0], param[1], param[2], param[3], param[4], param[5],
+			 param[6], param[7]);
+		/* Send the MM_SET_VENDOR_HWCONFIG_CFM  message to UMAC FW */
+		error =
+			rwnx_send_msg(rwnx_hw, req5, 1, MM_SET_VENDOR_HWCONFIG_CFM, NULL);
+
+		break;
+	default:
+		return -ENOMEM;
+	}
+	return error;
+}
+
+int rwnx_send_vendor_swconfig_req(struct rwnx_hw *rwnx_hw, u32 swconfig_id,
+				  s32 *param_in, s32 *param_out)
+{
+	struct mm_set_vendor_swconfig_req *req;
+	struct mm_set_vendor_swconfig_cfm cfm = {
+		0,
+	};
+	int error;
+
+	req = rwnx_msg_zalloc(MM_SET_VENDOR_SWCONFIG_REQ, TASK_MM, DRV_TASK_ID,
+			      sizeof(struct mm_set_vendor_swconfig_req));
+	if (!req)
+		return -ENOMEM;
+	req->swconfig_id = swconfig_id;
+
+	switch (swconfig_id) {
+	case BCN_CFG_REQ:
+		/* Build the BCN_CFG_REQ message */
+		req->bcn_cfg_req.tim_bcmc_ignored_enable = (bool)param_in[0];
+		AICWFDBG(LOGDEBUG, "bcn_cfg_req: tim_bcmc_ignd=%d\n",
+			 req->bcn_cfg_req.tim_bcmc_ignored_enable);
+		/* Send the MM_SET_VENDOR_SWCONFIG_REQ message to UMAC FW */
+		error =
+			rwnx_send_msg(rwnx_hw, req, 1, MM_SET_VENDOR_SWCONFIG_CFM, &cfm);
+		if (!error) {
+			param_out[0] = (s32)cfm.bcn_cfg_cfm.tim_bcmc_ignored_status;
+			AICWFDBG(LOGDEBUG, "status=%d\n", cfm.bcn_cfg_cfm.tim_bcmc_ignored_status);
+		}
+		break;
+
+	case TEMP_COMP_SET_REQ:
+		/* Build the TEMP_COMP_SET_REQ message */
+		req->temp_comp_set_req.enable = (u8)param_in[0];
+		req->temp_comp_set_req.tmr_period_ms = (u32)param_in[1];
+		AICWFDBG(LOGDEBUG, "temp_comp_set_req: en=%d, tmr=%x\n",
+			 req->temp_comp_set_req.enable,
+			 req->temp_comp_set_req.tmr_period_ms);
+		/* Send the MM_SET_VENDOR_SWCONFIG_REQ message to UMAC FW */
+		error =
+			rwnx_send_msg(rwnx_hw, req, 1, MM_SET_VENDOR_SWCONFIG_CFM, &cfm);
+		if (!error) {
+			param_out[0] = (s32)cfm.temp_comp_set_cfm.status;
+			AICWFDBG(LOGDEBUG, "status=%d\n", cfm.temp_comp_set_cfm.status);
+		}
+		break;
+
+	case TEMP_COMP_GET_REQ:
+		AICWFDBG(LOGDEBUG, "temp_comp_get_req\n");
+		/* Send the MM_SET_VENDOR_SWCONFIG_REQ message to UMAC FW */
+		error =
+			rwnx_send_msg(rwnx_hw, req, 1, MM_SET_VENDOR_SWCONFIG_CFM, &cfm);
+		if (!error) {
+			param_out[0] = (s32)cfm.temp_comp_get_cfm.status;
+			param_out[1] = (s32)cfm.temp_comp_get_cfm.degree;
+			AICWFDBG(LOGDEBUG, "status=%d, degree=%d\n", cfm.temp_comp_get_cfm.status,
+				 cfm.temp_comp_get_cfm.degree);
+		}
+		break;
+
+	case EXT_FLAGS_SET_REQ:
+		/* Build the EXT_FLAGS_SET_REQ message */
+		req->ext_flags_set_req.user_flags = (u32)param_in[0];
+		AICWFDBG(LOGDEBUG, "ext_flags_set_req: flags=%x\n",
+			 req->ext_flags_set_req.user_flags);
+		/* Send the MM_SET_VENDOR_SWCONFIG_REQ message to UMAC FW */
+		error =
+			rwnx_send_msg(rwnx_hw, req, 1, MM_SET_VENDOR_SWCONFIG_CFM, &cfm);
+		if (!error) {
+			param_out[0] = (u32)cfm.ext_flags_set_cfm.user_flags;
+			AICWFDBG(LOGDEBUG, "cfm flags=%x\n", cfm.ext_flags_set_cfm.user_flags);
+		}
+		break;
+
+	case EXT_FLAGS_GET_REQ:
+		/* Build the EXT_FLAGS_GET_REQ message */
+		AICWFDBG(LOGDEBUG, "ext_flags_get_req\n");
+		/* Send the MM_SET_VENDOR_SWCONFIG_REQ message to UMAC FW */
+		error =
+			rwnx_send_msg(rwnx_hw, req, 1, MM_SET_VENDOR_SWCONFIG_CFM, &cfm);
+		if (!error) {
+			param_out[0] = (u32)cfm.ext_flags_get_cfm.user_flags;
+			AICWFDBG(LOGDEBUG, "cfm flags=%x\n", cfm.ext_flags_get_cfm.user_flags);
+		}
+		break;
+
+	case EXT_FLAGS_MASK_SET_REQ:
+		/* Build the EXT_FLAGS_MASK_SET_REQ message */
+		req->ext_flags_mask_set_req.user_flags_mask = (u32)param_in[0];
+		req->ext_flags_mask_set_req.user_flags_val = (u32)param_in[1];
+		AICWFDBG(LOGDEBUG, "ext_flags_mask_set_req: flags mask=0x%x, val=0x%x\n",
+			 req->ext_flags_mask_set_req.user_flags_mask,
+			 req->ext_flags_mask_set_req.user_flags_val);
+		/* Send the MM_SET_VENDOR_SWCONFIG_REQ message to UMAC FW */
+		error =
+			rwnx_send_msg(rwnx_hw, req, 1, MM_SET_VENDOR_SWCONFIG_CFM, &cfm);
+		if (!error) {
+			param_out[0] = (u32)cfm.ext_flags_mask_set_cfm.user_flags;
+			AICWFDBG(LOGDEBUG, "cfm flags=%x\n", cfm.ext_flags_mask_set_cfm.user_flags);
+		}
+		break;
+
+	default:
+		error = -ENOMEM;
+		break;
+	}
+
+	return error;
+}
+
+int rwnx_send_mask_set_ext_flags_req(struct rwnx_hw *rwnx_hw,
+				     u32 flags_mask, u32 flags_val,
+				     struct mm_set_vendor_swconfig_cfm *cfm)
+{
+	struct mm_set_vendor_swconfig_req *req;
+	int ret;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+	/* Build the REQ message */
+	req = rwnx_msg_zalloc(MM_SET_VENDOR_SWCONFIG_REQ, TASK_MM, DRV_TASK_ID,
+			      sizeof(struct mm_set_vendor_swconfig_req));
+	if (!req) {
+		AICWFDBG(LOGERROR, "%s msg_alloc fail\n", __func__);
+		return -ENOMEM;
+	}
+	req->swconfig_id = EXT_FLAGS_MASK_SET_REQ;
+	req->ext_flags_mask_set_req.user_flags_mask = (u32)flags_mask;
+	req->ext_flags_mask_set_req.user_flags_val = (u32)flags_val;
+
+	ret = rwnx_send_msg(rwnx_hw, req, 1, MM_SET_VENDOR_SWCONFIG_CFM, cfm);
+	if (!ret) {
+		AICWFDBG(LOGINFO, "curr ext_flags=%x\n",
+			 cfm->ext_flags_mask_set_cfm.user_flags);
+	} else {
+		AICWFDBG(LOGERROR, "%s send_msg_fail\n", __func__);
+		return ret;
+	}
+	return ret;
+}
+
+int rwnx_send_get_fw_version_req(struct rwnx_hw *rwnx_hw,
+				 struct mm_get_fw_version_cfm *cfm)
+{
+	void *req;
+	int error;
+
+	/* Build the MM_GET_FW_VERSION_REQ message */
+	req = rwnx_msg_zalloc(MM_GET_FW_VERSION_REQ, TASK_MM, DRV_TASK_ID,
+			      sizeof(u8));
+
+	if (!req)
+		return -ENOMEM;
+
+	/* Send the MM_GET_FW_VERSION_REQ  message to UMAC FW */
+	error = rwnx_send_msg(rwnx_hw, req, 1, MM_GET_FW_VERSION_CFM, cfm);
+
+	return error;
+}
+
+int rwnx_send_txpwr_lvl_req(struct rwnx_hw *rwnx_hw)
+{
+	struct mm_set_txpwr_lvl_req *txpwr_lvl_req;
+	struct txpwr_lvl_conf_v2 txpwr_lvl_v2_tmp;
+	struct txpwr_lvl_conf_v2 *txpwr_lvl_v2;
+	int error;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	/* Build the MM_SET_TXPWR_LVL_REQ message */
+	txpwr_lvl_req =
+		rwnx_msg_zalloc(MM_SET_TXPWR_IDX_LVL_REQ, TASK_MM, DRV_TASK_ID,
+				sizeof(struct mm_set_txpwr_lvl_req));
+
+	if (!txpwr_lvl_req)
+		return -ENOMEM;
+
+	txpwr_lvl_v2 = &txpwr_lvl_v2_tmp;
+
+	get_userconfig_txpwr_lvl_v2_in_fdrv(txpwr_lvl_v2);
+
+	if (txpwr_lvl_v2->enable == 0) {
+		rwnx_msg_free(rwnx_hw, txpwr_lvl_req);
+		return 0;
+	}
+
+	AICWFDBG(LOGINFO, "%s:enable:%d\r\n", __func__, txpwr_lvl_v2->enable);
+	AICWFDBG(LOGINFO, "%s:lvl_11b_11ag_1m_2g4:%d\r\n", __func__,
+		 txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[0]);
+	AICWFDBG(LOGINFO, "%s:lvl_11b_11ag_2m_2g4:%d\r\n", __func__,
+		 txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[1]);
+	AICWFDBG(LOGINFO, "%s:lvl_11b_11ag_5m5_2g4:%d\r\n", __func__,
+		 txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[2]);
+	AICWFDBG(LOGINFO, "%s:lvl_11b_11ag_11m_2g4:%d\r\n", __func__,
+		 txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[3]);
+	AICWFDBG(LOGINFO, "%s:lvl_11b_11ag_6m_2g4:%d\r\n", __func__,
+		 txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[4]);
+	AICWFDBG(LOGINFO, "%s:lvl_11b_11ag_9m_2g4:%d\r\n", __func__,
+		 txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[5]);
+	AICWFDBG(LOGINFO, "%s:lvl_11b_11ag_12m_2g4:%d\r\n", __func__,
+		 txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[6]);
+	AICWFDBG(LOGINFO, "%s:lvl_11b_11ag_18m_2g4:%d\r\n", __func__,
+		 txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[7]);
+	AICWFDBG(LOGINFO, "%s:lvl_11b_11ag_24m_2g4:%d\r\n", __func__,
+		 txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[8]);
+	AICWFDBG(LOGINFO, "%s:lvl_11b_11ag_36m_2g4:%d\r\n", __func__,
+		 txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[9]);
+	AICWFDBG(LOGINFO, "%s:lvl_11b_11ag_48m_2g4:%d\r\n", __func__,
+		 txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[10]);
+	AICWFDBG(LOGINFO, "%s:lvl_11b_11ag_54m_2g4:%d\r\n", __func__,
+		 txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[11]);
+	AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs0_2g4:%d\r\n", __func__,
+		 txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[0]);
+	AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs1_2g4:%d\r\n", __func__,
+		 txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[1]);
+	AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs2_2g4:%d\r\n", __func__,
+		 txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[2]);
+	AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs3_2g4:%d\r\n", __func__,
+		 txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[3]);
+	AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs4_2g4:%d\r\n", __func__,
+		 txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[4]);
+	AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs5_2g4:%d\r\n", __func__,
+		 txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[5]);
+	AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs6_2g4:%d\r\n", __func__,
+		 txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[6]);
+	AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs7_2g4:%d\r\n", __func__,
+		 txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[7]);
+	AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs8_2g4:%d\r\n", __func__,
+		 txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[8]);
+	AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs9_2g4:%d\r\n", __func__,
+		 txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[9]);
+	AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs0_2g4:%d\r\n", __func__,
+		 txpwr_lvl_v2->pwrlvl_11ax_2g4[0]);
+	AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs1_2g4:%d\r\n", __func__,
+		 txpwr_lvl_v2->pwrlvl_11ax_2g4[1]);
+	AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs2_2g4:%d\r\n", __func__,
+		 txpwr_lvl_v2->pwrlvl_11ax_2g4[2]);
+	AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs3_2g4:%d\r\n", __func__,
+		 txpwr_lvl_v2->pwrlvl_11ax_2g4[3]);
+	AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs4_2g4:%d\r\n", __func__,
+		 txpwr_lvl_v2->pwrlvl_11ax_2g4[4]);
+	AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs5_2g4:%d\r\n", __func__,
+		 txpwr_lvl_v2->pwrlvl_11ax_2g4[5]);
+	AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs6_2g4:%d\r\n", __func__,
+		 txpwr_lvl_v2->pwrlvl_11ax_2g4[6]);
+	AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs7_2g4:%d\r\n", __func__,
+		 txpwr_lvl_v2->pwrlvl_11ax_2g4[7]);
+	AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs8_2g4:%d\r\n", __func__,
+		 txpwr_lvl_v2->pwrlvl_11ax_2g4[8]);
+	AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs9_2g4:%d\r\n", __func__,
+		 txpwr_lvl_v2->pwrlvl_11ax_2g4[9]);
+	AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs10_2g4:%d\r\n", __func__,
+		 txpwr_lvl_v2->pwrlvl_11ax_2g4[10]);
+	AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs11_2g4:%d\r\n", __func__,
+		 txpwr_lvl_v2->pwrlvl_11ax_2g4[11]);
+
+	if (rwnx_hw->testmode == 0 && chip_sub_id == 0) {
+		txpwr_lvl_req->txpwr_lvl.enable = txpwr_lvl_v2->enable;
+		txpwr_lvl_req->txpwr_lvl.dsss =
+			txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[3]; // 11M
+		txpwr_lvl_req->txpwr_lvl.ofdmlowrate_2g4 =
+			txpwr_lvl_v2->pwrlvl_11ax_2g4[4]; // MCS4
+		txpwr_lvl_req->txpwr_lvl.ofdm64qam_2g4 =
+			txpwr_lvl_v2->pwrlvl_11ax_2g4[7]; // MCS7
+		txpwr_lvl_req->txpwr_lvl.ofdm256qam_2g4 =
+			txpwr_lvl_v2->pwrlvl_11ax_2g4[9]; // MCS9
+		txpwr_lvl_req->txpwr_lvl.ofdm1024qam_2g4 =
+			txpwr_lvl_v2->pwrlvl_11ax_2g4[11];        // MCS11
+		txpwr_lvl_req->txpwr_lvl.ofdmlowrate_5g = 13; // unused
+		txpwr_lvl_req->txpwr_lvl.ofdm64qam_5g = 13;   // unused
+		txpwr_lvl_req->txpwr_lvl.ofdm256qam_5g = 13;  // unused
+		txpwr_lvl_req->txpwr_lvl.ofdm1024qam_5g = 13; // unused
+	} else {
+		txpwr_lvl_req->txpwr_lvl_v2 = *txpwr_lvl_v2;
+	}
+
+	/* Send the MM_SET_TXPWR_LVL_REQ message to UMAC FW */
+	error = rwnx_send_msg(rwnx_hw, txpwr_lvl_req, 1,
+			      MM_SET_TXPWR_IDX_LVL_CFM, NULL);
+
+	return error;
+}
+
+int rwnx_send_txpwr_lvl_v3_req(struct rwnx_hw *rwnx_hw, int index_reg)
+{
+	struct mm_set_txpwr_lvl_req *txpwr_lvl_req;
+	struct txpwr_lvl_conf_v3 txpwr_lvl_v3_tmp;
+	struct txpwr_lvl_conf_v3 *txpwr_lvl_v3;
+	struct txpwr_loss_conf txpwr_loss_tmp;
+	struct txpwr_loss_conf *txpwr_loss;
+	int error;
+	int i;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	/* Build the MM_SET_TXPWR_LVL_REQ message */
+	txpwr_lvl_req =
+		rwnx_msg_zalloc(MM_SET_TXPWR_IDX_LVL_REQ, TASK_MM, DRV_TASK_ID,
+				sizeof(struct mm_set_txpwr_lvl_req));
+
+	if (!txpwr_lvl_req)
+		return -ENOMEM;
+
+	txpwr_lvl_v3 = &txpwr_lvl_v3_tmp;
+	txpwr_loss = &txpwr_loss_tmp;
+	txpwr_loss->loss_enable_2g4 = 0;
+	txpwr_loss->loss_enable_5g = 0;
+
+	//get_userconfig_txpwr_lvl_v3_in_fdrv(txpwr_lvl_v3);
+	get_userconfig_txpwr_lvl_group_in_fdrv(txpwr_lvl_v3, index_reg);
+	get_userconfig_txpwr_loss(txpwr_loss);
+
+	if (txpwr_loss->loss_enable_2g4 == 1) {
+		AICWFDBG(LOGINFO, "%s:loss_value_2g4: %d\r\n", __func__,
+			 txpwr_loss->loss_value_2g4);
+
+		for (i = 0; i <= 11; i++)
+			txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[i] -= txpwr_loss->loss_value_2g4;
+		for (i = 0; i <= 9; i++)
+			txpwr_lvl_v3->pwrlvl_11n_11ac_2g4[i] -= txpwr_loss->loss_value_2g4;
+		for (i = 0; i <= 11; i++)
+			txpwr_lvl_v3->pwrlvl_11ax_2g4[i] -= txpwr_loss->loss_value_2g4;
+	}
+
+	if (txpwr_loss->loss_enable_5g == 1) {
+		AICWFDBG(LOGINFO, "%s:loss_value_5g: %d\r\n", __func__,
+			 txpwr_loss->loss_value_5g);
+
+		for (i = 0; i <= 11; i++)
+			txpwr_lvl_v3->pwrlvl_11a_5g[i] -= txpwr_loss->loss_value_5g;
+		for (i = 0; i <= 9; i++)
+			txpwr_lvl_v3->pwrlvl_11n_11ac_5g[i] -= txpwr_loss->loss_value_5g;
+		for (i = 0; i <= 11; i++)
+			txpwr_lvl_v3->pwrlvl_11ax_5g[i] -= txpwr_loss->loss_value_5g;
+	}
+
+	if (txpwr_lvl_v3->enable == 0) {
+		rwnx_msg_free(rwnx_hw, txpwr_lvl_req);
+		return 0;
+	}
+
+	AICWFDBG(LOGDEBUG, "%s: dump\n", __func__);
+	AICWFDBG(LOGDEBUG, "txpwr_lvl_v3->enable: %d\r\n", txpwr_lvl_v3->enable);
+	AICWFDBG(LOGDEBUG, "lvl_11b_11ag_1m_2g4:%d\r\n",
+		 txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[0]);
+
+	txpwr_lvl_req->txpwr_lvl_v3 = *txpwr_lvl_v3;
+
+	/* Send the MM_SET_TXPWR_LVL_REQ message to UMAC FW */
+	error = rwnx_send_msg(rwnx_hw, txpwr_lvl_req, 1,
+			      MM_SET_TXPWR_IDX_LVL_CFM, NULL);
+
+	return error;
+}
+
+int rwnx_send_txpwr_lvl_adj_req(struct rwnx_hw *rwnx_hw)
+{
+	struct mm_set_txpwr_lvl_adj_req *txpwr_lvl_adj_req;
+	struct txpwr_lvl_adj_conf txpwr_lvl_adj_tmp;
+	struct txpwr_lvl_adj_conf *txpwr_lvl_adj;
+	int error;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	/* Build the MM_SET_TXPWR_LVL_REQ message */
+	txpwr_lvl_adj_req =
+		rwnx_msg_zalloc(MM_SET_TXPWR_LVL_ADJ_REQ, TASK_MM, DRV_TASK_ID,
+				sizeof(struct mm_set_txpwr_lvl_adj_req));
+
+	if (!txpwr_lvl_adj_req)
+		return -ENOMEM;
+
+	txpwr_lvl_adj = &txpwr_lvl_adj_tmp;
+
+	get_userconfig_txpwr_lvl_adj_in_fdrv(txpwr_lvl_adj);
+
+	if (txpwr_lvl_adj->enable == 0) {
+		rwnx_msg_free(rwnx_hw, txpwr_lvl_adj_req);
+		return 0;
+	}
+
+	AICWFDBG(LOGINFO, "%s:enable:%d\r\n", __func__, txpwr_lvl_adj->enable);
+	AICWFDBG(LOGINFO, "%s:lvl_adj_2g4_chan_1_4:%d\r\n", __func__,
+		 txpwr_lvl_adj->pwrlvl_adj_tbl_2g4[0]);
+	AICWFDBG(LOGINFO, "%s:lvl_adj_2g4_chan_5_9:%d\r\n", __func__,
+		 txpwr_lvl_adj->pwrlvl_adj_tbl_2g4[1]);
+	AICWFDBG(LOGINFO, "%s:lvl_adj_2g4_chan_10_13:%d\r\n", __func__,
+		 txpwr_lvl_adj->pwrlvl_adj_tbl_2g4[2]);
+	AICWFDBG(LOGINFO, "%s:lvl_adj_5g_chan_42:%d\r\n", __func__,
+		 txpwr_lvl_adj->pwrlvl_adj_tbl_5g[0]);
+	AICWFDBG(LOGINFO, "%s:lvl_adj_5g_chan_58:%d\r\n", __func__,
+		 txpwr_lvl_adj->pwrlvl_adj_tbl_5g[1]);
+	AICWFDBG(LOGINFO, "%s:lvl_adj_5g_chan_106:%d\r\n", __func__,
+		 txpwr_lvl_adj->pwrlvl_adj_tbl_5g[2]);
+	AICWFDBG(LOGINFO, "%s:lvl_adj_5g_chan_122:%d\r\n", __func__,
+		 txpwr_lvl_adj->pwrlvl_adj_tbl_5g[3]);
+	AICWFDBG(LOGINFO, "%s:lvl_adj_5g_chan_138:%d\r\n", __func__,
+		 txpwr_lvl_adj->pwrlvl_adj_tbl_5g[4]);
+	AICWFDBG(LOGINFO, "%s:lvl_adj_5g_chan_155:%d\r\n", __func__,
+		 txpwr_lvl_adj->pwrlvl_adj_tbl_5g[5]);
+
+	txpwr_lvl_adj_req->txpwr_lvl_adj = *txpwr_lvl_adj;
+
+	/* Send the MM_SET_TXPWR_LVL_REQ message to UMAC FW */
+	error = rwnx_send_msg(rwnx_hw, txpwr_lvl_adj_req, 1,
+			      MM_SET_TXPWR_LVL_ADJ_CFM, NULL);
+
+	return error;
+}
+
+int rwnx_send_txpwr_idx_req(struct rwnx_hw *rwnx_hw)
+{
+	struct mm_set_txpwr_idx_req *txpwr_idx_req;
+	struct txpwr_idx_conf *txpwr_idx;
+	int error;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	/* Build the MM_SET_TXPWR_IDX_LVL_REQ message */
+	txpwr_idx_req =
+		rwnx_msg_zalloc(MM_SET_TXPWR_IDX_LVL_REQ, TASK_MM, DRV_TASK_ID,
+				sizeof(struct mm_set_txpwr_idx_req));
+
+	if (!txpwr_idx_req)
+		return -ENOMEM;
+
+	txpwr_idx = &txpwr_idx_req->txpwr_idx;
+	txpwr_idx->enable = 1;
+	txpwr_idx->dsss = 9;
+	txpwr_idx->ofdmlowrate_2g4 = 8;
+	txpwr_idx->ofdm64qam_2g4 = 8;
+	txpwr_idx->ofdm256qam_2g4 = 8;
+	txpwr_idx->ofdm1024qam_2g4 = 8;
+	txpwr_idx->ofdmlowrate_5g = 11;
+	txpwr_idx->ofdm64qam_5g = 10;
+	txpwr_idx->ofdm256qam_5g = 9;
+	txpwr_idx->ofdm1024qam_5g = 9;
+
+	get_userconfig_txpwr_idx(txpwr_idx);
+
+	if (txpwr_idx->enable == 0) {
+		rwnx_msg_free(rwnx_hw, txpwr_idx_req);
+		return 0;
+	}
+	/* Send the MM_SET_TXPWR_IDX_LVL_REQ message to UMAC FW */
+	error = rwnx_send_msg(rwnx_hw, txpwr_idx_req, 1,
+			      MM_SET_TXPWR_IDX_LVL_CFM, NULL);
+
+	return error;
+};
+
+int rwnx_send_txpwr_ofst_req(struct rwnx_hw *rwnx_hw)
+{
+	struct mm_set_txpwr_ofst_req *txpwr_ofst_req;
+	struct txpwr_ofst_conf *txpwr_ofst;
+	int error;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	/* Build the MM_SET_TXPWR_OFST_REQ message */
+	txpwr_ofst_req =
+		rwnx_msg_zalloc(MM_SET_TXPWR_OFST_REQ, TASK_MM, DRV_TASK_ID,
+				sizeof(struct mm_set_txpwr_ofst_req));
+
+	if (!txpwr_ofst_req)
+		return -ENOMEM;
+
+	txpwr_ofst = &txpwr_ofst_req->txpwr_ofst;
+	txpwr_ofst->enable = 1;
+	txpwr_ofst->chan_1_4 = 0;
+	txpwr_ofst->chan_5_9 = 0;
+	txpwr_ofst->chan_10_13 = 0;
+	txpwr_ofst->chan_36_64 = 0;
+	txpwr_ofst->chan_100_120 = 0;
+	txpwr_ofst->chan_122_140 = 0;
+	txpwr_ofst->chan_142_165 = 0;
+
+	aic_chip_get_userconfig_txpwr_ofst(rwnx_hw, txpwr_ofst);
+
+	if (txpwr_ofst->enable == 0) {
+		rwnx_msg_free(rwnx_hw, txpwr_ofst_req);
+		return 0;
+	}
+	/* Send the MM_SET_TXPWR_OFST_REQ message to UMAC FW */
+	error = rwnx_send_msg(rwnx_hw, txpwr_ofst_req, 1, MM_SET_TXPWR_OFST_CFM,
+			      NULL);
+
+	return error;
+};
+
+int rwnx_send_txpwr_ofst2x_req(struct rwnx_hw *rwnx_hw)
+{
+	struct mm_set_txpwr_ofst_req *txpwr_ofst_req;
+	struct txpwr_ofst2x_conf *txpwr_ofst2x;
+	int error = 0;
+	int type, ch_grp;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	/* Build the MM_SET_TXPWR_OFST_REQ message */
+	txpwr_ofst_req =
+		rwnx_msg_zalloc(MM_SET_TXPWR_OFST_REQ, TASK_MM, DRV_TASK_ID,
+				sizeof(struct mm_set_txpwr_ofst_req));
+
+	if (!txpwr_ofst_req)
+		return -ENOMEM;
+
+	txpwr_ofst2x = &txpwr_ofst_req->txpwr_ofst2x;
+	txpwr_ofst2x->enable = 0;
+	for (type = 0; type < 3; type++) {
+		for (ch_grp = 0; ch_grp < 6; ch_grp++) {
+			if (ch_grp < 3)
+				txpwr_ofst2x->pwrofst2x_tbl_2g4[type][ch_grp] = 0;
+			txpwr_ofst2x->pwrofst2x_tbl_5g[type][ch_grp] = 0;
+		}
+	}
+
+	aic_chip_get_userconfig_txpwr_ofst2x(rwnx_hw, txpwr_ofst2x);
+
+	if (txpwr_ofst2x->enable) {
+		AICWFDBG(LOGINFO, "%s:enable:%d\r\n", __func__, txpwr_ofst2x->enable);
+		AICWFDBG(LOGDEBUG,
+			 "pwrofst2x 2.4g: [0]:11b, [1]:ofdm_highrate, [2]:ofdm_lowrate\n  chan=\t1-4\t5-9\t10-13");
+		for (type = 0; type < 3; type++) {
+			AICWFDBG(LOGDEBUG, "\n  [%d] =", type);
+			for (ch_grp = 0; ch_grp < 3; ch_grp++) {
+				AICWFDBG(LOGDEBUG, "\t%d",
+					 txpwr_ofst2x->pwrofst2x_tbl_2g4[type][ch_grp]);
+			}
+		}
+		AICWFDBG(LOGDEBUG,
+			 "\npwrofst2x 5g: [0]:ofdm_lowrate, [1]:ofdm_highrate, [2]:ofdm_midrate\n  chan=\t36-50\t51-64\t98-114\t115-130\t131-146\t147-166");
+		for (type = 0; type < 3; type++) {
+			AICWFDBG(LOGDEBUG, "\n  [%d] =", type);
+			for (ch_grp = 0; ch_grp < 6; ch_grp++)
+				AICWFDBG(LOGDEBUG, "\t%d",
+					 txpwr_ofst2x->pwrofst2x_tbl_5g[type][ch_grp]);
+		}
+		AICWFDBG(LOGDEBUG, "\n");
+
+		/* Send the MM_SET_TXPWR_OFST_REQ message to UMAC FW */
+		error = rwnx_send_msg(rwnx_hw, txpwr_ofst_req, 1, MM_SET_TXPWR_OFST_CFM,
+				      NULL);
+	} else {
+		AICWFDBG(LOGDEBUG, "%s:Do not use txpwr_ofst2x\r\n", __func__);
+		rwnx_msg_free(rwnx_hw, txpwr_ofst_req);
+	}
+
+	return error;
+}
+
+/******************************************************************************
+ *    Control messages handling functions (FULLMAC only)
+ *****************************************************************************/
+#ifdef CONFIG_RWNX_FULLMAC
+int rwnx_send_me_config_req(struct rwnx_hw *rwnx_hw)
+{
+	struct me_config_req *req;
+	struct wiphy *wiphy = rwnx_hw->wiphy;
+	struct ieee80211_supported_band *band_2ghz =
+		wiphy->bands[NL80211_BAND_2GHZ];
+	struct ieee80211_sta_ht_cap *ht_cap;
+	struct ieee80211_sta_vht_cap *vht_cap;
+	struct ieee80211_sta_he_cap const *he_cap = NULL;
+
+	u8 *ht_mcs;
+	int i;
+
+	if (rwnx_hw->chip_ops->use_80_bandwidth)
+		rwnx_hw->mod_params->use_80 = true;
+
+	if (rwnx_hw->band_5g_support) {
+		ht_cap = &wiphy->bands[NL80211_BAND_5GHZ]->ht_cap;
+		vht_cap = &wiphy->bands[NL80211_BAND_5GHZ]->vht_cap;
+	} else {
+		ht_cap = &wiphy->bands[NL80211_BAND_2GHZ]->ht_cap;
+		vht_cap = &wiphy->bands[NL80211_BAND_2GHZ]->vht_cap;
+	}
+
+	ht_mcs = (u8 *)&ht_cap->mcs;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	/* Build the ME_CONFIG_REQ message */
+	req = rwnx_msg_zalloc(ME_CONFIG_REQ, TASK_ME, DRV_TASK_ID,
+			      sizeof(struct me_config_req));
+	if (!req)
+		return -ENOMEM;
+
+	/* Set parameters for the ME_CONFIG_REQ message */
+	req->ht_supp = ht_cap->ht_supported;
+	req->vht_supp = vht_cap->vht_supported;
+	req->ht_cap.ht_capa_info =
+		cpu_to_le16(ht_cap->cap | IEEE80211_HT_CAP_LDPC_CODING);
+	req->ht_cap.a_mpdu_param =
+		ht_cap->ampdu_factor |
+		(ht_cap->ampdu_density << IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT);
+	for (i = 0; i < sizeof(ht_cap->mcs); i++)
+		req->ht_cap.mcs_rate[i] = ht_mcs[i];
+	req->ht_cap.ht_extended_capa = 0;
+	req->ht_cap.tx_beamforming_capa = 0;
+	req->ht_cap.asel_capa = 0;
+
+	if (req->vht_supp) {
+		req->vht_cap.vht_capa_info = cpu_to_le32(vht_cap->cap);
+		req->vht_cap.rx_highest = vht_cap->vht_mcs.rx_highest;
+		req->vht_cap.rx_mcs_map = vht_cap->vht_mcs.rx_mcs_map;
+		req->vht_cap.tx_highest = vht_cap->vht_mcs.tx_highest;
+		req->vht_cap.tx_mcs_map = vht_cap->vht_mcs.tx_mcs_map;
+	}
+
+	he_cap = ieee80211_get_he_iftype_cap(band_2ghz, NL80211_IFTYPE_STATION);
+	if (he_cap) {
+		req->he_supp = he_cap->has_he;
+		for (i = 0; i < ARRAY_SIZE(he_cap->he_cap_elem.mac_cap_info); i++)
+			req->he_cap.mac_cap_info[i] = he_cap->he_cap_elem.mac_cap_info[i];
+
+		for (i = 0; i < ARRAY_SIZE(he_cap->he_cap_elem.phy_cap_info); i++)
+			req->he_cap.phy_cap_info[i] = he_cap->he_cap_elem.phy_cap_info[i];
+
+		req->he_cap.mcs_supp.rx_mcs_80 = he_cap->he_mcs_nss_supp.rx_mcs_80;
+		req->he_cap.mcs_supp.tx_mcs_80 = he_cap->he_mcs_nss_supp.tx_mcs_80;
+		req->he_cap.mcs_supp.rx_mcs_160 = he_cap->he_mcs_nss_supp.rx_mcs_160;
+		req->he_cap.mcs_supp.tx_mcs_160 = he_cap->he_mcs_nss_supp.tx_mcs_160;
+		req->he_cap.mcs_supp.rx_mcs_80p80 =
+			he_cap->he_mcs_nss_supp.rx_mcs_80p80;
+		req->he_cap.mcs_supp.tx_mcs_80p80 =
+			he_cap->he_mcs_nss_supp.tx_mcs_80p80;
+		for (i = 0; i < MAC_HE_PPE_THRES_MAX_LEN; i++)
+			req->he_cap.ppe_thres[i] = he_cap->ppe_thres[i];
+
+		req->he_ul_on = rwnx_hw->mod_params->he_ul_on;
+	}
+	req->ps_on = rwnx_hw->mod_params->ps_on;
+	req->dpsm = rwnx_hw->mod_params->dpsm;
+	req->tx_lft = rwnx_hw->mod_params->tx_lft;
+	req->ant_div_on = rwnx_hw->mod_params->ant_div;
+	if (rwnx_hw->mod_params->use_80)
+		req->phy_bw_max = PHY_CHNL_BW_80;
+	else if (rwnx_hw->mod_params->use_2040)
+		req->phy_bw_max = PHY_CHNL_BW_40;
+	else
+		req->phy_bw_max = PHY_CHNL_BW_20;
+
+	wiphy_info(wiphy, "AICWF HT supp %d, VHT supp %d, HE supp %d\n", req->ht_supp,
+		   req->vht_supp, req->he_supp);
+	/* Send the ME_CONFIG_REQ message to LMAC FW */
+	return rwnx_send_msg(rwnx_hw, req, 1, ME_CONFIG_CFM, NULL);
+}
+
+int rwnx_send_me_chan_config_req(struct rwnx_hw *rwnx_hw)
+{
+	struct me_chan_config_req *req;
+	struct wiphy *wiphy = rwnx_hw->wiphy;
+	int i;
+#ifdef CONFIG_AIC8800_POWER_LIMIT
+	u8 enable = 0;
+	s8 max_pwr;
+	struct txpwr_loss_conf txpwr_loss_tmp;
+	struct txpwr_loss_conf *txpwr_loss;
+#endif
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	/* Build the ME_CHAN_CONFIG_REQ message */
+	req = rwnx_msg_zalloc(ME_CHAN_CONFIG_REQ, TASK_ME, DRV_TASK_ID,
+			      sizeof(struct me_chan_config_req));
+	if (!req)
+		return -ENOMEM;
+
+#ifdef CONFIG_AIC8800_POWER_LIMIT
+	txpwr_loss = &txpwr_loss_tmp;
+	get_userconfig_txpwr_loss(txpwr_loss);
+
+	if (txpwr_loss->loss_enable_2g4 == 1)
+		AICWFDBG(LOGINFO, "%s:loss_value_2g4: %d\r\n", __func__,
+			 txpwr_loss->loss_value_2g4);
+	if (txpwr_loss->loss_enable_5g == 1)
+		AICWFDBG(LOGINFO, "%s:loss_value_5g: %d\r\n", __func__,
+			 txpwr_loss->loss_value_5g);
+#endif
+
+	req->chan2G4_cnt = 0;
+	if (wiphy->bands[NL80211_BAND_2GHZ]) {
+		struct ieee80211_supported_band *b = wiphy->bands[NL80211_BAND_2GHZ];
+
+		for (i = 0; i < b->n_channels; i++) {
+			req->chan2G4[req->chan2G4_cnt].flags = 0;
+
+			if (b->channels[i].flags & IEEE80211_CHAN_DISABLED)
+				req->chan2G4[req->chan2G4_cnt].flags |= CHAN_DISABLED;
+			req->chan2G4[req->chan2G4_cnt].flags |=
+				get_chan_flags(b->channels[i].flags);
+			req->chan2G4[req->chan2G4_cnt].band = NL80211_BAND_2GHZ;
+			req->chan2G4[req->chan2G4_cnt].freq = b->channels[i].center_freq;
+			req->chan2G4[req->chan2G4_cnt].tx_power =
+				chan_to_fw_pwr(b->channels[i].max_power);
+#ifdef CONFIG_AIC8800_POWER_LIMIT
+			max_pwr = get_powerlimit_by_freq(PHY_BAND_2G4,
+							 req->chan2G4[req->chan2G4_cnt].freq,
+							 &enable);
+			if (txpwr_loss->loss_enable_2g4 == 1)
+				max_pwr -= txpwr_loss->loss_value_2g4;
+			if (enable) {
+				if (req->chan2G4[req->chan2G4_cnt].tx_power > max_pwr)
+					req->chan2G4[req->chan2G4_cnt].tx_power = max_pwr;
+			}
+#endif
+			req->chan2G4_cnt++;
+			if (req->chan2G4_cnt == MAC_DOMAINCHANNEL_24G_MAX)
+				break;
+		}
+	}
+
+	req->chan5G_cnt = 0;
+	if (wiphy->bands[NL80211_BAND_5GHZ]) {
+		struct ieee80211_supported_band *b = wiphy->bands[NL80211_BAND_5GHZ];
+
+		for (i = 0; i < b->n_channels; i++) {
+			req->chan5G[req->chan5G_cnt].flags = 0;
+			if (b->channels[i].flags & IEEE80211_CHAN_DISABLED)
+				req->chan5G[req->chan5G_cnt].flags |= CHAN_DISABLED;
+			req->chan5G[req->chan5G_cnt].flags |=
+				get_chan_flags(b->channels[i].flags);
+			req->chan5G[req->chan5G_cnt].band = NL80211_BAND_5GHZ;
+			req->chan5G[req->chan5G_cnt].freq = b->channels[i].center_freq;
+			req->chan5G[req->chan5G_cnt].tx_power =
+				chan_to_fw_pwr(b->channels[i].max_power);
+#ifdef CONFIG_AIC8800_POWER_LIMIT
+			max_pwr = get_powerlimit_by_freq(PHY_BAND_5G,
+							 req->chan5G[req->chan5G_cnt].freq,
+							 &enable);
+			if (txpwr_loss->loss_enable_5g == 1)
+				max_pwr -= txpwr_loss->loss_value_5g;
+			if (enable) {
+				if (req->chan5G[req->chan5G_cnt].tx_power > max_pwr)
+					req->chan5G[req->chan5G_cnt].tx_power = max_pwr;
+			}
+#endif
+			req->chan5G_cnt++;
+			if (req->chan5G_cnt == MAC_DOMAINCHANNEL_5G_MAX)
+				break;
+		}
+	}
+
+	/* Send the ME_CHAN_CONFIG_REQ message to LMAC FW */
+	return rwnx_send_msg(rwnx_hw, req, 1, ME_CHAN_CONFIG_CFM, NULL);
+}
+
+int rwnx_send_me_set_control_port_req(struct rwnx_hw *rwnx_hw, bool opened,
+				      u8 sta_idx)
+{
+	struct me_set_control_port_req *req;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	/* Build the ME_SET_CONTROL_PORT_REQ message */
+	req = rwnx_msg_zalloc(ME_SET_CONTROL_PORT_REQ, TASK_ME, DRV_TASK_ID,
+			      sizeof(struct me_set_control_port_req));
+	if (!req)
+		return -ENOMEM;
+
+	/* Set parameters for the ME_SET_CONTROL_PORT_REQ message */
+	req->sta_idx = sta_idx;
+	req->control_port_open = opened;
+
+	/* Send the ME_SET_CONTROL_PORT_REQ message to LMAC FW */
+	return rwnx_send_msg(rwnx_hw, req, 1, ME_SET_CONTROL_PORT_CFM, NULL);
+}
+
+int rwnx_send_me_sta_add(struct rwnx_hw *rwnx_hw,
+			 struct station_parameters *params, const u8 *mac,
+			 u8 inst_nbr, struct me_sta_add_cfm *cfm)
+{
+	struct me_sta_add_req *req;
+	struct link_station_parameters *link_sta_params = &params->link_sta_params;
+	struct rwnx_vif *rwnx_vif = rwnx_hw->vif_table[inst_nbr];
+
+	u8 *ht_mcs = (u8 *)&link_sta_params->ht_capa->mcs;
+
+	int i;
+
+	/* Build the MM_STA_ADD_REQ message */
+	req = rwnx_msg_zalloc(ME_STA_ADD_REQ, TASK_ME, DRV_TASK_ID,
+			      sizeof(struct me_sta_add_req));
+	if (!req)
+		return -ENOMEM;
+
+	/* Set parameters for the MM_STA_ADD_REQ message */
+	memcpy(&req->mac_addr.array[0], mac, ETH_ALEN);
+
+	req->rate_set.length = link_sta_params->supported_rates_len;
+	for (i = 0; i < link_sta_params->supported_rates_len; i++)
+		req->rate_set.array[i] = link_sta_params->supported_rates[i];
+
+	req->flags = 0;
+	if (link_sta_params->ht_capa) {
+		const struct ieee80211_ht_cap *ht_capa = link_sta_params->ht_capa;
+
+		req->flags |= STA_HT_CAPA;
+		req->ht_cap.ht_capa_info = ht_capa->cap_info;
+		req->ht_cap.a_mpdu_param = ht_capa->ampdu_params_info;
+		for (i = 0; i < sizeof(ht_capa->mcs); i++)
+			req->ht_cap.mcs_rate[i] = ht_mcs[i];
+		req->ht_cap.ht_extended_capa = ht_capa->extended_ht_cap_info;
+		req->ht_cap.tx_beamforming_capa = ht_capa->tx_BF_cap_info;
+		req->ht_cap.asel_capa = ht_capa->antenna_selection_info;
+	}
+
+	if (link_sta_params->vht_capa) {
+		const struct ieee80211_vht_cap *vht_capa = link_sta_params->vht_capa;
+
+		req->flags |= STA_VHT_CAPA;
+		req->vht_cap.vht_capa_info = vht_capa->vht_cap_info;
+		req->vht_cap.rx_highest = vht_capa->supp_mcs.rx_highest;
+		req->vht_cap.rx_mcs_map = vht_capa->supp_mcs.rx_mcs_map;
+		req->vht_cap.tx_highest = vht_capa->supp_mcs.tx_highest;
+		req->vht_cap.tx_mcs_map = vht_capa->supp_mcs.tx_mcs_map;
+	}
+
+	if (link_sta_params->he_capa) {
+		const struct ieee80211_he_cap_elem *he_capa = link_sta_params->he_capa;
+		struct ieee80211_he_mcs_nss_supp *mcs_nss_supp =
+			(struct ieee80211_he_mcs_nss_supp *)(he_capa + 1);
+
+		req->flags |= STA_HE_CAPA;
+		for (i = 0; i < ARRAY_SIZE(he_capa->mac_cap_info); i++)
+			req->he_cap.mac_cap_info[i] = he_capa->mac_cap_info[i];
+		for (i = 0; i < ARRAY_SIZE(he_capa->phy_cap_info); i++)
+			req->he_cap.phy_cap_info[i] = he_capa->phy_cap_info[i];
+		req->he_cap.mcs_supp.rx_mcs_80 = mcs_nss_supp->rx_mcs_80;
+		req->he_cap.mcs_supp.tx_mcs_80 = mcs_nss_supp->tx_mcs_80;
+		req->he_cap.mcs_supp.rx_mcs_160 = mcs_nss_supp->rx_mcs_160;
+		req->he_cap.mcs_supp.tx_mcs_160 = mcs_nss_supp->tx_mcs_160;
+		req->he_cap.mcs_supp.rx_mcs_80p80 = mcs_nss_supp->rx_mcs_80p80;
+		req->he_cap.mcs_supp.tx_mcs_80p80 = mcs_nss_supp->tx_mcs_80p80;
+	}
+
+	if (params->sta_flags_set & BIT(NL80211_STA_FLAG_WME))
+		req->flags |= STA_QOS_CAPA;
+
+	if (params->sta_flags_set & BIT(NL80211_STA_FLAG_MFP))
+		req->flags |= STA_MFP_CAPA;
+
+	if (link_sta_params->opmode_notif_used) {
+		req->flags |= STA_OPMOD_NOTIF;
+		req->opmode = link_sta_params->opmode_notif_used;
+	}
+
+	req->aid = cpu_to_le16(params->aid);
+	req->uapsd_queues = params->uapsd_queues;
+	req->max_sp_len = params->max_sp * 2;
+	req->vif_idx = inst_nbr;
+
+	if (params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) {
+		req->tdls_sta = true;
+		if ((params->ext_capab[3] & WLAN_EXT_CAPA4_TDLS_CHAN_SWITCH) &&
+		    !rwnx_vif->tdls_chsw_prohibited)
+			req->tdls_chsw_allowed = true;
+		if (rwnx_vif->tdls_status == TDLS_SETUP_RSP_TX)
+			req->tdls_sta_initiator = true;
+	}
+
+	/* Send the ME_STA_ADD_REQ message to LMAC FW */
+	return rwnx_send_msg(rwnx_hw, req, 1, ME_STA_ADD_CFM, cfm);
+}
+
+int rwnx_send_me_sta_del(struct rwnx_hw *rwnx_hw, u8 sta_idx, bool tdls_sta)
+{
+	struct me_sta_del_req *req;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	/* Build the MM_STA_DEL_REQ message */
+	req = rwnx_msg_zalloc(ME_STA_DEL_REQ, TASK_ME, DRV_TASK_ID,
+			      sizeof(struct me_sta_del_req));
+	if (!req)
+		return -ENOMEM;
+
+	/* Set parameters for the MM_STA_DEL_REQ message */
+	req->sta_idx = sta_idx;
+	req->tdls_sta = tdls_sta;
+
+	/* Send the ME_STA_DEL_REQ message to LMAC FW */
+	return rwnx_send_msg(rwnx_hw, req, 1, ME_STA_DEL_CFM, NULL);
+}
+
+int rwnx_send_me_traffic_ind(struct rwnx_hw *rwnx_hw, u8 sta_idx, bool uapsd,
+			     u8 tx_status)
+{
+	struct me_traffic_ind_req *req;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	/* Build the ME_UTRAFFIC_IND_REQ message */
+	req = rwnx_msg_zalloc(ME_TRAFFIC_IND_REQ, TASK_ME, DRV_TASK_ID,
+			      sizeof(struct me_traffic_ind_req));
+	if (!req)
+		return -ENOMEM;
+
+	/* Set parameters for the ME_TRAFFIC_IND_REQ message */
+	req->sta_idx = sta_idx;
+	req->tx_avail = tx_status;
+	req->uapsd = uapsd;
+
+	/* Send the ME_TRAFFIC_IND_REQ to UMAC FW */
+	return rwnx_send_msg(rwnx_hw, req, 1, ME_TRAFFIC_IND_CFM, NULL);
+}
+
+int rwnx_send_me_rc_stats(struct rwnx_hw *rwnx_hw, u8 sta_idx,
+			  struct me_rc_stats_cfm *cfm)
+{
+	struct me_rc_stats_req *req;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	/* Build the ME_RC_STATS_REQ message */
+	req = rwnx_msg_zalloc(ME_RC_STATS_REQ, TASK_ME, DRV_TASK_ID,
+			      sizeof(struct me_rc_stats_req));
+	if (!req)
+		return -ENOMEM;
+
+	/* Set parameters for the ME_RC_STATS_REQ message */
+	req->sta_idx = sta_idx;
+
+	/* Send the ME_RC_STATS_REQ message to LMAC FW */
+	return rwnx_send_msg(rwnx_hw, req, 1, ME_RC_STATS_CFM, cfm);
+}
+
+int rwnx_send_me_rc_set_rate(struct rwnx_hw *rwnx_hw, u8 sta_idx, u16 rate_cfg)
+{
+	struct me_rc_set_rate_req *req;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	/* Build the ME_RC_SET_RATE_REQ message */
+	req = rwnx_msg_zalloc(ME_RC_SET_RATE_REQ, TASK_ME, DRV_TASK_ID,
+			      sizeof(struct me_rc_set_rate_req));
+	if (!req)
+		return -ENOMEM;
+
+	/* Set parameters for the ME_RC_SET_RATE_REQ message */
+	req->sta_idx = sta_idx;
+	req->fixed_rate_cfg = rate_cfg;
+
+	/* Send the ME_RC_SET_RATE_REQ message to FW */
+	return rwnx_send_msg(rwnx_hw, req, 0, 0, NULL);
+}
+
+int rwnx_send_me_set_ps_mode(struct rwnx_hw *rwnx_hw, u8 ps_mode)
+{
+	struct me_set_ps_mode_req *req;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	/* Build the ME_SET_PS_MODE_REQ message */
+	req = rwnx_msg_zalloc(ME_SET_PS_MODE_REQ, TASK_ME, DRV_TASK_ID,
+			      sizeof(struct me_set_ps_mode_req));
+	if (!req)
+		return -ENOMEM;
+
+	/* Set parameters for the ME_SET_PS_MODE_REQ message */
+	req->ps_state = ps_mode;
+
+	/* Send the ME_SET_PS_MODE_REQ message to FW */
+	return rwnx_send_msg(rwnx_hw, req, 1, ME_SET_PS_MODE_CFM, NULL);
+}
+
+int rwnx_send_me_set_lp_level(struct rwnx_hw *rwnx_hw, u8 lp_level, u8 disable_filter)
+{
+	struct me_set_lp_level_req *req;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+	AICWFDBG(LOGDEBUG, "lp_lvl: %d %d\n", lp_level, disable_filter);
+	/* Build the ME_SET_LP_LEVEL_REQ message */
+	req = rwnx_msg_zalloc(ME_SET_LP_LEVEL_REQ, TASK_ME, DRV_TASK_ID,
+			      sizeof(struct me_set_lp_level_req));
+	if (!req)
+		return -ENOMEM;
+
+	/* Set parameters for the ME_SET_LP_LEVEL_REQ message */
+	req->lp_level = lp_level;
+	req->disable_filter = disable_filter;
+
+	/* Send the ME_SET_LP_LEVEL_REQ message to FW */
+	return rwnx_send_msg(rwnx_hw, req, 1, ME_SET_LP_LEVEL_CFM, NULL);
+}
+
+int rwnx_send_sm_connect_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif,
+			     struct cfg80211_connect_params *sme,
+			     struct sm_connect_cfm *cfm)
+{
+	struct sm_connect_req *req;
+	int i;
+	u32 flags = 0;
+	bool gval = false;
+	bool pval = false;
+
+	rwnx_vif->wep_enabled = false;
+	rwnx_vif->wep_auth_err = false;
+	rwnx_vif->last_auth_type = 0;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	/* Build the SM_CONNECT_REQ message */
+	req = rwnx_msg_zalloc(SM_CONNECT_REQ, TASK_SM, DRV_TASK_ID,
+			      sizeof(struct sm_connect_req));
+	if (!req)
+		return -ENOMEM;
+
+	if (sme->crypto.cipher_group == WLAN_CIPHER_SUITE_WEP40 ||
+	    sme->crypto.cipher_group == WLAN_CIPHER_SUITE_WEP104)
+		gval = true;
+
+	if (sme->crypto.n_ciphers_pairwise &&
+	    (sme->crypto.ciphers_pairwise[0] == WLAN_CIPHER_SUITE_WEP40 ||
+	     sme->crypto.ciphers_pairwise[0] == WLAN_CIPHER_SUITE_WEP104))
+		pval = true;
+
+	/* Set parameters for the SM_CONNECT_REQ message */
+	if (sme->crypto.n_ciphers_pairwise &&
+	    (sme->crypto.ciphers_pairwise[0] == WLAN_CIPHER_SUITE_WEP40 ||
+	     sme->crypto.ciphers_pairwise[0] == WLAN_CIPHER_SUITE_TKIP ||
+	     sme->crypto.ciphers_pairwise[0] == WLAN_CIPHER_SUITE_WEP104))
+		flags |= DISABLE_HT;
+
+	if (sme->crypto.control_port)
+		flags |= CONTROL_PORT_HOST;
+
+	if (sme->crypto.control_port_no_encrypt)
+		flags |= CONTROL_PORT_NO_ENC;
+
+	if (use_pairwise_key(&sme->crypto))
+		flags |= WPA_WPA2_IN_USE;
+
+	if (sme->mfp == NL80211_MFP_REQUIRED)
+		flags |= MFP_IN_USE;
+
+	if (rwnx_vif->sta.ap)
+		flags |= REASSOCIATION;
+
+	req->ctrl_port_ethertype = sme->crypto.control_port_ethertype;
+
+	if (sme->bssid)
+		memcpy(&req->bssid, sme->bssid, ETH_ALEN);
+	else
+		req->bssid = mac_addr_bcst;
+#ifdef CONFIG_AIC8800_AUTO_CUSTREG
+	if (rwnx_hw->ccode.auto_set) {
+		memcpy(&rwnx_hw->ccode.bssid, &req->bssid, ETH_ALEN);
+		atomic_set(&rwnx_hw->ccode.link_scan, 1);
+	}
+#endif
+	req->vif_idx = rwnx_vif->vif_index;
+	if (sme->channel) {
+		req->chan.band = sme->channel->band;
+		req->chan.freq = sme->channel->center_freq;
+		req->chan.flags = get_chan_flags(sme->channel->flags);
+	} else {
+		req->chan.freq = (u16)-1;
+	}
+	for (i = 0; i < sme->ssid_len; i++)
+		req->ssid.array[i] = sme->ssid[i];
+	req->ssid.length = sme->ssid_len;
+	req->flags = flags;
+	if (WARN_ON(sme->ie_len > sizeof(req->ie_buf)))
+		goto invalid_param;
+	if (sme->ie_len)
+		memcpy(req->ie_buf, sme->ie, sme->ie_len);
+	req->ie_len = sme->ie_len;
+	req->listen_interval = rwnx_mod_params.listen_itv;
+	req->dont_wait_bcmc = !rwnx_mod_params.listen_bcmc;
+
+	/* Set auth_type */
+	if (sme->auth_type == NL80211_AUTHTYPE_AUTOMATIC)
+		req->auth_type = WLAN_AUTH_OPEN;
+	else if (sme->auth_type == NL80211_AUTHTYPE_OPEN_SYSTEM)
+		req->auth_type = WLAN_AUTH_OPEN;
+	else if (sme->auth_type == NL80211_AUTHTYPE_SHARED_KEY)
+		req->auth_type = WLAN_AUTH_SHARED_KEY;
+	else if (sme->auth_type == NL80211_AUTHTYPE_FT)
+		req->auth_type = WLAN_AUTH_FT;
+	else if (sme->auth_type == NL80211_AUTHTYPE_SAE)
+		req->auth_type = WLAN_AUTH_SAE;
+	else
+		goto invalid_param;
+
+	rwnx_vif->sta.auth_type = sme->auth_type;
+
+	/* Set UAPSD queues */
+	req->uapsd_queues = rwnx_mod_params.uapsd_queues;
+
+	rwnx_vif->wep_enabled = pval & gval;
+
+	if (rwnx_vif->wep_enabled)
+		rwnx_vif->last_auth_type = sme->auth_type;
+
+	rwnx_vif->sta.ssid_len = (int)sme->ssid_len;
+	memset(rwnx_vif->sta.ssid, 0, rwnx_vif->sta.ssid_len + 1);
+	memcpy(rwnx_vif->sta.ssid, sme->ssid, rwnx_vif->sta.ssid_len);
+	if (sme->bssid)
+		memcpy(rwnx_vif->sta.bssid, sme->bssid, ETH_ALEN);
+
+	AICWFDBG(LOGDEBUG,
+		 "AICWF %s drv_vif_index:%d connect to %s(%d) channel:%d auth_type:%d\r\n",
+		 __func__, rwnx_vif->drv_vif_index, rwnx_vif->sta.ssid,
+		 rwnx_vif->sta.ssid_len, req->chan.freq, req->auth_type);
+
+	/* Send the SM_CONNECT_REQ message to LMAC FW */
+	return rwnx_send_msg(rwnx_hw, req, 1, SM_CONNECT_CFM, cfm);
+
+invalid_param:
+	rwnx_msg_free(rwnx_hw, req);
+	return -EINVAL;
+}
+
+int rwnx_send_sm_disconnect_req(struct rwnx_hw *rwnx_hw,
+				struct rwnx_vif *rwnx_vif, u16 reason)
+{
+	struct sm_disconnect_req *req;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	/* Build the SM_DISCONNECT_REQ message */
+	req = rwnx_msg_zalloc(SM_DISCONNECT_REQ, TASK_SM, DRV_TASK_ID,
+			      sizeof(struct sm_disconnect_req));
+	if (!req)
+		return -ENOMEM;
+
+	/* Set parameters for the SM_DISCONNECT_REQ message */
+	req->reason_code = reason;
+	req->vif_idx = rwnx_vif->vif_index;
+
+	/* Send the SM_DISCONNECT_REQ message to LMAC FW */
+	return rwnx_send_msg(rwnx_hw, req, 1, SM_DISCONNECT_CFM, NULL);
+}
+
+int rwnx_send_sm_external_auth_required_rsp(struct rwnx_hw *rwnx_hw,
+					    struct rwnx_vif *rwnx_vif,
+					    u16 status)
+{
+	struct sm_external_auth_required_rsp *rsp;
+
+	/* Build the SM_EXTERNAL_AUTH_CFM message */
+	rsp = rwnx_msg_zalloc(SM_EXTERNAL_AUTH_REQUIRED_RSP, TASK_SM, DRV_TASK_ID,
+			      sizeof(struct sm_external_auth_required_rsp));
+	if (!rsp)
+		return -ENOMEM;
+
+	rsp->status = status;
+	rsp->vif_idx = rwnx_vif->vif_index;
+
+	/* send the SM_EXTERNAL_AUTH_REQUIRED_RSP message UMAC FW */
+	return rwnx_send_msg(rwnx_hw, rsp, 1, SM_EXTERNAL_AUTH_REQUIRED_RSP_CFM,
+			     NULL);
+}
+
+int rwnx_send_apm_start_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif,
+			    struct cfg80211_ap_settings *settings,
+			    struct apm_start_cfm *cfm,
+			    struct rwnx_ipc_elem_var *elem)
+{
+	struct apm_start_req *req;
+	struct rwnx_bcn *bcn = &vif->ap.bcn;
+	u8 *buf;
+	u32 flags = 0;
+	const u8 *rate_ie;
+	u8 rate_len = 0;
+	int var_offset = offsetof(struct ieee80211_mgmt, u.beacon.variable);
+	const u8 *var_pos;
+	int len, i;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	/* Build the APM_START_REQ message */
+	req = rwnx_msg_zalloc(APM_START_REQ, TASK_APM, DRV_TASK_ID,
+			      sizeof(struct apm_start_req));
+	if (!req)
+		return -ENOMEM;
+
+	// Build the beacon
+	bcn->dtim = (u8)settings->dtim_period;
+	buf = rwnx_build_bcn(bcn, &settings->beacon);
+	if (!buf) {
+		rwnx_msg_free(rwnx_hw, req);
+		return -ENOMEM;
+	}
+
+	// Retrieve the basic rate set from the beacon buffer
+	len = bcn->len - var_offset;
+	var_pos = buf + var_offset;
+
+// Assume that rate higher that 54 Mbps are BSS membership
+
+	rate_ie = cfg80211_find_ie(WLAN_EID_SUPP_RATES, var_pos, len);
+	if (rate_ie) {
+		const u8 *rates = rate_ie + 2;
+		u8 rate1 = 0;
+		bool is_basic1;
+
+		for (i = 0; i < rate_ie[1] && rate_len < MAC_RATESET_LEN; i++) {
+			rate1 = rates[i];
+			is_basic1 = ((rate1 & 0x80) && ((rate1 & ~0x80) <= (54 * 2)));
+			if (is_basic1)
+				req->basic_rates.array[rate_len++] = rate1;
+		}
+	}
+	rate_ie = cfg80211_find_ie(WLAN_EID_EXT_SUPP_RATES, var_pos, len);
+	if (rate_ie) {
+		const u8 *rates = rate_ie + 2;
+		u8 rate2 = 0;
+		bool is_basic2;
+
+		for (i = 0; i < rate_ie[1] && rate_len < MAC_RATESET_LEN; i++) {
+			rate2 = rates[i];
+			is_basic2 = ((rate2 & 0x80) && ((rate2 & ~0x80) <= (54 * 2)));
+			if (is_basic2)
+				req->basic_rates.array[rate_len++] = rate2;
+		}
+	}
+	req->basic_rates.length = rate_len;
+
+	rwnx_send_bcn(rwnx_hw, buf, vif->vif_index, bcn->len);
+
+	/* Set parameters for the APM_START_REQ message */
+	req->vif_idx = vif->vif_index;
+	req->bcn_addr = elem->dma_addr;
+	req->bcn_len = bcn->len;
+	req->tim_oft = bcn->head_len;
+	req->tim_len = bcn->tim_len;
+	req->chan.band = settings->chandef.chan->band;
+	req->chan.freq = settings->chandef.chan->center_freq;
+	req->chan.flags = 0;
+	req->chan.tx_power = chan_to_fw_pwr(settings->chandef.chan->max_power);
+	req->center_freq1 = settings->chandef.center_freq1;
+	req->center_freq2 = settings->chandef.center_freq2;
+	req->ch_width = bw2chnl[settings->chandef.width];
+	req->bcn_int = settings->beacon_interval;
+
+	if (settings->crypto.control_port)
+		flags |= CONTROL_PORT_HOST;
+
+	if (settings->crypto.control_port_no_encrypt)
+		flags |= CONTROL_PORT_NO_ENC;
+
+	if (use_pairwise_key(&settings->crypto))
+		flags |= WPA_WPA2_IN_USE;
+
+	if (settings->crypto.control_port_ethertype)
+		req->ctrl_port_ethertype = settings->crypto.control_port_ethertype;
+	else
+		req->ctrl_port_ethertype = cpu_to_be16(ETH_P_PAE);
+	req->flags = flags;
+
+	/* Send the APM_START_REQ message to LMAC FW */
+	return rwnx_send_msg(rwnx_hw, req, 1, APM_START_CFM, cfm);
+}
+
+int rwnx_send_apm_stop_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif)
+{
+	struct apm_stop_req *req;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	/* Build the APM_STOP_REQ message */
+	req = rwnx_msg_zalloc(APM_STOP_REQ, TASK_APM, DRV_TASK_ID,
+			      sizeof(struct apm_stop_req));
+	if (!req)
+		return -ENOMEM;
+
+	/* Set parameters for the APM_STOP_REQ message */
+	req->vif_idx = vif->vif_index;
+
+	/* Send the APM_STOP_REQ message to LMAC FW */
+	return rwnx_send_msg(rwnx_hw, req, 1, APM_STOP_CFM, NULL);
+}
+
+#define P2P_WILDCARD_SSID     "DIRECT-"
+#define P2P_WILDCARD_SSID_LEN (sizeof(P2P_WILDCARD_SSID) - 1)
+
+#ifdef CONFIG_AIC8800_SET_VENDOR_EXTENSION_IE
+u8 vendor_extension_data[256];
+u8 vendor_extension_len;
+
+static void rwnx_insert_vendor_extension_data(struct scanu_vendor_ie_req *ie_req)
+{
+	u8 temp_ie[256];
+	u8 vendor_extension_subelement[3] = {0x00, 0x37, 0x2A};
+	u8 vendor_extension_id[2] = {0x10, 0x49};
+	int index = 0;
+	int vendor_extension_subelement_len = 0;
+
+	memset(temp_ie, 0, 256);
+
+	// find vendor_extension_subelement
+	for (index = 0; index < ie_req->add_ie_len; index++) {
+		if (ie_req->ie[index] == vendor_extension_id[0]) {
+			index++;
+			if (index == ie_req->add_ie_len)
+				return;
+
+			if (ie_req->ie[index] == vendor_extension_id[1] &&
+			    ie_req->ie[index + 3] == vendor_extension_subelement[0] &&
+			    ie_req->ie[index + 4] == vendor_extension_subelement[1] &&
+			    ie_req->ie[index + 5] == vendor_extension_subelement[2]) {
+				index = index + 2;
+				vendor_extension_subelement_len = ie_req->ie[index];
+				AICWFDBG(LOGDEBUG,
+					 "AICWF %s find vendor_extension_subelement,index:%d len:%d\r\n",
+					 __func__, index, ie_req->ie[index]);
+				break;
+			}
+		}
+	}
+	index = index + vendor_extension_subelement_len;
+
+	// insert vendor extension
+	memcpy(&temp_ie[0], ie_req->ie, index + 1);
+	memcpy(&temp_ie[index + 1], vendor_extension_data,
+	       vendor_extension_len /*sizeof(vendor_extension_data) */);
+	memcpy(&temp_ie[index + 1 +
+	       vendor_extension_len /*sizeof(vendor_extension_data) */],
+	       &ie_req->ie[index + 1], ie_req->add_ie_len - index);
+
+	memcpy(ie_req->ie, temp_ie,
+	       ie_req->add_ie_len +
+	       vendor_extension_len /*sizeof(vendor_extension_data) */);
+	ie_req->add_ie_len =
+		ie_req->add_ie_len +
+		vendor_extension_len /*sizeof(vendor_extension_data) */;
+	ie_req->ie[1] =
+		ie_req->ie[1] + vendor_extension_len /*sizeof(vendor_extension_data) */;
+
+	// rwnx_data_dump((char*)__func__, (void*)ie_req->ie,
+	// ie_req->add_ie_len);
+}
+#endif // CONFIG_AIC8800_SET_VENDOR_EXTENSION_IE
+
+int rwnx_send_scanu_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif,
+			struct cfg80211_scan_request *param)
+{
+	struct scanu_start_req *req = NULL;
+	struct scanu_vendor_ie_req *ie_req = NULL;
+	struct mm_add_if_cfm add_if_cfm;
+	int i;
+	u8 chan_flags = 0;
+	int err;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	/* Build the SCANU_START_REQ message */
+	req = rwnx_msg_zalloc(SCANU_START_REQ, TASK_SCANU, DRV_TASK_ID,
+			      sizeof(struct scanu_start_req));
+	if (!req)
+		return -ENOMEM;
+
+	rwnx_hw->scanning = 1;
+	/* Set parameters */
+	req->vif_idx = rwnx_vif->vif_index;
+	req->chan_cnt = (u8)min_t(int, SCAN_CHANNEL_MAX, param->n_channels);
+
+#ifdef CONFIG_AIC8800_AUTO_CUSTREG
+	if (rwnx_hw->ccode.ccode_set || rwnx_hw->ccode.ccode_cnt >= MAX_SEARCH_CNT)
+		req->ssid_cnt = (u8)min_t(int, SCAN_SSID_MAX, param->n_ssids);
+	else
+		req->ssid_cnt = (u8)min_t(int, 0, param->n_ssids);
+#else
+	req->ssid_cnt = (u8)min_t(int, SCAN_SSID_MAX, param->n_ssids);
+#endif
+
+	req->bssid = mac_addr_bcst;
+	req->no_cck = param->no_cck;
+	if (param->duration_mandatory)
+		// req->duration = ieee80211_tu_to_usec(param->duration);
+		req->duration = 0;
+
+#ifdef AIC8800_RADAR_OR_IR_DETECT
+	if (req->ssid_cnt == 0)
+		chan_flags |= CHAN_NO_IR;
+#endif
+	for (i = 0; i < req->ssid_cnt; i++) {
+		int j;
+
+		for (j = 0; j < param->ssids[i].ssid_len; j++)
+			req->ssid[i].array[j] = param->ssids[i].ssid[j];
+		req->ssid[i].length = param->ssids[i].ssid_len;
+
+		if (!memcmp(P2P_WILDCARD_SSID, param->ssids[i].ssid,
+			    P2P_WILDCARD_SSID_LEN)) {
+			AICWFDBG(LOGINFO, "p2p scanu:%d,%d,%d\n", rwnx_vif->vif_index,
+				 rwnx_vif->is_p2p_vif, rwnx_hw->is_p2p_alive);
+#ifdef CONFIG_AIC8800_USE_P2P0
+			if (rwnx_vif->is_p2p_vif && !rwnx_hw->is_p2p_alive) {
+#else
+			if (rwnx_vif == rwnx_hw->p2p_dev_vif && !rwnx_vif->up) {
+#endif
+				err = rwnx_send_add_if(rwnx_hw, rwnx_vif->wdev.address,
+						       RWNX_VIF_TYPE(rwnx_vif), false,
+						       &add_if_cfm);
+				if (err)
+					goto error;
+
+				if (add_if_cfm.status != 0)
+					return -EIO;
+
+				/* Save the index retrieved from LMAC */
+				spin_lock_bh(&rwnx_hw->cb_lock);
+				rwnx_vif->vif_index = add_if_cfm.inst_nbr;
+				rwnx_vif->up = true;
+				rwnx_hw->vif_started++;
+				rwnx_hw->vif_table[add_if_cfm.inst_nbr] = rwnx_vif;
+				spin_unlock_bh(&rwnx_hw->cb_lock);
+			}
+			rwnx_hw->is_p2p_alive = 1;
+#ifndef CONFIG_AIC8800_USE_P2P0
+			mod_timer(&rwnx_hw->p2p_alive_timer,
+				  jiffies + msecs_to_jiffies(1000));
+			atomic_set(&rwnx_hw->p2p_alive_timer_count, 0);
+#endif
+			AICWFDBG(LOGINFO, "p2p scan start\n");
+		}
+	}
+
+	if (param->ie) {
+		ie_req = rwnx_msg_zalloc(SCANU_VENDOR_IE_REQ, TASK_SCANU, DRV_TASK_ID,
+					 sizeof(struct scanu_vendor_ie_req));
+		if (!ie_req)
+			return -ENOMEM;
+
+		ie_req->add_ie_len = param->ie_len;
+		ie_req->vif_idx = rwnx_vif->vif_index;
+		memcpy(ie_req->ie, param->ie, param->ie_len);
+#ifdef CONFIG_AIC8800_SET_VENDOR_EXTENSION_IE
+		rwnx_insert_vendor_extension_data(ie_req);
+#endif // CONFIG_AIC8800_SET_VENDOR_EXTENSION_IE
+		req->add_ie_len = 0;
+		req->add_ies = 0;
+
+		err = rwnx_send_msg(rwnx_hw, ie_req, 1, SCANU_VENDOR_IE_CFM, NULL);
+		if (err)
+			goto error;
+	} else {
+		req->add_ie_len = 0;
+		req->add_ies = 0;
+	}
+
+	for (i = 0; i < req->chan_cnt; i++) {
+		struct ieee80211_channel *chan = param->channels[i];
+
+		AICWFDBG(LOGDEBUG, "scan channel:%d(%d) \r\n",
+			 ieee80211_frequency_to_channel(chan->center_freq),
+			 chan->center_freq);
+		req->chan[i].band = chan->band;
+		req->chan[i].freq = chan->center_freq;
+		req->chan[i].flags = chan_flags | get_chan_flags(chan->flags);
+		req->chan[i].tx_power = chan_to_fw_pwr(chan->max_reg_power);
+	}
+
+	/* Send the SCANU_START_REQ message to LMAC FW */
+	return rwnx_send_msg(rwnx_hw, req, 1, SCANU_START_CFM_ADDTIONAL, NULL);
+error:
+	if (req)
+		rwnx_msg_free(rwnx_hw, req);
+	if (ie_req)
+		rwnx_msg_free(rwnx_hw, ie_req);
+	return -ENOMEM;
+}
+
+int rwnx_send_scanu_cancel_req(struct rwnx_hw *rwnx_hw,
+			       struct scan_cancel_cfm *cfm)
+{
+	struct scan_cancel_req *req;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	/* Build the SCAN_CANCEL_REQ message */
+	req = rwnx_msg_zalloc(SCANU_CANCEL_REQ, TASK_SCANU, DRV_TASK_ID,
+			      sizeof(struct scan_cancel_req));
+	if (!req)
+		return -ENOMEM;
+
+	/* Send the SCAN_CANCEL_REQ message to LMAC FW */
+	return rwnx_send_msg(rwnx_hw, req, 1, SCANU_CANCEL_CFM, cfm);
+}
+
+int rwnx_send_apm_start_cac_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif,
+				struct cfg80211_chan_def *chandef,
+				struct apm_start_cac_cfm *cfm)
+{
+	struct apm_start_cac_req *req;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	/* Build the APM_START_CAC_REQ message */
+	req = rwnx_msg_zalloc(APM_START_CAC_REQ, TASK_APM, DRV_TASK_ID,
+			      sizeof(struct apm_start_cac_req));
+	if (!req)
+		return -ENOMEM;
+
+	/* Set parameters for the APM_START_CAC_REQ message */
+	req->vif_idx = vif->vif_index;
+	req->chan.band = chandef->chan->band;
+	req->chan.freq = chandef->chan->center_freq;
+	req->chan.flags = 0;
+	req->center_freq1 = chandef->center_freq1;
+	req->center_freq2 = chandef->center_freq2;
+	req->ch_width = bw2chnl[chandef->width];
+
+	/* Send the APM_START_CAC_REQ message to LMAC FW */
+	return rwnx_send_msg(rwnx_hw, req, 1, APM_START_CAC_CFM, cfm);
+}
+
+int rwnx_send_apm_stop_cac_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif)
+{
+	struct apm_stop_cac_req *req;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	/* Build the APM_STOP_CAC_REQ message */
+	req = rwnx_msg_zalloc(APM_STOP_CAC_REQ, TASK_APM, DRV_TASK_ID,
+			      sizeof(struct apm_stop_cac_req));
+	if (!req)
+		return -ENOMEM;
+
+	/* Set parameters for the APM_STOP_CAC_REQ message */
+	req->vif_idx = vif->vif_index;
+
+	/* Send the APM_STOP_CAC_REQ message to LMAC FW */
+	return rwnx_send_msg(rwnx_hw, req, 1, APM_STOP_CAC_CFM, NULL);
+}
+
+int rwnx_send_mesh_start_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif,
+			     const struct mesh_config *conf,
+			     const struct mesh_setup *setup,
+			     struct mesh_start_cfm *cfm)
+{
+	// Message to send
+	struct mesh_start_req *req;
+	// Supported basic rates
+	struct ieee80211_supported_band *band_2ghz =
+		rwnx_hw->wiphy->bands[NL80211_BAND_2GHZ];
+	/* Counter */
+	int i;
+	/* Return status */
+	int status;
+	/* DMA Address to be unmapped after confirmation reception */
+	u32 dma_addr = 0;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	/* Build the MESH_START_REQ message */
+	req = rwnx_msg_zalloc(MESH_START_REQ, TASK_MESH, DRV_TASK_ID,
+			      sizeof(struct mesh_start_req));
+	if (!req)
+		return -ENOMEM;
+
+	req->vif_index = vif->vif_index;
+	req->bcn_int = setup->beacon_interval;
+	req->dtim_period = setup->dtim_period;
+	req->mesh_id_len = setup->mesh_id_len;
+
+	for (i = 0; i < setup->mesh_id_len; i++)
+		req->mesh_id[i] = *(setup->mesh_id + i);
+
+	req->user_mpm = setup->user_mpm;
+	req->is_auth = setup->is_authenticated;
+	req->auth_id = setup->auth_id;
+	req->ie_len = setup->ie_len;
+
+	if (setup->ie_len) {
+		/*
+		 * Need to provide a Virtual Address to the MAC so that it can
+		 * download the additional information elements.
+		 */
+		req->ie_addr = dma_map_single(rwnx_hw->dev, (void *)setup->ie,
+					      setup->ie_len, DMA_FROM_DEVICE);
+
+		/* Check DMA mapping result */
+		if (dma_mapping_error(rwnx_hw->dev, req->ie_addr)) {
+			AICWFDBG(LOGDEBUG, KERN_CRIT
+				 "AICWF %s - DMA Mapping error on additional IEs\n",
+				 __func__);
+
+			/* Consider there is no Additional IEs */
+			req->ie_len = 0;
+		} else {
+			/* Store DMA Address so that we can unmap the memory section
+			 * once MESH_START_CFM is received
+			 */
+			dma_addr = req->ie_addr;
+		}
+	}
+
+	/* Provide rate information */
+	req->basic_rates.length = 0;
+	for (i = 0; i < band_2ghz->n_bitrates; i++) {
+		u16 rate = band_2ghz->bitrates[i].bitrate;
+
+		/* Read value is in units of 100 Kbps, provided value is in units
+		 * of 1Mbps, and multiplied by 2 so that 5.5 becomes 11
+		 */
+		rate = (rate << 1) / 10;
+
+		if (setup->basic_rates & CO_BIT(i))
+			rate |= 0x80;
+
+		req->basic_rates.array[i] = (u8)rate;
+		req->basic_rates.length++;
+	}
+
+	/* Provide channel information */
+	req->chan.band = setup->chandef.chan->band;
+	req->chan.freq = setup->chandef.chan->center_freq;
+	req->chan.flags = 0;
+	req->chan.tx_power = chan_to_fw_pwr(setup->chandef.chan->max_power);
+	req->center_freq1 = setup->chandef.center_freq1;
+	req->center_freq2 = setup->chandef.center_freq2;
+	req->ch_width = bw2chnl[setup->chandef.width];
+
+	/* Send the MESH_START_REQ message to UMAC FW */
+	status = rwnx_send_msg(rwnx_hw, req, 1, MESH_START_CFM, cfm);
+
+	/* Unmap DMA area */
+	if (setup->ie_len)
+		dma_unmap_single(rwnx_hw->dev, dma_addr, setup->ie_len, DMA_TO_DEVICE);
+
+	/* Return the status */
+	return status;
+}
+
+int rwnx_send_mesh_stop_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif,
+			    struct mesh_stop_cfm *cfm)
+{
+	// Message to send
+	struct mesh_stop_req *req;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	/* Build the MESH_STOP_REQ message */
+	req = rwnx_msg_zalloc(MESH_STOP_REQ, TASK_MESH, DRV_TASK_ID,
+			      sizeof(struct mesh_stop_req));
+	if (!req)
+		return -ENOMEM;
+
+	req->vif_idx = vif->vif_index;
+
+	/* Send the MESH_STOP_REQ message to UMAC FW */
+	return rwnx_send_msg(rwnx_hw, req, 1, MESH_STOP_CFM, cfm);
+}
+
+int rwnx_send_mesh_update_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif,
+			      u32 mask, const struct mesh_config *p_mconf,
+			      struct mesh_update_cfm *cfm)
+{
+	// Message to send
+	struct mesh_update_req *req;
+	// Keep only bit for fields which can be updated
+	u32 supp_mask = (mask << 1) & (CO_BIT(NL80211_MESHCONF_GATE_ANNOUNCEMENTS) |
+			CO_BIT(NL80211_MESHCONF_HWMP_ROOTMODE) |
+			CO_BIT(NL80211_MESHCONF_FORWARDING) |
+			CO_BIT(NL80211_MESHCONF_POWER_MODE));
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	if (!supp_mask)
+		return -ENOENT;
+
+	/* Build the MESH_UPDATE_REQ message */
+	req = rwnx_msg_zalloc(MESH_UPDATE_REQ, TASK_MESH, DRV_TASK_ID,
+			      sizeof(struct mesh_update_req));
+
+	if (!req)
+		return -ENOMEM;
+
+	req->vif_idx = vif->vif_index;
+
+	if (supp_mask & CO_BIT(NL80211_MESHCONF_GATE_ANNOUNCEMENTS)) {
+		req->flags |= CO_BIT(MESH_UPDATE_FLAGS_GATE_MODE_BIT);
+		req->gate_announ = p_mconf->dot11MeshGateAnnouncementProtocol;
+	}
+
+	if (supp_mask & CO_BIT(NL80211_MESHCONF_HWMP_ROOTMODE)) {
+		req->flags |= CO_BIT(MESH_UPDATE_FLAGS_ROOT_MODE_BIT);
+		req->root_mode = p_mconf->dot11MeshHWMPRootMode;
+	}
+
+	if (supp_mask & CO_BIT(NL80211_MESHCONF_FORWARDING)) {
+		req->flags |= CO_BIT(MESH_UPDATE_FLAGS_MESH_FWD_BIT);
+		req->mesh_forward = p_mconf->dot11MeshForwarding;
+	}
+
+	if (supp_mask & CO_BIT(NL80211_MESHCONF_POWER_MODE)) {
+		req->flags |= CO_BIT(MESH_UPDATE_FLAGS_LOCAL_PSM_BIT);
+		req->local_ps_mode = p_mconf->power_mode;
+	}
+
+	/* Send the MESH_UPDATE_REQ message to UMAC FW */
+	return rwnx_send_msg(rwnx_hw, req, 1, MESH_UPDATE_CFM, cfm);
+}
+
+int rwnx_send_mesh_peer_info_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif,
+				 u8 sta_idx, struct mesh_peer_info_cfm *cfm)
+{
+	// Message to send
+	struct mesh_peer_info_req *req;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	/* Build the MESH_PEER_INFO_REQ message */
+	req = rwnx_msg_zalloc(MESH_PEER_INFO_REQ, TASK_MESH, DRV_TASK_ID,
+			      sizeof(struct mesh_peer_info_req));
+	if (!req)
+		return -ENOMEM;
+
+	req->sta_idx = sta_idx;
+
+	/* Send the MESH_PEER_INFO_REQ message to UMAC FW */
+	return rwnx_send_msg(rwnx_hw, req, 1, MESH_PEER_INFO_CFM, cfm);
+}
+
+void rwnx_send_mesh_peer_update_ntf(struct rwnx_hw *rwnx_hw,
+				    struct rwnx_vif *vif, u8 sta_idx,
+				    u8 mlink_state)
+{
+	// Message to send
+	struct mesh_peer_update_ntf *ntf;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	/* Build the MESH_PEER_UPDATE_NTF message */
+	ntf = rwnx_msg_zalloc(MESH_PEER_UPDATE_NTF, TASK_MESH, DRV_TASK_ID,
+			      sizeof(struct mesh_peer_update_ntf));
+
+	if (ntf) {
+		ntf->vif_idx = vif->vif_index;
+		ntf->sta_idx = sta_idx;
+		ntf->state = mlink_state;
+
+		/* Send the MESH_PEER_INFO_REQ message to UMAC FW */
+		rwnx_send_msg(rwnx_hw, ntf, 0, 0, NULL);
+	}
+}
+
+void rwnx_send_mesh_path_create_req(struct rwnx_hw *rwnx_hw,
+				    struct rwnx_vif *vif, u8 *tgt_addr)
+{
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	/* Check if we are already waiting for a confirmation */
+	if (!vif->ap.create_path) {
+		// Message to send
+		struct mesh_path_create_req *req;
+
+		/* Build the MESH_PATH_CREATE_REQ message */
+		req = rwnx_msg_zalloc(MESH_PATH_CREATE_REQ, TASK_MESH, DRV_TASK_ID,
+				      sizeof(struct mesh_path_create_req));
+
+		if (req) {
+			req->vif_idx = vif->vif_index;
+			memcpy(&req->tgt_mac_addr, tgt_addr, ETH_ALEN);
+
+			vif->ap.create_path = true;
+
+			/* Send the MESH_PATH_CREATE_REQ message to UMAC FW */
+			rwnx_send_msg(rwnx_hw, req, 0, 0, NULL);
+		}
+	}
+}
+
+int rwnx_send_mesh_path_update_req(struct rwnx_hw *rwnx_hw,
+				   struct rwnx_vif *vif, const u8 *tgt_addr,
+				   const u8 *p_nhop_addr,
+				   struct mesh_path_update_cfm *cfm)
+{
+	// Message to send
+	struct mesh_path_update_req *req;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	/* Build the MESH_PATH_UPDATE_REQ message */
+	req = rwnx_msg_zalloc(MESH_PATH_UPDATE_REQ, TASK_MESH, DRV_TASK_ID,
+			      sizeof(struct mesh_path_update_req));
+	if (!req)
+		return -ENOMEM;
+
+	req->delete = (!p_nhop_addr);
+	req->vif_idx = vif->vif_index;
+	memcpy(&req->tgt_mac_addr, tgt_addr, ETH_ALEN);
+
+	if (p_nhop_addr)
+		memcpy(&req->nhop_mac_addr, p_nhop_addr, ETH_ALEN);
+
+	/* Send the MESH_PATH_UPDATE_REQ message to UMAC FW */
+	return rwnx_send_msg(rwnx_hw, req, 1, MESH_PATH_UPDATE_CFM, cfm);
+}
+
+void rwnx_send_mesh_proxy_add_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif,
+				  u8 *ext_addr)
+{
+	// Message to send
+	struct mesh_proxy_add_req *req;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	/* Build the MESH_PROXY_ADD_REQ message */
+	req = rwnx_msg_zalloc(MESH_PROXY_ADD_REQ, TASK_MESH, DRV_TASK_ID,
+			      sizeof(struct mesh_proxy_add_req));
+
+	if (req) {
+		req->vif_idx = vif->vif_index;
+		memcpy(&req->ext_sta_addr, ext_addr, ETH_ALEN);
+
+		/* Send the MESH_PROXY_ADD_REQ message to UMAC FW */
+		rwnx_send_msg(rwnx_hw, req, 0, 0, NULL);
+	}
+}
+
+int rwnx_send_tdls_peer_traffic_ind_req(struct rwnx_hw *rwnx_hw,
+					struct rwnx_vif *rwnx_vif)
+{
+	struct tdls_peer_traffic_ind_req *tdls_peer_traffic_ind_req;
+
+	if (!rwnx_vif->sta.tdls_sta)
+		return -ENOLINK;
+
+	/* Build the TDLS_PEER_TRAFFIC_IND_REQ message */
+	tdls_peer_traffic_ind_req =
+		rwnx_msg_zalloc(TDLS_PEER_TRAFFIC_IND_REQ, TASK_TDLS, DRV_TASK_ID,
+				sizeof(struct tdls_peer_traffic_ind_req));
+
+	if (!tdls_peer_traffic_ind_req)
+		return -ENOMEM;
+
+	/* Set parameters for the TDLS_PEER_TRAFFIC_IND_REQ message */
+	tdls_peer_traffic_ind_req->vif_index = rwnx_vif->vif_index;
+	tdls_peer_traffic_ind_req->sta_idx = rwnx_vif->sta.tdls_sta->sta_idx;
+	memcpy(&tdls_peer_traffic_ind_req->peer_mac_addr.array[0],
+	       rwnx_vif->sta.tdls_sta->mac_addr, ETH_ALEN);
+	tdls_peer_traffic_ind_req->dialog_token = 0; // check dialog token value
+	tdls_peer_traffic_ind_req->last_tid = rwnx_vif->sta.tdls_sta->tdls.last_tid;
+	tdls_peer_traffic_ind_req->last_sn = rwnx_vif->sta.tdls_sta->tdls.last_sn;
+
+	/* Send the TDLS_PEER_TRAFFIC_IND_REQ message to LMAC FW */
+	return rwnx_send_msg(rwnx_hw, tdls_peer_traffic_ind_req, 0, 0, NULL);
+}
+
+int rwnx_send_config_monitor_req(struct rwnx_hw *rwnx_hw,
+				 struct cfg80211_chan_def *chandef,
+				 struct me_config_monitor_cfm *cfm)
+{
+	struct me_config_monitor_req *req;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	/* Build the ME_CONFIG_MONITOR_REQ message */
+	req = rwnx_msg_zalloc(ME_CONFIG_MONITOR_REQ, TASK_ME, DRV_TASK_ID,
+			      sizeof(struct me_config_monitor_req));
+	if (!req)
+		return -ENOMEM;
+
+	if (chandef) {
+		req->chan_set = true;
+
+		req->chan.band = chandef->chan->band;
+		req->chan.type = bw2chnl[chandef->width];
+		req->chan.prim20_freq = chandef->chan->center_freq;
+		req->chan.center1_freq = chandef->center_freq1;
+		req->chan.center2_freq = chandef->center_freq2;
+		req->chan.tx_power = chan_to_fw_pwr(chandef->chan->max_power);
+
+		if (rwnx_hw->phy.limit_bw)
+			limit_chan_bw(&req->chan.type, req->chan.prim20_freq,
+				      &req->chan.center1_freq);
+	} else {
+		req->chan_set = false;
+	}
+
+	req->uf = rwnx_hw->mod_params->uf;
+	req->auto_reply = rwnx_hw->mod_params->auto_reply;
+
+	/* Send the ME_CONFIG_MONITOR_REQ message to FW */
+	return rwnx_send_msg(rwnx_hw, req, 1, ME_CONFIG_MONITOR_CFM, cfm);
+}
+#endif /* CONFIG_RWNX_FULLMAC */
+
+int rwnx_send_tdls_chan_switch_req(struct rwnx_hw *rwnx_hw,
+				   struct rwnx_vif *rwnx_vif,
+				   struct rwnx_sta *rwnx_sta,
+				   bool sta_initiator, u8 oper_class,
+				   struct cfg80211_chan_def *chandef,
+				   struct tdls_chan_switch_cfm *cfm)
+{
+	struct tdls_chan_switch_req *tdls_chan_switch_req;
+
+	/* Build the TDLS_CHAN_SWITCH_REQ message */
+	tdls_chan_switch_req =
+		rwnx_msg_zalloc(TDLS_CHAN_SWITCH_REQ, TASK_TDLS, DRV_TASK_ID,
+				sizeof(struct tdls_chan_switch_req));
+
+	if (!tdls_chan_switch_req)
+		return -ENOMEM;
+
+	/* Set parameters for the TDLS_CHAN_SWITCH_REQ message */
+	tdls_chan_switch_req->vif_index = rwnx_vif->vif_index;
+	tdls_chan_switch_req->sta_idx = rwnx_sta->sta_idx;
+	memcpy(&tdls_chan_switch_req->peer_mac_addr.array[0],
+	       rwnx_sta_addr(rwnx_sta), ETH_ALEN);
+	tdls_chan_switch_req->initiator = sta_initiator;
+	tdls_chan_switch_req->band = chandef->chan->band;
+	tdls_chan_switch_req->type = bw2chnl[chandef->width];
+	tdls_chan_switch_req->prim20_freq = chandef->chan->center_freq;
+	tdls_chan_switch_req->center1_freq = chandef->center_freq1;
+	tdls_chan_switch_req->center2_freq = chandef->center_freq2;
+	tdls_chan_switch_req->tx_power = chan_to_fw_pwr(chandef->chan->max_power);
+	tdls_chan_switch_req->op_class = oper_class;
+
+	/* Send the TDLS_CHAN_SWITCH_REQ message to LMAC FW */
+	return rwnx_send_msg(rwnx_hw, tdls_chan_switch_req, 1, TDLS_CHAN_SWITCH_CFM,
+			     cfm);
+}
+
+int rwnx_send_tdls_cancel_chan_switch_req(struct rwnx_hw *rwnx_hw,
+					  struct rwnx_vif *rwnx_vif,
+					  struct rwnx_sta *rwnx_sta,
+					  struct tdls_cancel_chan_switch_cfm *cfm)
+{
+	struct tdls_cancel_chan_switch_req *tdls_cancel_chan_switch_req;
+
+	/* Build the TDLS_CHAN_SWITCH_REQ message */
+	tdls_cancel_chan_switch_req =
+		rwnx_msg_zalloc(TDLS_CANCEL_CHAN_SWITCH_REQ, TASK_TDLS, DRV_TASK_ID,
+				sizeof(struct tdls_cancel_chan_switch_req));
+	if (!tdls_cancel_chan_switch_req)
+		return -ENOMEM;
+
+	/* Set parameters for the TDLS_CHAN_SWITCH_REQ message */
+	tdls_cancel_chan_switch_req->vif_index = rwnx_vif->vif_index;
+	tdls_cancel_chan_switch_req->sta_idx = rwnx_sta->sta_idx;
+	memcpy(&tdls_cancel_chan_switch_req->peer_mac_addr.array[0],
+	       rwnx_sta_addr(rwnx_sta), ETH_ALEN);
+
+	/* Send the TDLS_CHAN_SWITCH_REQ message to LMAC FW */
+	return rwnx_send_msg(rwnx_hw, tdls_cancel_chan_switch_req, 1,
+			     TDLS_CANCEL_CHAN_SWITCH_CFM, cfm);
+}
+
+#ifdef CONFIG_RWNX_BFMER
+#ifdef CONFIG_RWNX_FULLMAC
+void rwnx_send_bfmer_enable(struct rwnx_hw *rwnx_hw, struct rwnx_sta *rwnx_sta,
+			    const struct ieee80211_vht_cap *vht_cap)
+#endif /* CONFIG_RWNX_FULLMAC */
+{
+	struct mm_bfmer_enable_req *bfmer_en_req;
+#ifdef CONFIG_RWNX_FULLMAC
+	u32 vht_capability;
+	u8 rx_nss = 0;
+#endif /* CONFIG_RWNX_FULLMAC */
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+#ifdef CONFIG_RWNX_FULLMAC
+	if (!vht_cap) {
+#endif /* CONFIG_RWNX_FULLMAC */
+		goto end;
+	}
+
+#ifdef CONFIG_RWNX_FULLMAC
+	vht_capability = le32_to_cpu(vht_cap->vht_cap_info);
+#endif /* CONFIG_RWNX_FULLMAC */
+
+	if (!(vht_capability & IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE))
+		goto end;
+
+#ifdef CONFIG_RWNX_FULLMAC
+	rx_nss = rwnx_bfmer_get_rx_nss(vht_cap);
+#endif /* CONFIG_RWNX_FULLMAC */
+
+	/* Allocate a structure that will contain the beamforming report */
+	if (rwnx_bfmer_report_add(rwnx_hw, rwnx_sta,
+				  RWNX_BFMER_REPORT_SPACE_SIZE))
+		goto end;
+
+	/* Build the MM_BFMER_ENABLE_REQ message */
+	bfmer_en_req = rwnx_msg_zalloc(MM_BFMER_ENABLE_REQ, TASK_MM, DRV_TASK_ID,
+				       sizeof(struct mm_bfmer_enable_req));
+
+	/* Check message allocation */
+	if (!bfmer_en_req) {
+		/* Free memory allocated for the report */
+		rwnx_bfmer_report_del(rwnx_hw, rwnx_sta);
+		/* Do not use beamforming */
+		goto end;
+	}
+
+	/* Provide DMA address to the MAC */
+	bfmer_en_req->host_bfr_addr = rwnx_sta->bfm_report->dma_addr;
+	bfmer_en_req->host_bfr_size = RWNX_BFMER_REPORT_SPACE_SIZE;
+	bfmer_en_req->sta_idx = rwnx_sta->sta_idx;
+#ifdef CONFIG_RWNX_FULLMAC
+	bfmer_en_req->aid = rwnx_sta->aid;
+	bfmer_en_req->rx_nss = rx_nss;
+#endif /* CONFIG_RWNX_FULLMAC */
+
+	if (vht_capability & IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE)
+		bfmer_en_req->vht_mu_bfmee = true;
+	else
+		bfmer_en_req->vht_mu_bfmee = false;
+
+	/* Send the MM_BFMER_EN_REQ message to LMAC FW */
+	rwnx_send_msg(rwnx_hw, bfmer_en_req, 0, 0, NULL);
+
+end:
+	return;
+}
+
+#ifdef CONFIG_RWNX_MUMIMO_TX
+int rwnx_send_mu_group_update_req(struct rwnx_hw *rwnx_hw,
+				  struct rwnx_sta *rwnx_sta)
+{
+	struct mm_mu_group_update_req *req;
+	int group_id, i = 0;
+	u64 map;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	/* Build the MM_MU_GROUP_UPDATE_REQ message */
+	req =
+		rwnx_msg_zalloc(MM_MU_GROUP_UPDATE_REQ, TASK_MM, DRV_TASK_ID,
+				sizeof(struct mm_mu_group_update_req) +
+				rwnx_sta->group_info.cnt * sizeof(req->groups[0]));
+
+	/* Check message allocation */
+	if (!req)
+		return -ENOMEM;
+
+	map = rwnx_sta->group_info.map & RWNX_MU_GROUP_MASK;
+	for (group_id = (fls64(map) - 1); group_id > 0;
+	     map &= ~(u64)BIT_ULL(group_id), group_id = (fls64(map) - 1)) {
+		int user_pos = rwnx_mu_group_sta_get_pos(rwnx_hw, rwnx_sta, group_id);
+
+		if (WARN(i >= rwnx_sta->group_info.cnt,
+			 "STA%d: Too much group (%d)\n", rwnx_sta->sta_idx, i + 1))
+			break;
+
+		req->groups[i].group_id = group_id;
+		req->groups[i].user_pos = user_pos;
+
+		i++;
+	}
+
+	req->group_cnt = rwnx_sta->group_info.cnt;
+	req->sta_idx = rwnx_sta->sta_idx;
+
+	/* Send the MM_MU_GROUP_UPDATE_REQ message to LMAC FW */
+	return rwnx_send_msg(rwnx_hw, req, 1, MM_MU_GROUP_UPDATE_CFM, NULL);
+}
+#endif /* CONFIG_RWNX_MUMIMO_TX */
+#endif /* CONFIG_RWNX_BFMER */
+
+/**********************************************************************
+ *    Debug Messages
+ *********************************************************************/
+int rwnx_send_dbg_trigger_req(struct rwnx_hw *rwnx_hw, char *msg)
+{
+	struct mm_dbg_trigger_req *req;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	/* Build the MM_DBG_TRIGGER_REQ message */
+	req = rwnx_msg_zalloc(MM_DBG_TRIGGER_REQ, TASK_MM, DRV_TASK_ID,
+			      sizeof(struct mm_dbg_trigger_req));
+	if (!req)
+		return -ENOMEM;
+
+	/* Set parameters for the MM_DBG_TRIGGER_REQ message */
+	strscpy(req->error, msg, sizeof(req->error));
+
+	/* Send the MM_DBG_TRIGGER_REQ message to LMAC FW */
+	return rwnx_send_msg(rwnx_hw, req, 0, -1, NULL);
+}
+
+int rwnx_send_dbg_mem_read_req(struct rwnx_hw *rwnx_hw, u32 mem_addr,
+			       struct dbg_mem_read_cfm *cfm)
+{
+	struct dbg_mem_read_req *mem_read_req;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	/* Build the DBG_MEM_READ_REQ message */
+	mem_read_req = rwnx_msg_zalloc(DBG_MEM_READ_REQ, TASK_DBG, DRV_TASK_ID,
+				       sizeof(struct dbg_mem_read_req));
+	if (!mem_read_req)
+		return -ENOMEM;
+
+	/* Set parameters for the DBG_MEM_READ_REQ message */
+	mem_read_req->memaddr = mem_addr;
+
+	/* Send the DBG_MEM_READ_REQ message to LMAC FW */
+	return rwnx_send_msg(rwnx_hw, mem_read_req, 1, DBG_MEM_READ_CFM, cfm);
+}
+
+int rwnx_send_dbg_mem_write_req(struct rwnx_hw *rwnx_hw, u32 mem_addr,
+				u32 mem_data)
+{
+	struct dbg_mem_write_req *mem_write_req;
+
+	// RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	/* Build the DBG_MEM_WRITE_REQ message */
+	mem_write_req = rwnx_msg_zalloc(DBG_MEM_WRITE_REQ, TASK_DBG, DRV_TASK_ID,
+					sizeof(struct dbg_mem_write_req));
+	if (!mem_write_req)
+		return -ENOMEM;
+
+	/* Set parameters for the DBG_MEM_WRITE_REQ message */
+	mem_write_req->memaddr = mem_addr;
+	mem_write_req->memdata = mem_data;
+
+	/* Send the DBG_MEM_WRITE_REQ message to LMAC FW */
+	return rwnx_send_msg(rwnx_hw, mem_write_req, 1, DBG_MEM_WRITE_CFM, NULL);
+}
+
+int rwnx_send_dbg_mem_mask_write_req(struct rwnx_hw *rwnx_hw, u32 mem_addr,
+				     u32 mem_mask, u32 mem_data)
+{
+	struct dbg_mem_mask_write_req *mem_mask_write_req;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	/* Build the DBG_MEM_MASK_WRITE_REQ message */
+	mem_mask_write_req =
+		rwnx_msg_zalloc(DBG_MEM_MASK_WRITE_REQ, TASK_DBG, DRV_TASK_ID,
+				sizeof(struct dbg_mem_mask_write_req));
+	if (!mem_mask_write_req)
+		return -ENOMEM;
+
+	/* Set parameters for the DBG_MEM_MASK_WRITE_REQ message */
+	mem_mask_write_req->memaddr = mem_addr;
+	mem_mask_write_req->memmask = mem_mask;
+	mem_mask_write_req->memdata = mem_data;
+
+	/* Send the DBG_MEM_MASK_WRITE_REQ message to LMAC FW */
+	return rwnx_send_msg(rwnx_hw, mem_mask_write_req, 1, DBG_MEM_MASK_WRITE_CFM,
+			     NULL);
+}
+
+int rwnx_send_rftest_req(struct rwnx_hw *rwnx_hw, u32 cmd, u32 argc,
+			 u8 *argv, struct dbg_rftest_cmd_cfm *cfm)
+{
+	struct dbg_rftest_cmd_req *mem_rftest_cmd_req;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	/* Build the DBG_RFTEST_CMD_REQ message */
+	mem_rftest_cmd_req =
+		rwnx_msg_zalloc(DBG_RFTEST_CMD_REQ, TASK_DBG, DRV_TASK_ID,
+				sizeof(struct dbg_rftest_cmd_req));
+	if (!mem_rftest_cmd_req)
+		return -ENOMEM;
+
+	if (argc > 10)
+		return -ENOMEM;
+
+	/* Set parameters for the DBG_MEM_MASK_WRITE_REQ message */
+	mem_rftest_cmd_req->cmd = cmd;
+	mem_rftest_cmd_req->argc = argc;
+	if (argc != 0)
+		memcpy(mem_rftest_cmd_req->argv, argv, argc);
+
+	/* Send the DBG_RFTEST_CMD_REQ message to LMAC FW */
+	return rwnx_send_msg(rwnx_hw, mem_rftest_cmd_req, 1, DBG_RFTEST_CMD_CFM,
+			     cfm);
+}
+
+int rwnx_send_dbg_set_mod_filter_req(struct rwnx_hw *rwnx_hw, u32 filter)
+{
+	struct dbg_set_mod_filter_req *set_mod_filter_req;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	/* Build the DBG_SET_MOD_FILTER_REQ message */
+	set_mod_filter_req =
+		rwnx_msg_zalloc(DBG_SET_MOD_FILTER_REQ, TASK_DBG, DRV_TASK_ID,
+				sizeof(struct dbg_set_mod_filter_req));
+	if (!set_mod_filter_req)
+		return -ENOMEM;
+
+	/* Set parameters for the DBG_SET_MOD_FILTER_REQ message */
+	set_mod_filter_req->mod_filter = filter;
+
+	/* Send the DBG_SET_MOD_FILTER_REQ message to LMAC FW */
+	return rwnx_send_msg(rwnx_hw, set_mod_filter_req, 1, DBG_SET_MOD_FILTER_CFM,
+						 NULL);
+}
+
+int rwnx_send_dbg_set_sev_filter_req(struct rwnx_hw *rwnx_hw, u32 filter)
+{
+	struct dbg_set_sev_filter_req *set_sev_filter_req;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	/* Build the DBG_SET_SEV_FILTER_REQ message */
+	set_sev_filter_req =
+		rwnx_msg_zalloc(DBG_SET_SEV_FILTER_REQ, TASK_DBG, DRV_TASK_ID,
+				sizeof(struct dbg_set_sev_filter_req));
+	if (!set_sev_filter_req)
+		return -ENOMEM;
+
+	/* Set parameters for the DBG_SET_SEV_FILTER_REQ message */
+	set_sev_filter_req->sev_filter = filter;
+
+	/* Send the DBG_SET_SEV_FILTER_REQ message to LMAC FW */
+	return rwnx_send_msg(rwnx_hw, set_sev_filter_req, 1, DBG_SET_SEV_FILTER_CFM,
+			     NULL);
+}
+
+int rwnx_send_dbg_get_sys_stat_req(struct rwnx_hw *rwnx_hw,
+				   struct dbg_get_sys_stat_cfm *cfm)
+{
+	void *req;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	/* Allocate the message */
+	req = rwnx_msg_zalloc(DBG_GET_SYS_STAT_REQ, TASK_DBG, DRV_TASK_ID, 0);
+	if (!req)
+		return -ENOMEM;
+
+	/* Send the DBG_MEM_READ_REQ message to LMAC FW */
+	return rwnx_send_msg(rwnx_hw, req, 1, DBG_GET_SYS_STAT_CFM, cfm);
+}
+
+int rwnx_send_dbg_mem_block_write_req(struct rwnx_hw *rwnx_hw, u32 mem_addr,
+				      u32 mem_size, u32 *mem_data)
+{
+	struct dbg_mem_block_write_req *mem_blk_write_req;
+
+	// RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	/* Build the DBG_MEM_BLOCK_WRITE_REQ message */
+	mem_blk_write_req =
+		rwnx_msg_zalloc(DBG_MEM_BLOCK_WRITE_REQ, TASK_DBG, DRV_TASK_ID,
+				sizeof(struct dbg_mem_block_write_req));
+	if (!mem_blk_write_req)
+		return -ENOMEM;
+
+	/* Set parameters for the DBG_MEM_BLOCK_WRITE_REQ message */
+	mem_blk_write_req->memaddr = mem_addr;
+	mem_blk_write_req->memsize = mem_size;
+	memcpy(mem_blk_write_req->memdata, mem_data, mem_size);
+
+	/* Send the DBG_MEM_BLOCK_WRITE_REQ message to LMAC FW */
+	return rwnx_send_msg(rwnx_hw, mem_blk_write_req, 1, DBG_MEM_BLOCK_WRITE_CFM,
+			     NULL);
+}
+
+int rwnx_send_dbg_start_app_req(struct rwnx_hw *rwnx_hw, u32 boot_addr,
+				u32 boot_type)
+{
+	struct dbg_start_app_req *start_app_req;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	/* Build the DBG_START_APP_REQ message */
+	start_app_req = rwnx_msg_zalloc(DBG_START_APP_REQ, TASK_DBG, DRV_TASK_ID,
+					sizeof(struct dbg_start_app_req));
+	if (!start_app_req)
+		return -ENOMEM;
+
+	/* Set parameters for the DBG_START_APP_REQ message */
+	start_app_req->bootaddr = boot_addr;
+	start_app_req->boottype = boot_type;
+
+	/* Send the DBG_START_APP_REQ message to LMAC FW */
+	return rwnx_send_msg(rwnx_hw, start_app_req, 1, DBG_START_APP_CFM, NULL);
+}
+
+int rwnx_send_cfg_rssi_req(struct rwnx_hw *rwnx_hw, u8 vif_index,
+			   int rssi_thold, u32 rssi_hyst)
+{
+	struct mm_cfg_rssi_req *req;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	/* Build the MM_CFG_RSSI_REQ message */
+	req = rwnx_msg_zalloc(MM_CFG_RSSI_REQ, TASK_MM, DRV_TASK_ID,
+			      sizeof(struct mm_cfg_rssi_req));
+	if (!req)
+		return -ENOMEM;
+
+	if (!rwnx_hw->vif_table[vif_index])
+		return 0;
+
+	/* Set parameters for the MM_CFG_RSSI_REQ message */
+	req->vif_index = vif_index;
+	req->rssi_thold = (s8)rssi_thold;
+	req->rssi_hyst = (u8)rssi_hyst;
+
+	/* Send the MM_CFG_RSSI_REQ message to LMAC FW */
+	return rwnx_send_msg(rwnx_hw, req, 1, MM_CFG_RSSI_CFM, NULL);
+}
diff --git a/drivers/net/wireless/aic/aic8800_fdrv/rwnx_msg_tx.h b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_msg_tx.h
new file mode 100644
index 0000000000000..1e01ef8f7976c
--- /dev/null
+++ b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_msg_tx.h
@@ -0,0 +1,266 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _RWNX_MSG_TX_H_
+#define _RWNX_MSG_TX_H_
+
+#include "lmac_types.h"
+#include "rwnx_defs.h"
+
+int rwnx_send_reset(struct rwnx_hw *rwnx_hw);
+
+int rwnx_send_start(struct rwnx_hw *rwnx_hw);
+
+int rwnx_send_version_req(struct rwnx_hw *rwnx_hw, struct mm_version_cfm *cfm);
+
+int rwnx_send_add_if(struct rwnx_hw *rwnx_hw, const unsigned char *mac,
+		     enum nl80211_iftype iftype, bool p2p,
+		     struct mm_add_if_cfm *cfm);
+
+int rwnx_send_remove_if(struct rwnx_hw *rwnx_hw, u8 vif_index, bool defer);
+
+int rwnx_send_set_channel(struct rwnx_hw *rwnx_hw, int phy_idx,
+			  struct mm_set_channel_cfm *cfm);
+
+int rwnx_send_key_add(struct rwnx_hw *rwnx_hw, u8 vif_idx, u8 sta_idx,
+		      bool pairwise, u8 *key, u8 key_len, u8 key_idx,
+		      u8 cipher_suite, struct mm_key_add_cfm *cfm);
+
+int rwnx_send_key_del(struct rwnx_hw *rwnx_hw, uint8_t hw_key_idx);
+
+int rwnx_send_bcn(struct rwnx_hw *rwnx_hw, u8 *buf, u8 vif_idx, u16 bcn_len);
+
+int rwnx_send_bcn_change(struct rwnx_hw *rwnx_hw, u8 vif_idx, u32 bcn_addr,
+			 u16 bcn_len, u16 tim_oft, u16 tim_len, u16 *csa_oft);
+
+int rwnx_send_tim_update(struct rwnx_hw *rwnx_hw, u8 vif_idx, u16 aid,
+			 u8 tx_status);
+
+int rwnx_send_roc(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif,
+		  struct ieee80211_channel *chan, unsigned int duration,
+		  struct mm_remain_on_channel_cfm *roc_cfm);
+
+int rwnx_send_cancel_roc(struct rwnx_hw *rwnx_hw);
+
+int rwnx_send_set_power(struct rwnx_hw *rwnx_hw, u8 vif_idx, s8 pwr,
+			struct mm_set_power_cfm *cfm);
+
+int rwnx_send_set_edca(struct rwnx_hw *rwnx_hw, u8 hw_queue, u32 param,
+		       bool uapsd, u8 inst_nbr);
+
+int rwnx_send_tdls_chan_switch_req(struct rwnx_hw *rwnx_hw,
+				   struct rwnx_vif *rwnx_vif,
+				   struct rwnx_sta *rwnx_sta,
+				   bool sta_initiator, u8 oper_class,
+				   struct cfg80211_chan_def *chandef,
+				   struct tdls_chan_switch_cfm *cfm);
+
+int rwnx_send_tdls_cancel_chan_switch_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif,
+					  struct rwnx_sta *rwnx_sta,
+					  struct tdls_cancel_chan_switch_cfm *cfm);
+
+#ifdef AICWF_ARP_OFFLOAD
+int rwnx_send_arpoffload_en_req(struct rwnx_hw *rwnx_hw,
+				struct rwnx_vif *rwnx_vif, u32 ipaddr,
+				u8 enable);
+#endif
+int rwnx_send_rf_config_req(struct rwnx_hw *rwnx_hw, u8 ofst, u8 sel, u8 *tbl, u16 len);
+
+int rwnx_send_rf_calib_req(struct rwnx_hw *rwnx_hw,
+			   struct mm_set_rf_calib_cfm *cfm);
+
+int rwnx_send_get_macaddr_req(struct rwnx_hw *rwnx_hw,
+			      struct mm_get_mac_addr_cfm *cfm);
+
+#ifdef CONFIG_RWNX_FULLMAC
+int rwnx_send_me_config_req(struct rwnx_hw *rwnx_hw);
+
+int rwnx_send_me_chan_config_req(struct rwnx_hw *rwnx_hw);
+
+int rwnx_send_me_set_control_port_req(struct rwnx_hw *rwnx_hw, bool opened,
+				      u8 sta_idx);
+
+int rwnx_send_me_sta_add(struct rwnx_hw *rwnx_hw,
+			 struct station_parameters *params, const u8 *mac,
+			 u8 inst_nbr, struct me_sta_add_cfm *cfm);
+
+int rwnx_send_me_sta_del(struct rwnx_hw *rwnx_hw, u8 sta_idx, bool tdls_sta);
+
+int rwnx_send_me_traffic_ind(struct rwnx_hw *rwnx_hw, u8 sta_idx, bool uapsd,
+			     u8 tx_status);
+
+int rwnx_send_me_rc_stats(struct rwnx_hw *rwnx_hw, u8 sta_idx,
+			  struct me_rc_stats_cfm *cfm);
+
+int rwnx_send_me_rc_set_rate(struct rwnx_hw *rwnx_hw, u8 sta_idx, u16 rate_idx);
+
+int rwnx_send_me_set_ps_mode(struct rwnx_hw *rwnx_hw, u8 ps_mode);
+
+int rwnx_send_me_set_lp_level(struct rwnx_hw *rwnx_hw, u8 lp_level, u8 disable_filter);
+
+int rwnx_send_sm_connect_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif,
+			     struct cfg80211_connect_params *sme, struct sm_connect_cfm *cfm);
+
+int rwnx_send_sm_disconnect_req(struct rwnx_hw *rwnx_hw,
+				struct rwnx_vif *rwnx_vif, u16 reason);
+
+int rwnx_send_sm_external_auth_required_rsp(struct rwnx_hw *rwnx_hw,
+					    struct rwnx_vif *rwnx_vif, u16 status);
+
+int rwnx_send_apm_start_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif,
+			    struct cfg80211_ap_settings *settings, struct apm_start_cfm *cfm,
+			    struct rwnx_ipc_elem_var *elem);
+
+int rwnx_send_apm_stop_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif);
+
+int rwnx_send_scanu_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif,
+			struct cfg80211_scan_request *param);
+
+int rwnx_send_scanu_cancel_req(struct rwnx_hw *rwnx_hw,
+			       struct scan_cancel_cfm *cfm);
+
+int rwnx_send_apm_start_cac_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif,
+				struct cfg80211_chan_def *chandef,
+				struct apm_start_cac_cfm *cfm);
+
+int rwnx_send_apm_stop_cac_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif);
+
+int rwnx_send_tdls_peer_traffic_ind_req(struct rwnx_hw *rwnx_hw,
+					struct rwnx_vif *rwnx_vif);
+
+int rwnx_send_config_monitor_req(struct rwnx_hw *rwnx_hw,
+				 struct cfg80211_chan_def *chandef,
+				 struct me_config_monitor_cfm *cfm);
+
+int rwnx_send_mesh_start_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif,
+			     const struct mesh_config *conf, const struct mesh_setup *setup,
+			     struct mesh_start_cfm *cfm);
+
+int rwnx_send_mesh_stop_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif,
+			    struct mesh_stop_cfm *cfm);
+
+int rwnx_send_mesh_update_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif,
+			      u32 mask, const struct mesh_config *p_mconf,
+			      struct mesh_update_cfm *cfm);
+
+int rwnx_send_mesh_peer_info_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif,
+				 u8 sta_idx, struct mesh_peer_info_cfm *cfm);
+
+void rwnx_send_mesh_peer_update_ntf(struct rwnx_hw *rwnx_hw,
+				    struct rwnx_vif *vif, u8 sta_idx,
+				    u8 mlink_state);
+
+void rwnx_send_mesh_path_create_req(struct rwnx_hw *rwnx_hw,
+				    struct rwnx_vif *vif, u8 *tgt_addr);
+
+int rwnx_send_mesh_path_update_req(struct rwnx_hw *rwnx_hw,
+				   struct rwnx_vif *vif, const u8 *tgt_addr,
+				   const u8 *p_nhop_addr,
+				   struct mesh_path_update_cfm *cfm);
+
+void rwnx_send_mesh_proxy_add_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif,
+				  u8 *ext_addr);
+
+#endif /* CONFIG_RWNX_FULLMAC */
+
+#ifdef CONFIG_RWNX_BFMER
+#ifdef CONFIG_RWNX_FULLMAC
+void rwnx_send_bfmer_enable(struct rwnx_hw *rwnx_hw, struct rwnx_sta *rwnx_sta,
+			    const struct ieee80211_vht_cap *vht_cap);
+
+#endif /* CONFIG_RWNX_FULLMAC */
+#ifdef CONFIG_RWNX_MUMIMO_TX
+int rwnx_send_mu_group_update_req(struct rwnx_hw *rwnx_hw,
+				  struct rwnx_sta *rwnx_sta);
+#endif /* CONFIG_RWNX_MUMIMO_TX */
+#endif /* CONFIG_RWNX_BFMER */
+
+/* Debug messages */
+int rwnx_send_dbg_trigger_req(struct rwnx_hw *rwnx_hw, char *msg);
+
+int rwnx_send_dbg_mem_read_req(struct rwnx_hw *rwnx_hw, u32 mem_addr,
+			       struct dbg_mem_read_cfm *cfm);
+
+int rwnx_send_dbg_mem_write_req(struct rwnx_hw *rwnx_hw, u32 mem_addr,
+				u32 mem_data);
+
+int rwnx_send_dbg_mem_mask_write_req(struct rwnx_hw *rwnx_hw, u32 mem_addr,
+				     u32 mem_mask, u32 mem_data);
+
+int rwnx_send_dbg_set_mod_filter_req(struct rwnx_hw *rwnx_hw, u32 filter);
+
+int rwnx_send_rftest_req(struct rwnx_hw *rwnx_hw, u32 cmd, u32 argc,
+			 u8 *argv, struct dbg_rftest_cmd_cfm *cfm);
+
+int rwnx_send_dbg_set_sev_filter_req(struct rwnx_hw *rwnx_hw, u32 filter);
+
+int rwnx_send_dbg_get_sys_stat_req(struct rwnx_hw *rwnx_hw,
+				   struct dbg_get_sys_stat_cfm *cfm);
+
+int rwnx_send_dbg_mem_block_write_req(struct rwnx_hw *rwnx_hw, u32 mem_addr,
+				      u32 mem_size, u32 *mem_data);
+
+int rwnx_send_dbg_start_app_req(struct rwnx_hw *rwnx_hw, u32 boot_addr,
+				u32 boot_type);
+
+int rwnx_send_cfg_rssi_req(struct rwnx_hw *rwnx_hw, u8 vif_index,
+			   int rssi_thold, u32 rssi_hyst);
+
+int rwnx_send_disable_agg_req(struct rwnx_hw *rwnx_hw, u8 agg_disable,
+			      u8 agg_disable_rx, u8 sta_idx);
+
+int rwnx_send_coex_req(struct rwnx_hw *rwnx_hw, u8 disable_coexnull,
+		       u8 enable_nullcts);
+
+int rwnx_send_get_sta_info_req(struct rwnx_hw *rwnx_hw, u8 sta_idx,
+			       struct mm_get_sta_info_cfm *cfm);
+
+int rwnx_send_set_stack_start_req(struct rwnx_hw *rwnx_hw, u8 on,
+				  u8 efuse_valid, u8 set_vendor_info,
+				  u8 fwtrace_redir_en,
+				  struct mm_set_stack_start_cfm *cfm);
+
+int rwnx_send_get_temp_req(struct rwnx_hw *rwnx_hw,
+			   struct mm_set_vendor_swconfig_cfm *cfm);
+
+int rwnx_send_get_temp_hwconfig_req(struct rwnx_hw *rwnx_hw,
+				    struct mm_set_vendor_hwconfig_cfm *cfm);
+
+int rwnx_send_set_temp_comp_req(struct rwnx_hw *rwnx_hw,
+				struct mm_set_vendor_swconfig_cfm *cfm);
+
+int rwnx_send_txop_req(struct rwnx_hw *rwnx_hw, uint16_t *txop,
+		       u8 long_nav_en, u8 cfe_en);
+
+int rwnx_send_vendor_hwconfig_req(struct rwnx_hw *rwnx_hw, u32 hwconfig_id,
+				  s32 *param, s32 *param_out);
+
+int rwnx_send_vendor_swconfig_req(struct rwnx_hw *rwnx_hw, u32 swconfig_id,
+				  s32 *param_in, s32 *param_out);
+
+int rwnx_send_mask_set_ext_flags_req(struct rwnx_hw *rwnx_hw,
+				     u32 flags_mask, u32 flags_val,
+				     struct mm_set_vendor_swconfig_cfm *cfm);
+
+int rwnx_send_get_fw_version_req(struct rwnx_hw *rwnx_hw,
+				 struct mm_get_fw_version_cfm *cfm);
+
+int rwnx_send_txpwr_idx_req(struct rwnx_hw *rwnx_hw);
+
+int rwnx_send_txpwr_ofst_req(struct rwnx_hw *rwnx_hw);
+
+int rwnx_send_txpwr_ofst2x_req(struct rwnx_hw *rwnx_hw);
+
+int rwnx_send_txpwr_lvl_req(struct rwnx_hw *rwnx_hw);
+
+int rwnx_send_txpwr_lvl_v3_req(struct rwnx_hw *rwnx_hw, int index_reg);
+
+int rwnx_send_txpwr_lvl_adj_req(struct rwnx_hw *rwnx_hw);
+
+struct rwnx_cmd *rwnx_cmd_malloc(void);
+
+void rwnx_cmd_free(struct rwnx_cmd *cmd);
+
+int rwnx_init_cmd_array(void);
+
+void rwnx_free_cmd_array(void);
+
+#endif /* _RWNX_MSG_TX_H_ */
diff --git a/drivers/net/wireless/aic/aic8800_fdrv/rwnx_platform.c b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_platform.c
new file mode 100644
index 0000000000000..a4d96a6a082f5
--- /dev/null
+++ b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_platform.c
@@ -0,0 +1,2868 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ ******************************************************************************
+ *
+ * Copyright (C) 2020 AIC semiconductor.
+ *
+ * @file rwnx_platform.c
+ *
+ * @brief Platform profile
+ *
+ ******************************************************************************
+ */
+
+#include <linux/delay.h>
+#include <crypto/md5.h>
+#include <linux/firmware.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/sprintf.h>
+#include <linux/string.h>
+#include <linux/vmalloc.h>
+
+#include "hal_desc.h"
+#include "reg_access.h"
+#include "rwnx_main.h"
+#include "rwnx_platform.h"
+#include "ipc_host.h"
+#include "rwnx_msg_tx.h"
+
+#ifdef AICWF_SDIO_SUPPORT
+#include "aicwf_sdio.h"
+#endif
+
+#include "aicwf_compat_8800d80.h"
+#include "aicwf_compat_8800dc.h"
+#include "aicwf_chip_ops.h"
+
+// Parser state
+#define INIT      0
+#define CMD       1
+#define PRINT     2
+#define GET_VALUE 3
+
+struct reg_table {
+	char ccode[3];
+	enum regions_code region;
+};
+
+/* If the region conflicts with the kernel, the actual authentication standard prevails */
+static struct reg_table reg_tables[] = {
+	{.ccode = "CN", .region = REGIONS_SRRC},
+	{.ccode = "US", .region = REGIONS_FCC},
+	{.ccode = "DE", .region = REGIONS_ETSI},
+	{.ccode = "00", .region = REGIONS_DEFAULT},
+	{.ccode = "WW", .region = REGIONS_DEFAULT},
+	{.ccode = "XX", .region = REGIONS_DEFAULT},
+	{.ccode = "JP", .region = REGIONS_JP},
+	{.ccode = "AD", .region = REGIONS_ETSI},
+	{.ccode = "AE", .region = REGIONS_ETSI},
+	{.ccode = "AF", .region = REGIONS_ETSI},
+	{.ccode = "AI", .region = REGIONS_ETSI},
+	{.ccode = "AL", .region = REGIONS_ETSI},
+	{.ccode = "AM", .region = REGIONS_ETSI},
+	{.ccode = "AN", .region = REGIONS_ETSI},
+	{.ccode = "AR", .region = REGIONS_FCC},
+	{.ccode = "AS", .region = REGIONS_FCC},
+	{.ccode = "AT", .region = REGIONS_ETSI},
+	{.ccode = "AU", .region = REGIONS_ETSI},
+	{.ccode = "AW", .region = REGIONS_ETSI},
+	{.ccode = "AZ", .region = REGIONS_ETSI},
+	{.ccode = "BA", .region = REGIONS_ETSI},
+	{.ccode = "BB", .region = REGIONS_FCC},
+	{.ccode = "BD", .region = REGIONS_ETSI},
+	{.ccode = "BE", .region = REGIONS_ETSI},
+	{.ccode = "BF", .region = REGIONS_FCC},
+	{.ccode = "BG", .region = REGIONS_ETSI},
+	{.ccode = "BH", .region = REGIONS_ETSI},
+	{.ccode = "BL", .region = REGIONS_ETSI},
+	{.ccode = "BM", .region = REGIONS_FCC},
+	{.ccode = "BN", .region = REGIONS_JP},
+	{.ccode = "BO", .region = REGIONS_JP},
+	{.ccode = "BR", .region = REGIONS_FCC},
+	{.ccode = "BS", .region = REGIONS_FCC},
+	{.ccode = "BT", .region = REGIONS_ETSI},
+	{.ccode = "BW", .region = REGIONS_ETSI},
+	{.ccode = "BY", .region = REGIONS_ETSI},
+	{.ccode = "BZ", .region = REGIONS_JP},
+	{.ccode = "CA", .region = REGIONS_FCC},
+	{.ccode = "CF", .region = REGIONS_FCC},
+	{.ccode = "CH", .region = REGIONS_ETSI},
+	{.ccode = "CI", .region = REGIONS_FCC},
+	{.ccode = "CL", .region = REGIONS_ETSI},
+	{.ccode = "CO", .region = REGIONS_FCC},
+	{.ccode = "CR", .region = REGIONS_FCC},
+	{.ccode = "CU", .region = REGIONS_FCC},
+	{.ccode = "CX", .region = REGIONS_FCC},
+	{.ccode = "CY", .region = REGIONS_ETSI},
+	{.ccode = "CZ", .region = REGIONS_ETSI},
+	{.ccode = "DK", .region = REGIONS_ETSI},
+	{.ccode = "DM", .region = REGIONS_FCC},
+	{.ccode = "DO", .region = REGIONS_FCC},
+	{.ccode = "DZ", .region = REGIONS_ETSI},
+	{.ccode = "EC", .region = REGIONS_FCC},
+	{.ccode = "EE", .region = REGIONS_ETSI},
+	{.ccode = "EG", .region = REGIONS_ETSI},
+	{.ccode = "ES", .region = REGIONS_ETSI},
+	{.ccode = "ET", .region = REGIONS_ETSI},
+	{.ccode = "FI", .region = REGIONS_ETSI},
+	{.ccode = "FK", .region = REGIONS_ETSI},
+	{.ccode = "FM", .region = REGIONS_FCC},
+	{.ccode = "FO", .region = REGIONS_ETSI},
+	{.ccode = "FR", .region = REGIONS_ETSI},
+	{.ccode = "GB", .region = REGIONS_ETSI},
+	{.ccode = "GD", .region = REGIONS_FCC},
+	{.ccode = "GE", .region = REGIONS_ETSI},
+	{.ccode = "GF", .region = REGIONS_ETSI},
+	{.ccode = "GH", .region = REGIONS_ETSI},
+	{.ccode = "GI", .region = REGIONS_ETSI},
+	{.ccode = "GL", .region = REGIONS_ETSI},
+	{.ccode = "GP", .region = REGIONS_ETSI},
+	{.ccode = "GR", .region = REGIONS_ETSI},
+	{.ccode = "GT", .region = REGIONS_FCC},
+	{.ccode = "GU", .region = REGIONS_FCC},
+	{.ccode = "GY", .region = REGIONS_DEFAULT},
+	{.ccode = "HK", .region = REGIONS_ETSI},
+	{.ccode = "HN", .region = REGIONS_FCC},
+	{.ccode = "HR", .region = REGIONS_ETSI},
+	{.ccode = "HT", .region = REGIONS_FCC},
+	{.ccode = "HU", .region = REGIONS_ETSI},
+	{.ccode = "ID", .region = REGIONS_ETSI},
+	{.ccode = "IE", .region = REGIONS_ETSI},
+	{.ccode = "IL", .region = REGIONS_ETSI},
+	{.ccode = "IN", .region = REGIONS_ETSI},
+	{.ccode = "IQ", .region = REGIONS_ETSI},
+	{.ccode = "IR", .region = REGIONS_JP},
+	{.ccode = "IS", .region = REGIONS_ETSI},
+	{.ccode = "IT", .region = REGIONS_ETSI},
+	{.ccode = "JM", .region = REGIONS_FCC},
+	{.ccode = "JO", .region = REGIONS_ETSI},
+	{.ccode = "KE", .region = REGIONS_ETSI},
+	{.ccode = "KG", .region = REGIONS_ETSI},
+	{.ccode = "KH", .region = REGIONS_ETSI},
+	{.ccode = "KN", .region = REGIONS_ETSI},
+	{.ccode = "KP", .region = REGIONS_JP},
+	{.ccode = "KR", .region = REGIONS_ETSI},
+	{.ccode = "KW", .region = REGIONS_ETSI},
+	{.ccode = "KY", .region = REGIONS_FCC},
+	{.ccode = "KZ", .region = REGIONS_ETSI},
+	{.ccode = "LB", .region = REGIONS_ETSI},
+	{.ccode = "LC", .region = REGIONS_ETSI},
+	{.ccode = "LI", .region = REGIONS_ETSI},
+	{.ccode = "LK", .region = REGIONS_FCC},
+	{.ccode = "LS", .region = REGIONS_ETSI},
+	{.ccode = "LT", .region = REGIONS_ETSI},
+	{.ccode = "LU", .region = REGIONS_ETSI},
+	{.ccode = "LV", .region = REGIONS_ETSI},
+	{.ccode = "LY", .region = REGIONS_ETSI},
+	{.ccode = "MA", .region = REGIONS_ETSI},
+	{.ccode = "MC", .region = REGIONS_ETSI},
+	{.ccode = "MD", .region = REGIONS_ETSI},
+	{.ccode = "ME", .region = REGIONS_ETSI},
+	{.ccode = "MF", .region = REGIONS_ETSI},
+	{.ccode = "MH", .region = REGIONS_FCC},
+	{.ccode = "MK", .region = REGIONS_ETSI},
+	{.ccode = "MN", .region = REGIONS_ETSI},
+	{.ccode = "MO", .region = REGIONS_ETSI},
+	{.ccode = "MP", .region = REGIONS_FCC},
+	{.ccode = "MQ", .region = REGIONS_ETSI},
+	{.ccode = "MR", .region = REGIONS_ETSI},
+	{.ccode = "MT", .region = REGIONS_ETSI},
+	{.ccode = "MU", .region = REGIONS_FCC},
+	{.ccode = "MV", .region = REGIONS_ETSI},
+	{.ccode = "MW", .region = REGIONS_ETSI},
+	{.ccode = "MX", .region = REGIONS_FCC},
+	{.ccode = "MY", .region = REGIONS_ETSI},
+	{.ccode = "NA", .region = REGIONS_ETSI},
+	{.ccode = "NG", .region = REGIONS_ETSI},
+	{.ccode = "NI", .region = REGIONS_FCC},
+	{.ccode = "NL", .region = REGIONS_ETSI},
+	{.ccode = "NO", .region = REGIONS_ETSI},
+	{.ccode = "NP", .region = REGIONS_JP},
+	{.ccode = "NZ", .region = REGIONS_ETSI},
+	{.ccode = "OM", .region = REGIONS_ETSI},
+	{.ccode = "PA", .region = REGIONS_FCC},
+	{.ccode = "PE", .region = REGIONS_ETSI},
+	{.ccode = "PF", .region = REGIONS_ETSI},
+	{.ccode = "PG", .region = REGIONS_FCC},
+	{.ccode = "PH", .region = REGIONS_ETSI},
+	{.ccode = "PK", .region = REGIONS_ETSI},
+	{.ccode = "PL", .region = REGIONS_ETSI},
+	{.ccode = "PM", .region = REGIONS_ETSI},
+	{.ccode = "PR", .region = REGIONS_FCC},
+	{.ccode = "PT", .region = REGIONS_ETSI},
+	{.ccode = "PW", .region = REGIONS_FCC},
+	{.ccode = "PY", .region = REGIONS_FCC},
+	{.ccode = "QA", .region = REGIONS_ETSI},
+	{.ccode = "RE", .region = REGIONS_ETSI},
+	{.ccode = "RO", .region = REGIONS_ETSI},
+	{.ccode = "RS", .region = REGIONS_ETSI},
+	{.ccode = "RU", .region = REGIONS_ETSI},
+	{.ccode = "RW", .region = REGIONS_FCC},
+	{.ccode = "SA", .region = REGIONS_ETSI},
+	{.ccode = "SE", .region = REGIONS_ETSI},
+	{.ccode = "SG", .region = REGIONS_ETSI},
+	{.ccode = "SI", .region = REGIONS_ETSI},
+	{.ccode = "SK", .region = REGIONS_ETSI},
+	{.ccode = "SM", .region = REGIONS_ETSI},
+	{.ccode = "SN", .region = REGIONS_FCC},
+	{.ccode = "SR", .region = REGIONS_ETSI},
+	{.ccode = "SV", .region = REGIONS_FCC},
+	{.ccode = "SY", .region = REGIONS_DEFAULT},
+	{.ccode = "TC", .region = REGIONS_FCC},
+	{.ccode = "TD", .region = REGIONS_ETSI},
+	{.ccode = "TG", .region = REGIONS_ETSI},
+	{.ccode = "TH", .region = REGIONS_ETSI},
+	{.ccode = "TJ", .region = REGIONS_ETSI},
+	{.ccode = "TM", .region = REGIONS_ETSI},
+	{.ccode = "TN", .region = REGIONS_ETSI},
+	{.ccode = "TR", .region = REGIONS_ETSI},
+	{.ccode = "TT", .region = REGIONS_FCC},
+	{.ccode = "TW", .region = REGIONS_FCC},
+	{.ccode = "TZ", .region = REGIONS_DEFAULT},
+	{.ccode = "UA", .region = REGIONS_ETSI},
+	{.ccode = "UG", .region = REGIONS_FCC},
+	{.ccode = "UY", .region = REGIONS_ETSI},
+	{.ccode = "UZ", .region = REGIONS_ETSI},
+	{.ccode = "VC", .region = REGIONS_ETSI},
+	{.ccode = "VE", .region = REGIONS_FCC},
+	{.ccode = "VI", .region = REGIONS_FCC},
+	{.ccode = "VN", .region = REGIONS_JP},
+	{.ccode = "VU", .region = REGIONS_FCC},
+	{.ccode = "WF", .region = REGIONS_ETSI},
+	{.ccode = "WS", .region = REGIONS_ETSI},
+	{.ccode = "YE", .region = REGIONS_DEFAULT},
+	{.ccode = "YT", .region = REGIONS_ETSI},
+	{.ccode = "ZA", .region = REGIONS_ETSI},
+	{.ccode = "ZM", .region = REGIONS_ETSI},
+	{.ccode = "ZW", .region = REGIONS_ETSI},
+	{.ccode = "XK", .region = REGIONS_ETSI},
+};
+
+struct rwnx_plat *g_rwnx_plat;
+
+struct userconfig_info_t {
+	struct txpwr_lvl_conf txpwr_lvl;
+	struct txpwr_lvl_conf_v2 txpwr_lvl_v2;
+	struct txpwr_lvl_conf_v3 txpwr_lvl_v3;
+	struct txpwr_lvl_conf_v3 txpwr_lvl_v3_gp[REGION_NUM_MAX];
+	struct txpwr_lvl_adj_conf txpwr_lvl_adj;
+	struct txpwr_loss_conf txpwr_loss;
+	struct txpwr_ofst_conf txpwr_ofst;
+	struct txpwr_ofst2x_conf txpwr_ofst2x;
+	struct xtal_cap_conf xtal_cap;
+};
+
+static struct userconfig_info_t userconfig_info = {
+	.txpwr_lvl = { .enable = 1,
+				  .dsss = 9,
+				  .ofdmlowrate_2g4 = 8,
+				  .ofdm64qam_2g4 = 8,
+				  .ofdm256qam_2g4 = 8,
+				  .ofdm1024qam_2g4 = 8,
+				  .ofdmlowrate_5g = 11,
+				  .ofdm64qam_5g = 10,
+				  .ofdm256qam_5g = 9,
+				  .ofdm1024qam_5g = 9},
+	.txpwr_lvl_v2 =	{ .enable = 1,
+			.pwrlvl_11b_11ag_2g4 =
+				// 1M,   2M,   5M5,  11M,  6M,   9M,   12M,  18M,  24M,  36M,
+				// 48M,  54M
+			{20, 20, 20, 20, 20, 20, 20, 20, 18, 18, 16, 16},
+			.pwrlvl_11n_11ac_2g4 =
+				// MCS0, MCS1, MCS2, MCS3, MCS4, MCS5, MCS6, MCS7, MCS8, MCS9
+			{20, 20, 20, 20, 18, 18, 16, 16, 16, 16},
+			.pwrlvl_11ax_2g4 =
+				// MCS0, MCS1, MCS2, MCS3, MCS4, MCS5, MCS6, MCS7, MCS8, MCS9,
+				// MCS10,MCS11
+			{20, 20, 20, 20, 18, 18, 16, 16, 16, 16, 15, 15},
+		},
+	.txpwr_lvl_v3 =	{ .enable = 1,
+			.pwrlvl_11b_11ag_2g4 =
+				// 1M,   2M,   5M5,  11M,  6M,   9M,   12M,  18M,  24M,  36M,
+				// 48M,  54M
+			{20, 20, 20, 20, 20, 20, 20, 20, 18, 18, 16, 16},
+			.pwrlvl_11n_11ac_2g4 =
+				// MCS0, MCS1, MCS2, MCS3, MCS4, MCS5, MCS6, MCS7, MCS8, MCS9
+			{20, 20, 20, 20, 18, 18, 16, 16, 16, 16},
+			.pwrlvl_11ax_2g4 =
+				// MCS0, MCS1, MCS2, MCS3, MCS4, MCS5, MCS6, MCS7, MCS8, MCS9,
+				// MCS10,MCS11
+			{20, 20, 20, 20, 18, 18, 16, 16, 16, 16, 15, 15},
+			.pwrlvl_11a_5g =
+				// NA,   NA,   NA,   NA,   6M,   9M,   12M,  18M,  24M,  36M,
+				// 48M,  54M
+			{0x80, 0x80, 0x80, 0x80, 20, 20, 20, 20, 18, 18, 16, 16},
+			.pwrlvl_11n_11ac_5g =
+				// MCS0, MCS1, MCS2, MCS3, MCS4, MCS5, MCS6, MCS7, MCS8, MCS9
+			{20, 20, 20, 20, 18, 18, 16, 16, 16, 15},
+			.pwrlvl_11ax_5g =
+				// MCS0, MCS1, MCS2, MCS3, MCS4, MCS5, MCS6, MCS7, MCS8, MCS9,
+				// MCS10,MCS11
+			{20, 20, 20, 20, 18, 18, 16, 16, 16, 15, 14, 14},
+		},
+	.txpwr_lvl_v3_gp[0] = { .enable = 1,
+			.pwrlvl_11b_11ag_2g4 =
+				// 1M,   2M,   5M5,  11M,  6M,   9M,   12M,  18M,  24M,  36M,
+				// 48M,  54M
+			{20, 20, 20, 20, 20, 20, 20, 20, 18, 18, 16, 16},
+			.pwrlvl_11n_11ac_2g4 =
+				// MCS0, MCS1, MCS2, MCS3, MCS4, MCS5, MCS6, MCS7, MCS8, MCS9
+			{20, 20, 20, 20, 18, 18, 16, 16, 16, 16},
+			.pwrlvl_11ax_2g4 =
+				// MCS0, MCS1, MCS2, MCS3, MCS4, MCS5, MCS6, MCS7, MCS8, MCS9,
+				// MCS10,MCS11
+			{20, 20, 20, 20, 18, 18, 16, 16, 16, 16, 15, 15},
+			.pwrlvl_11a_5g =
+				// NA,   NA,   NA,   NA,   6M,   9M,   12M,  18M,  24M,  36M,
+				// 48M,  54M
+			{0x80, 0x80, 0x80, 0x80, 20, 20, 20, 20, 18, 18, 16, 16},
+			.pwrlvl_11n_11ac_5g =
+				// MCS0, MCS1, MCS2, MCS3, MCS4, MCS5, MCS6, MCS7, MCS8, MCS9
+			{20, 20, 20, 20, 18, 18, 16, 16, 16, 15},
+			.pwrlvl_11ax_5g =
+				// MCS0, MCS1, MCS2, MCS3, MCS4, MCS5, MCS6, MCS7, MCS8, MCS9,
+				// MCS10,MCS11
+			{20, 20, 20, 20, 18, 18, 16, 16, 16, 15, 14, 14},
+		},
+	.txpwr_lvl_v3_gp[1] = { .enable = 1,
+			.pwrlvl_11b_11ag_2g4 = {20, 20, 20, 20, 20, 20, 20, 20, 18, 18, 16, 16},
+			.pwrlvl_11n_11ac_2g4 = {20, 20, 20, 20, 18, 18, 16, 16, 16, 16},
+			.pwrlvl_11ax_2g4 = {20, 20, 20, 20, 18, 18, 16, 16, 16, 16, 15, 15},
+			.pwrlvl_11a_5g = {0x80, 0x80, 0x80, 0x80, 20, 20, 20, 20, 18, 18, 16, 16},
+			.pwrlvl_11n_11ac_5g = {20, 20, 20, 20, 18, 18, 16, 16, 16, 15},
+			.pwrlvl_11ax_5g = {20, 20, 20, 20, 18, 18, 16, 16, 16, 15, 14, 14},
+		},
+	.txpwr_lvl_v3_gp[2] = { .enable = 1,
+			.pwrlvl_11b_11ag_2g4 = {20, 20, 20, 20, 20, 20, 20, 20, 18, 18, 16, 16},
+			.pwrlvl_11n_11ac_2g4 = {20, 20, 20, 20, 18, 18, 16, 16, 16, 16},
+			.pwrlvl_11ax_2g4 = {20, 20, 20, 20, 18, 18, 16, 16, 16, 16, 15, 15},
+			.pwrlvl_11a_5g = {0x80, 0x80, 0x80, 0x80, 20, 20, 20, 20, 18, 18, 16, 16},
+			.pwrlvl_11n_11ac_5g = {20, 20, 20, 20, 18, 18, 16, 16, 16, 15},
+			.pwrlvl_11ax_5g = {20, 20, 20, 20, 18, 18, 16, 16, 16, 15, 14, 14},
+		},
+	.txpwr_lvl_v3_gp[3] = { .enable = 1,
+			.pwrlvl_11b_11ag_2g4 = {20, 20, 20, 20, 20, 20, 20, 20, 18, 18, 16, 16},
+			.pwrlvl_11n_11ac_2g4 = {20, 20, 20, 20, 18, 18, 16, 16, 16, 16},
+			.pwrlvl_11ax_2g4 = {20, 20, 20, 20, 18, 18, 16, 16, 16, 16, 15, 15},
+			.pwrlvl_11a_5g = {0x80, 0x80, 0x80, 0x80, 20, 20, 20, 20, 18, 18, 16, 16},
+			.pwrlvl_11n_11ac_5g = {20, 20, 20, 20, 18, 18, 16, 16, 16, 15},
+			.pwrlvl_11ax_5g = {20, 20, 20, 20, 18, 18, 16, 16, 16, 15, 14, 14},
+		},
+	.txpwr_lvl_v3_gp[4] = { .enable = 1,
+			.pwrlvl_11b_11ag_2g4 = {20, 20, 20, 20, 20, 20, 20, 20, 18, 18, 16, 16},
+			.pwrlvl_11n_11ac_2g4 = {20, 20, 20, 20, 18, 18, 16, 16, 16, 16},
+			.pwrlvl_11ax_2g4 = {20, 20, 20, 20, 18, 18, 16, 16, 16, 16, 15, 15},
+			.pwrlvl_11a_5g = {0x80, 0x80, 0x80, 0x80, 20, 20, 20, 20, 18, 18, 16, 16},
+			.pwrlvl_11n_11ac_5g = {20, 20, 20, 20, 18, 18, 16, 16, 16, 15},
+			.pwrlvl_11ax_5g = {20, 20, 20, 20, 18, 18, 16, 16, 16, 15, 14, 14},
+		},
+	.txpwr_loss = { .loss_enable_2g4 = 1,
+			.loss_value_2g4 = 0,
+			.loss_enable_5g = 1,
+			.loss_value_5g = 0,
+		},
+	.txpwr_ofst = { .enable = 1,
+			.chan_1_4 = 0,
+			.chan_5_9 = 0,
+			.chan_10_13 = 0,
+			.chan_36_64 = 0,
+			.chan_100_120 = 0,
+			.chan_122_140 = 0,
+			.chan_142_165 = 0,
+		},
+	.txpwr_ofst2x = { .enable = 0,
+			.pwrofst2x_tbl_2g4 = {
+					// ch1-4, ch5-9, ch10-13
+					{0, 0, 0}, // 11b
+					{0, 0, 0}, // ofdm_highrate
+					{0, 0, 0}, // ofdm_lowrate
+				},
+			.pwrofst2x_tbl_5g = {
+					// ch42,  ch58, ch106,ch122,ch138,ch155
+					{0, 0, 0, 0, 0, 0}, // ofdm_lowrate
+					{0, 0, 0, 0, 0, 0}, // ofdm_highrate
+					{0, 0, 0, 0, 0, 0}, // ofdm_midrate
+				},
+		},
+	.xtal_cap = { .enable = 0,
+			.xtal_cap = 24,
+			.xtal_cap_fine = 31,
+		},
+};
+
+#ifdef CONFIG_AIC8800_POWER_LIMIT
+#define POWER_LIMIT_INVALID_VAL POWER_LEVEL_INVALID_VAL
+
+#define POWER_LIMIT_CC_MATCHED_BIT (0x1U << 0)
+
+struct txpwr_lmt_info_t {
+	u8 ch_cnt_2g4;
+	u8 ch_cnt_5g;
+	u8 ch_num_2g4[MAC_DOMAINCHANNEL_24G_MAX];
+	u8 ch_num_5g[MAC_DOMAINCHANNEL_5G_MAX];
+	s8 max_pwr_2g4[MAC_DOMAINCHANNEL_24G_MAX];
+	s8 max_pwr_5g[MAC_DOMAINCHANNEL_5G_MAX];
+};
+
+struct powerlimit_info_t {
+	u32 flags;
+	struct txpwr_lmt_info_t txpwr_lmt;
+};
+
+static struct powerlimit_info_t powerlimit_info = {
+	0,
+};
+#endif
+
+int aicwf_request_firmware(const struct firmware **fw, const char *name,
+			   struct device *dev)
+{
+	char *fw_name;
+	int ret;
+
+	if (!name || !name[0])
+		return -EINVAL;
+
+	fw_name = kasprintf(GFP_KERNEL, AIC8800_FW_DIR "%s", name);
+	if (!fw_name)
+		return -ENOMEM;
+
+	ret = request_firmware(fw, fw_name, dev);
+	kfree(fw_name);
+
+	return ret;
+}
+
+#define MD5PINRT                                                               \
+	"file "                                                                    \
+	"md5:%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\r\n"
+
+static int rwnx_load_firmware(u32 **fw_buf, const char *name,
+			      struct device *device)
+{
+	const struct firmware *fw = NULL;
+	u32 *dst = NULL;
+	void *buffer = NULL;
+	u8 digest[MD5_DIGEST_SIZE];
+	int size = 0;
+	int ret = 0;
+
+	AICWFDBG(LOGINFO, "AICWF %s: request firmware = %s\n", __func__, name);
+
+	ret = aicwf_request_firmware(&fw, name, device);
+
+	if (ret < 0) {
+		AICWFDBG(LOGDEBUG, "AICWF Load %s fail\n", name);
+		return ret;
+	}
+
+	size = fw->size;
+	dst = (u32 *)fw->data;
+
+	if (size <= 0) {
+		AICWFDBG(LOGDEBUG, "AICWF wrong size of firmware file\n");
+		release_firmware(fw);
+		return -1;
+	}
+
+	buffer = vmalloc(size);
+	if (!buffer) {
+		release_firmware(fw);
+		return -ENOMEM;
+	}
+	memset(buffer, 0, size);
+	memcpy(buffer, dst, size);
+
+	*fw_buf = buffer;
+
+	md5(buffer, size, digest);
+	AICWFDBG(LOGDEBUG, "AICWF " MD5PINRT, digest[0], digest[1], digest[2], digest[3],
+		 digest[4], digest[5], digest[6], digest[7], digest[8], digest[9],
+		 digest[10], digest[11], digest[12], digest[13], digest[14], digest[15]);
+
+	release_firmware(fw);
+
+	return size;
+}
+
+/* buffer is allocated by kzalloc */
+int rwnx_request_firmware_common(struct rwnx_hw *rwnx_hw, u32 **buffer,
+				 const char *filename)
+{
+	int size;
+
+	AICWFDBG(LOGDEBUG, "### Load file %s\n", filename);
+
+	size = rwnx_load_firmware(buffer, filename, rwnx_hw->dev);
+
+	return size;
+}
+
+static void rwnx_restore_firmware(u32 **fw_buf)
+{
+	vfree(*fw_buf);
+	*fw_buf = NULL;
+}
+
+void rwnx_release_firmware_common(u32 **buffer)
+{
+	rwnx_restore_firmware(buffer);
+}
+
+/**
+ * rwnx_plat_bin_fw_upload_2 - Load the requested binary FW into embedded
+ * side.
+ *
+ * @rwnx_hw: Main driver data
+ * @fw_addr: Address where the fw must be loaded
+ * @filename: Name of the fw.
+ *
+ * Load a fw, stored as a binary file, into the specified address
+ *
+ * Return: 0 on success, or a negative error code.
+ */
+int rwnx_plat_bin_fw_upload_2(struct rwnx_hw *rwnx_hw, u32 fw_addr,
+			      char *filename)
+{
+	int err = 0;
+	unsigned int i = 0, size;
+	//    u32 *src;
+	u32 *dst = NULL;
+
+	/* Copy the file on the Embedded side */
+	AICWFDBG(LOGINFO, "### Upload %s firmware, @ = %x\n", filename, fw_addr);
+
+	size = rwnx_request_firmware_common(rwnx_hw, &dst, filename);
+	if (!dst) {
+		AICWFDBG(LOGERROR, "No such file or directory\n");
+		return -1;
+	}
+	if (size <= 0) {
+		AICWFDBG(LOGERROR, "wrong size of firmware file\n");
+		dst = NULL;
+		err = -1;
+	}
+
+	AICWFDBG(LOGINFO, "size=%d, dst[0]=%x\n", size, dst[0]);
+	if (size > 512) {
+		for (; i < (size - 512); i += 512) {
+			// AICWFDBG(LOGDEBUG, "AICWF wr blk 0: %p -> %x\r\n",
+			// dst + i / 4, fw_addr + i);
+			err = rwnx_send_dbg_mem_block_write_req(rwnx_hw, fw_addr + i, 512,
+								dst + i / 4);
+			if (err) {
+				AICWFDBG(LOGERROR, "bin upload fail: %x, err:%d\r\n",
+					 fw_addr + i, err);
+				break;
+			}
+		}
+	}
+	if (!err && i < size) {
+		// AICWFDBG(LOGDEBUG, "AICWF wr blk 1: %p -> %x\r\n", dst + i / 4, fw_addr + i);
+		err = rwnx_send_dbg_mem_block_write_req(rwnx_hw, fw_addr + i, size - i,
+							dst + i / 4);
+		if (err) {
+			AICWFDBG(LOGERROR, "bin upload fail: %x, err:%d\r\n", fw_addr + i,
+				 err);
+		}
+	}
+
+	if (dst)
+		rwnx_release_firmware_common(&dst);
+
+	return err;
+}
+
+struct nvram_info_t {
+	struct txpwr_idx_conf txpwr_idx;
+	struct txpwr_ofst_conf txpwr_ofst;
+	struct xtal_cap_conf xtal_cap;
+};
+
+static struct nvram_info_t nvram_info = {
+	.txpwr_idx = {.enable = 1,
+			.dsss = 9,
+			.ofdmlowrate_2g4 = 8,
+			.ofdm64qam_2g4 = 8,
+			.ofdm256qam_2g4 = 8,
+			.ofdm1024qam_2g4 = 8,
+			.ofdmlowrate_5g = 11,
+			.ofdm64qam_5g = 10,
+			.ofdm256qam_5g = 9,
+			.ofdm1024qam_5g = 9},
+	.txpwr_ofst = { .enable = 1,
+			.chan_1_4 = 0,
+			.chan_5_9 = 0,
+			.chan_10_13 = 0,
+			.chan_36_64 = 0,
+			.chan_100_120 = 0,
+			.chan_122_140 = 0,
+			.chan_142_165 = 0,
+		},
+	.xtal_cap = { .enable = 0,
+			.xtal_cap = 24,
+			.xtal_cap_fine = 31,
+		},
+};
+
+void get_userconfig_txpwr_ofst_in_fdrv(struct txpwr_ofst_conf *txpwr_ofst)
+{
+	txpwr_ofst->enable = userconfig_info.txpwr_ofst.enable;
+	txpwr_ofst->chan_1_4 = userconfig_info.txpwr_ofst.chan_1_4;
+	txpwr_ofst->chan_5_9 = userconfig_info.txpwr_ofst.chan_5_9;
+	txpwr_ofst->chan_10_13 = userconfig_info.txpwr_ofst.chan_10_13;
+	txpwr_ofst->chan_36_64 = userconfig_info.txpwr_ofst.chan_36_64;
+	txpwr_ofst->chan_100_120 = userconfig_info.txpwr_ofst.chan_100_120;
+	txpwr_ofst->chan_122_140 = userconfig_info.txpwr_ofst.chan_122_140;
+	txpwr_ofst->chan_142_165 = userconfig_info.txpwr_ofst.chan_142_165;
+
+	AICWFDBG(LOGINFO, "%s:enable      :%d\r\n", __func__, txpwr_ofst->enable);
+	AICWFDBG(LOGINFO, "%s:chan_1_4    :%d\r\n", __func__, txpwr_ofst->chan_1_4);
+	AICWFDBG(LOGINFO, "%s:chan_5_9    :%d\r\n", __func__, txpwr_ofst->chan_5_9);
+	AICWFDBG(LOGINFO, "%s:chan_10_13  :%d\r\n", __func__,
+		 txpwr_ofst->chan_10_13);
+	AICWFDBG(LOGINFO, "%s:chan_36_64  :%d\r\n", __func__,
+		 txpwr_ofst->chan_36_64);
+	AICWFDBG(LOGINFO, "%s:chan_100_120:%d\r\n", __func__,
+		 txpwr_ofst->chan_100_120);
+	AICWFDBG(LOGINFO, "%s:chan_122_140:%d\r\n", __func__,
+		 txpwr_ofst->chan_122_140);
+	AICWFDBG(LOGINFO, "%s:chan_142_165:%d\r\n", __func__,
+		 txpwr_ofst->chan_142_165);
+}
+
+void get_userconfig_txpwr_ofst2x_in_fdrv(struct txpwr_ofst2x_conf *txpwr_ofst2x)
+{
+	int type, ch_grp;
+	*txpwr_ofst2x = userconfig_info.txpwr_ofst2x;
+	AICWFDBG(LOGINFO, "%s:enable      :%d\r\n", __func__, txpwr_ofst2x->enable);
+	AICWFDBG(LOGDEBUG,
+		 "pwrofst2x 2.4g: [0]:11b, [1]:ofdm_highrate, [2]:ofdm_lowrate\n  chan=\t1-4\t5-9\t10-13");
+
+	for (type = 0; type < 3; type++) {
+		AICWFDBG(LOGDEBUG, "\n  [%d] =", type);
+		for (ch_grp = 0; ch_grp < 3; ch_grp++) {
+			AICWFDBG(LOGDEBUG, "\t%d",
+				 txpwr_ofst2x->pwrofst2x_tbl_2g4[type][ch_grp]);
+		}
+	}
+	AICWFDBG(LOGDEBUG,
+		 "\npwrofst2x 5g: [0]:ofdm_lowrate, [1]:ofdm_highrate, [2]:ofdm_midrate\n  chan=\t36-50\t51-64\t98-114\t115-130\t131-146\t147-166");
+	for (type = 0; type < 3; type++) {
+		AICWFDBG(LOGDEBUG, "\n  [%d] =", type);
+		for (ch_grp = 0; ch_grp < 6; ch_grp++) {
+			AICWFDBG(LOGDEBUG, "\t%d",
+				 txpwr_ofst2x->pwrofst2x_tbl_5g[type][ch_grp]);
+		}
+	}
+	AICWFDBG(LOGDEBUG, "\n");
+}
+
+void get_userconfig_txpwr_idx(struct txpwr_idx_conf *txpwr_idx)
+{
+	memcpy(txpwr_idx, &nvram_info.txpwr_idx, sizeof(struct txpwr_idx_conf));
+}
+
+void get_userconfig_txpwr_ofst(struct txpwr_ofst_conf *txpwr_ofst)
+{
+	memcpy(txpwr_ofst, &nvram_info.txpwr_ofst, sizeof(struct txpwr_ofst_conf));
+}
+
+void get_userconfig_xtal_cap(struct xtal_cap_conf *xtal_cap)
+{
+	if (nvram_info.xtal_cap.enable)
+		*xtal_cap = nvram_info.xtal_cap;
+
+	if (userconfig_info.xtal_cap.enable)
+		*xtal_cap = userconfig_info.xtal_cap;
+
+	AICWFDBG(LOGINFO, "%s:enable       :%d\r\n", __func__, xtal_cap->enable);
+	AICWFDBG(LOGINFO, "%s:xtal_cap     :%d\r\n", __func__, xtal_cap->xtal_cap);
+	AICWFDBG(LOGINFO, "%s:xtal_cap_fine:%d\r\n", __func__,
+		 xtal_cap->xtal_cap_fine);
+}
+
+s8 get_txpwr_max(s8 power)
+{
+	int i = 0;
+
+	for (i = 0; i <= 11; i++) {
+		if (power < userconfig_info.txpwr_lvl_v3.pwrlvl_11b_11ag_2g4[i])
+			power = userconfig_info.txpwr_lvl_v3.pwrlvl_11b_11ag_2g4[i];
+	}
+	for (i = 0; i <= 9; i++) {
+		if (power < userconfig_info.txpwr_lvl_v3.pwrlvl_11n_11ac_2g4[i])
+			power = userconfig_info.txpwr_lvl_v3.pwrlvl_11n_11ac_2g4[i];
+	}
+	for (i = 0; i <= 11; i++) {
+		if (power < userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_2g4[i])
+			power = userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_2g4[i];
+	}
+	for (i = 4; i <= 11; i++) {
+		if (power < userconfig_info.txpwr_lvl_v3.pwrlvl_11a_5g[i])
+			power = userconfig_info.txpwr_lvl_v3.pwrlvl_11a_5g[i];
+	}
+	for (i = 0; i <= 9; i++) {
+		if (power < userconfig_info.txpwr_lvl_v3.pwrlvl_11n_11ac_5g[i])
+			power = userconfig_info.txpwr_lvl_v3.pwrlvl_11n_11ac_5g[i];
+	}
+	for (i = 0; i <= 11; i++) {
+		if (power < userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_5g[i])
+			power = userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_5g[i];
+	}
+
+	if (userconfig_info.txpwr_loss.loss_enable_2g4 == 1 ||
+	    userconfig_info.txpwr_loss.loss_enable_5g == 1) {
+		if (userconfig_info.txpwr_loss.loss_value_2g4 <
+		    userconfig_info.txpwr_loss.loss_value_5g)
+			power += userconfig_info.txpwr_loss.loss_value_5g;
+		else
+			power += userconfig_info.txpwr_loss.loss_value_2g4;
+	}
+
+	AICWFDBG(LOGDEBUG, "AICWF %s:txpwr_max:%d \r\n", __func__, power);
+	return power;
+}
+
+void set_txpwr_loss_ofst(s8 value)
+{
+	int i = 0;
+
+	for (i = 0; i <= 11; i++)
+		userconfig_info.txpwr_lvl_v3.pwrlvl_11b_11ag_2g4[i] += value;
+
+	for (i = 0; i <= 9; i++)
+		userconfig_info.txpwr_lvl_v3.pwrlvl_11n_11ac_2g4[i] += value;
+
+	for (i = 0; i <= 11; i++)
+		userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_2g4[i] += value;
+
+	for (i = 4; i <= 11; i++)
+		userconfig_info.txpwr_lvl_v3.pwrlvl_11a_5g[i] += value;
+
+	for (i = 0; i <= 9; i++)
+		userconfig_info.txpwr_lvl_v3.pwrlvl_11n_11ac_5g[i] += value;
+
+	for (i = 0; i <= 11; i++)
+		userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_5g[i] += value;
+
+	AICWFDBG(LOGDEBUG, "AICWF %s:value:%d\r\n", __func__, value);
+}
+
+#define MATCH_NODE(type, node, cfg_key) {cfg_key, offsetof(type, node)}
+
+struct parse_match_t {
+	char keyname[64];
+	int offset;
+};
+
+static const char * const parse_key_prefix[] = {
+	[0x01] = "module0_",
+	[0x21] = "module1_",
+};
+
+static const struct parse_match_t parse_match_tab[] = {
+	MATCH_NODE(struct nvram_info_t, txpwr_idx.enable, "enable"),
+	MATCH_NODE(struct nvram_info_t, txpwr_idx.dsss, "dsss"),
+	MATCH_NODE(struct nvram_info_t, txpwr_idx.ofdmlowrate_2g4, "ofdmlowrate_2g4"),
+	MATCH_NODE(struct nvram_info_t, txpwr_idx.ofdm64qam_2g4, "ofdm64qam_2g4"),
+	MATCH_NODE(struct nvram_info_t, txpwr_idx.ofdm256qam_2g4, "ofdm256qam_2g4"),
+	MATCH_NODE(struct nvram_info_t, txpwr_idx.ofdm1024qam_2g4, "ofdm1024qam_2g4"),
+	MATCH_NODE(struct nvram_info_t, txpwr_idx.ofdmlowrate_5g, "ofdmlowrate_5g"),
+	MATCH_NODE(struct nvram_info_t, txpwr_idx.ofdm64qam_5g, "ofdm64qam_5g"),
+	MATCH_NODE(struct nvram_info_t, txpwr_idx.ofdm256qam_5g, "ofdm256qam_5g"),
+	MATCH_NODE(struct nvram_info_t, txpwr_idx.ofdm1024qam_5g, "ofdm1024qam_5g"),
+
+	MATCH_NODE(struct nvram_info_t, txpwr_ofst.enable, "ofst_enable"),
+	MATCH_NODE(struct nvram_info_t, txpwr_ofst.chan_1_4, "ofst_chan_1_4"),
+	MATCH_NODE(struct nvram_info_t, txpwr_ofst.chan_5_9, "ofst_chan_5_9"),
+	MATCH_NODE(struct nvram_info_t, txpwr_ofst.chan_10_13, "ofst_chan_10_13"),
+	MATCH_NODE(struct nvram_info_t, txpwr_ofst.chan_36_64, "ofst_chan_36_64"),
+	MATCH_NODE(struct nvram_info_t, txpwr_ofst.chan_100_120, "ofst_chan_100_120"),
+	MATCH_NODE(struct nvram_info_t, txpwr_ofst.chan_122_140, "ofst_chan_122_140"),
+	MATCH_NODE(struct nvram_info_t, txpwr_ofst.chan_142_165, "ofst_chan_142_165"),
+
+	MATCH_NODE(struct nvram_info_t, xtal_cap.enable, "xtal_enable"),
+	MATCH_NODE(struct nvram_info_t, xtal_cap.xtal_cap, "xtal_cap"),
+	MATCH_NODE(struct nvram_info_t, xtal_cap.xtal_cap_fine, "xtal_cap_fine"),
+};
+
+static int parse_key_val(const char *str, const char *key, char *val)
+{
+	const char *p = NULL;
+	const char *dst = NULL;
+	int keysize = 0;
+	int bufsize = 0;
+
+	if (!str || !key || !val)
+		return -1;
+
+	keysize = strlen(key);
+	bufsize = strlen(str);
+	if (bufsize <= keysize)
+		return -1;
+
+	p = str;
+	while (*p != 0 && *p == ' ')
+		p++;
+
+	if (*p == '#')
+		return -1;
+
+	if (str + bufsize - p <= keysize)
+		return -1;
+
+	if (strncmp(p, key, keysize) != 0)
+		return -1;
+
+	p += keysize;
+
+	while (*p != 0 && *p == ' ')
+		p++;
+
+	if (*p != '=')
+		return -1;
+
+	p++;
+	while (*p != 0 && *p == ' ')
+		p++;
+
+	if (*p == '"')
+		p++;
+
+	dst = p;
+	while (*p != 0)
+		p++;
+
+	p--;
+	while (*p == ' ')
+		p--;
+
+	if (*p == '"')
+		p--;
+
+	while (*p == '\r' || *p == '\n')
+		p--;
+
+	p++;
+	strscpy(val, dst, p - dst);
+	val[p - dst] = 0;
+	return 0;
+}
+
+int rwnx_atoi(char *value)
+{
+	int len = 0;
+	int i = 0;
+	int result = 0;
+	int flag = 1;
+
+	if (value[0] == '-') {
+		flag = -1;
+		value++;
+	}
+	len = strlen(value);
+
+	for (i = 0; i < len; i++) {
+		result = result * 10;
+		if (value[i] >= 48 && value[i] <= 57) {
+			result += value[i] - 48;
+		} else {
+			result = 0;
+			break;
+		}
+	}
+
+	return result * flag;
+}
+
+static int rwnx_parse_mul_value(char *line, char *val[])
+{
+	int nargs = 0;
+
+	while (nargs < 10) {
+		while ((*line == ' ') || (*line == '\t'))
+			++line;
+
+		if (*line == '\0') { /* end of line, no more args    */
+			//AICWFDBG(LOGDEBUG, "%s p1,%d\n", __func__, nargs);
+			return nargs;
+		}
+
+		val[nargs++] = line; /* begin of argument string    */
+
+		/* find end of string */
+		while (*line && (*line != ' ') && (*line != '\t'))
+			++line;
+
+		if (*line == '\0') { /* end of line, no more args    */
+			//AICWFDBG(LOGDEBUG, "%s p2,%d\n", __func__, nargs);
+			return nargs;
+		}
+
+		*line++ = '\0'; /* terminate current arg     */
+	}
+
+	AICWFDBG(LOGDEBUG, "nargs: %d\n", nargs);
+	//AICWFDBG(LOGDEBUG, "%s exit,%d\n", __func__, nargs);
+	return nargs;
+}
+
+int get_ccode_region(char *ccode)
+{
+	int i, cnt;
+
+	AICWFDBG(LOGDEBUG, "%s ccode:%s\r\n", __func__, ccode);
+	cnt = ARRAY_SIZE(reg_tables);
+
+	for (i = 0; i < cnt; i++) {
+		if (reg_tables[i].ccode[0] == ccode[0] &&
+		    reg_tables[i].ccode[1] == ccode[1]) {
+			AICWFDBG(LOGDEBUG, "region: %d\r\n", reg_tables[i].region);
+			return reg_tables[i].region;
+		}
+	}
+	AICWFDBG(LOGDEBUG, "use default region\r\n");
+	return REGIONS_DEFAULT;
+}
+
+void rwnx_plat_nvram_set_value(char *command, char *value)
+{
+	// TODO send command
+	AICWFDBG(LOGDEBUG, "%s:command=%s value=%s\n", __func__, command, value);
+	if (!strcmp(command, "enable")) {
+		userconfig_info.txpwr_lvl.enable = rwnx_atoi(value);
+		userconfig_info.txpwr_lvl_v2.enable = rwnx_atoi(value);
+	} else if (!strcmp(command, "dsss")) {
+		userconfig_info.txpwr_lvl.dsss = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofdmlowrate_2g4")) {
+		userconfig_info.txpwr_lvl.ofdmlowrate_2g4 = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofdm64qam_2g4")) {
+		userconfig_info.txpwr_lvl.ofdm64qam_2g4 = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofdm256qam_2g4")) {
+		userconfig_info.txpwr_lvl.ofdm256qam_2g4 = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofdm1024qam_2g4")) {
+		userconfig_info.txpwr_lvl.ofdm1024qam_2g4 = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofdmlowrate_5g")) {
+		userconfig_info.txpwr_lvl.ofdmlowrate_5g = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofdm64qam_5g")) {
+		userconfig_info.txpwr_lvl.ofdm64qam_5g = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofdm256qam_5g")) {
+		userconfig_info.txpwr_lvl.ofdm256qam_5g = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofdm1024qam_5g")) {
+		userconfig_info.txpwr_lvl.ofdm1024qam_5g = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11b_11ag_1m_2g4")) {
+		userconfig_info.txpwr_lvl_v2.pwrlvl_11b_11ag_2g4[0] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11b_11ag_2m_2g4")) {
+		userconfig_info.txpwr_lvl_v2.pwrlvl_11b_11ag_2g4[1] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11b_11ag_5m5_2g4")) {
+		userconfig_info.txpwr_lvl_v2.pwrlvl_11b_11ag_2g4[2] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11b_11ag_11m_2g4")) {
+		userconfig_info.txpwr_lvl_v2.pwrlvl_11b_11ag_2g4[3] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11b_11ag_6m_2g4")) {
+		userconfig_info.txpwr_lvl_v2.pwrlvl_11b_11ag_2g4[4] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11b_11ag_9m_2g4")) {
+		userconfig_info.txpwr_lvl_v2.pwrlvl_11b_11ag_2g4[5] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11b_11ag_12m_2g4")) {
+		userconfig_info.txpwr_lvl_v2.pwrlvl_11b_11ag_2g4[6] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11b_11ag_18m_2g4")) {
+		userconfig_info.txpwr_lvl_v2.pwrlvl_11b_11ag_2g4[7] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11b_11ag_24m_2g4")) {
+		userconfig_info.txpwr_lvl_v2.pwrlvl_11b_11ag_2g4[8] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11b_11ag_36m_2g4")) {
+		userconfig_info.txpwr_lvl_v2.pwrlvl_11b_11ag_2g4[9] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11b_11ag_48m_2g4")) {
+		userconfig_info.txpwr_lvl_v2.pwrlvl_11b_11ag_2g4[10] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11b_11ag_54m_2g4")) {
+		userconfig_info.txpwr_lvl_v2.pwrlvl_11b_11ag_2g4[11] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11n_11ac_mcs0_2g4")) {
+		userconfig_info.txpwr_lvl_v2.pwrlvl_11n_11ac_2g4[0] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11n_11ac_mcs1_2g4")) {
+		userconfig_info.txpwr_lvl_v2.pwrlvl_11n_11ac_2g4[1] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11n_11ac_mcs2_2g4")) {
+		userconfig_info.txpwr_lvl_v2.pwrlvl_11n_11ac_2g4[2] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11n_11ac_mcs3_2g4")) {
+		userconfig_info.txpwr_lvl_v2.pwrlvl_11n_11ac_2g4[3] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11n_11ac_mcs4_2g4")) {
+		userconfig_info.txpwr_lvl_v2.pwrlvl_11n_11ac_2g4[4] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11n_11ac_mcs5_2g4")) {
+		userconfig_info.txpwr_lvl_v2.pwrlvl_11n_11ac_2g4[5] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11n_11ac_mcs6_2g4")) {
+		userconfig_info.txpwr_lvl_v2.pwrlvl_11n_11ac_2g4[6] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11n_11ac_mcs7_2g4")) {
+		userconfig_info.txpwr_lvl_v2.pwrlvl_11n_11ac_2g4[7] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11n_11ac_mcs8_2g4")) {
+		userconfig_info.txpwr_lvl_v2.pwrlvl_11n_11ac_2g4[8] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11n_11ac_mcs9_2g4")) {
+		userconfig_info.txpwr_lvl_v2.pwrlvl_11n_11ac_2g4[9] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11ax_mcs0_2g4")) {
+		userconfig_info.txpwr_lvl_v2.pwrlvl_11ax_2g4[0] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11ax_mcs1_2g4")) {
+		userconfig_info.txpwr_lvl_v2.pwrlvl_11ax_2g4[1] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11ax_mcs2_2g4")) {
+		userconfig_info.txpwr_lvl_v2.pwrlvl_11ax_2g4[2] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11ax_mcs3_2g4")) {
+		userconfig_info.txpwr_lvl_v2.pwrlvl_11ax_2g4[3] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11ax_mcs4_2g4")) {
+		userconfig_info.txpwr_lvl_v2.pwrlvl_11ax_2g4[4] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11ax_mcs5_2g4")) {
+		userconfig_info.txpwr_lvl_v2.pwrlvl_11ax_2g4[5] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11ax_mcs6_2g4")) {
+		userconfig_info.txpwr_lvl_v2.pwrlvl_11ax_2g4[6] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11ax_mcs7_2g4")) {
+		userconfig_info.txpwr_lvl_v2.pwrlvl_11ax_2g4[7] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11ax_mcs8_2g4")) {
+		userconfig_info.txpwr_lvl_v2.pwrlvl_11ax_2g4[8] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11ax_mcs9_2g4")) {
+		userconfig_info.txpwr_lvl_v2.pwrlvl_11ax_2g4[9] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11ax_mcs10_2g4")) {
+		userconfig_info.txpwr_lvl_v2.pwrlvl_11ax_2g4[10] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11ax_mcs11_2g4")) {
+		userconfig_info.txpwr_lvl_v2.pwrlvl_11ax_2g4[11] = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_enable")) {
+		userconfig_info.txpwr_ofst.enable = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_chan_1_4")) {
+		userconfig_info.txpwr_ofst.chan_1_4 = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_chan_5_9")) {
+		userconfig_info.txpwr_ofst.chan_5_9 = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_chan_10_13")) {
+		userconfig_info.txpwr_ofst.chan_10_13 = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_chan_36_64")) {
+		userconfig_info.txpwr_ofst.chan_36_64 = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_chan_100_120")) {
+		userconfig_info.txpwr_ofst.chan_100_120 = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_chan_122_140")) {
+		userconfig_info.txpwr_ofst.chan_122_140 = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_chan_142_165")) {
+		userconfig_info.txpwr_ofst.chan_142_165 = rwnx_atoi(value);
+	} else if (!strcmp(command, "xtal_enable")) {
+		userconfig_info.xtal_cap.enable = rwnx_atoi(value);
+	} else if (!strcmp(command, "xtal_cap")) {
+		userconfig_info.xtal_cap.xtal_cap = rwnx_atoi(value);
+	} else if (!strcmp(command, "xtal_cap_fine")) {
+		userconfig_info.xtal_cap.xtal_cap_fine = rwnx_atoi(value);
+	} else {
+		AICWFDBG(LOGERROR, "invalid cmd: %s\n", command);
+	}
+}
+
+void rwnx_plat_nvram_set_value_group(char *command, char *value)
+{
+	char *mul_val[10];
+	int i;
+
+	// TODO send command
+	AICWFDBG(LOGDEBUG, "%s:command=%s value=%s\n", __func__, command, value);
+	if (!strcmp(command, "enable")) {
+		userconfig_info.txpwr_lvl.enable = rwnx_atoi(value);
+		for (i = 0; i < REGION_NUM_MAX; i++)
+			userconfig_info.txpwr_lvl_v3_gp[i].enable = rwnx_atoi(value);
+	} else if (!strcmp(command, "dsss")) {
+		userconfig_info.txpwr_lvl.dsss = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofdmlowrate_2g4")) {
+		userconfig_info.txpwr_lvl.ofdmlowrate_2g4 = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofdm64qam_2g4")) {
+		userconfig_info.txpwr_lvl.ofdm64qam_2g4 = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofdm256qam_2g4")) {
+		userconfig_info.txpwr_lvl.ofdm256qam_2g4 = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofdm1024qam_2g4")) {
+		userconfig_info.txpwr_lvl.ofdm1024qam_2g4 = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofdmlowrate_5g")) {
+		userconfig_info.txpwr_lvl.ofdmlowrate_5g = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofdm64qam_5g")) {
+		userconfig_info.txpwr_lvl.ofdm64qam_5g = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofdm256qam_5g")) {
+		userconfig_info.txpwr_lvl.ofdm256qam_5g = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofdm1024qam_5g")) {
+		userconfig_info.txpwr_lvl.ofdm1024qam_5g = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11b_11ag_1m_2g4")) {
+		rwnx_parse_mul_value(value, mul_val);
+		for (i = 0; i < REGION_NUM_MAX; i++)
+			userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11b_11ag_2g4[0] =
+				rwnx_atoi(mul_val[i]);
+	} else if (!strcmp(command, "lvl_11b_11ag_2m_2g4")) {
+		rwnx_parse_mul_value(value, mul_val);
+		for (i = 0; i < REGION_NUM_MAX; i++)
+			userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11b_11ag_2g4[1] =
+				rwnx_atoi(mul_val[i]);
+	} else if (!strcmp(command, "lvl_11b_11ag_5m5_2g4")) {
+		rwnx_parse_mul_value(value, mul_val);
+		for (i = 0; i < REGION_NUM_MAX; i++)
+			userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11b_11ag_2g4[2] =
+				rwnx_atoi(mul_val[i]);
+	} else if (!strcmp(command, "lvl_11b_11ag_11m_2g4")) {
+		rwnx_parse_mul_value(value, mul_val);
+		for (i = 0; i < REGION_NUM_MAX; i++)
+			userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11b_11ag_2g4[3] =
+				rwnx_atoi(mul_val[i]);
+	} else if (!strcmp(command, "lvl_11b_11ag_6m_2g4")) {
+		rwnx_parse_mul_value(value, mul_val);
+		for (i = 0; i < REGION_NUM_MAX; i++)
+			userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11b_11ag_2g4[4] =
+				rwnx_atoi(mul_val[i]);
+	} else if (!strcmp(command, "lvl_11b_11ag_9m_2g4")) {
+		rwnx_parse_mul_value(value, mul_val);
+		for (i = 0; i < REGION_NUM_MAX; i++)
+			userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11b_11ag_2g4[5] =
+				rwnx_atoi(mul_val[i]);
+	} else if (!strcmp(command, "lvl_11b_11ag_12m_2g4")) {
+		rwnx_parse_mul_value(value, mul_val);
+		for (i = 0; i < REGION_NUM_MAX; i++)
+			userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11b_11ag_2g4[6] =
+				rwnx_atoi(mul_val[i]);
+	} else if (!strcmp(command, "lvl_11b_11ag_18m_2g4")) {
+		rwnx_parse_mul_value(value, mul_val);
+		for (i = 0; i < REGION_NUM_MAX; i++)
+			userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11b_11ag_2g4[7] =
+				rwnx_atoi(mul_val[i]);
+	} else if (!strcmp(command, "lvl_11b_11ag_24m_2g4")) {
+		rwnx_parse_mul_value(value, mul_val);
+		for (i = 0; i < REGION_NUM_MAX; i++)
+			userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11b_11ag_2g4[8] =
+				rwnx_atoi(mul_val[i]);
+	} else if (!strcmp(command, "lvl_11b_11ag_36m_2g4")) {
+		rwnx_parse_mul_value(value, mul_val);
+		for (i = 0; i < REGION_NUM_MAX; i++)
+			userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11b_11ag_2g4[9] =
+				rwnx_atoi(mul_val[i]);
+	} else if (!strcmp(command, "lvl_11b_11ag_48m_2g4")) {
+		rwnx_parse_mul_value(value, mul_val);
+		for (i = 0; i < REGION_NUM_MAX; i++)
+			userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11b_11ag_2g4[10] =
+				rwnx_atoi(mul_val[i]);
+	} else if (!strcmp(command, "lvl_11b_11ag_54m_2g4")) {
+		rwnx_parse_mul_value(value, mul_val);
+		for (i = 0; i < REGION_NUM_MAX; i++)
+			userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11b_11ag_2g4[11] =
+				rwnx_atoi(mul_val[i]);
+	} else if (!strcmp(command, "lvl_11n_11ac_mcs0_2g4")) {
+		rwnx_parse_mul_value(value, mul_val);
+		for (i = 0; i < REGION_NUM_MAX; i++)
+			userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11n_11ac_2g4[0] =
+				rwnx_atoi(mul_val[i]);
+	} else if (!strcmp(command, "lvl_11n_11ac_mcs1_2g4")) {
+		rwnx_parse_mul_value(value, mul_val);
+		for (i = 0; i < REGION_NUM_MAX; i++)
+			userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11n_11ac_2g4[1] =
+				rwnx_atoi(mul_val[i]);
+	} else if (!strcmp(command, "lvl_11n_11ac_mcs2_2g4")) {
+		rwnx_parse_mul_value(value, mul_val);
+		for (i = 0; i < REGION_NUM_MAX; i++)
+			userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11n_11ac_2g4[2] =
+				rwnx_atoi(mul_val[i]);
+	} else if (!strcmp(command, "lvl_11n_11ac_mcs3_2g4")) {
+		rwnx_parse_mul_value(value, mul_val);
+		for (i = 0; i < REGION_NUM_MAX; i++)
+			userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11n_11ac_2g4[3] =
+				rwnx_atoi(mul_val[i]);
+	} else if (!strcmp(command, "lvl_11n_11ac_mcs4_2g4")) {
+		rwnx_parse_mul_value(value, mul_val);
+		for (i = 0; i < REGION_NUM_MAX; i++)
+			userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11n_11ac_2g4[4] =
+				rwnx_atoi(mul_val[i]);
+	} else if (!strcmp(command, "lvl_11n_11ac_mcs5_2g4")) {
+		rwnx_parse_mul_value(value, mul_val);
+		for (i = 0; i < REGION_NUM_MAX; i++)
+			userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11n_11ac_2g4[5] =
+				rwnx_atoi(mul_val[i]);
+	} else if (!strcmp(command, "lvl_11n_11ac_mcs6_2g4")) {
+		rwnx_parse_mul_value(value, mul_val);
+		for (i = 0; i < REGION_NUM_MAX; i++)
+			userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11n_11ac_2g4[6] =
+				rwnx_atoi(mul_val[i]);
+	} else if (!strcmp(command, "lvl_11n_11ac_mcs7_2g4")) {
+		rwnx_parse_mul_value(value, mul_val);
+		for (i = 0; i < REGION_NUM_MAX; i++)
+			userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11n_11ac_2g4[7] =
+				rwnx_atoi(mul_val[i]);
+	} else if (!strcmp(command, "lvl_11n_11ac_mcs8_2g4")) {
+		rwnx_parse_mul_value(value, mul_val);
+		for (i = 0; i < REGION_NUM_MAX; i++)
+			userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11n_11ac_2g4[8] =
+				rwnx_atoi(mul_val[i]);
+	} else if (!strcmp(command, "lvl_11n_11ac_mcs9_2g4")) {
+		rwnx_parse_mul_value(value, mul_val);
+		for (i = 0; i < REGION_NUM_MAX; i++)
+			userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11n_11ac_2g4[9] =
+				rwnx_atoi(mul_val[i]);
+	} else if (!strcmp(command, "lvl_11ax_mcs0_2g4")) {
+		rwnx_parse_mul_value(value, mul_val);
+		for (i = 0; i < REGION_NUM_MAX; i++)
+			userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11ax_2g4[0] =
+				rwnx_atoi(mul_val[i]);
+	} else if (!strcmp(command, "lvl_11ax_mcs1_2g4")) {
+		rwnx_parse_mul_value(value, mul_val);
+		for (i = 0; i < REGION_NUM_MAX; i++)
+			userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11ax_2g4[1] =
+				rwnx_atoi(mul_val[i]);
+	} else if (!strcmp(command, "lvl_11ax_mcs2_2g4")) {
+		rwnx_parse_mul_value(value, mul_val);
+		for (i = 0; i < REGION_NUM_MAX; i++)
+			userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11ax_2g4[2] =
+				rwnx_atoi(mul_val[i]);
+	} else if (!strcmp(command, "lvl_11ax_mcs3_2g4")) {
+		rwnx_parse_mul_value(value, mul_val);
+		for (i = 0; i < REGION_NUM_MAX; i++)
+			userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11ax_2g4[3] =
+				rwnx_atoi(mul_val[i]);
+	} else if (!strcmp(command, "lvl_11ax_mcs4_2g4")) {
+		rwnx_parse_mul_value(value, mul_val);
+		for (i = 0; i < REGION_NUM_MAX; i++)
+			userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11ax_2g4[4] =
+				rwnx_atoi(mul_val[i]);
+	} else if (!strcmp(command, "lvl_11ax_mcs5_2g4")) {
+		rwnx_parse_mul_value(value, mul_val);
+		for (i = 0; i < REGION_NUM_MAX; i++)
+			userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11ax_2g4[5] =
+				rwnx_atoi(mul_val[i]);
+	} else if (!strcmp(command, "lvl_11ax_mcs6_2g4")) {
+		rwnx_parse_mul_value(value, mul_val);
+		for (i = 0; i < REGION_NUM_MAX; i++)
+			userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11ax_2g4[6] =
+				rwnx_atoi(mul_val[i]);
+	} else if (!strcmp(command, "lvl_11ax_mcs7_2g4")) {
+		rwnx_parse_mul_value(value, mul_val);
+		for (i = 0; i < REGION_NUM_MAX; i++)
+			userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11ax_2g4[7] =
+				rwnx_atoi(mul_val[i]);
+	} else if (!strcmp(command, "lvl_11ax_mcs8_2g4")) {
+		rwnx_parse_mul_value(value, mul_val);
+		for (i = 0; i < REGION_NUM_MAX; i++)
+			userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11ax_2g4[8] =
+				rwnx_atoi(mul_val[i]);
+	} else if (!strcmp(command, "lvl_11ax_mcs9_2g4")) {
+		rwnx_parse_mul_value(value, mul_val);
+		for (i = 0; i < REGION_NUM_MAX; i++)
+			userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11ax_2g4[9] =
+				rwnx_atoi(mul_val[i]);
+	} else if (!strcmp(command, "lvl_11ax_mcs10_2g4")) {
+		rwnx_parse_mul_value(value, mul_val);
+		for (i = 0; i < REGION_NUM_MAX; i++)
+			userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11ax_2g4[10] =
+				rwnx_atoi(mul_val[i]);
+	} else if (!strcmp(command, "lvl_11ax_mcs11_2g4")) {
+		rwnx_parse_mul_value(value, mul_val);
+		for (i = 0; i < REGION_NUM_MAX; i++)
+			userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11ax_2g4[11] =
+				rwnx_atoi(mul_val[i]);
+	} else if (!strcmp(command, "lvl_11a_1m_5g")) {
+		rwnx_parse_mul_value(value, mul_val);
+		for (i = 0; i < REGION_NUM_MAX; i++)
+			userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11a_5g[0] =
+				rwnx_atoi(mul_val[i]);
+	} else if (!strcmp(command, "lvl_11a_2m_5g")) {
+		rwnx_parse_mul_value(value, mul_val);
+		for (i = 0; i < REGION_NUM_MAX; i++)
+			userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11a_5g[1] =
+				rwnx_atoi(mul_val[i]);
+	} else if (!strcmp(command, "lvl_11a_5m5_5g")) {
+		rwnx_parse_mul_value(value, mul_val);
+		for (i = 0; i < REGION_NUM_MAX; i++)
+			userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11a_5g[2] =
+				rwnx_atoi(mul_val[i]);
+	} else if (!strcmp(command, "lvl_11a_11m_5g")) {
+		rwnx_parse_mul_value(value, mul_val);
+		for (i = 0; i < REGION_NUM_MAX; i++)
+			userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11a_5g[3] =
+				rwnx_atoi(mul_val[i]);
+	} else if (!strcmp(command, "lvl_11a_6m_5g")) {
+		rwnx_parse_mul_value(value, mul_val);
+		for (i = 0; i < REGION_NUM_MAX; i++)
+			userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11a_5g[4] =
+				rwnx_atoi(mul_val[i]);
+	} else if (!strcmp(command, "lvl_11a_9m_5g")) {
+		rwnx_parse_mul_value(value, mul_val);
+		for (i = 0; i < REGION_NUM_MAX; i++)
+			userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11a_5g[5] =
+				rwnx_atoi(mul_val[i]);
+	} else if (!strcmp(command, "lvl_11a_12m_5g")) {
+		rwnx_parse_mul_value(value, mul_val);
+		for (i = 0; i < REGION_NUM_MAX; i++)
+			userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11a_5g[6] =
+				rwnx_atoi(mul_val[i]);
+	} else if (!strcmp(command, "lvl_11a_18m_5g")) {
+		rwnx_parse_mul_value(value, mul_val);
+		for (i = 0; i < REGION_NUM_MAX; i++)
+			userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11a_5g[7] =
+				rwnx_atoi(mul_val[i]);
+	} else if (!strcmp(command, "lvl_11a_24m_5g")) {
+		rwnx_parse_mul_value(value, mul_val);
+		for (i = 0; i < REGION_NUM_MAX; i++)
+			userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11a_5g[8] =
+				rwnx_atoi(mul_val[i]);
+	} else if (!strcmp(command, "lvl_11a_36m_5g")) {
+		rwnx_parse_mul_value(value, mul_val);
+		for (i = 0; i < REGION_NUM_MAX; i++)
+			userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11a_5g[9] =
+				rwnx_atoi(mul_val[i]);
+	} else if (!strcmp(command, "lvl_11a_48m_5g")) {
+		rwnx_parse_mul_value(value, mul_val);
+		for (i = 0; i < REGION_NUM_MAX; i++)
+			userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11a_5g[10] =
+				rwnx_atoi(mul_val[i]);
+	} else if (!strcmp(command, "lvl_11a_54m_5g")) {
+		rwnx_parse_mul_value(value, mul_val);
+		for (i = 0; i < REGION_NUM_MAX; i++)
+			userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11a_5g[11] =
+				rwnx_atoi(mul_val[i]);
+	} else if (!strcmp(command, "lvl_11n_11ac_mcs0_5g")) {
+		rwnx_parse_mul_value(value, mul_val);
+		for (i = 0; i < REGION_NUM_MAX; i++)
+			userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11n_11ac_5g[0] =
+				rwnx_atoi(mul_val[i]);
+	} else if (!strcmp(command, "lvl_11n_11ac_mcs1_5g")) {
+		rwnx_parse_mul_value(value, mul_val);
+		for (i = 0; i < REGION_NUM_MAX; i++)
+			userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11n_11ac_5g[1] =
+				rwnx_atoi(mul_val[i]);
+	} else if (!strcmp(command, "lvl_11n_11ac_mcs2_5g")) {
+		rwnx_parse_mul_value(value, mul_val);
+		for (i = 0; i < REGION_NUM_MAX; i++)
+			userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11n_11ac_5g[2] =
+				rwnx_atoi(mul_val[i]);
+	} else if (!strcmp(command, "lvl_11n_11ac_mcs3_5g")) {
+		rwnx_parse_mul_value(value, mul_val);
+		for (i = 0; i < REGION_NUM_MAX; i++)
+			userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11n_11ac_5g[3] =
+				rwnx_atoi(mul_val[i]);
+	} else if (!strcmp(command, "lvl_11n_11ac_mcs4_5g")) {
+		rwnx_parse_mul_value(value, mul_val);
+		for (i = 0; i < REGION_NUM_MAX; i++)
+			userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11n_11ac_5g[4] =
+				rwnx_atoi(mul_val[i]);
+	} else if (!strcmp(command, "lvl_11n_11ac_mcs5_5g")) {
+		rwnx_parse_mul_value(value, mul_val);
+		for (i = 0; i < REGION_NUM_MAX; i++)
+			userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11n_11ac_5g[5] =
+				rwnx_atoi(mul_val[i]);
+	} else if (!strcmp(command, "lvl_11n_11ac_mcs6_5g")) {
+		rwnx_parse_mul_value(value, mul_val);
+		for (i = 0; i < REGION_NUM_MAX; i++)
+			userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11n_11ac_5g[6] =
+				rwnx_atoi(mul_val[i]);
+	} else if (!strcmp(command, "lvl_11n_11ac_mcs7_5g")) {
+		rwnx_parse_mul_value(value, mul_val);
+		for (i = 0; i < REGION_NUM_MAX; i++)
+			userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11n_11ac_5g[7] =
+				rwnx_atoi(mul_val[i]);
+	} else if (!strcmp(command, "lvl_11n_11ac_mcs8_5g")) {
+		rwnx_parse_mul_value(value, mul_val);
+		for (i = 0; i < REGION_NUM_MAX; i++)
+			userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11n_11ac_5g[8] =
+				rwnx_atoi(mul_val[i]);
+	} else if (!strcmp(command, "lvl_11n_11ac_mcs9_5g")) {
+		rwnx_parse_mul_value(value, mul_val);
+		for (i = 0; i < REGION_NUM_MAX; i++)
+			userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11n_11ac_5g[9] =
+				rwnx_atoi(mul_val[i]);
+	} else if (!strcmp(command, "lvl_11ax_mcs0_5g")) {
+		rwnx_parse_mul_value(value, mul_val);
+		for (i = 0; i < REGION_NUM_MAX; i++)
+			userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11ax_5g[0] =
+				rwnx_atoi(mul_val[i]);
+	} else if (!strcmp(command, "lvl_11ax_mcs1_5g")) {
+		rwnx_parse_mul_value(value, mul_val);
+		for (i = 0; i < REGION_NUM_MAX; i++)
+			userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11ax_5g[1] =
+				rwnx_atoi(mul_val[i]);
+	} else if (!strcmp(command, "lvl_11ax_mcs2_5g")) {
+		rwnx_parse_mul_value(value, mul_val);
+		for (i = 0; i < REGION_NUM_MAX; i++)
+			userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11ax_5g[2] =
+				rwnx_atoi(mul_val[i]);
+	} else if (!strcmp(command, "lvl_11ax_mcs3_5g")) {
+		rwnx_parse_mul_value(value, mul_val);
+		for (i = 0; i < REGION_NUM_MAX; i++)
+			userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11ax_5g[3] =
+				rwnx_atoi(mul_val[i]);
+	} else if (!strcmp(command, "lvl_11ax_mcs4_5g")) {
+		rwnx_parse_mul_value(value, mul_val);
+		for (i = 0; i < REGION_NUM_MAX; i++)
+			userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11ax_5g[4] =
+				rwnx_atoi(mul_val[i]);
+	} else if (!strcmp(command, "lvl_11ax_mcs5_5g")) {
+		rwnx_parse_mul_value(value, mul_val);
+		for (i = 0; i < REGION_NUM_MAX; i++)
+			userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11ax_5g[5] =
+				rwnx_atoi(mul_val[i]);
+	} else if (!strcmp(command, "lvl_11ax_mcs6_5g")) {
+		rwnx_parse_mul_value(value, mul_val);
+		for (i = 0; i < REGION_NUM_MAX; i++)
+			userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11ax_5g[6] =
+				rwnx_atoi(mul_val[i]);
+	} else if (!strcmp(command, "lvl_11ax_mcs7_5g")) {
+		rwnx_parse_mul_value(value, mul_val);
+		for (i = 0; i < REGION_NUM_MAX; i++)
+			userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11ax_5g[7] =
+				rwnx_atoi(mul_val[i]);
+	} else if (!strcmp(command, "lvl_11ax_mcs8_5g")) {
+		rwnx_parse_mul_value(value, mul_val);
+		for (i = 0; i < REGION_NUM_MAX; i++)
+			userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11ax_5g[8] =
+				rwnx_atoi(mul_val[i]);
+	} else if (!strcmp(command, "lvl_11ax_mcs9_5g")) {
+		rwnx_parse_mul_value(value, mul_val);
+		for (i = 0; i < REGION_NUM_MAX; i++)
+			userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11ax_5g[9] =
+				rwnx_atoi(mul_val[i]);
+	} else if (!strcmp(command, "lvl_11ax_mcs10_5g")) {
+		rwnx_parse_mul_value(value, mul_val);
+		for (i = 0; i < REGION_NUM_MAX; i++)
+			userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11ax_5g[10] =
+				rwnx_atoi(mul_val[i]);
+	} else if (!strcmp(command, "lvl_11ax_mcs11_5g")) {
+		rwnx_parse_mul_value(value, mul_val);
+		for (i = 0; i < REGION_NUM_MAX; i++)
+			userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11ax_5g[11] =
+				rwnx_atoi(mul_val[i]);
+	} else if (!strcmp(command, "lvl_adj_enable")) {
+		userconfig_info.txpwr_lvl_adj.enable = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_adj_2g4_chan_1_4")) {
+		userconfig_info.txpwr_lvl_adj.pwrlvl_adj_tbl_2g4[0] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_adj_2g4_chan_5_9")) {
+		userconfig_info.txpwr_lvl_adj.pwrlvl_adj_tbl_2g4[1] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_adj_2g4_chan_10_13")) {
+		userconfig_info.txpwr_lvl_adj.pwrlvl_adj_tbl_2g4[2] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_adj_5g_chan_42")) {
+		userconfig_info.txpwr_lvl_adj.pwrlvl_adj_tbl_5g[0] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_adj_5g_chan_58")) {
+		userconfig_info.txpwr_lvl_adj.pwrlvl_adj_tbl_5g[1] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_adj_5g_chan_106")) {
+		userconfig_info.txpwr_lvl_adj.pwrlvl_adj_tbl_5g[2] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_adj_5g_chan_122")) {
+		userconfig_info.txpwr_lvl_adj.pwrlvl_adj_tbl_5g[3] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_adj_5g_chan_138")) {
+		userconfig_info.txpwr_lvl_adj.pwrlvl_adj_tbl_5g[4] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_adj_5g_chan_155")) {
+		userconfig_info.txpwr_lvl_adj.pwrlvl_adj_tbl_5g[5] = rwnx_atoi(value);
+	} else if (!strcmp(command, "loss_enable_2g4")) {
+		userconfig_info.txpwr_loss.loss_enable_2g4 = rwnx_atoi(value);
+	} else if (!strcmp(command, "loss_value_2g4")) {
+		userconfig_info.txpwr_loss.loss_value_2g4 = rwnx_atoi(value);
+	} else if (!strcmp(command, "loss_enable_5g")) {
+		userconfig_info.txpwr_loss.loss_enable_5g = rwnx_atoi(value);
+	} else if (!strcmp(command, "loss_value_5g")) {
+		userconfig_info.txpwr_loss.loss_value_5g = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_enable")) {
+		userconfig_info.txpwr_ofst.enable = rwnx_atoi(value);
+		userconfig_info.txpwr_ofst2x.enable = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_chan_1_4")) {
+		userconfig_info.txpwr_ofst.chan_1_4 = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_chan_5_9")) {
+		userconfig_info.txpwr_ofst.chan_5_9 = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_chan_10_13")) {
+		userconfig_info.txpwr_ofst.chan_10_13 = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_chan_36_64")) {
+		userconfig_info.txpwr_ofst.chan_36_64 = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_chan_100_120")) {
+		userconfig_info.txpwr_ofst.chan_100_120 = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_chan_122_140")) {
+		userconfig_info.txpwr_ofst.chan_122_140 = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_chan_142_165")) {
+		userconfig_info.txpwr_ofst.chan_142_165 = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_2g4_11b_chan_1_4")) {
+		userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_2g4[0][0] = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_2g4_11b_chan_5_9")) {
+		userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_2g4[0][1] = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_2g4_11b_chan_10_13")) {
+		userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_2g4[0][2] = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_2g4_ofdm_highrate_chan_1_4")) {
+		userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_2g4[1][0] = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_2g4_ofdm_highrate_chan_5_9")) {
+		userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_2g4[1][1] = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_2g4_ofdm_highrate_chan_10_13")) {
+		userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_2g4[1][2] = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_2g4_ofdm_lowrate_chan_1_4")) {
+		userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_2g4[2][0] = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_2g4_ofdm_lowrate_chan_5_9")) {
+		userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_2g4[2][1] = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_2g4_ofdm_lowrate_chan_10_13")) {
+		userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_2g4[2][0] = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_5g_ofdm_lowrate_chan_42")) {
+		userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_5g[0][0] = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_5g_ofdm_lowrate_chan_58")) {
+		userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_5g[0][1] = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_5g_ofdm_lowrate_chan_106")) {
+		userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_5g[0][2] = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_5g_ofdm_lowrate_chan_122")) {
+		userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_5g[0][3] = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_5g_ofdm_lowrate_chan_138")) {
+		userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_5g[0][4] = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_5g_ofdm_lowrate_chan_155")) {
+		userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_5g[0][5] = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_5g_ofdm_highrate_chan_42")) {
+		userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_5g[1][0] = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_5g_ofdm_highrate_chan_58")) {
+		userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_5g[1][1] = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_5g_ofdm_highrate_chan_106")) {
+		userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_5g[1][2] = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_5g_ofdm_highrate_chan_122")) {
+		userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_5g[1][3] = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_5g_ofdm_highrate_chan_138")) {
+		userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_5g[1][4] = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_5g_ofdm_highrate_chan_155")) {
+		userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_5g[1][5] = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_5g_ofdm_midrate_chan_42")) {
+		userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_5g[2][0] = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_5g_ofdm_midrate_chan_58")) {
+		userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_5g[2][1] = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_5g_ofdm_midrate_chan_106")) {
+		userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_5g[2][2] = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_5g_ofdm_midrate_chan_122")) {
+		userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_5g[2][3] = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_5g_ofdm_midrate_chan_138")) {
+		userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_5g[2][4] = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_5g_ofdm_midrate_chan_155")) {
+		userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_5g[2][5] = rwnx_atoi(value);
+	} else if (!strcmp(command, "xtal_enable")) {
+		userconfig_info.xtal_cap.enable = rwnx_atoi(value);
+	} else if (!strcmp(command, "xtal_cap")) {
+		userconfig_info.xtal_cap.xtal_cap = rwnx_atoi(value);
+	} else if (!strcmp(command, "xtal_cap_fine")) {
+		userconfig_info.xtal_cap.xtal_cap_fine = rwnx_atoi(value);
+	} else {
+		AICWFDBG(LOGERROR, "invalid cmd: %s\n", command);
+	}
+}
+
+void rwnx_plat_nvram_set_value_v3(char *command, char *value)
+{
+	// TODO send command
+	AICWFDBG(LOGDEBUG, "%s:command=%s value=%s\n", __func__, command, value);
+	//AICWFDBG(LOGDEBUG, "AICWF command=%s value=%s\n", command, value);
+	if (!strcmp(command, "enable")) {
+		userconfig_info.txpwr_lvl.enable = rwnx_atoi(value);
+		userconfig_info.txpwr_lvl_v3.enable = rwnx_atoi(value);
+	} else if (!strcmp(command, "dsss")) {
+		userconfig_info.txpwr_lvl.dsss = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofdmlowrate_2g4")) {
+		userconfig_info.txpwr_lvl.ofdmlowrate_2g4 = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofdm64qam_2g4")) {
+		userconfig_info.txpwr_lvl.ofdm64qam_2g4 = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofdm256qam_2g4")) {
+		userconfig_info.txpwr_lvl.ofdm256qam_2g4 = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofdm1024qam_2g4")) {
+		userconfig_info.txpwr_lvl.ofdm1024qam_2g4 = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofdmlowrate_5g")) {
+		userconfig_info.txpwr_lvl.ofdmlowrate_5g = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofdm64qam_5g")) {
+		userconfig_info.txpwr_lvl.ofdm64qam_5g = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofdm256qam_5g")) {
+		userconfig_info.txpwr_lvl.ofdm256qam_5g = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofdm1024qam_5g")) {
+		userconfig_info.txpwr_lvl.ofdm1024qam_5g = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11b_11ag_1m_2g4")) {
+		userconfig_info.txpwr_lvl_v3.pwrlvl_11b_11ag_2g4[0] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11b_11ag_2m_2g4")) {
+		userconfig_info.txpwr_lvl_v3.pwrlvl_11b_11ag_2g4[1] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11b_11ag_5m5_2g4")) {
+		userconfig_info.txpwr_lvl_v3.pwrlvl_11b_11ag_2g4[2] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11b_11ag_11m_2g4")) {
+		userconfig_info.txpwr_lvl_v3.pwrlvl_11b_11ag_2g4[3] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11b_11ag_6m_2g4")) {
+		userconfig_info.txpwr_lvl_v3.pwrlvl_11b_11ag_2g4[4] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11b_11ag_9m_2g4")) {
+		userconfig_info.txpwr_lvl_v3.pwrlvl_11b_11ag_2g4[5] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11b_11ag_12m_2g4")) {
+		userconfig_info.txpwr_lvl_v3.pwrlvl_11b_11ag_2g4[6] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11b_11ag_18m_2g4")) {
+		userconfig_info.txpwr_lvl_v3.pwrlvl_11b_11ag_2g4[7] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11b_11ag_24m_2g4")) {
+		userconfig_info.txpwr_lvl_v3.pwrlvl_11b_11ag_2g4[8] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11b_11ag_36m_2g4")) {
+		userconfig_info.txpwr_lvl_v3.pwrlvl_11b_11ag_2g4[9] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11b_11ag_48m_2g4")) {
+		userconfig_info.txpwr_lvl_v3.pwrlvl_11b_11ag_2g4[10] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11b_11ag_54m_2g4")) {
+		userconfig_info.txpwr_lvl_v3.pwrlvl_11b_11ag_2g4[11] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11n_11ac_mcs0_2g4")) {
+		userconfig_info.txpwr_lvl_v3.pwrlvl_11n_11ac_2g4[0] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11n_11ac_mcs1_2g4")) {
+		userconfig_info.txpwr_lvl_v3.pwrlvl_11n_11ac_2g4[1] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11n_11ac_mcs2_2g4")) {
+		userconfig_info.txpwr_lvl_v3.pwrlvl_11n_11ac_2g4[2] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11n_11ac_mcs3_2g4")) {
+		userconfig_info.txpwr_lvl_v3.pwrlvl_11n_11ac_2g4[3] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11n_11ac_mcs4_2g4")) {
+		userconfig_info.txpwr_lvl_v3.pwrlvl_11n_11ac_2g4[4] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11n_11ac_mcs5_2g4")) {
+		userconfig_info.txpwr_lvl_v3.pwrlvl_11n_11ac_2g4[5] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11n_11ac_mcs6_2g4")) {
+		userconfig_info.txpwr_lvl_v3.pwrlvl_11n_11ac_2g4[6] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11n_11ac_mcs7_2g4")) {
+		userconfig_info.txpwr_lvl_v3.pwrlvl_11n_11ac_2g4[7] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11n_11ac_mcs8_2g4")) {
+		userconfig_info.txpwr_lvl_v3.pwrlvl_11n_11ac_2g4[8] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11n_11ac_mcs9_2g4")) {
+		userconfig_info.txpwr_lvl_v3.pwrlvl_11n_11ac_2g4[9] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11ax_mcs0_2g4")) {
+		userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_2g4[0] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11ax_mcs1_2g4")) {
+		userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_2g4[1] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11ax_mcs2_2g4")) {
+		userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_2g4[2] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11ax_mcs3_2g4")) {
+		userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_2g4[3] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11ax_mcs4_2g4")) {
+		userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_2g4[4] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11ax_mcs5_2g4")) {
+		userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_2g4[5] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11ax_mcs6_2g4")) {
+		userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_2g4[6] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11ax_mcs7_2g4")) {
+		userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_2g4[7] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11ax_mcs8_2g4")) {
+		userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_2g4[8] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11ax_mcs9_2g4")) {
+		userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_2g4[9] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11ax_mcs10_2g4")) {
+		userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_2g4[10] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11ax_mcs11_2g4")) {
+		userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_2g4[11] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11a_1m_5g")) {
+		userconfig_info.txpwr_lvl_v3.pwrlvl_11a_5g[0] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11a_2m_5g")) {
+		userconfig_info.txpwr_lvl_v3.pwrlvl_11a_5g[1] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11a_5m5_5g")) {
+		userconfig_info.txpwr_lvl_v3.pwrlvl_11a_5g[2] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11a_11m_5g")) {
+		userconfig_info.txpwr_lvl_v3.pwrlvl_11a_5g[3] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11a_6m_5g")) {
+		userconfig_info.txpwr_lvl_v3.pwrlvl_11a_5g[4] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11a_9m_5g")) {
+		userconfig_info.txpwr_lvl_v3.pwrlvl_11a_5g[5] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11a_12m_5g")) {
+		userconfig_info.txpwr_lvl_v3.pwrlvl_11a_5g[6] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11a_18m_5g")) {
+		userconfig_info.txpwr_lvl_v3.pwrlvl_11a_5g[7] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11a_24m_5g")) {
+		userconfig_info.txpwr_lvl_v3.pwrlvl_11a_5g[8] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11a_36m_5g")) {
+		userconfig_info.txpwr_lvl_v3.pwrlvl_11a_5g[9] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11a_48m_5g")) {
+		userconfig_info.txpwr_lvl_v3.pwrlvl_11a_5g[10] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11a_54m_5g")) {
+		userconfig_info.txpwr_lvl_v3.pwrlvl_11a_5g[11] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11n_11ac_mcs0_5g")) {
+		userconfig_info.txpwr_lvl_v3.pwrlvl_11n_11ac_5g[0] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11n_11ac_mcs1_5g")) {
+		userconfig_info.txpwr_lvl_v3.pwrlvl_11n_11ac_5g[1] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11n_11ac_mcs2_5g")) {
+		userconfig_info.txpwr_lvl_v3.pwrlvl_11n_11ac_5g[2] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11n_11ac_mcs3_5g")) {
+		userconfig_info.txpwr_lvl_v3.pwrlvl_11n_11ac_5g[3] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11n_11ac_mcs4_5g")) {
+		userconfig_info.txpwr_lvl_v3.pwrlvl_11n_11ac_5g[4] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11n_11ac_mcs5_5g")) {
+		userconfig_info.txpwr_lvl_v3.pwrlvl_11n_11ac_5g[5] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11n_11ac_mcs6_5g")) {
+		userconfig_info.txpwr_lvl_v3.pwrlvl_11n_11ac_5g[6] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11n_11ac_mcs7_5g")) {
+		userconfig_info.txpwr_lvl_v3.pwrlvl_11n_11ac_5g[7] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11n_11ac_mcs8_5g")) {
+		userconfig_info.txpwr_lvl_v3.pwrlvl_11n_11ac_5g[8] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11n_11ac_mcs9_5g")) {
+		userconfig_info.txpwr_lvl_v3.pwrlvl_11n_11ac_5g[9] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11ax_mcs0_5g")) {
+		userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_5g[0] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11ax_mcs1_5g")) {
+		userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_5g[1] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11ax_mcs2_5g")) {
+		userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_5g[2] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11ax_mcs3_5g")) {
+		userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_5g[3] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11ax_mcs4_5g")) {
+		userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_5g[4] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11ax_mcs5_5g")) {
+		userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_5g[5] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11ax_mcs6_5g")) {
+		userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_5g[6] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11ax_mcs7_5g")) {
+		userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_5g[7] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11ax_mcs8_5g")) {
+		userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_5g[8] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11ax_mcs9_5g")) {
+		userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_5g[9] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11ax_mcs10_5g")) {
+		userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_5g[10] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_11ax_mcs11_5g")) {
+		userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_5g[11] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_adj_enable")) {
+		userconfig_info.txpwr_lvl_adj.enable = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_adj_2g4_chan_1_4")) {
+		userconfig_info.txpwr_lvl_adj.pwrlvl_adj_tbl_2g4[0] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_adj_2g4_chan_5_9")) {
+		userconfig_info.txpwr_lvl_adj.pwrlvl_adj_tbl_2g4[1] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_adj_2g4_chan_10_13")) {
+		userconfig_info.txpwr_lvl_adj.pwrlvl_adj_tbl_2g4[2] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_adj_5g_chan_42")) {
+		userconfig_info.txpwr_lvl_adj.pwrlvl_adj_tbl_5g[0] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_adj_5g_chan_58")) {
+		userconfig_info.txpwr_lvl_adj.pwrlvl_adj_tbl_5g[1] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_adj_5g_chan_106")) {
+		userconfig_info.txpwr_lvl_adj.pwrlvl_adj_tbl_5g[2] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_adj_5g_chan_122")) {
+		userconfig_info.txpwr_lvl_adj.pwrlvl_adj_tbl_5g[3] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_adj_5g_chan_138")) {
+		userconfig_info.txpwr_lvl_adj.pwrlvl_adj_tbl_5g[4] = rwnx_atoi(value);
+	} else if (!strcmp(command, "lvl_adj_5g_chan_155")) {
+		userconfig_info.txpwr_lvl_adj.pwrlvl_adj_tbl_5g[5] = rwnx_atoi(value);
+	} else if (!strcmp(command, "loss_enable_2g4")) {
+		userconfig_info.txpwr_loss.loss_enable_2g4 = rwnx_atoi(value);
+	} else if (!strcmp(command, "loss_value_2g4")) {
+		userconfig_info.txpwr_loss.loss_value_2g4 = rwnx_atoi(value);
+	} else if (!strcmp(command, "loss_enable_5g")) {
+		userconfig_info.txpwr_loss.loss_enable_5g = rwnx_atoi(value);
+	} else if (!strcmp(command, "loss_value_5g")) {
+		userconfig_info.txpwr_loss.loss_value_5g = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_enable")) {
+		userconfig_info.txpwr_ofst.enable = rwnx_atoi(value);
+		userconfig_info.txpwr_ofst2x.enable = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_chan_1_4")) {
+		userconfig_info.txpwr_ofst.chan_1_4 = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_chan_5_9")) {
+		userconfig_info.txpwr_ofst.chan_5_9 = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_chan_10_13")) {
+		userconfig_info.txpwr_ofst.chan_10_13 = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_chan_36_64")) {
+		userconfig_info.txpwr_ofst.chan_36_64 = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_chan_100_120")) {
+		userconfig_info.txpwr_ofst.chan_100_120 = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_chan_122_140")) {
+		userconfig_info.txpwr_ofst.chan_122_140 = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_chan_142_165")) {
+		userconfig_info.txpwr_ofst.chan_142_165 = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_2g4_11b_chan_1_4")) {
+		userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_2g4[0][0] = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_2g4_11b_chan_5_9")) {
+		userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_2g4[0][1] = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_2g4_11b_chan_10_13")) {
+		userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_2g4[0][2] = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_2g4_ofdm_highrate_chan_1_4")) {
+		userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_2g4[1][0] = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_2g4_ofdm_highrate_chan_5_9")) {
+		userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_2g4[1][1] = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_2g4_ofdm_highrate_chan_10_13")) {
+		userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_2g4[1][2] = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_2g4_ofdm_lowrate_chan_1_4")) {
+		userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_2g4[2][0] = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_2g4_ofdm_lowrate_chan_5_9")) {
+		userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_2g4[2][1] = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_2g4_ofdm_lowrate_chan_10_13")) {
+		userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_2g4[2][0] = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_5g_ofdm_lowrate_chan_42")) {
+		userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_5g[0][0] = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_5g_ofdm_lowrate_chan_58")) {
+		userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_5g[0][1] = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_5g_ofdm_lowrate_chan_106")) {
+		userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_5g[0][2] = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_5g_ofdm_lowrate_chan_122")) {
+		userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_5g[0][3] = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_5g_ofdm_lowrate_chan_138")) {
+		userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_5g[0][4] = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_5g_ofdm_lowrate_chan_155")) {
+		userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_5g[0][5] = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_5g_ofdm_highrate_chan_42")) {
+		userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_5g[1][0] = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_5g_ofdm_highrate_chan_58")) {
+		userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_5g[1][1] = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_5g_ofdm_highrate_chan_106")) {
+		userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_5g[1][2] = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_5g_ofdm_highrate_chan_122")) {
+		userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_5g[1][3] = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_5g_ofdm_highrate_chan_138")) {
+		userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_5g[1][4] = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_5g_ofdm_highrate_chan_155")) {
+		userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_5g[1][5] = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_5g_ofdm_midrate_chan_42")) {
+		userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_5g[2][0] = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_5g_ofdm_midrate_chan_58")) {
+		userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_5g[2][1] = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_5g_ofdm_midrate_chan_106")) {
+		userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_5g[2][2] = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_5g_ofdm_midrate_chan_122")) {
+		userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_5g[2][3] = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_5g_ofdm_midrate_chan_138")) {
+		userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_5g[2][4] = rwnx_atoi(value);
+	} else if (!strcmp(command, "ofst_5g_ofdm_midrate_chan_155")) {
+		userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_5g[2][5] = rwnx_atoi(value);
+	} else if (!strcmp(command, "xtal_enable")) {
+		userconfig_info.xtal_cap.enable = rwnx_atoi(value);
+	} else if (!strcmp(command, "xtal_cap")) {
+		userconfig_info.xtal_cap.xtal_cap = rwnx_atoi(value);
+	} else if (!strcmp(command, "xtal_cap_fine")) {
+		userconfig_info.xtal_cap.xtal_cap_fine = rwnx_atoi(value);
+	} else {
+		AICWFDBG(LOGERROR, "invalid cmd: %s\n", command);
+	}
+}
+
+void rwnx_plat_userconfig_parsing2(char *buffer, int size)
+{
+	int i = 0;
+	int parse_state = 0;
+	char command[30];
+	char value[100];
+	int char_counter = 0;
+
+	memset(command, 0, 30);
+	memset(value, 0, 100);
+
+	for (i = 0; i < size; i++) {
+		// Send command or print nvram log when char is \r or \n
+		if (buffer[i] == 0x0a || buffer[i] == 0x0d) {
+			if (command[0] != 0 && value[0] != 0) {
+				if (parse_state == PRINT)
+					AICWFDBG(LOGINFO, "%s:%s\r\n", __func__, value);
+				else if (parse_state == GET_VALUE)
+					rwnx_plat_nvram_set_value(command, value);
+			}
+			// Reset command value and char_counter
+			memset(command, 0, 30);
+			memset(value, 0, 100);
+			char_counter = 0;
+			parse_state = INIT;
+			continue;
+		}
+
+		// Switch parser state
+		if (parse_state == INIT) {
+			if (buffer[i] == '#') {
+				parse_state = PRINT;
+				continue;
+			} else if (buffer[i] == 0x0a || buffer[i] == 0x0d) {
+				parse_state = INIT;
+				continue;
+			} else {
+				parse_state = CMD;
+			}
+		}
+
+		// Fill data to command and value
+		if (parse_state == PRINT) {
+			command[0] = 0x01;
+			value[char_counter] = buffer[i];
+			char_counter++;
+		} else if (parse_state == CMD) {
+			if (command[0] != 0 && buffer[i] == '=') {
+				parse_state = GET_VALUE;
+				char_counter = 0;
+				continue;
+			}
+			command[char_counter] = buffer[i];
+			char_counter++;
+		} else if (parse_state == GET_VALUE) {
+			value[char_counter] = buffer[i];
+			char_counter++;
+		}
+	}
+}
+
+void rwnx_plat_userconfig_parsing3(char *buffer, int size)
+{
+	int i = 0;
+	int parse_state = 0;
+	char command[64];
+	char value[100];
+	int char_counter = 0;
+	u8 area_pw = 0;
+
+	memset(command, 0, 64);
+	memset(value, 0, 100);
+
+	for (i = 0; i < size; i++) {
+		// Send command or print nvram log when char is \r or \n
+		if (buffer[i] == 0x0a || buffer[i] == 0x0d) {
+			if (command[0] != 0 && value[0] != 0) {
+				if (parse_state == PRINT) {
+					if (area_pw == 0 &&
+					    strnstr(value, "SRRC", strlen(value))) {
+						area_pw = 1;
+						AICWFDBG(LOGINFO,
+							 "use diff_area_power userconfig\n");
+					}
+					AICWFDBG(LOGINFO, "%s:%s\r\n", __func__, value);
+				} else if (parse_state == GET_VALUE) {
+					if (area_pw)
+						rwnx_plat_nvram_set_value_group(command, value);
+					else
+						rwnx_plat_nvram_set_value_v3(command, value);
+				}
+			}
+			// Reset command value and char_counter
+			memset(command, 0, 64);
+			memset(value, 0, 100);
+			char_counter = 0;
+			parse_state = INIT;
+			continue;
+		}
+
+		// Switch parser state
+		if (parse_state == INIT) {
+			if (buffer[i] == '#') {
+				parse_state = PRINT;
+				continue;
+			} else if (buffer[i] == 0x0a || buffer[i] == 0x0d) {
+				parse_state = INIT;
+				continue;
+			} else {
+				parse_state = CMD;
+			}
+		}
+
+		// Fill data to command and value
+		if (parse_state == PRINT) {
+			command[0] = 0x01;
+			value[char_counter] = buffer[i];
+			char_counter++;
+		} else if (parse_state == CMD) {
+			if (command[0] != 0 && buffer[i] == '=') {
+				parse_state = GET_VALUE;
+				char_counter = 0;
+				continue;
+			}
+			command[char_counter] = buffer[i];
+			char_counter++;
+		} else if (parse_state == GET_VALUE) {
+			if (buffer[i] != 0x2D &&
+			    (buffer[i] < 0x30 || buffer[i] > 0x39) &&
+			    buffer[i] != 0x20)
+				continue;
+			value[char_counter] = buffer[i];
+			char_counter++;
+		}
+	}
+}
+
+void rwnx_plat_userconfig_parsing(struct rwnx_hw *rwnx_hw, char *buffer,
+				  int size)
+{
+	char conf[100], keyname[64];
+	char *line;
+	char *data;
+	int i = 0, len = 0;
+	long val;
+
+	if (size <= 0) {
+		pr_err("Config buffer size %d error\n", size);
+		return;
+	}
+
+	AICWFDBG(LOGDEBUG, "AICWF %s rwnx_hw->vendor_info:0x%02X \r\n", __func__,
+		 rwnx_hw->vendor_info);
+	if (rwnx_hw->vendor_info == 0x00 ||
+	    rwnx_hw->vendor_info > (ARRAY_SIZE(parse_key_prefix) - 1)) {
+		AICWFDBG(LOGDEBUG, "AICWF Unsuppor vendor info config\n");
+		AICWFDBG(LOGDEBUG, "AICWF Using module0 config\n");
+		rwnx_hw->vendor_info = 0x01;
+		// return;
+	}
+
+	data = vmalloc(size + 1);
+	if (!data) {
+		AICWFDBG(LOGERROR, "%s vmalloc fail\n", __func__);
+		return;
+	}
+
+	memcpy(data, buffer, size);
+	buffer = data;
+
+	while (1) {
+		line = buffer;
+		if (*line == 0)
+			break;
+
+		while (*buffer != '\r' && *buffer != '\n' &&
+		       *buffer != 0 && len++ < size)
+			buffer++;
+
+		while ((*buffer == '\r' || *buffer == '\n') && len++ < size)
+			*buffer++ = 0;
+
+		if (len >= size)
+			*buffer = 0;
+
+		// store value to data struct
+		for (i = 0; i < ARRAY_SIZE(parse_match_tab); i++) {
+			scnprintf(keyname, sizeof(keyname), "%s%s",
+				  parse_key_prefix[rwnx_hw->vendor_info],
+				  parse_match_tab[i].keyname);
+			if (parse_key_val(line, keyname, conf) == 0) {
+				if (kstrtol(conf, 0, &val)) {
+					pr_warn("Invalid value for %s\n", keyname);
+					continue;
+				}
+				*(unsigned long *)((unsigned long)&nvram_info +
+								parse_match_tab[i].offset) = val;
+				AICWFDBG(LOGDEBUG, "AICWF %s, %s = %ld\n", __func__,
+					 parse_match_tab[i].keyname, val);
+				break;
+			}
+		}
+	}
+	vfree(data);
+}
+
+int rwnx_plat_userconfig_upload(struct rwnx_hw *rwnx_hw,
+				const char *filename)
+{
+	int size;
+	u32 *dst = NULL;
+
+	AICWFDBG(LOGDEBUG, "AICWF userconfig file path:%s \r\n", filename);
+
+	/* load aic firmware */
+	size = rwnx_load_firmware(&dst, filename, rwnx_hw->dev);
+	if (size <= 0) {
+		AICWFDBG(LOGDEBUG, "AICWF wrong size of firmware file\n");
+		vfree(dst);
+		dst = NULL;
+		return 0;
+	}
+
+	/* Copy the file on the Embedded side */
+	AICWFDBG(LOGDEBUG, "AICWF ### Upload %s userconfig, size=%d\n", filename, size);
+
+	rwnx_plat_userconfig_parsing(rwnx_hw, (char *)dst, size);
+
+	if (dst) {
+		vfree(dst);
+		dst = NULL;
+	}
+
+	AICWFDBG(LOGDEBUG, "AICWF userconfig download complete\n\n");
+
+	return 0;
+}
+
+#ifdef CONFIG_AIC8800_POWER_LIMIT
+static inline char *get_line_from_buffer(char **buffer)
+{
+	return strsep(buffer, "\n");
+}
+
+int is_all_space_or_tab(u8 *data, u8 size)
+{
+	u8 cnt = 0;
+	u8 num_of_space_and_tab = 0;
+
+	while (size > cnt) {
+		if (data[cnt] == ' ' || data[cnt] == '\t' || data[cnt] == '\0')
+			++num_of_space_and_tab;
+		++cnt;
+	}
+	return size == num_of_space_and_tab;
+}
+
+int is_comment_string(char *sz_str)
+{
+	if (*sz_str == '#' && *(sz_str + 1) == ' ')
+		return 1;
+	else
+		return 0;
+}
+
+int is_enable_limit(char *szs_str)
+{
+	int i = 0, value = 0;
+	char en[15];
+
+	if (*szs_str == '#' && *(szs_str + 1) == '*') {
+		i = 2;
+		while (szs_str[i] != '=' && (i < 15)) {
+			en[i - 2] = szs_str[i];
+			i++;
+		}
+		en[i - 2] = '\0';
+		i += 1;
+		if (strncmp(en, "enable", 6) == 0) {
+			value = rwnx_atoi(szs_str + i);
+			if (value == 1)
+				return 1;
+			else
+				return 0;
+		}
+	}
+	return -1;
+}
+
+int parse_qualified_string(char *in, u32 *start, char *out, char left_qualifier,
+			   char right_qualifier)
+{
+	u32 i = 0, j = 0;
+	char c = in[(*start)++];
+
+	if (c != left_qualifier)
+		return 0;
+	i = (*start);
+	c = in[(*start)++];
+	while (c != right_qualifier && c != '\0')
+		c = in[(*start)++];
+	if (c == '\0')
+		return 0;
+	j = (*start) - 2;
+	strscpy((char *)out, (const char *)(in + i), j - i + 1);
+	return 1;
+}
+
+int get_u1_byte_integer_from_string_in_decimal(char *str, u8 *p_int)
+{
+	u16 i = 0;
+	*p_int = 0;
+	while (str[i] != '\0') {
+		if (str[i] >= '0' && str[i] <= '9') {
+			*p_int *= 10;
+			*p_int += (str[i] - '0');
+		} else {
+			return 0;
+		}
+		++i;
+	}
+	return 1;
+}
+
+int get_s1_byte_integer_from_string_in_decimal(char *str, s8 *val)
+{
+	u8 negative = 0;
+	u16 i = 0;
+	*val = 0;
+	while (str[i] != '\0') {
+		if (i == 0 && (str[i] == '+' || str[i] == '-')) {
+			if (str[i] == '-')
+				negative = 1;
+		} else if (str[i] >= '0' && str[i] <= '9') {
+			*val *= 10;
+			*val += (str[i] - '0');
+		} else {
+			return 0;
+		}
+		++i;
+	}
+	if (negative)
+		*val = -*val;
+	return 1;
+}
+
+static void update_power_limit(u8 band_cc, const char *channel, u8 channel_num,
+			       const char *power_limit, u8 power_limit_val,
+			       struct powerlimit_info_t *powerlimit_info)
+{
+	if (band_cc == PHY_BAND_2G4) {
+		u8 cur_idx = powerlimit_info->txpwr_lmt.ch_cnt_2g4;
+
+		if (cur_idx == 0)
+			AICWFDBG(LOGDEBUG, "[%d]: ch=%s, pwr=%s\n", cur_idx, channel, power_limit);
+		if (cur_idx < MAC_DOMAINCHANNEL_24G_MAX) {
+			powerlimit_info->txpwr_lmt.ch_num_2g4[cur_idx] = channel_num;
+			powerlimit_info->txpwr_lmt.max_pwr_2g4[cur_idx] = power_limit_val;
+			powerlimit_info->txpwr_lmt.ch_cnt_2g4++;
+		} else {
+			AICWFDBG(LOGERROR, "band %d chan_cnt reached %d\n",
+				 band_cc, MAC_DOMAINCHANNEL_24G_MAX);
+			AICWFDBG(LOGERROR, "channel=%s(%d) powerLimit=%s(%d)\n",
+				 channel, channel_num, power_limit, power_limit_val);
+		}
+	} else if (band_cc == PHY_BAND_5G) {
+		u8 cur_idx = powerlimit_info->txpwr_lmt.ch_cnt_5g;
+
+		if (cur_idx == 0)
+			AICWFDBG(LOGDEBUG, "[%d]: ch=%s, pwr=%s\n", cur_idx, channel, power_limit);
+		if (cur_idx < MAC_DOMAINCHANNEL_5G_MAX) {
+			powerlimit_info->txpwr_lmt.ch_num_5g[cur_idx] = channel_num;
+			powerlimit_info->txpwr_lmt.max_pwr_5g[cur_idx] = power_limit_val;
+			powerlimit_info->txpwr_lmt.ch_cnt_5g++;
+		} else {
+			AICWFDBG(LOGERROR, "band %d chan_cnt reached %d\n",
+				 band_cc, MAC_DOMAINCHANNEL_5G_MAX);
+			AICWFDBG(LOGERROR, "channel=%s(%d) powerLimit=%s(%d)\n",
+				 channel, channel_num, power_limit, power_limit_val);
+		}
+	}
+}
+
+void rwnx_plat_powerlimit_parsing(char *buffer, int size, char *cc)
+{
+#define LD_STAGE_EXC_MAPPING   0
+#define LD_STAGE_TAB_DEFINE    1
+#define LD_STAGE_TAB_START     2
+#define LD_STAGE_COLUMN_DEFINE 3
+#define LD_STAGE_CH_ROW        4
+
+	u8 loading_stage = LD_STAGE_EXC_MAPPING;
+	u32 i = 0, for_cnt = 0;
+	u32 i_cc;
+	char *sz_line, *ptmp;
+	char band[10], col_num_buf[10];
+	u8 col_num = 0, col_num_cc = 255, band_cc = 0;
+	bool sp_cc = false;
+	// clear powerlimit info at first
+	memset((void *)&powerlimit_info, 0, sizeof(struct powerlimit_info_t));
+	ptmp = buffer;
+	for (sz_line = get_line_from_buffer(&ptmp); sz_line;
+	     sz_line = get_line_from_buffer(&ptmp)) {
+		if (is_all_space_or_tab(sz_line, sizeof(*sz_line)))
+			continue;
+
+		if (is_enable_limit(sz_line) == 0)
+			return;
+
+		if (is_comment_string(sz_line))
+			continue;
+
+		if (loading_stage == LD_STAGE_EXC_MAPPING) {
+			if (sz_line[0] == '#' || sz_line[1] == '#')
+				loading_stage = LD_STAGE_TAB_DEFINE;
+			else
+				continue;
+		}
+
+		if (loading_stage == LD_STAGE_TAB_DEFINE) {
+			/* read "##	2.4G" */
+			if (sz_line[0] != '#' || sz_line[1] != '#')
+				continue;
+
+			/* skip the space */
+			i = 2;
+			while (sz_line[i] == ' ' || sz_line[i] == '\t')
+				++i;
+
+			sz_line[--i] =
+				' '; /* return the space in front of the regulation info */
+
+			/* Parse the label of the table */
+			memset((void *)band, 0, 10);
+			if (!parse_qualified_string(sz_line, &i, band, ' ', ',')) {
+				AICWFDBG(LOGERROR, "Fail to parse band!\n");
+				goto exit;
+			}
+			if (strncmp(band, "2.4G", 4) == 0)
+				band_cc = PHY_BAND_2G4;
+			else if (strncmp(band, "5G", 2) == 0)
+				band_cc = PHY_BAND_5G;
+
+			memset((void *)col_num_buf, 0, 10);
+			if (!parse_qualified_string(sz_line, &i, col_num_buf, '#', '#')) {
+				AICWFDBG(LOGERROR, "Fail to parse column number!\n");
+				goto exit;
+			}
+			if (!get_u1_byte_integer_from_string_in_decimal(col_num_buf, &col_num)) {
+				AICWFDBG(LOGERROR,
+					 "Column number \"%s\" is not unsigned decimal\n",
+					 col_num_buf);
+				goto exit;
+			}
+			if (col_num == 0) {
+				AICWFDBG(LOGERROR, "Column number is 0\n");
+				goto exit;
+			}
+
+			AICWFDBG(LOGDEBUG, "band=%s(%d)\n", band, band_cc);
+			loading_stage = LD_STAGE_TAB_START;
+		} else if (loading_stage == LD_STAGE_TAB_START) {
+			/* read "##	START" */
+			if (sz_line[0] != '#' || sz_line[1] != '#')
+				continue;
+
+			/* skip the space */
+			i = 2;
+			while (sz_line[i] == ' ' || sz_line[i] == '\t')
+				++i;
+
+			if (strncmp((u8 *)(sz_line + i), "START", 5)) {
+				AICWFDBG(LOGERROR, "Missing \"##   START\" label\n");
+				goto exit;
+			}
+
+			loading_stage = LD_STAGE_COLUMN_DEFINE;
+		} else if (loading_stage == LD_STAGE_COLUMN_DEFINE) {
+			/* read "##	CN	US" */
+			if (sz_line[0] != '#' || sz_line[1] != '#')
+				continue;
+
+			/* skip the space */
+			i = 2;
+			while (sz_line[i] == ' ' || sz_line[i] == '\t')
+				++i;
+
+			for (for_cnt = 0; for_cnt < col_num; for_cnt++) {
+				/* skip the space */
+				while (sz_line[i] == ' ' || sz_line[i] == '\t')
+					i++;
+				i_cc = i;
+
+				while (sz_line[i] != ' ' && sz_line[i] != '\t' &&
+				       sz_line[i] != '\0')
+					i++;
+
+				if ((i - i_cc) != 2) {
+					AICWFDBG(LOGERROR, "CC len err\n");
+					goto exit;
+				} else if ((sz_line[i_cc] == cc[0]) &&
+						   (sz_line[i_cc + 1] == cc[1])) {
+					AICWFDBG(LOGDEBUG, "CC matched: %s, col=%d\n", cc, for_cnt);
+					col_num_cc = for_cnt;
+					powerlimit_info.flags |= POWER_LIMIT_CC_MATCHED_BIT;
+					sp_cc = true;
+					break;
+				}
+			}
+			if (!sp_cc) {
+				col_num_cc = col_num - 1;
+				AICWFDBG(LOGDEBUG, "use 00: %s, col_num_cc=%d\n", cc, col_num_cc);
+				powerlimit_info.flags |= POWER_LIMIT_CC_MATCHED_BIT;
+			}
+
+			loading_stage = LD_STAGE_CH_ROW;
+		} else if (loading_stage == LD_STAGE_CH_ROW) {
+			char channel[10] = {0}, power_limit[10] = {0};
+			u8 channel_num, power_limit_val, cnt = 0;
+
+			/* the table ends */
+			if (sz_line[0] == '#' && sz_line[1] == '#') {
+				i = 2;
+				while (sz_line[i] == ' ' || sz_line[i] == '\t')
+					++i;
+
+				if (strncmp((u8 *)(sz_line + i), "END", 3) == 0) {
+					loading_stage = LD_STAGE_TAB_DEFINE;
+					col_num = 0;
+					continue;
+				} else {
+					AICWFDBG(LOGERROR, "Missing \"##   END\" label\n");
+					goto exit;
+				}
+			}
+
+			if ((sz_line[0] != 'c' && sz_line[0] != 'C') ||
+			    (sz_line[1] != 'h' && sz_line[1] != 'H')) {
+				AICWFDBG(LOGERROR, "Wrong channel prefix: '%c','%c'(%d,%d)\n",
+					 sz_line[0], sz_line[1], sz_line[0], sz_line[1]);
+				continue;
+			}
+			i = 2; /* move to the  location behind 'h' */
+
+			/* load the channel number */
+			cnt = 0;
+			while (sz_line[i] >= '0' && sz_line[i] <= '9') {
+				channel[cnt] = sz_line[i];
+				++cnt;
+				++i;
+			}
+
+			for (for_cnt = 0; for_cnt < col_num; ++for_cnt) {
+				/* skip the space between channel number and the power limit
+				 * value
+				 */
+				while (sz_line[i] == ' ' || sz_line[i] == '\t')
+					++i;
+
+				/* load the power limit value */
+				memset((void *)power_limit, 0, 10);
+
+				if (sz_line[i] == 'N' && sz_line[i + 1] == 'A') {
+					/*
+					 * means channel not available
+					 */
+					sprintf(power_limit, "%d", POWER_LIMIT_INVALID_VAL);
+					i += 2;
+				} else if ((sz_line[i] >= '0' && sz_line[i] <= '9') ||
+						   sz_line[i] == '+' || sz_line[i] == '-') {
+					/* case of dBm value */
+					cnt = 0;
+					while ((sz_line[i] >= '0' && sz_line[i] <= '9') ||
+					       sz_line[i] == '+' || sz_line[i] == '-') {
+						power_limit[cnt] = sz_line[i];
+						++cnt;
+						++i;
+					}
+				} else {
+					AICWFDBG
+					(LOGERROR, "Wrong limit expression \"%c%c\"(%d, %d)\n",
+					 sz_line[i], sz_line[i + 1], sz_line[i], sz_line[i + 1]);
+					goto exit;
+				}
+
+				if (for_cnt == col_num_cc) {
+					/* store the power limit value */
+					if (get_u1_byte_integer_from_string_in_decimal
+						((char *)channel, &channel_num) == 0 ||
+					    get_s1_byte_integer_from_string_in_decimal
+						((char *)power_limit, &power_limit_val) == 0) {
+						AICWFDBG(LOGERROR,
+							 "Illegal index of power limit table [ch %s][val %s]\n",
+							 channel, power_limit);
+						goto exit;
+					}
+
+					update_power_limit(band_cc, channel, channel_num,
+							   power_limit, power_limit_val,
+							   &powerlimit_info);
+					break;
+				}
+			}
+		}
+	}
+exit:
+	return;
+}
+
+/// 5G lower bound freq
+#define PHY_FREQ_5G 5000
+
+u16 phy_channel_to_freq(u8 band, int channel)
+{
+	if (band == PHY_BAND_2G4 && channel >= 1 && channel <= 14) {
+		if (channel == 14)
+			return 2484;
+		else
+			return 2407 + channel * 5;
+	} else if (band == PHY_BAND_5G && channel >= 1 && channel <= 165) {
+		return PHY_FREQ_5G + channel * 5;
+	}
+	return 0;
+}
+
+s8 get_powerlimit_by_freq(u8 band, u16 freq, u8 *enable)
+{
+	s8 ret = POWER_LIMIT_INVALID_VAL;
+	u8 idx;
+
+	if ((powerlimit_info.flags & POWER_LIMIT_CC_MATCHED_BIT) == 0) {
+		*enable = 0;
+	} else if (powerlimit_info.flags & POWER_LIMIT_CC_MATCHED_BIT) {
+		*enable = 1;
+
+		if (band == PHY_BAND_2G4) {
+			u8 idx_cnt = powerlimit_info.txpwr_lmt.ch_cnt_2g4;
+
+			for (idx = 0; idx < idx_cnt; idx++) {
+				int ch_num = powerlimit_info.txpwr_lmt.ch_num_2g4[idx];
+				u16 freq_tmp = phy_channel_to_freq(PHY_BAND_2G4, ch_num);
+
+				if (freq == freq_tmp) {
+					ret = powerlimit_info.txpwr_lmt.max_pwr_2g4[idx];
+					// AICWFDBG(LOGINFO, "[%d]: ch=%d(freq=%d), pwr=%d\n", idx,
+					// ch_num, freq, ret);
+					break;
+				}
+			}
+			if (idx == idx_cnt) {
+				AICWFDBG(LOGERROR,
+					 "powerlimit search failed: band=%d freq=%d\n", band,
+					 freq);
+			}
+		} else if (band == PHY_BAND_5G) {
+			u8 idx_cnt = powerlimit_info.txpwr_lmt.ch_cnt_5g;
+
+			for (idx = 0; idx < idx_cnt; idx++) {
+				int ch_num = powerlimit_info.txpwr_lmt.ch_num_5g[idx];
+				u16 freq_tmp = phy_channel_to_freq(PHY_BAND_5G, ch_num);
+
+				if (freq == freq_tmp) {
+					ret = powerlimit_info.txpwr_lmt.max_pwr_5g[idx];
+					// AICWFDBG(LOGINFO, "[%d]: ch=%d(freq=%d), pwr=%d\n", idx,
+					// ch_num, freq, ret);
+					break;
+				}
+			}
+			if (idx == idx_cnt) {
+				AICWFDBG(LOGERROR,
+					 "powerlimit search failed: band=%d freq=%d\n", band,
+					 freq);
+			}
+		}
+	}
+	return ret;
+}
+
+s8 get_powerlimit_by_chnum(u8 chnum, u8 *enable)
+{
+	s8 ret = POWER_LIMIT_INVALID_VAL;
+	u8 idx;
+
+	if ((powerlimit_info.flags & POWER_LIMIT_CC_MATCHED_BIT) == 0) {
+		*enable = 0;
+	} else if (powerlimit_info.flags & POWER_LIMIT_CC_MATCHED_BIT) {
+		*enable = 1;
+
+		if (chnum <= 14) {
+			u8 idx_cnt = powerlimit_info.txpwr_lmt.ch_cnt_2g4;
+
+			for (idx = 0; idx < idx_cnt; idx++) {
+				u8 ch_num = powerlimit_info.txpwr_lmt.ch_num_2g4[idx];
+
+				if (chnum == ch_num) {
+					ret = powerlimit_info.txpwr_lmt.max_pwr_2g4[idx];
+					// AICWFDBG(LOGINFO, "[%d]: ch=%d, pwr=%d\n", idx, ch_num,
+					// ret);
+					break;
+				}
+			}
+			if (idx == idx_cnt) {
+				AICWFDBG(LOGERROR, "powerlimit search failed: chnum=%d\n",
+					 chnum);
+			}
+		} else if (chnum <= 165) {
+			u8 idx_cnt = powerlimit_info.txpwr_lmt.ch_cnt_5g;
+
+			for (idx = 0; idx < idx_cnt; idx++) {
+				int ch_num = powerlimit_info.txpwr_lmt.ch_num_5g[idx];
+
+				if (chnum == ch_num) {
+					ret = powerlimit_info.txpwr_lmt.max_pwr_5g[idx];
+					// AICWFDBG(LOGINFO, "[%d]: ch=%d, pwr=%d\n", idx, ch_num,
+					// ret);
+					break;
+				}
+			}
+			if (idx == idx_cnt) {
+				AICWFDBG(LOGERROR, "powerlimit search failed: chnum=%d\n",
+					 chnum);
+			}
+		}
+	}
+	return ret;
+}
+#endif
+
+/**
+ * rwnx_platform_reset - Reset the platform
+ *
+ * @rwnx_plat: platform data
+ *
+ * Return: 0 on success, or %-EIO if the platform did not reset.
+ */
+static int rwnx_platform_reset(struct rwnx_plat *rwnx_plat)
+{
+	u32 regval;
+
+#if defined(AICWF_SDIO_SUPPORT)
+	return 0;
+#endif
+
+	/*
+	 * the doc states that SOFT implies FPGA_B_RESET
+	 * adding FPGA_B_RESET is clearer
+	 */
+	RWNX_REG_WRITE(SOFT_RESET | FPGA_B_RESET, rwnx_plat, RWNX_ADDR_SYSTEM,
+		       SYSCTRL_MISC_CNTL_ADDR);
+	msleep(100);
+
+	regval = RWNX_REG_READ(rwnx_plat, RWNX_ADDR_SYSTEM, SYSCTRL_MISC_CNTL_ADDR);
+
+	if (regval & SOFT_RESET) {
+		dev_err(rwnx_platform_get_dev(rwnx_plat), "reset: failed\n");
+		return -EIO;
+	}
+
+	RWNX_REG_WRITE(regval & ~FPGA_B_RESET, rwnx_plat, RWNX_ADDR_SYSTEM,
+		       SYSCTRL_MISC_CNTL_ADDR);
+	msleep(100);
+	return 0;
+}
+
+/*
+ * rwmx_platform_save_config() - Save hardware config before reload
+ *
+ * @rwnx_plat: Pointer to platform data
+ *
+ * Return configuration registers values.
+ */
+static void *rwnx_term_save_config(struct rwnx_plat *rwnx_plat)
+{
+	const u32 *reg_list;
+	u32 *reg_value, *res;
+	int i, size = 0;
+
+	if (rwnx_plat->get_config_reg)
+		size = rwnx_plat->get_config_reg(rwnx_plat, &reg_list);
+
+	if (size <= 0)
+		return NULL;
+
+	// res = kmalloc(sizeof(u32) * size, GFP_KERNEL);
+	res = kmalloc_array(size, sizeof(u32), GFP_KERNEL);
+	if (!res)
+		return NULL;
+
+	reg_value = res;
+	for (i = 0; i < size; i++)
+		*reg_value++ = RWNX_REG_READ(rwnx_plat, RWNX_ADDR_SYSTEM, *reg_list++);
+
+	return res;
+}
+
+void get_userconfig_txpwr_lvl_in_fdrv(struct txpwr_lvl_conf *txpwr_lvl)
+{
+	txpwr_lvl->enable = userconfig_info.txpwr_lvl.enable;
+	txpwr_lvl->dsss = userconfig_info.txpwr_lvl.dsss;
+	txpwr_lvl->ofdmlowrate_2g4 = userconfig_info.txpwr_lvl.ofdmlowrate_2g4;
+	txpwr_lvl->ofdm64qam_2g4 = userconfig_info.txpwr_lvl.ofdm64qam_2g4;
+	txpwr_lvl->ofdm256qam_2g4 = userconfig_info.txpwr_lvl.ofdm256qam_2g4;
+	txpwr_lvl->ofdm1024qam_2g4 = userconfig_info.txpwr_lvl.ofdm1024qam_2g4;
+	txpwr_lvl->ofdmlowrate_5g = userconfig_info.txpwr_lvl.ofdmlowrate_5g;
+	txpwr_lvl->ofdm64qam_5g = userconfig_info.txpwr_lvl.ofdm64qam_5g;
+	txpwr_lvl->ofdm256qam_5g = userconfig_info.txpwr_lvl.ofdm256qam_5g;
+	txpwr_lvl->ofdm1024qam_5g = userconfig_info.txpwr_lvl.ofdm1024qam_5g;
+
+	AICWFDBG(LOGDEBUG, "%s:enable:%d\r\n", __func__, txpwr_lvl->enable);
+	AICWFDBG(LOGDEBUG, "%s:dsss:%d\r\n", __func__, txpwr_lvl->dsss);
+	AICWFDBG(LOGDEBUG, "%s:ofdmlowrate_2g4:%d\r\n", __func__,
+		 txpwr_lvl->ofdmlowrate_2g4);
+	AICWFDBG(LOGDEBUG, "%s:ofdm64qam_2g4:%d\r\n", __func__,
+		 txpwr_lvl->ofdm64qam_2g4);
+	AICWFDBG(LOGDEBUG, "%s:ofdm256qam_2g4:%d\r\n", __func__,
+		 txpwr_lvl->ofdm256qam_2g4);
+	AICWFDBG(LOGDEBUG, "%s:ofdm1024qam_2g4:%d\r\n", __func__,
+		 txpwr_lvl->ofdm1024qam_2g4);
+	AICWFDBG(LOGDEBUG, "%s:ofdmlowrate_5g:%d\r\n", __func__,
+		 txpwr_lvl->ofdmlowrate_5g);
+	AICWFDBG(LOGDEBUG, "%s:ofdm64qam_5g:%d\r\n", __func__,
+		 txpwr_lvl->ofdm64qam_5g);
+	AICWFDBG(LOGDEBUG, "%s:ofdm256qam_5g:%d\r\n", __func__,
+		 txpwr_lvl->ofdm256qam_5g);
+	AICWFDBG(LOGDEBUG, "%s:ofdm1024qam_5g:%d\r\n", __func__,
+		 txpwr_lvl->ofdm1024qam_5g);
+}
+
+void get_userconfig_txpwr_lvl_v2_in_fdrv(struct txpwr_lvl_conf_v2 *txpwr_lvl_v2)
+{
+	*txpwr_lvl_v2 = userconfig_info.txpwr_lvl_v2;
+
+	AICWFDBG(LOGDEBUG, "%s:enable:%d\r\n", __func__, txpwr_lvl_v2->enable);
+	AICWFDBG(LOGDEBUG, "%s:lvl_11b_11ag_1m_2g4:%d\r\n", __func__,
+		 txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[0]);
+	AICWFDBG(LOGDEBUG, "%s:lvl_11b_11ag_2m_2g4:%d\r\n", __func__,
+		 txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[1]);
+	AICWFDBG(LOGDEBUG, "%s:lvl_11b_11ag_5m5_2g4:%d\r\n", __func__,
+		 txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[2]);
+	AICWFDBG(LOGDEBUG, "%s:lvl_11b_11ag_11m_2g4:%d\r\n", __func__,
+		 txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[3]);
+	AICWFDBG(LOGDEBUG, "%s:lvl_11b_11ag_6m_2g4:%d\r\n", __func__,
+		 txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[4]);
+	AICWFDBG(LOGDEBUG, "%s:lvl_11b_11ag_9m_2g4:%d\r\n", __func__,
+		 txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[5]);
+	AICWFDBG(LOGDEBUG, "%s:lvl_11b_11ag_12m_2g4:%d\r\n", __func__,
+		 txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[6]);
+	AICWFDBG(LOGDEBUG, "%s:lvl_11b_11ag_18m_2g4:%d\r\n", __func__,
+		 txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[7]);
+	AICWFDBG(LOGDEBUG, "%s:lvl_11b_11ag_24m_2g4:%d\r\n", __func__,
+		 txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[8]);
+	AICWFDBG(LOGDEBUG, "%s:lvl_11b_11ag_36m_2g4:%d\r\n", __func__,
+		 txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[9]);
+	AICWFDBG(LOGDEBUG, "%s:lvl_11b_11ag_48m_2g4:%d\r\n", __func__,
+		 txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[10]);
+	AICWFDBG(LOGDEBUG, "%s:lvl_11b_11ag_54m_2g4:%d\r\n", __func__,
+		 txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[11]);
+	AICWFDBG(LOGDEBUG, "%s:lvl_11n_11ac_mcs0_2g4:%d\r\n", __func__,
+		 txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[0]);
+	AICWFDBG(LOGDEBUG, "%s:lvl_11n_11ac_mcs1_2g4:%d\r\n", __func__,
+		 txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[1]);
+	AICWFDBG(LOGDEBUG, "%s:lvl_11n_11ac_mcs2_2g4:%d\r\n", __func__,
+		 txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[2]);
+	AICWFDBG(LOGDEBUG, "%s:lvl_11n_11ac_mcs3_2g4:%d\r\n", __func__,
+		 txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[3]);
+	AICWFDBG(LOGDEBUG, "%s:lvl_11n_11ac_mcs4_2g4:%d\r\n", __func__,
+		 txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[4]);
+	AICWFDBG(LOGDEBUG, "%s:lvl_11n_11ac_mcs5_2g4:%d\r\n", __func__,
+		 txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[5]);
+	AICWFDBG(LOGDEBUG, "%s:lvl_11n_11ac_mcs6_2g4:%d\r\n", __func__,
+		 txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[6]);
+	AICWFDBG(LOGDEBUG, "%s:lvl_11n_11ac_mcs7_2g4:%d\r\n", __func__,
+		 txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[7]);
+	AICWFDBG(LOGDEBUG, "%s:lvl_11n_11ac_mcs8_2g4:%d\r\n", __func__,
+		 txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[8]);
+	AICWFDBG(LOGDEBUG, "%s:lvl_11n_11ac_mcs9_2g4:%d\r\n", __func__,
+		 txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[9]);
+	AICWFDBG(LOGDEBUG, "%s:lvl_11ax_mcs0_2g4:%d\r\n", __func__,
+		 txpwr_lvl_v2->pwrlvl_11ax_2g4[0]);
+	AICWFDBG(LOGDEBUG, "%s:lvl_11ax_mcs1_2g4:%d\r\n", __func__,
+		 txpwr_lvl_v2->pwrlvl_11ax_2g4[1]);
+	AICWFDBG(LOGDEBUG, "%s:lvl_11ax_mcs2_2g4:%d\r\n", __func__,
+		 txpwr_lvl_v2->pwrlvl_11ax_2g4[2]);
+	AICWFDBG(LOGDEBUG, "%s:lvl_11ax_mcs3_2g4:%d\r\n", __func__,
+		 txpwr_lvl_v2->pwrlvl_11ax_2g4[3]);
+	AICWFDBG(LOGDEBUG, "%s:lvl_11ax_mcs4_2g4:%d\r\n", __func__,
+		 txpwr_lvl_v2->pwrlvl_11ax_2g4[4]);
+	AICWFDBG(LOGDEBUG, "%s:lvl_11ax_mcs5_2g4:%d\r\n", __func__,
+		 txpwr_lvl_v2->pwrlvl_11ax_2g4[5]);
+	AICWFDBG(LOGDEBUG, "%s:lvl_11ax_mcs6_2g4:%d\r\n", __func__,
+		 txpwr_lvl_v2->pwrlvl_11ax_2g4[6]);
+	AICWFDBG(LOGDEBUG, "%s:lvl_11ax_mcs7_2g4:%d\r\n", __func__,
+		 txpwr_lvl_v2->pwrlvl_11ax_2g4[7]);
+	AICWFDBG(LOGDEBUG, "%s:lvl_11ax_mcs8_2g4:%d\r\n", __func__,
+		 txpwr_lvl_v2->pwrlvl_11ax_2g4[8]);
+	AICWFDBG(LOGDEBUG, "%s:lvl_11ax_mcs9_2g4:%d\r\n", __func__,
+		 txpwr_lvl_v2->pwrlvl_11ax_2g4[9]);
+	AICWFDBG(LOGDEBUG, "%s:lvl_11ax_mcs10_2g4:%d\r\n", __func__,
+		 txpwr_lvl_v2->pwrlvl_11ax_2g4[10]);
+	AICWFDBG(LOGDEBUG, "%s:lvl_11ax_mcs11_2g4:%d\r\n", __func__,
+		 txpwr_lvl_v2->pwrlvl_11ax_2g4[11]);
+}
+
+void get_userconfig_txpwr_lvl_group_in_fdrv(struct txpwr_lvl_conf_v3 *txpwr_lvl_v3, int index_reg)
+{
+	*txpwr_lvl_v3 = userconfig_info.txpwr_lvl_v3_gp[index_reg];
+
+	AICWFDBG(LOGDEBUG, "%s:enable:%d\r\n", __func__, txpwr_lvl_v3->enable);
+	AICWFDBG(LOGDEBUG, "lvl_11b_11ag_1m_2g4:%d\r\n", txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[0]);
+}
+
+void get_userconfig_txpwr_lvl_v3_in_fdrv(struct txpwr_lvl_conf_v3 *txpwr_lvl_v3)
+{
+	*txpwr_lvl_v3 = userconfig_info.txpwr_lvl_v3;
+
+	AICWFDBG(LOGDEBUG, "%s:enable:%d\r\n", __func__, txpwr_lvl_v3->enable);
+	AICWFDBG(LOGDEBUG, "lvl_11b_11ag_1m_2g4:%d\r\n", txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[0]);
+}
+
+void get_userconfig_txpwr_lvl_adj_in_fdrv(struct txpwr_lvl_adj_conf *txpwr_lvl_adj)
+{
+	*txpwr_lvl_adj = userconfig_info.txpwr_lvl_adj;
+
+	AICWFDBG(LOGDEBUG, "%s:enable:%d\r\n", __func__, txpwr_lvl_adj->enable);
+	AICWFDBG(LOGDEBUG, "lvl_adj_2g4_chan_1_4:%d\r\n", txpwr_lvl_adj->pwrlvl_adj_tbl_2g4[0]);
+}
+
+/*
+ * rwnx_plat_userconfig_load  ---Load aic_userconfig.txt
+ *@filename name of config
+ */
+static int rwnx_plat_userconfig_load(struct rwnx_hw *rwnx_hw)
+{
+	aic_chip_userconfig_load(rwnx_hw);
+#ifdef CONFIG_AIC8800_POWER_LIMIT
+	aic_chip_powerlimit_load(rwnx_hw);
+#endif
+	return 0;
+}
+
+void get_userconfig_txpwr_loss(struct txpwr_loss_conf *txpwr_loss)
+{
+	txpwr_loss->loss_enable_2g4 = userconfig_info.txpwr_loss.loss_enable_2g4;
+	txpwr_loss->loss_value_2g4 = userconfig_info.txpwr_loss.loss_value_2g4;
+	txpwr_loss->loss_enable_5g = userconfig_info.txpwr_loss.loss_enable_5g;
+	txpwr_loss->loss_value_5g = userconfig_info.txpwr_loss.loss_value_5g;
+
+	AICWFDBG(LOGDEBUG,
+		 "%s:loss_enable_2g4: %d, val_2g4: %d, loss_enable_5g: %d, val_5g: %d\r\n",
+		 __func__,
+		 txpwr_loss->loss_enable_2g4, txpwr_loss->loss_value_2g4,
+		 txpwr_loss->loss_enable_5g, txpwr_loss->loss_value_5g);
+}
+
+/**
+ * rwnx_platform_on - Start the platform
+ *
+ * @rwnx_hw: Main driver data
+ * @config: Config to restore (NULL if nothing to restore)
+ *
+ * It starts the platform :
+ * - load fw and ucodes
+ * - initialize IPC
+ * - boot the fw
+ * - enable link communication/IRQ
+ *
+ * Called by 802.11 part
+ *
+ * Return: 0 on success, or a negative error code.
+ */
+int rwnx_platform_on(struct rwnx_hw *rwnx_hw, void *config)
+{
+	int ret;
+	struct rwnx_plat *rwnx_plat = rwnx_hw->plat;
+	(void)ret;
+
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	if (rwnx_plat->enabled)
+		return 0;
+
+	rwnx_plat_userconfig_load(rwnx_hw);
+	// rwnx_plat->enabled = true;
+	return 0;
+}
+
+/**
+ * rwnx_platform_off() - Stop the platform
+ *
+ * @rwnx_hw: Main driver data
+ * @config: Updated with pointer to config, to be able to restore it with
+ * rwnx_platform_on(). It's up to the caller to free the config. Set to NULL
+ * if configuration is not needed.
+ *
+ * Called by 802.11 part
+ */
+void rwnx_platform_off(struct rwnx_hw *rwnx_hw, void **config)
+{
+#if defined(AICWF_SDIO_SUPPORT)
+	tasklet_kill(&rwnx_hw->task);
+	rwnx_hw->plat->enabled = false;
+	return;
+#endif
+
+	if (!rwnx_hw->plat->enabled) {
+		if (config)
+			*config = NULL;
+		return;
+	}
+
+	if (config)
+		*config = rwnx_term_save_config(rwnx_hw->plat);
+
+	rwnx_hw->plat->disable(rwnx_hw);
+
+	tasklet_kill(&rwnx_hw->task);
+	rwnx_platform_reset(rwnx_hw->plat);
+
+	rwnx_hw->plat->enabled = false;
+}
+
+/**
+ * rwnx_platform_init() - Initialize the platform
+ *
+ * @rwnx_plat: platform data (already updated by platform driver)
+ * @platform_data: Pointer to store the main driver data pointer (aka rwnx_hw)
+ *                That will be set as driver data for the platform driver
+ * Return: 0 on success, < 0 otherwise
+ *
+ * Called by the platform driver after it has been probed
+ */
+int rwnx_platform_init(struct rwnx_plat *rwnx_plat, void **platform_data)
+{
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	rwnx_plat->enabled = false;
+	g_rwnx_plat = rwnx_plat;
+
+	return rwnx_cfg80211_init(rwnx_plat, platform_data);
+}
+
+/**
+ * rwnx_platform_deinit() - Deinitialize the platform
+ *
+ * @rwnx_hw: main driver data
+ *
+ * Called by the platform driver after it is removed
+ */
+void rwnx_platform_deinit(struct rwnx_hw *rwnx_hw)
+{
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+	rwnx_cfg80211_deinit(rwnx_hw);
+}
+
+struct device *rwnx_platform_get_dev(struct rwnx_plat *rwnx_plat)
+{
+#ifdef AICWF_SDIO_SUPPORT
+	return rwnx_plat->sdiodev->dev;
+#endif
+}
+
+MODULE_FIRMWARE(AIC8800_FW_DIR RWNX_AGC_FW_NAME);
+MODULE_FIRMWARE(AIC8800_FW_DIR RWNX_FCU_FW_NAME);
+MODULE_FIRMWARE(AIC8800_FW_DIR RWNX_LDPC_RAM_NAME);
+
+MODULE_FIRMWARE(AIC8800_FW_DIR RWNX_MAC_FW_NAME);
+MODULE_FIRMWARE(AIC8800_FW_DIR RWNX_MAC_FW_NAME2);
diff --git a/drivers/net/wireless/aic/aic8800_fdrv/rwnx_platform.h b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_platform.h
new file mode 100644
index 0000000000000..efcff3b08b2a3
--- /dev/null
+++ b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_platform.h
@@ -0,0 +1,183 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _RWNX_PLATFORM_H_
+#define _RWNX_PLATFORM_H_
+
+#include "lmac_msg.h"
+#include <linux/pci.h>
+
+#define AIC8800_FW_DIR "aic8800/"
+
+#define RWNX_CONFIG_FW_NAME        "rwnx_settings.ini"
+#define RWNX_PHY_CONFIG_TRD_NAME   "rwnx_trident.ini"
+#define RWNX_PHY_CONFIG_KARST_NAME "rwnx_karst.ini"
+#define RWNX_AGC_FW_NAME           "agcram.bin"
+#define RWNX_LDPC_RAM_NAME         "ldpcram.bin"
+
+#define RWNX_MAC_FW_BASE_NAME "fmacfw"
+
+#define RWNX_MAC_FW_NAME  RWNX_MAC_FW_BASE_NAME ".ihex"
+#define RWNX_MAC_FW_NAME2 RWNX_MAC_FW_BASE_NAME ".bin"
+
+#define RWNX_FCU_FW_NAME "fcuram.bin"
+#if (defined(CONFIG_DPD) && !defined(CONFIG_FORCE_DPD_CALIB))
+#define FW_DPDRESULT_NAME_8800DC "aic_dpdresult_lite_8800dc.bin"
+#endif
+
+#define POWER_LEVEL_INVALID_VAL (127)
+
+/*
+ * Type of memory to access (cf rwnx_plat.get_address)
+ *
+ * @RWNX_ADDR_CPU To access memory of the embedded CPU
+ * @RWNX_ADDR_SYSTEM To access memory/registers of one subsystem of the
+ * embedded system
+ *
+ */
+enum rwnx_platform_addr {
+	RWNX_ADDR_CPU,
+	RWNX_ADDR_SYSTEM,
+	RWNX_ADDR_MAX,
+};
+
+enum regions_code {
+	REGIONS_SRRC,
+	REGIONS_FCC,
+	REGIONS_ETSI,
+	REGIONS_JP,
+	REGIONS_DEFAULT,
+};
+
+struct rwnx_hw;
+struct device;
+struct firmware;
+
+int aicwf_request_firmware(const struct firmware **fw, const char *name,
+			   struct device *dev);
+
+/**
+ * struct rwnx_plat - Operations and state for an RWNX platform
+ *
+ * @pci_dev: pointer to pci dev
+ * @sdiodev: SDIO device data
+ * @enabled: Set if embedded platform has been enabled (i.e. fw loaded and
+ *          ipc started)
+ * @enable: Configure communication with the fw (i.e. configure the transfers
+ *         enable and register interrupt)
+ * @disable: Stop communication with the fw
+ * @deinit: Free all resources allocated for the embedded platform
+ * @get_address: Return the virtual address to access the requested address on
+ *              the platform.
+ * @ack_irq: Acknowledge the irq at link level.
+ * @get_config_reg: Return the list (size + pointer) of registers to restore in
+ * order to reload the platform while keeping the current configuration.
+ *
+ * @priv: Private data for the link driver
+ */
+struct rwnx_plat {
+	struct pci_dev *pci_dev;
+
+#ifdef AICWF_SDIO_SUPPORT
+	struct aic_sdio_dev *sdiodev;
+#endif
+
+	bool enabled;
+
+	int (*enable)(struct rwnx_hw *rwnx_hw);
+	int (*disable)(struct rwnx_hw *rwnx_hw);
+	void (*deinit)(struct rwnx_plat *rwnx_plat);
+	u8 __iomem *(*get_address)(struct rwnx_plat *rwnx_plat, int addr_name,
+				   unsigned int offset);
+	void (*ack_irq)(struct rwnx_plat *rwnx_plat);
+	int (*get_config_reg)(struct rwnx_plat *rwnx_plat, const u32 **list);
+
+	u8 priv[0] __aligned(sizeof(void *));
+};
+
+static inline void __iomem *RWNX_ADDR(void *plat, int base, int offset)
+{
+	struct rwnx_plat *p = (struct rwnx_plat *)plat;
+
+	return p->get_address(p, base, offset);
+}
+
+static inline u32 RWNX_REG_READ(void *plat, int base, int offset)
+{
+	struct rwnx_plat *p = (struct rwnx_plat *)plat;
+
+	return readl(p->get_address(p, base, offset));
+}
+
+static inline void RWNX_REG_WRITE(u32 val, void *plat, int base, int offset)
+{
+	struct rwnx_plat *p = (struct rwnx_plat *)plat;
+
+	writel(val, p->get_address(p, base, offset));
+}
+
+extern struct rwnx_plat *g_rwnx_plat;
+
+int rwnx_platform_init(struct rwnx_plat *rwnx_plat, void **platform_data);
+void rwnx_platform_deinit(struct rwnx_hw *rwnx_hw);
+
+int rwnx_platform_on(struct rwnx_hw *rwnx_hw, void *config);
+void rwnx_platform_off(struct rwnx_hw *rwnx_hw, void **config);
+
+int rwnx_platform_register_drv(void);
+void rwnx_platform_unregister_drv(void);
+
+void get_userconfig_txpwr_idx(struct txpwr_idx_conf *txpwr_idx);
+void get_userconfig_txpwr_ofst(struct txpwr_ofst_conf *txpwr_ofst);
+void get_userconfig_xtal_cap(struct xtal_cap_conf *xtal_cap);
+s8 get_txpwr_max(s8 power);
+void set_txpwr_loss_ofst(s8 value);
+
+int get_ccode_region(char *ccode);
+
+void rwnx_release_firmware_common(u32 **buffer);
+
+int rwnx_plat_userconfig_upload(struct rwnx_hw *rwnx_hw,
+				const char *filename);
+void rwnx_plat_userconfig_parsing(struct rwnx_hw *rwnx_hw, char *buffer, int size);
+void rwnx_plat_userconfig_parsing2(char *buffer, int size);
+void rwnx_plat_userconfig_parsing3(char *buffer, int size);
+
+void get_userconfig_txpwr_lvl_in_fdrv(struct txpwr_lvl_conf *txpwr_lvl);
+void get_userconfig_txpwr_lvl_v2_in_fdrv(struct txpwr_lvl_conf_v2 *txpwr_lvl_v2);
+void get_userconfig_txpwr_lvl_group_in_fdrv(struct txpwr_lvl_conf_v3 *txpwr_lvl_v3, int index_reg);
+void get_userconfig_txpwr_lvl_v3_in_fdrv(struct txpwr_lvl_conf_v3 *txpwr_lvl_v3);
+void get_userconfig_txpwr_lvl_adj_in_fdrv(struct txpwr_lvl_adj_conf *txpwr_lvl_adj);
+
+void get_userconfig_txpwr_ofst_in_fdrv(struct txpwr_ofst_conf *txpwr_ofst);
+void get_userconfig_txpwr_ofst2x_in_fdrv(struct txpwr_ofst2x_conf *txpwr_ofst2x);
+void get_userconfig_txpwr_loss(struct txpwr_loss_conf *txpwr_loss);
+#ifdef CONFIG_AIC8800_POWER_LIMIT
+int is_all_space_or_tab(u8 *data, u8 size);
+int is_comment_string(char *sz_str);
+int is_enable_limit(char *sz_str);
+int parse_qualified_string(char *in, u32 *start, char *out, char left_qualifier,
+			   char right_qualifier);
+int get_u1_byte_integer_from_string_in_decimal(char *str, u8 *p_int);
+int get_s1_byte_integer_from_string_in_decimal(char *str, s8 *val);
+void rwnx_plat_powerlimit_parsing(char *buffer, int size, char *cc);
+s8 get_powerlimit_by_freq(u8 band, u16 freq, u8 *enable);
+s8 get_powerlimit_by_chnum(u8 chnum, u8 *enable);
+u16 phy_channel_to_freq(u8 band, int channel);
+#endif
+
+struct device *rwnx_platform_get_dev(struct rwnx_plat *rwnx_plat);
+
+int rwnx_request_firmware_common(struct rwnx_hw *rwnx_hw, u32 **buffer,
+				 const char *filename);
+int rwnx_plat_bin_fw_upload_2(struct rwnx_hw *rwnx_hw, u32 fw_addr,
+			      char *filename);
+int rwnx_atoi(char *value);
+void rwnx_plat_nvram_set_value(char *command, char *value);
+void rwnx_plat_nvram_set_value_group(char *command, char *value);
+void rwnx_plat_nvram_set_value_v3(char *command, char *value);
+
+static inline unsigned int rwnx_platform_get_irq(struct rwnx_plat *rwnx_plat)
+{
+	return rwnx_plat->pci_dev->irq;
+}
+
+#endif /* _RWNX_PLATFORM_H_ */
diff --git a/drivers/net/wireless/aic/aic8800_fdrv/rwnx_prof.h b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_prof.h
new file mode 100644
index 0000000000000..093e243f3c94f
--- /dev/null
+++ b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_prof.h
@@ -0,0 +1,142 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _RWNX_PROF_H_
+#define _RWNX_PROF_H_
+
+#include "reg_access.h"
+#include "rwnx_platform.h"
+#include "rwnx_radar.h"
+
+static inline void rwnx_prof_set(struct rwnx_hw *rwnx_hw, int val)
+{
+	struct rwnx_plat *rwnx_plat = rwnx_hw->plat;
+
+	RWNX_REG_WRITE(val, rwnx_plat, RWNX_ADDR_SYSTEM,
+		       NXMAC_SW_SET_PROFILING_ADDR);
+}
+
+static inline void rwnx_prof_clear(struct rwnx_hw *rwnx_hw, int val)
+{
+	struct rwnx_plat *rwnx_plat = rwnx_hw->plat;
+
+	RWNX_REG_WRITE(val, rwnx_plat, RWNX_ADDR_SYSTEM,
+		       NXMAC_SW_CLEAR_PROFILING_ADDR);
+}
+
+enum {
+	SW_PROF_HOSTBUF_IDX = 12,
+	/****** IPC IRQs related signals ******/
+	/* E2A direction */
+	SW_PROF_IRQ_E2A_RXDESC =
+		16, // to make sure we let 16 bits available for LMAC FW
+	SW_PROF_IRQ_E2A_TXCFM,
+	SW_PROF_IRQ_E2A_DBG,
+	SW_PROF_IRQ_E2A_MSG,
+	SW_PROF_IPC_MSGPUSH,
+	SW_PROF_MSGALLOC,
+	SW_PROF_MSGIND,
+	SW_PROF_DBGIND,
+
+	/* A2E direction */
+	SW_PROF_IRQ_A2E_TXCFM_BACK,
+
+	/****** Driver functions related signals ******/
+	SW_PROF_WAIT_QUEUE_STOP,
+	SW_PROF_WAIT_QUEUE_WAKEUP,
+	SW_PROF_RWNXDATAIND,
+	SW_PROF_RWNX_IPC_IRQ_HDLR,
+	SW_PROF_RWNX_IPC_THR_IRQ_HDLR,
+	SW_PROF_IEEE80211RX,
+	SW_PROF_RWNX_PATTERN,
+	SW_PROF_MAX
+};
+
+// [LT]For debug purpose only
+#if (0)
+#define SW_PROF_CHAN_CTXT_CFM_HDL_BIT    (21)
+#define SW_PROF_CHAN_CTXT_CFM_BIT        (22)
+#define SW_PROF_CHAN_CTXT_CFM_SWDONE_BIT (23)
+#define SW_PROF_CHAN_CTXT_PUSH_BIT       (24)
+#define SW_PROF_CHAN_CTXT_QUEUE_BIT      (25)
+#define SW_PROF_CHAN_CTXT_TX_BIT         (26)
+#define SW_PROF_CHAN_CTXT_TX_PAUSE_BIT   (27)
+#define SW_PROF_CHAN_CTXT_PSWTCH_BIT     (28)
+#define SW_PROF_CHAN_CTXT_SWTCH_BIT      (29)
+
+#else
+#define SW_PROF_CHAN_CTXT_CFM_HDL_BIT    (0)
+#define SW_PROF_CHAN_CTXT_CFM_BIT        (0)
+#define SW_PROF_CHAN_CTXT_CFM_SWDONE_BIT (0)
+#define SW_PROF_CHAN_CTXT_PUSH_BIT       (0)
+#define SW_PROF_CHAN_CTXT_QUEUE_BIT      (0)
+#define SW_PROF_CHAN_CTXT_TX_BIT         (0)
+#define SW_PROF_CHAN_CTXT_TX_PAUSE_BIT   (0)
+#define SW_PROF_CHAN_CTXT_PSWTCH_BIT     (0)
+#define SW_PROF_CHAN_CTXT_SWTCH_BIT      (0)
+#endif
+
+static inline void REG_SW_SET_PROFILING_CHAN(void *env, int bit)
+{
+#if (0)
+	rwnx_prof_clear((struct rwnx_hw *)env, BIT(bit));
+#else
+
+#endif
+}
+
+static inline void REG_SW_CLEAR_PROFILING_CHAN(void *env, int bit)
+{
+#if (0)
+	rwnx_prof_clear((struct rwnx_hw *)env, BIT(bit));
+#else
+
+#endif
+}
+
+#ifdef CONFIG_RWNX_SW_PROFILING
+/* Macros for SW PRofiling registers access */
+static inline void REG_SW_SET_PROFILING(void *env, int bit)
+{
+	rwnx_prof_set((struct rwnx_hw *)env, BIT(bit));
+}
+
+static inline void REG_SW_SET_HOSTBUF_IDX_PROFILING(void *env, int val)
+{
+	rwnx_prof_set((struct rwnx_hw *)env, (val) << (SW_PROF_HOSTBUF_IDX));
+}
+
+static inline void REG_SW_CLEAR_PROFILING(void *env, int bit)
+{
+	rwnx_prof_clear((struct rwnx_hw *)env, BIT(bit));
+}
+
+static inline void REG_SW_CLEAR_HOSTBUF_IDX_PROFILING(void *env)
+{
+	rwnx_prof_clear((struct rwnx_hw *)env, 0x0F << (SW_PROF_HOSTBUF_IDX));
+}
+
+#else
+static inline void REG_SW_SET_PROFILING(void *env, int value)
+{
+	(void)env;
+	(void)value;
+}
+
+static inline void REG_SW_CLEAR_PROFILING(void *env, int value)
+{
+	(void)env;
+	(void)value;
+}
+
+static inline void REG_SW_SET_HOSTBUF_IDX_PROFILING(void *env, int val)
+{
+	(void)env;
+	(void)val;
+}
+
+static inline void REG_SW_CLEAR_HOSTBUF_IDX_PROFILING(void *env)
+{
+	(void)env;
+}
+#endif
+
+#endif /* _RWNX_PROF_H_ */
diff --git a/drivers/net/wireless/aic/aic8800_fdrv/rwnx_strs.c b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_strs.c
new file mode 100644
index 0000000000000..50e81d81abf1e
--- /dev/null
+++ b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_strs.c
@@ -0,0 +1,269 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ ****************************************************************************************
+ *
+ * Copyright (C) 2020 AIC semiconductor.
+ *
+ * @file rwnx_strs.c
+ *
+ * @brief Miscellaneous debug strings
+ *
+ ****************************************************************************************
+ */
+
+#include "lmac_msg.h"
+#include "rwnx_strs.h"
+static const char *const rwnx_mmid2str[MSG_I(MM_MAX)] = {
+	[MSG_I(MM_RESET_REQ)] = "MM_RESET_REQ",
+	[MSG_I(MM_RESET_CFM)] = "MM_RESET_CFM",
+	[MSG_I(MM_START_REQ)] = "MM_START_REQ",
+	[MSG_I(MM_START_CFM)] = "MM_START_CFM",
+	[MSG_I(MM_VERSION_REQ)] = "MM_VERSION_REQ",
+	[MSG_I(MM_VERSION_CFM)] = "MM_VERSION_CFM",
+	[MSG_I(MM_ADD_IF_REQ)] = "MM_ADD_IF_REQ",
+	[MSG_I(MM_ADD_IF_CFM)] = "MM_ADD_IF_CFM",
+	[MSG_I(MM_REMOVE_IF_REQ)] = "MM_REMOVE_IF_REQ",
+	[MSG_I(MM_REMOVE_IF_CFM)] = "MM_REMOVE_IF_CFM",
+	[MSG_I(MM_STA_ADD_REQ)] = "MM_STA_ADD_REQ",
+	[MSG_I(MM_STA_ADD_CFM)] = "MM_STA_ADD_CFM",
+	[MSG_I(MM_STA_DEL_REQ)] = "MM_STA_DEL_REQ",
+	[MSG_I(MM_STA_DEL_CFM)] = "MM_STA_DEL_CFM",
+	[MSG_I(MM_SET_FILTER_REQ)] = "MM_SET_FILTER_REQ",
+	[MSG_I(MM_SET_FILTER_CFM)] = "MM_SET_FILTER_CFM",
+	[MSG_I(MM_SET_CHANNEL_REQ)] = "MM_SET_CHANNEL_REQ",
+	[MSG_I(MM_SET_CHANNEL_CFM)] = "MM_SET_CHANNEL_CFM",
+	[MSG_I(MM_SET_DTIM_REQ)] = "MM_SET_DTIM_REQ",
+	[MSG_I(MM_SET_DTIM_CFM)] = "MM_SET_DTIM_CFM",
+	[MSG_I(MM_SET_BEACON_INT_REQ)] = "MM_SET_BEACON_INT_REQ",
+	[MSG_I(MM_SET_BEACON_INT_CFM)] = "MM_SET_BEACON_INT_CFM",
+	[MSG_I(MM_SET_BASIC_RATES_REQ)] = "MM_SET_BASIC_RATES_REQ",
+	[MSG_I(MM_SET_BASIC_RATES_CFM)] = "MM_SET_BASIC_RATES_CFM",
+	[MSG_I(MM_SET_BSSID_REQ)] = "MM_SET_BSSID_REQ",
+	[MSG_I(MM_SET_BSSID_CFM)] = "MM_SET_BSSID_CFM",
+	[MSG_I(MM_SET_EDCA_REQ)] = "MM_SET_EDCA_REQ",
+	[MSG_I(MM_SET_EDCA_CFM)] = "MM_SET_EDCA_CFM",
+	[MSG_I(MM_SET_MODE_REQ)] = "MM_SET_MODE_REQ",
+	[MSG_I(MM_SET_MODE_CFM)] = "MM_SET_MODE_CFM",
+	[MSG_I(MM_SET_VIF_STATE_REQ)] = "MM_SET_VIF_STATE_REQ",
+	[MSG_I(MM_SET_VIF_STATE_CFM)] = "MM_SET_VIF_STATE_CFM",
+	[MSG_I(MM_SET_SLOTTIME_REQ)] = "MM_SET_SLOTTIME_REQ",
+	[MSG_I(MM_SET_SLOTTIME_CFM)] = "MM_SET_SLOTTIME_CFM",
+	[MSG_I(MM_SET_IDLE_REQ)] = "MM_SET_IDLE_REQ",
+	[MSG_I(MM_SET_IDLE_CFM)] = "MM_SET_IDLE_CFM",
+	[MSG_I(MM_KEY_ADD_REQ)] = "MM_KEY_ADD_REQ",
+	[MSG_I(MM_KEY_ADD_CFM)] = "MM_KEY_ADD_CFM",
+	[MSG_I(MM_KEY_DEL_REQ)] = "MM_KEY_DEL_REQ",
+	[MSG_I(MM_KEY_DEL_CFM)] = "MM_KEY_DEL_CFM",
+	[MSG_I(MM_BA_ADD_REQ)] = "MM_BA_ADD_REQ",
+	[MSG_I(MM_BA_ADD_CFM)] = "MM_BA_ADD_CFM",
+	[MSG_I(MM_BA_DEL_REQ)] = "MM_BA_DEL_REQ",
+	[MSG_I(MM_BA_DEL_CFM)] = "MM_BA_DEL_CFM",
+	[MSG_I(MM_PRIMARY_TBTT_IND)] = "MM_PRIMARY_TBTT_IND",
+	[MSG_I(MM_SECONDARY_TBTT_IND)] = "MM_SECONDARY_TBTT_IND",
+	[MSG_I(MM_SET_POWER_REQ)] = "MM_SET_POWER_REQ",
+	[MSG_I(MM_SET_POWER_CFM)] = "MM_SET_POWER_CFM",
+	[MSG_I(MM_DBG_TRIGGER_REQ)] = "MM_DBG_TRIGGER_REQ",
+	[MSG_I(MM_SET_PS_MODE_REQ)] = "MM_SET_PS_MODE_REQ",
+	[MSG_I(MM_SET_PS_MODE_CFM)] = "MM_SET_PS_MODE_CFM",
+	[MSG_I(MM_CHAN_CTXT_ADD_REQ)] = "MM_CHAN_CTXT_ADD_REQ",
+	[MSG_I(MM_CHAN_CTXT_ADD_CFM)] = "MM_CHAN_CTXT_ADD_CFM",
+	[MSG_I(MM_CHAN_CTXT_DEL_REQ)] = "MM_CHAN_CTXT_DEL_REQ",
+	[MSG_I(MM_CHAN_CTXT_DEL_CFM)] = "MM_CHAN_CTXT_DEL_CFM",
+	[MSG_I(MM_CHAN_CTXT_LINK_REQ)] = "MM_CHAN_CTXT_LINK_REQ",
+	[MSG_I(MM_CHAN_CTXT_LINK_CFM)] = "MM_CHAN_CTXT_LINK_CFM",
+	[MSG_I(MM_CHAN_CTXT_UNLINK_REQ)] = "MM_CHAN_CTXT_UNLINK_REQ",
+	[MSG_I(MM_CHAN_CTXT_UNLINK_CFM)] = "MM_CHAN_CTXT_UNLINK_CFM",
+	[MSG_I(MM_CHAN_CTXT_UPDATE_REQ)] = "MM_CHAN_CTXT_UPDATE_REQ",
+	[MSG_I(MM_CHAN_CTXT_UPDATE_CFM)] = "MM_CHAN_CTXT_UPDATE_CFM",
+	[MSG_I(MM_CHAN_CTXT_SCHED_REQ)] = "MM_CHAN_CTXT_SCHED_REQ",
+	[MSG_I(MM_CHAN_CTXT_SCHED_CFM)] = "MM_CHAN_CTXT_SCHED_CFM",
+	[MSG_I(MM_BCN_CHANGE_REQ)] = "MM_BCN_CHANGE_REQ",
+	[MSG_I(MM_BCN_CHANGE_CFM)] = "MM_BCN_CHANGE_CFM",
+	[MSG_I(MM_TIM_UPDATE_REQ)] = "MM_TIM_UPDATE_REQ",
+	[MSG_I(MM_TIM_UPDATE_CFM)] = "MM_TIM_UPDATE_CFM",
+	[MSG_I(MM_CONNECTION_LOSS_IND)] = "MM_CONNECTION_LOSS_IND",
+	[MSG_I(MM_CHANNEL_SWITCH_IND)] = "MM_CHANNEL_SWITCH_IND",
+	[MSG_I(MM_CHANNEL_PRE_SWITCH_IND)] = "MM_CHANNEL_PRE_SWITCH_IND",
+	[MSG_I(MM_REMAIN_ON_CHANNEL_REQ)] = "MM_REMAIN_ON_CHANNEL_REQ",
+	[MSG_I(MM_REMAIN_ON_CHANNEL_CFM)] = "MM_REMAIN_ON_CHANNEL_CFM",
+	[MSG_I(MM_REMAIN_ON_CHANNEL_EXP_IND)] = "MM_REMAIN_ON_CHANNEL_EXP_IND",
+	[MSG_I(MM_PS_CHANGE_IND)] = "MM_PS_CHANGE_IND",
+	[MSG_I(MM_TRAFFIC_REQ_IND)] = "MM_TRAFFIC_REQ_IND",
+	[MSG_I(MM_SET_PS_OPTIONS_REQ)] = "MM_SET_PS_OPTIONS_REQ",
+	[MSG_I(MM_SET_PS_OPTIONS_CFM)] = "MM_SET_PS_OPTIONS_CFM",
+	[MSG_I(MM_P2P_VIF_PS_CHANGE_IND)] = "MM_P2P_VIF_PS_CHANGE_IND",
+	[MSG_I(MM_CSA_COUNTER_IND)] = "MM_CSA_COUNTER_IND",
+	[MSG_I(MM_CHANNEL_SURVEY_IND)] = "MM_CHANNEL_SURVEY_IND",
+	[MSG_I(MM_SET_P2P_NOA_REQ)] = "MM_SET_P2P_NOA_REQ",
+	[MSG_I(MM_SET_P2P_OPPPS_REQ)] = "MM_SET_P2P_OPPPS_REQ",
+	[MSG_I(MM_SET_P2P_NOA_CFM)] = "MM_SET_P2P_NOA_CFM",
+	[MSG_I(MM_SET_P2P_OPPPS_CFM)] = "MM_SET_P2P_OPPPS_CFM",
+	[MSG_I(MM_CFG_RSSI_REQ)] = "MM_CFG_RSSI_REQ",
+	[MSG_I(MM_RSSI_STATUS_IND)] = "MM_RSSI_STATUS_IND",
+	[MSG_I(MM_CSA_FINISH_IND)] = "MM_CSA_FINISH_IND",
+	[MSG_I(MM_CSA_TRAFFIC_IND)] = "MM_CSA_TRAFFIC_IND",
+	[MSG_I(MM_MU_GROUP_UPDATE_REQ)] = "MM_MU_GROUP_UPDATE_REQ",
+	[MSG_I(MM_MU_GROUP_UPDATE_CFM)] = "MM_MU_GROUP_UPDATE_CFM",
+
+	[MSG_I(MM_SET_ARPOFFLOAD_REQ)] = "MM_SET_ARPOFFLOAD_REQ",
+	[MSG_I(MM_SET_ARPOFFLOAD_CFM)] = "MM_SET_ARPOFFLOAD_CFM",
+	[MSG_I(MM_SET_AGG_DISABLE_REQ)] = "MM_SET_AGG_DISABLE_REQ",
+	[MSG_I(MM_SET_AGG_DISABLE_CFM)] = "MM_SET_AGG_DISABLE_CFM",
+	[MSG_I(MM_SET_COEX_REQ)] = "MM_SET_COEX_REQ",
+	[MSG_I(MM_SET_COEX_CFM)] = "MM_SET_COEX_CFM",
+	[MSG_I(MM_SET_RF_CONFIG_REQ)] = "MM_SET_RF_CONFIG_REQ",
+	[MSG_I(MM_SET_RF_CONFIG_CFM)] = "MM_SET_RF_CONFIG_CFM",
+	[MSG_I(MM_SET_RF_CALIB_REQ)] = "MM_SET_RF_CALIB_REQ",
+	[MSG_I(MM_SET_RF_CALIB_CFM)] = "MM_SET_RF_CALIB_CFM",
+
+	[MSG_I(MM_GET_MAC_ADDR_REQ)] = "MM_GET_MAC_ADDR_REQ",
+	[MSG_I(MM_GET_MAC_ADDR_CFM)] = "MM_GET_MAC_ADDR_CFM",
+	[MSG_I(MM_GET_STA_INFO_REQ)] = "MM_GET_STA_INFO_REQ",
+	[MSG_I(MM_GET_STA_INFO_CFM)] = "MM_GET_STA_INFO_CFM",
+	[MSG_I(MM_SET_TXPWR_IDX_LVL_REQ)] = "MM_SET_TXPWR_IDX_LVL_REQ",
+	[MSG_I(MM_SET_TXPWR_IDX_LVL_CFM)] = "MM_SET_TXPWR_IDX_LVL_CFM",
+	[MSG_I(MM_SET_TXPWR_OFST_REQ)] = "MM_SET_TXPWR_OFST_REQ",
+	[MSG_I(MM_SET_TXPWR_OFST_CFM)] = "MM_SET_TXPWR_OFST_CFM",
+	[MSG_I(MM_SET_STACK_START_REQ)] = "MM_SET_STACK_START_REQ",
+	[MSG_I(MM_SET_STACK_START_CFM)] = "MM_SET_STACK_START_CFM",
+	[MSG_I(MM_APM_STALOSS_IND)] = "MM_APM_STALOSS_IND",
+	[MSG_I(MM_SET_VENDOR_HWCONFIG_REQ)] = "MM_SET_VENDOR_HWCONFIG_REQ",
+	[MSG_I(MM_SET_VENDOR_HWCONFIG_CFM)] = "MM_SET_VENDOR_HWCONFIG_CFM",
+	[MSG_I(MM_GET_FW_VERSION_REQ)] = "MM_GET_FW_VERSION_REQ",
+	[MSG_I(MM_GET_FW_VERSION_CFM)] = "MM_GET_FW_VERSION_CFM",
+	[MSG_I(MM_SET_RESUME_RESTORE_REQ)] = "MM_SET_RESUME_RESTORE_REQ",
+	[MSG_I(MM_SET_RESUME_RESTORE_CFM)] = "MM_SET_RESUME_RESTORE_CFM",
+	[MSG_I(MM_GET_WIFI_DISABLE_REQ)] = "MM_GET_WIFI_DISABLE_REQ",
+	[MSG_I(MM_GET_WIFI_DISABLE_CFM)] = "MM_GET_WIFI_DISABLE_CFM",
+	[MSG_I(MM_CFG_RSSI_CFM)] = "MM_CFG_RSSI_CFM",
+	[MSG_I(MM_SET_VENDOR_SWCONFIG_REQ)] = "MM_SET_VENDOR_SWCONFIG_REQ",
+	[MSG_I(MM_SET_VENDOR_SWCONFIG_CFM)] = "MM_SET_VENDOR_SWCONFIG_CFM",
+};
+
+static const char *const rwnx_dbgid2str[MSG_I(DBG_MAX)] = {
+	[MSG_I(DBG_MEM_READ_REQ)] = "DBG_MEM_READ_REQ",
+	[MSG_I(DBG_MEM_READ_CFM)] = "DBG_MEM_READ_CFM",
+	[MSG_I(DBG_MEM_WRITE_REQ)] = "DBG_MEM_WRITE_REQ",
+	[MSG_I(DBG_MEM_WRITE_CFM)] = "DBG_MEM_WRITE_CFM",
+	[MSG_I(DBG_SET_MOD_FILTER_REQ)] = "DBG_SET_MOD_FILTER_REQ",
+	[MSG_I(DBG_SET_MOD_FILTER_CFM)] = "DBG_SET_MOD_FILTER_CFM",
+	[MSG_I(DBG_SET_SEV_FILTER_REQ)] = "DBG_SET_SEV_FILTER_REQ",
+	[MSG_I(DBG_SET_SEV_FILTER_CFM)] = "DBG_SET_SEV_FILTER_CFM",
+	[MSG_I(DBG_ERROR_IND)] = "DBG_ERROR_IND",
+	[MSG_I(DBG_GET_SYS_STAT_REQ)] = "DBG_GET_SYS_STAT_REQ",
+	[MSG_I(DBG_GET_SYS_STAT_CFM)] = "DBG_GET_SYS_STAT_CFM",
+};
+
+static const char *const rwnx_scanid2str[MSG_I(SCAN_MAX)] = {
+	[MSG_I(SCAN_START_REQ)] = "SCAN_START_REQ",
+	[MSG_I(SCAN_START_CFM)] = "SCAN_START_CFM",
+	[MSG_I(SCAN_DONE_IND)] = "SCAN_DONE_IND",
+};
+
+static const char *const rwnx_tdlsid2str[MSG_I(TDLS_MAX)] = {
+	[MSG_I(TDLS_CHAN_SWITCH_CFM)] = "TDLS_CHAN_SWITCH_CFM",
+	[MSG_I(TDLS_CHAN_SWITCH_REQ)] = "TDLS_CHAN_SWITCH_REQ",
+	[MSG_I(TDLS_CHAN_SWITCH_IND)] = "TDLS_CHAN_SWITCH_IND",
+	[MSG_I(TDLS_CHAN_SWITCH_BASE_IND)] = "TDLS_CHAN_SWITCH_BASE_IND",
+	[MSG_I(TDLS_CANCEL_CHAN_SWITCH_REQ)] = "TDLS_CANCEL_CHAN_SWITCH_REQ",
+	[MSG_I(TDLS_CANCEL_CHAN_SWITCH_CFM)] = "TDLS_CANCEL_CHAN_SWITCH_CFM",
+	[MSG_I(TDLS_PEER_PS_IND)] = "TDLS_PEER_PS_IND",
+	[MSG_I(TDLS_PEER_TRAFFIC_IND_REQ)] = "TDLS_PEER_TRAFFIC_IND_REQ",
+	[MSG_I(TDLS_PEER_TRAFFIC_IND_CFM)] = "TDLS_PEER_TRAFFIC_IND_CFM",
+};
+
+#ifdef CONFIG_RWNX_FULLMAC
+
+static const char *const rwnx_scanuid2str[MSG_I(SCANU_MAX)] = {
+	[MSG_I(SCANU_START_REQ)] = "SCANU_START_REQ",
+	[MSG_I(SCANU_START_CFM)] = "SCANU_START_CFM",
+	[MSG_I(SCANU_JOIN_REQ)] = "SCANU_JOIN_REQ",
+	[MSG_I(SCANU_JOIN_CFM)] = "SCANU_JOIN_CFM",
+	[MSG_I(SCANU_RESULT_IND)] = "SCANU_RESULT_IND",
+	[MSG_I(SCANU_FAST_REQ)] = "SCANU_FAST_REQ",
+	[MSG_I(SCANU_FAST_CFM)] = "SCANU_FAST_CFM",
+	[MSG_I(SCANU_VENDOR_IE_REQ)] = "SCANU_VENDOR_IE_REQ",
+	[MSG_I(SCANU_VENDOR_IE_CFM)] = "SCANU_VENDOR_IE_CFM",
+	[MSG_I(SCANU_START_CFM_ADDTIONAL)] = "SCANU_START_CFM_ADDTIONAL",
+	[MSG_I(SCANU_CANCEL_REQ)] = "SCANU_CANCEL_REQ",
+	[MSG_I(SCANU_CANCEL_CFM)] = "SCANU_CANCEL_CFM",
+};
+
+static const char *const rwnx_meid2str[MSG_I(ME_MAX)] = {
+	[MSG_I(ME_CONFIG_REQ)] = "ME_CONFIG_REQ",
+	[MSG_I(ME_CONFIG_CFM)] = "ME_CONFIG_CFM",
+	[MSG_I(ME_CHAN_CONFIG_REQ)] = "ME_CHAN_CONFIG_REQ",
+	[MSG_I(ME_CHAN_CONFIG_CFM)] = "ME_CHAN_CONFIG_CFM",
+	[MSG_I(ME_SET_CONTROL_PORT_REQ)] = "ME_SET_CONTROL_PORT_REQ",
+	[MSG_I(ME_SET_CONTROL_PORT_CFM)] = "ME_SET_CONTROL_PORT_CFM",
+	[MSG_I(ME_TKIP_MIC_FAILURE_IND)] = "ME_TKIP_MIC_FAILURE_IND",
+	[MSG_I(ME_STA_ADD_REQ)] = "ME_STA_ADD_REQ",
+	[MSG_I(ME_STA_ADD_CFM)] = "ME_STA_ADD_CFM",
+	[MSG_I(ME_STA_DEL_REQ)] = "ME_STA_DEL_REQ",
+	[MSG_I(ME_STA_DEL_CFM)] = "ME_STA_DEL_CFM",
+	[MSG_I(ME_TX_CREDITS_UPDATE_IND)] = "ME_TX_CREDITS_UPDATE_IND",
+	[MSG_I(ME_RC_STATS_REQ)] = "ME_RC_STATS_REQ",
+	[MSG_I(ME_RC_STATS_CFM)] = "ME_RC_STATS_CFM",
+	[MSG_I(ME_RC_SET_RATE_REQ)] = "ME_RC_SET_RATE_REQ",
+	[MSG_I(ME_TRAFFIC_IND_REQ)] = "ME_TRAFFIC_IND_REQ",
+	[MSG_I(ME_TRAFFIC_IND_CFM)] = "ME_TRAFFIC_IND_CFM",
+	[MSG_I(ME_SET_PS_MODE_REQ)] = "ME_SET_PS_MODE_REQ",
+	[MSG_I(ME_SET_PS_MODE_CFM)] = "ME_SET_PS_MODE_CFM",
+	[MSG_I(ME_SET_LP_LEVEL_REQ)] = "ME_SET_LP_LEVEL_REQ",
+	[MSG_I(ME_SET_LP_LEVEL_CFM)] = "ME_SET_LP_LEVEL_CFM",
+};
+
+static const char *const rwnx_smid2str[MSG_I(SM_MAX)] = {
+	[MSG_I(SM_CONNECT_REQ)] = "SM_CONNECT_REQ",
+	[MSG_I(SM_CONNECT_CFM)] = "SM_CONNECT_CFM",
+	[MSG_I(SM_CONNECT_IND)] = "SM_CONNECT_IND",
+	[MSG_I(SM_DISCONNECT_REQ)] = "SM_DISCONNECT_REQ",
+	[MSG_I(SM_DISCONNECT_CFM)] = "SM_DISCONNECT_CFM",
+	[MSG_I(SM_DISCONNECT_IND)] = "SM_DISCONNECT_IND",
+	[MSG_I(SM_EXTERNAL_AUTH_REQUIRED_IND)] = "SM_EXTERNAL_AUTH_REQUIRED_IND",
+	[MSG_I(SM_EXTERNAL_AUTH_REQUIRED_RSP)] = "SM_EXTERNAL_AUTH_REQUIRED_RSP",
+	[MSG_I(SM_EXTERNAL_AUTH_REQUIRED_RSP_CFM)] =
+		"SM_EXTERNAL_AUTH_REQUIRED_RSP_CFM",
+};
+
+static const char *const rwnx_apmid2str[MSG_I(APM_MAX)] = {
+	[MSG_I(APM_START_REQ)] = "APM_START_REQ",
+	[MSG_I(APM_START_CFM)] = "APM_START_CFM",
+	[MSG_I(APM_STOP_REQ)] = "APM_STOP_REQ",
+	[MSG_I(APM_STOP_CFM)] = "APM_STOP_CFM",
+	[MSG_I(APM_START_CAC_REQ)] = "APM_START_CAC_REQ",
+	[MSG_I(APM_START_CAC_CFM)] = "APM_START_CAC_CFM",
+	[MSG_I(APM_STOP_CAC_REQ)] = "APM_STOP_CAC_REQ",
+	[MSG_I(APM_STOP_CAC_CFM)] = "APM_STOP_CAC_CFM",
+	[MSG_I(APM_SET_BEACON_IE_REQ)] = "APM_SET_BEACON_IE_REQ",
+	[MSG_I(APM_SET_BEACON_IE_CFM)] = "APM_SET_BEACON_IE_CFM",
+};
+
+static const char *const rwnx_meshid2str[MSG_I(MESH_MAX)] = {
+	[MSG_I(MESH_START_REQ)] = "MESH_START_REQ",
+	[MSG_I(MESH_START_CFM)] = "MESH_START_CFM",
+	[MSG_I(MESH_STOP_REQ)] = "MESH_STOP_REQ",
+	[MSG_I(MESH_STOP_CFM)] = "MESH_STOP_CFM",
+	[MSG_I(MESH_UPDATE_REQ)] = "MESH_UPDATE_REQ",
+	[MSG_I(MESH_UPDATE_CFM)] = "MESH_UPDATE_CFM",
+	[MSG_I(MESH_PATH_CREATE_REQ)] = "MESH_PATH_CREATE_REQ",
+	[MSG_I(MESH_PATH_CREATE_CFM)] = "MESH_PATH_CREATE_CFM",
+	[MSG_I(MESH_PATH_UPDATE_REQ)] = "MESH_PATH_UPDATE_REQ",
+	[MSG_I(MESH_PATH_UPDATE_CFM)] = "MESH_PATH_UPDATE_CFM",
+	[MSG_I(MESH_PROXY_ADD_REQ)] = "MESH_PROXY_ADD_REQ",
+	[MSG_I(MESH_PEER_UPDATE_IND)] = "MESH_PEER_UPDATE_IND",
+	[MSG_I(MESH_PATH_UPDATE_IND)] = "MESH_PATH_UPDATE_IND",
+	[MSG_I(MESH_PROXY_UPDATE_IND)] = "MESH_PROXY_UPDATE_IND",
+};
+
+#endif /* CONFIG_RWNX_FULLMAC */
+
+const char *const *rwnx_id2str[TASK_LAST_EMB + 1] = {
+	[TASK_MM] = rwnx_mmid2str,       [TASK_DBG] = rwnx_dbgid2str,
+	[TASK_SCAN] = rwnx_scanid2str,   [TASK_TDLS] = rwnx_tdlsid2str,
+#ifdef CONFIG_RWNX_FULLMAC
+	[TASK_SCANU] = rwnx_scanuid2str, [TASK_ME] = rwnx_meid2str,
+	[TASK_SM] = rwnx_smid2str,       [TASK_APM] = rwnx_apmid2str,
+	[TASK_MESH] = rwnx_meshid2str,
+#endif
+};
diff --git a/drivers/net/wireless/aic/aic8800_fdrv/rwnx_strs.h b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_strs.h
new file mode 100644
index 0000000000000..1b4df40f2a912
--- /dev/null
+++ b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_strs.h
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ ****************************************************************************************
+ *
+ * Copyright (C) 2020 AIC semiconductor.
+ *
+ * @file rwnx_strs.h
+ *
+ * @brief Miscellaneous debug strings
+ *
+ ****************************************************************************************
+ */
+
+#ifndef _RWNX_STRS_H_
+#define _RWNX_STRS_H_
+
+#include "lmac_msg.h"
+
+#define RWNX_ID2STR(tag)                                               \
+	({                                                                 \
+		const typeof(tag) __tag = (tag);                               \
+		((MSG_T(__tag) < ARRAY_SIZE(rwnx_id2str)) &&                   \
+		 (rwnx_id2str[MSG_T(__tag)]) &&                                \
+		 ((rwnx_id2str[MSG_T(__tag)])[MSG_I(__tag)]))                  \
+		 ? (rwnx_id2str[MSG_T(__tag)])[MSG_I(__tag)]                \
+		 : "unknown";                                               \
+	})
+
+extern const char *const *rwnx_id2str[TASK_LAST_EMB + 1];
+
+#endif /* _RWNX_STRS_H_ */
diff --git a/drivers/net/wireless/aic/aic8800_fdrv/rwnx_utils.c b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_utils.c
new file mode 100644
index 0000000000000..73e8ab7c8af5c
--- /dev/null
+++ b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_utils.c
@@ -0,0 +1,35 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ ****************************************************************************************
+ *
+ * Copyright (C) 2020 AIC semiconductor.
+ *
+ * @file rwnx_utils.c
+ *
+ * @brief IPC utility function declarations
+ *
+ ****************************************************************************************
+ */
+
+#include "rwnx_utils.h"
+#include "ipc_host.h"
+#include "rwnx_defs.h"
+#include "rwnx_msg_rx.h"
+#include "rwnx_prof.h"
+#include "rwnx_rx.h"
+#include "rwnx_tx.h"
+
+int rwnx_init_aic(struct rwnx_hw *rwnx_hw)
+{
+	RWNX_DBG(RWNX_FN_ENTRY_STR);
+#ifdef AICWF_SDIO_SUPPORT
+	aicwf_sdio_host_init(&rwnx_hw->sdio_env, NULL, NULL, rwnx_hw);
+#else
+	aicwf_usb_host_init(&rwnx_hw->usb_env, NULL, NULL, rwnx_hw);
+#endif
+	rwnx_cmd_mgr_init(rwnx_hw->cmd_mgr);
+
+	rwnx_hw->testmode = get_testmode();
+
+	return 0;
+}
diff --git a/drivers/net/wireless/aic/aic8800_fdrv/rwnx_utils.h b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_utils.h
new file mode 100644
index 0000000000000..aecd627ff9867
--- /dev/null
+++ b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_utils.h
@@ -0,0 +1,131 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ ****************************************************************************************
+ *
+ * Copyright (C) 2020 AIC semiconductor.
+ *
+ * @file rwnx_utils.c
+ *
+ * @brief IPC utility function declarations
+ *
+ ****************************************************************************************
+ */
+
+#ifndef _RWNX_IPC_UTILS_H_
+#define _RWNX_IPC_UTILS_H_
+
+#include "aicwf_debug.h"
+#include <linux/dma-mapping.h>
+#include <linux/dmapool.h>
+#include <linux/skbuff.h>
+#include "lmac_msg.h"
+#include "rwnx_compat.h"
+
+enum rwnx_dev_flag {
+	RWNX_DEV_RESTARTING,
+	RWNX_DEV_STACK_RESTARTING,
+	RWNX_DEV_STARTED,
+};
+
+struct rwnx_hw;
+struct rwnx_sta;
+
+/**
+ * struct rwnx_ipc_elem - Generic IPC buffer of fixed size
+ *
+ * @addr: Host address of the buffer.
+ * @dma_addr: DMA address of the buffer.
+ */
+struct rwnx_ipc_elem {
+	void *addr;
+	dma_addr_t dma_addr;
+};
+
+/**
+ * struct rwnx_ipc_elem_pool - Generic pool of IPC buffers of fixed size
+ *
+ * @nb: Number of buffers currenlty allocated in the pool
+ * @buf: Array of buffers (size of array is @nb)
+ * @pool: DMA pool in which buffers have been allocated
+ */
+struct rwnx_ipc_elem_pool {
+	int nb;
+	struct rwnx_ipc_elem *buf;
+	struct dma_pool *pool;
+};
+
+/**
+ * struct rwnx_ipc_elem_var - Generic IPC buffer of variable size
+ *
+ * @addr: Host address of the buffer.
+ * @dma_addr: DMA address of the buffer.
+ * @size: Size, in bytes, of the buffer
+ */
+struct rwnx_ipc_elem_var {
+	void *addr;
+	dma_addr_t dma_addr;
+	size_t size;
+};
+
+/**
+ * struct rwnx_ipc_dbgdump_elem - IPC buffer for debug dump
+ *
+ * @mutex: Mutex to protect access to debug dump
+ * @buf: IPC buffer
+ */
+struct rwnx_ipc_dbgdump_elem {
+	struct mutex mutex; //Mutex to protect access to debug dump
+	struct rwnx_ipc_elem_var buf;
+};
+
+/*
+ * Maximum Length of Radiotap header vendor specific data(in bytes)
+ */
+#define RADIOTAP_HDR_VEND_MAX_LEN 16
+
+/*
+ * Maximum Radiotap Header Length without vendor specific data (in bytes)
+ */
+#define RADIOTAP_HDR_MAX_LEN 80
+
+/*
+ * Unsupported HT Frame data length (in bytes)
+ */
+#define UNSUP_RX_VEC_DATA_LEN 2
+
+/**
+ * struct rwnx_ipc_skb_elem - IPC buffer for SKB element
+ *
+ * @skb: Pointer to the skb buffer allocated
+ * @dma_addr: DMA address of the data buffer fo skb
+ *
+ */
+struct rwnx_ipc_skb_elem {
+	struct sk_buff *skb;
+	dma_addr_t dma_addr;
+};
+
+#ifdef CONFIG_RWNX_FULLMAC
+
+/* Maximum number of rx buffer the fw may use at the same time */
+#define RWNX_RXBUFF_MAX (64 * NX_REMOTE_STA_MAX)
+
+/**
+ * struct rwnx_ipc_rxbuf_elems - IPC buffers for RX
+ *
+ * @skb: Array of buffer push to FW.
+ * @idx: Index of the last pushed skb.(Use to find the next free entry quicker)
+ *
+ * Note: contrary to softmac version, dma_addr are stored inside skb->cb.
+ */
+struct rwnx_ipc_rxbuf_elems {
+	struct sk_buff *skb[RWNX_RXBUFF_MAX];
+	int idx;
+};
+
+#endif /* CONFIG_RWNX_FULLMAC */
+int rwnx_init_aic(struct rwnx_hw *rwnx_hw);
+
+int get_testmode(void);
+
+#endif /* _RWNX_IPC_UTILS_H_ */
diff --git a/drivers/net/wireless/aic/aic8800_fdrv/rwnx_v7.c b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_v7.c
new file mode 100644
index 0000000000000..4138c7f7d07e4
--- /dev/null
+++ b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_v7.c
@@ -0,0 +1,181 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ ****************************************************************************************
+ *
+ * Copyright (C) 2020 AIC semiconductor.
+ *
+ * @file rwnx_v7.h
+ *
+ ****************************************************************************************
+ */
+
+#include "rwnx_v7.h"
+#include "hal_desc.h"
+#include "reg_access.h"
+#include "rwnx_defs.h"
+#include "rwnx_irqs.h"
+
+struct rwnx_v7 {
+	u8 __iomem *pci_bar0_vaddr;
+	u8 __iomem *pci_bar1_vaddr;
+};
+
+static int rwnx_v7_platform_enable(struct rwnx_hw *rwnx_hw)
+{
+	int ret;
+
+	/* sched_setscheduler on ONESHOT threaded irq handler for BCNs ? */
+	ret = request_irq(rwnx_hw->plat->pci_dev->irq, rwnx_irq_hdlr, 0, "rwnx",
+			  rwnx_hw);
+	return ret;
+}
+
+static int rwnx_v7_platform_disable(struct rwnx_hw *rwnx_hw)
+{
+	free_irq(rwnx_hw->plat->pci_dev->irq, rwnx_hw);
+	return 0;
+}
+
+static void rwnx_v7_platform_deinit(struct rwnx_plat *rwnx_plat)
+{
+#ifdef CONFIG_PCI
+	struct rwnx_v7 *rwnx_v7 = (struct rwnx_v7 *)rwnx_plat->priv;
+
+	pci_disable_device(rwnx_plat->pci_dev);
+	iounmap(rwnx_v7->pci_bar0_vaddr);
+	iounmap(rwnx_v7->pci_bar1_vaddr);
+	pci_release_regions(rwnx_plat->pci_dev);
+	pci_clear_master(rwnx_plat->pci_dev);
+	pci_disable_msi(rwnx_plat->pci_dev);
+#endif
+	kfree(rwnx_plat);
+}
+
+static u8 __iomem *rwnx_v7_get_address(struct rwnx_plat *rwnx_plat,
+				       int addr_name, unsigned int offset)
+{
+	struct rwnx_v7 *rwnx_v7 = (struct rwnx_v7 *)rwnx_plat->priv;
+
+	if (WARN(addr_name >= RWNX_ADDR_MAX, "Invalid address %d", addr_name))
+		return NULL;
+
+	if (addr_name == RWNX_ADDR_CPU)
+		return rwnx_v7->pci_bar0_vaddr + offset;
+	else
+		return rwnx_v7->pci_bar1_vaddr + offset;
+}
+
+static void rwnx_v7_ack_irq(struct rwnx_plat *rwnx_plat)
+{
+}
+
+static const u32 rwnx_v7_config_reg[] = {
+	NXMAC_DEBUG_PORT_SEL_ADDR, SYSCTRL_DIAG_CONF_ADDR,
+	SYSCTRL_PHYDIAG_CONF_ADDR, SYSCTRL_RIUDIAG_CONF_ADDR,
+	RF_V7_DIAGPORT_CONF1_ADDR,
+};
+
+static const u32 rwnx_v7_he_config_reg[] = {
+	SYSCTRL_DIAG_CONF0,
+	SYSCTRL_DIAG_CONF1,
+	SYSCTRL_DIAG_CONF2,
+	SYSCTRL_DIAG_CONF3,
+};
+
+static int rwnx_v7_get_config_reg(struct rwnx_plat *rwnx_plat, const u32 **list)
+{
+	u32 fpga_sign;
+
+	if (!list)
+		return 0;
+
+	fpga_sign =
+		RWNX_REG_READ(rwnx_plat, RWNX_ADDR_SYSTEM, SYSCTRL_SIGNATURE_ADDR);
+	if (__FPGA_TYPE(fpga_sign) == 0xc0ca) {
+		*list = rwnx_v7_he_config_reg;
+		return ARRAY_SIZE(rwnx_v7_he_config_reg);
+	}
+	*list = rwnx_v7_config_reg;
+	return ARRAY_SIZE(rwnx_v7_config_reg);
+}
+
+/**
+ * rwnx_v7_platform_init - Initialize the DINI platform
+ * @pci_dev: PCI device
+ * @rwnx_plat: Location in which to return the allocated platform data
+ *
+ * Allocate and initialize a rwnx_plat structure for the dini platform.
+ *
+ * Return: 0 on success, or a negative error code.
+ */
+int rwnx_v7_platform_init(struct pci_dev *pci_dev, struct rwnx_plat **rwnx_plat)
+{
+	struct rwnx_v7 *rwnx_v7;
+	u16 pci_cmd;
+	int ret = 0;
+
+	*rwnx_plat =
+		kzalloc(sizeof(struct rwnx_plat) + sizeof(struct rwnx_v7), GFP_KERNEL);
+	if (!*rwnx_plat)
+		return -ENOMEM;
+
+	rwnx_v7 = (struct rwnx_v7 *)(*rwnx_plat)->priv;
+
+	/* Hotplug fixups */
+	pci_read_config_word(pci_dev, PCI_COMMAND, &pci_cmd);
+	pci_cmd |= PCI_COMMAND_PARITY | PCI_COMMAND_SERR;
+	pci_write_config_word(pci_dev, PCI_COMMAND, pci_cmd);
+
+	ret = pci_enable_device(pci_dev);
+	if (ret) {
+		dev_err(&pci_dev->dev, "pci_enable_device failed\n");
+		goto out_enable;
+	}
+
+	pci_set_master(pci_dev);
+
+#ifdef CONFIG_PCI
+	if (pci_enable_msi(pci_dev)) {
+		dev_err(&pci_dev->dev, "pci_enable_msi failed\n");
+		goto out_msi;
+	}
+#endif
+
+	rwnx_v7->pci_bar0_vaddr = pci_ioremap_bar(pci_dev, 0);
+	if (!rwnx_v7->pci_bar0_vaddr) {
+		dev_err(&pci_dev->dev, "pci_ioremap_bar(%d) failed\n", 0);
+		ret = -ENOMEM;
+		goto out_bar0;
+	}
+	rwnx_v7->pci_bar1_vaddr = pci_ioremap_bar(pci_dev, 1);
+	if (!rwnx_v7->pci_bar1_vaddr) {
+		dev_err(&pci_dev->dev, "pci_ioremap_bar(%d) failed\n", 1);
+		ret = -ENOMEM;
+		goto out_bar1;
+	}
+
+	(*rwnx_plat)->enable = rwnx_v7_platform_enable;
+	(*rwnx_plat)->disable = rwnx_v7_platform_disable;
+	(*rwnx_plat)->deinit = rwnx_v7_platform_deinit;
+	(*rwnx_plat)->get_address = rwnx_v7_get_address;
+	(*rwnx_plat)->ack_irq = rwnx_v7_ack_irq;
+	(*rwnx_plat)->get_config_reg = rwnx_v7_get_config_reg;
+
+	return 0;
+
+out_bar1:
+	iounmap(rwnx_v7->pci_bar0_vaddr);
+out_bar0:
+#ifdef CONFIG_PCI
+	pci_disable_msi(pci_dev);
+out_msi:
+#endif
+	pci_release_regions(pci_dev);
+#ifdef CONFIG_PCI
+	pci_clear_master(pci_dev);
+#endif
+	pci_disable_device(pci_dev);
+out_enable:
+	kfree(*rwnx_plat);
+	return ret;
+}
diff --git a/drivers/net/wireless/aic/aic8800_fdrv/rwnx_v7.h b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_v7.h
new file mode 100644
index 0000000000000..709553f96c2a1
--- /dev/null
+++ b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_v7.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ ****************************************************************************************
+ *
+ * Copyright (C) 2020 AIC semiconductor.
+ *
+ * @file rwnx_v7.h
+ *
+ ****************************************************************************************
+ */
+
+#ifndef _RWNX_V7_H_
+#define _RWNX_V7_H_
+
+#include "rwnx_platform.h"
+#include <linux/pci.h>
+
+int rwnx_v7_platform_init(struct pci_dev *pci_dev,
+			  struct rwnx_plat **rwnx_plat);
+
+#endif /* _RWNX_V7_H_ */
-- 
2.34.1

  parent reply	other threads:[~2026-08-29  8:22 UTC|newest]

Thread overview: 20+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-23  2:13 [RFC PATCH wireless-next v2 0/4] wifi: aic: add AIC8800 SDIO FullMAC driver Yanli Yang
2026-07-23  2:13 ` [RFC PATCH wireless-next v2 1/4] wifi: aic: add SDIO BSP and build configuration Yanli Yang
2026-07-23  2:13 ` [RFC PATCH wireless-next v2 2/4] wifi: aic: add FullMAC firmware and platform interface Yanli Yang
2026-07-23  2:13 ` [RFC PATCH wireless-next v2 3/4] wifi: aic: add FullMAC WLAN driver Yanli Yang
2026-07-23  2:13 ` [RFC PATCH wireless-next v2 4/4] wifi: aic: integrate AIC8800 driver Yanli Yang
2026-07-23  8:42 ` [RFC PATCH wireless-next v2 0/4] wifi: aic: add AIC8800 SDIO FullMAC driver Johannes Berg
2026-07-23  8:48   ` Johannes Berg
2026-08-28 12:11     ` [RFC PATCH wireless-next v3 0/4] wifi: aic: add AIC8800D80 " Yanli Yang
2026-08-28 12:11       ` [RFC PATCH wireless-next v3 1/4] wifi: aic: add SDIO BSP and build configuration Yanli Yang
2026-08-29  8:18         ` Krzysztof Kozlowski
2026-08-28 12:11       ` [RFC PATCH wireless-next v3 2/4] wifi: aic: add FullMAC firmware and platform interface Yanli Yang
2026-08-28 12:11       ` [RFC PATCH wireless-next v3 3/4] wifi: aic: add FullMAC WLAN driver Yanli Yang
2026-08-29  8:21         ` Krzysztof Kozlowski
2026-08-28 12:11       ` [RFC PATCH wireless-next v3 4/4] wifi: aic: integrate AIC8800 driver Yanli Yang
2026-08-29  8:21       ` [RFC PATCH wireless-next v4 0/4] wifi: aic: add AIC8800D80 SDIO FullMAC driver Yanli Yang
2026-08-29  8:21         ` [RFC PATCH wireless-next v4 1/4] wifi: aic: add SDIO BSP and build configuration Yanli Yang
2026-08-29  8:21         ` Yanli Yang [this message]
2026-08-29  8:21         ` [RFC PATCH wireless-next v4 3/4] wifi: aic: add FullMAC WLAN driver Yanli Yang
2026-08-29  8:21         ` [RFC PATCH wireless-next v4 4/4] wifi: aic: integrate AIC8800 driver Yanli Yang
2026-08-31  7:16         ` [RFC PATCH wireless-next v4 0/4] wifi: aic: add AIC8800D80 SDIO FullMAC driver Christian Hewitt

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=9d002f2a238c38ef40a3434ae47b4d91fa191840.1787991457.git.yanli.yang@bedmex.com \
    --to=yanli.yang@bedmex.com \
    --cc=chunqiuliu@aicsemi.com \
    --cc=dijiaxu@aicsemi.com \
    --cc=johannes@sipsolutions.net \
    --cc=liheng.wei@bedmex.com \
    --cc=linux-wireless@vger.kernel.org \
    --cc=zhirunliu@aicsemi.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox